You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2008/12/08 16:53:49 UTC

svn commit: r724385 - in /incubator/sling/trunk/servlets/get: ./ src/main/java/org/apache/sling/servlets/get/ src/main/java/org/apache/sling/servlets/get/helpers/ src/main/resources/OSGI-INF/metatype/

Author: fmeschbe
Date: Mon Dec  8 07:53:49 2008
New Revision: 724385

URL: http://svn.apache.org/viewvc?rev=724385&view=rev
Log:
SLING-723 Enhance functionality of the streamer servlet (autoindex style)
and make renderers and indexing configurable

Modified:
    incubator/sling/trunk/servlets/get/pom.xml
    incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/DefaultGetServlet.java
    incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/helpers/StreamRendererServlet.java
    incubator/sling/trunk/servlets/get/src/main/resources/OSGI-INF/metatype/metatype.properties

Modified: incubator/sling/trunk/servlets/get/pom.xml
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/get/pom.xml?rev=724385&r1=724384&r2=724385&view=diff
==============================================================================
--- incubator/sling/trunk/servlets/get/pom.xml (original)
+++ incubator/sling/trunk/servlets/get/pom.xml Mon Dec  8 07:53:49 2008
@@ -106,6 +106,11 @@
             <version>2.0.3-incubator-SNAPSHOT</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.0.3-incubator-SNAPSHOT</version>
+        </dependency>
+        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.osgi.core</artifactId>
         </dependency>

Modified: incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/DefaultGetServlet.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/DefaultGetServlet.java?rev=724385&r1=724384&r2=724385&view=diff
==============================================================================
--- incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/DefaultGetServlet.java (original)
+++ incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/DefaultGetServlet.java Mon Dec  8 07:53:49 2008
@@ -17,6 +17,7 @@
 package org.apache.sling.servlets.get;
 
 import java.io.IOException;
+import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -42,9 +43,9 @@
 
 /**
  * A SlingSafeMethodsServlet that renders the current Resource as simple HTML
- *
+ * 
  * @scr.component immediate="true" label="%servlet.get.name"
- *                                 description="%servlet.get.description"
+ *                description="%servlet.get.description"
  * @scr.service interface="javax.servlet.Servlet"
  *
  * @scr.property name="service.description" value="Default GET Servlet"
@@ -70,15 +71,71 @@
     /** @scr.property */
     private static final String ALIAS_PROPERTY = "aliases";
 
+    /** @scr.property valueRef="DEFAULT_INDEX_PROPERTY" type="Boolean" */
+    private static final String INDEX_PROPERTY = "index";
+
+    private static final boolean DEFAULT_INDEX_PROPERTY = false;
+
+    /** @scr.property valueRef="DEFAULT_INDEX_FILES_PROPERTY" */
+    private static final String INDEX_FILES_PROPERTY = "index.files";
+
+    private static final String[] DEFAULT_INDEX_FILES_PROPERTY = { "index",
+        "index.html" };
+
+    /** Default value for renderer selection (value is "true"). */
+    private static final boolean DEFAULT_RENDERER_PROPERTY = true;
+
+    /** @scr.property valueRef="DEFAULT_RENDERER_PROPERTY" type="Boolean" */
+    private static final String HTML_RENDERER_PROPERTY = "enable.html";
+
+    /** @scr.property valueRef="DEFAULT_RENDERER_PROPERTY" type="Boolean" */
+    private static final String TXT_RENDERER_PROPERTY = "enable.txt";
+
+    /** @scr.property valueRef="DEFAULT_RENDERER_PROPERTY" type="Boolean" */
+    private static final String JSON_RENDERER_PROPERTY = "enable.json";
+
+    /** @scr.property valueRef="DEFAULT_RENDERER_PROPERTY" type="Boolean" */
+    private static final String XML_RENDERER_PROPERTY = "enable.xml";
+
     /** Additional aliases. */
     private String[] aliases;
 
