tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cos...@apache.org
Subject svn commit: r761964 [8/10] - in /tomcat/trunk/modules/tomcat-lite/java: ./ org/ org/apache/ org/apache/tomcat/ org/apache/tomcat/addons/ org/apache/tomcat/integration/ org/apache/tomcat/integration/jmx/ org/apache/tomcat/integration/simple/ org/apache/...
Date Sat, 04 Apr 2009 16:24:36 GMT
Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/WebdavServlet.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/WebdavServlet.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/WebdavServlet.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/WebdavServlet.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,1840 @@
+/*
+ * 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.tomcat.servlets.file;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.tomcat.servlets.util.Range;
+import org.apache.tomcat.servlets.util.RequestUtil;
+import org.apache.tomcat.servlets.util.UrlUtils;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ * Servlet which adds support for WebDAV level 1. All the basic HTTP requests
+ * are handled by the DefaultServlet. 
+ * 
+ * Based on Catalina WebdavServlet, with following changes:
+ *  - removed the JNDI abstraction, use File instead
+ *  - removed WebDAV 2 support ( moved to Webdav2Servlet ) - i.e. no locks
+ *    supported
+ *  - simplified and cleaned up the code
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class WebdavServlet extends DefaultServlet {
+
+
+    // -------------------------------------------------------------- Constants
+
+    protected static final String METHOD_PROPFIND = "PROPFIND";
+    protected static final String METHOD_PROPPATCH = "PROPPATCH";
+    protected static final String METHOD_MKCOL = "MKCOL";
+    protected static final String METHOD_COPY = "COPY";
+    protected static final String METHOD_DELETE = "DELETE";
+    protected static final String METHOD_MOVE = "MOVE";
+    protected static final String METHOD_LOCK = "LOCK";
+    protected static final String METHOD_UNLOCK = "UNLOCK";
+
+
+    /**
+     * Default depth is infite.
+     */
+    protected static final int INFINITY = 3; // To limit tree browsing a bit
+
+
+    /**
+     * PROPFIND - Specify a property mask.
+     */
+    protected static final int FIND_BY_PROPERTY = 0;
+
+
+    /**
+     * PROPFIND - Display all properties.
+     */
+    protected static final int FIND_ALL_PROP = 1;
+
+
+    /**
+     * PROPFIND - Return property names.
+     */
+    protected static final int FIND_PROPERTY_NAMES = 2;
+
+    /**
+     * Default namespace.
+     */
+    protected static final String DEFAULT_NAMESPACE = "DAV:";
+
+
+    /**
+     * Simple date format for the creation date ISO representation (partial).
+     * TODO: ThreadLocal
+     */
+    protected static final SimpleDateFormat creationDateFormat =
+        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
+
+    static {
+        creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+    }
+
+    // TODO: replace it with writeRole - who is enabled to write
+    protected boolean readOnly = false;
+
+    /**
+     * Repository of the lock-null resources.
+     * <p>
+     * Key : path of the collection containing the lock-null resource<br>
+     * Value : Vector of lock-null resource which are members of the
+     * collection. Each element of the Vector is the path associated with
+     * the lock-null resource.
+     */
+    protected Hashtable lockNullResources = new Hashtable();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Initialize this servlet.
+     */
+    public void init()
+        throws ServletException {
+
+        super.init();
+
+        try {
+            String value = getServletConfig().getInitParameter("readonly");
+            if (value != null)
+                readOnly = (new Boolean(value)).booleanValue();
+        } catch (Throwable t) {
+            ;
+        }
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Handles the special WebDAV methods.
+     */
+    protected void service(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        String method = req.getMethod();
+
+        if (method.equals(METHOD_PROPFIND)) {
+            doPropfind(req, resp);
+        } else if (method.equals(METHOD_PROPPATCH)) {
+            doProppatch(req, resp);
+        } else if (method.equals(METHOD_MKCOL)) {
+            doMkcol(req, resp);
+        } else if (method.equals(METHOD_COPY)) {
+            doCopy(req, resp);
+        } else if (method.equals(METHOD_MOVE)) {
+            doMove(req, resp);
+        } else if (method.equals(METHOD_LOCK)) {
+            doLock(req, resp);
+        } else if (method.equals(METHOD_UNLOCK)) {
+            doUnlock(req, resp);
+        } else if (method.equals(METHOD_DELETE)) {
+            doDelete(req, resp);
+        } else {
+            // DefaultServlet processing - GET, HEAD, POST
+            super.service(req, resp);
+        }
+
+    }
+
+    /**
+     * Check if the conditions specified in the optional If headers are
+     * satisfied.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param resourceAttributes The resource information
+     * @return boolean true if the resource meets all the specified conditions,
+     * and false if any of the conditions is not satisfied, in which case
+     * request processing is stopped
+     */
+    protected boolean checkIfHeaders(HttpServletRequest request,
+                                     HttpServletResponse response,
+                                     File resourceAttributes)
+        throws IOException {
+
+        if (!super.checkIfHeaders(request, response, resourceAttributes))
+            return false;
+
+        // TODO : Checking the WebDAV If header
+        return true;
+
+    }
+
+
+    /**
+     * OPTIONS Method.
+     *
+     * @param req The request
+     * @param resp The response
+     * @throws ServletException If an error occurs
+     * @throws IOException If an IO error occurs
+     */
+    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        resp.addHeader("DAV", "1"); // And not: ,2");
+
+        StringBuffer methodsAllowed = determineMethodsAllowed(basePath,
+                                                              req);
+        resp.addHeader("Allow", methodsAllowed.toString());
+        resp.addHeader("MS-Author-Via", "DAV");
+    }
+
+    // ------------------ PROPFIND --------------------
+
+    /**
+     * PROPFIND Method.
+     */
+    protected void doPropfind(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        String path = getRelativePath(req);
+        if (path.endsWith("/"))
+            path = path.substring(0, path.length() - 1);
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        // Propfind depth
+        int depth = getDepth(req);
+
+        File object = new File(basePath, path);
+
+        if (!object.exists()) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
+            return;
+        }
+
+        // Properties which are to be displayed.
+        Vector properties = new Vector();
+        int type = getPropfindType(req, properties);
+
+        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+        resp.setContentType("text/xml; charset=UTF-8");
+
+        // Create multistatus object
+        XMLWriter generatedXML = new XMLWriter(resp.getWriter());
+        generatedXML.writeXMLHeader();
+
+        generatedXML.writeElement(null, "multistatus"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        if (depth == 0) {
+            parseProperties(req, generatedXML, path, type,
+                            properties);
+        } else {
+            propfindRecurse(req, path, depth, properties, 
+                    type, generatedXML);
+        }
+
+        generatedXML.writeElement(null, "multistatus",
+                                  XMLWriter.CLOSING);
+        generatedXML.sendData();
+    }
+
+
+    private void propfindRecurse(HttpServletRequest req, String path, int depth, Vector properties, int type, XMLWriter generatedXML) throws IOException {
+        File object;
+        // The stack always contains the object of the current level
+        Stack stack = new Stack();
+        stack.push(path);
+
+        // Stack of the objects one level below
+        Stack stackBelow = new Stack();
+
+        while ((!stack.isEmpty()) && (depth >= 0)) {
+            String currentPath = (String) stack.pop();
+            parseProperties(req, generatedXML, currentPath,
+                            type, properties);
+
+            object = new File(basePath,currentPath);
+            if (!object.exists()) {
+                continue;
+            }
+
+            if ((object.isDirectory()) && (depth > 0)) {
+
+                File[] files = object.listFiles();
+                for (int i=0; i < files.length; i++) {
+                    String newPath = currentPath;
+                    if (!(newPath.endsWith("/")))
+                        newPath += "/";
+                    newPath += files[i].getName();
+                    stackBelow.push(newPath);
+                }
+            }
+
+            if (stack.isEmpty()) {
+                depth--;
+                stack = stackBelow;
+                stackBelow = new Stack();
+            }
+
+            generatedXML.sendData();
+        }
+    }
+
+
+    private int getPropfindType(HttpServletRequest req, Vector properties) throws ServletException {
+        // Propfind type
+        int type = FIND_ALL_PROP;
+
+        DocumentBuilder documentBuilder = getDocumentBuilder();
+        try {
+            Document document = documentBuilder.parse
+                (new InputSource(req.getInputStream()));
+            // Get the root element of the document
+            Element rootElement = document.getDocumentElement();
+            NodeList childList = rootElement.getChildNodes();
+
+            for (int i=0; i < childList.getLength(); i++) {
+                Node currentNode = childList.item(i);
+                switch (currentNode.getNodeType()) {
+                case Node.TEXT_NODE:
+                    break;
+                case Node.ELEMENT_NODE:
+                    if (currentNode.getNodeName().endsWith("prop")) {
+                        type = FIND_BY_PROPERTY;
+                        Node propNode = currentNode;
+                        NodeList childListPN = propNode.getChildNodes();
+                        for (int iPN=0; iPN < childListPN.getLength(); iPN++) {
+                            Node currentNodePN = childListPN.item(iPN);
+                            switch (currentNodePN.getNodeType()) {
+                            case Node.TEXT_NODE:
+                                break;
+                            case Node.ELEMENT_NODE:
+                                String nodeName = currentNodePN.getNodeName();
+                                String propertyName = null;
+                                if (nodeName.indexOf(':') != -1) {
+                                    propertyName = nodeName.substring
+                                        (nodeName.indexOf(':') + 1);
+                                } else {
+                                    propertyName = nodeName;
+                                }
+                                // href is a live property which is handled differently
+                                properties.addElement(propertyName);
+                                break;
+                            }
+                        }
+                    }
+                    if (currentNode.getNodeName().endsWith("propname")) {
+                        type = FIND_PROPERTY_NAMES;
+                    }
+                    if (currentNode.getNodeName().endsWith("allprop")) {
+                        type = FIND_ALL_PROP;
+                    }
+                    break;
+                }
+            }
+        } catch(Exception e) {
+            // Most likely there was no content : we use the defaults.
+            // TODO : Enhance that !
+        }
+        return type;
+    }
+
+
+    private int getDepth(HttpServletRequest req) {
+        int depth = INFINITY;
+        String depthStr = req.getHeader("Depth");
+
+        if (depthStr == null) {
+            depth = INFINITY;
+        } else {
+            if (depthStr.equals("0")) {
+                depth = 0;
+            } else if (depthStr.equals("1")) {
+                depth = 1;
+            } else if (depthStr.equals("infinity")) {
+                depth = INFINITY;
+            }
+        }
+        return depth;
+    }
+
+
+    /**
+     * URL rewriter.
+     *
+     * @param path Path which has to be rewiten
+     */
+    protected String rewriteUrl(String path) {
+        return urlEncoder.encode( path );
+    }
+
+
+    /**
+     * Propfind helper method.
+     *
+     * @param req The servlet request
+     * @param resources Resources object associated with this context
+     * @param out XML response to the Propfind request
+     * @param path Path of the current resource
+     * @param type Propfind type
+     * @param propertiesVector If the propfind type is find properties by
+     * name, then this Vector contains those properties
+     */
+    protected void parseProperties(HttpServletRequest req,
+                                 XMLWriter out,
+                                 String path, int type,
+                                 Vector propertiesVector) {
+
+        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
+        // (the "toUpperCase()" avoids problems on Windows systems)
+        if (path.toUpperCase().startsWith("/WEB-INF") ||
+            path.toUpperCase().startsWith("/META-INF"))
+            return;
+
+        File cacheEntry = new File(basePath, path);
+
+        out.writeElement(null, "response", XMLWriter.OPENING);
+        String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
+                                   + WebdavStatus.getStatusText
+                                   (WebdavStatus.SC_OK));
+
+        // Generating href element
+        out.writeElement(null, "href", XMLWriter.OPENING);
+
+        String href = req.getContextPath();// + req.getServletPath() + 
+        //  req.getPathInfo();
+        
+        // ??? 
+        if ((href.endsWith("/")) && (path.startsWith("/")))
+            href += path.substring(1);
+        else
+            href += path;
+        if ((cacheEntry.isDirectory()) && (!href.endsWith("/")))
+            href += "/";
+
+        out.writeText(rewriteUrl(href));
+
+        out.writeElement(null, "href", XMLWriter.CLOSING);
+
+        String resourceName = path;
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash != -1)
+            resourceName = resourceName.substring(lastSlash + 1);
+
+        switch (type) {
+
+        case FIND_ALL_PROP :
+
+            out.writeElement(null, "propstat", XMLWriter.OPENING);
+            out.writeElement(null, "prop", XMLWriter.OPENING);
+
+            out.writeProperty
+                (null, "creationdate",
+                 getISOCreationDate(cacheEntry.lastModified()));
+            out.writeElement(null, "displayname", XMLWriter.OPENING);
+            out.writeData(resourceName);
+            out.writeElement(null, "displayname", XMLWriter.CLOSING);
+            if (!cacheEntry.isDirectory()) {
+                out.writeProperty(null, "getlastmodified", 
+                        FastHttpDateFormat.formatDate(cacheEntry.lastModified(), 
+                                null));
+                out.writeProperty(null, "getcontentlength",  
+                        String.valueOf(cacheEntry.length()));
+                String contentType = 
+                    getServletContext().getMimeType(cacheEntry.getName());
+                if (contentType != null) {
+                    out.writeProperty(null, "getcontenttype",  contentType);
+                }
+                out.writeProperty(null, "getetag",  getETag(cacheEntry));
+                out.writeElement(null, "resourcetype", XMLWriter.NO_CONTENT);
+            } else {
+                out.writeElement(null, "resourcetype", XMLWriter.OPENING);
+                out.writeElement(null, "collection", XMLWriter.NO_CONTENT);
+                out.writeElement(null, "resourcetype", XMLWriter.CLOSING);
+            }
+
+            out.writeProperty(null, "source", "");
+            out.writeElement(null, "prop", XMLWriter.CLOSING);
+            out.writeElement(null, "status", XMLWriter.OPENING);
+            out.writeText(status);
+            out.writeElement(null, "status", XMLWriter.CLOSING);
+            out.writeElement(null, "propstat", XMLWriter.CLOSING);
+            break;
+
+        case FIND_PROPERTY_NAMES :
+            out.writeElement(null, "propstat", XMLWriter.OPENING);
+            out.writeElement(null, "prop", XMLWriter.OPENING);
+            out.writeElement(null, "creationdate",  XMLWriter.NO_CONTENT);
+            out.writeElement(null, "displayname", XMLWriter.NO_CONTENT);
+            if (! cacheEntry.isDirectory()) {
+                out.writeElement(null, "getcontentlanguage",
+                                          XMLWriter.NO_CONTENT);
+                out.writeElement(null, "getcontentlength",
+                                          XMLWriter.NO_CONTENT);
+                out.writeElement(null, "getcontenttype",
+                                          XMLWriter.NO_CONTENT);
+                out.writeElement(null, "getetag",
+                                          XMLWriter.NO_CONTENT);
+                out.writeElement(null, "getlastmodified",
+                                          XMLWriter.NO_CONTENT);
+            }
+            out.writeElement(null, "resourcetype", XMLWriter.NO_CONTENT);
+            out.writeElement(null, "source", XMLWriter.NO_CONTENT);
+            out.writeElement(null, "lockdiscovery", XMLWriter.NO_CONTENT);
+
+            out.writeElement(null, "prop", XMLWriter.CLOSING);
+            out.writeElement(null, "status", XMLWriter.OPENING);
+            out.writeText(status);
+            out.writeElement(null, "status", XMLWriter.CLOSING);
+            out.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            break;
+
+        case FIND_BY_PROPERTY :
+
+            Vector propertiesNotFound = new Vector();
+
+            // Parse the list of properties
+            out.writeElement(null, "propstat", XMLWriter.OPENING);
+            out.writeElement(null, "prop", XMLWriter.OPENING);
+
+            genPropertiesFound(out, propertiesVector, cacheEntry, 
+                    resourceName, propertiesNotFound);
+
+            out.writeElement(null, "prop", XMLWriter.CLOSING);
+            out.writeElement(null, "status", XMLWriter.OPENING);
+            out.writeText(status);
+            out.writeElement(null, "status", XMLWriter.CLOSING);
+            out.writeElement(null, "propstat", XMLWriter.CLOSING);
+
+            Enumeration propertiesNotFoundList = propertiesNotFound.elements();
+
+            if (propertiesNotFoundList.hasMoreElements()) {
+                status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+                                    + " " + WebdavStatus.getStatusText
+                                    (WebdavStatus.SC_NOT_FOUND));
+
+                out.writeElement(null, "propstat", XMLWriter.OPENING);
+                out.writeElement(null, "prop", XMLWriter.OPENING);
+                while (propertiesNotFoundList.hasMoreElements()) {
+                    out.writeElement
+                        (null, (String) propertiesNotFoundList.nextElement(),
+                         XMLWriter.NO_CONTENT);
+                }
+                out.writeElement(null, "prop", XMLWriter.CLOSING);
+                out.writeElement(null, "status", XMLWriter.OPENING);
+                out.writeText(status);
+                out.writeElement(null, "status", XMLWriter.CLOSING);
+                out.writeElement(null, "propstat", XMLWriter.CLOSING);
+            }
+            break;
+
+        }
+
+        out.writeElement(null, "response", XMLWriter.CLOSING);
+    }
+
+
+    private void genPropertiesFound(XMLWriter out, Vector propertiesVector, 
+                                    File cacheEntry, String resourceName, 
+                                    Vector propertiesNotFound) {
+        Enumeration properties = propertiesVector.elements();
+
+        while (properties.hasMoreElements()) {
+            String property = (String) properties.nextElement();
+            if (property.equals("creationdate")) {
+                out.writeProperty(null, "creationdate",
+                     getISOCreationDate(cacheEntry.lastModified()));
+            } else if (property.equals("displayname")) {
+                out.writeElement(null, "displayname", XMLWriter.OPENING);
+                out.writeData(resourceName);
+                out.writeElement(null, "displayname", XMLWriter.CLOSING);
+            } else if (property.equals("getcontentlanguage")) {
+                if (cacheEntry.isDirectory()) {
+                    propertiesNotFound.addElement(property);
+                } else {
+                    out.writeElement(null, "getcontentlanguage",
+                            XMLWriter.NO_CONTENT);
+                }
+            } else if (property.equals("getcontentlength")) {
+                if (cacheEntry.isDirectory()) {
+                    propertiesNotFound.addElement(property);
+                } else {
+                    out.writeProperty(null, "getcontentlength",
+                         (String.valueOf(cacheEntry.length())));
+                }
+            } else if (property.equals("getcontenttype")) {
+                if (cacheEntry.isDirectory()) {
+                    propertiesNotFound.addElement(property);
+                } else {
+                    out.writeProperty(null, "getcontenttype",
+                         getServletContext().getMimeType(cacheEntry.getName()));
+                }
+            } else if (property.equals("getetag")) {
+                if (cacheEntry.isDirectory()) {
+                    propertiesNotFound.addElement(property);
+                } else {
+                    out.writeProperty(null, "getetag", getETag(cacheEntry));
+                }
+            } else if (property.equals("getlastmodified")) {
+                if (cacheEntry.isDirectory()) {
+                    propertiesNotFound.addElement(property);
+                } else {
+                    out.writeProperty(null, "getlastmodified", 
+                            FastHttpDateFormat
+                            .formatDate(cacheEntry.lastModified(), null));
+                }
+            } else if (property.equals("resourcetype")) {
+                if (cacheEntry.isDirectory()) {
+                    out.writeElement(null, "resourcetype",
+                                              XMLWriter.OPENING);
+                    out.writeElement(null, "collection",
+                                              XMLWriter.NO_CONTENT);
+                    out.writeElement(null, "resourcetype",
+                                              XMLWriter.CLOSING);
+                } else {
+                    out.writeElement(null, "resourcetype",
+                                              XMLWriter.NO_CONTENT);
+                }
+            } else if (property.equals("source")) {
+                out.writeProperty(null, "source", "");
+            } else if (property.equals("supportedlock")) {
+                // TODO: hook for Webdav2
+                propertiesNotFound.addElement(property);
+            } else if (property.equals("lockdiscovery")) {
+                propertiesNotFound.addElement(property);
+            } else {
+                propertiesNotFound.addElement(property);
+            }
+
+        }
+    }
+
+    // ------------------ PROPPATCH --------------------
+    
+    /**
+     * PROPPATCH Method.
+     */
+    protected void doProppatch(HttpServletRequest req,
+                               HttpServletResponse resp)
+            throws ServletException, IOException {
+        resp.sendError(WebdavStatus.SC_FORBIDDEN);
+    }
+
+    // ------------------ MKCOL --------------------
+
+    /**
+     * MKCOL Method.
+     */
+    protected void doMkcol(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        File object = new File(basePath,path);
+        
+        // Can't create a collection if a resource already exists at the given
+        // path
+        if (object.exists()) {
+            // Get allowed methods
+            StringBuffer methodsAllowed = determineMethodsAllowed(basePath,
+                                                                  req);
+
+            resp.addHeader("Allow", methodsAllowed.toString());
+
+            resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
+            return;
+        }
+
+        if (req.getInputStream().available() > 0) {
+            DocumentBuilder documentBuilder = getDocumentBuilder();
+            try {
+                Document document = documentBuilder.parse
+                    (new InputSource(req.getInputStream()));
+                // TODO : Process this request body
+                resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
+                return;
+
+            } catch(SAXException saxe) {
+                // Parse error - assume invalid content
+                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
+                return;
+            }
+        }
+
+        boolean result = true;
+        
+        File newDir = new File(basePath, path);
+        result = newDir.mkdir();
+
+        if (!result) {
+            resp.sendError(WebdavStatus.SC_CONFLICT,
+                           WebdavStatus.getStatusText
+                           (WebdavStatus.SC_CONFLICT));
+        } else {
+            resp.setStatus(WebdavStatus.SC_CREATED);
+            // Removing any lock-null resource which would be present
+            lockNullResources.remove(path);
+        }
+
+    }
+
+    /**
+     * DELETE Method.
+     */
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        deleteResource(req, resp);
+
+    }
+
+
+    /**
+     * This is not part of DefaultServlet - all PUT and write operations
+     * should be handled by webdav servlet, which allows more control
+     */
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        if (readOnly) {
+            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        if (path.indexOf("..") >= 0) {
+            // not supported, too dangerous
+            // what else to escape ?
+            resp.setStatus(404);
+            return;
+        }
+
+        File resFile = new File(basePath, path);
+        boolean exists = resFile.exists();
+        
+        // extra check
+        if (!resFile.getCanonicalPath().startsWith(basePathName)) {
+            //log.info("File outside basedir " + basePathS + " " + f);
+            resp.setStatus(404);
+            return;
+        }
+
+
+        boolean result = true;
+
+        // Temp. content file used to support partial PUT
+        //File contentFile = null;
+
+        String rangeHeader = req.getHeader("Content-Range");
+        Range range = null;
+        if ( rangeHeader != null ) {
+            // we have a header, but has bad value.
+            // original catalina code has a bug I think
+            range = Range.parseContentRange(rangeHeader);
+            if (range == null) { // can't parse
+                resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+                return;
+            }
+        }
+
+        InputStream resourceInputStream = null;
+
+        // Append data specified in ranges to existing content for this
+        // resource - create a temp. file on the local filesystem to
+        // perform this operation
+        // Assume just one range is specified for now
+        if (range != null) {
+            //contentFile = executePartialPut(req, range, path);
+            //resourceInputStream = new FileInputStream(contentFile);
+            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
+            return;
+        } else {
+            resourceInputStream = req.getInputStream();
+        }
+
+        try {
+            // will override 
+            FileOutputStream fos = new FileOutputStream(resFile);
+            CopyUtils.copy(resourceInputStream, fos);
+        } catch(IOException e) {
+            result = false;
+        }
+
+        if (result) {
+            if (exists) {
+                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+            } else {
+                resp.setStatus(HttpServletResponse.SC_CREATED);
+            }
+        } else {
+            resp.sendError(HttpServletResponse.SC_CONFLICT);
+        }
+
+        // Removing any lock-null resource which would be present
+        lockNullResources.remove(path);
+    }
+
+
+    /**
+     * Handle a partial PUT.  New content specified in request is appended to
+     * existing content in oldRevisionContent (if present). This code does
+     * not support simultaneous partial updates to the same resource.
+     */
+//    protected File executePartialPut(HttpServletRequest req, Range range,
+//                                     String path)
+//        throws IOException {
+//
+//        // Append data specified in ranges to existing content for this
+//        // resource - create a temp. file on the local filesystem to
+//        // perform this operation
+//        File tempDir = (File) getServletContext().getAttribute
+//            ("javax.servlet.context.tempdir");
+//        // Convert all '/' characters to '.' in resourcePath
+//        String convertedResourcePath = path.replace('/', '.');
+//        File contentFile = new File(tempDir, convertedResourcePath);
+//        if (contentFile.createNewFile()) {
+//            // Clean up contentFile when Tomcat is terminated
+//            contentFile.deleteOnExit();
+//        }
+//
+//        RandomAccessFile randAccessContentFile =
+//            new RandomAccessFile(contentFile, "rw");
+//
+//        Resource oldResource = null;
+//        try {
+//            Object obj = resources.lookup(path);
+//            if (obj instanceof Resource)
+//                oldResource = (Resource) obj;
+//        } catch (NamingException e) {
+//        }
+//
+//        // Copy data in oldRevisionContent to contentFile
+//        if (oldResource != null) {
+//            BufferedInputStream bufOldRevStream =
+//                new BufferedInputStream(oldResource.streamContent(),
+//                                        BUFFER_SIZE);
+//
+//            int numBytesRead;
+//            byte[] copyBuffer = new byte[BUFFER_SIZE];
+//            while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
+//                randAccessContentFile.write(copyBuffer, 0, numBytesRead);
+//            }
+//
+//            bufOldRevStream.close();
+//        }
+//
+//        randAccessContentFile.setLength(range.length);
+//
+//        // Append data in request input stream to contentFile
+//        randAccessContentFile.seek(range.start);
+//        int numBytesRead;
+//        byte[] transferBuffer = new byte[BUFFER_SIZE];
+//        BufferedInputStream requestBufInStream =
+//            new BufferedInputStream(req.getInputStream(), BUFFER_SIZE);
+//        while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
+//            randAccessContentFile.write(transferBuffer, 0, numBytesRead);
+//        }
+//        randAccessContentFile.close();
+//        requestBufInStream.close();
+//
+//        return contentFile;
+//
+//    }
+
+
+    /**
+     * COPY Method.
+     */
+    protected void doCopy(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        copyResource(req, resp);
+    }
+
+
+    /**
+     * MOVE Method.
+     */
+    protected void doMove(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        if (readOnly) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return;
+        }
+
+        if (isLocked(req)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return;
+        }
+
+        String path = getRelativePath(req);
+
+        if (copyResource(req, resp)) {
+            deleteResource(path, req, resp, false);
+        }
+
+    }
+
+
+    /**
+     * LOCK Method.
+     */
+    protected void doLock(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
+    }
+
+    /**
+     * UNLOCK Method.
+     */
+    protected void doUnlock(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
+    }
+
+    // -------------------------------------------------------- protected Methods
+
+    /**
+     * Generate the namespace declarations.
+     */
+    protected String generateNamespaceDeclarations() {
+        return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
+    }
+
+
+    /**
+     * Check to see if a resource is currently write locked. The method
+     * will look at the "If" header to make sure the client
+     * has give the appropriate lock tokens.
+     *
+     * @param req Servlet request
+     * @return boolean true if the resource is locked (and no appropriate
+     * lock token has been found for at least one of the non-shared locks which
+     * are present on the resource).
+     */
+    protected boolean isLocked(HttpServletRequest req) {
+        return false;
+    }
+
+    /**
+     * Check to see if a resource is currently write locked.
+     *
+     * @param path Path of the resource
+     * @param ifHeader "If" HTTP header which was included in the request
+     * @return boolean true if the resource is locked (and no appropriate
+     * lock token has been found for at least one of the non-shared locks which
+     * are present on the resource).
+     */
+    protected boolean isLocked(String path, String ifHeader) {
+        return false;
+    }
+
+
+    /**
+     * Copy a resource.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @return boolean true if the copy is successful
+     */
+    protected boolean copyResource(HttpServletRequest req,
+                                 HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        // Parsing destination header
+
+        String destinationPath = req.getHeader("Destination");
+
+        if (destinationPath == null) {
+            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
+            return false;
+        }
+
+        // Remove url encoding from destination
+        destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
+
+        destinationPath = removeDestinationPrefix(req, destinationPath);
+
+        // Normalise destination path (remove '.' and '..')
+        destinationPath = UrlUtils.normalize(destinationPath);
+
+        String contextPath = req.getContextPath();
+        if ((contextPath != null) &&
+            (destinationPath.startsWith(contextPath))) {
+            destinationPath = destinationPath.substring(contextPath.length());
+        }
+
+        String pathInfo = req.getPathInfo();
+        if (pathInfo != null) {
+            String servletPath = req.getServletPath();
+            if ((servletPath != null) &&
+                (destinationPath.startsWith(servletPath))) {
+                destinationPath = destinationPath.substring(servletPath.length());
+            }
+        }
+
+
+        if ((destinationPath.toUpperCase().startsWith("/WEB-INF")) ||
+            (destinationPath.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        String path = getRelativePath(req);
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        if (destinationPath.equals(path)) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        // Parsing overwrite header
+
+        boolean overwrite = true;
+        String overwriteHeader = req.getHeader("Overwrite");
+
+        if (overwriteHeader != null) {
+            if (overwriteHeader.equalsIgnoreCase("T")) {
+                overwrite = true;
+            } else {
+                overwrite = false;
+            }
+        }
+
+        // Overwriting the destination
+
+        boolean exists = true;
+        File f1 = new File(basePath, destinationPath);
+        if (!f1.exists()) {
+            exists = false;
+        }
+
+        if (overwrite) {
+            // Delete destination resource, if it exists
+            if (exists) {
+                if (!deleteResource(destinationPath, req, resp, true)) {
+                    return false;
+                }
+            } else {
+                resp.setStatus(WebdavStatus.SC_CREATED);
+            }
+        } else {
+            // If the destination exists, then it's a conflict
+            if (exists) {
+                resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
+                return false;
+            }
+        }
+
+        // Copying source to destination
+
+        Hashtable errorList = new Hashtable();
+
+        boolean result = copyResource(basePath, errorList,
+                                      path, destinationPath);
+
+        if ((!result) || (!errorList.isEmpty())) {
+            sendReport(req, resp, errorList);
+            return false;
+        }
+
+        // Removing any lock-null resource which would be present at
+        // the destination path
+        lockNullResources.remove(destinationPath);
+        return true;
+
+    }
+
+
+    private String removeDestinationPrefix(HttpServletRequest req, 
+                                           String destinationPath) {
+        int protocolIndex = destinationPath.indexOf("://");
+        if (protocolIndex >= 0) {
+            // if the Destination URL contains the protocol, we can safely
+            // trim everything upto the first "/" character after "://"
+            int firstSeparator =
+                destinationPath.indexOf("/", protocolIndex + 4);
+            if (firstSeparator < 0) {
+                destinationPath = "/";
+            } else {
+                destinationPath = destinationPath.substring(firstSeparator);
+            }
+        } else {
+            String hostName = req.getServerName();
+            if ((hostName != null) && (destinationPath.startsWith(hostName))) {
+                destinationPath = destinationPath.substring(hostName.length());
+            }
+
+            int portIndex = destinationPath.indexOf(":");
+            if (portIndex >= 0) {
+                destinationPath = destinationPath.substring(portIndex);
+            }
+
+            if (destinationPath.startsWith(":")) {
+                int firstSeparator = destinationPath.indexOf("/");
+                if (firstSeparator < 0) {
+                    destinationPath = "/";
+                } else {
+                    destinationPath =
+                        destinationPath.substring(firstSeparator);
+                }
+            }
+        }
+        return destinationPath;
+    }
+
+
+    /**
+     * Copy a collection.
+     *
+     * @param resources Resources implementation to be used
+     * @param errorList Hashtable containing the list of errors which occurred
+     * during the copy operation
+     * @param source Path of the resource to be copied
+     * @param dest Destination path
+     */
+    protected boolean copyResource(File resources, Hashtable errorList,
+                                   String source, String dest) {
+
+        File object = new File(basePath, source);
+        File destF = new File(basePath, dest);
+        
+        if (object.isDirectory()) {
+
+            boolean done = destF.mkdirs();
+            if (!done) {
+                errorList.put
+                    (dest, new Integer(WebdavStatus.SC_CONFLICT));
+                return false;
+            }
+
+            File[] enumeration = object.listFiles();
+            for (int i=0; i<enumeration.length; i++) {
+                String childDest = dest;
+                if (!childDest.equals("/"))
+                    childDest += "/";
+                childDest += enumeration[i].getName();
+                String childSrc = source;
+                if (!childSrc.equals("/"))
+                    childSrc += "/";
+                childSrc += enumeration[i].getName();
+                copyResource(resources, errorList, childSrc, childDest);
+            }
+
+        } else {
+
+            try {
+                CopyUtils.copy( new FileInputStream(object), 
+                    new FileOutputStream(dest));
+            } catch(IOException ex ) {
+                errorList.put
+                (source,
+                        new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                return false;
+            }
+
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Delete a resource.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @return boolean true if the copy is successful
+     */
+    protected boolean deleteResource(HttpServletRequest req,
+                                   HttpServletResponse resp)
+        throws ServletException, IOException {
+
+        String path = getRelativePath(req);
+
+        return deleteResource(path, req, resp, true);
+
+    }
+
+
+    /**
+     * Delete a resource.
+     *
+     * @param path Path of the resource which is to be deleted
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @param setStatus Should the response status be set on successful
+     *                  completion
+     */
+    protected boolean deleteResource(String path, HttpServletRequest req,
+                                   HttpServletResponse resp, boolean setStatus)
+        throws ServletException, IOException {
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            resp.sendError(WebdavStatus.SC_FORBIDDEN);
+            return false;
+        }
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        if (isLocked(path, ifHeader + lockTokenHeader)) {
+            resp.sendError(WebdavStatus.SC_LOCKED);
+            return false;
+        }
+
+        boolean exists = true;
+        File object = new File(basePath, path);
+        if (!object.exists()) {
+            exists = false;
+        }
+
+        if (!exists) {
+            resp.sendError(WebdavStatus.SC_NOT_FOUND);
+            return false;
+        }
+
+        boolean collection = object.isDirectory();
+
+        if (!collection) {
+            boolean deleted = object.delete();
+            if (!deleted) {
+                resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
+                return false;
+            }
+        } else {
+
+            Hashtable errorList = new Hashtable();
+
+            deleteCollection(req, basePath, path, errorList);
+            boolean deleted = object.delete();
+            if (!deleted) {
+                errorList.put(path, new Integer
+                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+            }
+
+            if (!errorList.isEmpty()) {
+
+                sendReport(req, resp, errorList);
+                return false;
+
+            }
+
+        }
+        if (setStatus) {
+            resp.setStatus(WebdavStatus.SC_NO_CONTENT);
+        }
+        return true;
+
+    }
+
+
+    /**
+     * Deletes a collection.
+     *
+     * @param resources Resources implementation associated with the context
+     * @param path Path to the collection to be deleted
+     * @param errorList Contains the list of the errors which occurred
+     */
+    protected void deleteCollection(HttpServletRequest req,
+                                  File resources,
+                                  String path, Hashtable errorList) {
+
+        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
+            (path.toUpperCase().startsWith("/META-INF"))) {
+            errorList.put(path, new Integer(WebdavStatus.SC_FORBIDDEN));
+            return;
+        }
+
+        String ifHeader = req.getHeader("If");
+        if (ifHeader == null)
+            ifHeader = "";
+
+        String lockTokenHeader = req.getHeader("Lock-Token");
+        if (lockTokenHeader == null)
+            lockTokenHeader = "";
+
+        File f = new File(basePath, path);
+        File[] enumeration = f.listFiles();
+
+        for (int i=0; i<enumeration.length; i++) {
+            String childName = path;
+            if (!childName.equals("/"))
+                childName += "/";
+            
+            childName += enumeration[i].getName();
+
+            if (isLocked(childName, ifHeader + lockTokenHeader)) {
+
+                errorList.put(childName, new Integer(WebdavStatus.SC_LOCKED));
+
+            } else {
+                File object = enumeration[i];
+                if (object.isDirectory()) {
+                    deleteCollection(req, resources, childName, errorList);
+                }
+                
+                boolean deleted = object.delete();
+                if (!deleted) {
+                    if (!(object.isDirectory())) {
+                        // If it's not a collection, then it's an unknown
+                        // error
+                        errorList.put
+                        (childName, new Integer
+                                (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
+                    }
+                }
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Send a multistatus element containing a complete error report to the
+     * client.
+     *
+     * @param req Servlet request
+     * @param resp Servlet response
+     * @param errorList List of error to be displayed
+     */
+    protected void sendReport(HttpServletRequest req, HttpServletResponse resp,
+                            Hashtable errorList)
+        throws ServletException, IOException {
+
+        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
+
+        String absoluteUri = req.getRequestURI();
+        String relativePath = getRelativePath(req);
+
+        XMLWriter generatedXML = new XMLWriter();
+        generatedXML.writeXMLHeader();
+
+        generatedXML.writeElement(null, "multistatus"
+                                  + generateNamespaceDeclarations(),
+                                  XMLWriter.OPENING);
+
+        Enumeration pathList = errorList.keys();
+        while (pathList.hasMoreElements()) {
+
+            String errorPath = (String) pathList.nextElement();
+            int errorCode = ((Integer) errorList.get(errorPath)).intValue();
+
+            generatedXML.writeElement(null, "response", XMLWriter.OPENING);
+
+            generatedXML.writeElement(null, "href", XMLWriter.OPENING);
+            String toAppend = errorPath.substring(relativePath.length());
+            if (!toAppend.startsWith("/"))
+                toAppend = "/" + toAppend;
+            generatedXML.writeText(absoluteUri + toAppend);
+            generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
+            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
+            generatedXML
+                .writeText("HTTP/1.1 " + errorCode + " "
+                           + WebdavStatus.getStatusText(errorCode));
+            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
+
+            generatedXML.writeElement(null, "response", XMLWriter.CLOSING);
+
+        }
+
+        generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING);
+
+        Writer writer = resp.getWriter();
+        writer.write(generatedXML.toString());
+        writer.close();
+
+    }
+
+
+
+
+    /**
+     * Return JAXP document builder instance.
+     */
+    protected DocumentBuilder getDocumentBuilder()
+        throws ServletException {
+        DocumentBuilder documentBuilder = null;
+        DocumentBuilderFactory documentBuilderFactory = null;
+        try {
+            documentBuilderFactory = DocumentBuilderFactory.newInstance();
+            documentBuilderFactory.setNamespaceAware(true);
+            documentBuilder = documentBuilderFactory.newDocumentBuilder();
+        } catch(ParserConfigurationException e) {
+            throw new ServletException("Parser error " + e);
+        }
+        return documentBuilder;
+    }
+
+
+
+    /**
+     * Get creation date in ISO format.
+     */
+    protected String getISOCreationDate(long creationDate) {
+        StringBuffer creationDateValue = new StringBuffer
+            (creationDateFormat.format
+             (new Date(creationDate)));
+        /*
+        int offset = Calendar.getInstance().getTimeZone().getRawOffset()
+            / 3600000; // FIXME ?
+        if (offset < 0) {
+            creationDateValue.append("-");
+            offset = -offset;
+        } else if (offset > 0) {
+            creationDateValue.append("+");
+        }
+        if (offset != 0) {
+            if (offset < 10)
+                creationDateValue.append("0");
+            creationDateValue.append(offset + ":00");
+        } else {
+            creationDateValue.append("Z");
+        }
+        */
+        return creationDateValue.toString();
+    }
+
+    /**
+     * Determines the methods normally allowed for the resource.
+     *
+     */
+    protected StringBuffer determineMethodsAllowed(File basePath,
+                                                   HttpServletRequest req) {
+
+        StringBuffer methodsAllowed = new StringBuffer();
+        String path = getRelativePath(req);
+        File object = new File(basePath, path);
+        if (!object.exists()) {
+            methodsAllowed.append("OPTIONS, MKCOL, PUT");
+            return methodsAllowed;
+        }
+        methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
+        methodsAllowed.append(", PROPPATCH, COPY, MOVE, PROPFIND");
+
+        if (object.isDirectory()) {
+            methodsAllowed.append(", PUT");
+        }
+
+        return methodsAllowed;
+    }
+}
+
+
+// --------------------------------------------------------  WebdavStatus Class
+
+
+/**
+ * Wraps the HttpServletResponse class to abstract the
+ * specific protocol used.  To support other protocols
+ * we would only need to modify this class and the
+ * WebDavRetCode classes.
+ *
+ * @author              Marc Eaddy
+ * @version             1.0, 16 Nov 1997
+ */
+class WebdavStatus {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * This Hashtable contains the mapping of HTTP and WebDAV
+     * status codes to descriptive text.  This is a static
+     * variable.
+     */
+    protected static Hashtable mapStatusCodes = new Hashtable();
+
+
+    // ------------------------------------------------------ HTTP Status Codes
+
+
+    /**
+     * Status code (200) indicating the request succeeded normally.
+     */
+    public static final int SC_OK = HttpServletResponse.SC_OK;
+
+
+    /**
+     * Status code (201) indicating the request succeeded and created
+     * a new resource on the server.
+     */
+    public static final int SC_CREATED = HttpServletResponse.SC_CREATED;
+
+
+    /**
+     * Status code (202) indicating that a request was accepted for
+     * processing, but was not completed.
+     */
+    public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED;
+
+
+    /**
+     * Status code (204) indicating that the request succeeded but that
+     * there was no new information to return.
+     */
+    public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT;
+
+
+    /**
+     * Status code (301) indicating that the resource has permanently
+     * moved to a new location, and that future references should use a
+     * new URI with their requests.
+     */
+    public static final int SC_MOVED_PERMANENTLY =
+        HttpServletResponse.SC_MOVED_PERMANENTLY;
+
+
+    /**
+     * Status code (302) indicating that the resource has temporarily
+     * moved to another location, but that future references should
+     * still use the original URI to access the resource.
+     */
+    public static final int SC_MOVED_TEMPORARILY =
+        HttpServletResponse.SC_MOVED_TEMPORARILY;
+
+
+    /**
+     * Status code (304) indicating that a conditional GET operation
+     * found that the resource was available and not modified.
+     */
+    public static final int SC_NOT_MODIFIED =
+        HttpServletResponse.SC_NOT_MODIFIED;
+
+
+    /**
+     * Status code (400) indicating the request sent by the client was
+     * syntactically incorrect.
+     */
+    public static final int SC_BAD_REQUEST =
+        HttpServletResponse.SC_BAD_REQUEST;
+
+
+    /**
+     * Status code (401) indicating that the request requires HTTP
+     * authentication.
+     */
+    public static final int SC_UNAUTHORIZED =
+        HttpServletResponse.SC_UNAUTHORIZED;
+
+
+    /**
+     * Status code (403) indicating the server understood the request
+     * but refused to fulfill it.
+     */
+    public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
+
+
+    /**
+     * Status code (404) indicating that the requested resource is not
+     * available.
+     */
+    public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
+
+
+    /**
+     * Status code (500) indicating an error inside the HTTP service
+     * which prevented it from fulfilling the request.
+     */
+    public static final int SC_INTERNAL_SERVER_ERROR =
+        HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+
+
+    /**
+     * Status code (501) indicating the HTTP service does not support
+     * the functionality needed to fulfill the request.
+     */
+    public static final int SC_NOT_IMPLEMENTED =
+        HttpServletResponse.SC_NOT_IMPLEMENTED;
+
+
+    /**
+     * Status code (502) indicating that the HTTP server received an
+     * invalid response from a server it consulted when acting as a
+     * proxy or gateway.
+     */
+    public static final int SC_BAD_GATEWAY =
+        HttpServletResponse.SC_BAD_GATEWAY;
+
+
+    /**
+     * Status code (503) indicating that the HTTP service is
+     * temporarily overloaded, and unable to handle the request.
+     */
+    public static final int SC_SERVICE_UNAVAILABLE =
+        HttpServletResponse.SC_SERVICE_UNAVAILABLE;
+
+
+    /**
+     * Status code (100) indicating the client may continue with
+     * its request.  This interim response is used to inform the
+     * client that the initial part of the request has been
+     * received and has not yet been rejected by the server.
+     */
+    public static final int SC_CONTINUE = 100;
+
+
+    /**
+     * Status code (405) indicating the method specified is not
+     * allowed for the resource.
+     */
+    public static final int SC_METHOD_NOT_ALLOWED = 405;
+
+
+    /**
+     * Status code (409) indicating that the request could not be
+     * completed due to a conflict with the current state of the
+     * resource.
+     */
+    public static final int SC_CONFLICT = 409;
+
+
+    /**
+     * Status code (412) indicating the precondition given in one
+     * or more of the request-header fields evaluated to false
+     * when it was tested on the server.
+     */
+    public static final int SC_PRECONDITION_FAILED = 412;
+
+
+    /**
+     * Status code (413) indicating the server is refusing to
+     * process a request because the request entity is larger
+     * than the server is willing or able to process.
+     */
+    public static final int SC_REQUEST_TOO_LONG = 413;
+
+
+    /**
+     * Status code (415) indicating the server is refusing to service
+     * the request because the entity of the request is in a format
+     * not supported by the requested resource for the requested
+     * method.
+     */
+    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+
+
+    // -------------------------------------------- Extended WebDav status code
+
+
+    /**
+     * Status code (207) indicating that the response requires
+     * providing status for multiple independent operations.
+     */
+    public static final int SC_MULTI_STATUS = 207;
+    // This one colides with HTTP 1.1
+    // "207 Parital Update OK"
+
+
+    /**
+     * Status code (418) indicating the entity body submitted with
+     * the PATCH method was not understood by the resource.
+     */
+    public static final int SC_UNPROCESSABLE_ENTITY = 418;
+    // This one colides with HTTP 1.1
+    // "418 Reauthentication Required"
+
+
+    /**
+     * Status code (419) indicating that the resource does not have
+     * sufficient space to record the state of the resource after the
+     * execution of this method.
+     */
+    public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
+    // This one colides with HTTP 1.1
+    // "419 Proxy Reauthentication Required"
+
+
+    /**
+     * Status code (420) indicating the method was not executed on
+     * a particular resource within its scope because some part of
+     * the method's execution failed causing the entire method to be
+     * aborted.
+     */
+    public static final int SC_METHOD_FAILURE = 420;
+
+
+    /**
+     * Status code (423) indicating the destination resource of a
+     * method is locked, and either the request did not contain a
+     * valid Lock-Info header, or the Lock-Info header identifies
+     * a lock held by another principal.
+     */
+    public static final int SC_LOCKED = 423;
+
+
+    // ------------------------------------------------------------ Initializer
+
+
+    static {
+        // HTTP 1.0 tatus Code
+        addStatusCodeMap(SC_OK, "OK");
+        addStatusCodeMap(SC_CREATED, "Created");
+        addStatusCodeMap(SC_ACCEPTED, "Accepted");
+        addStatusCodeMap(SC_NO_CONTENT, "No Content");
+        addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
+        addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
+        addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
+        addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
+        addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
+        addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
+        addStatusCodeMap(SC_NOT_FOUND, "Not Found");
+        addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
+        addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
+        addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
+        addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        addStatusCodeMap(SC_CONTINUE, "Continue");
+        addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
+        addStatusCodeMap(SC_CONFLICT, "Conflict");
+        addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
+        addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
+        addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
+        // WebDav Status Codes
+        addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
+        addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
+        addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
+                         "Insufficient Space On Resource");
+        addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
+        addStatusCodeMap(SC_LOCKED, "Locked");
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Returns the HTTP status text for the HTTP or WebDav status code
+     * specified by looking it up in the static mapping.  This is a
+     * static function.
+     *
+     * @param   nHttpStatusCode [IN] HTTP or WebDAV status code
+     * @return  A string with a short descriptive phrase for the
+     *                  HTTP status code (e.g., "OK").
+     */
+    public static String getStatusText(int nHttpStatusCode) {
+        Integer intKey = new Integer(nHttpStatusCode);
+
+        if (!mapStatusCodes.containsKey(intKey)) {
+            return "";
+        } else {
+            return (String) mapStatusCodes.get(intKey);
+        }
+    }
+
+
+    // -------------------------------------------------------- protected Methods
+
+
+    /**
+     * Adds a new status code -> status text mapping.  This is a static
+     * method because the mapping is a static variable.
+     *
+     * @param   nKey    [IN] HTTP or WebDAV status code
+     * @param   strVal  [IN] HTTP status text
+     */
+    protected static void addStatusCodeMap(int nKey, String strVal) {
+        mapStatusCodes.put(new Integer(nKey), strVal);
+    }
+
+};
+

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/WebdavServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/XMLWriter.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/XMLWriter.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/XMLWriter.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/XMLWriter.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,243 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.tomcat.servlets.file;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * XMLWriter helper class.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class XMLWriter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Opening tag.
+     */
+    public static final int OPENING = 0;
+
+
+    /**
+     * Closing tag.
+     */
+    public static final int CLOSING = 1;
+
+
+    /**
+     * Element with no content.
+     */
+    public static final int NO_CONTENT = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Buffer.
+     */
+    protected StringBuffer buffer = new StringBuffer();
+
+
+    /**
+     * Writer.
+     */
+    protected Writer writer = null;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructor.
+     */
+    public XMLWriter() {
+    }
+
+
+    /**
+     * Constructor.
+     */
+    public XMLWriter(Writer writer) {
+        this.writer = writer;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieve generated XML.
+     *
+     * @return String containing the generated XML
+     */
+    public String toString() {
+        return buffer.toString();
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param namespaceInfo Namespace info
+     * @param name Property name
+     * @param value Property value
+     */
+    public void writeProperty(String namespace, String namespaceInfo,
+                              String name, String value) {
+        writeElement(namespace, namespaceInfo, name, OPENING);
+        buffer.append(value);
+        writeElement(namespace, namespaceInfo, name, CLOSING);
+
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param name Property name
+     * @param value Property value
+     */
+    public void writeProperty(String namespace, String name, String value) {
+        writeElement(namespace, name, OPENING);
+        buffer.append(value);
+        writeElement(namespace, name, CLOSING);
+    }
+
+
+    /**
+     * Write property to the XML.
+     *
+     * @param namespace Namespace
+     * @param name Property name
+     */
+    public void writeProperty(String namespace, String name) {
+        writeElement(namespace, name, NO_CONTENT);
+    }
+
+
+    /**
+     * Write an element.
+     *
+     * @param name Element name
+     * @param namespace Namespace abbreviation
+     * @param type Element type
+     */
+    public void writeElement(String namespace, String name, int type) {
+        writeElement(namespace, null, name, type);
+    }
+
+
+    /**
+     * Write an element.
+     *
+     * @param namespace Namespace abbreviation
+     * @param namespaceInfo Namespace info
+     * @param name Element name
+     * @param type Element type
+     */
+    public void writeElement(String namespace, String namespaceInfo,
+                             String name, int type) {
+        if ((namespace != null) && (namespace.length() > 0)) {
+            switch (type) {
+            case OPENING:
+                if (namespaceInfo != null) {
+                    buffer.append("<" + namespace + ":" + name + " xmlns:"
+                                  + namespace + "=\""
+                                  + namespaceInfo + "\">");
+                } else {
+                    buffer.append("<" + namespace + ":" + name + ">");
+                }
+                break;
+            case CLOSING:
+                buffer.append("</" + namespace + ":" + name + ">\n");
+                break;
+            case NO_CONTENT:
+            default:
+                if (namespaceInfo != null) {
+                    buffer.append("<" + namespace + ":" + name + " xmlns:"
+                                  + namespace + "=\""
+                                  + namespaceInfo + "\"/>");
+                } else {
+                    buffer.append("<" + namespace + ":" + name + "/>");
+                }
+                break;
+            }
+        } else {
+            switch (type) {
+            case OPENING:
+                buffer.append("<" + name + ">");
+                break;
+            case CLOSING:
+                buffer.append("</" + name + ">\n");
+                break;
+            case NO_CONTENT:
+            default:
+                buffer.append("<" + name + "/>");
+                break;
+            }
+        }
+    }
+
+
+    /**
+     * Write text.
+     *
+     * @param text Text to append
+     */
+    public void writeText(String text) {
+        buffer.append(text);
+    }
+
+
+    /**
+     * Write data.
+     *
+     * @param data Data to append
+     */
+    public void writeData(String data) {
+        buffer.append("<![CDATA[" + data + "]]>");
+    }
+
+
+    /**
+     * Write XML Header.
+     */
+    public void writeXMLHeader() {
+        buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    }
+
+
+    /**
+     * Send data and reinitializes buffer.
+     */
+    public void sendData()
+        throws IOException {
+        if (writer != null) {
+            writer.write(buffer.toString());
+            buffer = new StringBuffer();
+        }
+    }
+
+
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/file/XMLWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,173 @@
+/*
+ * 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.tomcat.servlets.jsp;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.naming.NamingException;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.InstanceManager;
+import org.apache.jasper.EmbeddedServletOptions;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspC;
+import org.apache.jasper.Options;
+import org.apache.jasper.compiler.JspRuntimeContext;
+import org.apache.jasper.servlet.JspServletWrapper;
+
+/** 
+ * The actual compiler. Maps and compile a jsp-file to a class.
+ */
+public class JasperCompilerTemplateClassMapper 
+        extends SimpleTemplateClassMapper {
+    
+    public void init(ServletConfig config) {
+        this.config = config;
+        ServletContext context = config.getServletContext();
+        context.setAttribute(InstanceManager.class.getName(), 
+                new InstanceManager() {
+
+                    public void destroyInstance(Object arg0)
+                            throws IllegalAccessException,
+                            InvocationTargetException {
+                    }
+
+                    public Object newInstance(String arg0)
+                            throws IllegalAccessException,
+                            InvocationTargetException, NamingException,
+                            InstantiationException, ClassNotFoundException {
+                        return newInstance(arg0, 
+                                this.getClass().getClassLoader());
+                    }
+
+                    public void newInstance(Object o)
+                            throws IllegalAccessException,
+                            InvocationTargetException, NamingException {
+                    }
+
+                    public Object newInstance(String className,
+                                              ClassLoader classLoader)
+                            throws IllegalAccessException,
+                            InvocationTargetException, NamingException,
+                            InstantiationException, ClassNotFoundException {
+                        Class clazz = classLoader.loadClass(className);
+                        return clazz.newInstance();
+                    }
+            
+        });
+        //      Initialize the JSP Runtime Context
+        options = new EmbeddedServletOptions(config, context);
+        
+        rctxt = new JspRuntimeContext(context, options);
+        String basePath = context.getRealPath("/");
+        File f = new File(basePath + "/WEB-INF/classes");
+        f.mkdirs();
+        //fileS.initParams.put("scratchdir",  f.getAbsolutePath());
+        // if load-on-startup: allow other servlets to find us
+
+        
+    }
+    
+    private Options options;
+    private JspRuntimeContext rctxt;
+    private ServletConfig config;
+
+    public boolean needsReload(String jspFile, Servlet s) {
+        JspServletWrapper wrapper =
+            (JspServletWrapper) rctxt.getWrapper(jspFile);
+        // TODO: extract outdate info, compilation date, etc
+        return false;
+    }
+    
+    protected Servlet compileAndInitPage(ServletContext ctx, 
+                                         String jspUri, 
+                                         ServletConfig cfg) 
+    throws ServletException {
+        try {
+            if (config == null) {
+                init(cfg);
+            }
+            JspServletWrapper wrapper =
+                (JspServletWrapper) rctxt.getWrapper(jspUri);
+            if (wrapper == null) {
+                synchronized(this) {
+                    wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
+                    if (wrapper == null) {
+                        // Check if the requested JSP page exists, to avoid
+                        // creating unnecessary directories and files.
+                        if (null == ctx.getResource(jspUri)) {
+                            return null;
+                        }
+                        //boolean isErrorPage = exception != null;
+                        wrapper = new JspServletWrapper(cfg, options, jspUri,
+                                false, rctxt);
+                        rctxt.addWrapper(jspUri,wrapper);
+                    }
+                }
+            }
+
+            wrapper.getJspEngineContext().compile();
+            return wrapper.getServlet();
+        } catch (IOException ex) {
+            throw new ServletException(ex);
+        }
+    }
+
+    /**
+     *  
+     * Do the compilation - without JspServletWrapper
+     * 
+     * Options: 
+     *  - jasper.jar in classpath, we do Class.forName for main()
+     *  - TODO: exec jasper.sh ( or any other script set in params ) 
+     *  - TODO: redirect to a different servlet
+     * 
+     * Not used right - see previous method for a safer approach
+     * 
+     * @param ctx
+     * @param jspPath
+     */
+   public void compileJspDirect(ServletContext ctx, String jspPath) {
+        //ServletContextImpl ctx = (ServletContextImpl)sctx;
+        // Params to pass to jspc:
+        // classpath 
+        // webapp base dir
+        String baseDir = ctx.getRealPath("/");
+        // jsp path ( rel. base dir )
+
+        JspC jspc = new JspC();
+        jspc.setUriroot(baseDir);
+        jspc.setTrimSpaces(false);
+        jspc.setPoolingEnabled(true);
+        jspc.setErrorOnUseBeanInvalidClassAttribute(false);
+        jspc.setClassDebugInfo(true);
+        jspc.setCaching(true);
+        jspc.setSmapDumped(true);
+
+        try {
+            jspc.execute();
+        } catch (JasperException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JasperCompilerTemplateClassMapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,90 @@
+/*
+ * 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.tomcat.servlets.jsp;
+
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.addons.UserTemplateClassMapper;
+import org.apache.tomcat.integration.ObjectManager;
+
+/**
+ * Support for <servlet><jsp-file> config. 
+ * 
+ * A servlet can be configured with a jsp-file instead of a class. This can
+ * be translated into a regular servlet, with JspFileTemplateServlet class and
+ * the jsp-file as init parameter.  
+ * 
+ * This servlet is not jsp specific - you can put any templating file in the 
+ * jsp-file config ( if you can ignore the jsp in the name of the attribute ).
+ * The file will be converted to a servlet by a custom addon ( jasper in most
+ * cases ) 
+ * 
+ * @author Costin Manolache
+ */
+public class JspFileTemplateServlet extends HttpServlet {
+    
+    String jspFile; 
+    Servlet realJspServlet;
+
+    UserTemplateClassMapper mapper;
+
+    /** 
+     * If called from a <jsp-file> servlet, compile the servlet and init it.
+     */
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+        // Support <servlet><jsp-file>
+        String jspFileParam = config.getInitParameter("jsp-file");
+        // TODO: use extension to find the right UserTemplateClassMapper.
+        ObjectManager om = 
+            (ObjectManager) config.getServletContext().getAttribute(ObjectManager.ATTRIBUTE);
+        mapper = 
+            (UserTemplateClassMapper) om.get(
+                    UserTemplateClassMapper.class);
+        if (mapper == null) {
+            mapper = new SimpleTemplateClassMapper();
+        }
+        
+        if (jspFile == null && jspFileParam != null) {
+            jspFile = jspFileParam;
+        }
+        // exception will be thrown if not set properly
+        realJspServlet = mapper.loadProxy(jspFile, getServletContext(), 
+                config);
+        realJspServlet.init(config);
+    }
+
+    public void setJspFile(String jspFile) {
+        this.jspFile = jspFile;
+    }
+    
+    protected void service(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException 
+    {
+        // TODO: support reload
+        
+        // realJspServlet will be set - init will fail otherwise.
+        realJspServlet.service(req, res);
+    }
+} 

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/JspFileTemplateServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/PreCompileFilter.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/PreCompileFilter.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/PreCompileFilter.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/PreCompileFilter.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,71 @@
+package org.apache.tomcat.servlets.jsp;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+
+/** 
+ * Filter for JSPs to support 'preCompile' support.
+ * This is a silly feature, can and should be left out in prod.
+ * It needs to be used in the default config to pass the tests.
+ * 
+ * @author Costin Manolache
+ */
+public class PreCompileFilter extends HttpServlet {
+    // Copied from jasper.Constants to avoid compile dep
+    /**
+     * The query parameter that causes the JSP engine to just
+     * pregenerated the servlet but not invoke it. 
+     */
+    public static final String PRECOMPILE = 
+      System.getProperty("org.apache.jasper.Constants.PRECOMPILE", "jsp_precompile");
+    
+    /** 
+     * If called from a <jsp-file> servlet, compile the servlet and init it.
+     */
+    public void init(ServletConfig arg0) throws ServletException {
+        super.init(arg0);
+    }
+    
+    boolean preCompile(HttpServletRequest request) throws ServletException {
+        String queryString = request.getQueryString();
+        if (queryString == null) {
+            return (false);
+        }
+        int start = queryString.indexOf(PRECOMPILE);
+        if (start < 0) {
+            return (false);
+        }
+        queryString =
+            queryString.substring(start + PRECOMPILE.length());
+        if (queryString.length() == 0) {
+            return (true);             // ?jsp_precompile
+        }
+        if (queryString.startsWith("&")) {
+            return (true);             // ?jsp_precompile&foo=bar...
+        }
+        if (!queryString.startsWith("=")) {
+            return (false);            // part of some other name or value
+        }
+        int limit = queryString.length();
+        int ampersand = queryString.indexOf("&");
+        if (ampersand > 0) {
+            limit = ampersand;
+        }
+        String value = queryString.substring(1, limit);
+        if (value.equals("true")) {
+            return (true);             // ?jsp_precompile=true
+        } else if (value.equals("false")) {
+            // Spec says if jsp_precompile=false, the request should not
+            // be delivered to the JSP page; the easiest way to implement
+            // this is to set the flag to true, and precompile the page anyway.
+            // This still conforms to the spec, since it says the
+            // precompilation request can be ignored.
+            return (true);             // ?jsp_precompile=false
+        } else {
+            throw new ServletException("Cannot have request parameter " +
+                        PRECOMPILE + " set to " + value);
+        }
+    }
+} 

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/jsp/PreCompileFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Mime
View raw message