incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r586431 - in /incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling: scripting/ scripting/engines/ slingservlets/
Date Fri, 19 Oct 2007 13:24:25 GMT
Author: fmeschbe
Date: Fri Oct 19 06:24:22 2007
New Revision: 586431

URL: http://svn.apache.org/viewvc?rev=586431&view=rev
Log:
SLING-64 Define simple ScriptEngine interface, make Rhino and Velocity into ScriptEngines
and add support for ScriptEngine in the ScriptResolver

Added:
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/RhinoJavasSriptEngine.java
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/VelocityTemplatesScriptEngine.java
Removed:
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/RhinoJavascriptServlet.java
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/slingservlets/VelocityTemplatesServlet.java
Modified:
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java

Added: incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java?rev=586431&view=auto
==============================================================================
--- incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java
(added)
+++ incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java
Fri Oct 19 06:24:22 2007
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.microsling.scripting;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Map;
+
+import org.apache.sling.microsling.api.exceptions.SlingException;
+
+public interface ScriptEngine {
+
+    static final String FILENAME = "sling.script_path";
+    static final String REQUEST = "sling.request";
+    static final String RESPONSE = "sling.response";
+    static final String RESOURCE = "sling.resource";
+
+    String[] getExtensions();
+
+    void eval(Reader script, Map<String, Object> props)
+            throws SlingException, IOException;
+
+}

Modified: incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java?rev=586431&r1=586430&r2=586431&view=diff
==============================================================================
--- incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java
(original)
+++ incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java
Fri Oct 19 06:24:22 2007
@@ -16,14 +16,30 @@
  */
 package org.apache.sling.microsling.scripting;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Map;
+
 import javax.jcr.Item;
 import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.microsling.api.Resource;
 import org.apache.sling.microsling.api.SlingRequestContext;
-import org.apache.sling.microsling.request.MicroslingRequestContext;
+import org.apache.sling.microsling.api.exceptions.SlingException;
+import org.apache.sling.microsling.scripting.engines.RhinoJavasSriptEngine;
+import org.apache.sling.microsling.scripting.engines.VelocityTemplatesScriptEngine;
 import org.apache.sling.microsling.scripting.helpers.ScriptFilenameBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,60 +60,128 @@
     private static final Logger log = LoggerFactory.getLogger(SlingScriptResolver.class);
 
     public static final String SCRIPT_BASE_PATH = "/sling/scripts";
-    
+
+    /**
+     * jcr:encoding
+     */
+    public static final String JCR_ENCODING = "jcr:encoding";
+
     private final ScriptFilenameBuilder scriptFilenameBuilder = new ScriptFilenameBuilder();
