You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2017/09/06 13:24:25 UTC

svn commit: r1807480 [2/5] - in /sling/whiteboard/microsling: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/sling/ src/main/java/org/apache/sling/microsling/ src/main/java/org/apache/sling/micros...

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,538 @@
+/*
+ * 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.helpers.servlets;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.sling.microsling.api.exceptions.HttpStatusCodeException;
+import org.apache.sling.microsling.helpers.constants.HttpConstants;
+
+/**
+ * Helper base class for read-only Servlets used in Sling. This base class is
+ * actually just a better implementation of the Servlet API <em>HttpServlet</em>
+ * class which accounts for extensibility. So extensions of this class have
+ * great control over what methods to overwrite.
+ * <p>
+ * If any of the default HTTP methods is to be implemented just overwrite the
+ * respective doXXX method. If additional methods should be supported implement
+ * appropriate doXXX methods and overwrite the
+ * {@link #mayService(HttpServletRequest, HttpServletResponse)} method to
+ * dispatch to the doXXX methods as appropriate and overwrite the
+ * {@link #getAllowedRequestMethods(Set)} to add the new method names.
+ * <p>
+ * Please note, that this base class is intended for applications where data is
+ * only read. As such, this servlet by itself does not support the <em>POST</em>,
+ * <em>PUT</em> and <em>DELETE</em> methods. Extensions of this class should
+ * either overwrite any of the doXXX methods of this class or add support for
+ * other read-only methods only. Applications wishing to support data
+ * modification should rather use or extend the {@link SlingAllMethodsServlet}
+ * which also contains support for the <em>POST</em>, <em>PUT</em> and
+ * <em>DELETE</em> methods. This latter class should also be overwritten to
+ * add support for HTTP methods modifying data.
+ *
+ * @see SlingAllMethodsServlet
+ */
+public class SlingSafeMethodsServlet extends GenericServlet {
+
+    /**
+     * Handles the <em>HEAD</em> method.
+     * <p>
+     * This base implementation just calls the
+     * {@link #doGet(HttpServletRequest, HttpServletResponse)} method dropping
+     * the output. Implementations of this class may overwrite this method if
+     * they have a more performing implementation. Otherwise, they may just keep
+     * this base implementation.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response which only gets the headers set
+     * @throws ServletException Forwarded from the
+     *             {@link #doGet(HttpServletRequest, HttpServletResponse)}
+     *             method called by this implementation.
+     * @throws IOException Forwarded from the
+     *             {@link #doGet(HttpServletRequest, HttpServletResponse)}
+     *             method called by this implementation.
+     */
+    protected void doHead(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // the null-output wrapper
+        NoBodyResponse wrappedResponse = new NoBodyResponse(response);
+
+        // do a normal get request, dropping the output
+        doGet(request, wrappedResponse);
+
+        // ensure the content length is set as gathered by the null-output
+        wrappedResponse.setContentLength();
+    }
+
+    /**
+     * Called by the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method to
+     * handle an HTTP <em>GET</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>GET</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException This implementation throws a
+     *             {@link HttpStatusCodeException} exception with the
+     *             appropriate status code and message.
+     */
+    protected void doGet(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        handleMethodNotImplemented(request);
+    }
+
+    /**
+     * Handles the <em>OPTIONS</em> method by setting the HTTP
+     * <code>Allow</code> header on the response depending on the methods
+     * declared in this class.
+     * <p>
+     * Extensions of this class should generally not overwrite this method but
+     * rather the {@link #getAllowedRequestMethods(Map)} method. This method
+     * gathers all declared public and protected methods for the concrete class
+     * (upto but not including this class) and calls the
+     * {@link #getAllowedRequestMethods(Map)} method with the methods gathered.
+     * The returned value is then used as the value of the <code>Allow</code>
+     * header set.
+     *
+     * @param request The HTTP request object. Not used.
+     * @param response The HTTP response object on which the header is set.
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException Not thrown by this implementation.
+     */
+    protected void doOptions(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        Map<String, Method> methods = getAllDeclaredMethods(this.getClass());
+        StringBuffer allowBuf = getAllowedRequestMethods(methods);
+        response.setHeader("Allow", allowBuf.toString());
+    }
+
+    /**
+     * Handles the <em>TRACE</em> method by just returning the list of all
+     * header values in the response body.
+     * <p>
+     * Extensions of this class do not generally need to overwrite this method
+     * as it contains all there is to be done to the <em>TRACE</em> method.
+     *
+     * @param request The HTTP request whose headers are returned.
+     * @param response The HTTP response into which the request headers are
+     *            written.
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException May be thrown if there is an problem sending back the
+     *             request headers in the response stream.
+     */
+    protected void doTrace(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        String CRLF = "\r\n";
+
+        StringBuffer responseString = new StringBuffer();
+        responseString.append("TRACE ").append(request.getRequestURI());
+        responseString.append(' ').append(request.getProtocol());
+
+        Enumeration<?> reqHeaderEnum = request.getHeaderNames();
+        while (reqHeaderEnum.hasMoreElements()) {
+            String headerName = (String) reqHeaderEnum.nextElement();
+
+            Enumeration<?> reqHeaderValEnum = request.getHeaders(headerName);
+            while (reqHeaderValEnum.hasMoreElements()) {
+                responseString.append(CRLF);
+                responseString.append(headerName).append(": ");
+                responseString.append(reqHeaderValEnum.nextElement());
+            }
+        }
+
+        responseString.append(CRLF);
+
+        String charset = "UTF-8";
+        byte[] rawResponse = responseString.toString().getBytes(charset);
+        int responseLength = rawResponse.length;
+
+        response.setContentType("message/http");
+        response.setCharacterEncoding(charset);
+        response.setContentLength(responseLength);
+
+        ServletOutputStream out = response.getOutputStream();
+        out.write(rawResponse);
+    }
+
+    /**
+     * Called by the {@link #service(HttpServletRequest, HttpServletResponse)}
+     * method to handle a request for an HTTP method, which is not known and
+     * handled by this class or its extension.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * This method should be overwritten with great care. It is better to
+     * overwrite the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method and
+     * add support for any extension HTTP methods through an additional doXXX
+     * method.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException This implementation throws a
+     *             {@link HttpStatusCodeException} exception with the
+     *             appropriate status code and message.
+     */
+    protected void doGeneric(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        handleMethodNotImplemented(request);
+    }
+
+    /**
+     * Tries to handle the request by calling a Java method implemented for the
+     * respective HTTP request method.
+     * <p>
+     * This base class implentation dispatches the <em>HEAD</em>,
+     * <em>GET</em>, <em>OPTIONS</em> and <em>TRACE</em> to the
+     * respective <em>doXXX</em> methods and returns <code>true</code> if
+     * any of these methods is requested. Otherwise <code>false</code> is just
+     * returned.
+     * <p>
+     * Implementations of this class may overwrite this method but should first
+     * call this base implementation and in case <code>false</code> is
+     * returned add handling for any other method and of course return whether
+     * the requested method was known or not.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @return <code>true</code> if the requested method (<code>request.getMethod()</code>)
+     *         is known. Otherwise <code>false</code> is returned.
+     * @throws ServletException Forwarded from any of the dispatched methods
+     * @throws IOException Forwarded from any of the dispatched methods
+     */
+    protected boolean mayService(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // assume the method is known for now
+        boolean methodKnown = true;
+
+        String method = request.getMethod().toUpperCase();
+        if (HttpConstants.METHOD_HEAD.equals(method)) {
+            doHead(request, response);
+        } else if (HttpConstants.METHOD_GET.equals(method)) {
+            doGet(request, response);
+        } else if (HttpConstants.METHOD_OPTIONS.equals(method)) {
+            doOptions(request, response);
+        } else if (HttpConstants.METHOD_TRACE.equals(method)) {
+            doTrace(request, response);
+        } else {
+            // actually we do not know the method
+            methodKnown = false;
+        }
+
+        // return whether we actually knew the request method or not
+        return methodKnown;
+    }
+
+    /**
+     * Helper method which causes an appropriate HTTP response to be sent for an
+     * unhandled HTTP request method. In case of HTTP/1.1 a 405 status code
+     * (Method Not Allowed) is returned, otherwise a 400 status (Bad Request) is
+     * returned.
+     *
+     * @param request The HTTP request from which the method and protocol values
+     *            are extracted to build the appropriate message.
+     * @throws HttpStatusCodeException Always thrown by this method containing
+     *             the appropriate status code and message.
+     */
+    protected void handleMethodNotImplemented(HttpServletRequest request)
+            throws HttpStatusCodeException {
+        String protocol = request.getProtocol();
+        String msg = "Method " + request.getMethod() + " not supported";
+
+        // for HTTP/1.1 use 405 Method Not Allowed
+        if (protocol.endsWith("1.1")) {
+            throw new HttpStatusCodeException(
+                HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
+        }
+
+        // otherwise use 400 Bad Request
+        throw new HttpStatusCodeException(HttpServletResponse.SC_BAD_REQUEST,
+            msg);
+    }
+
+    /**
+     * Called by the {@link #service(ServletRequest, ServletResponse)} method to
+     * handle the HTTP request. This implementation calls the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method and
+     * depedending on its return value call the
+     * {@link #doGeneric(HttpServletRequest, HttpServletResponse)} method. If
+     * the {@link #mayService(HttpServletRequest, HttpServletResponse)} method
+     * can handle the request, the
+     * {@link #doGeneric(HttpServletRequest, HttpServletResponse)} method is not
+     * called otherwise it is called.
+     * <p>
+     * Implementations of this class should not generally overwrite this method.
+     * Rather the {@link #mayService(HttpServletRequest, HttpServletResponse)}
+     * method should be overwritten to add support for more HTTP methods.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Forwarded from the
+     *             {@link #mayService(HttpServletRequest, HttpServletResponse)}
+     *             or
+     *             {@link #doGeneric(HttpServletRequest, HttpServletResponse)}
+     *             methods.
+     * @throws IOException Forwarded from the
+     *             {@link #mayService(HttpServletRequest, HttpServletResponse)}
+     *             or
+     *             {@link #doGeneric(HttpServletRequest, HttpServletResponse)}
+     *             methods.
+     */
+    protected void service(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // first try to handle the request by the known methods
+        boolean methodKnown = mayService(request, response);
+
+        // otherwise try to handle it through generic means
+        if (!methodKnown) {
+            doGeneric(request, response);
+        }
+    }
+
+    /**
+     * Forwards the request to the
+     * {@link #service(HttpServletRequest, HttpServletResponse)} method if the
+     * request is a HTTP request.
+     * <p>
+     * Implementations of this class will not generally overwrite this method.
+     *
+     * @param req The Servlet request
+     * @param res The Servlet response
+     * @throws ServletException If the request is not a HTTP request or
+     *             forwarded from the
+     *             {@link #service(HttpServletRequest, HttpServletResponse)}
+     *             called.
+     * @throws IOException Forwarded from the
+     *             {@link #service(HttpServletRequest, HttpServletResponse)}
+     *             called.
+     */
+    public void service(ServletRequest req, ServletResponse res)
+            throws ServletException, IOException {
+
+        try {
+            HttpServletRequest request = (HttpServletRequest) req;
+            HttpServletResponse response = (HttpServletResponse) res;
+
+            service(request, response);
+
+        } catch (ClassCastException cce) {
+            throw new ServletException("Not a HTTP request/response");
+        }
+
+    }
+
+    /**
+     * Helper method called by
+     * {@link #doOptions(HttpServletRequest, HttpServletResponse)} to calculate
+     * the value of the <em>Allow</em> header sent as the response to the HTTP
+     * <em>OPTIONS</em> request.
+     * <p>
+     * This base class implementation checks whether any doXXX methods exist for
+     * <em>GET</em> and <em>HEAD</em> and returns the list of methods
+     * supported found. The list returned always includes the HTTP
+     * <em>OPTIONS</em> and <em>TRACE</em> methods.
+     * <p>
+     * Implementations of this class may overwrite this method check for more
+     * methods supported by the extension (generally the same list as used in
+     * the {@link #mayService(HttpServletRequest, HttpServletResponse)} method).
+     * This base class implementation should always be called to make sure the
+     * default HTTP methods are included in the list.
+     *
+     * @param declaredMethods The public and protected methods declared in the
+     *            extension of this class.
+     * @return A <code>StringBuffer</code> containing the list of HTTP methods
+     *         supported.
+     */
+    protected StringBuffer getAllowedRequestMethods(
+            Map<String, Method> declaredMethods) {
+        StringBuffer allowBuf = new StringBuffer();
+
+        // OPTIONS and TRACE are always supported by this servlet
+        allowBuf.append(HttpConstants.METHOD_OPTIONS);
+        allowBuf.append(", ").append(HttpConstants.METHOD_TRACE);
+
+        // add more method names depending on the methods found
+        if (declaredMethods.containsKey("doHead")
+            && !declaredMethods.containsKey("doGet")) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_HEAD);
+
+        } else if (declaredMethods.containsKey("doGet")) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_GET);
+            allowBuf.append(", ").append(HttpConstants.METHOD_HEAD);
+
+        }
+
+        return allowBuf;
+    }
+
+    /**
+     * Returns a map of methods declared by the class indexed by method name.
+     * This method is called by the
+     * {@link #doOptions(HttpServletRequest, HttpServletResponse)} method to
+     * find the methods to be checked by the
+     * {@link #getAllowedRequestMethods(Map)} method. Note, that only extension
+     * classes of this class are considered to be sure to not account for the
+     * default implementations of the doXXX methods in this class.
+     *
+     * @param c The <code>Class</code> to get the declared methods from
+     * @return The Map of methods considered for support checking.
+     */
+    private Map<String, Method> getAllDeclaredMethods(Class<?> c) {
+        // stop (and do not include) the AbstractSlingServletClass
+        if (c == null || c.getName().equals(SlingSafeMethodsServlet.class.getName())) {
+            return new HashMap<String, Method>();
+        }
+
+        // get the declared methods from the base class
+        Map<String, Method> methodSet = getAllDeclaredMethods(c.getSuperclass());
+
+        // add declared methods of c (maybe overwrite base class methods)
+        Method[] declaredMethods = c.getDeclaredMethods();
+        for (Method method : declaredMethods) {
+            // only consider public and protected methods
+            if (Modifier.isProtected(method.getModifiers())
+                || Modifier.isPublic(method.getModifiers())) {
+                methodSet.put(method.getName(), method);
+            }
+        }
+
+        return methodSet;
+    }
+
+    /**
+     * A response that includes no body, for use in (dumb) "HEAD" support. This
+     * just swallows that body, counting the bytes in order to set the content
+     * length appropriately.
+     */
+    private class NoBodyResponse extends HttpServletResponseWrapper {
+
+        /** The byte sink and counter */
+        private NoBodyOutputStream noBody;
+
+        /** Optional writer around the byte sink */
+        private PrintWriter writer;
+
+        /** Whether the request processor set the content length itself or not. */
+        private boolean didSetContentLength;
+
+        NoBodyResponse(HttpServletResponse wrappedResponse) {
+            super(wrappedResponse);
+            noBody = new NoBodyOutputStream();
+        }
+
+        /**
+         * Called at the end of request processing to ensure the content length
+         * is set. If the processor already set the length, this method does not
+         * do anything. Otherwise the number of bytes written through the
+         * null-output is set on the response.
+         */
+        void setContentLength() {
+            if (!didSetContentLength) {
+                setContentLength(noBody.getContentLength());
+            }
+        }
+
+        /**
+         * Overwrite this to prevent setting the content length at the end of
+         * the request through {@link #setContentLength()}
+         */
+        public void setContentLength(int len) {
+            super.setContentLength(len);
+            didSetContentLength = true;
+        }
+
+        /**
+         * Just return the null output stream and don't check whether a writer
+         * has already been acquired.
+         */
+        public ServletOutputStream getOutputStream() {
+            return noBody;
+        }
+
+        /**
+         * Just return the writer to the null output stream and don't check
+         * whether an output stram has already been acquired.
+         */
+        public PrintWriter getWriter() throws UnsupportedEncodingException {
+            if (writer == null) {
+                OutputStreamWriter w;
+
+                w = new OutputStreamWriter(noBody, getCharacterEncoding());
+                writer = new PrintWriter(w);
+            }
+            return writer;
+        }
+    }
+
+    /**
+     * Simple ServletOutputStream which just does not write but counts the bytes
+     * written through it. This class is used by the NoBodyResponse.
+     */
+    private class NoBodyOutputStream extends ServletOutputStream {
+
+        private int contentLength = 0;
+
+        /**
+         * @return the number of bytes "written" through this stream
+         */
+        int getContentLength() {
+            return contentLength;
+        }
+
+        public void write(int b) {
+            contentLength++;
+        }
+
+        public void write(byte buf[], int offset, int len) {
+            if (len >= 0) {
+                contentLength += len;
+            } else {
+                throw new IndexOutOfBoundsException();
+            }
+        }
+    }
+
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/MicroslingRequestContext.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/MicroslingRequestContext.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/MicroslingRequestContext.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/MicroslingRequestContext.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,124 @@
+/*
+ * 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.request;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.sling.microsling.api.Resource;
+import org.apache.sling.microsling.api.ResourceResolver;
+import org.apache.sling.microsling.api.ServiceLocator;
+import org.apache.sling.microsling.api.SlingRequestContext;
+import org.apache.sling.microsling.api.SlingRequestPathInfo;
+import org.apache.sling.microsling.api.exceptions.SlingException;
+import org.apache.sling.microsling.api.requestparams.RequestParameterMap;
+import org.apache.sling.microsling.request.helpers.MicroslingRequestPathInfo;
+import org.apache.sling.microsling.request.helpers.SlingRequestParameterMap;
+
+/** Additional Request-based info used by for SlingServlets */
+public class MicroslingRequestContext extends SlingRequestContext {
+
+    private Session session;
+    private String responseContentType;
+    private final Repository repository;
+    private Resource resource;
+    private MicroslingRequestPathInfo requestPathInfo;
+    private final ServiceLocator serviceLocator;
+    private RequestParameterMap parameterMap;
+
+    /** Build a MicroslingRequestContext and add it to the request attributes */
+    public MicroslingRequestContext(ServletContext sctx,ServletRequest req,ServletResponse resp,ServiceLocator serviceLocator) 
+    throws SlingException {
+        super(req,resp,sctx);
+        this.serviceLocator = serviceLocator;
+
+        // Access our Repository
+        final String repoAttr = Repository.class.getName();
+        repository = (Repository)sctx.getAttribute(repoAttr);
+        if(repository==null) {
+            throw new SlingException("Repository not available in ServletContext attribute " + repoAttr);
+        }
+
+        // load the request parameter map
+        // this can be done lazily when we keep the request here
+        parameterMap = new SlingRequestParameterMap(request);
+    }
+
+    /** Acquire a JCR Session if not done yet, and return it */
+    public Session getSession() throws SlingException {
+        if(session==null) {
+            try {
+                session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
+            } catch(RepositoryException re) {
+                throw new SlingException("Repository.login() failed: " + re.getMessage(),re);
+            }
+        }
+        return session;
+    }
+
+    public Resource getResource() throws SlingException {
+        if(resource==null) {
+            final ResourceResolver rr = (ResourceResolver)serviceLocator.getRequiredService(ResourceResolver.class.getName());
+            resource = rr.getResource(request);
+        }
+        return resource;
+    }
+
+    public void setResponseContentType(String responseContentType) {
+        this.responseContentType = responseContentType;
+    }
+
+    /** Use the ServletContext to compute the mime-type for given filename */
+    public String getMimeType(String filename) {
+        return servletContext.getMimeType(filename);
+    }
+
+    public SlingRequestPathInfo getRequestPathInfo() throws SlingException {
+        if(requestPathInfo == null) {
+            // make sure Resource is resolved, as we need it to compute the path info
+            getResource();
+            requestPathInfo = new MicroslingRequestPathInfo(resource,request.getPathInfo());
+        }
+        return requestPathInfo;
+    }
+
+    /** Return the map of request parameters */
+    public RequestParameterMap getRequestParameterMap() {
+        return parameterMap;
+    }
+    
+    public ServiceLocator getServiceLocator() {
+        return serviceLocator;
+    }
+
+    @Override
+    public String getPreferredResponseContentType() throws SlingException {
+        return responseContentType;
+    }
+
+    @Override
+    /** microsling only initializes the first value in the returned array */
+    public String[] getResponseContentTypes() throws SlingException {
+        return new String [] { responseContentType };
+    }
+
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/MicroslingRequestPathInfo.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/MicroslingRequestPathInfo.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/MicroslingRequestPathInfo.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/MicroslingRequestPathInfo.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,151 @@
+/*
+ * 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.request.helpers;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.sling.microsling.api.Resource;
+import org.apache.sling.microsling.api.ResourceMetadata;
+import org.apache.sling.microsling.api.SlingRequestPathInfo;
+
+/** microsling request URI parser that provides SlingRequestPathInfo 
+ *  for the current request, based on the path of the Resource.
+ *  
+ *  The values provided by this depend on the Resource.getURI() value,
+ *  as the ResourceResolver might use all or only part of the request 
+ *  URI path to locate the resource (see also SLING-60 ).
+ *  
+ *  What we're after is the remainder of the path, the part that was
+ *  not used to locate the Resource, and we split that part in different
+ *  subparts: selectors, extension and suffix.
+ *  
+ *  @see MicroslingRequestPathInfoTest for a number of examples.
+ */
+public class MicroslingRequestPathInfo implements SlingRequestPathInfo {
+    
+    private final String unusedContentPath;
+    private final String selectorString;
+    private final String [] selectors;
+    private final String extension;
+    private final String suffix;
+    private final String resourcePath;
+    
+    private final static String EMPTY = "";
+    
+    private static Pattern splitInThreePattern;
+    private static Pattern selectorExtPattern; 
+    private static PatternSyntaxException parsingPatternException;
+    
+    static {
+        try {
+            // we love regular expressions (along with their unit tests ;-)
+            // first pattern splits (path) (selectors + extension) (suffix) 
+            splitInThreePattern = Pattern.compile("([^\\.]*)?((\\.[^/]*)?(/.*)?)?");
+            
+            // second pattern separates (selectors) and (extension)
+            selectorExtPattern = Pattern.compile("(\\.(.*))?(\\.([^\\.]*))");
+            
+            parsingPatternException = null;
+        } catch(PatternSyntaxException pse) {
+            parsingPatternException = pse;
+        }
+    }
+    
+    /** break requestPath as required by SlingRequestPathInfo */
+    public MicroslingRequestPathInfo(Resource r,String requestPath) throws PatternSyntaxException {
+        if(parsingPatternException != null) {
+            throw parsingPatternException;
+        }
+        
+        String pathToParse = requestPath;
+        if(pathToParse == null) {
+            pathToParse = "";
+        }
+        
+        if(r==null) {
+            resourcePath = null;
+        } else {
+            resourcePath = (String)r.getMetadata().get(ResourceMetadata.RESOLUTION_PATH);
+            if(resourcePath!=null && pathToParse.length() >= resourcePath.length()) {
+                pathToParse = pathToParse.substring(resourcePath.length());
+            }
+        }
+        
+        Matcher m = splitInThreePattern.matcher(pathToParse);
+        unusedContentPath = groupOrEmpty(m,1);
+        suffix = groupOrEmpty(m,4);
+        
+        final String selAndExt = groupOrEmpty(m,3);
+        m = selectorExtPattern.matcher(selAndExt);
+        selectorString = groupOrEmpty(m,2);
+        selectors = selectorString.split("\\.");
+        extension = groupOrEmpty(m,4);
+    }
+    
+    /** Return the contents of m.group(index), empty string if that's null */
+    private static String groupOrEmpty(Matcher m, int index) {
+        String result = null;
+        if(m.matches()) {
+            result = m.group(index);
+        }
+        return result == null ? EMPTY : result;
+    }
+    
+    @Override
+    public String toString() {
+        return 
+          "SlingRequestPathInfoParser:"
+          + ", path='" + unusedContentPath + "'"
+          + ", selectorString='" + selectorString + "'"
+          + ", extension='" + extension + "'"
+          + ", suffix='" + suffix + "'"
+        ;
+    }
+    
+    public String getExtension() {
+        return extension;
+    }
+
+    public String getSelector(int i) {
+        if(i >= 0 && i < selectors.length) {
+            return selectors[i];
+        }
+        return null;
+    }
+
+    public String[] getSelectors() {
+        return selectors;
+    }
+
+    public String getSelectorString() {
+        return selectorString;
+    }
+
+    public String getSuffix() {
+        return suffix;
+    }
+    
+    public String getUnusedContentPath() {
+        return unusedContentPath;
+    }
+
+    public String getResourcePath() {
+        return resourcePath;
+    }
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/SlingRequestParameterMap.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/SlingRequestParameterMap.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/SlingRequestParameterMap.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/request/helpers/SlingRequestParameterMap.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,119 @@
+/*
+ * 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.request.helpers;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.microsling.api.requestparams.RequestParameter;
+import org.apache.sling.microsling.api.requestparams.RequestParameterMap;
+
+/**
+ * The <code>SlingRequestParameterMap</code> implements the
+ * <code>RequestParameterMap</code> map interface simply containing all
+ * request parameters as <code>RequestParameter</code> instances.
+ */
+public class SlingRequestParameterMap extends
+        HashMap<String, RequestParameter[]> implements RequestParameterMap {
+
+    /** Create from the HTTP request parameters */
+    public SlingRequestParameterMap(HttpServletRequest request) {
+        Map<?, ?> parameters = request.getParameterMap();
+        for (Map.Entry<?, ?> entry : parameters.entrySet()) {
+            String[] values = (String[]) entry.getValue();
+            RequestParameter[] rpValues = new RequestParameter[values.length];
+            for (int i = 0; i < values.length; i++) {
+                rpValues[i] = new SimpleRequestParameter(values[i]);
+            }
+            put((String) entry.getKey(), rpValues);
+        }
+    }
+
+    public RequestParameter[] getValues(String name) {
+        return get(name);
+    }
+
+    public RequestParameter getValue(String name) {
+        RequestParameter[] values = get(name);
+        return (values != null && values.length > 0) ? values[0] : null;
+    }
+
+    /** Simple implementation of the RequestParameter interface */
+    private static class SimpleRequestParameter implements RequestParameter {
+
+        private String value;
+
+        private byte[] cachedBytes;
+
+        SimpleRequestParameter(String value) {
+            this.value = value;
+        }
+
+        /**
+         * Convert the parameter string value to a byte[] using ISO-8859-1
+         * encoding, which is assumed to be the default for parameters
+         */
+        public byte[] get() {
+            if (cachedBytes == null) {
+                try {
+                    cachedBytes = getString().getBytes("ISO-88591-1");
+                } catch (UnsupportedEncodingException uee) {
+                    // don't care, fall back to platform default
+                    // actually, this is not expected as ISO-8859-1 is required
+                    cachedBytes = getString().getBytes();
+                }
+            }
+            return cachedBytes;
+        }
+
+        public String getContentType() {
+            return null;
+        }
+
+        public String getFileName() {
+            return null;
+        }
+
+        public InputStream getInputStream() {
+            return new ByteArrayInputStream(get());
+        }
+
+        public long getSize() {
+            return get().length;
+        }
+
+        public String getString() {
+            return value;
+        }
+
+        public String getString(String encoding) {
+            return value;
+        }
+
+        public boolean isFormField() {
+            return true;
+        }
+
+    }
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/JcrNodeResource.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/JcrNodeResource.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/JcrNodeResource.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/JcrNodeResource.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,98 @@
+/*
+ * 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.resource;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.microsling.api.Resource;
+import org.apache.sling.microsling.api.ResourceMetadata;
+
+/** A Resource that wraps a JCR Node */
+class JcrNodeResource implements Resource {
+    private final Node node;
+    private final String path;
+    private final String resourceType;
+    private Object data;
+    private final ResourceMetadata metadata;
+
+    /** JCR Property that defines the resource type of this node
+     *  (TODO: use a sling:namespaced property name)
+     */
+    public static final String SLING_RESOURCE_TYPE_PROPERTY = "slingResourceType";
+
+    public static final String NODE_TYPE_RT_PREFIX = "NODETYPES/";
+
+    JcrNodeResource(javax.jcr.Session s,String path) throws RepositoryException {
+        node = (Node)s.getItem(path);
+        this.path = node.getPath();
+        metadata = new ResourceMetadata();
+        resourceType = getResourceTypeForNode(node);
+    }
+
+    public void setData(Object data) {
+        this.data = data;
+    }
+
+    public String toString() {
+        return "JcrNodeResource, type=" + resourceType + ", path=" + path;
+    }
+
+    public Item getItem() {
+        return node;
+    }
+
+    public String getURI() {
+        return path;
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    // no object mapping yet
+    public Object getData() {
+        return data;
+    }
+
+    public ResourceMetadata getMetadata() {
+        return metadata;
+    }
+
+    /** Compute the resource type of the given node, using either the SLING_RESOURCE_TYPE_PROPERTY,
+     *  or the node's primary type, if the property is not set
+     */
+    public static String getResourceTypeForNode(Node node) throws RepositoryException {
+        String result = null;
+
+        if(node.hasProperty(SLING_RESOURCE_TYPE_PROPERTY)) {
+            result = node.getProperty(SLING_RESOURCE_TYPE_PROPERTY).getValue().getString().toLowerCase().trim();
+        }
+
+        if(result==null || result.length() == 0) {
+            result = NODE_TYPE_RT_PREFIX + filterName(node.getPrimaryNodeType().getName());
+        }
+
+        return result;
+    }
+
+    /** Filter a node type name so that it can be used in a resource type value */
+    public static String filterName(String name) {
+        return name.toLowerCase().replaceAll("\\:","/");
+    }
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,64 @@
+/*
+ * 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.resource;
+
+import javax.jcr.RepositoryException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.microsling.api.Resource;
+import org.apache.sling.microsling.api.ResourceMetadata;
+import org.apache.sling.microsling.api.ResourceResolver;
+import org.apache.sling.microsling.api.SlingRequestContext;
+import org.apache.sling.microsling.api.exceptions.SlingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** The microsling ResourceResolver locates a Resource using a ResourcePathIterator:
+ *  it first tries to find an exact match for the Request URI, and if not goes up
+ *  the path, breaking it a "." and "/", stopping if it finds a Resource. 
+ */
+public class MicroslingResourceResolver implements ResourceResolver {
+
+    private static final Logger log = LoggerFactory.getLogger(MicroslingResourceResolver.class);    
+            
+    public Resource getResource(HttpServletRequest request) throws SlingException {
+        final SlingRequestContext ctx = SlingRequestContext.getFromRequest(request);
+        Resource result = null;
+        String path = null;
+
+        try {
+            final ResourcePathIterator it = new ResourcePathIterator(request.getPathInfo());
+            while(it.hasNext() && result == null) {
+                path = it.next();
+                if(log.isDebugEnabled()) {
+                    log.debug("Trying to locate Resource at path '" + path + "'");
+                }
+                if (ctx.getSession().itemExists(path)) {
+                    result = new JcrNodeResource(ctx.getSession(),path);
+                    result.getMetadata().put(ResourceMetadata.RESOLUTION_PATH, path);
+                    if(log.isInfoEnabled()) {
+                        log.info("Found Resource at path '" + path + "'");
+                    }
+                }
+            }
+        } catch(RepositoryException re) {
+            throw new SlingException("RepositoryException for path=" + path,re);
+        }
+        
+        return result;
+    }
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,70 @@
+/*
+ * 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.resource;
+
+import java.util.Iterator;
+
+/** Iterate over the the HTTP request path by creating shorter segments
+ *  of that path using "." and "/" as separators.
+ *  
+ *  For example, if path = /some/stuff.a4.html/xyz the sequence is:
+ *  <ol>
+ *    <li>
+ *      /some/stuff.a4.html/xyz
+ *    </li>
+ *    <li>
+ *      /some/stuff.a4.html
+ *    </li>
+ *    <li>
+ *      /some/stuff.a4
+ *    </li>
+ *    <li>
+ *      /some/stuff
+ *    </li>
+ *    <li>
+ *      /some
+ *    </li>
+ *  </ol>
+ */
+class ResourcePathIterator implements Iterator<String> {
+
+    private String nextPath;
+    
+    ResourcePathIterator(String path) {
+        nextPath = path;
+    }
+    
+    public boolean hasNext() {
+        return nextPath != null && nextPath.length() > 0;
+    }
+
+    public String next() {
+        final String result = nextPath;
+        final int pos = Math.max(result.lastIndexOf('.'),result.lastIndexOf('/'));
+        if(pos < 0) {
+            nextPath = null;
+        } else {
+            nextPath = result.substring(0,pos);
+        }
+        return result;
+    }
+
+    public void remove() {
+        throw new UnsupportedOperationException("Cannot remove() on ResourcePathIterator");
+    }
+
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/ScriptEngine.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,42 @@
+/*
+ * 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 = "script_path";
+    static final String REQUEST = "request";
+    static final String RESPONSE = "response";
+    static final String RESOURCE = "resource";
+    static final String REQUEST_CONTEXT = "requestContext";
+    static final String OUT = "out";
+    static final String SLING = "sling";
+
+    String[] getExtensions();
+
+    void eval(Reader script, Map<String, Object> props)
+            throws SlingException, IOException;
+
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/SlingScriptResolver.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,331 @@
+/*
+ * 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.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.api.exceptions.SlingException;
+import org.apache.sling.microsling.scripting.engines.freemarker.FreemarkerScriptEngine;
+import org.apache.sling.microsling.scripting.engines.rhino.RhinoJavasSriptEngine;
+import org.apache.sling.microsling.scripting.engines.velocity.VelocityTemplatesScriptEngine;
+import org.apache.sling.microsling.scripting.helpers.ScriptFilenameBuilder;
+import org.apache.sling.microsling.scripting.helpers.ScriptHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Find scripts in the repository, based on the current Resource type.
+ *  The script filename is built using the current HTTP request method name,
+ *  followed by the extension of the current request and the desired script
+ *  extension.
+ *  For example, a "js" script for a GET request on a Resource of type some/type
+ *  with request extension "html" should be stored as
+ *  <pre>
+ *      /sling/scripts/some/type/get.html.js
+ *  </pre>
+ *  in the repository.
+ */
+public class SlingScriptResolver {
+
+    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 SlingException {
+        scriptEngines = new HashMap<String, ScriptEngine>();
+        addScriptEngine(new RhinoJavasSriptEngine());
+        addScriptEngine(new VelocityTemplatesScriptEngine());
+        addScriptEngine(new FreemarkerScriptEngine());
+    }
+
+    /**
+     *
+     * @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(final HttpServletRequest req,final HttpServletResponse resp) throws ServletException, IOException {
+        try {
+            final SlingRequestContext ctx = SlingRequestContext.getFromRequest(req);
+
+            Script script = resolveScript(ctx, req.getMethod());
+            if (script == null) {
+                return false;
+            }
+
+            // the script helper
+            ScriptHelper helper = new ScriptHelper(req, resp);
+
+            // prepare the properties for the script
+            Map<String, Object> props = new HashMap<String, Object>();
+            props.put(ScriptEngine.FILENAME, script.getScriptPath());
+            props.put(ScriptEngine.SLING, helper);
+            props.put(ScriptEngine.REQUEST, helper.getRequest());
+            props.put(ScriptEngine.RESPONSE, helper.getResponse());
+            props.put(ScriptEngine.REQUEST_CONTEXT, ctx);
+            props.put(ScriptEngine.RESOURCE, ctx.getResource());
+            props.put(ScriptEngine.OUT, helper.getResponse().getWriter());
+
+            // TODO the chosen Content-Type should be available
+            // based on the actual decision made...where?
+            resp.setContentType(ctx.getPreferredResponseContentType() + "; charset=UTF-8");
+
+            // evaluate the script now using the ScriptEngine
+            script.getScriptEngine().eval(script.getScriptSource(), props);
+
+            // ensure data is flushed to the underlying writer in case
+            // anything has been written
+            helper.getResponse().getWriter().flush();
+
+            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.
+     */
+    private Script resolveScript(final SlingRequestContext ctx, final String method) throws Exception {
+
+        final Resource r = ctx.getResource();
+        final Session s = ctx.getSession();
+        Script result = null;
+
+        if(r==null) {
+            return null;
+        }
+
+        String scriptFilename = scriptFilenameBuilder.buildScriptFilename(
+            method,
+            ctx.getRequestPathInfo().getSelectorString(),
+            ctx.getPreferredResponseContentType(),
+            "*"
+        );
+        String scriptPath =
+            SCRIPT_BASE_PATH
+            + "/"
+            + r.getResourceType()
+            ;
+
+        // SLING-72: if the scriptfilename contains a relative path, move that
+        // to the scriptPath and make the scriptFilename a direct child pattern
+        int lastSlash = scriptFilename.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            scriptPath += "/" + scriptFilename.substring(0, lastSlash);
+            scriptFilename = scriptFilename.substring(lastSlash + 1);
+        }
+
+        // this is the location of the trailing asterisk
+        final int scriptExtensionOffset = scriptFilename.length() - 1;
+
+        if(log.isDebugEnabled()) {
+            log.debug("Looking for script with filename=" + scriptFilename + " under " + scriptPath);
+        }
+
+        if(s.itemExists(scriptPath)) {
+
+            // get the item and ensure it is a node
+            final Item i = s.getItem(scriptPath);
+            if (i.isNode()) {
+                Node parent = (Node) i;
+                NodeIterator scriptNodeIterator = parent.getNodes(scriptFilename);
+                while (scriptNodeIterator.hasNext()) {
+                    Node scriptNode = scriptNodeIterator.nextNode();
+
+                    // SLING-72: Require the node to be an nt:file
+                    if (scriptNode.isNodeType("nt:file")) {
+
+                        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.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();
+        for(int i=0; i < str.length(); i++) {
+            final char c = str.charAt(i);
+            if(Character.isLetterOrDigit(c)) {
+                sb.append(c);
+            } else {
+                sb.append("_");
+            }
+        }
+        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 {
+
+            // SLING-72: Cannot use primary items due to WebDAV creating
+            // nt:unstructured as jcr:content node. So we just assume
+            // nt:file and try to use the well-known data path
+            Property property = getNode().getProperty("jcr:content/jcr:data");
+            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: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,71 @@
+/*
+ * 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.freemarker;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import org.apache.sling.microsling.api.exceptions.HttpStatusCodeException;
+import org.apache.sling.microsling.api.exceptions.SlingException;
+import org.apache.sling.microsling.scripting.ScriptEngine;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * A ScriptEngine that uses {@link http://freemarker.org/ FreeMarker}
+ * templates to render a Resource in HTML.
+ */
+public class FreemarkerScriptEngine implements ScriptEngine {
+
+    public final static String FREEMARKER_SCRIPT_EXTENSION = "ftl";
+    private final Configuration configuration;
+
+    public FreemarkerScriptEngine() throws SlingException {
+        configuration = new Configuration();
+    }
+
+    public String[] getExtensions() {
+        return new String[] { FREEMARKER_SCRIPT_EXTENSION };
+    }
+
+    public void eval(Reader script, Map<String, Object> props)
+            throws SlingException, IOException {
+        String scriptName = (String)props.get(FILENAME);                                                                    
+        // ensure get method
+        HttpServletRequest request = (HttpServletRequest) props.get(REQUEST);
+        if (!"GET".equals(request.getMethod())) {
+            throw new HttpStatusCodeException(
+                HttpServletResponse.SC_METHOD_NOT_ALLOWED,
+                "FreeMarker templates only support GET requests");
+        }
+
+        Template tmpl = new Template(scriptName, script, configuration);
+
+        try {
+            Writer w = ((HttpServletResponse) props.get(RESPONSE)).getWriter();
+            tmpl.process(props, w);            
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (Throwable t) {
+            throw new SlingException("Failure running FreeMarker script " + scriptName, t);
+        }
+    }
+}
\ No newline at end of file

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,103 @@
+/*
+ * 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.rhino;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.sling.microsling.api.Resource;
+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 ScriptEngine that uses the Rhino interpreter to process Sling requests 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 = wrap(scope, entry.getValue(), rhinoContext);
+                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();
+        }
+    }
+    /**
+     * Wraps Java objects in Rhino host objects.
+     * @param scope the current scope
+     * @param entry the object to wrap
+     * @param context the current context, if needed for class definitions
+     * @return a rhino hostobject
+     */
+	protected Object wrap(final ScriptableObject scope,
+			Object entry, Context context) {
+		//wrap the resource to make it friendlier for javascript
+		if (entry instanceof Resource) {
+			try {
+				ScriptableObject.defineClass(scope, ScriptableResource.class);
+				return context.newObject(scope, "Resource", new Object[] {entry});
+			} catch (Exception e) {
+				return Context.javaToJS(entry, scope);
+			}
+		}
+		return Context.javaToJS(entry, scope);
+	}
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,150 @@
+/*
+ * 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.rhino;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.NativeArray;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+/**
+ * A wrapper for JCR nodes that exposes all properties and child nodes
+ * as properties of a Javascript object.
+ */
+public class ScriptableNode extends ScriptableObject {
+	private Node node;
+	
+	public ScriptableNode(Node item) {
+		super();
+		this.node = item;
+	}
+	
+	public String getClassName() {
+		return "Node";
+	}
+	/**
+	 * Gets the value of a (Javascript) property. If there is a single single-value
+	 * JCR property of this node, return its string value. If there are multiple properties
+	 * of the same name or child nodes of the same name, return an array.
+	 */
+	@Override
+	public Object get(String name, Scriptable start) {
+		Set items = new HashSet();
+		try {
+			Iterator it = node.getNodes(name);
+			while (it.hasNext()) {
+				items.add(new ScriptableNode((Node) it.next()));
+			}
+		} catch (RepositoryException e) {}
+		
+		try {
+			Iterator it = node.getProperties(name);
+			while (it.hasNext()) {
+				Property prop = (Property) it.next();
+				int type = prop.getType();
+				if (prop.getDefinition().isMultiple()) {
+					Value[] values = prop.getValues();
+					for (int i=0;i<values.length;i++) {
+						items.add(wrap(values[i], type));
+					}
+				} else {
+					if (type==PropertyType.REFERENCE) {
+						items.add(new ScriptableNode(prop.getNode()));
+					} else {
+						items.add(wrap(prop.getValue(), type));
+					}
+				}
+			}
+		} catch (RepositoryException e) {}
+		
+		if (items.size()==0) {
+			return null;
+		} else if (items.size()==1) {
+			return items.iterator().next();
+		} else {
+			//TODO: add write support
+			NativeArray result = new NativeArray(items.toArray());
+	        ScriptRuntime.setObjectProtoAndParent(result, this);
+	        return result;
+		}
+	}
+	private Object wrap(Value value, int type) throws ValueFormatException, IllegalStateException, RepositoryException {
+		if (type==PropertyType.BINARY) {
+			return Context.toBoolean(value.getBoolean());
+		} else if (type==PropertyType.DOUBLE) {
+			return Context.toNumber(value.getDouble());
+		} else if (type==PropertyType.LONG) {
+			return Context.toNumber(value.getLong());
+		}
+		return value.getString();
+	}
+
+	@Override
+	public Object[] getIds() {
+		Collection<String> ids = new HashSet<String>();
+		try {
+			PropertyIterator pit = node.getProperties();
+			while (pit.hasNext()) {
+				ids.add(pit.nextProperty().getName());
+			}
+		} catch (RepositoryException e) {
+			//do nothing, just do not list properties
+		}
+		try {
+			NodeIterator nit = node.getNodes();
+			while (nit.hasNext()) {
+				ids.add(nit.nextNode().getName());
+			}
+		} catch (RepositoryException e) {
+			//do nothing, just do not list child nodes
+		}
+		return ids.toArray();
+	}
+
+	@Override
+	public boolean has(String name, Scriptable start) {
+		try {
+			return node.hasProperty(name) || node.hasNode(name);
+		} catch (RepositoryException e) {
+			return false;
+		}
+	}
+
+	@Override
+	public Object getDefaultValue(Class typeHint) {
+		try {
+			return node.getPath();
+		} catch (RepositoryException e) {
+			return null;
+		}
+	}
+
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,104 @@
+/*
+ * 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.rhino;
+
+import javax.jcr.Node;
+
+import org.apache.sling.microsling.api.Resource;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptableObject;
+/**
+ * Resource in JavaScript has following signature:
+ * [Object] getData();
+ * [Object] data
+ * [Item] getItem();
+ * [Item] item
+ * [String] getResourceType();
+ * [String] type
+ * [String] getURI();
+ * [String] uri
+ * 
+ */
+public class ScriptableResource extends ScriptableObject {
+	private Resource resource;
+	
+	public ScriptableResource() {
+	}
+	
+	public void jsConstructor(Object res) {
+		this.resource = (Resource) res;
+	}
+
+	@Override
+	public String getClassName() {
+		return "Resource";
+	}
+
+	public Object jsFunction_getData() {
+		return resource.getData();
+	}
+	
+	public Object jsGet_data() {
+		return this.jsFunction_getData();
+	}
+
+	public Object jsFunction_getItem() {
+		return resource.getItem();
+	}
+	
+	public Object jsGet_item() {
+		if (resource.getItem() instanceof Node) {
+			return new ScriptableNode((Node) resource.getItem());
+		}
+		return Context.javaToJS(resource.getItem(), this);
+	}
+
+	public String jsFunction_getResourceType() {
+		return resource.getResourceType();
+	}
+	
+	public String jsGet_type() {
+		return this.jsFunction_getResourceType();
+	}
+
+	public Object jsFunction_getURI() {
+		return Context.javaToJS(resource.getURI(), this);
+	}
+	
+	public Object jsGet_uri() {
+		return this.jsFunction_getURI();
+	}
+	
+	public Object jsFunction_getMetadata() {
+		return resource.getMetadata();
+	}
+	
+	public Object jsGet_meta() {
+		return resource.getMetadata();
+	}
+
+	@Override
+	public Object getDefaultValue(Class typeHint) {
+		return resource.getURI();
+	}
+
+	public void setResource(Resource entry) {
+		this.resource = entry;
+	}
+	
+	
+}

Added: sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java?rev=1807480&view=auto
==============================================================================
--- sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java (added)
+++ sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java Wed Sep  6 13:24:23 2017
@@ -0,0 +1,85 @@
+/*
+ * 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.velocity;
+
+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 ScriptEngine that uses Velocity templates to render a Resource
+ */
+public class VelocityTemplatesScriptEngine implements ScriptEngine {
+
+    public final static String VELOCITY_SCRIPT_EXTENSION = "vlt";
+
+    private final VelocityEngine velocity;
+
+    public VelocityTemplatesScriptEngine() throws SlingException {
+        velocity = new VelocityEngine();
+        try {
+            velocity.init();
+        } catch (Exception e) {
+            throw new SlingException("Exception in Velocity.init() " + e.getMessage(),e);
+        }
+    }
+
+    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);
+        }
+    }
+}