You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by vg...@apache.org on 2008/02/22 18:23:47 UTC
svn commit: r630256 - in
/cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src: changes/
main/java/org/apache/cocoon/components/flow/
main/java/org/apache/cocoon/components/flow/javascript/fom/
Author: vgritsenko
Date: Fri Feb 22 09:23:41 2008
New Revision: 630256
URL: http://svn.apache.org/viewvc?rev=630256&view=rev
Log:
<action dev="vgritsenko" type="update">
Reduce thred contention in flowscript interpreter. Do not synchronize
execution of scripts within current thread scope on global map of
compiled scripts.
</action>
Modified:
cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/changes/changes.xml
cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java
cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_Cocoon.java
cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_JavaScriptInterpreter.java
Modified: cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/changes/changes.xml?rev=630256&r1=630255&r2=630256&view=diff
==============================================================================
--- cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/changes/changes.xml (original)
+++ cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/changes/changes.xml Fri Feb 22 09:23:41 2008
@@ -27,6 +27,11 @@
</properties>
<body>
<release version="1.0.0" date="2008-??-??" description="unreleased">
+ <action dev="vgritsenko" type="update">
+ Reduce thred contention in flowscript interpreter. Do not synchronize
+ execution of scripts within current thread scope on global map of
+ compiled scripts.
+ </action>
<action dev="vgritsenko" type="fix">
Fix flow scripts reload check logic.
</action>
Modified: cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java?rev=630256&r1=630255&r2=630256&view=diff
==============================================================================
--- cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java (original)
+++ cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/CompilingInterpreter.java Fri Feb 22 09:23:41 2008
@@ -33,8 +33,7 @@
*
* @version $Id$
*/
-public abstract class CompilingInterpreter
- extends AbstractInterpreter {
+public abstract class CompilingInterpreter extends AbstractInterpreter {
/**
* A source resolver
@@ -44,10 +43,11 @@
/**
* Mapping of String objects (source uri's) to ScriptSourceEntry's
*/
- protected Map compiledScripts = new HashMap();
+ protected final Map compiledScripts = new HashMap();
- /* (non-Javadoc)
- * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
+
+ /**
+ * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
*/
public void service(ServiceManager manager) throws ServiceException {
super.service(manager);
@@ -58,33 +58,32 @@
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public void dispose() {
- if (this.compiledScripts != null) {
- Iterator i = this.compiledScripts.values().iterator();
- while (i.hasNext()) {
- ScriptSourceEntry current = (ScriptSourceEntry)i.next();
- this.sourceresolver.release(current.getSource());
- }
- this.compiledScripts = null;
+ for (Iterator i = this.compiledScripts.values().iterator(); i.hasNext(); ) {
+ ScriptSourceEntry entry = (ScriptSourceEntry) i.next();
+ this.sourceresolver.release(entry.getSource());
}
+ this.compiledScripts.clear();
+
if (this.manager != null) {
this.manager.release(this.sourceresolver);
this.sourceresolver = null;
}
+
super.dispose();
}
/**
* TODO - This is a little bit strange, the interpreter calls
- * get Script on the ScriptSourceEntry which in turn
- * calls compileScript on the interpreter. I think we
- * need more refactoring here.
+ * {@link ScriptSourceEntry#compile} which in turn calls this
+ * compileScript mthod on the interpreter. I think we need
+ * more refactoring here.
*/
protected abstract Script compileScript(Context context,
Scriptable scope,
Source source) throws Exception;
protected class ScriptSourceEntry {
- final private Source source;
+ private final Source source;
private Script script;
private long compileTime;
@@ -92,31 +91,29 @@
this.source = source;
}
- public ScriptSourceEntry(Source source, Script script, long t) {
- this.source = source;
- this.script = script;
- this.compileTime = t;
- }
-
public Source getSource() {
return source;
}
+ public Script getScript() {
+ return script;
+ }
+
public long getCompileTime() {
return compileTime;
}
- public Script getScript(Context context, Scriptable scope,
- boolean refresh, CompilingInterpreter interpreter)
+ public void compile(Context context, Scriptable scope)
throws Exception {
- if (refresh) {
+ // If not first compile() call, refresh the source.
+ if (script != null) {
source.refresh();
}
- if (script == null || (refresh && compileTime < source.getLastModified())) {
- script = interpreter.compileScript(context, scope, source);
+ // Compile script if this is first compile() call or source was modified.
+ if (script == null || compileTime < source.getLastModified()) {
+ script = CompilingInterpreter.this.compileScript(context, scope, source);
compileTime = source.getLastModified();
}
- return script;
}
}
}
Modified: cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_Cocoon.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_Cocoon.java?rev=630256&r1=630255&r2=630256&view=diff
==============================================================================
--- cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_Cocoon.java (original)
+++ cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_Cocoon.java Fri Feb 22 09:23:41 2008
@@ -341,13 +341,13 @@
* @return an <code>Object</code> value
* @exception JavaScriptException if an error occurs
*/
- public Object jsFunction_load( String filename )
- throws Exception {
+ public Object jsFunction_load(String filename) throws Exception {
org.mozilla.javascript.Context cx =
- org.mozilla.javascript.Context.getCurrentContext();
+ org.mozilla.javascript.Context.getCurrentContext();
Scriptable scope = getParentScope();
+
Script script = getInterpreter().compileScript(cx, filename);
- return script.exec( cx, scope );
+ return script.exec(cx, scope);
}
/**
Modified: cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_JavaScriptInterpreter.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_JavaScriptInterpreter.java?rev=630256&r1=630255&r2=630256&view=diff
==============================================================================
--- cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_JavaScriptInterpreter.java (original)
+++ cocoon/trunk/blocks/cocoon-flowscript/cocoon-flowscript-impl/src/main/java/org/apache/cocoon/components/flow/javascript/fom/FOM_JavaScriptInterpreter.java Fri Feb 22 09:23:41 2008
@@ -25,15 +25,21 @@
import java.io.PushbackInputStream;
import java.io.Reader;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
import javax.servlet.http.HttpSession;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
+import org.apache.excalibur.source.Source;
+import org.apache.regexp.RE;
+import org.apache.regexp.RECompiler;
+import org.apache.regexp.REProgram;
+
import org.apache.cocoon.ResourceNotFoundException;
import org.apache.cocoon.components.flow.CompilingInterpreter;
import org.apache.cocoon.components.flow.Interpreter;
@@ -47,12 +53,7 @@
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Request;
-import org.apache.commons.jxpath.JXPathIntrospector;
-import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
-import org.apache.excalibur.source.Source;
-import org.apache.regexp.RE;
-import org.apache.regexp.RECompiler;
-import org.apache.regexp.REProgram;
+
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.Function;
@@ -98,7 +99,8 @@
/**
* When was the last time we checked for script modifications. Used
- * only if {@link #reloadScripts} is true.
+ * only if {@link #reloadScripts} is true. Access is synchronized by
+ * {@link #compiledScripts}.
*/
private long lastReloadCheckTime;
@@ -115,14 +117,7 @@
private boolean enableDebugger;
- /**
- * Needed to get things working with JDK 1.3. Can be removed once we
- * don't support that platform any more.
- */
- protected ServiceManager getServiceManager() {
- return manager;
- }
-
+
/**
* JavaScript debugger: there's only one of these: it can debug multiple
* threads executing JS code.
@@ -150,6 +145,7 @@
return debugger;
}
+
public void configure(Configuration config) throws ConfigurationException {
super.configure(config);
@@ -184,7 +180,6 @@
FOM_Cocoon.init(scope);
} catch (Exception e) {
Context.exit();
- e.printStackTrace();
throw e;
}
}
@@ -247,14 +242,16 @@
}
public static class ThreadScope extends ScriptableObject {
- private static final String[] BUILTIN_PACKAGES = {"javax", "org", "com"};
+ private static final String[] BUILTIN_PACKAGES = { "javax", "org", "com" };
+ private static final String[] BUILTIN_FUNCTIONS = { "importClass" };
private ClassLoader classLoader;
- /* true if this scope has assigned any global vars */
+ /** true if this scope has assigned any global vars */
boolean useSession;
- boolean locked = false;
+ /** true if this scope is locked for implicit variable declarations */
+ boolean locked;
/**
* Initializes new top-level scope.
@@ -262,8 +259,7 @@
public ThreadScope(Global scope) throws Exception {
final Context context = Context.getCurrentContext();
- final String[] names = { "importClass" };
- defineFunctionProperties(names,
+ defineFunctionProperties(BUILTIN_FUNCTIONS,
ThreadScope.class,
ScriptableObject.DONTENUM);
@@ -318,7 +314,7 @@
super.put(index, start, value);
}
- // Invoked after script execution
+ /** Invoked after script execution */
void onExec() {
this.useSession = false;
super.put(LAST_EXEC_TIME, this, new Long(System.currentTimeMillis()));
@@ -369,7 +365,7 @@
}
/**
- * Returns a new Scriptable object to be used as the global scope
+ * Sets up a ThreadScope object to be used as the global scope
* when running the JavaScript scripts in the context of a request.
*
* <p>If you want to maintain the state of global variables across
@@ -385,80 +381,96 @@
private void setupContext(Redirector redirector, Context context,
ThreadScope thrScope)
throws Exception {
- // Try to retrieve the scope object from the session instance. If
- // no scope is found, we create a new one, but don't place it in
- // the session.
- //
- // When a user script "creates" a session using
- // cocoon.createSession() in JavaScript, the thrScope is placed in
- // the session object, where it's later retrieved from here. This
- // behaviour allows multiple JavaScript functions to share the
- // same global scope.
-
- FOM_Cocoon cocoon = (FOM_Cocoon) thrScope.get("cocoon", thrScope);
- long lastExecTime = ((Long) thrScope.get(LAST_EXEC_TIME,
- thrScope)).longValue();
- boolean needsRefresh = false;
- if (reloadScripts) {
- long now = System.currentTimeMillis();
- if (now >= lastReloadCheckTime + checkTime) {
- needsRefresh = true;
- lastReloadCheckTime = now;
- }
- }
// We need to setup the FOM_Cocoon object according to the current
// request. Everything else remains the same.
ClassLoader contextClassloader = Thread.currentThread().getContextClassLoader();
thrScope.setupPackages(contextClassloader);
+
+ FOM_Cocoon cocoon = (FOM_Cocoon) thrScope.get("cocoon", thrScope);
cocoon.pushCallContext(this, redirector, avalonContext, null);
- // Check if we need to compile and/or execute scripts
+ // Time when scripts were last executed, in this thread scope
+ final long lastExecuted = ((Long) thrScope.get(LAST_EXEC_TIME,
+ thrScope)).longValue();
+
+ // List of scripts (ScriptSourceEntry objects) which might have to be
+ // executed in this thread scope.
+ List execList = new ArrayList();
+
+ // Check if we need to (re)compile any of the scripts
synchronized (compiledScripts) {
- List execList = new ArrayList();
- // If we've never executed scripts in this scope or
- // if reload-scripts is true and the check interval has expired
- // or if new scripts have been specified in the sitemap,
- // then create a list of scripts to compile/execute
- if (lastExecTime == 0 || needsRefresh || needResolve.size() > 0) {
- topLevelScripts.addAll(needResolve);
- if (lastExecTime != 0 && !needsRefresh) {
- execList.addAll(needResolve);
- } else {
- execList.addAll(topLevelScripts);
+ // Determine if refresh is needed.
+ boolean needsRefresh = false;
+ if (reloadScripts) {
+ long now = System.currentTimeMillis();
+ if (now >= lastReloadCheckTime + checkTime) {
+ needsRefresh = true;
+ lastReloadCheckTime = now;
}
+ }
+
+ // List of script URIs to resolve
+ List resolveList = new ArrayList();
+ // If reloadScripts is true, recompile all top level scripts
+ if (needsRefresh) {
+ resolveList.addAll(topLevelScripts);
+ }
+ // If new scripts has been specified in sitemap, load and compile them
+ if (needResolve.size() > 0) {
+ topLevelScripts.addAll(needResolve);
+ resolveList.addAll(needResolve);
needResolve.clear();
}
+
// Compile all the scripts first. That way you can set breakpoints
// in the debugger before they execute.
- for (int i = 0, size = execList.size(); i < size; i++) {
- String sourceURI = (String)execList.get(i);
+ for (int i = 0, size = resolveList.size(); i < size; i++) {
+ String sourceURI = (String) resolveList.get(i);
ScriptSourceEntry entry =
- (ScriptSourceEntry)compiledScripts.get(sourceURI);
+ (ScriptSourceEntry) compiledScripts.get(sourceURI);
if (entry == null) {
Source src = this.sourceresolver.resolveURI(sourceURI);
entry = new ScriptSourceEntry(src);
compiledScripts.put(sourceURI, entry);
}
- // Compile the script if necessary
- entry.getScript(context, this.scope, needsRefresh, this);
- }
- // Execute the scripts if necessary
- for (int i = 0, size = execList.size(); i < size; i++) {
- String sourceURI = (String) execList.get(i);
- ScriptSourceEntry entry =
- (ScriptSourceEntry) compiledScripts.get(sourceURI);
- long lastMod = 0;
- if (reloadScripts && lastExecTime != 0) {
- lastMod = entry.getSource().getLastModified();
+ entry.compile(context, this.scope);
+ // If top level scripts were executed in this thread scope,
+ // collect only newly added scripts for execution.
+ if (lastExecuted != 0) {
+ execList.add(entry);
}
- Script script = entry.getScript(context, this.scope, false, this);
- if (lastExecTime == 0 || lastMod > lastExecTime) {
- script.exec(context, thrScope);
- thrScope.onExec();
+ }
+
+ // If scripts were never executed in this thread scope yet,
+ // then collect all top level scripts for execution.
+ if (lastExecuted == 0) {
+ for (int i = 0, size = topLevelScripts.size(); i < size; i++) {
+ String sourceURI = (String) resolveList.get(i);
+ ScriptSourceEntry entry =
+ (ScriptSourceEntry) compiledScripts.get(sourceURI);
+ if (entry != null) {
+ execList.add(entry);
+ }
}
}
}
+
+ // Execute the scripts identified above, as necessary
+ boolean executed = false;
+ for (int i = 0, size = execList.size(); i < size; i++) {
+ ScriptSourceEntry entry = (ScriptSourceEntry) execList.get(i);
+ if (lastExecuted == 0 || entry.getCompileTime() > lastExecuted) {
+ entry.getScript().exec(context, thrScope);
+ executed = true;
+ }
+ }
+
+ // If any of the scripts has been executed, inform ThreadScope,
+ // which will update last execution timestamp.
+ if (executed) {
+ thrScope.onExec();
+ }
}
/**
@@ -470,24 +482,23 @@
*/
Script compileScript(Context cx, String fileName) throws Exception {
Source src = this.sourceresolver.resolveURI(fileName);
- if (src != null) {
- synchronized (compiledScripts) {
- ScriptSourceEntry entry =
- (ScriptSourceEntry)compiledScripts.get(src.getURI());
- Script compiledScript;
- if (entry == null) {
- compiledScripts.put(src.getURI(),
- entry = new ScriptSourceEntry(src));
- } else {
- this.sourceresolver.release(src);
- }
- boolean needsRefresh = reloadScripts &&
- (entry.getCompileTime() + checkTime < System.currentTimeMillis());
- compiledScript = entry.getScript(cx, this.scope, needsRefresh, this);
- return compiledScript;
+ synchronized (compiledScripts) {
+ ScriptSourceEntry entry =
+ (ScriptSourceEntry) compiledScripts.get(src.getURI());
+ if (entry == null) {
+ compiledScripts.put(src.getURI(),
+ entry = new ScriptSourceEntry(src));
+ } else {
+ this.sourceresolver.release(src);
+ }
+
+ long compileTime = entry.getCompileTime();
+ if (compileTime == 0 || reloadScripts && (compileTime + checkTime < System.currentTimeMillis())) {
+ entry.compile(cx, this.scope);
}
+
+ return entry.getScript();
}
- throw new ResourceNotFoundException(fileName + ": not found");
}
protected Script compileScript(Context cx, Scriptable scope, Source src)
@@ -497,9 +508,7 @@
String encoding = findEncoding(is);
Reader reader = encoding == null ? new InputStreamReader(is) : new InputStreamReader(is, encoding);
reader = new BufferedReader(reader);
- Script compiledScript = cx.compileReader(reader,
- src.getURI(), 1, null);
- return compiledScript;
+ return cx.compileReader(reader, src.getURI(), 1, null);
} finally {
is.close();
}
@@ -509,7 +518,7 @@
// (see http://www.iana.org/assignments/character-sets). So reading 100 bytes should be more than enough.
private final static int ENCODING_BUF_SIZE = 100;
// Match 'encoding = xxxx' on the first line
- REProgram encodingRE = new RECompiler().compile("^.*encoding\\s*=\\s*([^\\s]*)");
+ private final static REProgram encodingRE = new RECompiler().compile("encoding\\s*=\\s*([^\\s]*)");
/**
* Find the encoding of the stream, or null if not specified
@@ -555,7 +564,17 @@
context.setDebugger(locationTracker, null);
}
+ // Try to retrieve the scope object from the session instance. If
+ // no scope is found, we create a new one, but don't place it in
+ // the session.
+ //
+ // When a user script "creates" a session using
+ // cocoon.createSession() in JavaScript, the thrScope is placed in
+ // the session object, where it's later retrieved from here. This
+ // behaviour allows multiple JavaScript functions to share the
+ // same global scope.
ThreadScope thrScope = getSessionScope();
+
synchronized (thrScope) {
ClassLoader savedClassLoader =
Thread.currentThread().getContextClassLoader();
@@ -569,8 +588,8 @@
FOM_JavaScriptFlowHelper.setFOM_FlowScope(cocoon.getObjectModel(), thrScope);
if (enableDebugger) {
+ // only raise the debugger window if it isn't already visible
if (!getDebugger().isVisible()) {
- // only raise the debugger window if it isn't already visible
getDebugger().setVisible(true);
}
}
@@ -643,8 +662,9 @@
// Obtain the continuation object from it, and setup the
// FOM_Cocoon object associated in the dynamic scope of the saved
// continuation with the environment and context objects.
- Continuation k = (Continuation)wk.getContinuation();
- ThreadScope kScope = (ThreadScope)k.getParentScope();
+ Continuation k = (Continuation) wk.getContinuation();
+ ThreadScope kScope = (ThreadScope) k.getParentScope();
+
synchronized (kScope) {
ClassLoader savedClassLoader =
Thread.currentThread().getContextClassLoader();