001: // ========================================================================
002: // Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty.servlet;
016:
017: import java.io.File;
018: import java.io.FileInputStream;
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.OutputStream;
022: import java.net.MalformedURLException;
023: import java.util.Enumeration;
024: import java.util.List;
025:
026: import javax.servlet.RequestDispatcher;
027: import javax.servlet.ServletContext;
028: import javax.servlet.ServletException;
029: import javax.servlet.UnavailableException;
030: import javax.servlet.http.HttpServlet;
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpServletResponse;
033:
034: import org.mortbay.io.Buffer;
035: import org.mortbay.io.ByteArrayBuffer;
036: import org.mortbay.io.WriterOutputStream;
037: import org.mortbay.io.nio.NIOBuffer;
038: import org.mortbay.jetty.Connector;
039: import org.mortbay.jetty.HttpConnection;
040: import org.mortbay.jetty.HttpContent;
041: import org.mortbay.jetty.HttpFields;
042: import org.mortbay.jetty.HttpHeaders;
043: import org.mortbay.jetty.HttpMethods;
044: import org.mortbay.jetty.InclusiveByteRange;
045: import org.mortbay.jetty.MimeTypes;
046: import org.mortbay.jetty.ResourceCache;
047: import org.mortbay.jetty.Response;
048: import org.mortbay.jetty.handler.ContextHandler;
049: import org.mortbay.jetty.nio.NIOConnector;
050: import org.mortbay.log.Log;
051: import org.mortbay.resource.Resource;
052: import org.mortbay.resource.ResourceFactory;
053: import org.mortbay.util.IO;
054: import org.mortbay.util.MultiPartOutputStream;
055: import org.mortbay.util.TypeUtil;
056: import org.mortbay.util.URIUtil;
057:
058: /* ------------------------------------------------------------ */
059: /** The default servlet.
060: * This servlet, normally mapped to /, provides the handling for static
061: * content, OPTION and TRACE methods for the context.
062: * The following initParameters are supported:
063: * <PRE>
064: * acceptRanges If true, range requests and responses are
065: * supported
066: *
067: * dirAllowed If true, directory listings are returned if no
068: * welcome file is found. Else 403 Forbidden.
069: *
070: * redirectWelcome If true, welcome files are redirected rather than
071: * forwarded to.
072: *
073: * gzip If set to true, then static content will be served as
074: * gzip content encoded if a matching resource is
075: * found ending with ".gz"
076: *
077: * resourceBase Set to replace the context resource base
078: *
079: * relativeResourceBase
080: * Set with a pathname relative to the base of the
081: * servlet context root. Useful for only serving static content out
082: * of only specific subdirectories.
083: *
084: * aliases If True, aliases of resources are allowed (eg. symbolic
085: * links and caps variations). May bypass security constraints.
086: *
087: * maxCacheSize The maximum total size of the cache or 0 for no cache.
088: * maxCachedFileSize The maximum size of a file to cache
089: * maxCachedFiles The maximum number of files to cache
090: *
091: * useFileMappedBuffer
092: * If set to true, it will use mapped file buffer to serve static content
093: * when using NIO connector. Setting this value to false means that
094: * a direct buffer will be used instead of a mapped file buffer.
095: * By default, this is set to true.
096: * </PRE>
097: *
098: * The MOVE method is allowed if PUT and DELETE are allowed
099: *
100: * @author Greg Wilkins (gregw)
101: * @author Nigel Canonizado
102: */
103: public class DefaultServlet extends HttpServlet implements
104: ResourceFactory {
105: private static ByteArrayBuffer BYTE_RANGES = new ByteArrayBuffer(
106: "bytes");
107:
108: private ContextHandler.SContext _context;
109:
110: private boolean _acceptRanges = true;
111: private boolean _dirAllowed = true;
112: private boolean _redirectWelcome = false;
113: private boolean _gzip = true;
114:
115: private Resource _resourceBase;
116: private ResourceCache _cache;
117:
118: private MimeTypes _mimeTypes;
119: private String[] _welcomes;
120: private boolean _aliases = false;
121: private boolean _useFileMappedBuffer = false;
122:
123: /* ------------------------------------------------------------ */
124: public void init() throws UnavailableException {
125: ServletContext config = getServletContext();
126: _context = (ContextHandler.SContext) config;
127: _mimeTypes = _context.getContextHandler().getMimeTypes();
128:
129: _welcomes = _context.getContextHandler().getWelcomeFiles();
130: if (_welcomes == null)
131: _welcomes = new String[] { "index.jsp", "index.html" };
132:
133: _acceptRanges = getInitBoolean("acceptRanges", _acceptRanges);
134: _dirAllowed = getInitBoolean("dirAllowed", _dirAllowed);
135: _redirectWelcome = getInitBoolean("redirectWelcome",
136: _redirectWelcome);
137: _gzip = getInitBoolean("gzip", _gzip);
138:
139: _aliases = getInitBoolean("aliases", _aliases);
140: _useFileMappedBuffer = getInitBoolean("useFileMappedBuffer",
141: _useFileMappedBuffer);
142:
143: String rrb = getInitParameter("relativeResourceBase");
144: if (rrb != null) {
145: try {
146: _resourceBase = Resource.newResource(
147: _context.getResource("/")).addPath(rrb);
148: } catch (Exception e) {
149: Log.warn(Log.EXCEPTION, e);
150: throw new UnavailableException(e.toString());
151: }
152: }
153:
154: String rb = getInitParameter("resourceBase");
155: if (rrb != null && rb != null)
156: throw new UnavailableException(
157: "resourceBase & relativeResourceBase");
158:
159: if (rb != null) {
160: try {
161: _resourceBase = Resource.newResource(rb);
162: } catch (Exception e) {
163: Log.warn(Log.EXCEPTION, e);
164: throw new UnavailableException(e.toString());
165: }
166: }
167:
168: try {
169: if (_resourceBase == null)
170: _resourceBase = Resource.newResource(_context
171: .getResource("/"));
172:
173: int max_cache_size = getInitInt("maxCacheSize", -2);
174: if (max_cache_size == -2 || max_cache_size > 0) {
175: if (_cache == null)
176: _cache = new NIOResourceCache(_mimeTypes);
177:
178: if (max_cache_size > 0)
179: _cache.setMaxCacheSize(max_cache_size);
180: } else
181: _cache = null;
182:
183: if (_cache != null) {
184: int max_cached_file_size = getInitInt(
185: "maxCachedFileSize", -2);
186: if (max_cached_file_size >= -1)
187: _cache.setMaxCachedFileSize(max_cached_file_size);
188:
189: int max_cached_files = getInitInt("maxCachedFiles", -2);
190: if (max_cached_files >= -1)
191: _cache.setMaxCachedFiles(max_cached_files);
192:
193: _cache.start();
194: }
195: } catch (Exception e) {
196: Log.warn(Log.EXCEPTION, e);
197: throw new UnavailableException(e.toString());
198: }
199:
200: if (Log.isDebugEnabled())
201: Log.debug("resource base = " + _resourceBase);
202: }
203:
204: /* ------------------------------------------------------------ */
205: private boolean getInitBoolean(String name, boolean dft) {
206: String value = getInitParameter(name);
207: if (value == null || value.length() == 0)
208: return dft;
209: return (value.startsWith("t") || value.startsWith("T")
210: || value.startsWith("y") || value.startsWith("Y") || value
211: .startsWith("1"));
212: }
213:
214: /* ------------------------------------------------------------ */
215: private int getInitInt(String name, int dft) {
216: String value = getInitParameter(name);
217: if (value != null && value.length() > 0)
218: return Integer.parseInt(value);
219: return dft;
220: }
221:
222: /* ------------------------------------------------------------ */
223: /** get Resource to serve.
224: * Map a path to a resource. The default implementation calls
225: * HttpContext.getResource but derived servlets may provide
226: * their own mapping.
227: * @param pathInContext The path to find a resource for.
228: * @return The resource to serve.
229: */
230: public Resource getResource(String pathInContext) {
231: if (_resourceBase == null)
232: return null;
233: Resource r = null;
234: try {
235: r = _resourceBase.addPath(pathInContext);
236: if (!_aliases && r.getAlias() != null) {
237: if (r.exists())
238: Log.warn("Aliased resource: " + r + "=="
239: + r.getAlias());
240: return null;
241: }
242: if (Log.isDebugEnabled())
243: Log.debug("RESOURCE=" + r);
244: } catch (IOException e) {
245: Log.ignore(e);
246: }
247: return r;
248: }
249:
250: /* ------------------------------------------------------------ */
251: protected void doGet(HttpServletRequest request,
252: HttpServletResponse response) throws ServletException,
253: IOException {
254: String servletPath = null;
255: String pathInfo = null;
256: Enumeration reqRanges = null;
257: Boolean included = (Boolean) request
258: .getAttribute(Dispatcher.__INCLUDE_JETTY);
259: if (included != null && included.booleanValue()) {
260: servletPath = (String) request
261: .getAttribute(Dispatcher.__INCLUDE_SERVLET_PATH);
262: pathInfo = (String) request
263: .getAttribute(Dispatcher.__INCLUDE_PATH_INFO);
264: if (servletPath == null) {
265: servletPath = request.getServletPath();
266: pathInfo = request.getPathInfo();
267: }
268: } else {
269: included = Boolean.FALSE;
270: servletPath = request.getServletPath();
271: pathInfo = request.getPathInfo();
272:
273: // Is this a range request?
274: reqRanges = request.getHeaders(HttpHeaders.RANGE);
275: if (reqRanges != null && !reqRanges.hasMoreElements())
276: reqRanges = null;
277: }
278:
279: String pathInContext = URIUtil.addPaths(servletPath, pathInfo);
280: boolean endsWithSlash = pathInContext.endsWith("/");
281:
282: // Can we gzip this request?
283: String pathInContextGz = null;
284: boolean gzip = false;
285: if (_gzip && reqRanges == null && !endsWithSlash) {
286: String accept = request
287: .getHeader(HttpHeaders.ACCEPT_ENCODING);
288: if (accept != null && accept.indexOf("gzip") >= 0)
289: gzip = true;
290: }
291:
292: // Find the resource and content
293: Resource resource = null;
294: HttpContent content = null;
295: try {
296: // Try gzipped content first
297: if (gzip) {
298: pathInContextGz = pathInContext + ".gz";
299: if (_cache == null)
300: resource = getResource(pathInContextGz);
301: else {
302: content = _cache.lookup(pathInContextGz, this );
303: if (content != null)
304: resource = content.getResource();
305: else
306: resource = getResource(pathInContextGz);
307: }
308:
309: if (resource == null || !resource.exists()
310: || resource.isDirectory()) {
311: gzip = false;
312: pathInContextGz = null;
313: }
314: }
315:
316: // find resource
317: if (!gzip) {
318: if (_cache == null)
319: resource = getResource(pathInContext);
320: else {
321: content = _cache.lookup(pathInContext, this );
322:
323: if (content != null)
324: resource = content.getResource();
325: else
326: resource = getResource(pathInContext);
327: }
328: }
329:
330: if (Log.isDebugEnabled())
331: Log.debug("resource=" + resource
332: + (content != null ? " content" : ""));
333:
334: // Handle resource
335: if (resource == null || !resource.exists())
336: response.sendError(HttpServletResponse.SC_NOT_FOUND);
337: else if (!resource.isDirectory()) {
338: // ensure we have content
339: if (content == null)
340: content = new UnCachedContent(resource);
341:
342: if (included.booleanValue()
343: || passConditionalHeaders(request, response,
344: resource, content)) {
345: if (gzip) {
346: response.setHeader(
347: HttpHeaders.CONTENT_ENCODING, "gzip");
348: String mt = _context.getMimeType(pathInContext);
349: if (mt != null)
350: response.setContentType(mt);
351: }
352: sendData(request, response,
353: included.booleanValue(), resource, content,
354: reqRanges);
355: }
356: } else {
357: String welcome = null;
358:
359: if (!endsWithSlash && !pathInContext.equals("/")) {
360: StringBuffer buf = request.getRequestURL();
361: buf.append('/');
362: String q = request.getQueryString();
363: if (q != null && q.length() != 0) {
364: buf.append('?');
365: buf.append(q);
366: }
367: response.setContentLength(0);
368: response.sendRedirect(response
369: .encodeRedirectURL(buf.toString()));
370: }
371: // else look for a welcome file
372: else if (null != (welcome = getWelcomeFile(resource))) {
373: String ipath = URIUtil.addPaths(pathInContext,
374: welcome);
375: if (_redirectWelcome) {
376: // Redirect to the index
377: response.setContentLength(0);
378: String q = request.getQueryString();
379: if (q != null && q.length() != 0)
380: response.sendRedirect(URIUtil.addPaths(
381: _context.getContextPath(), ipath)
382: + "?" + q);
383: else
384: response.sendRedirect(URIUtil.addPaths(
385: _context.getContextPath(), ipath));
386: } else {
387: // Forward to the index
388: RequestDispatcher dispatcher = request
389: .getRequestDispatcher(ipath);
390: if (dispatcher != null) {
391: if (included.booleanValue())
392: dispatcher.include(request, response);
393: else
394: dispatcher.forward(request, response);
395: }
396: }
397: } else {
398: content = new UnCachedContent(resource);
399: if (included.booleanValue()
400: || passConditionalHeaders(request,
401: response, resource, content))
402: sendDirectory(request, response, resource,
403: pathInContext.length() > 1);
404: }
405: }
406: } catch (IllegalArgumentException e) {
407: Log.warn(Log.EXCEPTION, e);
408: } finally {
409: if (content != null)
410: content.release();
411: else if (resource != null)
412: resource.release();
413: }
414:
415: }
416:
417: /* ------------------------------------------------------------ */
418: protected void doPost(HttpServletRequest request,
419: HttpServletResponse response) throws ServletException,
420: IOException {
421: doGet(request, response);
422: }
423:
424: /* ------------------------------------------------------------ */
425: /**
426: * Finds a matching welcome file for the supplied {@link Resource}. This will be the first entry in the list of
427: * configured {@link #_welcomes welcome files} that existing within the directory referenced by the <code>Resource</code>.
428: * If the resource is not a directory, or no matching file is found, then <code>null</code> is returned.
429: * The list of welcome files is read from the {@link ContextHandler} for this servlet, or
430: * <code>"index.jsp" , "index.html"</code> if that is <code>null</code>.
431: * @param resource
432: * @return The name of the matching welcome file.
433: * @throws IOException
434: * @throws MalformedURLException
435: */
436: private String getWelcomeFile(Resource resource)
437: throws MalformedURLException, IOException {
438: if (!resource.isDirectory() || _welcomes == null)
439: return null;
440:
441: for (int i = 0; i < _welcomes.length; i++) {
442: Resource welcome = resource.addPath(_welcomes[i]);
443: if (welcome.exists())
444: return _welcomes[i];
445: }
446:
447: return null;
448: }
449:
450: /* ------------------------------------------------------------ */
451: /* Check modification date headers.
452: */
453: protected boolean passConditionalHeaders(
454: HttpServletRequest request, HttpServletResponse response,
455: Resource resource, HttpContent content) throws IOException {
456: if (!request.getMethod().equals(HttpMethods.HEAD)) {
457: String ifms = request
458: .getHeader(HttpHeaders.IF_MODIFIED_SINCE);
459: if (ifms != null) {
460: if (content != null) {
461: Buffer mdlm = content.getLastModified();
462: if (mdlm != null) {
463: if (ifms.equals(mdlm.toString())) {
464: response.reset();
465: response
466: .setStatus(HttpServletResponse.SC_NOT_MODIFIED);
467: response.flushBuffer();
468: return false;
469: }
470: }
471: }
472:
473: long ifmsl = request
474: .getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
475: if (ifmsl != -1) {
476: if (resource.lastModified() / 1000 <= ifmsl / 1000) {
477: response.reset();
478: response
479: .setStatus(HttpServletResponse.SC_NOT_MODIFIED);
480: response.flushBuffer();
481: return false;
482: }
483: }
484: }
485:
486: // Parse the if[un]modified dates and compare to resource
487: long date = request
488: .getDateHeader(HttpHeaders.IF_UNMODIFIED_SINCE);
489:
490: if (date != -1) {
491: if (resource.lastModified() / 1000 > date / 1000) {
492: response
493: .sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
494: return false;
495: }
496: }
497:
498: }
499: return true;
500: }
501:
502: /* ------------------------------------------------------------------- */
503: protected void sendDirectory(HttpServletRequest request,
504: HttpServletResponse response, Resource resource,
505: boolean parent) throws IOException {
506: if (!_dirAllowed) {
507: response.sendError(HttpServletResponse.SC_FORBIDDEN);
508: return;
509: }
510:
511: byte[] data = null;
512: String base = URIUtil.addPaths(request.getRequestURI(), "/");
513: String dir = resource.getListHTML(base, parent);
514: if (dir == null) {
515: response.sendError(HttpServletResponse.SC_FORBIDDEN,
516: "No directory");
517: return;
518: }
519:
520: data = dir.getBytes("UTF-8");
521: response.setContentType("text/html; charset=UTF-8");
522: response.setContentLength(data.length);
523: response.getOutputStream().write(data);
524: }
525:
526: /* ------------------------------------------------------------ */
527: protected void sendData(HttpServletRequest request,
528: HttpServletResponse response, boolean include,
529: Resource resource, HttpContent content,
530: Enumeration reqRanges) throws IOException {
531: long content_length = resource.length();
532:
533: // Get the output stream (or writer)
534: OutputStream out = null;
535: try {
536: out = response.getOutputStream();
537: } catch (IllegalStateException e) {
538: out = new WriterOutputStream(response.getWriter());
539: }
540:
541: if (reqRanges == null || !reqRanges.hasMoreElements()) {
542: // if there were no ranges, send entire entity
543: if (include) {
544: resource.writeTo(out, 0, content_length);
545: } else {
546: // See if a short direct method can be used?
547: if (out instanceof HttpConnection.Output) {
548: ((HttpConnection.Output) out).sendContent(content);
549: } else {
550:
551: // Write content normally
552: writeHeaders(response, content, content_length);
553: resource.writeTo(out, 0, content_length);
554: }
555: }
556: } else {
557: // Parse the satisfiable ranges
558: List ranges = InclusiveByteRange.satisfiableRanges(
559: reqRanges, content_length);
560:
561: // if there are no satisfiable ranges, send 416 response
562: if (ranges == null || ranges.size() == 0) {
563: writeHeaders(response, content, content_length);
564: response
565: .setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
566: response
567: .setHeader(
568: HttpHeaders.CONTENT_RANGE,
569: InclusiveByteRange
570: .to416HeaderRangeString(content_length));
571: resource.writeTo(out, 0, content_length);
572: return;
573: }
574:
575: // if there is only a single valid range (must be satisfiable
576: // since were here now), send that range with a 216 response
577: if (ranges.size() == 1) {
578: InclusiveByteRange singleSatisfiableRange = (InclusiveByteRange) ranges
579: .get(0);
580: long singleLength = singleSatisfiableRange
581: .getSize(content_length);
582: writeHeaders(response, content, singleLength);
583: response
584: .setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
585: response.setHeader(HttpHeaders.CONTENT_RANGE,
586: singleSatisfiableRange
587: .toHeaderRangeString(content_length));
588: resource.writeTo(out, singleSatisfiableRange
589: .getFirst(content_length), singleLength);
590: return;
591: }
592:
593: // multiple non-overlapping valid ranges cause a multipart
594: // 216 response which does not require an overall
595: // content-length header
596: //
597: writeHeaders(response, content, -1);
598: String mimetype = content.getContentType().toString();
599: MultiPartOutputStream multi = new MultiPartOutputStream(out);
600: response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
601:
602: // If the request has a "Request-Range" header then we need to
603: // send an old style multipart/x-byteranges Content-Type. This
604: // keeps Netscape and acrobat happy. This is what Apache does.
605: String ctp;
606: if (request.getHeader(HttpHeaders.REQUEST_RANGE) != null)
607: ctp = "multipart/x-byteranges; boundary=";
608: else
609: ctp = "multipart/byteranges; boundary=";
610: response.setContentType(ctp + multi.getBoundary());
611:
612: InputStream in = resource.getInputStream();
613: long pos = 0;
614:
615: for (int i = 0; i < ranges.size(); i++) {
616: InclusiveByteRange ibr = (InclusiveByteRange) ranges
617: .get(i);
618: String header = HttpHeaders.CONTENT_RANGE + ": "
619: + ibr.toHeaderRangeString(content_length);
620: multi.startPart(mimetype, new String[] { header });
621:
622: long start = ibr.getFirst(content_length);
623: long size = ibr.getSize(content_length);
624: if (in != null) {
625: // Handle non cached resource
626: if (start < pos) {
627: in.close();
628: in = resource.getInputStream();
629: pos = 0;
630: }
631: if (pos < start) {
632: in.skip(start - pos);
633: pos = start;
634: }
635: IO.copy(in, multi, size);
636: pos += size;
637: } else
638: // Handle cached resource
639: (resource).writeTo(multi, start, size);
640:
641: }
642: if (in != null)
643: in.close();
644: multi.close();
645: }
646: return;
647: }
648:
649: /* ------------------------------------------------------------ */
650: protected void writeHeaders(HttpServletResponse response,
651: HttpContent content, long count) throws IOException {
652: if (content.getContentType() != null)
653: response
654: .setContentType(content.getContentType().toString());
655:
656: if (response instanceof Response) {
657: Response r = (Response) response;
658: HttpFields headers = r.getHttpFields();
659:
660: if (content.getLastModified() != null)
661: headers.put(HttpHeaders.LAST_MODIFIED_BUFFER, content
662: .getLastModified());
663: else if (content.getResource() != null) {
664: long lml = content.getResource().lastModified();
665: if (lml != -1)
666: headers.putDateField(
667: HttpHeaders.LAST_MODIFIED_BUFFER, lml);
668: }
669:
670: if (count != -1)
671: r.setLongContentLength(count);
672:
673: if (_acceptRanges)
674: headers.put(HttpHeaders.ACCEPT_RANGES_BUFFER,
675: BYTE_RANGES);
676: } else {
677: if (content.getLastModified() != null)
678: response.setHeader(HttpHeaders.LAST_MODIFIED, content
679: .getLastModified().toString());
680: else
681: response.setDateHeader(HttpHeaders.LAST_MODIFIED,
682: content.getResource().lastModified());
683:
684: if (count != -1) {
685: if (count < Integer.MAX_VALUE)
686: response.setContentLength((int) count);
687: else
688: response.setHeader(HttpHeaders.CONTENT_LENGTH,
689: TypeUtil.toString(count));
690: }
691:
692: if (_acceptRanges)
693: response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
694: }
695: }
696:
697: /* ------------------------------------------------------------ */
698: /*
699: * @see javax.servlet.Servlet#destroy()
700: */
701: public void destroy() {
702: try {
703: if (_cache != null)
704: _cache.stop();
705: } catch (Exception e) {
706: Log.warn(Log.EXCEPTION, e);
707: } finally {
708: super .destroy();
709: }
710: }
711:
712: /* ------------------------------------------------------------ */
713: /* ------------------------------------------------------------ */
714: /* ------------------------------------------------------------ */
715: private class UnCachedContent implements HttpContent {
716: Resource _resource;
717:
718: UnCachedContent(Resource resource) {
719: _resource = resource;
720: }
721:
722: /* ------------------------------------------------------------ */
723: public Buffer getContentType() {
724: return _mimeTypes.getMimeByExtension(_resource.toString());
725: }
726:
727: /* ------------------------------------------------------------ */
728: public Buffer getLastModified() {
729: return null;
730: }
731:
732: /* ------------------------------------------------------------ */
733: public Buffer getBuffer() {
734: return null;
735: }
736:
737: /* ------------------------------------------------------------ */
738: public long getContentLength() {
739: return _resource.length();
740: }
741:
742: /* ------------------------------------------------------------ */
743: public InputStream getInputStream() throws IOException {
744: return _resource.getInputStream();
745: }
746:
747: /* ------------------------------------------------------------ */
748: public Resource getResource() {
749: return _resource;
750: }
751:
752: /* ------------------------------------------------------------ */
753: public void release() {
754: _resource.release();
755: _resource = null;
756: }
757:
758: }
759:
760: /* ------------------------------------------------------------ */
761: /* ------------------------------------------------------------ */
762: class NIOResourceCache extends ResourceCache {
763: /* ------------------------------------------------------------ */
764: public NIOResourceCache(MimeTypes mimeTypes) {
765: super (mimeTypes);
766: }
767:
768: /* ------------------------------------------------------------ */
769: protected void fill(Content content) throws IOException {
770: Connector connector = HttpConnection.getCurrentConnection()
771: .getConnector();
772: if (connector instanceof NIOConnector) {
773: Buffer buffer = null;
774: Resource resource = content.getResource();
775: long length = resource.length();
776:
777: if (_useFileMappedBuffer && resource.getFile() != null) {
778: File file = resource.getFile();
779: if (file != null)
780: buffer = new NIOBuffer(file);
781: } else {
782: InputStream is = resource.getInputStream();
783: try {
784: buffer = new NIOBuffer(
785: (int) length,
786: ((NIOConnector) connector)
787: .getUseDirectBuffers() ? NIOBuffer.DIRECT
788: : NIOBuffer.INDIRECT);
789: } catch (OutOfMemoryError e) {
790: Log.warn(e.toString());
791: Log.debug(e);
792: buffer = new NIOBuffer((int) length,
793: NIOBuffer.INDIRECT);
794: }
795: buffer.readFrom(is, (int) length);
796: is.close();
797: }
798: content.setBuffer(buffer);
799: } else {
800: super.fill(content);
801: }
802:
803: }
804: }
805: }
|