FileDocCategorySizeDatePackage
DefaultServlet.javaAPI DocApache Tomcat 6.0.1474362Fri Jul 20 04:20:36 BST 2007org.apache.catalina.servlets

DefaultServlet

public class DefaultServlet extends HttpServlet
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: 543680 $ $Date: 2007-06-02 02:42:36 +0200 (sam., 02 juin 2007) $

Fields Summary
protected int
debug
The debugging detail level for this servlet.
protected int
input
The input buffer size to use when serving resources.
protected boolean
listings
Should we generate directory listings?
protected boolean
readOnly
Read only flag. By default, it's set to true.
protected int
output
The output buffer size to use when serving resources.
protected static org.apache.catalina.util.URLEncoder
urlEncoder
Array containing the safe characters set.
protected String
localXsltFile
Allow customized directory listing per directory.
protected String
globalXsltFile
Allow customized directory listing per instance.
protected String
readmeFile
Allow a readme file to be included.
protected org.apache.naming.resources.ProxyDirContext
resources
Proxy directory context.
protected String
fileEncoding
File encoding to be used when reading static files. If none is specified the platform default is used.
protected int
sendfileSize
Minimum size for sendfile usage in bytes.
protected static ArrayList
FULL
Full range marker.
protected static final String
mimeSeparation
MIME multipart separation string
protected static final String
RESOURCES_JNDI_NAME
JNDI resources name.
protected static org.apache.catalina.util.StringManager
sm
The string manager for this package.
protected static final int
BUFFER_SIZE
Size of file transfer buffer in bytes.
Constructors Summary
Methods Summary
protected booleancheckIfHeaders(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.ResourceAttributes resourceAttributes)
Check if the conditions specified in the optional If headers are satisfied.

param
request The servlet request we are processing
param
response The servlet response we are creating
param
resourceAttributes The resource information
return
boolean true if the resource meets all the specified conditions, and false if any of the conditions is not satisfied, in which case request processing is stopped


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

    
protected booleancheckIfMatch(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.ResourceAttributes resourceAttributes)
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


        String eTag = getETag(resourceAttributes);
        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;

    
protected booleancheckIfModifiedSince(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.ResourceAttributes resourceAttributes)
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

        try {
            long headerValue = request.getDateHeader("If-Modified-Since");
            long lastModified = resourceAttributes.getLastModified();
            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);
                    response.setHeader("ETag", getETag(resourceAttributes));

                    return false;
                }
            }
        } catch (IllegalArgumentException illegalArgument) {
            return true;
        }
        return true;

    
protected booleancheckIfNoneMatch(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.ResourceAttributes resourceAttributes)
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


        String eTag = getETag(resourceAttributes);
        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);
                    response.setHeader("ETag", getETag(resourceAttributes));

                    return false;
                } else {
                    response.sendError
                        (HttpServletResponse.SC_PRECONDITION_FAILED);
                    return false;
                }
            }
        }
        return true;

    