+    /** Whether to support automatic index rendering */
+    private boolean index;
+
+    /** The names of index rendering children */
+    private String[] indexFiles;
+
+    private boolean enableHtml;
+
+    private boolean enableTxt;
+
+    private boolean enableJson;
+
+    private boolean enableXml;
+
     protected void activate(ComponentContext ctx) {
-        this.aliases = OsgiUtil.toStringArray(ctx.getProperties().get(ALIAS_PROPERTY));
+        Dictionary<?, ?> props = ctx.getProperties();
+        this.aliases = OsgiUtil.toStringArray(props.get(ALIAS_PROPERTY));
+        this.index = OsgiUtil.toBoolean(props.get(INDEX_PROPERTY),
+            DEFAULT_INDEX_PROPERTY);
+        this.indexFiles = OsgiUtil.toStringArray(
+            props.get(INDEX_FILES_PROPERTY), DEFAULT_INDEX_FILES_PROPERTY);
+
+        this.enableHtml = OsgiUtil.toBoolean(props.get(HTML_RENDERER_PROPERTY),
+            DEFAULT_RENDERER_PROPERTY);
+        this.enableTxt = OsgiUtil.toBoolean(props.get(TXT_RENDERER_PROPERTY),
+            DEFAULT_RENDERER_PROPERTY);
+        this.enableJson = OsgiUtil.toBoolean(props.get(JSON_RENDERER_PROPERTY),
+            DEFAULT_RENDERER_PROPERTY);
+        this.enableXml = OsgiUtil.toBoolean(props.get(XML_RENDERER_PROPERTY),
+            DEFAULT_RENDERER_PROPERTY);
     }
 
     protected void deactivate(ComponentContext ctx) {
         this.aliases = null;
+        this.index = false;
+        this.indexFiles = null;
     }
 
     @Override
@@ -86,28 +143,45 @@
         super.init();
 
         // Register renderer servlets
-        setupServlet(rendererMap, HtmlRendererServlet.EXT_HTML,
-            new HtmlRendererServlet());
-        setupServlet(rendererMap, PlainTextRendererServlet.EXT_TXT,
-            new PlainTextRendererServlet());
-        setupServlet(rendererMap, JsonRendererServlet.EXT_JSON,
-            new JsonRendererServlet());
         setupServlet(rendererMap, StreamRendererServlet.EXT_RES,
-            new StreamRendererServlet());
-        setupServlet(rendererMap, XMLRendererServlet.EXT_XML,
+            new StreamRendererServlet(index, indexFiles));
+
+        if (enableHtml) {
+            setupServlet(rendererMap, HtmlRendererServlet.EXT_HTML,
+                new HtmlRendererServlet());
+        }
+
+        if (enableTxt) {
+            setupServlet(rendererMap, PlainTextRendererServlet.EXT_TXT,
+                new PlainTextRendererServlet());
+        }
+        
+        if (enableJson) {
+            setupServlet(rendererMap, JsonRendererServlet.EXT_JSON,
+                new JsonRendererServlet());
+        }
+        
+        if (enableXml) {
+            setupServlet(rendererMap, XMLRendererServlet.EXT_XML,
                 new XMLRendererServlet());
+        }
+        
+        // use the servlet for rendering StreamRendererServlet.EXT_RES as the
+        // streamer servlet
+        streamerServlet = rendererMap.get(StreamRendererServlet.EXT_RES);
 
         // check additional aliases
