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);
+ }
+ }
+}