You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2008/09/26 17:42:13 UTC

svn commit: r699372 - in /incubator/sling/trunk/scripting/javascript/src/main: java/org/apache/sling/scripting/javascript/ java/org/apache/sling/scripting/javascript/helper/ java/org/apache/sling/scripting/javascript/internal/ resources/META-INF/services/

Author: fmeschbe
Date: Fri Sep 26 08:42:12 2008
New Revision: 699372

URL: http://svn.apache.org/viewvc?rev=699372&view=rev
Log:
SLING-676 Move JavaScriptEngineFactory to an internal package (for
SLING-634) and ensure proper cleanup when stopping the bundle. In
addition the factory is now a proper OSGi service because it needs
more control of its own lifecycle and for SLING-634 needs to be
able to get the host object providers.

Added:
    incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
      - copied, changed from r688978, incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/RhinoJavaScriptEngineFactory.java
Removed:
    incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/RhinoJavaScriptEngine.java
    incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/RhinoJavaScriptEngineFactory.java
    incubator/sling/trunk/scripting/javascript/src/main/resources/META-INF/services/
Modified:
    incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/helper/SlingWrapFactory.java

Modified: incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/helper/SlingWrapFactory.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/helper/SlingWrapFactory.java?rev=699372&r1=699371&r2=699372&view=diff
==============================================================================
--- incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/helper/SlingWrapFactory.java (original)
+++ incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/helper/SlingWrapFactory.java Fri Sep 26 08:42:12 2008
@@ -29,15 +29,13 @@
 
 public class SlingWrapFactory extends WrapFactory {
 
-    public static final SlingWrapFactory INSTANCE = new SlingWrapFactory();
-    
     /** List of classes that must not be wrapped (added for SLING-382) */
     private static final Class<?>[] EXCLUDED_CLASSES = {};
 
     /** default log */
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    private Map<Class<?>, String> wrappers = new HashMap<Class<?>, String>();
+    private final Map<Class<?>, String> wrappers = new HashMap<Class<?>, String>();
 
     /**
      * @param cx the current Context for this thread

Copied: incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java (from r688978, incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/RhinoJavaScriptEngineFactory.java)
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java?p2=incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java&p1=incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/RhinoJavaScriptEngineFactory.java&r1=688978&r2=699372&rev=699372&view=diff
==============================================================================
--- incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/RhinoJavaScriptEngineFactory.java (original)
+++ incubator/sling/trunk/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java Fri Sep 26 08:42:12 2008
@@ -16,14 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.scripting.javascript;
+package org.apache.sling.scripting.javascript.internal;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.script.ScriptEngine;
 
 import org.apache.sling.scripting.api.AbstractScriptEngineFactory;
+import org.apache.sling.scripting.javascript.RhinoHostObjectProvider;
+import org.apache.sling.scripting.javascript.SlingWrapper;
 import org.apache.sling.scripting.javascript.helper.SlingContextFactory;
 import org.apache.sling.scripting.javascript.helper.SlingWrapFactory;
-import org.apache.sling.scripting.javascript.helper.SlingWrapper;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableCalendar;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableItemMap;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableNode;
@@ -33,14 +38,27 @@
 import org.apache.sling.scripting.javascript.wrapper.ScriptableVersion;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableVersionHistory;
 import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.ImporterTopLevel;
+import org.mozilla.javascript.NativeJavaClass;
+import org.mozilla.javascript.NativeJavaPackage;
 import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.ScriptableObject;
 import org.mozilla.javascript.tools.debugger.ScopeProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * The <code>RhinoJavaScriptEngineFactory</code> TODO
+ * 
+ * @scr.component
+ * @scr.service interface="javax.script.ScriptEngineFactory"
+ * @scr.reference name="HostObjectProvider"
+ *                interface="org.apache.sling.scripting.javascript.RhinoHostObjectProvider"
+ *                cardinality="0..n" policy="dynamic"
+ *                bind="addHostObjectProvider" unbind="removeHostObjectProvider"
  */
 public class RhinoJavaScriptEngineFactory extends AbstractScriptEngineFactory
         implements ScopeProvider {
@@ -50,25 +68,103 @@
     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 */
     private final Logger log = LoggerFactory.getLogger(getClass());
-    
-    private final String languageVersion;
 
+    private String languageVersion;
+
+    private SlingWrapFactory wrapFactory;
+    
     private Scriptable rootScope;
 
-    public RhinoJavaScriptEngineFactory() {
+    private final Set<RhinoHostObjectProvider> hostObjectProvider = new HashSet<RhinoHostObjectProvider>();
+
+    public ScriptEngine getScriptEngine() {
+        return new RhinoJavaScriptEngine(this, getRootScope());
+    }
+
+    public String getLanguageName() {
+        return "ECMAScript";
+    }
+
+    public String getLanguageVersion() {
+        return languageVersion;
+    }
+
+    public Scriptable getScope() {
+        return getRootScope();
+    }
+
+    SlingWrapFactory getWrapFactory() {
+        return wrapFactory;
+    }
+    
+    @SuppressWarnings("unchecked")
+    private Scriptable getRootScope() {
+        if (rootScope == null) {
+
+            final Context rhinoContext = Context.enter();
+            try {
+                
+                Scriptable tmpScope = rhinoContext.initStandardObjects(
+                    new ImporterTopLevel(), false);
+    
+                // default 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());
+                }
+                
+                // only assign the root scope when complete set up
+                rootScope = tmpScope;
+                
+            } finally {
+                // ensure the context is exited after setting up the
+                // the new root scope
+                Context.exit();
+            }
+        }
+
+        return rootScope;
+    }
+
+    private void dropRootScope() {
+        
+        // ensure the debugger is closed if the root scope will
+        // be replaced to ensure no references to the old scope
+        // and context remain
+        ContextFactory contextFactory = ContextFactory.getGlobal();
+        if (contextFactory instanceof SlingContextFactory) {
+            ((SlingContextFactory) contextFactory).exitDebugger();
+        }
+        
+        // drop the scope
+        rootScope = null;
+    }
+    
+    // ---------- SCR integration
 
+    protected void activate(ComponentContext context) {
+        Dictionary<?, ?> props = context.getProperties();
+        boolean debugging = getProperty(
+            "org.apache.sling.scripting.javascript.debug", props,
+            context.getBundleContext(), false);
+
+        // setup the wrap factory
+        wrapFactory = new SlingWrapFactory();
+        
         // initialize the Rhino Context Factory
         SlingContextFactory.setup(this);
 
@@ -82,55 +178,111 @@
         setMimeTypes("text/javascript", "application/ecmascript",
             "application/javascript");
         setNames("javascript", ECMA_SCRIPT_EXTENSION, ESP_SCRIPT_EXTENSION);
-    }
 
-    public ScriptEngine getScriptEngine() {
-        return new RhinoJavaScriptEngine(this, getRootScope());
+        ContextFactory contextFactory = ContextFactory.getGlobal();
+        if (contextFactory instanceof SlingContextFactory) {
+            ((SlingContextFactory) contextFactory).setDebugging(debugging);
+        }
     }
 
-    public String getLanguageName() {
-        return "ECMAScript";
+    protected void deactivate(ComponentContext context) {
+
+        // remove the root scope
+        dropRootScope();
+        
+        // remove our context factory
+        SlingContextFactory.teardown();
+        
+        // remove references
+        wrapFactory = null;
+        hostObjectProvider.clear();
     }
 
-    public String getLanguageVersion() {
-        return languageVersion;
+    protected void addHostObjectProvider(RhinoHostObjectProvider provider) {
+        hostObjectProvider.add(provider);
+
+        if (rootScope != null) {
+            addHostObjects(rootScope, provider.getHostObjectClasses());
+        }
     }
 
-    public Scriptable getScope() {
-        return getRootScope();
+    protected void removeHostObjectProvider(RhinoHostObjectProvider provider) {
+        // remove the current root scope and have it recreated using the
+        // new host object classes
+        if (hostObjectProvider.remove(provider)) {
+            dropRootScope();
+        }
     }
 
-    private Scriptable getRootScope() {
-        if (rootScope == null) {
-            final Context rhinoContext = Context.enter();
-            rootScope = rhinoContext.initStandardObjects();
+    // ---------- internal
 
-            for (Class<?> clazz : HOSTOBJECT_CLASSES) {
+    private void addHostObjects(Scriptable scope,
+            Class<? extends Scriptable>[] classes) {
+        if (classes != null) {
+            for (Class<? extends Scriptable> clazz : classes) {
                 try {
 
                     // register the host object
-                    ScriptableObject.defineClass(rootScope, clazz);
-                    final ScriptableObject host = (ScriptableObject) clazz.newInstance();
+                    ScriptableObject.defineClass(scope, clazz);
 
                     if (SlingWrapper.class.isAssignableFrom(clazz)) {
+                        
                         // SlingWrappers can map to several classes if needed
-                        final SlingWrapper hostWrapper = (SlingWrapper) host;
+                        final SlingWrapper hostWrapper = (SlingWrapper) clazz.newInstance();;
                         for (Class<?> c : hostWrapper.getWrappedClasses()) {
-                            SlingWrapFactory.INSTANCE.registerWrapper(c,
+                            getWrapFactory().registerWrapper(c,
                                 hostWrapper.getClassName());
                         }
+                        
                     } else {
-                        // but other ScriptableObjects need to be registered as
-                        // well
-                        SlingWrapFactory.INSTANCE.registerWrapper(
+                        
+                        // but other Scriptable host objects need to be
+                        // registered as well
+                        final Scriptable host = clazz.newInstance();
+                        getWrapFactory().registerWrapper(
                             host.getClass(), host.getClassName());
+                        
                     }
+                    
                 } catch (Throwable t) {
-                    log.warn("getRootScope: Cannot prepare host object " + clazz, t);
+                    log.warn("addHostObjects: Cannot prepare host object "
+                        + clazz, t);
                 }
             }
         }
+    }
 
-        return rootScope;
+    private void addImportedClasses(Context cx, Scriptable scope,
+            Class<?>[] classes) {
+        if (classes != null && classes.length > 0) {
+            NativeJavaClass[] np = new NativeJavaClass[classes.length];
+            for (int i = 0; i < classes.length; i++) {
+                np[i] = new NativeJavaClass(scope, classes[i]);
+            }
+            ScriptableObject.callMethod(cx, scope, "importClass", np);
+        }
+    }
+
+    private void addImportedPackages(Context cx, Scriptable scope,
+            String[] packages) {
+        if (packages != null && packages.length > 0) {
+            NativeJavaPackage[] np = new NativeJavaPackage[packages.length];
+            for (int i = 0; i < packages.length; i++) {
+                np[i] = new NativeJavaPackage(packages[i]);
+            }
+            ScriptableObject.callMethod(cx, scope, "importPackage", np);
+        }
+    }
+
+    private boolean getProperty(String name, Dictionary<?, ?> props,
+            BundleContext bundleContext, boolean defaultValue) {
+        Object value = props.get(name);
+        if (value == null) {
+            value = bundleContext.getProperty(name);
+        }
+
+        return (value != null)
+                ? Boolean.parseBoolean(String.valueOf(value))
+                : defaultValue;
     }
 }