1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.servlet;
15
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.util.Enumeration;
22 import java.util.List;
23
24 import javax.servlet.RequestDispatcher;
25 import javax.servlet.ServletContext;
26 import javax.servlet.ServletException;
27 import javax.servlet.UnavailableException;
28 import javax.servlet.http.HttpServlet;
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
31 import javax.servlet.http.HttpServletResponseWrapper;
32
33 import org.eclipse.jetty.http.HttpContent;
34 import org.eclipse.jetty.http.HttpFields;
35 import org.eclipse.jetty.http.HttpHeaderValues;
36 import org.eclipse.jetty.http.HttpHeaders;
37 import org.eclipse.jetty.http.HttpMethods;
38 import org.eclipse.jetty.http.MimeTypes;
39 import org.eclipse.jetty.io.Buffer;
40 import org.eclipse.jetty.io.ByteArrayBuffer;
41 import org.eclipse.jetty.io.WriterOutputStream;
42 import org.eclipse.jetty.server.Connector;
43 import org.eclipse.jetty.server.Dispatcher;
44 import org.eclipse.jetty.server.HttpConnection;
45 import org.eclipse.jetty.server.InclusiveByteRange;
46 import org.eclipse.jetty.server.ResourceCache;
47 import org.eclipse.jetty.server.Response;
48 import org.eclipse.jetty.server.handler.ContextHandler;
49 import org.eclipse.jetty.server.nio.NIOConnector;
50 import org.eclipse.jetty.util.IO;
51 import org.eclipse.jetty.util.MultiPartOutputStream;
52 import org.eclipse.jetty.util.TypeUtil;
53 import org.eclipse.jetty.util.URIUtil;
54 import org.eclipse.jetty.util.log.Log;
55 import org.eclipse.jetty.util.resource.FileResource;
56 import org.eclipse.jetty.util.resource.Resource;
57 import org.eclipse.jetty.util.resource.ResourceFactory;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public class DefaultServlet extends HttpServlet implements ResourceFactory
116 {
117 private ServletContext _servletContext;
118 private ContextHandler _contextHandler;
119
120 private boolean _acceptRanges=true;
121 private boolean _dirAllowed=true;
122 private boolean _redirectWelcome=false;
123 private boolean _gzip=true;
124
125 private Resource _resourceBase;
126 private NIOResourceCache _nioCache;
127 private ResourceCache _bioCache;
128
129 private MimeTypes _mimeTypes;
130 private String[] _welcomes;
131 private boolean _useFileMappedBuffer=false;
132 private ByteArrayBuffer _cacheControl;
133 private String _relativeResourceBase;
134
135
136
137 public void init()
138 throws UnavailableException
139 {
140 _servletContext=getServletContext();
141 ContextHandler.Context scontext=ContextHandler.getCurrentContext();
142 if (scontext==null)
143 _contextHandler=((ContextHandler.Context)_servletContext).getContextHandler();
144 else
145 _contextHandler = ContextHandler.getCurrentContext().getContextHandler();
146
147 _mimeTypes = _contextHandler.getMimeTypes();
148
149 _welcomes = _contextHandler.getWelcomeFiles();
150 if (_welcomes==null)
151 _welcomes=new String[] {"index.jsp","index.html"};
152
153 _acceptRanges=getInitBoolean("acceptRanges",_acceptRanges);
154 _dirAllowed=getInitBoolean("dirAllowed",_dirAllowed);
155 _redirectWelcome=getInitBoolean("redirectWelcome",_redirectWelcome);
156 _gzip=getInitBoolean("gzip",_gzip);
157
158 if (getInitParameter("aliases")!=null)
159 _contextHandler.setAliases(getInitBoolean("aliases",false));
160 boolean aliases=_contextHandler.isAliases();
161 if (!aliases && !FileResource.getCheckAliases())
162 throw new IllegalStateException("Alias checking disabled");
163 if (aliases)
164 _servletContext.log("Aliases are enabled");
165
166 _useFileMappedBuffer=getInitBoolean("useFileMappedBuffer",_useFileMappedBuffer);
167
168 _relativeResourceBase = getInitParameter("relativeResourceBase");
169
170 String rb=getInitParameter("resourceBase");
171 if (rb!=null)
172 {
173 if (_relativeResourceBase!=null)
174 throw new UnavailableException("resourceBase & relativeResourceBase");
175 try{_resourceBase=_contextHandler.newResource(rb);}
176 catch (Exception e)
177 {
178 Log.warn(Log.EXCEPTION,e);
179 throw new UnavailableException(e.toString());
180 }
181 }
182
183 String t=getInitParameter("cacheControl");
184 if (t!=null)
185 _cacheControl=new ByteArrayBuffer(t);
186
187 try
188 {
189 String cache_type =getInitParameter("cacheType");
190 int max_cache_size=getInitInt("maxCacheSize", -2);
191 int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
192 int max_cached_files=getInitInt("maxCachedFiles", -2);
193
194 if (cache_type==null || "nio".equals(cache_type)|| "both".equals(cache_type))
195 {
196 if (max_cache_size==-2 || max_cache_size>0)
197 {
198 _nioCache=new NIOResourceCache(_mimeTypes);
199 _nioCache.setUseFileMappedBuffer(_useFileMappedBuffer);
200 if (max_cache_size>0)
201 _nioCache.setMaxCacheSize(max_cache_size);
202 if (max_cached_file_size>=-1)
203 _nioCache.setMaxCachedFileSize(max_cached_file_size);
204 if (max_cached_files>=-1)
205 _nioCache.setMaxCachedFiles(max_cached_files);
206 _nioCache.start();
207 }
208 }
209 if ("bio".equals(cache_type)|| "both".equals(cache_type))
210 {
211 if (max_cache_size==-2 || max_cache_size>0)
212 {
213 _bioCache=new ResourceCache(_mimeTypes);
214 if (max_cache_size>0)
215 _bioCache.setMaxCacheSize(max_cache_size);
216 if (max_cached_file_size>=-1)
217 _bioCache.setMaxCachedFileSize(max_cached_file_size);
218 if (max_cached_files>=-1)
219 _bioCache.setMaxCachedFiles(max_cached_files);
220 _bioCache.start();
221 }
222 }
223 if (_nioCache==null)
224 _bioCache=null;
225
226 }
227 catch (Exception e)
228 {
229 Log.warn(Log.EXCEPTION,e);
230 throw new UnavailableException(e.toString());
231 }
232
233 if (Log.isDebugEnabled()) Log.debug("resource base = "+_resourceBase);
234 }
235
236
237 public String getInitParameter(String name)
238 {
239 String value=getServletContext().getInitParameter("org.eclipse.jetty.servlet.Default."+name);
240 if (value==null)
241 value=super.getInitParameter(name);
242 return value;
243 }
244
245
246 private boolean getInitBoolean(String name, boolean dft)
247 {
248 String value=getInitParameter(name);
249 if (value==null || value.length()==0)
250 return dft;
251 return (value.startsWith("t")||
252 value.startsWith("T")||
253 value.startsWith("y")||
254 value.startsWith("Y")||
255 value.startsWith("1"));
256 }
257
258
259 private int getInitInt(String name, int dft)
260 {
261 String value=getInitParameter(name);
262 if (value==null)
263 value=getInitParameter(name);
264 if (value!=null && value.length()>0)
265 return Integer.parseInt(value);
266 return dft;
267 }
268
269
270
271
272
273
274
275
276
277 public Resource getResource(String pathInContext)
278 {
279 Resource r=null;
280 if (_relativeResourceBase!=null)
281 pathInContext=URIUtil.addPaths(_relativeResourceBase,pathInContext);
282
283 try
284 {
285 if (_resourceBase!=null)
286 r = _resourceBase.addPath(pathInContext);
287 else
288 {
289 URL u = _servletContext.getResource(pathInContext);
290 r = _contextHandler.newResource(u);
291 }
292
293 if (Log.isDebugEnabled()) Log.debug("RESOURCE="+r);
294 }
295 catch (IOException e)
296 {
297 Log.ignore(e);
298 }
299 return r;
300 }
301
302
303 protected void doGet(HttpServletRequest request, HttpServletResponse response)
304 throws ServletException, IOException
305 {
306 String servletPath=null;
307 String pathInfo=null;
308 Enumeration reqRanges = null;
309 Boolean included =request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI)!=null;
310 if (included!=null && included.booleanValue())
311 {
312 servletPath=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
313 pathInfo=(String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
314 if (servletPath==null)
315 {
316 servletPath=request.getServletPath();
317 pathInfo=request.getPathInfo();
318 }
319 }
320 else
321 {
322 included=Boolean.FALSE;
323 servletPath=request.getServletPath();
324 pathInfo=request.getPathInfo();
325
326
327 reqRanges = request.getHeaders(HttpHeaders.RANGE);
328 if (reqRanges!=null && !reqRanges.hasMoreElements())
329 reqRanges=null;
330 }
331
332 String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
333 boolean endsWithSlash=pathInContext.endsWith(URIUtil.SLASH);
334
335
336 String pathInContextGz=null;
337 boolean gzip=false;
338 if (!included.booleanValue() && _gzip && reqRanges==null && !endsWithSlash )
339 {
340 String accept=request.getHeader(HttpHeaders.ACCEPT_ENCODING);
341 if (accept!=null && accept.indexOf("gzip")>=0)
342 gzip=true;
343 }
344
345
346 Resource resource=null;
347 HttpContent content=null;
348
349 Connector connector = HttpConnection.getCurrentConnection().getConnector();
350 ResourceCache cache=(connector instanceof NIOConnector) ?_nioCache:_bioCache;
351 try
352 {
353
354 if (gzip)
355 {
356 pathInContextGz=pathInContext+".gz";
357
358 if (cache==null)
359 {
360 resource=getResource(pathInContextGz);
361 }
362 else
363 {
364 content=cache.lookup(pathInContextGz,this);
365
366 if (content!=null)
367 resource=content.getResource();
368 else
369 resource=getResource(pathInContextGz);
370 }
371
372 if (resource==null || !resource.exists()|| resource.isDirectory())
373 {
374 if (cache!=null && content==null)
375 {
376 String real_path=_servletContext.getRealPath(pathInContextGz);
377 if (real_path!=null)
378 cache.miss(pathInContextGz,_contextHandler.newResource(real_path));
379 }
380 gzip=false;
381 pathInContextGz=null;
382 }
383 }
384
385
386 if (!gzip)
387 {
388 if (cache==null)
389 resource=getResource(pathInContext);
390 else
391 {
392 content=cache.lookup(pathInContext,this);
393
394 if (content!=null)
395 resource=content.getResource();
396 else
397 resource=getResource(pathInContext);
398 }
399 }
400
401 if (Log.isDebugEnabled())
402 Log.debug("resource="+resource+(content!=null?" content":""));
403
404
405 if (resource==null || !resource.exists())
406 response.sendError(HttpServletResponse.SC_NOT_FOUND);
407 else if (!resource.isDirectory())
408 {
409
410 if (content==null)
411 content=new UnCachedContent(resource);
412
413 if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
414 {
415 if (gzip)
416 {
417 response.setHeader(HttpHeaders.CONTENT_ENCODING,"gzip");
418 String mt=_servletContext.getMimeType(pathInContext);
419 if (mt!=null)
420 response.setContentType(mt);
421 }
422 sendData(request,response,included.booleanValue(),resource,content,reqRanges);
423 }
424 }
425 else
426 {
427 String welcome=null;
428
429 if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null))
430 {
431 StringBuffer buf=request.getRequestURL();
432 synchronized(buf)
433 {
434 int param=buf.lastIndexOf(";");
435 if (param<0)
436 buf.append('/');
437 else
438 buf.insert(param,'/');
439 String q=request.getQueryString();
440 if (q!=null&&q.length()!=0)
441 {
442 buf.append('?');
443 buf.append(q);
444 }
445 response.setContentLength(0);
446 response.sendRedirect(response.encodeRedirectURL(buf.toString()));
447 }
448 }
449
450 else if (null!=(welcome=getWelcomeFile(resource)))
451 {
452 String ipath=URIUtil.addPaths(pathInContext,welcome);
453 if (_redirectWelcome)
454 {
455
456 response.setContentLength(0);
457 String q=request.getQueryString();
458 if (q!=null&&q.length()!=0)
459 response.sendRedirect(URIUtil.addPaths( _servletContext.getContextPath(),ipath)+"?"+q);
460 else
461 response.sendRedirect(URIUtil.addPaths( _servletContext.getContextPath(),ipath));
462 }
463 else
464 {
465
466 RequestDispatcher dispatcher=request.getRequestDispatcher(ipath);
467 if (dispatcher!=null)
468 {
469 if (included.booleanValue())
470 dispatcher.include(request,response);
471 else
472 {
473 request.setAttribute("org.eclipse.jetty.server.welcome",ipath);
474 dispatcher.forward(request,response);
475 }
476 }
477 }
478 }
479 else
480 {
481 content=new UnCachedContent(resource);
482 if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
483 sendDirectory(request,response,resource,pathInContext.length()>1);
484 }
485 }
486 }
487 catch(IllegalArgumentException e)
488 {
489 Log.warn(Log.EXCEPTION,e);
490 if(!response.isCommitted())
491 response.sendError(500, e.getMessage());
492 }
493 finally
494 {
495 if (content!=null)
496 content.release();
497 else if (resource!=null)
498 resource.release();
499 }
500
501 }
502
503
504 protected void doPost(HttpServletRequest request, HttpServletResponse response)
505 throws ServletException, IOException
506 {
507 doGet(request,response);
508 }
509
510
511
512
513
514 protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
515 {
516 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530
531 private String getWelcomeFile(Resource resource) throws MalformedURLException, IOException
532 {
533 if (!resource.isDirectory() || _welcomes==null)
534 return null;
535
536 for (int i=0;i<_welcomes.length;i++)
537 {
538 Resource welcome=resource.addPath(_welcomes[i]);
539 if (welcome.exists())
540 return _welcomes[i];
541 }
542
543 return null;
544 }
545
546
547
548
549 protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, Resource resource, HttpContent content)
550 throws IOException
551 {
552 try
553 {
554 if (!request.getMethod().equals(HttpMethods.HEAD) )
555 {
556 String ifms=request.getHeader(HttpHeaders.IF_MODIFIED_SINCE);
557 if (ifms!=null)
558 {
559 if (content!=null)
560 {
561 Buffer mdlm=content.getLastModified();
562 if (mdlm!=null)
563 {
564 if (ifms.equals(mdlm.toString()))
565 {
566 response.reset();
567 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
568 response.flushBuffer();
569 return false;
570 }
571 }
572 }
573
574 long ifmsl=request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
575 if (ifmsl!=-1)
576 {
577 if (resource.lastModified()/1000 <= ifmsl/1000)
578 {
579 response.reset();
580 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
581 response.flushBuffer();
582 return false;
583 }
584 }
585 }
586
587
588 long date=request.getDateHeader(HttpHeaders.IF_UNMODIFIED_SINCE);
589
590 if (date!=-1)
591 {
592 if (resource.lastModified()/1000 > date/1000)
593 {
594 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
595 return false;
596 }
597 }
598
599 }
600 }
601 catch(IllegalArgumentException iae)
602 {
603 if(!response.isCommitted())
604 response.sendError(400, iae.getMessage());
605 throw iae;
606 }
607 return true;
608 }
609
610
611
612 protected void sendDirectory(HttpServletRequest request,
613 HttpServletResponse response,
614 Resource resource,
615 boolean parent)
616 throws IOException
617 {
618 if (!_dirAllowed)
619 {
620 response.sendError(HttpServletResponse.SC_FORBIDDEN);
621 return;
622 }
623
624 byte[] data=null;
625 String base = URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH);
626 String dir = resource.getListHTML(base,parent);
627 if (dir==null)
628 {
629 response.sendError(HttpServletResponse.SC_FORBIDDEN,
630 "No directory");
631 return;
632 }
633
634 data=dir.getBytes("UTF-8");
635 response.setContentType("text/html; charset=UTF-8");
636 response.setContentLength(data.length);
637 response.getOutputStream().write(data);
638 }
639
640
641 protected void sendData(HttpServletRequest request,
642 HttpServletResponse response,
643 boolean include,
644 Resource resource,
645 HttpContent content,
646 Enumeration reqRanges)
647 throws IOException
648 {
649 long content_length=resource.length();
650
651
652 OutputStream out =null;
653 try{out = response.getOutputStream();}
654 catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());}
655
656 if ( reqRanges == null || !reqRanges.hasMoreElements())
657 {
658
659 if (include)
660 {
661 resource.writeTo(out,0,content_length);
662 }
663 else
664 {
665
666 if (out instanceof HttpConnection.Output)
667 {
668 if (response instanceof Response)
669 {
670 writeOptionHeaders(((Response)response).getHttpFields());
671 ((HttpConnection.Output)out).sendContent(content);
672 }
673 else
674 {
675 writeHeaders(response,content,content_length);
676 ((HttpConnection.Output)out).sendContent(content.getBuffer());
677 }
678 }
679 else
680 {
681
682 writeHeaders(response,content,content_length);
683 resource.writeTo(out,0,content_length);
684 }
685 }
686 }
687 else
688 {
689
690 List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length);
691
692
693 if (ranges==null || ranges.size()==0)
694 {
695 writeHeaders(response, content, content_length);
696 response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
697 response.setHeader(HttpHeaders.CONTENT_RANGE,
698 InclusiveByteRange.to416HeaderRangeString(content_length));
699 resource.writeTo(out,0,content_length);
700 return;
701 }
702
703
704
705
706 if ( ranges.size()== 1)
707 {
708 InclusiveByteRange singleSatisfiableRange =
709 (InclusiveByteRange)ranges.get(0);
710 long singleLength = singleSatisfiableRange.getSize(content_length);
711 writeHeaders(response,content,singleLength );
712 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
713 response.setHeader(HttpHeaders.CONTENT_RANGE,
714 singleSatisfiableRange.toHeaderRangeString(content_length));
715 resource.writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
716 return;
717 }
718
719
720
721
722
723
724 writeHeaders(response,content,-1);
725 String mimetype=content.getContentType().toString();
726 MultiPartOutputStream multi = new MultiPartOutputStream(out);
727 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
728
729
730
731
732 String ctp;
733 if (request.getHeader(HttpHeaders.REQUEST_RANGE)!=null)
734 ctp = "multipart/x-byteranges; boundary=";
735 else
736 ctp = "multipart/byteranges; boundary=";
737 response.setContentType(ctp+multi.getBoundary());
738
739 InputStream in=resource.getInputStream();
740 long pos=0;
741
742 for (int i=0;i<ranges.size();i++)
743 {
744 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
745 String header=HttpHeaders.CONTENT_RANGE+": "+
746 ibr.toHeaderRangeString(content_length);
747 multi.startPart(mimetype,new String[]{header});
748
749 long start=ibr.getFirst(content_length);
750 long size=ibr.getSize(content_length);
751 if (in!=null)
752 {
753
754 if (start<pos)
755 {
756 in.close();
757 in=resource.getInputStream();
758 pos=0;
759 }
760 if (pos<start)
761 {
762 in.skip(start-pos);
763 pos=start;
764 }
765 IO.copy(in,multi,size);
766 pos+=size;
767 }
768 else
769
770 (resource).writeTo(multi,start,size);
771
772 }
773 if (in!=null)
774 in.close();
775 multi.close();
776 }
777 return;
778 }
779
780
781 protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
782 throws IOException
783 {
784 if (content.getContentType()!=null && response.getContentType()==null)
785 response.setContentType(content.getContentType().toString());
786
787 if (response instanceof Response)
788 {
789 Response r=(Response)response;
790 HttpFields fields = r.getHttpFields();
791
792 if (content.getLastModified()!=null)
793 fields.put(HttpHeaders.LAST_MODIFIED_BUFFER,content.getLastModified(),content.getResource().lastModified());
794 else if (content.getResource()!=null)
795 {
796 long lml=content.getResource().lastModified();
797 if (lml!=-1)
798 fields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
799 }
800
801 if (count != -1)
802 r.setLongContentLength(count);
803
804 writeOptionHeaders(fields);
805 }
806 else
807 {
808 long lml=content.getResource().lastModified();
809 if (lml>=0)
810 response.setDateHeader(HttpHeaders.LAST_MODIFIED,lml);
811
812 if (count != -1)
813 {
814 if (count<Integer.MAX_VALUE)
815 response.setContentLength((int)count);
816 else
817 response.setHeader(HttpHeaders.CONTENT_LENGTH,TypeUtil.toString(count));
818 }
819
820 writeOptionHeaders(response);
821 }
822 }
823
824
825 protected void writeOptionHeaders(HttpFields fields) throws IOException
826 {
827 if (_acceptRanges)
828 fields.put(HttpHeaders.ACCEPT_RANGES_BUFFER,HttpHeaderValues.BYTES_BUFFER);
829
830 if (_cacheControl!=null)
831 fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl);
832 }
833
834
835 protected void writeOptionHeaders(HttpServletResponse response) throws IOException
836 {
837 if (_acceptRanges)
838 response.setHeader(HttpHeaders.ACCEPT_RANGES,"bytes");
839
840 if (_cacheControl!=null)
841 response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString());
842 }
843
844
845
846
847
848 public void destroy()
849 {
850 try
851 {
852 if (_nioCache!=null)
853 _nioCache.stop();
854 if (_bioCache!=null)
855 _bioCache.stop();
856 }
857 catch(Exception e)
858 {
859 Log.warn(Log.EXCEPTION,e);
860 }
861 finally
862 {
863 super.destroy();
864 }
865 }
866
867
868
869
870 private class UnCachedContent implements HttpContent
871 {
872 Resource _resource;
873
874 UnCachedContent(Resource resource)
875 {
876 _resource=resource;
877 }
878
879
880 public Buffer getContentType()
881 {
882 return _mimeTypes.getMimeByExtension(_resource.toString());
883 }
884
885
886 public Buffer getLastModified()
887 {
888 return null;
889 }
890
891
892 public Buffer getBuffer()
893 {
894 return null;
895 }
896
897
898 public long getContentLength()
899 {
900 return _resource.length();
901 }
902
903
904 public InputStream getInputStream() throws IOException
905 {
906 return _resource.getInputStream();
907 }
908
909
910 public Resource getResource()
911 {
912 return _resource;
913 }
914
915
916 public void release()
917 {
918 _resource.release();
919 _resource=null;
920 }
921
922 }
923 }