From 21838fe6066411a13866e06afec02995fef52cf1 Mon Sep 17 00:00:00 2001 From: hns Date: Mon, 29 Jan 2007 16:00:44 +0000 Subject: [PATCH] * 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 --- src/helma/framework/RedirectException.java | 26 +---- src/helma/framework/ResponseBean.java | 28 ++++++ .../framework/core/RequestEvaluator.java | 69 ++++++++++--- src/helma/framework/core/Skin.java | 45 ++++++--- src/helma/objectmodel/db/Transactor.java | 68 +++++++------ src/helma/scripting/rhino/GlobalObject.java | 71 ++++--------- src/helma/scripting/rhino/HopObject.java | 39 ++------ src/helma/scripting/rhino/JavaObject.java | 37 ++----- src/helma/scripting/rhino/RhinoCore.java | 44 ++------- src/helma/scripting/rhino/RhinoEngine.java | 99 +++++++++---------- 10 files changed, 236 insertions(+), 290 deletions(-) diff --git a/src/helma/framework/RedirectException.java b/src/helma/framework/RedirectException.java index f81fcee2..716eeba5 100644 --- a/src/helma/framework/RedirectException.java +++ b/src/helma/framework/RedirectException.java @@ -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 - } } diff --git a/src/helma/framework/ResponseBean.java b/src/helma/framework/ResponseBean.java index 6e80b068..9a9c4b2a 100644 --- a/src/helma/framework/ResponseBean.java +++ b/src/helma/framework/ResponseBean.java @@ -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 diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index d9c2c44b..fe9475fc 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -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"); } diff --git a/src/helma/framework/core/Skin.java b/src/helma/framework/core/Skin.java index 259335cf..cf5abdf3 100644 --- a/src/helma/framework/core/Skin.java +++ b/src/helma/framework/core/Skin.java @@ -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); diff --git a/src/helma/objectmodel/db/Transactor.java b/src/helma/objectmodel/db/Transactor.java index d119b5b0..49e0107f 100644 --- a/src/helma/objectmodel/db/Transactor.java +++ b/src/helma/objectmodel/db/Transactor.java @@ -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 { diff --git a/src/helma/scripting/rhino/GlobalObject.java b/src/helma/scripting/rhino/GlobalObject.java index 5b410ab7..a618eceb 100644 --- a/src/helma/scripting/rhino/GlobalObject.java +++ b/src/helma/scripting/rhino/GlobalObject.java @@ -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]"; } } diff --git a/src/helma/scripting/rhino/HopObject.java b/src/helma/scripting/rhino/HopObject.java index 3e8f3fc8..6254e74d 100644 --- a/src/helma/scripting/rhino/HopObject.java +++ b/src/helma/scripting/rhino/HopObject.java @@ -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 ""; diff --git a/src/helma/scripting/rhino/JavaObject.java b/src/helma/scripting/rhino/JavaObject.java index 6b15e1b3..e3f0234b 100644 --- a/src/helma/scripting/rhino/JavaObject.java +++ b/src/helma/scripting/rhino/JavaObject.java @@ -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 ""; diff --git a/src/helma/scripting/rhino/RhinoCore.java b/src/helma/scripting/rhino/RhinoCore.java index 171f985c..b9df07ec 100644 --- a/src/helma/scripting/rhino/RhinoCore.java +++ b/src/helma/scripting/rhino/RhinoCore.java @@ -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. * diff --git a/src/helma/scripting/rhino/RhinoEngine.java b/src/helma/scripting/rhino/RhinoEngine.java index 192301df..af19f7ad 100644 --- a/src/helma/scripting/rhino/RhinoEngine.java +++ b/src/helma/scripting/rhino/RhinoEngine.java @@ -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