r9632 - in helma-ng/trunk/src/org/helma: javascript repository tools

[email protected] Mon, 20 Apr 2009 18:31:16 +0200 (CEST)
Newsgroups gmane.comp.java.helma.cvs
Message-ID <20090420163116.1FB643D0D6@mia>
Author: hannes
Date: 2009-04-20 18:31:15 +0200 (Mon, 20 Apr 2009)
New Revision: 9632

Modified:
   helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java
   helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java
   helma-ng/trunk/src/org/helma/repository/AbstractRepository.java
   helma-ng/trunk/src/org/helma/repository/AbstractResource.java
   helma-ng/trunk/src/org/helma/repository/FileRepository.java
   helma-ng/trunk/src/org/helma/repository/FileResource.java
   helma-ng/trunk/src/org/helma/repository/Repository.java
   helma-ng/trunk/src/org/helma/repository/Resource.java
   helma-ng/trunk/src/org/helma/repository/Trackable.java
   helma-ng/trunk/src/org/helma/repository/WebappRepository.java
   helma-ng/trunk/src/org/helma/repository/ZipRepository.java
   helma-ng/trunk/src/org/helma/repository/ZipResource.java
   helma-ng/trunk/src/org/helma/tools/HelmaConfiguration.java
Log:
Fix several issues in resource framework (trac ticket #26)
 * Add new methods in Resource/Repository/Trackable interfaces to
   mark root repositories and get relative repository paths
 * Disallow escaping repository roots via ".." paths
 * Provide modules a method to retrieve canonical module names
 * Allow "." to be resolved across root repositories
 * Add new resolveRootRepository() method in HelmaConfiguration to
   make it easier to add repositories programmatically

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

Modified: helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java
===================================================================
--- helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/javascript/ReloadableScript.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -34,6 +34,7 @@
 
     final Trackable source;
     final RhinoEngine engine;
+    final String moduleName;
     // the checksum of the underlying resource or repository when
     // the script was last compiled
     long checksum = -1;
@@ -60,6 +61,7 @@
     public ReloadableScript(Trackable source, RhinoEngine engine) {
         this.source = source;
         this.engine = engine;
+        moduleName = source.getModuleName();
     }
 
     /**
@@ -101,7 +103,7 @@
         Resource resource = (Resource) source;
         try {
             exception = null;
-            script = cx.compileReader(resource.getReader(), resource.getPath(), 1, null);
+            script = cx.compileReader(resource.getReader(), resource.getRelativePath(), 1, null);
         } catch (Exception x) {
             exception = x;
         } finally {
@@ -129,7 +131,7 @@
             exception = null;
             for (Resource res: resources) {
                 if (res.getName().endsWith(".js")) {
-                    scripts.add(cx.compileReader(res.getReader(), res.getPath(), 1, null));
+                    scripts.add(cx.compileReader(res.getReader(), res.getRelativePath(), 1, null));
                 }
            }
         } catch (Exception x) {
@@ -180,13 +182,12 @@
      * Get a module scope loaded with this script
      *
      * @param prototype the prototype for the module, usually the shared top level scope
-     * @param moduleName the module name
      * @param cx the rhino context
      * @return a new module scope
      * @throws JavaScriptException if an error occurred evaluating the script file
      * @throws IOException if an error occurred reading the script file
      */
-    protected synchronized Scriptable load(Scriptable prototype, String moduleName, Context cx)
+    protected synchronized Scriptable load(Scriptable prototype, Context cx)
             throws JavaScriptException, IOException {
         // check if we already came across the module in the current context/request
         Map<Trackable,Scriptable> modules = (Map<Trackable,Scriptable>) cx.getThreadLocal("modules");

Modified: helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java
===================================================================
--- helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/javascript/RhinoEngine.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -148,7 +148,7 @@
                     interpretedScripts : compiledScripts;
             commandLineArgs = Arrays.asList(scriptArgs);
             Resource resource = findResource(scriptName, null);
-            if (!resource.exists()) {
+            if (resource == null || !resource.exists()) {
                 resource = new FileResource(new File(scriptName));
             }
             if (!resource.exists()) {
@@ -389,7 +389,7 @@
         ReloadableScript parent = getCurrentScript(cx);
         try {
             setCurrentScript(cx, script);
-            module = script.load(topLevelScope, moduleName, cx);
+            module = script.load(topLevelScope, cx);
         } finally {
             if (parent != null) {
                 parent.addDependency(script);
@@ -491,10 +491,10 @@
     /**
      * Search for a resource in a local path, or the main repository path.
      * @param path the resource name
-     * @param localPath a repository to look first
+     * @param localRoot a repository to look first
      * @return the resource
      */
-    public Resource findResource(String path, Repository localPath) {
+    public Resource findResource(String path, Repository localRoot) {
         // FIXME: we allow absolute module paths, and we do not check
         // if relative paths escape their root repository. These are
         // of course gaping security holes for environments running
@@ -502,8 +502,8 @@
         File file = new File(path);
         if (file.isAbsolute()) {
             return new FileResource(file);
-        } else if (localPath != null && path.startsWith(".")) {
-            return localPath.getResource(path);
+        } else if (localRoot != null && path.startsWith(".")) {
+            return findResource(localRoot.getRelativePath() + path, null);
         } else {
             return config.getResource(path);
         }

Modified: helma-ng/trunk/src/org/helma/repository/AbstractRepository.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/AbstractRepository.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/AbstractRepository.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -31,7 +31,7 @@
     /**
      * Parent repository this repository is contained in.
      */
-    Repository parent;
+    AbstractRepository parent;
 
     /**
      * Holds direct child repositories
@@ -91,6 +91,44 @@
     }
 
     /**
+     * Mark this repository as root repository.
+     */
+    public void setRoot() {
+        parent = null;
+    }
+
+    /**
+     * Get the path of this repository relative to its root repository.
+     *
+     * @return the repository path
+     */
+    public String getRelativePath() {
+        if (parent == null) {
+            return "";
+        } else {
+            StringBuffer b = new StringBuffer();
+            getRelativePath(b);
+            return b.toString();
+        }
+    }
+
+    protected void getRelativePath(StringBuffer buffer) {
+        if (parent != null) {
+            parent.getRelativePath(buffer);
+            buffer.append(name).append('/');
+        } 
+    }
+
+    /**
+     * Utility method to get the name for the module defined by this resource.
+     *
+     * @return the module name according to the securable module spec
+     */
+    public String getModuleName() {
+        return getRelativePath();
+    }
+
+    /**
      * Get a resource contained in this repository identified by the given local name.
      * If the name can't be resolved to a resource, a resource object is returned
      * for which {@link Resource exists()} returns <code>false<code>.
@@ -107,10 +145,10 @@
         }
         Repository repository = this;
         int i = 0;
-        while (i < subs.length - 1) {
+        while (repository != null && i < subs.length - 1) {
             repository = repository.getChildRepository(subs[i++]);
         }
-        return repository.getResource(subs[i]);
+        return repository == null ? null : repository.getResource(subs[i]);
     }
 
     /**
@@ -165,6 +203,8 @@
         return allResources;
     }
 
+
+
     /**
      * Returns the repositories full name as string representation.
      * @see {getName()}

Modified: helma-ng/trunk/src/org/helma/repository/AbstractResource.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/AbstractResource.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/AbstractResource.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -7,7 +7,7 @@
 
 public abstract class AbstractResource implements Resource {
 
-    protected Repository repository;
+    protected AbstractRepository repository;
     protected String path;
     protected String name;
     protected String baseName;
@@ -56,6 +56,38 @@
         return getContent(null);
     }
 
+    /**
+     * Get the path of this resource relative to its root repository.
+     *
+     * @return the relative resource path
+     */
+    public String getRelativePath() {
+        if (repository == null) {
+            return name;
+        } else {
+            StringBuffer b = new StringBuffer();
+            repository.getRelativePath(b);
+            b.append(name);
+            return b.toString();
+        }
+    }
+
+    /**
+     * Utility method to get the name for the module defined by this resource.
+     *
+     * @return the module name according to the securable module spec
+     */
+    public String getModuleName() {
+        if (repository == null) {
+            return baseName;
+        } else {
+            StringBuffer b = new StringBuffer();
+            repository.getRelativePath(b);
+            b.append(baseName);
+            return b.toString();
+        }
+    }
+
     public long getChecksum() {
         return lastModified();
     }

Modified: helma-ng/trunk/src/org/helma/repository/FileRepository.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/FileRepository.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/FileRepository.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -96,6 +96,11 @@
      * @return the child repository
      */
     public Repository getChildRepository(String name) {
+        if (".".equals(name)) {
+            return this;
+        } else if ("..".equals(name)) {
+            return parent;
+        }
         return new FileRepository(new File(directory, name), this);
     }
 

Modified: helma-ng/trunk/src/org/helma/repository/FileResource.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/FileResource.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/FileResource.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -20,20 +20,16 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 
-public class FileResource implements Resource {
+public class FileResource extends AbstractResource {
 
     File file;
-    Repository repository;
-    String path;
-    String name;
-    String baseName;
     boolean stripShebang = false;
 
     public FileResource(File file) {
         this(file, null);
     }
 
-    protected FileResource(File file, Repository repository) {
+    protected FileResource(File file, FileRepository repository) {
         // make sure our directory has an absolute path,
         // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4117557
         try {
@@ -52,18 +48,6 @@
         baseName = (lastDot == -1) ? name : name.substring(0, lastDot);
     }
 
-    public String getPath() {
-        return path;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getBaseName() {
-        return baseName;
-    }
-
     public InputStream getInputStream() throws IOException {
         InputStream stream = new FileInputStream(file);
         if (stripShebang) {
@@ -89,10 +73,6 @@
         return stream;
     }
 
-    public Reader getReader() throws IOException {
-        return new InputStreamReader(getInputStream());
-    }
-
     public URL getUrl() throws MalformedURLException {
         return new URL("file:" + file.getAbsolutePath());
     }
@@ -101,32 +81,6 @@
         return file.lastModified();
     }
 
-    public long getChecksum() {
-        return lastModified();
-    }
-
-    public String getContent(String encoding) throws IOException {
-        InputStream in = getInputStream();
-        int size = (int) file.length();
-        byte[] buf = new byte[size];
-        int read = 0;
-        while (read < size) {
-            int r = in.read(buf, read, size - read);
-            if (r == -1)
-                break;
-            read += r;
-        }
-        if (in != null)
-            in.close();
-        return encoding == null ?
-                new String(buf) :
-                new String(buf, encoding);
-    }
-
-    public String getContent() throws IOException {
-        return getContent(null);
-    }
-
     public long getLength() {
         return file.length();
     }

Modified: helma-ng/trunk/src/org/helma/repository/Repository.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/Repository.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/Repository.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -68,4 +68,15 @@
      */
     public Repository getChildRepository(String name);
 
+    /**
+     * Mark this repository as root repository.
+     */
+    public void setRoot();
+
+    /**
+     * Get the path of this repository relative to its root repository.
+     * @return the repository path
+     */
+    public String getRelativePath();
+
 }
\ No newline at end of file

Modified: helma-ng/trunk/src/org/helma/repository/Resource.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/Resource.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/Resource.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -69,4 +69,10 @@
      */
     public String getBaseName();
 
+    /**
+     * Get the path of this resource relative to its root repository.
+     * @return the relative resource path
+     */
+    public String getRelativePath();
+
 }

Modified: helma-ng/trunk/src/org/helma/repository/Trackable.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/Trackable.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/Trackable.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -54,4 +54,10 @@
      */
     public Repository getParentRepository();
 
+    /**
+     * Utility method to get the name for the module defined by this resource.
+     * @return the module name according to the securable module spec
+     */
+    public String getModuleName();
+
 }

Modified: helma-ng/trunk/src/org/helma/repository/WebappRepository.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/WebappRepository.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/WebappRepository.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -56,6 +56,11 @@
     }
 
     public Repository getChildRepository(String name) {
+        if (".".equals(name)) {
+            return this;
+        } else if ("..".equals(name)) {
+            return parent;
+        }        
         return new WebappRepository(context, this, name);
     }
 

Modified: helma-ng/trunk/src/org/helma/repository/ZipRepository.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/ZipRepository.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/ZipRepository.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -59,7 +59,7 @@
      * repository
      * @param file a zip file
      */
-    protected ZipRepository(File file, Repository parent) {
+    protected ZipRepository(File file, AbstractRepository parent) {
         this(file, parent, null);
     }
 
@@ -70,7 +70,7 @@
      * @param zipentry zip entryName
      * @param parent repository
      */
-    private ZipRepository(File file, Repository parent, ZipEntry zipentry) {
+    private ZipRepository(File file, AbstractRepository parent, ZipEntry zipentry) {
         // make sure our file has an absolute path,
         // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4117557
         if (file.isAbsolute()) {
@@ -222,6 +222,11 @@
      * @return the child repository
      */
     public Repository getChildRepository(String name) {
+        if (".".equals(name)) {
+            return this;
+        } else if ("..".equals(name)) {
+            return parent;
+        }
         return new ZipRepository(file, this, new ZipEntry(entryPath + "/" + name));
     }
 

Modified: helma-ng/trunk/src/org/helma/repository/ZipResource.java
===================================================================
--- helma-ng/trunk/src/org/helma/repository/ZipResource.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/repository/ZipResource.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -21,13 +21,9 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
-public final class ZipResource implements Resource {
+public final class ZipResource extends AbstractResource {
 
     private String entryName;
-    private ZipRepository repository;
-    private String path;
-    private String name;
-    private String baseName;
 
     protected ZipResource(String zipentryName, ZipRepository repository) {
         this.entryName = zipentryName;
@@ -55,7 +51,7 @@
     public InputStream getInputStream() throws IOException {
         ZipFile zipfile = null;
         try {
-            zipfile = repository.getZipFile();
+            zipfile = getZipFile();
             ZipEntry entry = zipfile.getEntry(entryName);
             if (entry == null) {
                 throw new IOException("Zip resource " + this + " does not exist");
@@ -81,14 +77,10 @@
         }
     }
 
-    public Reader getReader() throws IOException {
-        return new InputStreamReader(getInputStream());
-    }
-
     public boolean exists() {
         ZipFile zipfile = null;
         try {
-            zipfile = repository.getZipFile();
+            zipfile = getZipFile();
             return (zipfile.getEntry(entryName) != null);
         } catch (Exception ex) {
             return false;
@@ -104,7 +96,7 @@
     public String getContent(String encoding) throws IOException {
         ZipFile zipfile = null;
         try {
-            zipfile = repository.getZipFile();
+            zipfile = getZipFile();
             ZipEntry entry = zipfile.getEntry(entryName);
             if (entry == null) {
                 return "";
@@ -130,22 +122,6 @@
         }
     }
 
-    public String getContent() throws IOException {
-        return getContent(null);
-    }
-
-    public String getPath() {
-        return path;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getBaseName() {
-        return baseName;
-    }
-
     public URL getUrl() {
         // TODO: we might want to return a Jar URL
         // http://java.sun.com/j2se/1.5.0/docs/api/java/net/JarURLConnection.html
@@ -155,7 +131,7 @@
     public long getLength() {
         ZipFile zipfile = null;
         try {
-            zipfile = repository.getZipFile();
+            zipfile = getZipFile();
             return zipfile.getEntry(entryName).getSize();            
         } catch (Exception ex) {
             return 0;
@@ -186,4 +162,11 @@
     public String toString() {
         return getPath();
     }
+
+    private ZipFile getZipFile() throws IOException {
+        if (!(repository instanceof ZipRepository)) {
+            throw new IOException("Parent is not a ZipRepository: " + repository);
+        }
+        return ((ZipRepository) repository).getZipFile();
+    }
 }

Modified: helma-ng/trunk/src/org/helma/tools/HelmaConfiguration.java
===================================================================
--- helma-ng/trunk/src/org/helma/tools/HelmaConfiguration.java	2009-04-20 16:31:11 UTC (rev 9631)
+++ helma-ng/trunk/src/org/helma/tools/HelmaConfiguration.java	2009-04-20 16:31:15 UTC (rev 9632)
@@ -69,47 +69,66 @@
             String[] paths = StringUtils.split(modulePath, File.pathSeparator);
             for (int i = 0; i < paths.length; i++) {
                 String path = paths[i].trim();
-                Repository repo = home.getChildRepository(path);
-                if (repo.exists()) {
-                    repositories.add(repo);
-                    continue;
-                }
-                File file = new File(path);
-                if (!file.isAbsolute()) {
-                    // if path is relative, try to resolve against current directory first,
-                    // then relative to helma installation directory.
-                    file = file.getAbsoluteFile();
-                    if (!file.exists()) {
-                        file = new File(home.getPath(), path);
-                    }
-                }
-                if (!file.exists()) {
-                    throw new FileNotFoundException("File '" + file + "' does not exist.");
-                }
-                if (path.toLowerCase().endsWith(".zip")) {
-                    repositories.add(new ZipRepository(file));
+                Repository repository = resolveRootRepository(path);
+                if (repository != null && repository.exists()) {
+                    repositories.add(repository);
                 } else {
-                    if (i == 0 && file.isFile()) {
-                        Resource res = new FileResource(file);
-                        mainModule = res.getBaseName();
-                        repositories.add(res.getParentRepository());
-                    } else {
-                        repositories.add(new FileRepository(file));
-                    }
+                    getLogger().error("Cannot resolve module path entry: " + path);
                 }
             }
         }
 
         // append system modules path relative to helma home
         if (systemModules != null) {
-            Repository modules = home.getChildRepository(systemModules);
-            repositories.add(modules);
+            Repository repository = resolveRootRepository(systemModules);
+                if (repository != null && repository.exists()) {
+                    repositories.add(repository);
+                } else {
+                    getLogger().error("Cannot resolve system module root: " + systemModules);
+                }
         }
 
-        Logger.getLogger("org.helma.tools").debug("Parsed repository list: " + repositories);
+        getLogger().debug("Parsed repository list: " + repositories);
     }
 
     /**
+     * Resolve a module repository path.
+     * @param path the path
+     * @return a repository
+     * @throws FileNotFoundException if the path couldn't be resolved
+     */
+    public Repository resolveRootRepository(String path) throws FileNotFoundException {
+        Repository repository = home.getChildRepository(path);
+        if (repository != null && repository.exists()) {
+            repository.setRoot();
+            return repository;
+        }
+        File file = new File(path);
+        if (!file.isAbsolute()) {
+            // if path is relative, try to resolve against current directory first,
+            // then relative to helma installation directory.
+            file = file.getAbsoluteFile();
+            if (!file.exists()) {
+                file = new File(home.getPath(), path);
+            }
+        }
+        if (!file.exists()) {
+            throw new FileNotFoundException("File '" + file + "' does not exist.");
+        }
+        if (path.toLowerCase().endsWith(".zip")) {
+            return new ZipRepository(file);
+        } else {
+            if (file.isFile()) {
+                Resource res = new FileResource(file);
+                mainModule = res.getBaseName();
+                return res.getParentRepository();
+            } else {
+                return new FileRepository(file);
+            }
+        }
+    }
+
+    /**
      * If the scriptName argument is not null, prepend the script's parent repository
      * to the module path. Otherwise, prepend the current working directory to the module path.
      * @param scriptName the name of the script, or null.
@@ -216,7 +235,7 @@
     public Resource getResource(String path) {
         for (Repository repo: repositories) {
             Resource res = repo.getResource(path);
-            if (res.exists()) {
+            if (res != null && res.exists()) {
                 return res;
             }
         }
@@ -269,4 +288,8 @@
         this.sealed = sealed;
     }
 
+    private Logger getLogger() {
+        return Logger.getLogger("org.helma.tools");
+    }
+
 }
\ No newline at end of file