Persistent sessions did not work anymore, because the Application (which became part of the information to be serialized at some point in time) was not serializable.
743 lines
26 KiB
Java
743 lines
26 KiB
Java
/*
|
|
* Helma License Notice
|
|
*
|
|
* The contents of this file are subject to the Helma License
|
|
* Version 2.0 (the "License"). You may not use this file except in
|
|
* compliance with the License. A copy of the License is available at
|
|
* http://adele.helma.org/download/helma/license.txt
|
|
*
|
|
* Copyright 1998-2003 Helma Software. All Rights Reserved.
|
|
*
|
|
* $RCSfile$
|
|
* $Author$
|
|
* $Revision$
|
|
* $Date$
|
|
*/
|
|
|
|
package helma.scripting.rhino;
|
|
|
|
import helma.extensions.ConfigurationException;
|
|
import helma.extensions.HelmaExtension;
|
|
import helma.framework.*;
|
|
import helma.framework.repository.Resource;
|
|
import helma.framework.core.*;
|
|
import helma.main.Server;
|
|
import helma.objectmodel.*;
|
|
import helma.objectmodel.db.DbMapping;
|
|
import helma.objectmodel.db.Relation;
|
|
import helma.objectmodel.db.Node;
|
|
import helma.scripting.*;
|
|
import helma.scripting.rhino.debug.Tracer;
|
|
import helma.scripting.rhino.debug.Profiler;
|
|
import helma.util.StringUtils;
|
|
import org.mozilla.javascript.*;
|
|
import org.mozilla.javascript.serialize.ScriptableOutputStream;
|
|
import org.mozilla.javascript.serialize.ScriptableInputStream;
|
|
|
|
import java.util.*;
|
|
import java.io.*;
|
|
import java.lang.ref.WeakReference;
|
|
|
|
/**
|
|
* This is the implementation of ScriptingEnvironment for the Mozilla Rhino EcmaScript interpreter.
|
|
*/
|
|
public class RhinoEngine implements ScriptingEngine {
|
|
// map for Application to RhinoCore binding
|
|
static final Map coreMap = new WeakHashMap();
|
|
|
|
// the application we're running in
|
|
public Application app;
|
|
|
|
// The Rhino context
|
|
Context context;
|
|
|
|
// the per-thread global object
|
|
GlobalObject global;
|
|
|
|
// the request evaluator instance owning this rhino engine
|
|
RequestEvaluator reval;
|
|
|
|
// the rhino core
|
|
RhinoCore core;
|
|
|
|
// the global vars set by extensions
|
|
HashMap extensionGlobals;
|
|
|
|
// the thread currently running this engine
|
|
volatile Thread thread;
|
|
|
|
// thread local engine registry
|
|
static ThreadLocal engines = new ThreadLocal();
|
|
|
|
/**
|
|
* Zero argument constructor.
|
|
*/
|
|
public RhinoEngine() {
|
|
// nothing to do
|
|
}
|
|
|
|
/**
|
|
* Init the scripting engine with an application and a request evaluator
|
|
*/
|
|
public synchronized void init(Application app, RequestEvaluator reval) {
|
|
this.app = app;
|
|
this.reval = reval;
|
|
initRhinoCore(app);
|
|
|
|
context = core.contextFactory.enterContext();
|
|
|
|
try {
|
|
extensionGlobals = new HashMap();
|
|
|
|
if (Server.getServer() != null) {
|
|
Vector extVec = Server.getServer().getExtensions();
|
|
|
|
for (int i = 0; i < extVec.size(); i++) {
|
|
HelmaExtension ext = (HelmaExtension) extVec.get(i);
|
|
|
|
try {
|
|
HashMap tmpGlobals = ext.initScripting(app, this);
|
|
|
|
if (tmpGlobals != null) {
|
|
extensionGlobals.putAll(tmpGlobals);
|
|
}
|
|
} catch (ConfigurationException e) {
|
|
app.logError("Couldn't initialize extension " + ext.getName(), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
app.logError("Cannot initialize interpreter", e);
|
|
throw new RuntimeException(e.getMessage(), e);
|
|
} finally {
|
|
Context.exit();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shut down the scripting engine.
|
|
*/
|
|
public void shutdown() {
|
|
core.shutdown();
|
|
}
|
|
|
|
/**
|
|
* Return the RhinoEngine associated with the current thread, or null.
|
|
* @return the RhinoEngine assocated with the current thread
|
|
*/
|
|
public static RhinoEngine getRhinoEngine() {
|
|
return (RhinoEngine) engines.get();
|
|
}
|
|
|
|
/**
|
|
* Initialize the RhinoCore instance for this engine and application.
|
|
* @param app the application we belong to
|
|
*/
|
|
private synchronized void initRhinoCore(Application app) {
|
|
synchronized (coreMap) {
|
|
WeakReference ref = (WeakReference) coreMap.get(app);
|
|
if (ref != null) {
|
|
core = (RhinoCore) ref.get();
|
|
}
|
|
|
|
if (core == null) {
|
|
core = new RhinoCore(app);
|
|
core.initialize();
|
|
coreMap.put(app, new WeakReference(core));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is called before an execution context is entered to let the
|
|
* engine know it should update its prototype information.
|
|
*/
|
|
public synchronized void enterContext() throws IOException {
|
|
// remember the current thread as our thread - we do this here so
|
|
// the thread is already set when the RequestEvaluator calls
|
|
// Application.getDataRoot(), which may result in a function invocation
|
|
// (chicken and egg problem, kind of)
|
|
thread = Thread.currentThread();
|
|
global = new GlobalObject(core, app, true);
|
|
context = core.contextFactory.enterContext();
|
|
|
|
if (core.hasTracer) {
|
|
context.setDebugger(new Tracer(getResponse()), null);
|
|
} else if (useProfiler()) {
|
|
context.setDebugger(new Profiler(), null);
|
|
}
|
|
|
|
// register the engine with the current thread
|
|
engines.set(this);
|
|
// update prototypes
|
|
core.updatePrototypes();
|
|
}
|
|
|
|
/**
|
|
* This method is called when an execution context for a request
|
|
* evaluation is entered. The globals parameter contains the global values
|
|
* to be applied during this execution context.
|
|
*/
|
|
public synchronized void setGlobals(Map globals) throws ScriptingException {
|
|
// remember the current thread as our thread
|
|
thread = Thread.currentThread();
|
|
|
|
// set globals on the global object
|
|
// add globals from extensions
|
|
globals.putAll(extensionGlobals);
|
|
// loop through global vars and set them
|
|
for (Iterator i = globals.keySet().iterator(); i.hasNext();) {
|
|
String k = (String) i.next();
|
|
Object v = globals.get(k);
|
|
Scriptable scriptable;
|
|
|
|
// create a special wrapper for the path object.
|
|
// other objects are wrapped in the default way.
|
|
if (v == null) {
|
|
continue;
|
|
} else if (v instanceof RequestPath) {
|
|
scriptable = new PathWrapper((RequestPath) v, core);
|
|
scriptable.setPrototype(core.pathProto);
|
|
} else {
|
|
scriptable = Context.toObject(v, global);
|
|
}
|
|
|
|
global.put(k, global, scriptable);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is called to let the scripting engine know that the current
|
|
* execution context has terminated.
|
|
*/
|
|
public synchronized void exitContext() {
|
|
if (useProfiler()) {
|
|
try {
|
|
Profiler profiler = (Profiler) Context.getCurrentContext().getDebugger();
|
|
String result = profiler.getResult();
|
|
ResponseTrans res = getResponse();
|
|
if (res != null) {
|
|
getResponse().debug("<pre>" + result + "</pre>");
|
|
}
|
|
app.logEvent("Profiler data for " + getRequest() + ":\n" + result);
|
|
} catch (Exception x) {
|
|
app.logError("Error in profiler: " + x, x);
|
|
}
|
|
}
|
|
// unregister the engine threadlocal
|
|
engines.set(null);
|
|
Context.exit();
|
|
thread = null;
|
|
global = null;
|
|
}
|
|
|
|
/**
|
|
* Invoke a function on some object, using the given arguments and global vars.
|
|
* XML-RPC calls require special input and output parameter conversion.
|
|
*
|
|
* @param thisObject the object to invoke the function on, or null for
|
|
* global functions
|
|
* @param function the function or name of the function to be invoked
|
|
* @param args array of argument objects
|
|
* @param argsWrapMode indicated the way to process the arguments. Must be
|
|
* one of <code>ARGS_WRAP_NONE</code>,
|
|
* <code>ARGS_WRAP_DEFAULT</code>,
|
|
* <code>ARGS_WRAP_XMLRPC</code>
|
|
* @param resolve indicates whether functionName may contain an object path
|
|
* or just the plain function name
|
|
* @return the return value of the function
|
|
* @throws ScriptingException to indicate something went wrong
|
|
* with the invocation
|
|
*/
|
|
public Object invoke(Object thisObject, Object function, Object[] args,
|
|
int argsWrapMode, boolean resolve) throws ScriptingException {
|
|
if (function == null) {
|
|
throw new IllegalArgumentException("Function argument must not be null");
|
|
}
|
|
if (args == null) {
|
|
throw new IllegalArgumentException("Arguments array must not be null");
|
|
}
|
|
try {
|
|
Scriptable obj = thisObject == null ? global : Context.toObject(thisObject, global);
|
|
Function func;
|
|
if (function instanceof String) {
|
|
String funcName = (String) function;
|
|
// if function name should be resolved interpret it as member expression,
|
|
// otherwise replace dots with underscores.
|
|
if (resolve) {
|
|
if (funcName.indexOf('.') > 0) {
|
|
String[] path = StringUtils.split(funcName, ".");
|
|
for (int i = 0; i < path.length - 1; i++) {
|
|
Object propValue = ScriptableObject.getProperty(obj, path[i]);
|
|
if (propValue instanceof Scriptable) {
|
|
obj = (Scriptable) propValue;
|
|
} else {
|
|
throw new RuntimeException("Can't resolve function name " +
|
|
funcName + " in " + thisObject);
|
|
}
|
|
}
|
|
funcName = path[path.length - 1];
|
|
}
|
|
} else {
|
|
funcName = funcName.replace('.', '_');
|
|
}
|
|
Object funcvalue = ScriptableObject.getProperty(obj, funcName);
|
|
|
|
if (!(funcvalue instanceof Function))
|
|
return null;
|
|
func = (Function) funcvalue;
|
|
|
|
} else {
|
|
if (function instanceof Wrapper)
|
|
function = ((Wrapper) function).unwrap();
|
|
if (!(function instanceof Function))
|
|
throw new IllegalArgumentException("Not a function or function name: " + function);
|
|
func = (Function) function;
|
|
}
|
|
|
|
|
|
for (int i = 0; i < args.length; i++) {
|
|
switch (argsWrapMode) {
|
|
case ARGS_WRAP_DEFAULT:
|
|
// convert java objects to JavaScript
|
|
if (args[i] != null) {
|
|
args[i] = Context.javaToJS(args[i], global);
|
|
}
|
|
break;
|
|
case ARGS_WRAP_XMLRPC:
|
|
// XML-RPC requires special argument conversion
|
|
args[i] = core.processXmlRpcArgument(args[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// use Context.call() in order to set the context's factory
|
|
Object retval = Context.call(core.contextFactory, func, global, obj, args);
|
|
|
|
if (retval instanceof Wrapper) {
|
|
retval = ((Wrapper) retval).unwrap();
|
|
}
|
|
|
|
if ((retval == null) || (retval == Undefined.instance)) {
|
|
return null;
|
|
} else if (argsWrapMode == ARGS_WRAP_XMLRPC) {
|
|
return core.processXmlRpcResponse (retval);
|
|
} else {
|
|
return retval;
|
|
}
|
|
} catch (RedirectException redirect) {
|
|
throw redirect;
|
|
} catch (TimeoutException timeout) {
|
|
throw timeout;
|
|
} catch (ConcurrencyException concur) {
|
|
throw concur;
|
|
} catch (Exception x) {
|
|
// has the request timed out? If so, throw TimeoutException
|
|
if (thread != Thread.currentThread()) {
|
|
throw new TimeoutException();
|
|
}
|
|
|
|
if (x instanceof WrappedException) {
|
|
// wrapped java excepiton
|
|
Throwable wrapped = ((WrappedException) x).getWrappedException();
|
|
// rethrow if this is a wrapped concurrency or redirect exception
|
|
if (wrapped instanceof ConcurrencyException) {
|
|
throw (ConcurrencyException) wrapped;
|
|
} else if (wrapped instanceof RedirectException) {
|
|
throw (RedirectException) wrapped;
|
|
}
|
|
}
|
|
// create and throw a ScriptingException with the right message
|
|
String msg = x.getMessage();
|
|
throw new ScriptingException(msg, x);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Let the evaluator know that the current evaluation has been
|
|
* aborted.
|
|
*/
|
|
public void abort() {
|
|
// current request has been aborted.
|
|
Thread t = thread;
|
|
// set thread to null
|
|
thread = null;
|
|
if (t != null && t.isAlive()) {
|
|
t.interrupt();
|
|
try {
|
|
t.join(1000);
|
|
} catch (InterruptedException ir) {
|
|
// interrupted by other thread
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if an object has a function property (public method if it
|
|
* is a java object) with that name.
|
|
*/
|
|
public boolean hasFunction(Object obj, String fname, boolean resolve) {
|
|
if (resolve) {
|
|
if (fname.indexOf('.') > 0) {
|
|
Scriptable op = obj == null ? global : Context.toObject(obj, global);
|
|
String[] path = StringUtils.split(fname, ".");
|
|
for (int i = 0; i < path.length; i++) {
|
|
Object value = ScriptableObject.getProperty(op, path[i]);
|
|
if (value instanceof Scriptable) {
|
|
op = (Scriptable) value;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return (op instanceof Function);
|
|
}
|
|
} else {
|
|
// Convert '.' to '_' in function name
|
|
fname = fname.replace('.', '_');
|
|
}
|
|
|
|
// Treat HopObjects separately - otherwise we risk to fetch database
|
|
// references/child objects just to check for function properties.
|
|
if (obj instanceof INode) {
|
|
String protoname = ((INode) obj).getPrototype();
|
|
if (protoname != null && core.hasFunction(protoname, fname))
|
|
return true;
|
|
}
|
|
|
|
Scriptable op = obj == null ? global : Context.toObject(obj, global);
|
|
return ScriptableObject.getProperty(op, fname) instanceof Callable;
|
|
}
|
|
|
|
/**
|
|
* Check if an object has a value property defined with that name.
|
|
*/
|
|
public boolean hasProperty(Object obj, String propname) {
|
|
if (obj == null || propname == null) {
|
|
return false;
|
|
} else if (obj instanceof Map) {
|
|
return ((Map) obj).containsKey(propname);
|
|
}
|
|
|
|
String prototypeName = app.getPrototypeName(obj);
|
|
|
|
if ("user".equalsIgnoreCase(prototypeName)
|
|
&& "password".equalsIgnoreCase(propname)) {
|
|
return false;
|
|
}
|
|
|
|
// if this is a HopObject, check if the property is defined
|
|
// in the type.properties db-mapping.
|
|
if (obj instanceof INode && ! "hopobject".equalsIgnoreCase(prototypeName)) {
|
|
DbMapping dbm = app.getDbMapping(prototypeName);
|
|
if (dbm != null) {
|
|
Relation rel = dbm.propertyToRelation(propname);
|
|
if (rel != null && (rel.isPrimitive() || rel.isCollection()))
|
|
return true;
|
|
}
|
|
}
|
|
Scriptable wrapped = Context.toObject(obj, global);
|
|
return wrapped.has(propname, wrapped);
|
|
}
|
|
|
|
/**
|
|
* Get a property from the global object.
|
|
* @param propname the property name
|
|
* @return the property value if the property is defined, or null
|
|
*/
|
|
public Object getGlobalProperty(String propname) {
|
|
if (propname == null) {
|
|
return null;
|
|
}
|
|
try {
|
|
Object prop = core.global.get(propname, global);
|
|
if (prop == null
|
|
|| prop == Undefined.instance
|
|
|| prop == ScriptableObject.NOT_FOUND) {
|
|
return null;
|
|
} else if (prop instanceof Wrapper) {
|
|
return ((Wrapper) prop).unwrap();
|
|
} else {
|
|
// Do not return functions as properties as this
|
|
// is a potential security problem
|
|
return (prop instanceof Function) ? null : prop;
|
|
}
|
|
} catch (Exception esx) {
|
|
app.logError("Error getting global property " + propname + ": " + esx);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if an object has a defined property (public field if it
|
|
* is a java object) with that name.
|
|
*/
|
|
public Object getProperty(Object obj, String propname) {
|
|
if (obj == null || propname == null) {
|
|
return null;
|
|
} else if (obj instanceof Map) {
|
|
Object prop = ((Map) obj).get(propname);
|
|
// Do not return functions as properties as this
|
|
// is a potential security problem
|
|
return (prop instanceof Function) ? null : prop;
|
|
}
|
|
|
|
// use Rhino wrappers and methods to get property
|
|
Scriptable so = Context.toObject(obj, global);
|
|
|
|
try {
|
|
Object prop = so.get(propname, so);
|
|
|
|
if (prop == null
|
|
|| prop == Undefined.instance
|
|
|| prop == ScriptableObject.NOT_FOUND) {
|
|
return null;
|
|
} else if (prop instanceof Wrapper) {
|
|
return ((Wrapper) prop).unwrap();
|
|
} else {
|
|
// Do not return functions as properties as this
|
|
// is a potential security problem
|
|
return (prop instanceof Function) ? null : prop;
|
|
}
|
|
} catch (Exception esx) {
|
|
app.logError("Error getting property " + propname + ": " + esx);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine if the given object is mapped to a type of the scripting engine
|
|
* @param obj an object
|
|
* @return true if the object is mapped to a type
|
|
*/
|
|
public boolean isTypedObject(Object obj) {
|
|
if (obj instanceof Wrapper)
|
|
obj = ((Wrapper) obj).unwrap();
|
|
if (obj == null || obj instanceof Map || obj instanceof NativeObject)
|
|
return false;
|
|
if (obj instanceof IPathElement) {
|
|
String protoName = ((IPathElement) obj).getPrototype();
|
|
return protoName != null && !"hopobject".equalsIgnoreCase(protoName);
|
|
}
|
|
// assume java object is typed
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return a string representation for the given object
|
|
* @param obj an object
|
|
* @return a string representing the object
|
|
*/
|
|
public String toString(Object obj) {
|
|
// not all Rhino types convert to a string as expected
|
|
// when calling toString() - try to do better by using
|
|
// Rhino's ScriptRuntime.toString(). Note that this
|
|
// assumes that people always use this method to get
|
|
// a string representation of the object - which is
|
|
// currently the case since it's only used in Skin rendering.
|
|
try {
|
|
return ScriptRuntime.toString(obj);
|
|
} catch (Exception x) {
|
|
// just return original property object
|
|
}
|
|
return obj.toString();
|
|
}
|
|
|
|
/**
|
|
* Provide object serialization for this engine's scripted objects. If no special
|
|
* provisions are required, this method should just wrap the stream with an
|
|
* ObjectOutputStream and write the object.
|
|
*
|
|
* @param obj the object to serialize
|
|
* @param out the stream to write to
|
|
* @throws java.io.IOException
|
|
*/
|
|
public void serialize(Object obj, OutputStream out) throws IOException {
|
|
core.contextFactory.enterContext();
|
|
engines.set(this);
|
|
try {
|
|
// use a special ScriptableOutputStream that unwraps Wrappers
|
|
ScriptableOutputStream sout = new ScriptableOutputStream(out, core.global) {
|
|
protected Object replaceObject(Object obj) throws IOException {
|
|
if (obj instanceof HopObject)
|
|
return new HopObjectProxy((HopObject) obj);
|
|
if (obj instanceof Node)
|
|
return new HopObjectProxy((Node) obj);
|
|
if (obj instanceof GlobalObject)
|
|
return new GlobalProxy((GlobalObject) obj);
|
|
if (obj instanceof ApplicationBean)
|
|
return new ScriptBeanProxy("app");
|
|
if (obj instanceof Application)
|
|
return new ApplicationProxy();
|
|
if (obj instanceof RequestBean)
|
|
return new ScriptBeanProxy("req");
|
|
if (obj instanceof ResponseBean)
|
|
return new ScriptBeanProxy("res");
|
|
if (obj instanceof PathWrapper)
|
|
return new ScriptBeanProxy("path");
|
|
return super.replaceObject(obj);
|
|
}
|
|
};
|
|
// sout.addExcludedName("Xml");
|
|
// sout.addExcludedName("global");
|
|
|
|
sout.writeObject(obj);
|
|
sout.flush();
|
|
} finally {
|
|
Context.exit();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provide object deserialization for this engine's scripted objects. If no special
|
|
* provisions are required, this method should just wrap the stream with an
|
|
* ObjectIntputStream and read the object.
|
|
*
|
|
* @param in the stream to read from
|
|
* @return the deserialized object
|
|
* @throws java.io.IOException
|
|
*/
|
|
public Object deserialize(InputStream in) throws IOException, ClassNotFoundException {
|
|
core.contextFactory.enterContext();
|
|
engines.set(this);
|
|
try {
|
|
ObjectInputStream sin = new ScriptableInputStream(in, core.global) {
|
|
protected Object resolveObject(Object obj) throws IOException {
|
|
if (obj instanceof SerializationProxy) {
|
|
return ((SerializationProxy) obj).getObject(RhinoEngine.this);
|
|
}
|
|
return super.resolveObject(obj);
|
|
}
|
|
};
|
|
return sin.readObject();
|
|
} finally {
|
|
Context.exit();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a code resource to a given prototype by immediately compiling and evaluating it.
|
|
*
|
|
* @param typename the type this resource belongs to
|
|
* @param resource a code resource
|
|
*/
|
|
public void injectCodeResource(String typename, Resource resource) {
|
|
// we activate recording on thread scope to make it forward
|
|
// property puts to the shared scope (bug 504)
|
|
if (global != null)
|
|
global.startRecording();
|
|
try {
|
|
core.injectCodeResource(typename, resource);
|
|
} finally {
|
|
if (global != null)
|
|
global.stopRecording();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the application we're running in
|
|
*/
|
|
public Application getApplication() {
|
|
return app;
|
|
}
|
|
|
|
/**
|
|
* Return the RequestEvaluator owningthis rhino engine.
|
|
*/
|
|
public RequestEvaluator getRequestEvaluator() {
|
|
return reval;
|
|
}
|
|
|
|
/**
|
|
* Return the Response object of the current evaluation context.
|
|
* Proxy method to RequestEvaluator.
|
|
*/
|
|
public ResponseTrans getResponse() {
|
|
return reval.getResponse();
|
|
}
|
|
|
|
/**
|
|
* Return the Request object of the current evaluation context.
|
|
* Proxy method to RequestEvaluator.
|
|
*/
|
|
public RequestTrans getRequest() {
|
|
return reval.getRequest();
|
|
}
|
|
|
|
/**
|
|
* Return the RhinoCore object for the application this engine belongs to.
|
|
*
|
|
* @return this engine's RhinoCore instance
|
|
*/
|
|
public RhinoCore getCore() {
|
|
return core;
|
|
}
|
|
|
|
/**
|
|
* Try to get a skin from the parameter object.
|
|
*/
|
|
public Skin toSkin(Object skinobj, String protoName) throws IOException {
|
|
if (skinobj == null) {
|
|
return null;
|
|
} else if (skinobj instanceof Wrapper) {
|
|
skinobj = ((Wrapper) skinobj).unwrap();
|
|
}
|
|
|
|
if (skinobj instanceof Skin) {
|
|
return (Skin) skinobj;
|
|
} else {
|
|
return getSkin(protoName, skinobj.toString());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a skin for the given prototype and skin name. This method considers the
|
|
* skinpath set in the current response object and does per-response skin
|
|
* caching.
|
|
*/
|
|
public Skin getSkin(String protoName, String skinName) throws IOException {
|
|
Skin skin;
|
|
ResponseTrans res = getResponse();
|
|
if (skinName.startsWith("#")) {
|
|
// evaluate relative subskin name against currently rendering skin
|
|
skin = res.getActiveSkin();
|
|
return skin == null ?
|
|
null : skin.getSubskin(skinName.substring(1));
|
|
}
|
|
|
|
SkinKey key = new SkinKey(protoName, skinName);
|
|
skin = res.getCachedSkin(key);
|
|
|
|
if (skin == null) {
|
|
// retrieve res.skinpath, an array of objects that tell us where to look for skins
|
|
// (strings for directory names and INodes for internal, db-stored skinsets)
|
|
Object[] skinpath = res.getSkinpath();
|
|
RhinoCore.unwrapSkinpath(skinpath);
|
|
skin = app.getSkin(protoName, skinName, skinpath);
|
|
res.cacheSkin(key, skin);
|
|
}
|
|
return skin;
|
|
}
|
|
|
|
/**
|
|
* Determine if we should use a profiler on the current thread. This returns true if
|
|
* the rhino.profile app property is set to true (requires restart) and the
|
|
* rhino.profile.session property is either unset, or set to "all", or matching
|
|
* the session id of the current request.
|
|
* @return true if the current request should be profiled
|
|
*/
|
|
private boolean useProfiler() {
|
|
if (!core.hasProfiler) {
|
|
return false;
|
|
}
|
|
String profilerSession = app.getProperty("rhino.profile.session");
|
|
if (profilerSession == null || "all".equalsIgnoreCase(profilerSession)) {
|
|
return true;
|
|
}
|
|
RequestTrans req = getRequest();
|
|
return req != null && req.getSession() != null
|
|
&& req.getSession().indexOf(profilerSession) == 0;
|
|
}
|
|
|
|
}
|