org.apache.catalina.servlets.DefaultServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.catalina.servlets.DefaultServlet.java

Source

/*
 *  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.catalina.servlets;

import com.darkhorseventures.database.ConnectionElement;
import com.darkhorseventures.database.ConnectionPool;
import com.darkhorseventures.framework.actions.ActionContext;
import com.zeroio.webdav.WebdavManager;
import com.zeroio.webdav.context.ModuleContext;
import org.apache.catalina.Globals;
import org.apache.catalina.util.MD5Encoder;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.util.StringManager;
import org.apache.catalina.util.URLEncoder;
import org.apache.commons.codec.binary.Base64;
import org.apache.naming.resources.Resource;
import org.apache.naming.resources.ResourceAttributes;
import org.apache.tomcat.util.http.FastHttpDateFormat;
import org.aspcfs.controller.ApplicationPrefs;
import org.aspcfs.controller.SystemStatus;
import org.aspcfs.controller.objectHookManager.ObjectHookAction;
import org.aspcfs.modules.login.base.AuthenticationItem;

import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;

/**
 * The default resource-serving servlet for most web applications,
 * used to serve static resources such as HTML pages and images.
 *
 * @author Craig R. McClanahan
 * @author Remy Maucherat
 * @version $Revision$ $Date$
 * @created October 26, 2004
 */

public class DefaultServlet extends HttpServlet {

    // ----------------------------------------------------- Instance Variables

    /**
     * The debugging detail level for this servlet.
     */
    protected int debug = 0;

    /**
     * The input buffer size to use when serving resources.
     */
    protected int input = 2048;

    /**
     * Should we generate directory listings?
     */
    protected boolean listings = true;

    /**
     * Read only flag. By default, it's set to true.
     */
    protected boolean readOnly = true;

    /**
     * The output buffer size to use when serving resources.
     */
    protected int output = 2048;

    /**
     * MD5 message digest provider.
     */
    protected static MessageDigest md5Helper;

    /**
     * The MD5 helper object for this class.
     */
    protected final static MD5Encoder md5Encoder = new MD5Encoder();

    /**
     * Array containing the safe characters set.
     */
    protected static URLEncoder urlEncoder;

    /**
     * Allow customized directory listing per directory.
     */
    protected String localXsltFile = null;

    /**
     * Allow customized directory listing per instance.
     */
    protected String globalXsltFile = null;

    /**
     * Allow a readme file to be included.
     */
    protected String readmeFile = null;

    // ----------------------------------------------------- Static Initializer

    protected final static String METHOD_GET = "GET";
    protected final static String METHOD_PUT = "PUT";
    protected final static String METHOD_HEAD = "HEAD";

    protected final static int CFS_SQLERROR = 425;

    public final static String fs = System.getProperty("file.separator");

    static {
        urlEncoder = new URLEncoder();
        urlEncoder.addSafeCharacter('-');
        urlEncoder.addSafeCharacter('_');
        urlEncoder.addSafeCharacter('.');
        urlEncoder.addSafeCharacter('*');
        urlEncoder.addSafeCharacter('/');
    }

    /**
     * MIME multipart separation string
     */
    protected final static String mimeSeparation = "CATALINA_MIME_BOUNDARY";

    /**
     * JNDI resources name.
     */
    protected final static String RESOURCES_JNDI_NAME = "java:/comp/Resources";

    /**
     * The string manager for this package.
     */
    protected static StringManager sm = StringManager.getManager(Constants.Package);

    /**
     * Size of file transfer buffer in bytes.
     */
    private final static int BUFFER_SIZE = 4096;

    private Hashtable connectionElements = new Hashtable();

    // --------------------------------------------------------- Public Methods

    /**
     * Finalize this servlet.
     */
    public void destroy() {
    }

    /**
     * Initialize this servlet.
     *
     * @throws ServletException Description of the Exception
     */
    public void init() throws ServletException {

        // Set our properties from the initialization parameters
        String value = null;
        try {
            value = getServletConfig().getInitParameter("debug");
            debug = Integer.parseInt(value);
        } catch (Throwable t) {
            ;
        }
        try {
            value = getServletConfig().getInitParameter("input");
            input = Integer.parseInt(value);
        } catch (Throwable t) {
            ;
        }
        try {
            value = getServletConfig().getInitParameter("listings");
            listings = (new Boolean(value)).booleanValue();
        } catch (Throwable t) {
            ;
        }
        try {
            value = getServletConfig().getInitParameter("readonly");
            if (value != null) {
                readOnly = (new Boolean(value)).booleanValue();
            }
        } catch (Throwable t) {
            ;
        }
        try {
            value = getServletConfig().getInitParameter("output");
            output = Integer.parseInt(value);
        } catch (Throwable t) {
            ;
        }

        globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");
        localXsltFile = getServletConfig().getInitParameter("localXsltFile");
        readmeFile = getServletConfig().getInitParameter("readmeFile");

        // Sanity check on the specified buffer sizes
        if (input < 256) {
            input = 256;
        }
        if (output < 256) {
            output = 256;
        }

        if (debug > 0) {
            log("DefaultServlet.init:  input buffer size=" + input + ", output buffer size=" + output);
        }

        // Load the MD5 helper used to calculate signatures.
        try {
            md5Helper = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new IllegalStateException();
        }
    }

    /*
     *  Utility Methods
     */
    /**
     * Description of the Method
     *
     * @param context Description of the Parameter
     * @return Description of the Return Value
     */
    protected boolean managerReady(ActionContext context) {
        if (context.getServletContext().getAttribute("WebdavManager") != null) {
            return true;
        } else {
            return false;
        }
    }

    protected String decodePath(String path) {
        try {
            return java.net.URLDecoder.decode(path, "UTF-8");
        } catch (java.io.UnsupportedEncodingException e) {
            return path;
        }
    }

    /**
     * Description of the Method
     *
     * @param context Description of the Parameter
     * @return Description of the Return Value
     */
    protected synchronized ConnectionElement retrieveConnectionElement(ActionContext context) {
        String serverName = context.getRequest().getServerName();
        if (!connectionElements.containsKey(serverName)) {
            System.out.println("DefaultServlet-> Initializing Connection Element");
            AuthenticationItem auth = new AuthenticationItem();
            try {
                ConnectionElement ce = auth.getConnectionElement(context);
                connectionElements.put(serverName, ce);
            } catch (SQLException e) {
                e.printStackTrace(System.out);
            }
        }
        return (ConnectionElement) connectionElements.get(serverName);
    }

    /**
     * Gets the path attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @return The path value
     */
    protected String getPath(ActionContext context) {
        ApplicationPrefs prefs = (ApplicationPrefs) context.getServletContext().getAttribute("applicationPrefs");
        return prefs.get("FILELIBRARY");
    }

    /**
     * Description of the Method
     *
     * @param context Description of the Parameter
     * @param object  Description of the Parameter
     */
    protected void processInsertHook(ActionContext context, Object object) {
        ConnectionPool sqlDriver = (ConnectionPool) context.getServletContext().getAttribute("ConnectionPool");
        ConnectionElement ce = this.getConnectionElement(context);
        this.getSystemStatus(context).processHook(context, ObjectHookAction.INSERT, null, object, sqlDriver, ce);
    }

    /**
     * Gets the systemStatus attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @param ce      Description of the Parameter
     * @return The systemStatus value
     */
    protected SystemStatus getSystemStatus(ActionContext context, ConnectionElement ce) {
        return (SystemStatus) ((Hashtable) context.getServletContext().getAttribute("SystemStatus"))
                .get(ce.getUrl());
    }

    /**
     * Gets the systemStatus attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @return The systemStatus value
     */
    protected SystemStatus getSystemStatus(ActionContext context) {
        return getSystemStatus(context, this.getConnectionElement(context));
    }

    /**
     * Gets the connectionElement attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @return The connectionElement value
     */
    protected ConnectionElement getConnectionElement(ActionContext context) {
        String serverName = context.getRequest().getServerName();
        return (ConnectionElement) connectionElements.get(serverName);
    }

    /**
     * Gets the webdavManager attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @return The webdavManager value
     */
    protected WebdavManager getWebdavManager(ActionContext context) {
        ConnectionElement ce = this.getConnectionElement(context);
        SystemStatus thisSystem = (SystemStatus) this.getSystemStatus(context, ce);
        return thisSystem.getWebdavManager();
    }

