KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > servlets > DefaultServlet


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.catalina.servlets;
20
21
22 import java.io.BufferedInputStream JavaDoc;
23 import java.io.ByteArrayInputStream JavaDoc;
24 import java.io.ByteArrayOutputStream JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileInputStream JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.io.InputStreamReader JavaDoc;
30 import java.io.OutputStreamWriter JavaDoc;
31 import java.io.PrintWriter JavaDoc;
32 import java.io.RandomAccessFile JavaDoc;
33 import java.io.Reader JavaDoc;
34 import java.io.StringReader JavaDoc;
35 import java.io.StringWriter JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.StringTokenizer JavaDoc;
39
40 import javax.naming.InitialContext JavaDoc;
41 import javax.naming.NameClassPair JavaDoc;
42 import javax.naming.NamingEnumeration JavaDoc;
43 import javax.naming.NamingException JavaDoc;
44 import javax.naming.directory.DirContext JavaDoc;
45 import javax.servlet.ServletException JavaDoc;
46 import javax.servlet.ServletOutputStream JavaDoc;
47 import javax.servlet.UnavailableException JavaDoc;
48 import javax.servlet.http.HttpServlet JavaDoc;
49 import javax.servlet.http.HttpServletRequest JavaDoc;
50 import javax.servlet.http.HttpServletResponse JavaDoc;
51 import javax.xml.transform.Source JavaDoc;
52 import javax.xml.transform.Transformer JavaDoc;
53 import javax.xml.transform.TransformerException JavaDoc;
54 import javax.xml.transform.TransformerFactory JavaDoc;
55 import javax.xml.transform.stream.StreamResult JavaDoc;
56 import javax.xml.transform.stream.StreamSource JavaDoc;
57
58 import org.apache.catalina.Globals;
59 import org.apache.catalina.util.ServerInfo;
60 import org.apache.catalina.util.StringManager;
61 import org.apache.catalina.util.URLEncoder;
62 import org.apache.naming.resources.CacheEntry;
63 import org.apache.naming.resources.ProxyDirContext;
64 import org.apache.naming.resources.Resource;
65 import org.apache.naming.resources.ResourceAttributes;
66
67
68 /**
69  * The default resource-serving servlet for most web applications,
70  * used to serve static resources such as HTML pages and images.
71  *
72  * @author Craig R. McClanahan
73  * @author Remy Maucherat
74  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
75  */

76
77 public class DefaultServlet
78     extends HttpServlet JavaDoc {
79
80
81     // ----------------------------------------------------- Instance Variables
82

83
84     /**
85      * The debugging detail level for this servlet.
86      */

87     protected int debug = 0;
88
89
90     /**
91      * The input buffer size to use when serving resources.
92      */

93     protected int input = 2048;
94
95
96     /**
97      * Should we generate directory listings?
98      */

99     protected boolean listings = false;
100
101
102     /**
103      * Read only flag. By default, it's set to true.
104      */

105     protected boolean readOnly = true;
106
107
108     /**
109      * The output buffer size to use when serving resources.
110      */

111     protected int output = 2048;
112
113
114     /**
115      * Array containing the safe characters set.
116      */

117     protected static URLEncoder urlEncoder;
118
119
120     /**
121      * Allow customized directory listing per directory.
122      */

123     protected String JavaDoc localXsltFile = null;
124
125
126     /**
127      * Allow customized directory listing per instance.
128      */

129     protected String JavaDoc globalXsltFile = null;
130
131
132     /**
133      * Allow a readme file to be included.
134      */

135     protected String JavaDoc readmeFile = null;
136
137
138     /**
139      * Proxy directory context.
140      */

141     protected ProxyDirContext resources = null;
142
143
144     /**
145      * File encoding to be used when reading static files. If none is specified
146      * the platform default is used.
147      */

148     protected String JavaDoc fileEncoding = null;
149     
150     
151     /**
152      * Minimum size for sendfile usage in bytes.
153      */

154     protected int sendfileSize = 48 * 1024;
155     
156     
157     /**
158      * Full range marker.
159      */

160     protected static ArrayList JavaDoc FULL = new ArrayList JavaDoc();
161     
162     
163     // ----------------------------------------------------- Static Initializer
164

165
166     /**
167      * GMT timezone - all HTTP dates are on GMT
168      */

169     static {
170         urlEncoder = new URLEncoder();
171         urlEncoder.addSafeCharacter('-');
172         urlEncoder.addSafeCharacter('_');
173         urlEncoder.addSafeCharacter('.');
174         urlEncoder.addSafeCharacter('*');
175         urlEncoder.addSafeCharacter('/');
176     }
177
178
179     /**
180      * MIME multipart separation string
181      */

182     protected static final String JavaDoc mimeSeparation = "CATALINA_MIME_BOUNDARY";
183
184
185     /**
186      * JNDI resources name.
187      */

188     protected static final String JavaDoc RESOURCES_JNDI_NAME = "java:/comp/Resources";
189
190
191     /**
192      * The string manager for this package.
193      */

194     protected static StringManager sm =
195         StringManager.getManager(Constants.Package);
196
197
198     /**
199      * Size of file transfer buffer in bytes.
200      */

201     protected static final int BUFFER_SIZE = 4096;
202
203
204     // --------------------------------------------------------- Public Methods
205

206
207     /**
208      * Finalize this servlet.
209      */

210     public void destroy() {
211     }
212
213
214     /**
215      * Initialize this servlet.
216      */

217     public void init() throws ServletException JavaDoc {
218
219         if (getServletConfig().getInitParameter("debug") != null)
220             debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));
221
222         if (getServletConfig().getInitParameter("input") != null)
223             input = Integer.parseInt(getServletConfig().getInitParameter("input"));
224
225         if (getServletConfig().getInitParameter("output") != null)
226             output = Integer.parseInt(getServletConfig().getInitParameter("output"));
227
228         listings = Boolean.parseBoolean(getServletConfig().getInitParameter("listings"));
229
230         if (getServletConfig().getInitParameter("readonly") != null)
231             readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly"));
232
233         if (getServletConfig().getInitParameter("sendfileSize") != null)
234             sendfileSize =
235                 Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024;
236
237         fileEncoding = getServletConfig().getInitParameter("fileEncoding");
238
239         globalXsltFile = getServletConfig().getInitParameter("globalXsltFile");
240         localXsltFile = getServletConfig().getInitParameter("localXsltFile");
241         readmeFile = getServletConfig().getInitParameter("readmeFile");
242
243         // Sanity check on the specified buffer sizes
244
if (input < 256)
245             input = 256;
246         if (output < 256)
247             output = 256;
248
249         if (debug > 0) {
250             log("DefaultServlet.init: input buffer size=" + input +
251                 ", output buffer size=" + output);
252         }
253
254         // Load the proxy dir context.
255
resources = (ProxyDirContext) getServletContext()
256             .getAttribute(Globals.RESOURCES_ATTR);
257         if (resources == null) {
258             try {
259                 resources =
260                     (ProxyDirContext) new InitialContext JavaDoc()
261                     .lookup(RESOURCES_JNDI_NAME);
262             } catch (NamingException JavaDoc e) {
263                 // Failed
264
throw new ServletException JavaDoc("No resources", e);
265             }
266         }
267
268         if (resources == null) {
269             throw new UnavailableException JavaDoc("No resources");
270         }
271
272     }
273
274
275     // ------------------------------------------------------ Protected Methods
276

277
278     /**
279      * Return the relative path associated with this servlet.
280      *
281      * @param request The servlet request we are processing
282      */

283     protected String JavaDoc getRelativePath(HttpServletRequest JavaDoc request) {
284
285         // Are we being processed by a RequestDispatcher.include()?
286
if (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) {
287             String JavaDoc result = (String JavaDoc) request.getAttribute(
288                                             Globals.INCLUDE_PATH_INFO_ATTR);
289             if (result == null)
290                 result = (String JavaDoc) request.getAttribute(
291                                             Globals.INCLUDE_SERVLET_PATH_ATTR);
292             if ((result == null) || (result.equals("")))
293                 result = "/";
294             return (result);
295         }
296
297         // No, extract the desired path directly from the request
298
String JavaDoc result = request.getPathInfo();
299         if (result == null) {
300             result = request.getServletPath();
301         }
302         if ((result == null) || (result.equals(""))) {
303             result = "/";
304         }
305         return (result);
306
307     }
308
309
310     /**
311      * Process a GET request for the specified resource.
312      *
313      * @param request The servlet request we are processing
314      * @param response The servlet response we are creating
315      *
316      * @exception IOException if an input/output error occurs
317      * @exception ServletException if a servlet-specified error occurs
318      */