protected booleancheckIfUnmodifiedSince(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.ResourceAttributes resourceAttributes)
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

        try {
            long lastModified = resourceAttributes.getLastModified();
            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;

    
protected booleancheckSendfile(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.CacheEntry entry, long length, org.apache.catalina.servlets.DefaultServlet$Range range)
Check if sendfile can be used.

        if ((sendfileSize > 0)
            && (entry.resource != null)
            && ((length > sendfileSize) || (entry.resource.getContent() == null))
            && (entry.attributes.getCanonicalPath() != null)
            && (Boolean.TRUE == request.getAttribute("org.apache.tomcat.sendfile.support"))
            && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade"))
            && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) {
            request.setAttribute("org.apache.tomcat.sendfile.filename", entry.attributes.getCanonicalPath());
            if (range == null) {
                request.setAttribute("org.apache.tomcat.sendfile.start", new Long(0L));
                request.setAttribute("org.apache.tomcat.sendfile.end", new Long(length));
            } else {
                request.setAttribute("org.apache.tomcat.sendfile.start", new Long(range.start));
                request.setAttribute("org.apache.tomcat.sendfile.end", new Long(range.end + 1));
            }
            request.setAttribute("org.apache.tomcat.sendfile.token", this);
            return true;
        } else {
            return false;
        }
    
protected voidcopy(org.apache.naming.resources.CacheEntry cacheEntry, java.io.InputStream is, javax.servlet.ServletOutputStream ostream)
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 resource information
param
ostream The output stream to write to
exception
IOException if an input/output error occurs


        IOException exception = null;
        InputStream resourceInputStream = null;

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

        InputStream istream = new BufferedInputStream
            (resourceInputStream, input);

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

        // Clean up the input stream
        istream.close();

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

    
protected voidcopy(org.apache.naming.resources.CacheEntry cacheEntry, java.io.InputStream is, java.io.PrintWriter writer)
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 resource info
param
writer The writer to write to
exception
IOException if an input/output error occurs


        IOException exception = null;

        InputStream resourceInputStream = null;
        if (cacheEntry.resource != null) {
            resourceInputStream = cacheEntry.resource.streamContent();
        } else {
            resourceInputStream = is;
        }

        Reader reader;
        if (fileEncoding == null) {
            reader = new InputStreamReader(resourceInputStream);
        } else {
            reader = new InputStreamReader(resourceInputStream,
                                           fileEncoding);
        }

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

        // Clean up the reader
        reader.close();

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

    
protected voidcopy(org.apache.naming.resources.CacheEntry cacheEntry, javax.servlet.ServletOutputStream ostream, org.apache.catalina.servlets.DefaultServlet$Range range)
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
exception
IOException if an input/output error occurs


        IOException exception = null;

        InputStream resourceInputStream = cacheEntry.resource.streamContent();
        InputStream istream =
            new BufferedInputStream(resourceInputStream, input);
        exception = copyRange(istream, ostream, range.start, range.end);

        // Clean up the input stream
        istream.close();

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

    
protected voidcopy(org.apache.naming.resources.CacheEntry cacheEntry, java.io.PrintWriter writer, org.apache.catalina.servlets.DefaultServlet$Range range)
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
exception
IOException if an input/output error occurs


        IOException exception = null;

        InputStream resourceInputStream = cacheEntry.resource.streamContent();

        Reader reader;
        if (fileEncoding == null) {
            reader = new InputStreamReader(resourceInputStream);
        } else {
            reader = new InputStreamReader(resourceInputStream,
                                           fileEncoding);
        }

        exception = copyRange(reader, writer, range.start, range.end);

        // Clean up the input stream
        reader.close();

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

    
protected voidcopy(org.apache.naming.resources.CacheEntry cacheEntry, javax.servlet.ServletOutputStream ostream, java.util.Iterator ranges, java.lang.String contentType)
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
exception
IOException if an input/output error occurs


        IOException exception = null;

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

            InputStream resourceInputStream = cacheEntry.resource.streamContent();
            InputStream istream =
                new BufferedInputStream(resourceInputStream, input);

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

            // 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);

            istream.close();

        }

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

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

    
protected voidcopy(org.apache.naming.resources.CacheEntry cacheEntry, java.io.PrintWriter writer, java.util.Iterator ranges, java.lang.String contentType)
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
exception
IOException if an input/output error occurs


        IOException exception = null;

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

            InputStream resourceInputStream = cacheEntry.resource.streamContent();
            
            Reader reader;
            if (fileEncoding == null) {
                reader = new InputStreamReader(resourceInputStream);
            } else {
                reader = new InputStreamReader(resourceInputStream,
                                               fileEncoding);
            }

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

            // 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);

            reader.close();

        }

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

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

    
protected java.io.IOExceptioncopyRange(java.io.InputStream istream, javax.servlet.ServletOutputStream ostream)
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


        // 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;

    
protected java.io.IOExceptioncopyRange(java.io.Reader reader, java.io.PrintWriter writer)
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


        // 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;

    