    /**
     * Gets the userId attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @return The userId value
     */
    protected int getUserId(ActionContext context) {
        String username = this.getUser(context);
        return (getWebdavManager(context).getUser(username).getUserId());
    }

    /**
     * Gets the connection attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @return The connection value
     * @throws SQLException Description of the Exception
     */
    protected Connection getConnection(ActionContext context) throws SQLException {
        ConnectionElement ce = this.getConnectionElement(context);
        ConnectionPool sqlDriver = (ConnectionPool) context.getServletContext().getAttribute("ConnectionPool");
        return sqlDriver.getConnection(ce);
    }

    /**
     * Description of the Method
     *
     * @param db      Description of the Parameter
     * @param context Description of the Parameter
     */
    protected void freeConnection(Connection db, ActionContext context) {
        if (db != null) {
            ConnectionPool sqlDriver = (ConnectionPool) context.getServletContext().getAttribute("ConnectionPool");
            sqlDriver.free(db);
        }
        db = null;
    }

    /**
     * Description of the Method
     *
     * @param context   Description of the Parameter
     * @param argHeader Description of the Parameter
     * @return The authenticationParams value
     */
    protected HashMap getAuthenticationParams(ActionContext context, String argHeader) {
        HashMap params = new HashMap();
        StringTokenizer st = new StringTokenizer(argHeader, ",");
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            if (token.startsWith("Digest")) {
                token = token.substring("Digest".length());
            }
            String param = token.substring(0, token.indexOf("=")).trim();
            String value = token.substring(token.indexOf("\"") + 1, token.lastIndexOf("\""));
            params.put(param, value);
        }
        return params;
    }

    /**
     * Gets the user attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @return The user value
     */
    protected String getUser(ActionContext context) {
        String argHeader = context.getRequest().getHeader("Authorization");
        if (argHeader.toUpperCase().startsWith("BASIC")) {
            //BASIC AUTHENTICATION
            String username = "";
            try {
                String userpassEncoded = argHeader.substring(6);
                Base64 dec = new Base64();
                String userpassDecoded = new String(dec.decode(userpassEncoded.getBytes()));
                username = userpassDecoded.substring(0, userpassDecoded.indexOf(":"));
            } catch (Exception e) {
                e.printStackTrace(System.out);
            }
            return username;
        } else {
            //DIGEST AUTHENTICATION
            return (String) getAuthenticationParams(context, argHeader).get("username");
        }
    }

    /**
     * Gets the nonce attribute of the DefaultServlet object
     *
     * @return The nonce value
     */
    protected String generateNonce() {
        String timestamp = new java.sql.Timestamp(new Date().getTime()).toString();
        String random = org.aspcfs.utils.PasswordHash.getRandomString(0, 1);
        return new String(Base64.encodeBase64((timestamp + ":" + random).getBytes(), true));
    }

    /**
     * Gets the opaque attribute of the DefaultServlet object
     *
     * @return The opaque value
     */
    protected String generateOpaque() {
        String random = org.aspcfs.utils.PasswordHash.getRandomString(0, 10);
        return new String(Base64.encodeBase64((":" + random + ":").getBytes(), true));

    }

    /**
     * Description of the Method
     *
     * @param req  Description of the Parameter
     * @param resp Description of the Parameter
     * @throws ServletException Description of the Exception
     * @throws IOException      Description of the Exception
     */
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        ActionContext context = new ActionContext(this, req, resp);
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            doGet(context);
        } else if (method.equals(METHOD_PUT)) {
            doPut(context);
        } else if (method.equals(METHOD_HEAD)) {
            doHead(context);
        }
    }

    // ------------------------------------------------------ Protected Methods

    /**
     * Get resources. This method will try to retrieve the resources through
     * JNDI first, then in the servlet context if JNDI has failed (it could be
     * disabled). It will return null.
     *
     * @return A JNDI DirContext, or null.
     */
    protected DirContext getResources() {

        DirContext result = null;

        // Try the servlet context
        try {
            result = (DirContext) getServletContext().getAttribute(Globals.RESOURCES_ATTR);
        } catch (ClassCastException e) {
            // Failed : Not the right type
        }

        if (result != null) {
            return result;
        }

        // Try JNDI
        try {
            result = (DirContext) new InitialContext().lookup(RESOURCES_JNDI_NAME);
        } catch (NamingException e) {
            // Failed
        } catch (ClassCastException e) {
            // Failed : Not the right type
        }
        return result;
    }

    /**
     * Gets the cFSResources attribute of the DefaultServlet object
     *
     * @param context Description of the Parameter
     * @param db      Description of the Parameter
     * @return The cFSResources value
     * @throws SQLException Description of the Exception
     */
    protected synchronized ModuleContext getCFSResources(Connection db, ActionContext context) throws SQLException {
        ModuleContext rootContext = null;
        try {
            String username = this.getUser(context);
            ConnectionElement ce = this.getConnectionElement(context);
            SystemStatus thisSystem = this.getSystemStatus(context, ce);
            WebdavManager thisManager = this.getWebdavManager(context);
            rootContext = (ModuleContext) thisManager.getResources(db, thisSystem, username);
        } catch (ClassCastException e) {
            // Failed : Not the right type
        }
        return rootContext;
    }

    /**
     * Show HTTP header information.
     *
     * @param req Description of the Parameter
     */
    protected void showRequestInfo(HttpServletRequest req) {

        System.out.println();
        System.out.println("SlideDAV Request Info");
        System.out.println();

        // Show generic info
        System.out.println("Encoding : " + req.getCharacterEncoding());
        System.out.println("Length : " + req.getContentLength());
        System.out.println("Type : " + req.getContentType());

        System.out.println();
        System.out.println("Parameters");

        Enumeration parameters = req.getParameterNames();

        while (parameters.hasMoreElements()) {
            String paramName = (String) parameters.nextElement();
            String[] values = req.getParameterValues(paramName);
            System.out.print(paramName + " : ");
            for (int i = 0; i < values.length; i++) {
                System.out.print(values[i] + ", ");
            }
            System.out.println();
        }

        System.out.println();

        System.out.println("Protocol : " + req.getProtocol());
        System.out.println("Address : " + req.getRemoteAddr());
        System.out.println("Host : " + req.getRemoteHost());
        System.out.println("Scheme : " + req.getScheme());
        System.out.println("Server Name : " + req.getServerName());
        System.out.println("Server Port : " + req.getServerPort());

        System.out.println();
        System.out.println("Attributes");

        Enumeration attributes = req.getAttributeNames();

        while (attributes.hasMoreElements()) {
            String attributeName = (String) attributes.nextElement();
            System.out.print(attributeName + " : ");
            System.out.println(req.getAttribute(attributeName).toString());
        }

        System.out.println();

        // Show HTTP info
        System.out.println();
        System.out.println("HTTP Header Info");
        System.out.println();

        System.out.println("Authentication Type : " + req.getAuthType());
        System.out.println("HTTP Method : " + req.getMethod());
        System.out.println("Path Info : " + req.getPathInfo());
        System.out.println("Path translated : " + req.getPathTranslated());
        System.out.println("Query string : " + req.getQueryString());
        System.out.println("Remote user : " + req.getRemoteUser());
        System.out.println("Requested session id : " + req.getRequestedSessionId());
        System.out.println("Request URI : " + req.getRequestURI());
        System.out.println("Context path : " + req.getContextPath());
        System.out.println("Servlet path : " + req.getServletPath());
        System.out.println("User principal : " + req.getUserPrincipal());

        System.out.println();
        System.out.println("Headers : ");

        Enumeration headers = req.getHeaderNames();

        while (headers.hasMoreElements()) {
            String headerName = (String) headers.nextElement();
            System.out.print(headerName + " : ");
            System.out.println(req.getHeader(headerName));
        }

        System.out.println();
        System.out.println();

    }

    /**
     * Return the relative path associated with this servlet.
     *
     * @param request The servlet request we are processing
     * @return The relativePath value
     */
    protected String getRelativePath(HttpServletRequest request) {

        // Are we being processed by a RequestDispatcher.include()?
        if (request.getAttribute("javax.servlet.include.request_uri") != null) {
            String result = (String) request.getAttribute("javax.servlet.include.path_info");
            if (result == null) {
                result = (String) request.getAttribute("javax.servlet.include.servlet_path");
            }
            if ((result == null) || (result.equals(""))) {
                result = "/";
            }
            return (result);
        }

        // No, extract the desired path directly from the request
        String result = request.getPathInfo();
        if (result == null) {
            result = request.getServletPath();
        }
        if ((result == null) || (result.equals(""))) {
            result = "/";
        }
        return (result);
    }

    /**
     * Process a GET request for the specified resource.
     *
     * @param context Description of the Parameter
     * @throws IOException      if an input/output error occurs
     * @throws ServletException if a servlet-specified error occurs
     */
    protected void doGet(ActionContext context) throws IOException, ServletException {

        if (debug > 999) {
            showRequestInfo(context.getRequest());
        }

        // Serve the requested resource, including the data content
        try {
            serveResource(context, true, METHOD_GET);
        } catch (IOException ex) {
            // we probably have this check somewhere else too.
            if (ex.getMessage() != null && ex.getMessage().indexOf("Broken pipe") >= 0) {
                // ignore it.
            }
            throw ex;
        }

    }

    /**
     * Process a HEAD request for the specified resource.
     *
     * @param context  The servlet request we are processing
     * @throws IOException      if an input/output error occurs
     * @throws ServletException if a servlet-specified error occurs
     */
    protected void doHead(ActionContext context) throws IOException, ServletException {

        // Serve the requested resource, without the data content
        serveResource(context, false, METHOD_HEAD);

    }

    /**
     * Process a POST request for the specified resource.
     *
     * @param request  The servlet request we are processing
     * @param response The servlet response we are creating
     * @throws IOException      if an input/output error occurs
     * @throws ServletException if a servlet-specified error occurs
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        doGet(request, response);
    }

    /**
     * Process a POST request for the specified resource.
     *
     * @throws IOException      if an input/output error occurs
     * @throws ServletException if a servlet-specified error occurs
     */
    protected void doPut(ActionContext context) throws ServletException, IOException {

        //showRequestInfo(context.getRequest());

        if (readOnly) {
            context.getResponse().sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        String path = getRelativePath(context.getRequest());

        //Fix for MACOSX finder. Do not allow requests for files starting with a period
        if (path.indexOf("/.") > -1 || path.indexOf(".DS_Store") > -1) {
            return;
        }

        // Retrieve the resources
        Connection db = null;
        ModuleContext resources = null;
        SystemStatus thisSystem = null;
        Object object = null;

        boolean exists = true;
        boolean result = true;

        try {
            db = this.getConnection(context);
            resources = getCFSResources(db, context);

            if (resources == null) {
                context.getResponse().sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }

            thisSystem = this.getSystemStatus(context);

            try {
                object = resources.lookup(thisSystem, db, path);
            } catch (NamingException e) {
                exists = false;
            }

            // Temp. content file used to support partial PUT
            File contentFile = null;

            // Input stream for temp. content file used to support partial PUT
            FileInputStream contentFileInStream = null;

            //ResourceInfo resourceInfo = new ResourceInfo(thisSystem, path, resources);
            Range range = parseContentRange(context.getRequest(), context.getResponse());

            //InputStream resourceInputStream = null;
            ServletInputStream 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(context.getRequest(), range, path);
                //resourceInputStream = new FileInputStream(contentFile);
            } else {
                resourceInputStream = context.getRequest().getInputStream();
                //System.out.println("RESOURCE INPUT STREAM: " + resourceInputStream);
                System.out.println("CONTENT LENGTH: " + context.getRequest().getContentLength());
                //System.out.println("DATA: " + resourceInputStream.available());
            }

            try {
                Object thisObject = null;
                if (exists) {
                    //resources.rebind(path, newResource);
                    Resource oldResource = (Resource) object;
                    oldResource.setContent(resourceInputStream);
                    thisObject = resources.copyResource(thisSystem, db, path, oldResource);
                } else {
                    Resource newResource = new Resource(resourceInputStream);
                    thisObject = resources.copyResource(thisSystem, db, path, newResource);
                }
                if (thisObject != null) {
                    processInsertHook(context, thisObject);
                }
            } catch (NamingException e) {
                //e.printStackTrace(System.out);
                result = false;
            }
        } catch (SQLException e) {
            e.printStackTrace(System.out);
        } finally {
            this.freeConnection(db, context);
        }

        if (result) {
            if (exists) {
                context.getResponse().setStatus(HttpServletResponse.SC_NO_CONTENT);
            } else {
                context.getResponse().setStatus(HttpServletResponse.SC_CREATED);
            }
        } else {
            context.getResponse().sendError(HttpServletResponse.SC_CONFLICT);
        }
    }

    /**
     * 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.
     *
     * @param req   Description of the Parameter
     * @param range Description of the Parameter
     * @param path  Description of the Parameter
     * @return Description of the Return Value
     * @throws IOException Description of the Exception
     */
    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 = getResources().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;
    }

    /**
     * Process a POST request for the specified resource.
     *
     * @param req  Description of the Parameter
     * @param resp Description of the Parameter
     * @throws IOException      if an input/output error occurs
     * @throws ServletException if a servlet-specified error occurs
     */
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        if (readOnly) {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        String path = getRelativePath(req);

        // Retrieve the Catalina context
        // Retrieve the resources
        DirContext resources = getResources();

        if (resources == null) {
            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
        }

        boolean exists = true;
        try {
            resources.lookup(path);
        } catch (NamingException e) {
            exists = false;
        }

        if (exists) {
            boolean result = true;
            try {
                resources.unbind(path);
            } catch (NamingException e) {
                result = false;
            }
            if (result) {
                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
            } else {
                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            }
        } else {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
        }

    }

    /**
     * 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 resourceInfo File object
     * @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
     * @throws IOException Description of the Exception
     */
    protected boolean checkIfHeaders(HttpServletRequest request, HttpServletResponse response,
            ResourceInfo resourceInfo) throws IOException {

        return checkIfMatch(request, response, resourceInfo)
                && checkIfModifiedSince(request, response, resourceInfo)
                && checkIfNoneMatch(request, response, resourceInfo)
                && checkIfUnmodifiedSince(request, response, resourceInfo);
    }

    /**
     * Get the ETag associated with a file.
     *
     * @param resourceInfo File object
     * @return The eTag value
     */
    protected String getETag(ResourceInfo resourceInfo) {
        if (resourceInfo.strongETag != null) {
            return resourceInfo.strongETag;
        } else if (resourceInfo.weakETag != null) {
            return resourceInfo.weakETag;
        } else {
            return "W/\"" + resourceInfo.length + "-" + resourceInfo.date + "\"";
        }
    }

    /**
     * Return a context-relative path, beginning with a "/", that represents
     * the canonical version of the specified path after ".." and "." elements
     * are resolved out.  If the specified path attempts to go outside the
     * boundaries of the current context (i.e. too many ".." path elements
     * are present), return <code>null</code> instead.
     *
     * @param path Path to be normalized
     * @return Description of the Return Value
     */
    protected String normalize(String path) {

        if (path == null) {
            return null;
        }

        // Create a place for the normalized path
        String normalized = path;

        if (normalized == null) {
            return (null);
        }

        if (normalized.equals("/.")) {
            return "/";
        }

        // Normalize the slashes and add leading slash if necessary
        if (normalized.indexOf('\\') >= 0) {
            normalized = normalized.replace('\\', '/');
        }
        if (!normalized.startsWith("/")) {
            normalized = "/" + normalized;
        }

        // Resolve occurrences of "//" in the normalized path
        while (true) {
            int index = normalized.indexOf("//");
            if (index < 0) {
                break;
            }
            normalized = normalized.substring(0, index) + normalized.substring(index + 1);
        }

        // Resolve occurrences of "/./" in the normalized path
        while (true) {
            int index = normalized.indexOf("/./");
            if (index < 0) {
                break;
            }
            normalized = normalized.substring(0, index) + normalized.substring(index + 2);
        }

        // Resolve occurrences of "/../" in the normalized path
        while (true) {
            int index = normalized.indexOf("/../");
            if (index < 0) {
                break;
            }
            if (index == 0) {
                return (null);
            }
            // Trying to go outside our context
            int index2 = normalized.lastIndexOf('/', index - 1);
            normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
        }

        // Return the normalized path that we have completed
        return (normalized);
    }

    /**
     * URL rewriter.
     *
     * @param path Path which has to be rewiten
     * @return Description of the Return Value
     */
    protected String rewriteUrl(String path) {
        return urlEncoder.encode(path);
    }

    /**
     * Display the size of a file.
     *
     * @param buf      Description of the Parameter
     * @param filesize Description of the Parameter
     */
    protected void displaySize(StringBuffer buf, int filesize) {

        int leftside = filesize / 1024;
        int rightside = (filesize % 1024) / 103;
        // makes 1 digit
        // To avoid 0.0 for non-zero file, we bump to 0.1
        if (leftside == 0 && rightside == 0 && filesize != 0) {
            rightside = 1;
        }
        buf.append(leftside).append(".").append(rightside);
        buf.append(" KB");

    }

    /**
     * Serve the specified resource, optionally including the data content.
     *
     * @param context  The servlet request we are processing
     * @param content  Should the content be included?
     * @param method  Description of the Parameter
     * @throws IOException      if an input/output error occurs
     * @throws ServletException if a servlet-specified error occurs
     */
    protected void serveResource(ActionContext context, boolean content, String method)
            throws IOException, ServletException {

        //TODO: remove this hardcoding
        debug = 2;
        // Identify the requested resource path
        String path = getRelativePath(context.getRequest());

        if (path.indexOf("/.") > -1) {
            //MAC OSX Fix
            return;
        }

        if (debug > 0) {
            if (content) {
                log("DefaultServlet.serveResource:  Serving resource '" + path + "' headers and data");
            } else {
                log("DefaultServlet.serveResource:  Serving resource '" + path + "' headers only");
            }
        }

        // Retrieve the Catalina context and Resources implementation

        Connection db = null;
        ModuleContext resources = null;
        ResourceInfo resourceInfo = null;
        SystemStatus thisSystem = null;
        boolean status = true;
        try {
            //System.out.println("DefaultServlet-> Serving Resource " + (content ? "WITH CONTENT" : "WITHOUT CONTENT"));
            db = this.getConnection(context);
            resources = getCFSResources(db, context);
            if (resources == null) {
                context.getResponse().sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }
            thisSystem = this.getSystemStatus(context);
            if (method.equals(METHOD_GET)) {
                Object current = resources.lookup(thisSystem, db, path);
            }
            resourceInfo = new ResourceInfo(thisSystem, path, resources);
        } catch (SQLException e) {
            e.printStackTrace(System.out);
            context.getResponse().sendError(CFS_SQLERROR, e.getMessage());
            status = false;
        } catch (Exception e) {
        } finally {
            this.freeConnection(db, context);
        }
        if (!status) {
            return;
        }

        if (!resourceInfo.exists) {
            context.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND, context.getRequest().getRequestURI());
            return;
        }

        // If the resource is not a collection, and the resource path
        // ends with "/" or "\", return NOT FOUND
        if (!resourceInfo.collection) {
            if (path.endsWith("/") || (path.endsWith("\\"))) {
                context.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND,
                        context.getRequest().getRequestURI());
                return;
            }
        }

        // Check if the conditions specified in the optional If headers are
        // satisfied.
        if (!resourceInfo.collection) {

            // Checking If headers
            boolean included = (context.getRequest().getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null);
            if (!included && !checkIfHeaders(context.getRequest(), context.getResponse(), resourceInfo)) {
                return;
            }
        }

        // Find content type
        String contentType = getServletContext().getMimeType(resourceInfo.clientFilename);

        Vector ranges = null;

        if (resourceInfo.collection) {

            // Skip directory listings if we have been configured to
            // suppress them
            if (!listings) {
                context.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND,
                        context.getRequest().getRequestURI());
                return;
            }
            contentType = "text/html;charset=UTF-8";

        } else {
            // Parse range specifier
            ranges = parseRange(context.getRequest(), context.getResponse(), resourceInfo);
            // ETag header
            context.getResponse().setHeader("ETag", getETag(resourceInfo));
            // Last-Modified header
            if (debug > 0) {
                log("DefaultServlet.serveFile:  lastModified='" + (new Timestamp(resourceInfo.date)).toString()
                        + "'");
            }
            context.getResponse().setHeader("Last-Modified", resourceInfo.httpDate);
        }

        ServletOutputStream ostream = null;
        PrintWriter writer = null;

        if (content) {
            // Trying to retrieve the servlet output stream
            try {
                ostream = context.getResponse().getOutputStream();
            } catch (IllegalStateException e) {
                // If it fails, we try to get a Writer instead if we're
                // trying to serve a text file
                if ((contentType == null) || (contentType.startsWith("text"))) {
                    writer = context.getResponse().getWriter();
                } else {
                    throw e;
                }
            }
        }

        if ((resourceInfo.collection) || (((ranges == null) || (ranges.isEmpty()))
                && (context.getRequest().getHeader("Range") == null))) {

            // Set the appropriate output headers
            if (contentType != null) {
                if (debug > 0) {
                    log("DefaultServlet.serveFile:  contentType='" + contentType + "'");
                }
                context.getResponse().setContentType(contentType);
            }
            long contentLength = resourceInfo.length;
            if ((!resourceInfo.collection) && (contentLength >= 0)) {
                if (debug > 0) {
                    log("DefaultServlet.serveFile:  contentLength=" + contentLength);
                }
                context.getResponse().setContentLength((int) contentLength);
            }
            if (resourceInfo.collection) {
                if (content) {
                    // Serve the directory browser
                    resourceInfo.setStream(render(context.getRequest().getContextPath(), resourceInfo));
                }
            }
            // Copy the input stream to our output stream (if requested)
            if (content) {
                try {
                    context.getResponse().setBufferSize(output);
                } catch (IllegalStateException e) {
                    // Silent catch
                }
                if (ostream != null) {
                    copy(resourceInfo, ostream);
                } else {
                    copy(resourceInfo, writer);
                }
            }
        } else {
            if ((ranges == null) || (ranges.isEmpty())) {
                return;
            }
            // Partial content response.
            context.getResponse().setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            if (ranges.size() == 1) {
                Range range = (Range) ranges.elementAt(0);
                context.getResponse().addHeader("Content-Range",
                        "bytes " + range.start + "-" + range.end + "/" + range.length);
                context.getResponse().setContentLength((int) (range.end - range.start + 1));

                if (contentType != null) {
                    if (debug > 0) {
                        log("DefaultServlet.serveFile:  contentType='" + contentType + "'");
                    }
                    context.getResponse().setContentType(contentType);
                }

                if (content) {
                    try {
                        context.getResponse().setBufferSize(output);
                    } catch (IllegalStateException e) {
                        // Silent catch
                    }
                    if (ostream != null) {
                        copy(resourceInfo, ostream, range);
                    } else {
                        copy(resourceInfo, writer, range);
                    }
                }
            } else {

                context.getResponse().setContentType("multipart/byteranges; boundary=" + mimeSeparation);

                if (content) {
                    try {
                        context.getResponse().setBufferSize(output);
                    } catch (IllegalStateException e) {
                        // Silent catch
                    }
                    if (ostream != null) {
                        copy(resourceInfo, ostream, ranges.elements(), contentType);
                    } else {
                        copy(resourceInfo, writer, ranges.elements(), contentType);
                    }
                }
            }
        }
    }

    /**
     * Parse the content-range header.
     *
     * @param request  The servlet request we are processing
     * @param response The servlet response we are creating
     * @return Range
     * @throws IOException Description of the Exception
     */
    protected Range parseContentRange(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Retrieving the content-range header (if any is specified
        String rangeHeader = request.getHeader("Content-Range");

        if (rangeHeader == null) {
            return null;
        }

        // bytes is the only range unit supported
        if (!rangeHeader.startsWith("bytes")) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return null;
        }

        rangeHeader = rangeHeader.substring(6).trim();

        int dashPos = rangeHeader.indexOf('-');
        int slashPos = rangeHeader.indexOf('/');

        if (dashPos == -1) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return null;
        }

        if (slashPos == -1) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return null;
        }

        Range range = new Range();

        try {
            range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
            range.end = Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
            range.length = Long.parseLong(rangeHeader.substring(slashPos + 1, rangeHeader.length()));
        } catch (NumberFormatException e) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return null;
        }

        if (!range.validate()) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return null;
        }

        return range;
    }

    /**
     * Parse the range header.
     *
     * @param request      The servlet request we are processing
     * @param response     The servlet response we are creating
     * @param resourceInfo Description of the Parameter
     * @return Vector of ranges
     * @throws IOException Description of the Exception
     */
    protected Vector parseRange(HttpServletRequest request, HttpServletResponse response, ResourceInfo resourceInfo)
            throws IOException {

        // Checking If-Range
        String headerValue = request.getHeader("If-Range");

        if (headerValue != null) {

            long headerValueTime = (-1L);
            try {
                headerValueTime = request.getDateHeader("If-Range");
            } catch (Exception e) {
                ;
            }

            String eTag = getETag(resourceInfo);
            long lastModified = resourceInfo.date;

            if (headerValueTime == (-1L)) {

                // If the ETag the client gave does not match the entity
                // etag, then the entire entity is returned.
                if (!eTag.equals(headerValue.trim())) {
                    return null;
                }
            } else {

                // If the timestamp of the entity the client got is older than
                // the last modification date of the entity, the entire entity
                // is returned.
                if (lastModified > (headerValueTime + 1000)) {
                    return null;
                }
            }

        }

        long fileLength = resourceInfo.length;

        if (fileLength == 0) {
            return null;
        }

        // Retrieving the range header (if any is specified
        String rangeHeader = request.getHeader("Range");

        if (rangeHeader == null) {
            return null;
        }
        // bytes is the only range unit supported (and I don't see the point
        // of adding new ones).
        if (!rangeHeader.startsWith("bytes")) {
            response.addHeader("Content-Range", "bytes */" + fileLength);
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return null;
        }

        rangeHeader = rangeHeader.substring(6);

        // Vector which will contain all the ranges which are successfully
        // parsed.
        Vector result = new Vector();
        StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");

        // Parsing the range list
        while (commaTokenizer.hasMoreTokens()) {
            String rangeDefinition = commaTokenizer.nextToken().trim();

            Range currentRange = new Range();
            currentRange.length = fileLength;

            int dashPos = rangeDefinition.indexOf('-');

            if (dashPos == -1) {
                response.addHeader("Content-Range", "bytes */" + fileLength);
                response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                return null;
            }

            if (dashPos == 0) {

                try {
                    long offset = Long.parseLong(rangeDefinition);
                    currentRange.start = fileLength + offset;
                    currentRange.end = fileLength - 1;
                } catch (NumberFormatException e) {
                    response.addHeader("Content-Range", "bytes */" + fileLength);
                    response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    return null;
                }

            } else {

                try {
                    currentRange.start = Long.parseLong(rangeDefinition.substring(0, dashPos));
                    if (dashPos < rangeDefinition.length() - 1) {
                        currentRange.end = Long
                                .parseLong(rangeDefinition.substring(dashPos + 1, rangeDefinition.length()));
                    } else {
                        currentRange.end = fileLength - 1;
                    }
                } catch (NumberFormatException e) {
                    response.addHeader("Content-Range", "bytes */" + fileLength);
                    response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    return null;
                }

            }

            if (!currentRange.validate()) {
                response.addHeader("Content-Range", "bytes */" + fileLength);
                response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                return null;
            }

            result.addElement(currentRange);
        }

        return result;
    }

    /**
     * Append the request parameters to the redirection string before calling
     * sendRedirect.
     *
     * @param request      Description of the Parameter
     * @param redirectPath Description of the Parameter
     * @return Description of the Return Value
     */
    protected String appendParameters(HttpServletRequest request, String redirectPath) {

        StringBuffer result = new StringBuffer(rewriteUrl(redirectPath));

        String query = request.getQueryString();
        if (query != null) {
            result.append("?").append(query);
        }

        return result.toString();
    }

    /**
     * Decide which way to render. HTML or XML.
     *
     * @param contextPath  Description of the Parameter
     * @param resourceInfo Description of the Parameter
     * @return Description of the Return Value
     */
    protected InputStream render(String contextPath, ResourceInfo resourceInfo) {
        InputStream xsltInputStream = findXsltInputStream(resourceInfo.directory);

        if (xsltInputStream == null) {
            return renderHtml(contextPath, resourceInfo);
        } else {
            return renderXml(contextPath, resourceInfo, xsltInputStream);
        }

    }

    /**
     * Return an InputStream to an HTML representation of the contents
     * of this directory.
     *
     * @param contextPath     Context path to which our internal paths are
     *                        relative
     * @param resourceInfo    Description of the Parameter
     * @param xsltInputStream Description of the Parameter
     * @return Description of the Return Value
     */
    protected InputStream renderXml(String contextPath, ResourceInfo resourceInfo, InputStream xsltInputStream) {

        StringBuffer sb = new StringBuffer();

        sb.append("<?xml version=\"1.0\"?>");
        sb.append("<listing ");
        sb.append(" contextPath='");
        sb.append(contextPath);
        sb.append("'");
        sb.append(" directory='");
        sb.append(resourceInfo.path);
        sb.append("' ");
        sb.append(" hasParent='").append(!resourceInfo.path.equals("/"));
        sb.append("'>");

        sb.append("<entries>");

        try {

            // Render the directory entries within this directory
            DirContext directory = resourceInfo.directory;
            NamingEnumeration enum1 = resourceInfo.resources.list(resourceInfo.path);
            while (enum1.hasMoreElements()) {

                NameClassPair ncPair = (NameClassPair) enum1.nextElement();
                String resourceName = ncPair.getName();
                ResourceInfo childResourceInfo = new ResourceInfo(resourceName, directory);

                String trimmed = resourceName;
                if (trimmed.equalsIgnoreCase("WEB-INF") || trimmed.equalsIgnoreCase("META-INF")
                        || trimmed.equalsIgnoreCase(localXsltFile)) {
                    continue;
                }

                sb.append("<entry");
                sb.append(" type='").append(childResourceInfo.collection ? "dir" : "file").append("'");
                sb.append(" urlPath='").append(rewriteUrl(contextPath))
                        .append(rewriteUrl(resourceInfo.path + resourceName))
                        .append(childResourceInfo.collection ? "/" : "").append("'");
                if (!childResourceInfo.collection) {
                    sb.append(" size='").append(renderSize(childResourceInfo.length)).append("'");
                }
                sb.append(" date='").append(childResourceInfo.httpDate).append("'");

                sb.append(">");
                sb.append(trimmed);
                if (childResourceInfo.collection) {
                    sb.append("/");
                }
                sb.append("</entry>");

            }

        } catch (NamingException e) {
            // Something went wrong
            e.printStackTrace();
        }

        sb.append("</entries>");

        String readme = getReadme(resourceInfo.directory);

        if (readme != null) {
            sb.append("<readme><![CDATA[");
            sb.append(readme);
            sb.append("]]></readme>");
        }

        sb.append("</listing>");

        try {
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Source xmlSource = new StreamSource(new StringReader(sb.toString()));
            Source xslSource = new StreamSource(xsltInputStream);
            Transformer transformer = tFactory.newTransformer(xslSource);

            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
            StreamResult out = new StreamResult(osWriter);
            transformer.transform(xmlSource, out);
            osWriter.flush();
            return (new ByteArrayInputStream(stream.toByteArray()));
        } catch (Exception e) {
            log("directory transform failure: " + e.getMessage());
            return renderHtml(contextPath, resourceInfo);
        }
    }

    /**
     * Return an InputStream to an HTML representation of the contents
     * of this directory.
     *
     * @param contextPath  Context path to which our internal paths are
     *                     relative
     * @param resourceInfo Description of the Parameter
     * @return Description of the Return Value
     */
    protected InputStream renderHtml(String contextPath, ResourceInfo resourceInfo) {

        String name = resourceInfo.path;

        // Number of characters to trim from the beginnings of filenames
        int trim = name.length();
        if (!name.endsWith("/")) {
            trim += 1;
        }
        if (name.equals("/")) {
            trim = 1;
        }

        // Prepare a writer to a buffered area
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        OutputStreamWriter osWriter = null;
        try {
            osWriter = new OutputStreamWriter(stream, "UTF8");
        } catch (Exception e) {
            // Should never happen
            osWriter = new OutputStreamWriter(stream);
        }
        PrintWriter writer = new PrintWriter(osWriter);

        StringBuffer sb = new StringBuffer();

        // Render the page header
        sb.append("<html>\r\n");
        sb.append("<head>\r\n");
        sb.append("<title>");
        sb.append(sm.getString("directory.title", name));
        sb.append("</title>\r\n");
        sb.append("<STYLE><!--");
        sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
        sb.append("--></STYLE> ");
        sb.append("</head>\r\n");
        sb.append("<body>");
        sb.append("<h1>");
        sb.append(sm.getString("directory.title", name));

        // Render the link to our parent (if required)
        String parentDirectory = name;
        if (parentDirectory.endsWith("/")) {
            parentDirectory = parentDirectory.substring(0, parentDirectory.length() - 1);
        }
        int slash = parentDirectory.lastIndexOf('/');
        if (slash >= 0) {
            String parent = name.substring(0, slash);
            sb.append(" - <a href=\"");
            sb.append(rewriteUrl(contextPath));
            if (parent.equals("")) {
                parent = "/";
            }
            sb.append(rewriteUrl(parent));
            if (!parent.endsWith("/")) {
                sb.append("/");
            }
            sb.append("\">");
            sb.append("<b>");
            sb.append(sm.getString("directory.parent", parent));
            sb.append("</b>");
            sb.append("</a>");
        }

        sb.append("</h1>");
        sb.append("<HR size=\"1\" noshade=\"noshade\">");

        sb.append("<table width=\"100%\" cellspacing=\"0\"" + " cellpadding=\"5\" align=\"center\">\r\n");

        // Render the column headings
        sb.append("<tr>\r\n");
        sb.append("<td align=\"left\"><font size=\"+1\"><strong>");
        sb.append(sm.getString("directory.filename"));
        sb.append("</strong></font></td>\r\n");
        sb.append("<td align=\"center\"><font size=\"+1\"><strong>");
        sb.append(sm.getString("directory.size"));
        sb.append("</strong></font></td>\r\n");
        sb.append("<td align=\"right\"><font size=\"+1\"><strong>");
        sb.append(sm.getString("directory.lastModified"));
        sb.append("</strong></font></td>\r\n");
        sb.append("</tr>");

        try {

            // Render the directory entries within this directory
            DirContext directory = resourceInfo.directory;
            NamingEnumeration enum1 = resourceInfo.resources.list(resourceInfo.path);
            boolean shade = false;
            while (enum1.hasMoreElements()) {

                NameClassPair ncPair = (NameClassPair) enum1.nextElement();
                String resourceName = ncPair.getName();
                ResourceInfo childResourceInfo = new ResourceInfo(resourceName, directory);

                String trimmed = resourceName;
                if (trimmed.equalsIgnoreCase("WEB-INF") || trimmed.equalsIgnoreCase("META-INF")) {
                    continue;
                }

                sb.append("<tr");
                if (shade) {
                    sb.append(" bgcolor=\"#eeeeee\"");
                }
                sb.append(">\r\n");
                shade = !shade;

                sb.append("<td align=\"left\">&nbsp;&nbsp;\r\n");
                sb.append("<a href=\"");
                sb.append(rewriteUrl(contextPath));
                resourceName = rewriteUrl(name + resourceName);
                sb.append(resourceName);
                if (childResourceInfo.collection) {
                    sb.append("/");
                }
                sb.append("\"><tt>");
                sb.append(trimmed);
                if (childResourceInfo.collection) {
                    sb.append("/");
                }
                sb.append("</tt></a></td>\r\n");

                sb.append("<td align=\"right\"><tt>");
                if (childResourceInfo.collection) {
                    sb.append("&nbsp;");
                } else {
                    sb.append(renderSize(childResourceInfo.length));
                }
                sb.append("</tt></td>\r\n");

                sb.append("<td align=\"right\"><tt>");
                sb.append(childResourceInfo.httpDate);
                sb.append("</tt></td>\r\n");

                sb.append("</tr>\r\n");
            }

        } catch (NamingException e) {
            // Something went wrong
            e.printStackTrace();
        }

        // Render the page footer
        sb.append("</table>\r\n");

        sb.append("<HR size=\"1\" noshade=\"noshade\">");

        String readme = getReadme(resourceInfo.directory);
        if (readme != null) {
            sb.append(readme);
            sb.append("<HR size=\"1\" noshade=\"noshade\">");
        }

        sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
        sb.append("</body>\r\n");
        sb.append("</html>\r\n");

        // Return an input stream to the underlying bytes
        writer.write(sb.toString());
        writer.flush();
        return (new ByteArrayInputStream(stream.toByteArray()));
    }

    /**
     * Render the specified file size (in bytes).
     *
     * @param size File size (in bytes)
     * @return Description of the Return Value
     */
    protected String renderSize(long size) {

        long leftSide = size / 1024;
        long rightSide = (size % 1024) / 103;
        // Makes 1 digit
        if ((leftSide == 0) && (rightSide == 0) && (size > 0)) {
            rightSide = 1;
        }

        return ("" + leftSide + "." + rightSide + " kb");
    }

    /**
     * Get the readme file as a string.
     *
     * @param directory Description of the Parameter
     * @return The readme value
     */
    protected String getReadme(DirContext directory) {
        if (readmeFile != null) {
            try {
                Object obj = directory.lookup(readmeFile);

                if (obj != null && obj instanceof Resource) {
                    StringWriter buffer = new StringWriter();
                    InputStream is = ((Resource) obj).streamContent();
                    copyRange(new InputStreamReader(is), new PrintWriter(buffer));

                    return buffer.toString();
                }
            } catch (Throwable e) {
                ;
                /*
                 *  Should only be IOException or NamingException
                 *  can be ignored
                 */
            }
        }

        return null;
    }

    /**
     * Return the xsl template inputstream (if possible)
     *
     * @param directory Description of the Parameter
     * @return Description of the Return Value
     */
    protected InputStream findXsltInputStream(DirContext directory) {
        if (localXsltFile != null) {
            try {
                Object obj = directory.lookup(localXsltFile);
                if (obj != null && obj instanceof Resource) {
                    InputStream is = ((Resource) obj).streamContent();
                    if (is != null) {
                        return is;
                    }
                }
            } catch (Throwable e) {
                ;
                /*
                 *  Should only be IOException or NamingException
                 *  can be ignored
                 */
            }
        }

        /*
         *  Open and read in file in one fell swoop to reduce chance
         *  chance of leaving handle open.
         */
        if (globalXsltFile != null) {
            FileInputStream fis = null;

            try {
                File f = new File(globalXsltFile);
                if (f.exists()) {
                    fis = new FileInputStream(f);
                    byte b[] = new byte[(int) f.length()];
                    /*
                     *  danger!
                     */
                    fis.read(b);
                    return new ByteArrayInputStream(b);
                }
            } catch (Throwable e) {
                log("This shouldn't happen (?)...", e);
                return null;
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (Throwable e) {
                    ;
                }
            }
        }

        return null;
    }

    // -------------------------------------------------------- Private Methods

    /**
     * Check if the if-match condition is satisfied.
     *
     * @param request      The servlet request we are processing
     * @param response     The servlet response we are creating
     * @param resourceInfo File object
     * @return boolean true if the resource meets the specified condition,
     *         and false if the condition is not satisfied, in which case request
     *         processing is stopped
     * @throws IOException Description of the Exception
     */
    private boolean checkIfMatch(HttpServletRequest request, HttpServletResponse response,
            ResourceInfo resourceInfo) throws IOException {

        String eTag = getETag(resourceInfo);
        String headerValue = request.getHeader("If-Match");
        if (headerValue != null) {
            if (headerValue.indexOf('*') == -1) {

                StringTokenizer commaTokenizer = new StringTokenizer(headerValue, ",");
                boolean conditionSatisfied = false;

                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
                    String currentToken = commaTokenizer.nextToken();
                    if (currentToken.trim().equals(eTag)) {
                        conditionSatisfied = true;
                    }
                }

                // If none of the given ETags match, 412 Precodition failed is
                // sent back
                if (!conditionSatisfied) {
                    response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Check if the if-modified-since condition is satisfied.
     *
     * @param request      The servlet request we are processing
     * @param response     The servlet response we are creating
     * @param resourceInfo File object
     * @return boolean true if the resource meets the specified condition,
     *         and false if the condition is not satisfied, in which case request
     *         processing is stopped
     * @throws IOException Description of the Exception
     */
    private boolean checkIfModifiedSince(HttpServletRequest request, HttpServletResponse response,
            ResourceInfo resourceInfo) throws IOException {
        try {
            long headerValue = request.getDateHeader("If-Modified-Since");
            long lastModified = resourceInfo.date;
            if (headerValue != -1) {

                // If an If-None-Match header has been specified, if modified since
                // is ignored.
                if ((request.getHeader("If-None-Match") == null) && (lastModified <= headerValue + 1000)) {
                    // The entity has not been modified since the date
                    // specified by the client. This is not an error case.
                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                    return false;
                }
            }
        } catch (IllegalArgumentException illegalArgument) {
            return true;
        }
        return true;
    }

    /**
     * Check if the if-none-match condition is satisfied.
     *
     * @param request      The servlet request we are processing
     * @param response     The servlet response we are creating
     * @param resourceInfo File object
     * @return boolean true if the resource meets the specified condition,
     *         and false if the condition is not satisfied, in which case request
     *         processing is stopped
     * @throws IOException Description of the Exception
     */
    private boolean checkIfNoneMatch(HttpServletRequest request, HttpServletResponse response,
            ResourceInfo resourceInfo) throws IOException {

        String eTag = getETag(resourceInfo);
        String headerValue = request.getHeader("If-None-Match");
        if (headerValue != null) {

            boolean conditionSatisfied = false;

            if (!headerValue.equals("*")) {

                StringTokenizer commaTokenizer = new StringTokenizer(headerValue, ",");

                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
                    String currentToken = commaTokenizer.nextToken();
                    if (currentToken.trim().equals(eTag)) {
                        conditionSatisfied = true;
                    }
                }

            } else {
                conditionSatisfied = true;
            }

            if (conditionSatisfied) {

                // For GET and HEAD, we should respond with
                // 304 Not Modified.
                // For every other method, 412 Precondition Failed is sent
                // back.
                if (("GET".equals(request.getMethod())) || ("HEAD".equals(request.getMethod()))) {
                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                    return false;
                } else {
                    response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Check if the if-unmodified-since condition is satisfied.
     *
     * @param request      The servlet request we are processing
     * @param response     The servlet response we are creating
     * @param resourceInfo File object
     * @return boolean true if the resource meets the specified condition,
     *         and false if the condition is not satisfied, in which case request
     *         processing is stopped
     * @throws IOException Description of the Exception
     */
    private boolean checkIfUnmodifiedSince(HttpServletRequest request, HttpServletResponse response,
            ResourceInfo resourceInfo) throws IOException {
        try {
            long lastModified = resourceInfo.date;
            long headerValue = request.getDateHeader("If-Unmodified-Since");
            if (headerValue != -1) {
                if (lastModified > (headerValue + 1000)) {
                    // The entity has not been modified since the date
                    // specified by the client. This is not an error case.
                    response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
                    return false;
                }
            }
        } catch (IllegalArgumentException illegalArgument) {
            return true;
        }
        return true;
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param ostream      The output stream to write to
     * @param resourceInfo Description of the Parameter
     * @throws IOException if an input/output error occurs
     */
    private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream) throws IOException {

        IOException exception = null;

        // Optimization: If the binary content has already been loaded, send
        // it directly
        if (resourceInfo.file != null) {
            byte buffer[] = resourceInfo.file.getContent();
            if (buffer != null) {
                ostream.write(buffer, 0, buffer.length);
                return;
            }
        }

        InputStream resourceInputStream = resourceInfo.getStream();
        InputStream istream = new BufferedInputStream(resourceInputStream, input);

        // Copy the input stream to the output stream
        exception = copyRange(istream, ostream);

        // Clean up the input stream
        try {
            istream.close();
        } catch (Throwable t) {
            ;
        }

        // Rethrow any exception that has occurred
        if (exception != null) {
            throw exception;
        }
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param writer       The writer to write to
     * @param resourceInfo Description of the Parameter
     * @throws IOException if an input/output error occurs
     */
    private void copy(ResourceInfo resourceInfo, PrintWriter writer) throws IOException {

        IOException exception = null;

        InputStream resourceInputStream = resourceInfo.getStream();
        // FIXME : i18n ?
        Reader reader = new InputStreamReader(resourceInputStream);

        // Copy the input stream to the output stream
        exception = copyRange(reader, writer);

        // Clean up the reader
        try {
            reader.close();
        } catch (Throwable t) {
            ;
        }

        // Rethrow any exception that has occurred
        if (exception != null) {
            throw exception;
        }
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param resourceInfo The ResourceInfo object
     * @param ostream      The output stream to write to
     * @param range        Range the client wanted to retrieve
     * @throws IOException if an input/output error occurs
     */
    private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream, Range range) throws IOException {

        IOException exception = null;

        InputStream resourceInputStream = resourceInfo.getStream();
        InputStream istream = new BufferedInputStream(resourceInputStream, input);
        exception = copyRange(istream, ostream, range.start, range.end);

        // Clean up the input stream
        try {
            istream.close();
        } catch (Throwable t) {
            ;
        }

        // Rethrow any exception that has occurred
        if (exception != null) {
            throw exception;
        }
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param resourceInfo The ResourceInfo object
     * @param writer       The writer to write to
     * @param range        Range the client wanted to retrieve
     * @throws IOException if an input/output error occurs
     */
    private void copy(ResourceInfo resourceInfo, PrintWriter writer, Range range) throws IOException {

        IOException exception = null;

        InputStream resourceInputStream = resourceInfo.getStream();
        Reader reader = new InputStreamReader(resourceInputStream);
        exception = copyRange(reader, writer, range.start, range.end);

        // Clean up the input stream
        try {
            reader.close();
        } catch (Throwable t) {
            ;
        }

        // Rethrow any exception that has occurred
        if (exception != null) {
            throw exception;
        }
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param resourceInfo The ResourceInfo object
     * @param ostream      The output stream to write to
     * @param ranges       Enumeration of the ranges the client wanted to retrieve
     * @param contentType  Content type of the resource
     * @throws IOException if an input/output error occurs
     */
    private void copy(ResourceInfo resourceInfo, ServletOutputStream ostream, Enumeration ranges,
            String contentType) throws IOException {

        IOException exception = null;

        while ((exception == null) && (ranges.hasMoreElements())) {

            InputStream resourceInputStream = resourceInfo.getStream();
            InputStream istream =
                    // FIXME: internationalization???????
                    new BufferedInputStream(resourceInputStream, input);

            Range currentRange = (Range) ranges.nextElement();

            // Writing MIME header.
            ostream.println();
            ostream.println("--" + mimeSeparation);
            if (contentType != null) {
                ostream.println("Content-Type: " + contentType);
            }
            ostream.println("Content-Range: bytes " + currentRange.start + "-" + currentRange.end + "/"
                    + currentRange.length);
            ostream.println();

            // Printing content
            exception = copyRange(istream, ostream, currentRange.start, currentRange.end);

            try {
                istream.close();
            } catch (Throwable t) {
                ;
            }

        }

        ostream.println();
        ostream.print("--" + mimeSeparation + "--");

        // Rethrow any exception that has occurred
        if (exception != null) {
            throw exception;
        }
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param resourceInfo The ResourceInfo object
     * @param writer       The writer to write to
     * @param ranges       Enumeration of the ranges the client wanted to retrieve
     * @param contentType  Content type of the resource
     * @throws IOException if an input/output error occurs
     */
    private void copy(ResourceInfo resourceInfo, PrintWriter writer, Enumeration ranges, String contentType)
            throws IOException {

        IOException exception = null;

        while ((exception == null) && (ranges.hasMoreElements())) {

            InputStream resourceInputStream = resourceInfo.getStream();
            Reader reader = new InputStreamReader(resourceInputStream);

            Range currentRange = (Range) ranges.nextElement();

            // Writing MIME header.
            writer.println();
            writer.println("--" + mimeSeparation);
            if (contentType != null) {
                writer.println("Content-Type: " + contentType);
            }
            writer.println("Content-Range: bytes " + currentRange.start + "-" + currentRange.end + "/"
                    + currentRange.length);
            writer.println();

            // Printing content
            exception = copyRange(reader, writer, currentRange.start, currentRange.end);

            try {
                reader.close();
            } catch (Throwable t) {
                ;
            }

        }

        writer.println();
        writer.print("--" + mimeSeparation + "--");

        // Rethrow any exception that has occurred
        if (exception != null) {
            throw exception;
        }
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param istream The input stream to read from
     * @param ostream The output stream to write to
     * @return Exception which occurred during processing
     */
    private IOException copyRange(InputStream istream, ServletOutputStream ostream) {

        // Copy the input stream to the output stream
        IOException exception = null;
        byte buffer[] = new byte[input];
        int len = buffer.length;
        while (true) {
            try {
                len = istream.read(buffer);
                if (len == -1) {
                    break;
                }
                ostream.write(buffer, 0, len);
            } catch (IOException e) {
                exception = e;
                len = -1;
                break;
            }
        }
        return exception;
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param reader The reader to read from
     * @param writer The writer to write to
     * @return Exception which occurred during processing
     */
    private IOException copyRange(Reader reader, PrintWriter writer) {

        // Copy the input stream to the output stream
        IOException exception = null;
        char buffer[] = new char[input];
        int len = buffer.length;
        while (true) {
            try {
                len = reader.read(buffer);
                if (len == -1) {
                    break;
                }
                writer.write(buffer, 0, len);
            } catch (IOException e) {
                exception = e;
                len = -1;
                break;
            }
        }
        return exception;
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param istream The input stream to read from
     * @param ostream The output stream to write to
     * @param start   Start of the range which will be copied
     * @param end     End of the range which will be copied
     * @return Exception which occurred during processing
     */
    private IOException copyRange(InputStream istream, ServletOutputStream ostream, long start, long end) {

        if (debug > 10) {
            log("Serving bytes:" + start + "-" + end);
        }

        try {
            istream.skip(start);
        } catch (IOException e) {
            return e;
        }

        IOException exception = null;
        long bytesToRead = end - start + 1;

        byte buffer[] = new byte[input];
        int len = buffer.length;
        while ((bytesToRead > 0) && (len >= buffer.length)) {
            try {
                len = istream.read(buffer);
                if (bytesToRead >= len) {
                    ostream.write(buffer, 0, len);
                    bytesToRead -= len;
                } else {
                    ostream.write(buffer, 0, (int) bytesToRead);
                    bytesToRead = 0;
                }
            } catch (IOException e) {
                exception = e;
                len = -1;
            }
            if (len < buffer.length) {
                break;
            }
        }

        return exception;
    }

    /**
     * Copy the contents of the specified input stream to the specified
     * output stream, and ensure that both streams are closed before returning
     * (even in the face of an exception).
     *
     * @param reader The reader to read from
     * @param writer The writer to write to
     * @param start  Start of the range which will be copied
     * @param end    End of the range which will be copied
     * @return Exception which occurred during processing
     */
    private IOException copyRange(Reader reader, PrintWriter writer, long start, long end) {

        try {
            reader.skip(start);
        } catch (IOException e) {
            return e;
        }

        IOException exception = null;
        long bytesToRead = end - start + 1;

        char buffer[] = new char[input];
        int len = buffer.length;
        while ((bytesToRead > 0) && (len >= buffer.length)) {
            try {
                len = reader.read(buffer);
                if (bytesToRead >= len) {
                    writer.write(buffer, 0, len);
                    bytesToRead -= len;
                } else {
                    writer.write(buffer, 0, (int) bytesToRead);
                    bytesToRead = 0;
                }
            } catch (IOException e) {
                exception = e;
                len = -1;
            }
            if (len < buffer.length) {
                break;
            }
        }

        return exception;
    }

    // ------------------------------------------------------ Range Inner Class

    /**
     * Description of the Class
     *
     * @author ananth
     * @created October 26, 2004
     */
    private class Range {

        public long start;
        public long end;
        public long length;

        /**
         * Validate range.
         *
         * @return Description of the Return Value
         */
        public boolean validate() {
            if (end >= length) {
                end = length - 1;
            }
            return ((start >= 0) && (end >= 0) && (start <= end) && (length > 0));
        }

        /**
         * Description of the Method
         */
        public void recycle() {
            start = 0;
            end = 0;
            length = 0;
        }

    }

    // ----------------------------------------------  ResourceInfo Inner Class

    /**
     * Description of the Class
     *
     * @author ananth
     * @created October 26, 2004
     */
    protected class ResourceInfo {

        /**
         * Constructor.
         *
         * @param path      Description of the Parameter
         * @param resources Description of the Parameter
         */
        public ResourceInfo(String path, DirContext resources) {
            set(path, resources);
        }

        /**
         * Constructor for the ResourceInfo object
         *
         * @param path       Description of the Parameter
         * @param resources  Description of the Parameter
         * @param thisSystem Description of the Parameter
         * @throws FileNotFoundException Description of the Exception
         */
        public ResourceInfo(SystemStatus thisSystem, String path, ModuleContext resources)
                throws FileNotFoundException {
            set(thisSystem, path, resources);
        }

        public Object object;
        public ModuleContext dir;
        public DirContext directory;
        public Resource file;
        public Attributes attributes;
        public String path;
        public String clientFilename;
        public String creationDate;
        public String httpDate;
        public long date;
        public long length;
        public boolean collection;
        public String weakETag;
        public String strongETag;
        public boolean exists;
        public ModuleContext resources;
        protected InputStream is;

        /**
         * Description of the Method
         */
        public void recycle() {
            object = null;
            directory = null;
            file = null;
            attributes = null;
            path = null;
            clientFilename = null;
            creationDate = null;
            httpDate = null;
            date = 0;
            length = -1;
            collection = true;
            weakETag = null;
            strongETag = null;
            exists = false;
            resources = null;
            is = null;
        }

        /**
         * Description of the Method
         *
         * @param path      Description of the Parameter
         * @param resources Description of the Parameter
         */
        public void set(String path, DirContext resources) {
        }

        /**
         * Description of the Method
         *
         * @param thisSystem Description of the Parameter
         * @param path       Description of the Parameter
         * @param resources  Description of the Parameter
         * @throws FileNotFoundException Description of the Exception
         */
        public void set(SystemStatus thisSystem, String path, ModuleContext resources)
                throws FileNotFoundException {
            recycle();

            this.path = path;
            this.resources = resources;
            exists = true;
            try {
                object = resources.lookup(path);
                if (object instanceof Resource) {
                    file = (Resource) object;
                    clientFilename = file.getName();
                    collection = false;
                } else if (object instanceof ModuleContext) {
                    dir = (ModuleContext) object;
                    collection = true;
                } else {
                    // Don't know how to serve another object type
                    exists = false;
                }
            } catch (NamingException e) {
                //e.printStackTrace(System.out);
                exists = false;
            }
            if (exists) {
                try {
                    attributes = resources.getAttributes(path);
                    if (attributes instanceof ResourceAttributes) {
                        ResourceAttributes tempAttrs = (ResourceAttributes) attributes;
                        Date tempDate = tempAttrs.getCreationDate();
                        if (tempDate != null) {
                            date = tempDate.getTime();
                            creationDate = FastHttpDateFormat.formatDate(date, null);
                        } else {
                            creationDate = FastHttpDateFormat.getCurrentDate();
                        }
                        tempDate = tempAttrs.getLastModifiedDate();
                        if (tempDate != null) {
                            date = tempDate.getTime();
                            httpDate = FastHttpDateFormat.formatDate(date, null);
                        } else {
                            httpDate = FastHttpDateFormat.getCurrentDate();
                        }
                        weakETag = tempAttrs.getETag();
                        strongETag = tempAttrs.getETag(true);
                        length = tempAttrs.getContentLength();
                    }
                } catch (NamingException e) {
                    // Shouldn't happen, the implementation of the DirContext
                    // is probably broken
                    exists = false;
                }
            }
        }

        /**
         * Test if the associated resource exists.
         *
         * @return Description of the Return Value
         */
        public boolean exists() {
            return exists;
        }

        /**
         * String representation.
         *
         * @return Description of the Return Value
         */
        public String toString() {
            return path;
        }

        /**
         * Set IS.
         *
         * @param is The new stream value
         */
        public void setStream(InputStream is) {
            this.is = is;
        }

        /**
         * Get IS from resource.
         *
         * @return The stream value
         * @throws IOException Description of the Exception
         */
        public InputStream getStream() throws IOException {
            if (is != null) {
                return is;
            }
            if (file != null) {
                return (file.streamContent());
            } else {
                return null;
            }
        }
    }
}