-    
+
+    private Map<String, ScriptEngine> scriptEngines;
+
+    public SlingScriptResolver() throws Exception {
+        scriptEngines = new HashMap<String, ScriptEngine>();
+        addScriptEngine(new RhinoJavasSriptEngine());
+        addScriptEngine(new VelocityTemplatesScriptEngine());
+    }
+
+    /**
+     *
+     * @param req
+     * @param scriptExtension
+     * @return <code>true</code> if a Script and a ScriptEngine to evaluate it
+     *      could be found. Otherwise <code>false</code> is returned.
+     * @throws ServletException
+     * @throws IOException
+     */
+    public boolean evaluateScript(HttpServletRequest req,HttpServletResponse resp) throws
ServletException, IOException {
+        try {
+            final SlingRequestContext ctx = SlingRequestContext.Accessor.getSlingRequestContext(req);
+
+            Script script = resolveScript(ctx, req.getMethod());
+            if (script == null) {
+                return false;
+            }
+
+            // prepare the properties for the script
+            Map<String, Object> props = new HashMap<String, Object>();
+            props.put(ScriptEngine.FILENAME, script.getScriptPath());
+            props.put(ScriptEngine.REQUEST, req);
+            props.put(ScriptEngine.RESPONSE, resp);
+            props.put(ScriptEngine.RESOURCE, ctx.getResource());
+
+            // evaluate the script now using the ScriptEngine
+            script.getScriptEngine().eval(script.getScriptSource(), props);
+
+            return true;
+
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (ServletException se) {
+            throw se;
+        } catch (Exception e) {
+            throw new SlingException("Cannot get Script: " + e.getMessage(), e);
+        }
+    }
+
     /** Try to find a script Node that can process the given request, based on the
      *  rules defined above.
      *  @return null if not found.
      */
-    public Node resolveScript(HttpServletRequest req,String scriptExtension) throws Exception
{
+    private Script resolveScript(final SlingRequestContext ctx, final String method) throws
Exception {
 
-        final SlingRequestContext ctx = SlingRequestContext.Accessor.getSlingRequestContext(req);
         final Resource r = ctx.getResource();
         final Session s = ctx.getSession();
-        Node result = null;
+        Script result = null;
 
         if(r==null) {
             return null;
         }
 
         final String scriptFilename = scriptFilenameBuilder.buildScriptFilename(
-                req.getMethod(),
-                ctx.getRequestPathInfo().getSelectorString(),
-                ctx.getResponseContentType(),
-                scriptExtension
+            method,
+            ctx.getRequestPathInfo().getSelectorString(),
+            ctx.getResponseContentType(),
+            "*"
         );
 
+        // this is the location of the trailing asterisk
+        final int scriptExtensionOffset = scriptFilename.length() - 1;
+
         final String scriptPath =
             SCRIPT_BASE_PATH
             + "/"
             + r.getResourceType()
-            + "/"
-            + scriptFilename
-        ;
+            ;
 
         if(s.itemExists(scriptPath)) {
+
+            // get the item and ensure it is a node
             final Item i = s.getItem(scriptPath);
-            if(i instanceof Node) {
-                result = (Node)i;
+            if (i.isNode()) {
+                return null;
             }
 
-            // the node must be (an extension of) nt:file
-            if (!result.isNodeType("nt:file")) {
-                result = null;
+            Node parent = (Node) i;
+            NodeIterator scriptNodeIterator = parent.getNodes(scriptFilename);
+            while (scriptNodeIterator.hasNext()) {
+                Node scriptNode = scriptNodeIterator.nextNode();
+
+                String scriptName = scriptNode.getName();
+                String scriptExt = scriptName.substring(scriptExtensionOffset);
+                ScriptEngine scriptEngine = scriptEngines.get(scriptExt);
+
+                if (scriptEngine != null) {
+                    Script script = new Script();
+                    script.setNode(scriptNode);
+                    script.setScriptPath(scriptNode.getPath());
+                    script.setScriptEngine(scriptEngine);
+                    result = script;
+                    break;
+                }
             }
         }
 
         if(result!=null) {
-            log.info("Found nt:file script node " + result.getPath() + " for Resource=" +
r);
+            log.info("Found nt:file script node " + result.getScriptPath() + " for Resource="
+ r);
         } else {
             log.debug("nt:file script node not found at path=" + scriptPath + " for Resource="
+ r);
         }
 
         return result;
     }
-    
+
     protected String filterStringForFilename(String inputString) {
         final StringBuffer sb = new StringBuffer();
         final String str = inputString.toLowerCase();
@@ -110,5 +194,118 @@
             }
         }
         return sb.toString();
+    }
+
+    private void addScriptEngine(ScriptEngine scriptEngine) {
+        String[] extensions = scriptEngine.getExtensions();
+        for (String extension : extensions) {
+            scriptEngines.put(extension, scriptEngine);
+        }
+    }
+
+    private static class Script {
+        private Node node;
+        private String scriptPath;
+        private ScriptEngine scriptEngine;
+
+        /**
+         * @return the node
+         */
+        Node getNode() {
+            return node;
+        }
+        /**
+         * @param node the node to set
+         */
+        void setNode(Node node) {
+            this.node = node;
+        }
+        /**
+         * @return the scriptPath
+         */
+        String getScriptPath() {
+            return scriptPath;
+        }
+        /**
+         * @param scriptPath the scriptPath to set
+         */
+        void setScriptPath(String scriptPath) {
+            this.scriptPath = scriptPath;
+        }
+        /**
+         * @return the scriptEngine
+         */
+        ScriptEngine getScriptEngine() {
+            return scriptEngine;
+        }
+        /**
+         * @param scriptEngine the scriptEngine to set
+         */
+        void setScriptEngine(ScriptEngine scriptEngine) {
+            this.scriptEngine = scriptEngine;
+        }
+
+        /**
+         * @return The script stored in the node as a Reader
+         */
+        Reader getScriptSource() throws RepositoryException, IOException {
+
+            // resolve the script node to a property or fail if not possible
+            // in case of a nt:file/nt:resource node tuple, the nt:file node
+            // will first resolve to the jcr:content child node of type
+            // nt:resource which in turn will resolve to the jcr:data node.
+            // Other node types may be defined similarly. Using the primary item
+            // prevents restricting this code to nt:file/nt:resource nodes
+            Item item = getNode();
+            while (item.isNode()) {
+                item = ((Node) item).getPrimaryItem();
+            }
+
+            // if we end the loop without being thrown, the item is now a
+            // Property, which we have to check whether it is multivalued
+            Property property = (Property) item;
+            Value value = null;
+            if (property.getDefinition().isMultiple()) {
+                // for a multi-valued property, we take the first non-null
+                // value (null values are possible in multi-valued properties)
+                // TODO: verify this claim ...
+                Value[] values = property.getValues();
+                for (Value candidateValue : values) {
+                    if (candidateValue != null) {
+                        value = candidateValue;
+                        break;
+                    }
+                }
+
+                // incase we could not find a non-null value, we bail out
+                if (value == null) {
+                    throw new IOException("Cannot access " + getScriptPath());
+                }
+            } else {
+                // for single-valued properties, we just take this value
+                value = property.getValue();
+            }
+
+            // Now know how to get the input stream, we still have to decide
+            // on the encoding of the stream's data. Primarily we assume it is
+            // UTF-8, which is a default in many places in JCR. Secondarily
+            // we try to get a jcr:encoding property besides the data property
+            // to provide a possible encoding
+            String encoding = "UTF-8";
+            try {
+                Node parent = property.getParent();
+                if (parent.hasNode(JCR_ENCODING)) {
+                    encoding = parent.getProperty(JCR_ENCODING).getString();
+                }
+            } catch (RepositoryException re) {
+                // don't care if we fail for any reason here, just assume default
+            }
+
+            // access the value as a stream and return a buffered reader
+            // converting the stream data using UTF-8 encoding, which is
+            // the default encoding used
+            InputStream input = value.getStream();
+            return new BufferedReader(new InputStreamReader(input, encoding));
+        }
     }
 }

Added: incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/RhinoJavasSriptEngine.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/RhinoJavasSriptEngine.java?rev=586431&view=auto
==============================================================================
--- incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/RhinoJavasSriptEngine.java
(added)
+++ incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/RhinoJavasSriptEngine.java
Fri Oct 19 06:24:22 2007
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.microsling.scripting.engines;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.sling.microsling.api.exceptions.SlingException;
+import org.apache.sling.microsling.scripting.ScriptEngine;
+import org.apache.sling.microsling.scripting.helpers.EspReader;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptableObject;
+
+/**
+ * A SlingServlet that uses the Rhino interpreter to process Sling request with
+ * server-side javascript.
+ */
+public class RhinoJavasSriptEngine implements ScriptEngine {
+
+    public final static String JS_SCRIPT_EXTENSION = "js";
+
+    public final static String ESP_SCRIPT_EXTENSION = "esp";
+
+    public String[] getExtensions() {
+        return new String[] { JS_SCRIPT_EXTENSION, ESP_SCRIPT_EXTENSION };
+    }
+
+    public void eval(Reader scriptReader, Map<String, Object> props)
+            throws SlingException, IOException {
+
+        String scriptName = (String) props.get(FILENAME);
+        if (scriptName == null || scriptName.length() == 0) {
+            throw new SlingException("Missing Script for JavaScript execution");
+        }
+
+        // wrap the reader in an EspReader for ESP scripts
+        if (scriptName.endsWith(ESP_SCRIPT_EXTENSION)) {
+            scriptReader = new EspReader(scriptReader);
+        }
+
+        // create a rhino Context and execute the script
+        try {
+            final Context rhinoContext = Context.enter();
+            final ScriptableObject scope = rhinoContext.initStandardObjects();
+
+            // add initial properties to the scope
+            for (Entry<String, Object> entry : props.entrySet()) {
+                Object wrapped = Context.javaToJS(entry.getValue(), scope);
+                ScriptableObject.putProperty(scope, entry.getKey(), wrapped);
+            }
+
+            final int lineNumber = 1;
+            final Object securityDomain = null;
+
+            rhinoContext.evaluateReader(scope, scriptReader, scriptName,
+                lineNumber, securityDomain);
+
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (Throwable t) {
+            throw new SlingException("Failure running script " + scriptName, t);
+        } finally {
+            Context.exit();
+        }
+    }
+}

Added: incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/VelocityTemplatesScriptEngine.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/VelocityTemplatesScriptEngine.java?rev=586431&view=auto
==============================================================================
--- incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/VelocityTemplatesScriptEngine.java
(added)
+++ incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/VelocityTemplatesScriptEngine.java
Fri Oct 19 06:24:22 2007
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.microsling.scripting.engines;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.microsling.api.exceptions.HttpStatusCodeException;
+import org.apache.sling.microsling.api.exceptions.SlingException;
+import org.apache.sling.microsling.scripting.ScriptEngine;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * A SlingServlet that uses Velocity templates to render a Resource in HTML.
+ */
+public class VelocityTemplatesScriptEngine implements ScriptEngine {
+
+    public final static String VELOCITY_SCRIPT_EXTENSION = "vlt";
+
+    private final VelocityEngine velocity;
+
+    public VelocityTemplatesScriptEngine() throws Exception {
+        velocity = new VelocityEngine();
+        velocity.init();
+    }
+
+    public String[] getExtensions() {
+        return new String[] { VELOCITY_SCRIPT_EXTENSION };
+    }
+
+    public void eval(Reader script, Map<String, Object> props)
+            throws SlingException, IOException {
+
+        // ensure get method
+        HttpServletRequest request = (HttpServletRequest) props.get(REQUEST);
+        if (!"GET".equals(request.getMethod())) {
+            throw new HttpStatusCodeException(
+                HttpServletResponse.SC_METHOD_NOT_ALLOWED,
+                "Velocity templates only support GET requests");
+        }
+
+        // initialize the Velocity context
+        final VelocityContext c = new VelocityContext();
+        for (Entry<String, Object> entry : props.entrySet()) {
+            c.put(entry.getKey(), entry.getValue());
+        }
+
+        // let Velocity evaluate the script, and send the output to the browser
+        final String logTag = getClass().getSimpleName();
+        try {
+            Writer w = ((HttpServletResponse) props.get(RESPONSE)).getWriter();
+            velocity.evaluate(c, w, logTag, script);
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (Throwable t) {
+            String scriptName = (String) props.get(FILENAME);
+            throw new SlingException("Failure running script " + scriptName, t);
+        }
+    }
+}



Mime
View raw message