319     protected void doGet(HttpServletRequest JavaDoc request,
320                          HttpServletResponse JavaDoc response)
321         throws IOException JavaDoc, ServletException JavaDoc {
322
323         // Serve the requested resource, including the data content
324
serveResource(request, response, true);
325
326     }
327
328
329     /**
330      * Process a HEAD request for the specified resource.
331      *
332      * @param request The servlet request we are processing
333      * @param response The servlet response we are creating
334      *
335      * @exception IOException if an input/output error occurs
336      * @exception ServletException if a servlet-specified error occurs
337      */

338     protected void doHead(HttpServletRequest JavaDoc request,
339                           HttpServletResponse JavaDoc response)
340         throws IOException JavaDoc, ServletException JavaDoc {
341
342         // Serve the requested resource, without the data content
343
serveResource(request, response, false);
344
345     }
346
347
348     /**
349      * Process a POST request for the specified resource.
350      *
351      * @param request The servlet request we are processing
352      * @param response The servlet response we are creating
353      *
354      * @exception IOException if an input/output error occurs
355      * @exception ServletException if a servlet-specified error occurs
356      */

357     protected void doPost(HttpServletRequest JavaDoc request,
358                           HttpServletResponse JavaDoc response)
359         throws IOException JavaDoc, ServletException JavaDoc {
360         doGet(request, response);
361     }
362
363
364     /**
365      * Process a POST request for the specified resource.
366      *
367      * @param req The servlet request we are processing
368      * @param resp The servlet response we are creating
369      *
370      * @exception IOException if an input/output error occurs
371      * @exception ServletException if a servlet-specified error occurs
372      */

373     protected void doPut(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
374         throws ServletException JavaDoc, IOException JavaDoc {
375
376         if (readOnly) {
377             resp.sendError(HttpServletResponse.SC_FORBIDDEN);
378             return;
379         }
380
381         String JavaDoc path = getRelativePath(req);
382
383         boolean exists = true;
384         try {
385             resources.lookup(path);
386         } catch (NamingException JavaDoc e) {
387             exists = false;
388         }
389
390         boolean result = true;
391
392         // Temp. content file used to support partial PUT
393
File JavaDoc contentFile = null;
394
395         Range range = parseContentRange(req, resp);
396
397         InputStream JavaDoc resourceInputStream = null;
398
399         // Append data specified in ranges to existing content for this
400
// resource - create a temp. file on the local filesystem to
401
// perform this operation
402
// Assume just one range is specified for now
403
if (range != null) {
404             contentFile = executePartialPut(req, range, path);
405             resourceInputStream = new FileInputStream JavaDoc(contentFile);
406         } else {
407             resourceInputStream = req.getInputStream();
408         }
409
410         try {
411             Resource newResource = new Resource(resourceInputStream);
412             // FIXME: Add attributes
413
if (exists) {
414                 resources.rebind(path, newResource);
415             } else {
416                 resources.bind(path, newResource);
417             }
418         } catch(NamingException JavaDoc e) {
419             result = false;
420         }
421
422         if (result) {
423             if (exists) {
424                 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
425             } else {
426                 resp.setStatus(HttpServletResponse.SC_CREATED);
427             }
428         } else {
429             resp.sendError(HttpServletResponse.SC_CONFLICT);
430         }
431
432     }
433
434
435     /**
436      * Handle a partial PUT. New content specified in request is appended to
437      * existing content in oldRevisionContent (if present). This code does
438      * not support simultaneous partial updates to the same resource.
439      */

440     protected File JavaDoc executePartialPut(HttpServletRequest JavaDoc req, Range range,
441                                      String JavaDoc path)
442         throws IOException JavaDoc {
443
444         // Append data specified in ranges to existing content for this
445
// resource - create a temp. file on the local filesystem to
446
// perform this operation
447
File JavaDoc tempDir = (File JavaDoc) getServletContext().getAttribute
448             ("javax.servlet.context.tempdir");
449         // Convert all '/' characters to '.' in resourcePath
450
String JavaDoc convertedResourcePath = path.replace('/', '.');
451         File JavaDoc contentFile = new File JavaDoc(tempDir, convertedResourcePath);
452         if (contentFile.createNewFile()) {
453             // Clean up contentFile when Tomcat is terminated
454
contentFile.deleteOnExit();
455         }
456
457         RandomAccessFile JavaDoc randAccessContentFile =
458             new RandomAccessFile JavaDoc(contentFile, "rw");
459
460         Resource oldResource = null;
461         try {
462             Object JavaDoc obj = resources.lookup(path);
463             if (obj instanceof Resource)
464                 oldResource = (Resource) obj;
465         } catch (NamingException JavaDoc e) {
466             ;
467         }
468
469         // Copy data in oldRevisionContent to contentFile
470
if (oldResource != null) {
471             BufferedInputStream JavaDoc bufOldRevStream =
472                 new BufferedInputStream JavaDoc(oldResource.streamContent(),
473                                         BUFFER_SIZE);
474
475             int numBytesRead;
476             byte[] copyBuffer = new byte[BUFFER_SIZE];
477             while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
478                 randAccessContentFile.write(copyBuffer, 0, numBytesRead);
479             }
480
481             bufOldRevStream.close();
482         }
483
484         randAccessContentFile.setLength(range.length);
485
486         // Append data in request input stream to contentFile
487
randAccessContentFile.seek(range.start);
488         int numBytesRead;
489         byte[] transferBuffer = new byte[BUFFER_SIZE];
490         BufferedInputStream JavaDoc requestBufInStream =
491             new BufferedInputStream JavaDoc(req.getInputStream(), BUFFER_SIZE);
492         while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
493             randAccessContentFile.write(transferBuffer, 0, numBytesRead);
494         }
495         randAccessContentFile.close();
496         requestBufInStream.close();
497
498         return contentFile;
499
500     }
501
502
503     /**
504      * Process a POST request for the specified resource.
505      *
506      * @param req The servlet request we are processing
507      * @param resp The servlet response we are creating
508      *
509      * @exception IOException if an input/output error occurs
510      * @exception ServletException if a servlet-specified error occurs
511      */

512     protected void doDelete(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc resp)
513         throws ServletException JavaDoc, IOException JavaDoc {
514
515         if (readOnly) {
516             resp.sendError(HttpServletResponse.SC_FORBIDDEN);
517             return;
518         }
519
520         String JavaDoc path = getRelativePath(req);
521
522         boolean exists = true;
523         try {
524             resources.lookup(path);
525         } catch (NamingException JavaDoc e) {
526             exists = false;
527         }
528
529         if (exists) {
530             boolean result = true;
531             try {
532                 resources.unbind(path);
533             } catch (NamingException JavaDoc e) {
534                 result = false;
535             }
536             if (result) {
537                 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
538             } else {
539                 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
540             }
541         } else {
542             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
543         }
544
545     }
546
547
548     /**
549      * Check if the conditions specified in the optional If headers are
550      * satisfied.
551      *
552      * @param request The servlet request we are processing
553      * @param response The servlet response we are creating
554      * @param resourceAttributes The resource information
555      * @return boolean true if the resource meets all the specified conditions,
556      * and false if any of the conditions is not satisfied, in which case
557      * request processing is stopped
558      */

559     protected boolean checkIfHeaders(HttpServletRequest JavaDoc request,
560                                      HttpServletResponse JavaDoc response,
561                                      ResourceAttributes resourceAttributes)
562         throws IOException JavaDoc {
563
564         return checkIfMatch(request, response, resourceAttributes)
565             && checkIfModifiedSince(request, response, resourceAttributes)
566             && checkIfNoneMatch(request, response, resourceAttributes)
567             && checkIfUnmodifiedSince(request, response, resourceAttributes);
568
569     }
570
571
572     /**
573      * Get the ETag associated with a file.
574      *
575      * @param resourceAttributes The resource information
576      */

577     protected String JavaDoc getETag(ResourceAttributes resourceAttributes) {
578         String JavaDoc result = null;
579         if ((result = resourceAttributes.getETag(true)) != null) {
580             return result;
581         } else if ((result = resourceAttributes.getETag()) != null) {
582             return result;
583         } else {
584             return "W/\"" + resourceAttributes.getContentLength() + "-"
585                 + resourceAttributes.getLastModified() + "\"";
586         }
587     }
588
589
590     /**
591      * URL rewriter.
592      *
593      * @param path Path which has to be rewiten
594      */

