r9571 - in helma-ng/trunk: modules/helma src/org/helma/javascript src/org/helma/tools src/org/helma/web

[email protected]
Newsgroups gmane.comp.java.helma.cvs
Message-ID <20090403101335.86BDC3D0D6@mia>
Author: hannes
Date: 2009-04-03 12:13:35 +0200 (Fri, 03 Apr 2009)
New Revision: 9571

Modified:
   helma-ng/trunk/modules/helma/system.js
   helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java
   helma-ng/trunk/src/org/helma/tools/HelmaRunner.java
   helma-ng/trunk/src/org/helma/web/HelmaServlet.java
Log:
Finish implementation of sandboxed engines, plus some mild refactoring.

Details at http://dev.helma.org/trac/helma/changeset/9571

Modified: helma-ng/trunk/modules/helma/system.js
===================================================================
--- helma-ng/trunk/modules/helma/system.js	2009-04-03 10:13:32 UTC (rev 9570)
+++ helma-ng/trunk/modules/helma/system.js	2009-04-03 10:13:35 UTC (rev 9571)
@@ -7,6 +7,7 @@
 
 export('addHostObject',
         'addRepository',
+        'createSandbox',
         'evaluate',
         'extendJavaClass',
         'getRepositories',
@@ -16,6 +17,7 @@
         'args');
 
 var log = helma.logging.getLogger(__name__);
+var omj = org.mozilla.javascript;
 
 // mark this module as shared between all requests
 var __shared__ = true;
@@ -24,6 +26,33 @@
     getRhinoEngine().defineHostClass(javaClass);
 }
 
+/**
+ * Create a sandboxed scripting engine with the same install directory as this and the
+ * given module paths, global properties, class shutter and sealing
+ * @param modulePath the comma separated module search path
+ * @param globals a map of predefined global properties, may be null
+ * @param shutter a Rhino class shutter, may be null
+ * @param sealed if the global object should be sealed, defaults to false
+ * @return a sandboxed RhinoEngine instance
+ * @throws FileNotFoundException if any part of the module paths does not exist
+ */
+function createSandbox(modulePath, globals, shutter, sealed) {
+    if (shutter) {
+        if (!(shutter instanceof omj.ClassShutter)) {
+            shutter = new omj.ClassShutter(shutter);
+        }
+    } else {
+        shutter = null;
+    }
+    sealed = Boolean(sealed);
+    return getRhinoEngine().createSandbox(modulePath, globals, shutter, sealed);
+}
+
+/**
+ * Get a wrapper around a java class that can be extended in javascript using
+ * the ClassName.prototype property
+ * @param javaClass a fully qualified java class name
+ */
 function extendJavaClass(javaClass) {
     return getRhinoEngine().getExtendedClass(javaClass);
 }
