* Remove synchronization problems in request timeout handling

* Implement skin parameter handling via res.handlers.param
* Implement onResponse() callback
* Slim down renderSkin method implementation
* Implement res.buffer to access current page output
* Implement res.stop() to stop request execution
* Do not reuse per thread scope, create a new one for each request
This commit is contained in:
hns 2007-01-29 16:00:44 +00:00
parent e6c287b35f
commit 21838fe606
10 changed files with 236 additions and 290 deletions

View file

@ -29,7 +29,7 @@ public class RedirectException extends Error {
/**
* Creates a new RedirectException object.
*
* @param url ...
* @param url the URL
*/
public RedirectException(String url) {
super("Redirection Request to " + url);
@ -37,29 +37,11 @@ public class RedirectException extends Error {
}
/**
*
*
* @return ...
* Return the URL to redirect to.
* @return the URL
*/
public String getMessage() {
public String getUrl() {
return url;
}
/**
*
*
* @param s ...
*/
public void printStackTrace(java.io.PrintStream s) {
// do nothing
}
/**
*
*
* @param w ...
*/
public void printStackTrace(java.io.PrintWriter w) {
// do nothing
}
}

View file

@ -58,6 +58,16 @@ public class ResponseBean implements Serializable {
res.encodeXml(obj);
}
/**
* Write an object to the response buffer by converting it to a string
* and then encoding it for form/text area content use.
*
* @param obj the object to write to the response buffer
*/
public void encodeForm(Object obj) {
res.encodeForm(obj);
}
/**
* Write an object to the response buffer by converting it to a string
* and then HTML-formatting it.
@ -88,6 +98,15 @@ public class ResponseBean implements Serializable {
res.forward(url);
}
/**
* Immediately stop processing the current request
*
* @throws RedirectException to immediately terminate the request
*/
public void stop() throws RedirectException {
res.redirect(null);
}
/**
* Reset the response object, clearing all content previously written to it
*/
@ -481,6 +500,15 @@ public class ResponseBean implements Serializable {
return res.popStringBuffer();
}
/**
* Returns the current response buffer as string.
*
* @return the response buffer as string
*/
public String getBuffer() {
return res.getBuffer().toString();
}
/**
* Commit changes made during the course of the current transaction
* and start a new one

View file

@ -387,12 +387,24 @@ public final class RequestEvaluator implements Runnable {
ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
}
// try calling onResponse() function on object before
// calling the actual action
scriptingEngine.invoke(currentElement,
"onResponse",
EMPTY_ARGS,
ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
} catch (RedirectException redirect) {
// if there is a message set, save it on the user object for the next request
session.storeResponseMessages(res);
if (res.getRedirect() != null)
session.storeResponseMessages(res);
}
// check if we're still the one and only or if the waiting thread has given up on us already
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
commitTransaction();
done = true;
@ -438,8 +450,14 @@ public final class RequestEvaluator implements Runnable {
functionName, args,
ScriptingEngine.ARGS_WRAP_XMLRPC,
false);
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
commitTransaction();
} catch (Exception x) {
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
abortTransaction();
app.logError(txname + ": " + error, x);
@ -466,8 +484,14 @@ public final class RequestEvaluator implements Runnable {
args,
ScriptingEngine.ARGS_WRAP_DEFAULT,
true);
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
commitTransaction();
} catch (Exception x) {
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
abortTransaction();
app.logError(txname + ": " + error, x);
@ -487,6 +511,9 @@ public final class RequestEvaluator implements Runnable {
} catch (AbortException x) {
// res.abort() just aborts the transaction and
// leaves the response untouched
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
abortTransaction();
done = true;
} catch (ConcurrencyException x) {
@ -494,6 +521,9 @@ public final class RequestEvaluator implements Runnable {
if (++tries < 8) {
// try again after waiting some period
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
abortTransaction();
try {
@ -508,6 +538,9 @@ public final class RequestEvaluator implements Runnable {
rtx = null;
}
} else {
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
abortTransaction();
if (error == null)
@ -519,6 +552,9 @@ public final class RequestEvaluator implements Runnable {
}
} catch (Throwable x) {
String txname = localrtx.getTransactionName();
// check if request is still valid, or if the requesting thread has stopped waiting already
if (localrtx != rtx)
return;
abortTransaction();
// If the transactor thread has been killed by the invoker thread we don't have to
@ -581,6 +617,7 @@ public final class RequestEvaluator implements Runnable {
/**
* Called by the transactor thread when it has successfully fulfilled a request.
* @throws Exception transaction couldn't be committed
*/
synchronized void commitTransaction() throws Exception {
Transactor localrtx = (Transactor) Thread.currentThread();
@ -592,11 +629,17 @@ public final class RequestEvaluator implements Runnable {
}
}
/**
* Called by the transactor thread when the request didn't terminate successfully.
*/
synchronized void abortTransaction() {
Transactor localrtx = (Transactor) Thread.currentThread();
localrtx.abort();
}
/**
* Initialize and start the transactor thread.
*/
private synchronized void startTransactor() {
if (!app.isRunning()) {
throw new ApplicationStoppedException();
@ -653,11 +696,10 @@ public final class RequestEvaluator implements Runnable {
* waiting thread when it times out and stops waiting, or from an outside
* thread. If currently active kill the request, otherwise just notify.
*/
public synchronized void stopTransactor() {
synchronized boolean stopTransactor() {
Transactor t = rtx;
rtx = null;
boolean stopped = false;
if (t != null && t.isActive()) {
// let the scripting engine know that the
// current transaction is being aborted.
@ -665,15 +707,17 @@ public final class RequestEvaluator implements Runnable {
scriptingEngine.abort();
}
app.logEvent("Killing Thread " + t);
app.logEvent("Request timeout for thread " + t);
reqtype = NONE;
t.kill();
t.abort();
t.closeConnections();
stopped = true;
}
notifyAll();
return stopped;
}
/**
@ -694,9 +738,7 @@ public final class RequestEvaluator implements Runnable {
startTransactor();
wait(app.requestTimeout);
if (reqtype != NONE) {
app.logEvent("Stopping Thread for Request " + app.getName() + "/" + req.getPath());
stopTransactor();
if (reqtype != NONE && stopTransactor()) {
res.reset();
res.reportError(app.getName(), "Request timed out");
}
@ -749,8 +791,7 @@ public final class RequestEvaluator implements Runnable {
startTransactor();
wait(app.requestTimeout);
if (reqtype != NONE) {
stopTransactor();
if (reqtype != NONE && stopTransactor()) {
exception = new RuntimeException("Request timed out");
}
@ -784,8 +825,7 @@ public final class RequestEvaluator implements Runnable {
startTransactor();
wait();
if (reqtype != NONE) {
stopTransactor();
if (reqtype != NONE && stopTransactor()) {
exception = new RuntimeException("Request timed out");
}
@ -853,8 +893,7 @@ public final class RequestEvaluator implements Runnable {
startTransactor();
wait(timeout);
if (reqtype != NONE) {
stopTransactor();
if (reqtype != NONE && stopTransactor()) {
exception = new RuntimeException("Request timed out");
}

View file

@ -144,23 +144,46 @@ public final class Skin {
return new String(source, 0, sourceLength);
}
/**
* Render this skin and return it as string
*/
public String renderAsString(RequestEvaluator reval, Object thisObject, Object paramObject)
throws RedirectException, UnsupportedEncodingException {
String result = "";
ResponseTrans res = reval.getResponse();
res.pushStringBuffer();
try {
render(reval, thisObject, paramObject);
} finally {
result = res.popStringBuffer();
}
return result;
}
/**
* Render this skin
*/
public void render(RequestEvaluator reval, Object thisObject, Map paramObject)
public void render(RequestEvaluator reval, Object thisObject, Object paramObject)
throws RedirectException, UnsupportedEncodingException {
// check for endless skin recursion
if (++reval.skinDepth > 50) {
throw new RuntimeException("Recursive skin invocation suspected");
}
ResponseTrans res = reval.getResponse();
if (macros == null) {
reval.getResponse().write(source, 0, sourceLength);
res.write(source, 0, sourceLength);
reval.skinDepth--;
return;
}
// register param object, remember previous one to reset afterwards
Map handlers = res.getMacroHandlers();
Object previousParam = handlers.put("param", paramObject);
try {
int written = 0;
Map handlerCache = null;
@ -171,18 +194,23 @@ public final class Skin {
for (int i = 0; i < macros.length; i++) {
if (macros[i].start > written) {
reval.getResponse().write(source, written, macros[i].start - written);
res.write(source, written, macros[i].start - written);
}
macros[i].render(reval, thisObject, paramObject, handlerCache);
macros[i].render(reval, thisObject, handlerCache);
written = macros[i].end;
}
if (written < sourceLength) {
reval.getResponse().write(source, written, sourceLength - written);
res.write(source, written, sourceLength - written);
}
} finally {
reval.skinDepth--;
if (previousParam == null) {
handlers.remove("param");
} else {
handlers.put("param", previousParam);
}
}
}
@ -395,8 +423,7 @@ public final class Skin {
/**
* Render the macro given a handler object
*/
public void render(RequestEvaluator reval, Object thisObject, Map paramObject,
Map handlerCache)
public void render(RequestEvaluator reval, Object thisObject, Map handlerCache)
throws RedirectException, UnsupportedEncodingException {
// immediately return for comment macros
if (isCommentMacro) {
@ -414,10 +441,6 @@ public final class Skin {
} else if ("request".equalsIgnoreCase(handler)) {
renderFromRequest(reval);
return;
} else if ("param".equalsIgnoreCase(handler)) {
renderFromParam(reval, paramObject);
return;
} else if ("session".equalsIgnoreCase(handler)) {
renderFromSession(reval);

View file

@ -161,19 +161,17 @@ public class Transactor extends Thread {
/**
*
*
* @return ...
* Returns true if a transaction is currently active.
* @return true if currently a transaction is active
*/
public boolean isActive() {
return active;
}
/**
*
*
* @param src ...
* @param con ...
* Register a db connection with this transactor thread.
* @param src the db source
* @param con the connection
*/
public void registerConnection(DbSource src, Connection con) {
sqlConnections.put(src, con);
@ -182,11 +180,9 @@ public class Transactor extends Thread {
}
/**
*
*
* @param src ...
*
* @return ...
* Get a db connection that was previously registered with this transactor thread.
* @param src the db source
* @return the connection
*/
public Connection getConnection(DbSource src) {
Connection con = (Connection) sqlConnections.get(src);
@ -218,9 +214,7 @@ public class Transactor extends Thread {
public synchronized void begin(String name) throws Exception {
if (killed) {
throw new DatabaseException("Transaction started on killed thread");
}
if (active) {
} else if (active) {
abort();
}
@ -241,10 +235,10 @@ public class Transactor extends Thread {
*/
public synchronized void commit() throws Exception {
if (killed) {
abort();
throw new DatabaseException("commit() called on killed transactor thread");
} else if (!active) {
return;
}
int inserted = 0;
int updated = 0;
int deleted = 0;
@ -396,7 +390,6 @@ public class Transactor extends Thread {
// clear the node collections
recycle();
// close any JDBC connections associated with this transactor thread
closeConnections();
@ -420,34 +413,39 @@ public class Transactor extends Thread {
* Kill this transaction thread. Used as last measure only.
*/
public synchronized void kill() {
killed = true;
// The thread is told to stop by setting the thread flag in the EcmaScript
// evaluator, so we can hope that it stops without doing anything else.
try {
join(500);
} catch (InterruptedException ir) {
// interrupted by other thread
}
killed = true;
interrupt();
// Interrupt the thread if it has not noticed the flag (e.g. because it is busy
// reading from a network socket).
if (isAlive()) {
interrupt();
interrupt();
try {
join(1000);
} catch (InterruptedException ir) {
// interrupted by other thread
}
}
try {
join(1000);
} catch (InterruptedException ir) {
// interrupted by other thread
}
if (isAlive() && "true".equals(nmgr.app.getProperty("requestTimeoutStop", "true"))) {
// still running - check if we ought to stop() it
try {
Thread.sleep(2000);
if (isAlive()) {
// thread is still running, pull emergency break
nmgr.app.logEvent("Stopping Thread for Transactor " + this);
stop();
}
} catch (InterruptedException ir) {
// interrupted by other thread
}
}
}
/**
*
* Closes all open JDBC connections
*/
public void closeConnections() {
// nmgr.app.logEvent("Cleaning up Transactor thread");
if (sqlConnections != null) {
for (Iterator i = sqlConnections.values().iterator(); i.hasNext();) {
try {

View file

@ -39,7 +39,7 @@ import java.io.*;
public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
Application app;
RhinoCore core;
GlobalObject sharedGlobal = null;
boolean isThreadScope = false;
// fields to implement PropertyRecorder
private volatile boolean isRecording = false;
@ -51,12 +51,12 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
* @param core ...
* @param app ...
*/
public GlobalObject(RhinoCore core, Application app, boolean perThread) {
public GlobalObject(RhinoCore core, Application app, boolean isThreadScope) {
this.core = core;
this.app = app;
if (perThread) {
sharedGlobal = core.global;
setPrototype(sharedGlobal);
this.isThreadScope = isThreadScope;
if (isThreadScope) {
setPrototype(core.global);
setParentScope(null);
}
}
@ -64,10 +64,8 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
/**
* Initializes the global object. This is only done for the shared
* global objects not the per-thread ones.
*
* @throws PropertyException ...
*/
public void init() throws PropertyException {
public void init() {
String[] globalFuncs = {
"renderSkin", "renderSkinAsString", "getProperty",
"authenticate", "createSkin", "format", "encode",
@ -127,18 +125,10 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
if (isRecording) {
changedProperties.add(name);
}
// check if this is a per-thread scope instance
if (sharedGlobal != null) {
// make thread scope accessible as "global"
if ("global".equals(name)) {
return this;
}
// use synchronized get on fast changing per-thread scopes just to be sure
synchronized(this) {
return super.get(name, start);
}
// expose thread scope as global variable "global"
if (isThreadScope && "global".equals(name)) {
return this;
}
// we're the shared scope
return super.get(name, start);
}
@ -153,22 +143,11 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
public boolean renderSkin(Object skinobj, Object paramobj)
throws UnsupportedEncodingException, IOException {
RhinoEngine engine = RhinoEngine.getRhinoEngine();
Skin skin;
if (skinobj instanceof Wrapper) {
skinobj = ((Wrapper) skinobj).unwrap();
}
if (skinobj instanceof Skin) {
skin = (Skin) skinobj;
} else {
skin = engine.getSkin("global", skinobj.toString());
}
Map param = RhinoCore.getSkinParam(paramobj);
Skin skin = engine.toSkin(skinobj, "global");
if (skin != null) {
skin.render(engine.reval, null, param);
skin.render(engine.reval, null,
(paramobj == Undefined.instance) ? null : paramobj);
}
return true;
@ -185,25 +164,11 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
public String renderSkinAsString(Object skinobj, Object paramobj)
throws UnsupportedEncodingException, IOException {
RhinoEngine engine = RhinoEngine.getRhinoEngine();
Skin skin;
if (skinobj instanceof Wrapper) {
skinobj = ((Wrapper) skinobj).unwrap();
}
if (skinobj instanceof Skin) {
skin = (Skin) skinobj;
} else {
skin = engine.getSkin("global", skinobj.toString());
}
Map param = RhinoCore.getSkinParam(paramobj);
Skin skin = engine.toSkin(skinobj, "global");
if (skin != null) {
ResponseTrans res = engine.getResponse();
res.pushStringBuffer();
skin.render(engine.reval, null, param);
return res.popStringBuffer();
return skin.renderAsString(engine.reval, null,
(paramobj == Undefined.instance) ? null : paramobj);
}
return "";
@ -671,7 +636,9 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
}
String str = (String) args[i];
if (obj.has(str, obj)) {
obj.setAttributes(str, obj.getAttributes(str) | DONTENUM);
int attr = obj.getAttributes(str);
if ((attr & PERMANENT) == 0)
obj.setAttributes(str, attr | DONTENUM);
}
}
return null;
@ -719,6 +686,6 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
}
public String toString() {
return sharedGlobal == null ? "[Shared Scope]" : "[Thread Scope]";
return isThreadScope ? "[Thread Scope]" : "[Shared Scope]";
}
}

View file

@ -197,24 +197,13 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
public boolean jsFunction_renderSkin(Object skinobj, Object paramobj)
throws UnsupportedEncodingException, IOException {
RhinoEngine engine = RhinoEngine.getRhinoEngine();
Skin skin;
if (skinobj instanceof Wrapper) {
skinobj = ((Wrapper) skinobj).unwrap();
}
if (skinobj instanceof Skin) {
skin = (Skin) skinobj;
} else {
skin = engine.getSkin(className, skinobj.toString());
}
Map param = RhinoCore.getSkinParam(paramobj);
Skin skin = engine.toSkin(skinobj, className);
checkNode();
if (skin != null) {
skin.render(engine.reval, node, param);
skin.render(engine.reval, node,
(paramobj == Undefined.instance) ? null : paramobj);
}
return true;
@ -278,27 +267,13 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
public String jsFunction_renderSkinAsString(Object skinobj, Object paramobj)
throws UnsupportedEncodingException, IOException {
RhinoEngine engine = RhinoEngine.getRhinoEngine();
Skin skin;
if (skinobj instanceof Wrapper) {
skinobj = ((Wrapper) skinobj).unwrap();
}
if (skinobj instanceof Skin) {
skin = (Skin) skinobj;
} else {
skin = engine.getSkin(className, skinobj.toString());
}
Map param = RhinoCore.getSkinParam(paramobj);
Skin skin = engine.toSkin(skinobj, className);
Object param = paramobj == Undefined.instance ? null : paramobj;
checkNode();
if (skin != null) {
ResponseTrans res = engine.getResponse();
res.pushStringBuffer();
skin.render(engine.reval, node, param);
return res.popStringBuffer();
return skin.renderAsString(engine.reval, node,
(paramobj == Undefined.instance) ? null : paramobj);
}
return "";

View file

@ -78,22 +78,11 @@ public class JavaObject extends NativeJavaObject {
public boolean renderSkin(Object skinobj, Object paramobj)
throws UnsupportedEncodingException, IOException {
RhinoEngine engine = RhinoEngine.getRhinoEngine();
Skin skin;
if (skinobj instanceof Wrapper) {
skinobj = ((Wrapper) skinobj).unwrap();
}
if (skinobj instanceof Skin) {
skin = (Skin) skinobj;
} else {
skin = engine.getSkin(protoName, skinobj.toString());
}
Map param = RhinoCore.getSkinParam(paramobj);
Skin skin = engine.toSkin(skinobj, protoName);
if (skin != null) {
skin.render(engine.reval, javaObject, param);
skin.render(engine.reval, javaObject,
(paramobj == Undefined.instance) ? null : paramobj);
}
return true;
@ -110,25 +99,11 @@ public class JavaObject extends NativeJavaObject {
public String renderSkinAsString(Object skinobj, Object paramobj)
throws UnsupportedEncodingException, IOException {
RhinoEngine engine = RhinoEngine.getRhinoEngine();
Skin skin;
if (skinobj instanceof Wrapper) {
skinobj = ((Wrapper) skinobj).unwrap();
}
if (skinobj instanceof Skin) {
skin = (Skin) skinobj;
} else {
skin = engine.getSkin(protoName, skinobj.toString());
}
Map param = RhinoCore.getSkinParam(paramobj);
Skin skin = engine.toSkin(skinobj, protoName);
if (skin != null) {
ResponseTrans res = engine.getResponse();
res.pushStringBuffer();
skin.render(engine.reval, javaObject, param);
return res.popStringBuffer();
return skin.renderAsString(engine.reval, javaObject,
(paramobj == Undefined.instance) ? null : paramobj);
}
return "";

View file

@ -635,7 +635,7 @@ public final class RhinoCore implements ScopeProvider {
return hobj;
}
protected String postProcessHref(Object obj, String protoName, String basicHref)
protected String postProcessHref(Object obj, String protoName, String href)
throws UnsupportedEncodingException, IOException {
// check if the app.properties specify a href-function to post-process the
// basic href.
@ -655,7 +655,7 @@ public final class RhinoCore implements ScopeProvider {
try {
result = eng.invoke(handler, hrefFunction,
new Object[] { basicHref },
new Object[] {href},
ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
} catch (ScriptingException x) {
@ -667,7 +667,7 @@ public final class RhinoCore implements ScopeProvider {
" returned null");
}
basicHref = result.toString();
href = result.toString();
break;
}
handler = app.getParentElement(handler);
@ -696,23 +696,17 @@ public final class RhinoCore implements ScopeProvider {
}
if (skin != null) {
Scriptable param = Context.getCurrentContext().newObject(global);
param.put("path", param, href);
href = skin.renderAsString(eng.getRequestEvaluator(), handler, param);
break;
}
handler = app.getParentElement(handler);
}
if (skin != null) {
eng.getResponse().pushStringBuffer();
HashMap param = new HashMap();
param.put("path", basicHref);
skin.render(eng.getRequestEvaluator(), handler, param);
basicHref = eng.getResponse().popStringBuffer().trim();
}
}
return basicHref;
return href;
}
/**
@ -741,30 +735,6 @@ public final class RhinoCore implements ScopeProvider {
return skinpath;
}
protected static Map getSkinParam(Object paramobj) {
Map param = null;
if ((paramobj != null) && (paramobj != Undefined.instance)) {
param = new HashMap();
if (paramobj instanceof Scriptable) {
Scriptable sp = (Scriptable) paramobj;
Object[] ids = sp.getIds();
for (int i = 0; i < ids.length; i++) {
Object obj = sp.get(ids[i].toString(), sp);
if (obj instanceof Wrapper) {
param.put(ids[i], ((Wrapper) obj).unwrap());
} else if (obj != Undefined.instance) {
param.put(ids[i], obj);
}
}
}
}
return param;
}
/**
* Add a code resource to a given prototype by immediately compiling and evaluating it.
*

View file

@ -58,15 +58,11 @@ public class RhinoEngine implements ScriptingEngine {
// the rhino core
RhinoCore core;
// remember global variables from last invokation to be able to
// do lazy cleanup
Map lastGlobals = null;
// the global vars set by extensions
HashMap extensionGlobals;
// the thread currently running this engine
Thread thread;
volatile Thread thread;
// thread local engine registry
static ThreadLocal engines = new ThreadLocal();
@ -92,8 +88,6 @@ public class RhinoEngine implements ScriptingEngine {
context = core.contextFactory.enter();
try {
global = new GlobalObject(core, app, true);
extensionGlobals = new HashMap();
if (Server.getServer() != null) {
@ -153,13 +147,13 @@ public class RhinoEngine implements ScriptingEngine {
* This method is called before an execution context is entered to let the
* engine know it should update its prototype information.
*/
public void updatePrototypes() throws IOException {
public synchronized void updatePrototypes() 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.enter();
if (core.hasTracer) {
@ -182,30 +176,26 @@ public class RhinoEngine implements ScriptingEngine {
thread = Thread.currentThread();
// set globals on the global object
if (globals != lastGlobals) {
// 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;
// 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);
// 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);
}
// remember the globals set on this evaluator
lastGlobals = globals;
global.put(k, global, scriptable);
}
}
@ -218,19 +208,7 @@ public class RhinoEngine implements ScriptingEngine {
engines.set(null);
core.contextFactory.exit();
thread = null;
// loop through previous globals and unset them, if necessary.
if (lastGlobals != null) {
for (Iterator i = lastGlobals.keySet().iterator(); i.hasNext();) {
String g = (String) i.next();
try {
global.delete(g);
} catch (Exception x) {
app.logEvent("Error unsetting global property: " + g);
}
}
lastGlobals = null;
}
global = null;
}
/**
@ -342,21 +320,17 @@ public class RhinoEngine implements ScriptingEngine {
* Let the evaluator know that the current evaluation has been
* aborted.
*/
public synchronized void abort() {
public void abort() {
// current request has been aborted.
Thread t = thread;
// set thread to null
thread = null;
if (t != null && t.isAlive()) {
t.interrupt();
if ("true".equals(app.getProperty("requestTimeoutStop", "true"))) {
try {
Thread.sleep(5000);
if (t.isAlive()) {
// thread is still running, gotta stop it.
t.stop();
}
} catch (InterruptedException i) {
// interrupted, ignore
}
try {
t.join(1000);
} catch (InterruptedException ir) {
// interrupted by other thread
}
}
}
@ -552,6 +526,21 @@ public class RhinoEngine implements ScriptingEngine {
return core;
}
/**
* Try to get a skin from the parameter object.
*/
public Skin toSkin(Object skinobj, String protoName) throws IOException {
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