-        if ( this.aliases != null ) {
-            for(final String m : aliases) {
+        if (this.aliases != null) {
+            for (final String m : aliases) {
                 final int pos = m.indexOf(':');
-                if ( pos != -1 ) {
+                if (pos != -1) {
                     final String type = m.substring(0, pos);
                     final Servlet servlet = rendererMap.get(type);
-                    if ( servlet != null ) {
-                        final String extensions = m.substring(pos+1);
-                        final StringTokenizer st = new StringTokenizer(extensions, ",");
-                        while ( st.hasMoreTokens() ) {
+                    if (servlet != null) {
+                        final String extensions = m.substring(pos + 1);
+                        final StringTokenizer st = new StringTokenizer(
+                            extensions, ",");
+                        while (st.hasMoreTokens()) {
                             final String ext = st.nextToken();
                             rendererMap.put(ext, servlet);
                         }
@@ -115,9 +189,6 @@
                 }
             }
         }
-        // use the servlet for rendering StreamRendererServlet.EXT_RES as the
-        // streamer servlet
-        streamerServlet = rendererMap.get(StreamRendererServlet.EXT_RES);
     }
 
     /**
@@ -145,13 +216,14 @@
 
         // fail if we should not just stream or we cannot support the ext.
         if (rendererServlet == null) {
-            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
-                "No renderer for extension='" + ext + "'");
+            request.getRequestProgressTracker().log(
+                "No Renderer for extension " + ext);
+            response.sendError(HttpServletResponse.SC_FORBIDDEN);
             return;
         }
 
-        request.getRequestProgressTracker().log("Using "
-                + rendererServlet.getClass().getName()
+        request.getRequestProgressTracker().log(
+            "Using " + rendererServlet.getClass().getName()
                 + " to render for extension=" + ext);
         rendererServlet.service(request, response);
     }

Modified: incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/helpers/StreamRendererServlet.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/helpers/StreamRendererServlet.java?rev=724385&r1=724384&r2=724385&view=diff
==============================================================================
--- incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/helpers/StreamRendererServlet.java (original)
+++ incubator/sling/trunk/servlets/get/src/main/java/org/apache/sling/servlets/get/helpers/StreamRendererServlet.java Mon Dec  8 07:53:49 2008
@@ -23,30 +23,47 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.Iterator;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.request.RequestDispatcherOptions;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceNotFoundException;
+import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
 
 /**
  * The <code>StreamRendererServlet</code> streams the current resource to the
- * client on behalf of the {@link org.apache.sling.servlets.get.DefaultGetServlet}.
- * If the current resource cannot be streamed it is rendered using the
+ * client on behalf of the
+ * {@link org.apache.sling.servlets.get.DefaultGetServlet}. If the current
+ * resource cannot be streamed it is rendered using the
  * {@link PlainTextRendererServlet}.
  */
-public class StreamRendererServlet extends PlainTextRendererServlet {
+public class StreamRendererServlet extends SlingSafeMethodsServlet {
 
     public static final String EXT_RES = "res";
 
     private static final long serialVersionUID = -1L;
 
+    private boolean index;
+
+    private String[] indexFiles;
+
+    public StreamRendererServlet(boolean index, String[] indexFiles) {
+        this.index = index;
+        this.indexFiles = indexFiles;
+    }
+
     @Override
     protected void doGet(SlingHttpServletRequest request,
             SlingHttpServletResponse response) throws ServletException,
@@ -65,6 +82,12 @@
             throw new ResourceNotFoundException("No data to render.");
         }
 
+        // trailing slash on url means directory listing
+        if ("/".equals(request.getRequestPathInfo().getSuffix())) {
+            renderDirectory(request, response);
+            return;
+        }
+
         // check the last modification time and If-Modified-Since header
         ResourceMetadata meta = resource.getResourceMetadata();
         long modifTime = meta.getModificationTime();
@@ -75,14 +98,60 @@
 
         // fall back to plain text rendering if the resource has no stream
         InputStream stream = resource.adaptTo(InputStream.class);
-        if (stream == null) {
-            super.doGet(request, response);
-            return;
+        if (stream != null) {
+            
+            streamResource(resource, stream, response);
+            
+        } else {
+            
+            // the resource is the root, do not redirect, immediately index
+            if ("/".equals(resource.getPath())) {
+                
+                renderDirectory(request, response);
+                
+            } else {
+                
+                // redirect to this with trailing slash to render the index
+                String url = request.getResourceResolver().map(request,
+                    resource.getPath())
+                    + "/";
+                response.sendRedirect(url);
+                
+            }
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if the request has a
+     * <code>If-Modified-Since</code> header whose date value is later than
+     * the last modification time given as <code>modifTime</code>.
+     * 
+     * @param request The <code>ComponentRequest</code> checked for the
+     *            <code>If-Modified-Since</code> header.
+     * @param modifTime The last modification time to compare the header to.
+     * @return <code>true</code> if the <code>modifTime</code> is less than
+     *         or equal to the time of the <code>If-Modified-Since</code>
+     *         header.
+     */
+    private boolean unmodified(HttpServletRequest request, long modifTime) {
+        if (modifTime > 0) {
+            long modTime = modifTime / 1000; // seconds
+            long ims = request.getDateHeader(HEADER_IF_MODIFIED_SINCE) / 1000;
+            return modTime <= ims;
         }
 
+        // we have no modification time value, assume modified
+        return false;
+    }
+
+    private void streamResource(Resource resource, InputStream stream,
+            SlingHttpServletResponse response) throws IOException {
         // finally stream the resource
         try {
 
+            ResourceMetadata meta = resource.getResourceMetadata();
+            long modifTime = meta.getModificationTime();
+
             if (modifTime > 0) {
                 response.setDateHeader(HEADER_LAST_MODIFIED, modifTime);
             }
@@ -130,28 +199,122 @@
             }
         }
     }
+    
+    private void renderDirectory(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
 
-    /**
-     * Returns <code>true</code> if the request has a
-     * <code>If-Modified-Since</code> header whose date value is later than
-     * the last modification time given as <code>modifTime</code>.
-     *
-     * @param request The <code>ComponentRequest</code> checked for the
-     *            <code>If-Modified-Since</code> header.
-     * @param modifTime The last modification time to compare the header to.
-     * @return <code>true</code> if the <code>modifTime</code> is less than
-     *         or equal to the time of the <code>If-Modified-Since</code>
-     *         header.
-     */
-    private boolean unmodified(HttpServletRequest request, long modifTime) {
-        if (modifTime > 0) {
-            long modTime = modifTime / 1000; // seconds
-            long ims = request.getDateHeader(HEADER_IF_MODIFIED_SINCE) / 1000;
-            return modTime <= ims;
+        Resource resource = request.getResource();
+        ResourceResolver resolver = request.getResourceResolver();
+
+        // check for an index file
+        for (String index : indexFiles) {
+            Resource fileRes = resolver.getResource(resource, index);
+            if (fileRes != null && !ResourceUtil.isSyntheticResource(fileRes)) {
+
+                // include the index resource with no suffix and selectors !
+                RequestDispatcherOptions rdo = new RequestDispatcherOptions();
+                rdo.setReplaceSuffix("");
+                rdo.setReplaceSelectors("");
+
+                RequestDispatcher dispatcher;
+                if (index.indexOf('.') < 0) {
+                    String filePath = fileRes.getPath() + ".html";
+                    dispatcher = request.getRequestDispatcher(filePath, rdo);
+                } else {
+                    dispatcher = request.getRequestDispatcher(fileRes, rdo);
+                }
+                
+                dispatcher.include(request, response);
+                return;
+            }
+        }
+
+        if (index) {
+//            RequestDispatcherOptions rdo = new RequestDispatcherOptions();
+//            rdo.setReplaceSelectors("sling.index");
+//            request.getRequestDispatcher(resource, rdo).include(request, response);
+            renderIndex(resource, response);
+        } else {
+            response.sendError(HttpServletResponse.SC_FORBIDDEN);
         }
 
-        // we have no modification time value, assume modified
-        return false;
     }
+    
+    private void renderIndex(Resource resource,
+            SlingHttpServletResponse response) throws IOException {
+        
+        response.setContentType("text/html");
+        response.setCharacterEncoding("UTF-8");
+
+        String path = resource.getPath();
+
+        PrintWriter pw = response.getWriter();
+        pw.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">");
+        pw.println("<html>");
+        pw.println("<head>");
+        pw.println("<title>Index of " + path + "</title>");
+        pw.println("</head>");
+
+        pw.println("<body>");
+        pw.println("<h1>Index of " + path + "</h1>");
+
+        pw.println("<pre>");
+        pw.println("Name                               Last modified                   Size  Description");
+        pw.println("<hr>");
+
+        if (!"/".equals(path)) {
+            pw.println("<a href='../'>../</a>                                                                 -     Parent");
+        }
 
+        // render the children
+        Iterator<Resource> children = ResourceUtil.listChildren(resource);
+        while (children.hasNext()) {
+            renderChild(pw, children.next());
+        }
+
+        pw.println("</pre>");
+        pw.println("</body>");
+        pw.println("</html>");
+
+    }
+
+    private void renderChild(PrintWriter pw, Resource resource) {
+
+        String name = ResourceUtil.getName(resource.getPath());
+
+        InputStream ins = resource.adaptTo(InputStream.class);
+        if (ins == null) {
+            name += "/";
+        } else {
+            try {
+                ins.close();
+            } catch (IOException ignore) {
+            }
+        }
+
+        String displayName = name;
+        String suffix;
+        if (displayName.length() >= 32) {
+            displayName = displayName.substring(0, 29) + "...";
+            suffix = "";
+        } else {
+            suffix = "                                               ".substring(
+                0, 32 - displayName.length());
+        }
+        pw.printf("<a href='%s'>%s</a>%s", name, displayName, suffix);
+
+        ResourceMetadata meta = resource.getResourceMetadata();
+        long lastModified = meta.getModificationTime();
+        pw.print("    " + new Date(lastModified) + "    ");
+
+        long length = meta.getContentLength();
+        if (length > 0) {
+            pw.print(length);
+        } else {
+            pw.print('-');
+        }
+
+        pw.println();
+    }
 }
\ No newline at end of file

Modified: incubator/sling/trunk/servlets/get/src/main/resources/OSGI-INF/metatype/metatype.properties
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/get/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=724385&r1=724384&r2=724385&view=diff
==============================================================================
--- incubator/sling/trunk/servlets/get/src/main/resources/OSGI-INF/metatype/metatype.properties (original)
+++ incubator/sling/trunk/servlets/get/src/main/resources/OSGI-INF/metatype/metatype.properties Mon Dec  8 07:53:49 2008
@@ -26,7 +26,43 @@
 servlet.get.name = Sling GET Servlet
 servlet.get.description = The Sling GET Servlet is registered as the default \
  servlet to handle GET requests in Sling.
+
 aliases.name = Extension Aliases
 aliases.description = The aliases can be used to map several extensions to a \
  single servlet. For instance "xml:pdf,rtf" maps the extensions ".pdf" and \
  ".rtf" to the servlet helper handling the ".xml" extension.
+
+index.files.name = Index Resources
+index.files.description = List of child resources to be considered for rendering \
+ the index of a "directory". The default value is [ "index", "index.html" ]. \
+ Each entry in the list is checked and the first entry found is included to \
+ render the index. If an entry is selected, which has not extension (for \
+ example the "index" resource), the extension ".html" is appended for the \
+ inclusion to indicate the desired text/html rendering. If the resource name \
+ has an extension (as in "index.html"), no additional extension is appended \
+ for the inclusion. This configuration corresponds to the DirectoryIndex> \
+ directive of Apache HTTP Server (httpd).
+ 
+index.name = Auto Index
+index.description = Controls whether a simple directory index is rendered for \
+ a directory request. A directory request is a request to a resource with a \
+ trailing slash (/) character, for example http://host/apps/. If none of the \
+ index resources exists, the default GET servlet may automatically render an \
+ index listing of the child resources if this option is checked, which is the \
+ default. If this option is not checked, the request to the resource is \
+ forbidden and results in a status 403/FORBIDDEN. This configuration \
+ corresponds to the "Index" option of the Options directive of Apache HTTP \
+ Server (httpd).
+ 
+enable.html.name = Enable HTML
+enable.html.description = Whether the renderer for HTML of the Default GET \
+ Servlet is enabled or not. By default the HTML renderer is enabled.
+enable.txt.name = Enabled Plain Text
+enable.txt.description = Whether the renderer for plain text of the Default GET \
+ Servlet is enabled or not. By default the plain text renderer is enabled.
+enable.json.name = Enabled JSON
+enable.json.description = Whether the renderer for JSON of the Default GET \
+ Servlet is enabled or not. By default the JSON renderer is enabled.
+enable.xml.name = Enabled XML
+enable.xml.description = Whether the renderer for XML of the Default GET \
+ Servlet is enabled or not. By default the XML renderer is enabled.