@@ -74,8 +103,7 @@
  * Get the org.mozilla.javascript.Context associated with the current thread.
  */
 function getRhinoContext() {
-    var Context = org.mozilla.javascript.Context;
-    return Context.getCurrentContext();
+    return omj.Context.getCurrentContext();
 }
 
 /**

Modified: helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java
===================================================================
--- helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java	2009-04-03 10:13:32 UTC (rev 9570)
+++ helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java	2009-04-03 10:13:35 UTC (rev 9571)
@@ -25,6 +25,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.FileNotFoundException;
 import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
 import java.net.MalformedURLException;
@@ -38,7 +39,7 @@
  */
 public class RhinoEngine {
 
-    HelmaConfiguration                 configuration;
+    HelmaConfiguration                 config;
     List<Repository>                   repositories;
     ScriptableObject                   topLevelScope;
     List<String>                       commandLineArgs;
@@ -60,9 +61,10 @@
      * Create a RhinoEngine which loads scripts from directory <code>dir</code>
      * and defines the given classes as native host objects.
      * @param config the configuration used to initialize the engine.
+     * @param globals an optional map of predefined global properties
      */
-    public RhinoEngine(HelmaConfiguration config) {
-        this.configuration = config;
+    public RhinoEngine(HelmaConfiguration config, Map<String, Object> globals) {
+        this.config = config;
         contextFactory = new HelmaContextFactory(this, config);
         this.repositories = config.getRepositories();
         if (repositories.isEmpty()) {
@@ -70,6 +72,7 @@
         }
         // create a new global scope level
         Context cx = contextFactory.enterContext();
+        Object[] threadLocals = checkThreadLocals(cx);
         try {
             if (config.getClassShutter() != null) {
                 cx.setClassShutter(config.getClassShutter());
@@ -88,6 +91,12 @@
             ScriptableObject.defineClass(topLevelScope, ScriptableWrapper.class);
             ScriptableObject.defineProperty(topLevelScope, "__name__", "global",
                     ScriptableObject.DONTENUM);
+            if (globals != null) {
+                for (Map.Entry<String, Object> entry : globals.entrySet()) {
+                    ScriptableObject.defineProperty(topLevelScope, entry.getKey(),
+                            entry.getValue(), ScriptableObject.DONTENUM);
+                }
+            }
             evaluate(cx, getScript("global"), topLevelScope);
             if (config.isSealed()) {
                 topLevelScope.sealObject();
@@ -96,6 +105,7 @@
             throw new IllegalArgumentException("Error initializing engine", x);
         } finally {
             Context.exit();
+            resetThreadLocals(cx, threadLocals);
         }
     }
 
@@ -128,9 +138,10 @@
      * @throws JavaScriptException the script threw an error during
      *         compilation or execution
      */
-    public Object runScript(String scriptName, String[] scriptArgs)
+    public Object runScript(String scriptName, String... scriptArgs)
             throws IOException, JavaScriptException {
         Context cx = contextFactory.enterContext();
+        Object[] threadLocals = checkThreadLocals(cx);
         try {
         	Object retval;
             Map<Trackable,ReloadableScript> scripts = cx.getOptimizationLevel() == -1 ?
@@ -157,6 +168,7 @@
         	return retval;
         } finally {
         	Context.exit();
+            resetThreadLocals(cx, threadLocals);
         }   	
     }
     
@@ -175,10 +187,11 @@
     public Object invoke(String moduleName, String method, Object... args)
             throws IOException, NoSuchMethodException {
         Context cx = contextFactory.enterContext();
+        Object[] threadLocals = checkThreadLocals(cx);
         try {
             initArguments(args);
             if (moduleName == null) {
-                moduleName = configuration.getMainModule("main");
+                moduleName = config.getMainModule("main");
             }
             Object retval;
             while (true) {
@@ -200,6 +213,7 @@
             return retval;
         } finally {
             Context.exit();
+            resetThreadLocals(cx, threadLocals);
         }
     }
 
@@ -210,6 +224,7 @@
      */
     public Scriptable getShellScope() throws IOException {
         Context cx = contextFactory.enterContext();
+        Object[] threadLocals = checkThreadLocals(cx);
         try {
             Repository repository = repositories.get(0);
             Resource resource = repository.getResource("<shell>");
@@ -223,6 +238,7 @@
             return scope;
         } finally {
             Context.exit();
+            resetThreadLocals(cx, threadLocals);
         }
     }
 
@@ -324,7 +340,7 @@
         return script;
     }
 
-    public Object evaluate(Context cx, ReloadableScript script, Scriptable scope)
+    protected Object evaluate(Context cx, ReloadableScript script, Scriptable scope)
             throws IOException {
         Object result;
         ReloadableScript parent = getCurrentScript(cx);
@@ -368,6 +384,25 @@
         return module;
     }
 
+    /**
+     * Create a sandboxed scripting engine with the same install directory as this and the
+     * given module paths, global properties, class shutter and sealing
+     * @param modulePath the comma separated module search path
+     * @param globals a map of predefined global properties, may be null
+     * @param shutter a Rhino class shutter, may be null
+     * @param sealed if the global object should be sealed, defaults to false
+     * @return a sandboxed RhinoEngine instance
+     * @throws FileNotFoundException if any part of the module paths does not exist
+     */
+    public RhinoEngine createSandbox(String modulePath, Map<String,Object> globals,
+                                     ClassShutter shutter, boolean sealed)
+            throws FileNotFoundException {
+        HelmaConfiguration sandbox = new HelmaConfiguration(config.getHelmaHome(), modulePath, null);
+        sandbox.setClassShutter(shutter);
+        sandbox.setSealed(sealed);
+        return new RhinoEngine(sandbox, globals);
+    }
+
     private ReloadableScript getCurrentScript(Context cx) {
         return (ReloadableScript) cx.getThreadLocal("current_script");
     }
@@ -376,7 +411,26 @@
         cx.putThreadLocal("current_script", script);
     }
 
+    private Object[] checkThreadLocals(Context cx) {
+        if (cx.getThreadLocal("engine") == this) {
+            return null;
+        }
+        Object[] retval = new Object[] {
+            cx.getThreadLocal("engine"),
+            cx.getThreadLocal("modules")
+        };
+        cx.putThreadLocal("engine", this);
+        cx.putThreadLocal("modules", new HashMap<Trackable, Scriptable>());
+        return retval;
+    }
 
+    private void resetThreadLocals(Context cx, Object[] objs) {
+        if (objs != null) {
+            cx.putThreadLocal("engine", objs[0]);
+            cx.putThreadLocal("modules", objs[1]);
+        }
+    }
+
     public ScriptableObject getTopLevelScope() {
         return topLevelScope;
     }
@@ -416,7 +470,7 @@
      * @return a list of all contained child resources
      */
     public List<Resource> findResources(String path, boolean recursive) {
-        return configuration.getResources(path, recursive);
+        return config.getResources(path, recursive);
     }
 
     /**
@@ -436,7 +490,7 @@
         } else if (path.startsWith(".")) {
             return localPath.getResource(path);
         } else {
-            return configuration.getResource(path);
+            return config.getResource(path);
         }
     }
 
@@ -460,7 +514,7 @@
                 return repository;
             }
         }
-        return configuration.getRepository(path);
+        return config.getRepository(path);
     }
 
     public void addToClasspath(Resource resource) throws MalformedURLException {

Modified: helma-ng/trunk/src/org/helma/tools/HelmaRunner.java
===================================================================
--- helma-ng/trunk/src/org/helma/tools/HelmaRunner.java	2009-04-03 10:13:32 UTC (rev 9570)
+++ helma-ng/trunk/src/org/helma/tools/HelmaRunner.java	2009-04-03 10:13:35 UTC (rev 9571)
@@ -68,7 +68,7 @@
         if (optlevel >= -1) {
             config.setOptLevel(optlevel);
         }
-        RhinoEngine engine = new RhinoEngine(config);
+        RhinoEngine engine = new RhinoEngine(config, null);
         if (scriptName != null) {
         	engine.runScript(scriptName, scriptArgs);
         }

Modified: helma-ng/trunk/src/org/helma/web/HelmaServlet.java
===================================================================
--- helma-ng/trunk/src/org/helma/web/HelmaServlet.java	2009-04-03 10:13:32 UTC (rev 9570)
+++ helma-ng/trunk/src/org/helma/web/HelmaServlet.java	2009-04-03 10:13:35 UTC (rev 9571)
@@ -66,24 +66,24 @@
         this.engine = engine;
     }
 
-    public void init(ServletConfig config) throws ServletException {
+    public void init(ServletConfig servletConfig) throws ServletException {
         // pool = Executors.newFixedThreadPool(8);
         pool = Executors.newCachedThreadPool();
-        moduleName = config.getInitParameter("moduleName");
+        moduleName = servletConfig.getInitParameter("moduleName");
         if (moduleName == null) {
             throw new ServletException("moduleName servlet parameter not defined");
         }
-        functionName = config.getInitParameter("functionName");
+        functionName = servletConfig.getInitParameter("functionName");
         if (functionName == null) {
             throw new ServletException("functionName servlet parameter not defined");
         }
-        String timeout = config.getInitParameter("requestTimeout");
+        String timeout = servletConfig.getInitParameter("requestTimeout");
         if (timeout != null) {
             requestTimeout = Integer.parseInt(timeout);
         }
         if (engine == null) {
             try {
-                String classNames = config.getInitParameter("hostClasses");
+                String classNames = servletConfig.getInitParameter("hostClasses");
                 Class[] classes = defaultHostClasses;
                 if (classNames != null) {
                     Class[] custom = StringUtils.toClassArray(classNames, ", ");
@@ -92,16 +92,16 @@
                     System.arraycopy(custom, 0, copy, classes.length, custom.length);
                     classes = copy;
                 }
-                String helmaHome = config.getInitParameter("helmaHome");
-                String modulePath = config.getInitParameter("modulePath");
+                String helmaHome = servletConfig.getInitParameter("helmaHome");
+                String modulePath = servletConfig.getInitParameter("modulePath");
                 Repository home = new FileRepository(helmaHome);
                 if (!home.exists()) {
-                    home = new WebappRepository(config.getServletContext(), helmaHome);
+                    home = new WebappRepository(servletConfig.getServletContext(), helmaHome);
                 }
-                HelmaConfiguration conf =
+                HelmaConfiguration config =
                         new HelmaConfiguration(home, modulePath, "modules");
-                conf.setHostClasses(classes);
-                engine = new RhinoEngine(conf);
+                config.setHostClasses(classes);
+                engine = new RhinoEngine(config, null);
             } catch (ClassNotFoundException x) {
                 throw new ServletException(x);
             } catch (FileNotFoundException x) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.