595     protected String JavaDoc rewriteUrl(String JavaDoc path) {
596         return urlEncoder.encode( path );
597     }
598
599
600     /**
601      * Display the size of a file.
602      */

603     protected void displaySize(StringBuffer JavaDoc buf, int filesize) {
604
605         int leftside = filesize / 1024;
606         int rightside = (filesize % 1024) / 103; // makes 1 digit
607
// To avoid 0.0 for non-zero file, we bump to 0.1
608
if (leftside == 0 && rightside == 0 && filesize != 0)
609             rightside = 1;
610         buf.append(leftside).append(".").append(rightside);
611         buf.append(" KB");
612
613     }
614
615
616     /**
617      * Serve the specified resource, optionally including the data content.
618      *
619      * @param request The servlet request we are processing
620      * @param response The servlet response we are creating
621      * @param content Should the content be included?
622      *
623      * @exception IOException if an input/output error occurs
624      * @exception ServletException if a servlet-specified error occurs
625      */

626     protected void serveResource(HttpServletRequest JavaDoc request,
627                                  HttpServletResponse JavaDoc response,
628                                  boolean content)
629         throws IOException JavaDoc, ServletException JavaDoc {
630
631         // Identify the requested resource path
632
String JavaDoc path = getRelativePath(request);
633         if (debug > 0) {
634             if (content)
635                 log("DefaultServlet.serveResource: Serving resource '" +
636                     path + "' headers and data");
637             else
638                 log("DefaultServlet.serveResource: Serving resource '" +
639                     path + "' headers only");
640         }
641
642         CacheEntry cacheEntry = resources.lookupCache(path);
643
644         if (!cacheEntry.exists) {
645             // Check if we're included so we can return the appropriate
646
// missing resource name in the error
647
String JavaDoc requestUri = (String JavaDoc) request.getAttribute(
648                                             Globals.INCLUDE_REQUEST_URI_ATTR);
649             if (requestUri == null) {
650                 requestUri = request.getRequestURI();
651             } else {
652                 // We're included, and the response.sendError() below is going
653
// to be ignored by the resource that is including us.
654
// Therefore, the only way we can let the including resource
655
// know is by including warning message in response
656
response.getWriter().write(
657                     sm.getString("defaultServlet.missingResource",
658                     requestUri));
659             }
660
661             response.sendError(HttpServletResponse.SC_NOT_FOUND,
662                                requestUri);
663             return;
664         }
665
666         // If the resource is not a collection, and the resource path
667
// ends with "/" or "\", return NOT FOUND
668
if (cacheEntry.context == null) {
669             if (path.endsWith("/") || (path.endsWith("\\"))) {
670                 // Check if we're included so we can return the appropriate
671
// missing resource name in the error
672
String JavaDoc requestUri = (String JavaDoc) request.getAttribute(
673                                             Globals.INCLUDE_REQUEST_URI_ATTR);
674                 if (requestUri == null) {
675                     requestUri = request.getRequestURI();
676                 }
677                 response.sendError(HttpServletResponse.SC_NOT_FOUND,
678                                    requestUri);
679                 return;
680             }
681         }
682
683         // Check if the conditions specified in the optional If headers are
684
// satisfied.
685
if (cacheEntry.context == null) {
686
687             // Checking If headers
688
boolean included =
689                 (request.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null);
690             if (!included
691                 && !checkIfHeaders(request, response, cacheEntry.attributes)) {
692                 return;
693             }
694
695         }
696
697         // Find content type.
698
String JavaDoc contentType = cacheEntry.attributes.getMimeType();
699         if (contentType == null) {
700             contentType = getServletContext().getMimeType(cacheEntry.name);
701             cacheEntry.attributes.setMimeType(contentType);
702         }
703
704         ArrayList JavaDoc ranges = null;
705         long contentLength = -1L;
706
707         if (cacheEntry.context != null) {
708
709             // Skip directory listings if we have been configured to
710
// suppress them
711
if (!listings) {
712                 response.sendError(HttpServletResponse.SC_NOT_FOUND,
713                                    request.getRequestURI());
714                 return;
715             }
716             contentType = "text/html;charset=UTF-8";
717
718         } else {
719
720             // Parse range specifier
721

722             ranges = parseRange(request, response, cacheEntry.attributes);
723
724             // ETag header
725
response.setHeader("ETag", getETag(cacheEntry.attributes));
726
727             // Last-Modified header
728
response.setHeader("Last-Modified",
729                     cacheEntry.attributes.getLastModifiedHttp());
730
731             // Get content length
732
contentLength = cacheEntry.attributes.getContentLength();
733             // Special case for zero length files, which would cause a
734
// (silent) ISE when setting the output buffer size
735
if (contentLength == 0L) {
736                 content = false;
737             }
738
739         }
740
741         ServletOutputStream JavaDoc ostream = null;
742         PrintWriter JavaDoc writer = null;
743
744         if (content) {
745
746             // Trying to retrieve the servlet output stream
747

748             try {
749                 ostream = response.getOutputStream();
750             } catch (IllegalStateException JavaDoc e) {
751                 // If it fails, we try to get a Writer instead if we're
752
// trying to serve a text file
753
if ( (contentType == null)
754                         || (contentType.startsWith("text"))
755                         || (contentType.endsWith("xml")) ) {
756                     writer = response.getWriter();
757                 } else {
758                     throw e;
759                 }
760             }
761
762         }
763
764         if ( (cacheEntry.context != null)
765                 || ( ((ranges == null) || (ranges.isEmpty()))
766                         && (request.getHeader("Range") == null) )
767                 || (ranges == FULL) ) {
768
769             // Set the appropriate output headers
770
if (contentType != null) {
771                 if (debug > 0)
772                     log("DefaultServlet.serveFile: contentType='" +
773                         contentType + "'");
774                 response.setContentType(contentType);
775             }
776             if ((cacheEntry.resource != null) && (contentLength >= 0)) {
777                 if (debug > 0)
778                     log("DefaultServlet.serveFile: contentLength=" +
779                         contentLength);
780                 if (contentLength < Integer.MAX_VALUE) {
781                     response.setContentLength((int) contentLength);
782                 } else {
783                     // Set the content-length as String to be able to use a long
784
response.setHeader("content-length", "" + contentLength);
785                 }
786             }
787
788             InputStream JavaDoc renderResult = null;
789             if (cacheEntry.context != null) {
790
791                 if (content) {
792                     // Serve the directory browser
793
renderResult =
794                         render(request.getContextPath(), cacheEntry);
795                 }
796
797             }
798
799             // Copy the input stream to our output stream (if requested)
800
if (content) {
801                 try {
802                     response.setBufferSize(output);
803                 } catch (IllegalStateException JavaDoc e) {
804                     // Silent catch
805
}
806                 if (ostream != null) {
807                     if (!checkSendfile(request, response, cacheEntry, contentLength, null))
808                         copy(cacheEntry, renderResult, ostream);
809                 } else {
810                     copy(cacheEntry, renderResult, writer);
811                 }
812             }
813
814         } else {
815
816             if ((ranges == null) || (ranges.isEmpty()))
817                 return;
818
819             // Partial content response.
820

821             response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
822
823             if (ranges.size() == 1) {
824
825                 Range range = (Range) ranges.get(0);
826                 response.addHeader("Content-Range", "bytes "
827                                    + range.start
828                                    + "-" + range.end + "/"
829                                    + range.length);
830                 long length = range.end - range.start + 1;
831                 if (length < Integer.MAX_VALUE) {
832                     response.setContentLength((int) length);
833                 } else {
834                     // Set the content-length as String to be able to use a long
835
response.setHeader("content-length", "" + length);
836                 }
837
838                 if (contentType != null) {
839                     if (debug > 0)
840                         log("DefaultServlet.serveFile: contentType='" +
841                             contentType + "'");
842                     response.setContentType(contentType);
843                 }
844
845                 if (content) {
846                     try {
847                         response.setBufferSize(output);
848                     } catch (IllegalStateException JavaDoc e) {
849                         // Silent catch
850
}
851                     if (ostream != null) {
852                         if (!checkSendfile(request, response, cacheEntry, range.end - range.start + 1, range))
853                             copy(cacheEntry, ostream, range);
854                     } else {
855                         copy(cacheEntry, writer, range);
856                     }
857                 }
858
859             } else {
860
861                 response.setContentType("multipart/byteranges; boundary="
862                                         + mimeSeparation);
863
864                 if (content) {
865                     try {
866                         response.setBufferSize(output);
867                     } catch (IllegalStateException JavaDoc e) {
868                         // Silent catch
869
}
870                     if (ostream != null) {
871                         copy(cacheEntry, ostream, ranges.iterator(),
872                              contentType);
873                     } else {
874                         copy(cacheEntry, writer, ranges.iterator(),
875                              contentType);
876                     }
877                 }
878
879             }
880
881         }
882
883     }
884
885
886     /**
887      * Parse the content-range header.
888      *
889      * @param request The servlet request we are processing
890      * @param response The servlet response we are creating
891      * @return Range
892      */