protected java.io.IOExceptioncopyRange(java.io.InputStream istream, javax.servlet.ServletOutputStream ostream, long start, long end)
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


        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;

    
protected java.io.IOExceptioncopyRange(java.io.Reader reader, java.io.PrintWriter writer, long start, long end)
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


        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;

    
public voiddestroy()
Finalize this servlet.



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


            
       
    
protected voiddisplaySize(java.lang.StringBuffer buf, int filesize)
Display the size of a file.


        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");

    
protected voiddoDelete(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
Process a POST request for the specified resource.

param
req The servlet request we are processing
param
resp The servlet response we are creating
exception
IOException if an input/output error occurs
exception
ServletException if a servlet-specified error occurs


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

        String path = getRelativePath(req);

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

    
protected voiddoGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
Process a GET request for the specified resource.

param
request The servlet request we are processing
param
response The servlet response we are creating
exception
IOException if an input/output error occurs
exception
ServletException if a servlet-specified error occurs


        // Serve the requested resource, including the data content
        serveResource(request, response, true);

    
protected voiddoHead(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
Process a HEAD request for the specified resource.

param
request The servlet request we are processing
param
response The servlet response we are creating
exception
IOException if an input/output error occurs
exception
ServletException if a servlet-specified error occurs


        // Serve the requested resource, without the data content
        serveResource(request, response, false);

    
protected voiddoPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
Process a POST request for the specified resource.

param
request The servlet request we are processing
param
response The servlet response we are creating
exception
IOException if an input/output error occurs
exception
ServletException if a servlet-specified error occurs

        doGet(request, response);
    
protected voiddoPut(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
Process a POST request for the specified resource.

param
req The servlet request we are processing
param
resp The servlet response we are creating
exception
IOException if an input/output error occurs
exception
ServletException if a servlet-specified error occurs


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

        String path = getRelativePath(req);

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

        boolean result = true;

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

        Range range = parseContentRange(req, resp);

        InputStream resourceInputStream = null;

        // Append data specified in ranges to existing content for this
        // resource - create a temp. file on the local filesystem to
        // perform this operation
        // Assume just one range is specified for now
        if (range != null) {
            contentFile = executePartialPut(req, range, path);
            resourceInputStream = new FileInputStream(contentFile);
        } else {
            resourceInputStream = req.getInputStream();
        }

        try {
            Resource newResource = new Resource(resourceInputStream);
            // FIXME: Add attributes
            if (exists) {
                resources.rebind(path, newResource);
            } else {
                resources.bind(path, newResource);
            }
        } catch(NamingException e) {
            result = false;
        }

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

    
protected java.io.FileexecutePartialPut(javax.servlet.http.HttpServletRequest req, org.apache.catalina.servlets.DefaultServlet$Range range, java.lang.String path)
Handle a partial PUT. New content specified in request is appended to existing content in oldRevisionContent (if present). This code does not support simultaneous partial updates to the same resource.


        // Append data specified in ranges to existing content for this
        // resource - create a temp. file on the local filesystem to
        // perform this operation
        File tempDir = (File) getServletContext().getAttribute
            ("javax.servlet.context.tempdir");
        // Convert all '/' characters to '.' in resourcePath
        String convertedResourcePath = path.replace('/", '.");
        File contentFile = new File(tempDir, convertedResourcePath);
        if (contentFile.createNewFile()) {
            // Clean up contentFile when Tomcat is terminated
            contentFile.deleteOnExit();
        }

        RandomAccessFile randAccessContentFile =
            new RandomAccessFile(contentFile, "rw");

        Resource oldResource = null;
        try {
            Object obj = resources.lookup(path);
            if (obj instanceof Resource)
                oldResource = (Resource) obj;
        } catch (NamingException e) {
            ;
        }

        // Copy data in oldRevisionContent to contentFile
        if (oldResource != null) {
            BufferedInputStream bufOldRevStream =
                new BufferedInputStream(oldResource.streamContent(),
                                        BUFFER_SIZE);

            int numBytesRead;
            byte[] copyBuffer = new byte[BUFFER_SIZE];
            while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
                randAccessContentFile.write(copyBuffer, 0, numBytesRead);
            }

            bufOldRevStream.close();
        }

        randAccessContentFile.setLength(range.length);

        // Append data in request input stream to contentFile
        randAccessContentFile.seek(range.start);
        int numBytesRead;
        byte[] transferBuffer = new byte[BUFFER_SIZE];
        BufferedInputStream requestBufInStream =
            new BufferedInputStream(req.getInputStream(), BUFFER_SIZE);
        while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
            randAccessContentFile.write(transferBuffer, 0, numBytesRead);
        }
        randAccessContentFile.close();
        requestBufInStream.close();

        return contentFile;

    
protected java.io.InputStreamfindXsltInputStream(javax.naming.directory.DirContext directory)
Return the xsl template inputstream (if possible)


        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 (NamingException e) {
                throw new ServletException("Error opening XSLT resource", e);
            }
        }

        /*  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);
                }
            } finally {
                if (fis!=null)
                    fis.close();
            }
        }

        return null;

    
protected java.lang.StringgetETag(org.apache.naming.resources.ResourceAttributes resourceAttributes)
Get the ETag associated with a file.

param
resourceAttributes The resource information

        String result = null;
        if ((result = resourceAttributes.getETag(true)) != null) {
            return result;
        } else if ((result = resourceAttributes.getETag()) != null) {
            return result;
        } else {
            return "W/\"" + resourceAttributes.getContentLength() + "-"
                + resourceAttributes.getLastModified() + "\"";
        }
    
protected java.lang.StringgetReadme(javax.naming.directory.DirContext directory)
Get the readme file as a string.


        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 (NamingException e) {
                throw new ServletException("Error opening readme resource", e);
            }
        }

        return null;
    
protected java.lang.StringgetRelativePath(javax.servlet.http.HttpServletRequest request)
Return the relative path associated with this servlet.

param
request The servlet request we are processing


        // Are we being processed by a RequestDispatcher.include()?
        if (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) {
            String result = (String) request.getAttribute(
                                            Globals.INCLUDE_PATH_INFO_ATTR);
            if (result == null)
                result = (String) request.getAttribute(
                                            Globals.INCLUDE_SERVLET_PATH_ATTR);
            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);

    
public voidinit()
Initialize this servlet.


        if (getServletConfig().getInitParameter("debug") != null)
            debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));

        if (getServletConfig().getInitParameter("input") != null)
            input = Integer.parseInt(getServletConfig().getInitParameter("input"));

        if (getServletConfig().getInitParameter("output") != null)
            output = Integer.parseInt(getServletConfig().getInitParameter("output"));

        listings = Boolean.parseBoolean(getServletConfig().getInitParameter("listings"));

        if (getServletConfig().getInitParameter("readonly") != null)
            readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));

        if (getServletConfig().getInitParameter("sendfileSize") != null)
            sendfileSize = 
                Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024;

        fileEncoding = getServletConfig().getInitParameter("fileEncoding");

        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 proxy dir context.
        resources = (ProxyDirContext) getServletContext()
            .getAttribute(Globals.RESOURCES_ATTR);
        if (resources == null) {
            try {
                resources =
                    (ProxyDirContext) new InitialContext()
                    .lookup(RESOURCES_JNDI_NAME);
            } catch (NamingException e) {
                // Failed
                throw new ServletException("No resources", e);
            }
        }

        if (resources == null) {
            throw new UnavailableException("No resources");
        }

    
protected org.apache.catalina.servlets.DefaultServlet$RangeparseContentRange(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
Parse the content-range header.

param
request The servlet request we are processing
param
response The servlet response we are creating
return
Range


        // 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;

    
protected java.util.ArrayListparseRange(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.ResourceAttributes resourceAttributes)
Parse the range header.

param
request The servlet request we are processing
param
response The servlet response we are creating
return
Vector of ranges


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

        if (headerValue != null) {

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

            String eTag = getETag(resourceAttributes);
            long lastModified = resourceAttributes.getLastModified();

            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 FULL;

            } 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 FULL;

            }

        }

        long fileLength = resourceAttributes.getContentLength();

        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.
        ArrayList<Range> result = new ArrayList<Range>();
        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.add(currentRange);
        }

        return result;
    
protected java.io.InputStreamrender(java.lang.String contextPath, org.apache.naming.resources.CacheEntry cacheEntry)
Decide which way to render. HTML or XML.


        InputStream xsltInputStream =
            findXsltInputStream(cacheEntry.context);

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

    
protected java.io.InputStreamrenderHtml(java.lang.String contextPath, org.apache.naming.resources.CacheEntry cacheEntry)
Return an InputStream to an HTML representation of the contents of this directory.

param
contextPath Context path to which our internal paths are relative


        String name = cacheEntry.name;

        // 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 = new OutputStreamWriter(stream, "UTF8");
        PrintWriter writer = new PrintWriter(osWriter);

        StringBuffer sb = new StringBuffer();
        
        // rewriteUrl(contextPath) is expensive. cache result for later reuse
        String rewrittenContextPath =  rewriteUrl(contextPath);

        // 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(rewrittenContextPath);
            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
            NamingEnumeration enumeration = resources.list(cacheEntry.name);
            boolean shade = false;
            while (enumeration.hasMoreElements()) {

                NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
                String resourceName = ncPair.getName();
                String trimmed = resourceName/*.substring(trim)*/;
                if (trimmed.equalsIgnoreCase("WEB-INF") ||
                    trimmed.equalsIgnoreCase("META-INF"))
                    continue;

                CacheEntry childCacheEntry =
                    resources.lookupCache(cacheEntry.name + resourceName);
                if (!childCacheEntry.exists) {
                    continue;
                }

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

                sb.append("<td align=\"left\">  \r\n");
                sb.append("<a href=\"");
                sb.append(rewrittenContextPath);
                resourceName = rewriteUrl(name + resourceName);
                sb.append(resourceName);
                if (childCacheEntry.context != null)
                    sb.append("/");
                sb.append("\"><tt>");
                sb.append(RequestUtil.filter(trimmed));
                if (childCacheEntry.context != null)
                    sb.append("/");
                sb.append("</tt></a></td>\r\n");

                sb.append("<td align=\"right\"><tt>");
                if (childCacheEntry.context != null)
                    sb.append(" ");
                else
                    sb.append(renderSize(childCacheEntry.attributes.getContentLength()));
                sb.append("</tt></td>\r\n");

                sb.append("<td align=\"right\"><tt>");
                sb.append(childCacheEntry.attributes.getLastModifiedHttp());
                sb.append("</tt></td>\r\n");

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

        } catch (NamingException e) {
            // Something went wrong
            throw new ServletException("Error accessing resource", e);
        }

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

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

        String readme = getReadme(cacheEntry.context);
        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()));

    
