SLING-915 - [Rhino] Implement Compilable
* implemented Compilable support
* added support for the ScripCache implemented in SLING-913 for scripts that are not executed as part of the normal Sling script resolution
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1690905 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 93b3450..d58d60d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,16 +103,25 @@
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.api</artifactId>
<version>2.1.0</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.scripting.api</artifactId>
- <version>2.1.0</version>
+ <version>2.1.7-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.scripting.core</artifactId>
+ <version>2.0.29-SNAPSHOT</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.jcr.resource</artifactId>
<version>2.3.8</version>
+ <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.apache.jackrabbit</groupId>
@@ -128,11 +137,13 @@
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.commons.classloader</artifactId>
<version>1.3.0</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.commons.json</artifactId>
<version>2.0.6</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
@@ -143,10 +154,12 @@
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.jcr</groupId>
@@ -157,10 +170,12 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mozilla</groupId>
@@ -170,6 +185,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
@@ -177,6 +193,12 @@
<version>3.2.1</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.4</version>
+ <scope>provided</scope>
+ </dependency>
<!-- Testing -->
<dependency>
<groupId>org.apache.sling</groupId>
@@ -184,5 +206,17 @@
<version>2.0.16</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4</artifactId>
+ <version>1.6.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito</artifactId>
+ <version>1.6.2</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java b/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java
index b762ece..d04d3d4 100644
--- a/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java
+++ b/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java
@@ -16,25 +16,36 @@
*/
package org.apache.sling.scripting.javascript.internal;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
+import org.apache.commons.io.IOUtils;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.apache.sling.commons.classloader.DynamicClassLoader;
import org.apache.sling.scripting.api.AbstractSlingScriptEngine;
+import org.apache.sling.scripting.api.CachedScript;
+import org.apache.sling.scripting.api.ScriptCache;
+import org.apache.sling.scripting.api.ScriptNameAware;
+import org.apache.sling.scripting.core.ScriptNameAwareReader;
import org.apache.sling.scripting.javascript.io.EspReader;
import org.mozilla.javascript.ClassCache;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.Script;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
@@ -48,152 +59,102 @@
* A ScriptEngine that uses the Rhino interpreter to process Sling requests with
* server-side javascript.
*/
-public class RhinoJavaScriptEngine extends AbstractSlingScriptEngine {
+public class RhinoJavaScriptEngine extends AbstractSlingScriptEngine implements Compilable {
private static final Logger LOGGER = LoggerFactory.getLogger(RhinoJavaScriptEngine.class);
+ private static final String NO_SCRIPT_NAME = "NO_SCRIPT_NAME";
private Scriptable rootScope;
+ private ScriptCache scriptCache;
- public RhinoJavaScriptEngine(ScriptEngineFactory factory,
- Scriptable rootScope) {
+ public RhinoJavaScriptEngine(ScriptEngineFactory factory, Scriptable rootScope, ScriptCache scriptCache) {
super(factory);
this.rootScope = rootScope;
+ this.scriptCache = scriptCache;
}
- public Object eval(Reader scriptReader, ScriptContext scriptContext)
- throws ScriptException {
- Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
- String scriptName = "NO_SCRIPT_NAME";
- {
- SlingScriptHelper helper = (SlingScriptHelper) bindings.get(SlingBindings.SLING);
- if (helper != null) {
- scriptName = helper.getScript().getScriptResource().getPath();
- }
- }
-
- // wrap the reader in an EspReader for ESP scripts
- if (scriptName.endsWith(RhinoJavaScriptEngineFactory.ESP_SCRIPT_EXTENSION)) {
- scriptReader = new EspReader(scriptReader);
- }
-
- // container for replaced properties
- Map<String, Object> replacedProperties = null;
- Scriptable scope = null;
- boolean isTopLevelCall = false;
-
- // create a rhino Context and execute the script
+ public CompiledScript compile(String script) throws ScriptException {
+ Reader reader = null;
try {
-
- final Context rhinoContext = Context.enter();
- rhinoContext.setOptimizationLevel(optimizationLevel());
-
- if (ScriptRuntime.hasTopCall(rhinoContext)) {
- // reuse the top scope if we are included
- scope = ScriptRuntime.getTopCallScope(rhinoContext);
-
- } else {
- // create the request top scope, use the ImporterToplevel here
- // to support the importPackage and importClasses functions
- scope = new ImporterTopLevel();
-
- // Set the global scope to be our prototype
- scope.setPrototype(rootScope);
-
- // We want "scope" to be a new top-level scope, so set its
- // parent scope to null. This means that any variables created
- // by assignments will be properties of "scope".
- scope.setParentScope(null);
-
- // setup the context for use
- WrapFactory wrapFactory = ((RhinoJavaScriptEngineFactory) getFactory()).getWrapFactory();
- rhinoContext.setWrapFactory(wrapFactory);
-
- // this is the top level call
- isTopLevelCall = true;
- }
-
- // add initial properties to the scope
- replacedProperties = setBoundProperties(scope, bindings);
-
- final int lineNumber = 1;
- final Object securityDomain = null;
-
- Object result = rhinoContext.evaluateReader(scope, scriptReader, scriptName,
- lineNumber, securityDomain);
-
- if (result instanceof Wrapper) {
- result = ((Wrapper) result).unwrap();
- }
-
- return (result instanceof Undefined) ? null : result;
-
- } catch (JavaScriptException t) {
-
- // prevent variables to be pushed back in case of errors
- isTopLevelCall = false;
-
- final ScriptException se = new ScriptException(t.details(),
- t.sourceName(), t.lineNumber());
-
- // log the script stack trace
- ((Logger) bindings.get(SlingBindings.LOG)).error(t.getScriptStackTrace());
-
- // set the exception cause
- Object value = t.getValue();
- if (value != null) {
- if (value instanceof Wrapper) {
- value = ((Wrapper) value).unwrap();
- }
- if (value instanceof Throwable) {
- se.initCause((Throwable) value);
- }
- }
-
- // if the cause could not be set, overwrite the stack trace
- if (se.getCause() == null) {
- se.setStackTrace(t.getStackTrace());
- }
-
- throw se;
-
- } catch (Throwable t) {
-
- // prevent variables to be pushed back in case of errors
- isTopLevelCall = false;
-
- final ScriptException se = new ScriptException(
- "Failure running script " + scriptName + ": " + t.getMessage());
- se.initCause(t);
- throw se;
-
+ reader = new InputStreamReader(IOUtils.toInputStream(script));
+ return compile(reader);
} finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
- // if we are the top call (the Context is now null) we have to
- // play back any properties from the scope back to the bindings
- if (isTopLevelCall) {
- getBoundProperties(scope, bindings);
+ public CompiledScript compile(Reader scriptReader) throws ScriptException {
+ final String scriptName = getScriptName(scriptReader);
+ CachedScript cachedScript = scriptCache.getScript(scriptName);
+ if (cachedScript != null) {
+ LOGGER.debug("Detected cached script for {}.", scriptName);
+ return cachedScript.getCompiledScript();
+ } else {
+ scriptReader = wrapReaderIfEspScript(scriptReader, scriptName);
+ try {
+ final Context rhinoContext = Context.enter();
+ rhinoContext.setOptimizationLevel(optimizationLevel());
+
+ if (!ScriptRuntime.hasTopCall(rhinoContext)) {
+ // setup the context for use
+ WrapFactory wrapFactory = ((RhinoJavaScriptEngineFactory) getFactory()).getWrapFactory();
+ rhinoContext.setWrapFactory(wrapFactory);
+ }
+
+ final int lineNumber = 1;
+ final Object securityDomain = null;
+
+ final Script script = rhinoContext.compileReader(scriptReader, scriptName, lineNumber, securityDomain);
+ final SlingCompiledScript slingCompiledScript = new SlingCompiledScript(script, this);
+ cachedScript = new CachedScript() {
+ @Override
+ public String getScriptPath() {
+ return scriptName;
+ }
+
+ @Override
+ public CompiledScript getCompiledScript() {
+ return slingCompiledScript;
+ }
+ };
+ scriptCache.putScript(cachedScript);
+ LOGGER.debug("Added {} script to Script Cache.", scriptName);
+ return slingCompiledScript;
+ } catch (IOException e) {
+ final ScriptException se = new ScriptException("Failure running script " + scriptName + ": " + e.getMessage());
+ se.initCause(e);
+ throw se;
+ } finally {
+ Context.exit();
}
+ }
+ }
- // if properties have been replaced, reset them
- resetBoundProperties(scope, replacedProperties);
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- if (classLoader instanceof DynamicClassLoader) {
- DynamicClassLoader dynamicClassLoader = (DynamicClassLoader) classLoader;
- if (!dynamicClassLoader.isLive()) {
- /**
- * if the classloader on this thread is a dynamic class loader and it's dirty we should clear Rhino's class cache
- * to avoid classloader leaks
- */
- if (scope != null) {
- ClassCache classCache = ClassCache.get(scope);
- classCache.clearCaches();
- LOGGER.info("Detected dirty classloader on thread {}. Emptying Rhino's class cache.", Thread.currentThread()
- .getName());
+ public Object eval(Reader scriptReader, ScriptContext scriptContext) throws ScriptException {
+ String scriptName = getScriptName(scriptReader);
+ Reader reader = wrapReaderIfEspScript(scriptReader, scriptName);
+ if (!(scriptReader instanceof ScriptNameAware)) {
+ if (NO_SCRIPT_NAME.equals(scriptName)) {
+ String script = (String) scriptContext.getBindings(ScriptContext.ENGINE_SCOPE).get(ScriptEngine.FILENAME);
+ if (script != null) {
+ for (String extension : getFactory().getExtensions()) {
+ if (script.endsWith(extension)) {
+ scriptName = script;
+ reader = new ScriptNameAwareReader(reader, scriptName);
+ break;
+ }
}
}
}
- Context.exit();
}
+ return compile(reader).eval(scriptContext);
+ }
+
+ private Reader wrapReaderIfEspScript(Reader scriptReader, String scriptName) {
+ if (scriptName.endsWith(RhinoJavaScriptEngineFactory.ESP_SCRIPT_EXTENSION)) {
+ scriptReader = new EspReader(scriptReader);
+ }
+ return scriptReader;
}
private Map<String, Object> setBoundProperties(Scriptable scope,
@@ -251,4 +212,154 @@
private int optimizationLevel() {
return ((RhinoJavaScriptEngineFactory)getFactory()).getOptimizationLevel();
}
+
+ private class SlingCompiledScript extends CompiledScript {
+
+ private final Script script;
+ private final ScriptEngine engine;
+
+ SlingCompiledScript(Script script, ScriptEngine engine) {
+ this.script = script;
+ this.engine = engine;
+ }
+
+ @Override
+ public Object eval(ScriptContext scriptContext) throws ScriptException {
+ Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
+ String scriptName = "NO_SCRIPT_NAME";
+ {
+ SlingScriptHelper helper = (SlingScriptHelper) bindings.get(SlingBindings.SLING);
+ if (helper != null) {
+ scriptName = helper.getScript().getScriptResource().getPath();
+ }
+ }
+
+ // container for replaced properties
+ Map<String, Object> replacedProperties = null;
+ Scriptable scope = null;
+ boolean isTopLevelCall = false;
+
+ // create a rhino Context and execute the script
+ try {
+
+ final Context rhinoContext = Context.enter();
+ rhinoContext.setOptimizationLevel(optimizationLevel());
+
+ if (ScriptRuntime.hasTopCall(rhinoContext)) {
+ // reuse the top scope if we are included
+ scope = ScriptRuntime.getTopCallScope(rhinoContext);
+
+ } else {
+ // create the request top scope, use the ImporterToplevel here
+ // to support the importPackage and importClasses functions
+ scope = new ImporterTopLevel();
+
+ // Set the global scope to be our prototype
+ scope.setPrototype(rootScope);
+
+ // We want "scope" to be a new top-level scope, so set its
+ // parent scope to null. This means that any variables created
+ // by assignments will be properties of "scope".
+ scope.setParentScope(null);
+
+ // setup the context for use
+ WrapFactory wrapFactory = ((RhinoJavaScriptEngineFactory) getFactory()).getWrapFactory();
+ rhinoContext.setWrapFactory(wrapFactory);
+
+ // this is the top level call
+ isTopLevelCall = true;
+ }
+
+ // add initial properties to the scope
+ replacedProperties = setBoundProperties(scope, bindings);
+
+ Object result = script.exec(rhinoContext, scope);
+
+ if (result instanceof Wrapper) {
+ result = ((Wrapper) result).unwrap();
+ }
+
+ return (result instanceof Undefined) ? null : result;
+
+ } catch (JavaScriptException t) {
+
+ // prevent variables to be pushed back in case of errors
+ isTopLevelCall = false;
+
+ final ScriptException se = new ScriptException(t.details(),
+ t.sourceName(), t.lineNumber());
+
+ // log the script stack trace
+ ((Logger) bindings.get(SlingBindings.LOG)).error(t.getScriptStackTrace());
+
+ // set the exception cause
+ Object value = t.getValue();
+ if (value != null) {
+ if (value instanceof Wrapper) {
+ value = ((Wrapper) value).unwrap();
+ }
+ if (value instanceof Throwable) {
+ se.initCause((Throwable) value);
+ }
+ }
+
+ // if the cause could not be set, overwrite the stack trace
+ if (se.getCause() == null) {
+ se.setStackTrace(t.getStackTrace());
+ }
+
+ throw se;
+
+ } catch (Throwable t) {
+
+ // prevent variables to be pushed back in case of errors
+ isTopLevelCall = false;
+
+ final ScriptException se = new ScriptException(
+ "Failure running script " + scriptName + ": " + t.getMessage());
+ se.initCause(t);
+ throw se;
+
+ } finally {
+
+ // if we are the top call (the Context is now null) we have to
+ // play back any properties from the scope back to the bindings
+ if (isTopLevelCall) {
+ getBoundProperties(scope, bindings);
+ }
+
+ // if properties have been replaced, reset them
+ resetBoundProperties(scope, replacedProperties);
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader instanceof DynamicClassLoader) {
+ DynamicClassLoader dynamicClassLoader = (DynamicClassLoader) classLoader;
+ if (!dynamicClassLoader.isLive()) {
+ /**
+ * if the class loader on this thread is a dynamic class loader and it's dirty we should clear Rhino's class cache
+ * to avoid class loader leaks
+ */
+ if (scope != null) {
+ ClassCache classCache = ClassCache.get(scope);
+ classCache.clearCaches();
+ LOGGER.info("Detected dirty class loader on thread {}. Emptying Rhino's class cache.", Thread.currentThread()
+ .getName());
+ }
+ }
+ }
+ Context.exit();
+ }
+ }
+
+ @Override
+ public ScriptEngine getEngine() {
+ return engine;
+ }
+ }
+
+ private String getScriptName(Reader scriptReader) {
+ if(scriptReader instanceof ScriptNameAware){
+ return ((ScriptNameAware) scriptReader).getScriptName();
+ }
+ return NO_SCRIPT_NAME;
+ }
}
diff --git a/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java b/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
index 53225b4..d2924b0 100644
--- a/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
+++ b/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
@@ -22,6 +22,7 @@
import java.util.HashSet;
import java.util.Set;
import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -34,6 +35,7 @@
import org.apache.sling.commons.classloader.DynamicClassLoaderManager;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.scripting.api.AbstractScriptEngineFactory;
+import org.apache.sling.scripting.api.ScriptCache;
import org.apache.sling.scripting.javascript.RhinoHostObjectProvider;
import org.apache.sling.scripting.javascript.SlingWrapper;
import org.apache.sling.scripting.javascript.helper.SlingContextFactory;
@@ -61,39 +63,48 @@
/**
* The <code>RhinoJavaScriptEngineFactory</code> TODO
- *
*/
@Component(
metatype = true,
- label="Apache Sling Rhino Javascript Engine Factory",
- description="Javascript engine based on Rino")
-@Service(value=javax.script.ScriptEngineFactory.class)
-@Reference(name="HostObjectProvider", referenceInterface=RhinoHostObjectProvider.class,
- cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC,
- bind="addHostObjectProvider", unbind="removeHostObjectProvider")
+ label = "Apache Sling Rhino Javascript Engine Factory",
+ description = "Javascript engine based on Rhino"
+)
+@Service(ScriptEngineFactory.class)
+@Reference(
+ name = "HostObjectProvider",
+ referenceInterface = RhinoHostObjectProvider.class,
+ cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC,
+ bind = "addHostObjectProvider",
+ unbind = "removeHostObjectProvider"
+)
@Property(
name = RhinoJavaScriptEngineFactory.OPTIMIZATION_CONFIG,
label = "Rhino optimization level",
intValue = RhinoJavaScriptEngineFactory.DEFAULT_OPTIMIZATION_LEVEL,
- description = "The level of optimization for the bytecode generated by Rhino. Provide values between 0-9, 9 being the most aggressive level of optimization. A value of -1 will run scripts in interpreted mode")
-public class RhinoJavaScriptEngineFactory extends AbstractScriptEngineFactory
- implements ScopeProvider {
+ description = "The level of optimization for the bytecode generated by Rhino. Provide values between 0-9, 9 being the most " +
+ "aggressive level of optimization. A value of -1 will run scripts in interpreted mode."
+)
+public class RhinoJavaScriptEngineFactory extends AbstractScriptEngineFactory implements ScopeProvider {
public final static String OPTIMIZATION_CONFIG = "org.apache.sling.scripting.javascript.rhino.optLevel";
- public final static int DEFAULT_OPTIMIZATION_LEVEL = - 1;
+ public final static int DEFAULT_OPTIMIZATION_LEVEL = 9;
public final static String ECMA_SCRIPT_EXTENSION = "ecma";
public final static String ESP_SCRIPT_EXTENSION = "esp";
private static final Class<?>[] HOSTOBJECT_CLASSES = {
- ScriptableResource.class, ScriptableNode.class,
- ScriptableProperty.class, ScriptableItemMap.class,
- ScriptablePrintWriter.class, ScriptableVersionHistory.class,
- ScriptableVersion.class, ScriptableCalendar.class };
+ ScriptableResource.class, ScriptableNode.class,
+ ScriptableProperty.class, ScriptableItemMap.class,
+ ScriptablePrintWriter.class, ScriptableVersionHistory.class,
+ ScriptableVersion.class, ScriptableCalendar.class
+ };
- /** default log */
+ /**
+ * default log
+ */
private final Logger log = LoggerFactory.getLogger(getClass());
private int optimizationLevel;
@@ -107,10 +118,13 @@
private final Set<RhinoHostObjectProvider> hostObjectProvider = new HashSet<RhinoHostObjectProvider>();
@Reference
- private DynamicClassLoaderManager dynamicClassLoaderManager;
+ private DynamicClassLoaderManager dynamicClassLoaderManager = null;
+
+ @Reference
+ private ScriptCache scriptCache = null;
public ScriptEngine getScriptEngine() {
- return new RhinoJavaScriptEngine(this, getRootScope());
+ return new RhinoJavaScriptEngine(this, getRootScope(), scriptCache);
}
public String getLanguageName() {
@@ -124,6 +138,7 @@
/**
* Get the optimization level that should be used when running JS scripts
* with Rhino
+ *
* @return an integer from 0-9 with 9 being the most aggressive optimization, or
* -1 if interpreted mode is to be used
*/
@@ -154,20 +169,16 @@
final Context rhinoContext = Context.enter();
try {
- Scriptable tmpScope = rhinoContext.initStandardObjects(
- new ImporterTopLevel(), false);
+ Scriptable tmpScope = rhinoContext.initStandardObjects(new ImporterTopLevel(), false);
// default classes
- addHostObjects(tmpScope,
- (Class<? extends ScriptableObject>[]) HOSTOBJECT_CLASSES);
+ addHostObjects(tmpScope, (Class<? extends ScriptableObject>[]) HOSTOBJECT_CLASSES);
// provided classes
for (RhinoHostObjectProvider provider : hostObjectProvider) {
addHostObjects(tmpScope, provider.getHostObjectClasses());
- addImportedClasses(rhinoContext, tmpScope,
- provider.getImportedClasses());
- addImportedPackages(rhinoContext, tmpScope,
- provider.getImportedPackages());
+ addImportedClasses(rhinoContext, tmpScope, provider.getImportedClasses());
+ addImportedPackages(rhinoContext, tmpScope, provider.getImportedPackages());
}
// only assign the root scope when complete set up
@@ -201,9 +212,7 @@
@Activate
protected void activate(ComponentContext context) {
Dictionary<?, ?> props = context.getProperties();
- boolean debugging = getProperty(
- "org.apache.sling.scripting.javascript.debug", props,
- context.getBundleContext(), false);
+ boolean debugging = getProperty("org.apache.sling.scripting.javascript.debug", props, context.getBundleContext(), false);
optimizationLevel = readOptimizationLevel(props);
@@ -214,14 +223,12 @@
SlingContextFactory.setup(this);
Context cx = Context.enter();
- setEngineName(getEngineName() + " (" + cx.getImplementationVersion()
- + ")");
+ setEngineName(getEngineName() + " (" + cx.getImplementationVersion() + ")");
languageVersion = String.valueOf(cx.getLanguageVersion());
Context.exit();
setExtensions(ECMA_SCRIPT_EXTENSION, ESP_SCRIPT_EXTENSION);
- setMimeTypes("text/javascript", "application/ecmascript",
- "application/javascript");
+ setMimeTypes("text/javascript", "application/ecmascript", "application/javascript");
setNames("javascript", ECMA_SCRIPT_EXTENSION, ESP_SCRIPT_EXTENSION);
final ContextFactory contextFactory = ContextFactory.getGlobal();
@@ -230,14 +237,15 @@
}
// set the dynamic class loader as the application class loader
final DynamicClassLoaderManager dclm = this.dynamicClassLoaderManager;
- if ( dclm != null ) {
+ if (dclm != null) {
contextFactory.initApplicationClassLoader(dynamicClassLoaderManager.getDynamicClassLoader());
}
-
+
log.info("Activated with optimization level {}", optimizationLevel);
}
@Deactivate
+ @SuppressWarnings("unused")
protected void deactivate(ComponentContext context) {
// remove the root scope
@@ -251,6 +259,7 @@
hostObjectProvider.clear();
}
+ @SuppressWarnings("unused")
protected void addHostObjectProvider(RhinoHostObjectProvider provider) {
hostObjectProvider.add(provider);
@@ -259,6 +268,7 @@
}
}
+ @SuppressWarnings("unused")
protected void removeHostObjectProvider(RhinoHostObjectProvider provider) {
// remove the current root scope and have it recreated using the
// new host object classes
@@ -269,8 +279,7 @@
// ---------- internal
- private void addHostObjects(Scriptable scope,
- Class<? extends Scriptable>[] classes) {
+ private void addHostObjects(Scriptable scope, Class<? extends Scriptable>[] classes) {
if (classes != null) {
for (Class<? extends Scriptable> clazz : classes) {
try {
@@ -283,30 +292,23 @@
// SlingWrappers can map to several classes if needed
final SlingWrapper hostWrapper = (SlingWrapper) clazz.newInstance();
for (Class<?> c : hostWrapper.getWrappedClasses()) {
- getWrapFactory().registerWrapper(c,
- hostWrapper.getClassName());
+ getWrapFactory().registerWrapper(c, hostWrapper.getClassName());
}
-
} else {
-
// but other Scriptable host objects need to be
// registered as well
final Scriptable host = clazz.newInstance();
- getWrapFactory().registerWrapper(
- host.getClass(), host.getClassName());
-
+ getWrapFactory().registerWrapper(host.getClass(), host.getClassName());
}
-
} catch (Throwable t) {
- log.warn("addHostObjects: Cannot prepare host object "
- + clazz, t);
+ log.warn("addHostObjects: Cannot prepare host object " + clazz, t);
}
}
}
}
private void addImportedClasses(Context cx, Scriptable scope,
- Class<?>[] classes) {
+ Class<?>[] classes) {
if (classes != null && classes.length > 0) {
NativeJavaClass[] np = new NativeJavaClass[classes.length];
for (int i = 0; i < classes.length; i++) {
@@ -317,7 +319,7 @@
}
private void addImportedPackages(Context cx, Scriptable scope,
- String[] packages) {
+ String[] packages) {
if (packages != null && packages.length > 0) {
NativeJavaPackage[] np = new NativeJavaPackage[packages.length];
for (int i = 0; i < packages.length; i++) {
@@ -328,7 +330,7 @@
}
private boolean getProperty(String name, Dictionary<?, ?> props,
- BundleContext bundleContext, boolean defaultValue) {
+ BundleContext bundleContext, boolean defaultValue) {
Object value = props.get(name);
if (value == null) {
value = bundleContext.getProperty(name);
diff --git a/src/test/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineTest.java b/src/test/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineTest.java
index c51ddb0..7c68c5b 100644
--- a/src/test/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineTest.java
+++ b/src/test/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineTest.java
@@ -23,15 +23,19 @@
import javax.script.ScriptException;
import javax.script.SimpleBindings;
-import junit.framework.TestCase;
-
+import org.apache.sling.scripting.api.ScriptCache;
import org.apache.sling.scripting.javascript.helper.SlingWrapFactory;
+import org.mockito.Mockito;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable;
+import junit.framework.TestCase;
+
public class RhinoJavaScriptEngineTest extends TestCase {
+ private static ScriptCache scriptCache = Mockito.mock(ScriptCache.class);
+
public void testPreserveScopeBetweenEvals() throws ScriptException {
MockRhinoJavaScriptEngineFactory factory = new MockRhinoJavaScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine();
@@ -55,7 +59,7 @@
public ScriptEngine getScriptEngine() {
final Context rhinoContext = Context.enter();
Scriptable scope = rhinoContext.initStandardObjects(new ImporterTopLevel(), false);
- return new RhinoJavaScriptEngine(this, scope);
+ return new RhinoJavaScriptEngine(this, scope, scriptCache);
}
@Override
diff --git a/src/test/java/org/apache/sling/scripting/javascript/internal/ScriptEngineHelper.java b/src/test/java/org/apache/sling/scripting/javascript/internal/ScriptEngineHelper.java
index b4c66cb..135112d 100644
--- a/src/test/java/org/apache/sling/scripting/javascript/internal/ScriptEngineHelper.java
+++ b/src/test/java/org/apache/sling/scripting/javascript/internal/ScriptEngineHelper.java
@@ -18,15 +18,11 @@
*/
package org.apache.sling.scripting.javascript.internal;
-import java.io.File;
-import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
-
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
@@ -36,24 +32,19 @@
import org.apache.sling.commons.testing.osgi.MockBundle;
import org.apache.sling.commons.testing.osgi.MockComponentContext;
+import org.apache.sling.scripting.api.ScriptCache;
+import org.mockito.Mockito;
+import org.mockito.internal.util.reflection.Whitebox;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Wrapper;
-import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.BundleListener;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkListener;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
/** Helpers to run javascript code fragments in tests */
public class ScriptEngineHelper {
private static ScriptEngine engine;
+ private static ScriptCache scriptCache = Mockito.mock(ScriptCache.class);
public static class Data extends HashMap<String, Object> {
}
@@ -62,6 +53,7 @@
if (engine == null) {
synchronized (ScriptEngineHelper.class) {
RhinoJavaScriptEngineFactory f = new RhinoJavaScriptEngineFactory();
+ Whitebox.setInternalState(f, "scriptCache", scriptCache);
f.activate(new RhinoMockComponentContext());
engine = f.getScriptEngine();
}
@@ -121,139 +113,15 @@
private static class RhinoMockComponentContext extends MockComponentContext {
+ private BundleContext bundleContext = Mockito.mock(BundleContext.class);
+
private RhinoMockComponentContext() {
super(new MockBundle(0));
}
@Override
public BundleContext getBundleContext() {
- return new BundleContext() {
-
- public void addBundleListener(BundleListener arg0) {
- // TODO Auto-generated method stub
-
- }
-
- public void addFrameworkListener(
- FrameworkListener arg0) {
- // TODO Auto-generated method stub
-
- }
-
- public void addServiceListener(ServiceListener arg0) {
- // TODO Auto-generated method stub
-
- }
-
- public void addServiceListener(
- ServiceListener arg0, String arg1)
- throws InvalidSyntaxException {
- // TODO Auto-generated method stub
-
- }
-
- public Filter createFilter(String arg0)
- throws InvalidSyntaxException {
- // TODO Auto-generated method stub
- return null;
- }
-
- public ServiceReference[] getAllServiceReferences(
- String arg0, String arg1)
- throws InvalidSyntaxException {
- // TODO Auto-generated method stub
- return null;
- }
-
- public Bundle getBundle() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public Bundle getBundle(long arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- public Bundle[] getBundles() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public File getDataFile(String arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- public String getProperty(String arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- public Object getService(ServiceReference arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- public ServiceReference getServiceReference(
- String arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- public ServiceReference[] getServiceReferences(
- String arg0, String arg1)
- throws InvalidSyntaxException {
- // TODO Auto-generated method stub
- return null;
- }
-
- public Bundle installBundle(String arg0)
- throws BundleException {
- // TODO Auto-generated method stub
- return null;
- }
-
- public Bundle installBundle(String arg0,
- InputStream arg1) throws BundleException {
- // TODO Auto-generated method stub
- return null;
- }
-
- public ServiceRegistration registerService(
- String[] arg0, Object arg1, Dictionary arg2) {
- // TODO Auto-generated method stub
- return null;
- }
-
- public ServiceRegistration registerService(
- String arg0, Object arg1, Dictionary arg2) {
- // TODO Auto-generated method stub
- return null;
- }
-
- public void removeBundleListener(BundleListener arg0) {
- // TODO Auto-generated method stub
-
- }
-
- public void removeFrameworkListener(
- FrameworkListener arg0) {
- // TODO Auto-generated method stub
-
- }
-
- public void removeServiceListener(
- ServiceListener arg0) {
- // TODO Auto-generated method stub
-
- }
-
- public boolean ungetService(ServiceReference arg0) {
- // TODO Auto-generated method stub
- return false;
- }
- };
+ return bundleContext;
}
}
}