893     protected Range parseContentRange(HttpServletRequest JavaDoc request,
894                                       HttpServletResponse JavaDoc response)
895         throws IOException JavaDoc {
896
897         // Retrieving the content-range header (if any is specified
898
String JavaDoc rangeHeader = request.getHeader("Content-Range");
899
900         if (rangeHeader == null)
901             return null;
902
903         // bytes is the only range unit supported
904
if (!rangeHeader.startsWith("bytes")) {
905             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
906             return null;
907         }
908
909         rangeHeader = rangeHeader.substring(6).trim();
910
911         int dashPos = rangeHeader.indexOf('-');
912         int slashPos = rangeHeader.indexOf('/');
913
914         if (dashPos == -1) {
915             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
916             return null;
917         }
918
919         if (slashPos == -1) {
920             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
921             return null;
922         }
923
924         Range range = new Range();
925
926         try {
927             range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
928             range.end =
929                 Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
930             range.length = Long.parseLong
931                 (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
932         } catch (NumberFormatException JavaDoc e) {
933             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
934             return null;
935         }
936
937         if (!range.validate()) {
938             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
939             return null;
940         }
941
942         return range;
943
944     }
945
946
947     /**
948      * Parse the range header.
949      *
950      * @param request The servlet request we are processing
951      * @param response The servlet response we are creating
952      * @return Vector of ranges
953      */

954     protected ArrayList JavaDoc parseRange(HttpServletRequest JavaDoc request,
955                                 HttpServletResponse JavaDoc response,
956                                 ResourceAttributes resourceAttributes)
957         throws IOException JavaDoc {
958
959         // Checking If-Range
960
String JavaDoc headerValue = request.getHeader("If-Range");
961
962         if (headerValue != null) {
963
964             long headerValueTime = (-1L);
965             try {
966                 headerValueTime = request.getDateHeader("If-Range");
967             } catch (IllegalArgumentException JavaDoc e) {
968                 ;
969             }
970
971             String JavaDoc eTag = getETag(resourceAttributes);
972             long lastModified = resourceAttributes.getLastModified();
973
974             if (headerValueTime == (-1L)) {
975
976                 // If the ETag the client gave does not match the entity
977
// etag, then the entire entity is returned.
978
if (!eTag.equals(headerValue.trim()))
979                     return FULL;
980
981             } else {
982
983                 // If the timestamp of the entity the client got is older than
984
// the last modification date of the entity, the entire entity
985
// is returned.
986
if (lastModified > (headerValueTime + 1000))
987                     return FULL;
988
989             }
990
991         }
992
993         long fileLength = resourceAttributes.getContentLength();
994
995         if (fileLength == 0)
996             return null;
997
998         // Retrieving the range header (if any is specified
999
String JavaDoc rangeHeader = request.getHeader("Range");
1000
1001        if (rangeHeader == null)
1002            return null;
1003        // bytes is the only range unit supported (and I don't see the point
1004
// of adding new ones).
1005
if (!rangeHeader.startsWith("bytes")) {
1006            response.addHeader("Content-Range", "bytes */" + fileLength);
1007            response.sendError
1008                (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1009            return null;
1010        }
1011
1012        rangeHeader = rangeHeader.substring(6);
1013
1014        // Vector which will contain all the ranges which are successfully
1015
// parsed.
1016
ArrayList JavaDoc result = new ArrayList JavaDoc();
1017        StringTokenizer JavaDoc commaTokenizer = new StringTokenizer JavaDoc(rangeHeader, ",");
1018
1019        // Parsing the range list
1020
while (commaTokenizer.hasMoreTokens()) {
1021            String JavaDoc rangeDefinition = commaTokenizer.nextToken().trim();
1022
1023            Range currentRange = new Range();
1024            currentRange.length = fileLength;
1025
1026            int dashPos = rangeDefinition.indexOf('-');
1027
1028            if (dashPos == -1) {
1029                response.addHeader("Content-Range", "bytes */" + fileLength);
1030                response.sendError
1031                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1032                return null;
1033            }
1034
1035            if (dashPos == 0) {
1036
1037                try {
1038                    long offset = Long.parseLong(rangeDefinition);
1039                    currentRange.start = fileLength + offset;
1040                    currentRange.end = fileLength - 1;
1041                } catch (NumberFormatException JavaDoc e) {
1042                    response.addHeader("Content-Range",
1043                                       "bytes */" + fileLength);
1044                    response.sendError
1045                        (HttpServletResponse
1046                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1047                    return null;
1048                }
1049
1050            } else {
1051
1052                try {
1053                    currentRange.start = Long.parseLong
1054                        (rangeDefinition.substring(0, dashPos));
1055                    if (dashPos < rangeDefinition.length() - 1)
1056                        currentRange.end = Long.parseLong
1057                            (rangeDefinition.substring
1058                             (dashPos + 1, rangeDefinition.length()));
1059                    else
1060                        currentRange.end = fileLength - 1;
1061                } catch (NumberFormatException JavaDoc e) {
1062                    response.addHeader("Content-Range",
1063                                       "bytes */" + fileLength);
1064                    response.sendError
1065                        (HttpServletResponse
1066                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1067                    return null;
1068                }
1069
1070            }
1071
1072            if (!currentRange.validate()) {
1073                response.addHeader("Content-Range", "bytes */" + fileLength);
1074                response.sendError
1075                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1076                return null;
1077            }
1078
1079            result.add(currentRange);
1080        }
1081
1082        return result;
1083    }
1084
1085
1086
1087    /**
1088     * Decide which way to render. HTML or XML.
1089     */

1090    protected InputStream JavaDoc render(String JavaDoc contextPath, CacheEntry cacheEntry)
1091        throws IOException JavaDoc, ServletException JavaDoc {
1092
1093        InputStream JavaDoc xsltInputStream =
1094            findXsltInputStream(cacheEntry.context);
1095
1096        if (xsltInputStream==null) {
1097            return renderHtml(contextPath, cacheEntry);
1098        } else {
1099            return renderXml(contextPath, cacheEntry, xsltInputStream);
1100        }
1101
1102    }
1103
1104    /**
1105     * Return an InputStream to an HTML representation of the contents
1106     * of this directory.
1107     *
1108     * @param contextPath Context path to which our internal paths are
1109     * relative
1110     */

1111    protected InputStream JavaDoc renderXml(String JavaDoc contextPath,
1112                                    CacheEntry cacheEntry,
1113                                    InputStream JavaDoc xsltInputStream)
1114        throws IOException JavaDoc, ServletException JavaDoc {
1115
1116        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1117
1118        sb.append("<?xml version=\"1.0\"?>");
1119        sb.append("<listing ");
1120        sb.append(" contextPath='");
1121        sb.append(contextPath);
1122        sb.append("'");
1123        sb.append(" directory='");
1124        sb.append(cacheEntry.name);
1125        sb.append("' ");
1126        sb.append(" hasParent='").append(!cacheEntry.name.equals("/"));
1127        sb.append("'>");
1128
1129        sb.append("<entries>");
1130
1131        try {
1132
1133            // Render the directory entries within this directory
1134
NamingEnumeration JavaDoc enumeration = resources.list(cacheEntry.name);
1135            
1136            // rewriteUrl(contextPath) is expensive. cache result for later reuse
1137
String JavaDoc rewrittenContextPath = rewriteUrl(contextPath);
1138
1139            while (enumeration.hasMoreElements()) {
1140
1141                NameClassPair JavaDoc ncPair = (NameClassPair JavaDoc) enumeration.nextElement();
1142                String JavaDoc resourceName = ncPair.getName();
1143                String JavaDoc trimmed = resourceName/*.substring(trim)*/;
1144                if (trimmed.equalsIgnoreCase("WEB-INF") ||
1145                    trimmed.equalsIgnoreCase("META-INF") ||
1146                    trimmed.equalsIgnoreCase(localXsltFile))
1147                    continue;
1148
1149                CacheEntry childCacheEntry =
1150                    resources.lookupCache(cacheEntry.name + resourceName);
1151                if (!childCacheEntry.exists) {
1152                    continue;
1153                }
1154
1155                sb.append("<entry");
1156                sb.append(" type='")
1157                  .append((childCacheEntry.context != null)?"dir":"file")
1158                  .append("'");
1159                sb.append(" urlPath='")
1160                  .append(rewrittenContextPath)
1161                  .append(rewriteUrl(cacheEntry.name + resourceName))
1162                  .append((childCacheEntry.context != null)?"/":"")
1163                  .append("'");
1164                if (childCacheEntry.resource != null) {
1165                    sb.append(" size='")
1166                      .append(renderSize(childCacheEntry.attributes.getContentLength()))
1167                      .append("'");
1168                }
1169                sb.append(" date='")
1170                  .append(childCacheEntry.attributes.getLastModifiedHttp())
1171                  .append("'");
1172
1173                sb.append(">");
1174                sb.append(trimmed);
1175                if (childCacheEntry.context != null)
1176                    sb.append("/");
1177                sb.append("</entry>");
1178
1179            }
1180
1181        } catch (NamingException JavaDoc e) {
1182            // Something went wrong
1183
throw new ServletException JavaDoc("Error accessing resource", e);
1184        }
1185
1186        sb.append("</entries>");
1187
1188        String JavaDoc readme = getReadme(cacheEntry.context);
1189
1190        if (readme!=null) {
1191            sb.append("<readme><![CDATA[");
1192            sb.append(readme);
1193            sb.append("]]></readme>");
1194        }
1195
1196
1197        sb.append("</listing>");
1198
1199
1200        try {
1201            TransformerFactory JavaDoc tFactory = TransformerFactory.newInstance();
1202            Source JavaDoc xmlSource = new StreamSource JavaDoc(new StringReader JavaDoc(sb.toString()));
1203            Source JavaDoc xslSource = new StreamSource JavaDoc(xsltInputStream);
1204            Transformer JavaDoc transformer = tFactory.newTransformer(xslSource);
1205
1206            ByteArrayOutputStream JavaDoc stream = new ByteArrayOutputStream JavaDoc();
1207            OutputStreamWriter JavaDoc osWriter = new OutputStreamWriter JavaDoc(stream, "UTF8");
1208            StreamResult JavaDoc out = new StreamResult JavaDoc(osWriter);
1209            transformer.transform(xmlSource, out);
1210            osWriter.flush();
1211            return (new ByteArrayInputStream JavaDoc(stream.toByteArray()));
1212        } catch (TransformerException JavaDoc e) {
1213            throw new ServletException JavaDoc("XSL transformer error", e);
1214        }
1215    }
1216
1217    /**
1218     * Return an InputStream to an HTML representation of the contents
1219     * of this directory.
1220     *
1221     * @param contextPath Context path to which our internal paths are
1222     * relative
1223     */

1224    protected InputStream JavaDoc renderHtml(String JavaDoc contextPath, CacheEntry cacheEntry)
1225        throws IOException JavaDoc, ServletException JavaDoc {
1226
1227        String JavaDoc name = cacheEntry.name;
1228
1229        // Number of characters to trim from the beginnings of filenames
1230
int trim = name.length();
1231        if (!name.endsWith("/"))
1232            trim += 1;
1233        if (name.equals("/"))
1234            trim = 1;
1235
1236        // Prepare a writer to a buffered area
1237
ByteArrayOutputStream JavaDoc stream = new ByteArrayOutputStream JavaDoc();
1238        OutputStreamWriter JavaDoc osWriter = new OutputStreamWriter JavaDoc(stream, "UTF8");
1239        PrintWriter JavaDoc writer = new PrintWriter JavaDoc(osWriter);
1240
1241        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1242        
1243        // rewriteUrl(contextPath) is expensive. cache result for later reuse
1244
String JavaDoc rewrittenContextPath = rewriteUrl(contextPath);
1245
1246        // Render the page header
1247
sb.append("<html>\r\n");
1248        sb.append("<head>\r\n");
1249        sb.append("<title>");
1250        sb.append(sm.getString("directory.title", name));
1251        sb.append("</title>\r\n");
1252        sb.append("<STYLE><!--");
1253        sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
1254        sb.append("--></STYLE> ");
1255        sb.append("</head>\r\n");
1256        sb.append("<body>");
1257        sb.append("<h1>");
1258        sb.append(sm.getString("directory.title", name));
1259
1260        // Render the link to our parent (if required)
1261
String JavaDoc parentDirectory = name;
1262        if (parentDirectory.endsWith("/")) {
1263            parentDirectory =
1264                parentDirectory.substring(0, parentDirectory.length() - 1);
1265        }
1266        int slash = parentDirectory.lastIndexOf('/');
1267        if (slash >= 0) {
1268            String JavaDoc parent = name.substring(0, slash);
1269            sb.append(" - <a HREF=\"");
1270            sb.append(rewrittenContextPath);
1271            if (parent.equals(""))
1272                parent = "/";
1273            sb.append(rewriteUrl(parent));
1274            if (!parent.endsWith("/"))
1275                sb.append("/");
1276            sb.append("\">");
1277            sb.append("<b>");
1278            sb.append(sm.getString("directory.parent", parent));
1279            sb.append("</b>");
1280            sb.append("</a>");
1281        }
1282
1283        sb.append("</h1>");
1284        sb.append("<HR size=\"1\" noshade=\"noshade\">");
1285
1286        sb.append("<table width=\"100%\" cellspacing=\"0\"" +
1287                     " cellpadding=\"5\" align=\"center\">\r\n");
1288
1289        // Render the column headings
1290
sb.append("<tr>\r\n");
1291        sb.append("<td align=\"left\"><font size=\"+1\"><strong>");
1292        sb.append(sm.getString("directory.filename"));
1293        sb.append("</strong></font></td>\r\n");
1294        sb.append("<td align=\"center\"><font size=\"+1\"><strong>");
1295        sb.append(sm.getString("directory.size"));
1296        sb.append("</strong></font></td>\r\n");
1297        sb.append("<td align=\"right\"><font size=\"+1\"><strong>");
1298        sb.append(sm.getString("directory.lastModified"));
1299        sb.append("</strong></font></td>\r\n");
1300        sb.append("</tr>");
1301
1302        try {
1303
1304            // Render the directory entries within this directory
1305
NamingEnumeration JavaDoc enumeration = resources.list(cacheEntry.name);
1306            boolean shade = false;
1307            while (enumeration.hasMoreElements()) {
1308
1309                NameClassPair JavaDoc ncPair = (NameClassPair JavaDoc) enumeration.nextElement();
1310                String JavaDoc resourceName = ncPair.getName();
1311                String JavaDoc trimmed = resourceName/*.substring(trim)*/;
1312                if (trimmed.equalsIgnoreCase("WEB-INF") ||
1313                    trimmed.equalsIgnoreCase("META-INF"))
1314                    continue;
1315
1316                CacheEntry childCacheEntry =
1317                    resources.lookupCache(cacheEntry.name + resourceName);
1318                if (!childCacheEntry.exists) {
1319                    continue;
1320                }
1321
1322                sb.append("<tr");
1323                if (shade)
1324                    sb.append(" bgcolor=\"#eeeeee\"");
1325                sb.append(">\r\n");
1326                shade = !shade;
1327
1328                sb.append("<td align=\"left\">&nbsp;&nbsp;\r\n");
1329                sb.append("<a HREF=\"");
1330                sb.append(rewrittenContextPath);
1331                resourceName = rewriteUrl(name + resourceName);
1332                sb.append(resourceName);
1333                if (childCacheEntry.context != null)
1334                    sb.append("/");
1335                sb.append("\"><tt>");
1336                sb.append(trimmed);
1337                if (childCacheEntry.context != null)
1338                    sb.append("/");
1339                sb.append("</tt></a></td>\r\n");
1340
1341                sb.append("<td align=\"right\"><tt>");
1342                if (childCacheEntry.context != null)
1343                    sb.append("&nbsp;");
1344                else
1345                    sb.append(renderSize(childCacheEntry.attributes.getContentLength()));
1346                sb.append("</tt></td>\r\n");
1347
1348                sb.append("<td align=\"right\"><tt>");
1349                sb.append(childCacheEntry.attributes.getLastModifiedHttp());
1350                sb.append("</tt></td>\r\n");
1351
1352                sb.append("</tr>\r\n");
1353            }
1354
1355        } catch (NamingException JavaDoc e) {
1356            // Something went wrong
1357
throw new ServletException JavaDoc("Error accessing resource", e);
1358        }
1359
1360        // Render the page footer
1361
sb.append("</table>\r\n");
1362
1363        sb.append("<HR size=\"1\" noshade=\"noshade\">");
1364
1365        String JavaDoc readme = getReadme(cacheEntry.context);
1366        if (readme!=null) {
1367            sb.append(readme);
1368            sb.append("<HR size=\"1\" noshade=\"noshade\">");
1369        }
1370
1371        sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
1372        sb.append("</body>\r\n");
1373        sb.append("</html>\r\n");
1374
1375        // Return an input stream to the underlying bytes
1376
writer.write(sb.toString());
1377        writer.flush();
1378        return (new ByteArrayInputStream JavaDoc(stream.toByteArray()));
1379
1380    }
1381
1382
1383    /**
1384     * Render the specified file size (in bytes).
1385     *
1386     * @param size File size (in bytes)
1387     */

1388    protected String JavaDoc renderSize(long size) {
1389
1390        long leftSide = size / 1024;
1391        long rightSide = (size % 1024) / 103; // Makes 1 digit
1392
if ((leftSide == 0) && (rightSide == 0) && (size > 0))
1393            rightSide = 1;
1394
1395        return ("" + leftSide + "." + rightSide + " kb");
1396
1397    }
1398
1399
1400    /**
1401     * Get the readme file as a string.
1402     */

1403    protected String JavaDoc getReadme(DirContext JavaDoc directory)
1404        throws IOException JavaDoc, ServletException JavaDoc {
1405
1406        if (readmeFile != null) {
1407            try {
1408                Object JavaDoc obj = directory.lookup(readmeFile);
1409                if ((obj != null) && (obj instanceof Resource)) {
1410                    StringWriter JavaDoc buffer = new StringWriter JavaDoc();
1411                    InputStream JavaDoc is = ((Resource) obj).streamContent();
1412                    copyRange(new InputStreamReader JavaDoc(is),
1413                            new PrintWriter JavaDoc(buffer));
1414                    return buffer.toString();
1415                }
1416            } catch (NamingException JavaDoc e) {
1417                throw new ServletException JavaDoc("Error opening readme resource", e);
1418            }
1419        }
1420
1421        return null;
1422    }
1423
1424
1425    /**
1426     * Return the xsl template inputstream (if possible)
1427     */

1428    protected InputStream JavaDoc findXsltInputStream(DirContext JavaDoc directory)
1429        throws IOException JavaDoc, ServletException JavaDoc {
1430
1431        if (localXsltFile != null) {
1432            try {
1433                Object JavaDoc obj = directory.lookup(localXsltFile);
1434                if ((obj != null) && (obj instanceof Resource)) {
1435                    InputStream JavaDoc is = ((Resource) obj).streamContent();
1436                    if (is != null)
1437                        return is;
1438                }
1439            } catch (NamingException JavaDoc e) {
1440                throw new ServletException JavaDoc("Error opening XSLT resource", e);
1441            }
1442        }
1443
1444        /* Open and read in file in one fell swoop to reduce chance
1445         * chance of leaving handle open.
1446         */

1447        if (globalXsltFile!=null) {
1448            FileInputStream JavaDoc fis = null;
1449
1450            try {
1451                File JavaDoc f = new File JavaDoc(globalXsltFile);
1452                if (f.exists()){
1453                    fis =new FileInputStream JavaDoc(f);
1454                    byte b[] = new byte[(int)f.length()]; /* danger! */
1455                    fis.read(b);
1456                    return new ByteArrayInputStream JavaDoc(b);
1457                }
1458            } finally {
1459                if (fis!=null)
1460                    fis.close();
1461            }
1462        }
1463
1464        return null;
1465
1466    }
1467
1468
1469    // -------------------------------------------------------- protected Methods
1470

1471
1472    /**
1473     * Check if sendfile can be used.
1474     */

1475    protected boolean checkSendfile(HttpServletRequest JavaDoc request,
1476                                  HttpServletResponse JavaDoc response,
1477                                  CacheEntry entry,
1478                                  long length, Range range) {
1479        if ((sendfileSize > 0)
1480            && (entry.resource != null)
1481            && ((length > sendfileSize) || (entry.resource.getContent() == null))
1482            && (entry.attributes.getCanonicalPath() != null)
1483            && (Boolean.TRUE == request.getAttribute("org.apache.tomcat.sendfile.support"))
1484            && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade"))
1485            && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) {
1486            request.setAttribute("org.apache.tomcat.sendfile.filename", entry.attributes.getCanonicalPath());
1487            if (range == null) {
1488                request.setAttribute("org.apache.tomcat.sendfile.start", new Long JavaDoc(0L));
1489                request.setAttribute("org.apache.tomcat.sendfile.end", new Long JavaDoc(length));
1490            } else {
1491                request.setAttribute("org.apache.tomcat.sendfile.start", new Long JavaDoc(range.start));
1492                request.setAttribute("org.apache.tomcat.sendfile.end", new Long JavaDoc(range.end + 1));
1493            }
1494            request.setAttribute("org.apache.tomcat.sendfile.token", this);
1495            return true;
1496        } else {
1497            return false;
1498        }
1499    }
1500    
1501    
1502    /**
1503     * Check if the if-match condition is satisfied.
1504     *
1505     * @param request The servlet request we are processing
1506     * @param response The servlet response we are creating
1507     * @param resourceInfo File object
1508     * @return boolean true if the resource meets the specified condition,
1509     * and false if the condition is not satisfied, in which case request
1510     * processing is stopped
1511     */

1512    protected boolean checkIfMatch(HttpServletRequest JavaDoc request,
1513                                 HttpServletResponse JavaDoc response,
1514                                 ResourceAttributes resourceAttributes)
1515        throws IOException JavaDoc {
1516
1517        String JavaDoc eTag = getETag(resourceAttributes);
1518        String JavaDoc headerValue = request.getHeader("If-Match");
1519        if (headerValue != null) {
1520            if (headerValue.indexOf('*') == -1) {
1521
1522                StringTokenizer JavaDoc commaTokenizer = new StringTokenizer JavaDoc
1523                    (headerValue, ",");
1524                boolean conditionSatisfied = false;
1525
1526                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
1527                    String JavaDoc currentToken = commaTokenizer.nextToken();
1528                    if (currentToken.trim().equals(eTag))
1529                        conditionSatisfied = true;
1530                }
1531
1532                // If none of the given ETags match, 412 Precodition failed is
1533
// sent back
1534
if (!conditionSatisfied) {
1535                    response.sendError
1536                        (HttpServletResponse.SC_PRECONDITION_FAILED);
1537                    return false;
1538                }
1539
1540            }
1541        }
1542        return true;
1543
1544    }
1545
1546
1547    /**
1548     * Check if the if-modified-since condition is satisfied.
1549     *
1550     * @param request The servlet request we are processing
1551     * @param response The servlet response we are creating
1552     * @param resourceInfo File object
1553     * @return boolean true if the resource meets the specified condition,
1554     * and false if the condition is not satisfied, in which case request
1555     * processing is stopped
1556     */

1557    protected boolean checkIfModifiedSince(HttpServletRequest JavaDoc request,
1558                                         HttpServletResponse JavaDoc response,
1559                                         ResourceAttributes resourceAttributes)
1560        throws IOException JavaDoc {
1561        try {
1562            long headerValue = request.getDateHeader("If-Modified-Since");
1563            long lastModified = resourceAttributes.getLastModified();
1564            if (headerValue != -1) {
1565
1566                // If an If-None-Match header has been specified, if modified since
1567
// is ignored.
1568
if ((request.getHeader("If-None-Match") == null)
1569                    && (lastModified <= headerValue + 1000)) {
1570                    // The entity has not been modified since the date
1571
// specified by the client. This is not an error case.
1572
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
1573                    return false;
1574                }
1575            }
1576        } catch (IllegalArgumentException JavaDoc illegalArgument) {
1577            return true;
1578        }
1579        return true;
1580
1581    }
1582
1583
1584    /**
1585     * Check if the if-none-match condition is satisfied.
1586     *
1587     * @param request The servlet request we are processing
1588     * @param response The servlet response we are creating
1589     * @param resourceInfo File object
1590     * @return boolean true if the resource meets the specified condition,
1591     * and false if the condition is not satisfied, in which case request
1592     * processing is stopped
1593     */

1594    protected boolean checkIfNoneMatch(HttpServletRequest JavaDoc request,
1595                                     HttpServletResponse JavaDoc response,
1596                                     ResourceAttributes resourceAttributes)
1597        throws IOException JavaDoc {
1598
1599        String JavaDoc eTag = getETag(resourceAttributes);
1600        String JavaDoc headerValue = request.getHeader("If-None-Match");
1601        if (headerValue != null) {
1602
1603            boolean conditionSatisfied = false;
1604
1605            if (!headerValue.equals("*")) {
1606
1607                StringTokenizer JavaDoc commaTokenizer =
1608                    new StringTokenizer JavaDoc(headerValue, ",");
1609
1610                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
1611                    String JavaDoc currentToken = commaTokenizer.nextToken();
1612                    if (currentToken.trim().equals(eTag))
1613                        conditionSatisfied = true;
1614                }
1615
1616            } else {
1617                conditionSatisfied = true;
1618            }
1619
1620            if (conditionSatisfied) {
1621
1622                // For GET and HEAD, we should respond with
1623
// 304 Not Modified.
1624
// For every other method, 412 Precondition Failed is sent
1625
// back.
1626
if ( ("GET".equals(request.getMethod()))
1627                     || ("HEAD".equals(request.getMethod())) ) {
1628                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
1629                    return false;
1630                } else {
1631                    response.sendError
1632                        (HttpServletResponse.SC_PRECONDITION_FAILED);
1633                    return false;
1634                }
1635            }
1636        }
1637        return true;
1638
1639    }
1640
1641
1642    /**
1643     * Check if the if-unmodified-since condition is satisfied.
1644     *
1645     * @param request The servlet request we are processing
1646     * @param response The servlet response we are creating
1647     * @param resourceInfo File object
1648     * @return boolean true if the resource meets the specified condition,
1649     * and false if the condition is not satisfied, in which case request
1650     * processing is stopped
1651     */

1652    protected boolean checkIfUnmodifiedSince(HttpServletRequest JavaDoc request,
1653                                           HttpServletResponse JavaDoc response,
1654                                           ResourceAttributes resourceAttributes)
1655        throws IOException JavaDoc {
1656        try {
1657            long lastModified = resourceAttributes.getLastModified();
1658            long headerValue = request.getDateHeader("If-Unmodified-Since");
1659            if (headerValue != -1) {
1660                if ( lastModified > (headerValue + 1000)) {
1661                    // The entity has not been modified since the date
1662
// specified by the client. This is not an error case.
1663
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
1664                    return false;
1665                }
1666            }
1667        } catch(IllegalArgumentException JavaDoc illegalArgument) {
1668            return true;
1669        }
1670        return true;
1671
1672    }
1673
1674
1675    /**
1676     * Copy the contents of the specified input stream to the specified
1677     * output stream, and ensure that both streams are closed before returning
1678     * (even in the face of an exception).
1679     *
1680     * @param resourceInfo The resource information
1681     * @param ostream The output stream to write to
1682     *
1683     * @exception IOException if an input/output error occurs
1684     */

1685    protected void copy(CacheEntry cacheEntry, InputStream JavaDoc is,
1686                      ServletOutputStream JavaDoc ostream)
1687        throws IOException JavaDoc {
1688
1689        IOException JavaDoc exception = null;
1690        InputStream JavaDoc resourceInputStream = null;
1691
1692        // Optimization: If the binary content has already been loaded, send
1693
// it directly
1694
if (cacheEntry.resource != null) {
1695            byte buffer[] = cacheEntry.resource.getContent();
1696            if (buffer != null) {
1697                ostream.write(buffer, 0, buffer.length);
1698                return;
1699            }
1700            resourceInputStream = cacheEntry.resource.streamContent();
1701        } else {
1702            resourceInputStream = is;
1703        }
1704
1705        InputStream JavaDoc istream = new BufferedInputStream JavaDoc
1706            (resourceInputStream, input);
1707
1708        // Copy the input stream to the output stream
1709
exception = copyRange(istream, ostream);
1710
1711        // Clean up the input stream
1712
istream.close();
1713
1714        // Rethrow any exception that has occurred
1715
if (exception != null)
1716            throw exception;
1717
1718    }
1719
1720
1721    /**
1722     * Copy the contents of the specified input stream to the specified
1723     * output stream, and ensure that both streams are closed before returning
1724     * (even in the face of an exception).
1725     *
1726     * @param resourceInfo The resource info
1727     * @param writer The writer to write to
1728     *
1729     * @exception IOException if an input/output error occurs
1730     */

1731    protected void copy(CacheEntry cacheEntry, InputStream JavaDoc is, PrintWriter JavaDoc writer)
1732        throws IOException JavaDoc {
1733
1734        IOException JavaDoc exception = null;
1735
1736        InputStream JavaDoc resourceInputStream = null;
1737        if (cacheEntry.resource != null) {
1738            resourceInputStream = cacheEntry.resource.streamContent();
1739        } else {
1740            resourceInputStream = is;
1741        }
1742
1743        Reader JavaDoc reader;
1744        if (fileEncoding == null) {
1745            reader = new InputStreamReader JavaDoc(resourceInputStream);
1746        } else {
1747            reader = new InputStreamReader JavaDoc(resourceInputStream,
1748                                           fileEncoding);
1749        }
1750
1751        // Copy the input stream to the output stream
1752
exception = copyRange(reader, writer);
1753
1754        // Clean up the reader
1755
reader.close();
1756
1757        // Rethrow any exception that has occurred
1758
if (exception != null)
1759            throw exception;
1760
1761    }
1762
1763
1764    /**
1765     * Copy the contents of the specified input stream to the specified
1766     * output stream, and ensure that both streams are closed before returning
1767     * (even in the face of an exception).
1768     *
1769     * @param resourceInfo The ResourceInfo object
1770     * @param ostream The output stream to write to
1771     * @param range Range the client wanted to retrieve
1772     * @exception IOException if an input/output error occurs
1773     */

1774    protected void copy(CacheEntry cacheEntry, ServletOutputStream JavaDoc ostream,
1775                      Range range)
1776        throws IOException JavaDoc {
1777
1778        IOException JavaDoc exception = null;
1779
1780        InputStream JavaDoc resourceInputStream = cacheEntry.resource.streamContent();
1781        InputStream JavaDoc istream =
1782            new BufferedInputStream JavaDoc(resourceInputStream, input);
1783        exception = copyRange(istream, ostream, range.start, range.end);
1784
1785        // Clean up the input stream
1786
istream.close();
1787
1788        // Rethrow any exception that has occurred
1789
if (exception != null)
1790            throw exception;
1791
1792    }
1793
1794
1795    /**
1796     * Copy the contents of the specified input stream to the specified
1797     * output stream, and ensure that both streams are closed before returning
1798     * (even in the face of an exception).
1799     *
1800     * @param resourceInfo The ResourceInfo object
1801     * @param writer The writer to write to
1802     * @param range Range the client wanted to retrieve
1803     * @exception IOException if an input/output error occurs
1804     */

1805    protected void copy(CacheEntry cacheEntry, PrintWriter JavaDoc writer,
1806                      Range range)
1807        throws IOException JavaDoc {
1808
1809        IOException JavaDoc exception = null;
1810
1811        InputStream JavaDoc resourceInputStream = cacheEntry.resource.streamContent();
1812
1813        Reader JavaDoc reader;
1814        if (fileEncoding == null) {
1815            reader = new InputStreamReader JavaDoc(resourceInputStream);
1816        } else {
1817            reader = new InputStreamReader JavaDoc(resourceInputStream,
1818                                           fileEncoding);
1819        }
1820
1821        exception = copyRange(reader, writer, range.start, range.end);
1822
1823        // Clean up the input stream
1824
reader.close();
1825
1826        // Rethrow any exception that has occurred
1827
if (exception != null)
1828            throw exception;
1829
1830    }
1831
1832
1833    /**
1834     * Copy the contents of the specified input stream to the specified
1835     * output stream, and ensure that both streams are closed before returning
1836     * (even in the face of an exception).
1837     *
1838     * @param resourceInfo The ResourceInfo object
1839     * @param ostream The output stream to write to
1840     * @param ranges Enumeration of the ranges the client wanted to retrieve
1841     * @param contentType Content type of the resource
1842     * @exception IOException if an input/output error occurs
1843     */

1844    protected void copy(CacheEntry cacheEntry, ServletOutputStream JavaDoc ostream,
1845                      Iterator JavaDoc ranges, String JavaDoc contentType)
1846        throws IOException JavaDoc {
1847
1848        IOException JavaDoc exception = null;
1849
1850        while ( (exception == null) && (ranges.hasNext()) ) {
1851
1852            InputStream JavaDoc resourceInputStream = cacheEntry.resource.streamContent();
1853            InputStream JavaDoc istream =
1854                new BufferedInputStream JavaDoc(resourceInputStream, input);
1855
1856            Range currentRange = (Range) ranges.next();
1857
1858            // Writing MIME header.
1859
ostream.println();
1860            ostream.println("--" + mimeSeparation);
1861            if (contentType != null)
1862                ostream.println("Content-Type: " + contentType);
1863            ostream.println("Content-Range: bytes " + currentRange.start
1864                           + "-" + currentRange.end + "/"
1865                           + currentRange.length);
1866            ostream.println();
1867
1868            // Printing content
1869
exception = copyRange(istream, ostream, currentRange.start,
1870                                  currentRange.end);
1871
1872            istream.close();
1873
1874        }
1875
1876        ostream.println();
1877        ostream.print("--" + mimeSeparation + "--");
1878
1879        // Rethrow any exception that has occurred
1880
if (exception != null)
1881            throw exception;
1882
1883    }
1884
1885
1886    /**
1887     * Copy the contents of the specified input stream to the specified
1888     * output stream, and ensure that both streams are closed before returning
1889     * (even in the face of an exception).
1890     *
1891     * @param resourceInfo The ResourceInfo object
1892     * @param writer The writer to write to
1893     * @param ranges Enumeration of the ranges the client wanted to retrieve
1894     * @param contentType Content type of the resource
1895     * @exception IOException if an input/output error occurs
1896     */

1897    protected void copy(CacheEntry cacheEntry, PrintWriter JavaDoc writer,
1898                      Iterator JavaDoc ranges, String JavaDoc contentType)
1899        throws IOException JavaDoc {
1900
1901        IOException JavaDoc exception = null;
1902
1903        while ( (exception == null) && (ranges.hasNext()) ) {
1904
1905            InputStream JavaDoc resourceInputStream = cacheEntry.resource.streamContent();
1906            
1907            Reader JavaDoc reader;
1908            if (fileEncoding == null) {
1909                reader = new InputStreamReader JavaDoc(resourceInputStream);
1910            } else {
1911                reader = new InputStreamReader JavaDoc(resourceInputStream,
1912                                               fileEncoding);
1913            }
1914
1915            Range currentRange = (Range) ranges.next();
1916
1917            // Writing MIME header.
1918
writer.println();
1919            writer.println("--" + mimeSeparation);
1920            if (contentType != null)
1921                writer.println("Content-Type: " + contentType);
1922            writer.println("Content-Range: bytes " + currentRange.start
1923                           + "-" + currentRange.end + "/"
1924                           + currentRange.length);
1925            writer.println();
1926
1927            // Printing content
1928
exception = copyRange(reader, writer, currentRange.start,
1929                                  currentRange.end);
1930
1931            reader.close();
1932
1933        }
1934
1935        writer.println();
1936        writer.print("--" + mimeSeparation + "--");
1937
1938        // Rethrow any exception that has occurred
1939
if (exception != null)
1940            throw exception;
1941
1942    }
1943
1944
1945    /**
1946     * Copy the contents of the specified input stream to the specified
1947     * output stream, and ensure that both streams are closed before returning
1948     * (even in the face of an exception).
1949     *
1950     * @param istream The input stream to read from
1951     * @param ostream The output stream to write to
1952     * @return Exception which occurred during processing
1953     */

1954    protected IOException JavaDoc copyRange(InputStream JavaDoc istream,
1955                                  ServletOutputStream JavaDoc ostream) {
1956
1957        // Copy the input stream to the output stream
1958
IOException JavaDoc exception = null;
1959        byte buffer[] = new byte[input];
1960        int len = buffer.length;
1961        while (true) {
1962            try {
1963                len = istream.read(buffer);
1964                if (len == -1)
1965                    break;
1966                ostream.write(buffer, 0, len);
1967            } catch (IOException JavaDoc e) {
1968                exception = e;
1969                len = -1;
1970                break;
1971            }
1972        }
1973        return exception;
1974
1975    }
1976
1977
1978    /**
1979     * Copy the contents of the specified input stream to the specified
1980     * output stream, and ensure that both streams are closed before returning
1981     * (even in the face of an exception).
1982     *
1983     * @param reader The reader to read from
1984     * @param writer The writer to write to
1985     * @return Exception which occurred during processing
1986     */

1987    protected IOException JavaDoc copyRange(Reader JavaDoc reader, PrintWriter JavaDoc writer) {
1988
1989        // Copy the input stream to the output stream
1990
IOException JavaDoc exception = null;
1991        char buffer[] = new char[input];
1992        int len = buffer.length;
1993        while (true) {
1994            try {
1995                len = reader.read(buffer);
1996                if (len == -1)
1997                    break;
1998                writer.write(buffer, 0, len);
1999            } catch (IOException JavaDoc e) {
2000                exception = e;
2001                len = -1;
2002                break;
2003            }
2004        }
2005        return exception;
2006
2007    }
2008
2009
2010    /**
2011     * Copy the contents of the specified input stream to the specified
2012     * output stream, and ensure that both streams are closed before returning
2013     * (even in the face of an exception).
2014     *
2015     * @param istream The input stream to read from
2016     * @param ostream The output stream to write to
2017     * @param start Start of the range which will be copied
2018     * @param end End of the range which will be copied
2019     * @return Exception which occurred during processing
2020     */

2021    protected IOException JavaDoc copyRange(InputStream JavaDoc istream,
2022                                  ServletOutputStream JavaDoc ostream,
2023                                  long start, long end) {
2024
2025        if (debug > 10)
2026            log("Serving bytes:" + start + "-" + end);
2027
2028        try {
2029            istream.skip(start);
2030        } catch (IOException JavaDoc e) {
2031            return e;
2032        }
2033
2034        IOException JavaDoc exception = null;
2035        long bytesToRead = end - start + 1;
2036
2037        byte buffer[] = new byte[input];
2038        int len = buffer.length;
2039        while ( (bytesToRead > 0) && (len >= buffer.length)) {
2040            try {
2041                len = istream.read(buffer);
2042                if (bytesToRead >= len) {
2043                    ostream.write(buffer, 0, len);
2044                    bytesToRead -= len;
2045                } else {
2046                    ostream.write(buffer, 0, (int) bytesToRead);
2047                    bytesToRead = 0;
2048                }
2049            } catch (IOException JavaDoc e) {
2050                exception = e;
2051                len = -1;
2052            }
2053            if (len < buffer.length)
2054                break;
2055        }
2056
2057        return exception;
2058
2059    }
2060
2061
2062    /**
2063     * Copy the contents of the specified input stream to the specified
2064     * output stream, and ensure that both streams are closed before returning
2065     * (even in the face of an exception).
2066     *
2067     * @param reader The reader to read from
2068     * @param writer The writer to write to
2069     * @param start Start of the range which will be copied
2070     * @param end End of the range which will be copied
2071     * @return Exception which occurred during processing
2072     */

2073    protected IOException JavaDoc copyRange(Reader JavaDoc reader, PrintWriter JavaDoc writer,
2074                                  long start, long end) {
2075
2076        try {
2077            reader.skip(start);
2078        } catch (IOException JavaDoc e) {
2079            return e;
2080        }
2081
2082        IOException JavaDoc exception = null;
2083        long bytesToRead = end - start + 1;
2084
2085        char buffer[] = new char[input];
2086        int len = buffer.length;
2087        while ( (bytesToRead > 0) && (len >= buffer.length)) {
2088            try {
2089                len = reader.read(buffer);
2090                if (bytesToRead >= len) {
2091                    writer.write(buffer, 0, len);
2092                    bytesToRead -= len;
2093                } else {
2094                    writer.write(buffer, 0, (int) bytesToRead);
2095                    bytesToRead = 0;
2096                }
2097            } catch (IOException JavaDoc e) {
2098                exception = e;
2099                len = -1;
2100            }
2101            if (len < buffer.length)
2102                break;
2103        }
2104
2105        return exception;
2106
2107    }
2108
2109
2110
2111    // ------------------------------------------------------ Range Inner Class
2112

2113
2114    protected class Range {
2115
2116        public long start;
2117        public long end;
2118        public long length;
2119
2120        /**
2121         * Validate range.
2122         */

2123        public boolean validate() {
2124            if (end >= length)
2125                end = length - 1;
2126            return ( (start >= 0) && (end >= 0) && (start <= end)
2127                     && (length > 0) );
2128        }
2129
2130        public void recycle() {
2131            start = 0;
2132            end = 0;
2133            length = 0;
2134        }
2135
2136    }
2137
2138
2139}
2140
Popular Tags