protected java.lang.StringrenderSize(long size)
Render the specified file size (in bytes).

param
size File size (in bytes)


        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");

    
protected java.io.InputStreamrenderXml(java.lang.String contextPath, org.apache.naming.resources.CacheEntry cacheEntry, java.io.InputStream 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


        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(cacheEntry.name);
        sb.append("' ");
        sb.append(" hasParent='").append(!cacheEntry.name.equals("/"));
        sb.append("'>");

        sb.append("<entries>");

        try {

            // Render the directory entries within this directory
            NamingEnumeration enumeration = resources.list(cacheEntry.name);
            
            // rewriteUrl(contextPath) is expensive. cache result for later reuse
            String rewrittenContextPath =  rewriteUrl(contextPath);

            while (enumeration.hasMoreElements()) {

                NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
                String resourceName = ncPair.getName();
                String trimmed = resourceName/*.substring(trim)*/;
                if (trimmed.equalsIgnoreCase("WEB-INF") ||
                    trimmed.equalsIgnoreCase("META-INF") ||
                    trimmed.equalsIgnoreCase(localXsltFile))
                    continue;

                CacheEntry childCacheEntry =
                    resources.lookupCache(cacheEntry.name + resourceName);
                if (!childCacheEntry.exists) {
                    continue;
                }

                sb.append("<entry");
                sb.append(" type='")
                  .append((childCacheEntry.context != null)?"dir":"file")
                  .append("'");
                sb.append(" urlPath='")
                  .append(rewrittenContextPath)
                  .append(rewriteUrl(cacheEntry.name + resourceName))
                  .append((childCacheEntry.context != null)?"/":"")
                  .append("'");
                if (childCacheEntry.resource != null) {
                    sb.append(" size='")
                      .append(renderSize(childCacheEntry.attributes.getContentLength()))
                      .append("'");
                }
                sb.append(" date='")
                  .append(childCacheEntry.attributes.getLastModifiedHttp())
                  .append("'");

                sb.append(">");
                sb.append(RequestUtil.filter(trimmed));
                if (childCacheEntry.context != null)
                    sb.append("/");
                sb.append("</entry>");

            }

        } catch (NamingException e) {
            // Something went wrong
            throw new ServletException("Error accessing resource", e);
        }

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

        String readme = getReadme(cacheEntry.context);

        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 (TransformerException e) {
            throw new ServletException("XSL transformer error", e);
        }
    
protected java.lang.StringrewriteUrl(java.lang.String path)
URL rewriter.

param
path Path which has to be rewiten

        return urlEncoder.encode( path );
    
protected voidserveResource(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, boolean content)
Serve the specified resource, optionally including the data content.

param
request The servlet request we are processing
param
response The servlet response we are creating
param
content Should the content be included?
exception
IOException if an input/output error occurs
exception
ServletException if a servlet-specified error occurs


        // Identify the requested resource path
        String path = getRelativePath(request);
        if (debug > 0) {
            if (content)
                log("DefaultServlet.serveResource:  Serving resource '" +
                    path + "' headers and data");
            else
                log("DefaultServlet.serveResource:  Serving resource '" +
                    path + "' headers only");
        }

        CacheEntry cacheEntry = resources.lookupCache(path);

        if (!cacheEntry.exists) {
            // Check if we're included so we can return the appropriate 
            // missing resource name in the error
            String requestUri = (String) request.getAttribute(
                                            Globals.INCLUDE_REQUEST_URI_ATTR);
            if (requestUri == null) {
                requestUri = request.getRequestURI();
            } else {
                // We're included, and the response.sendError() below is going
                // to be ignored by the resource that is including us.
                // Therefore, the only way we can let the including resource
                // know is by including warning message in response
                response.getWriter().write(
                    sm.getString("defaultServlet.missingResource",
                    requestUri));
            }

            response.sendError(HttpServletResponse.SC_NOT_FOUND,
                               requestUri);
            return;
        }

        // If the resource is not a collection, and the resource path
        // ends with "/" or "\", return NOT FOUND
        if (cacheEntry.context == null) {
            if (path.endsWith("/") || (path.endsWith("\\"))) {
                // Check if we're included so we can return the appropriate 
                // missing resource name in the error
                String requestUri = (String) request.getAttribute(
                                            Globals.INCLUDE_REQUEST_URI_ATTR);
                if (requestUri == null) {
                    requestUri = request.getRequestURI();
                }
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                                   requestUri);
                return;
            }
        }

        // Check if the conditions specified in the optional If headers are
        // satisfied.
        if (cacheEntry.context == null) {

            // Checking If headers
            boolean included =
                (request.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null);
            if (!included
                && !checkIfHeaders(request, response, cacheEntry.attributes)) {
                return;
            }

        }

        // Find content type.
        String contentType = cacheEntry.attributes.getMimeType();
        if (contentType == null) {
            contentType = getServletContext().getMimeType(cacheEntry.name);
            cacheEntry.attributes.setMimeType(contentType);
        }

        ArrayList ranges = null;
        long contentLength = -1L;

        if (cacheEntry.context != null) {

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

        } else {

            // Parse range specifier

            ranges = parseRange(request, response, cacheEntry.attributes);

            // ETag header
            response.setHeader("ETag", getETag(cacheEntry.attributes));

            // Last-Modified header
            response.setHeader("Last-Modified",
                    cacheEntry.attributes.getLastModifiedHttp());

            // Get content length
            contentLength = cacheEntry.attributes.getContentLength();
            // Special case for zero length files, which would cause a
            // (silent) ISE when setting the output buffer size
            if (contentLength == 0L) {
                content = false;
            }

        }

        ServletOutputStream ostream = null;
        PrintWriter writer = null;

        if (content) {

            // Trying to retrieve the servlet output stream

            try {
                ostream = response.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"))
                        || (contentType.endsWith("xml")) ) {
                    writer = response.getWriter();
                } else {
                    throw e;
                }
            }

        }

        if ( (cacheEntry.context != null) 
                || ( ((ranges == null) || (ranges.isEmpty()))
                        && (request.getHeader("Range") == null) )
                || (ranges == FULL) ) {

            // Set the appropriate output headers
            if (contentType != null) {
                if (debug > 0)
                    log("DefaultServlet.serveFile:  contentType='" +
                        contentType + "'");
                response.setContentType(contentType);
            }
            if ((cacheEntry.resource != null) && (contentLength >= 0)) {
                if (debug > 0)
                    log("DefaultServlet.serveFile:  contentLength=" +
                        contentLength);
                if (contentLength < Integer.MAX_VALUE) {
                    response.setContentLength((int) contentLength);
                } else {
                    // Set the content-length as String to be able to use a long
                    response.setHeader("content-length", "" + contentLength);
                }
            }

            InputStream renderResult = null;
            if (cacheEntry.context != null) {

                if (content) {
                    // Serve the directory browser
                    renderResult =
                        render(request.getContextPath(), cacheEntry);
                }

            }

            // Copy the input stream to our output stream (if requested)
            if (content) {
                try {
                    response.setBufferSize(output);
                } catch (IllegalStateException e) {
                    // Silent catch
                }
                if (ostream != null) {
                    if (!checkSendfile(request, response, cacheEntry, contentLength, null))
                        copy(cacheEntry, renderResult, ostream);
                } else {
                    copy(cacheEntry, renderResult, writer);
                }
            }

        } else {

            if ((ranges == null) || (ranges.isEmpty()))
                return;

            // Partial content response.

            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

            if (ranges.size() == 1) {

                Range range = (Range) ranges.get(0);
                response.addHeader("Content-Range", "bytes "
                                   + range.start
                                   + "-" + range.end + "/"
                                   + range.length);
                long length = range.end - range.start + 1;
                if (length < Integer.MAX_VALUE) {
                    response.setContentLength((int) length);
                } else {
                    // Set the content-length as String to be able to use a long
                    response.setHeader("content-length", "" + length);
                }

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

                if (content) {
                    try {
                        response.setBufferSize(output);
                    } catch (IllegalStateException e) {
                        // Silent catch
                    }
                    if (ostream != null) {
                        if (!checkSendfile(request, response, cacheEntry, range.end - range.start + 1, range))
                            copy(cacheEntry, ostream, range);
                    } else {
                        copy(cacheEntry, writer, range);
                    }
                }

            } else {

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

                if (content) {
                    try {
                        response.setBufferSize(output);
                    } catch (IllegalStateException e) {
                        // Silent catch
                    }
                    if (ostream != null) {
                        copy(cacheEntry, ostream, ranges.iterator(),
                             contentType);
                    } else {
                        copy(cacheEntry, writer, ranges.iterator(),
                             contentType);
                    }
                }

            }

        }