Mercurial Hosting > luan
comparison src/org/eclipse/jetty/util/resource/Resource.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children | 8e9db0bbf4f9 |
comparison
equal
deleted
inserted
replaced
801:6a21393191c1 | 802:3428c60d7cfc |
---|---|
1 // | |
2 // ======================================================================== | |
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. | |
4 // ------------------------------------------------------------------------ | |
5 // All rights reserved. This program and the accompanying materials | |
6 // are made available under the terms of the Eclipse Public License v1.0 | |
7 // and Apache License v2.0 which accompanies this distribution. | |
8 // | |
9 // The Eclipse Public License is available at | |
10 // http://www.eclipse.org/legal/epl-v10.html | |
11 // | |
12 // The Apache License v2.0 is available at | |
13 // http://www.opensource.org/licenses/apache2.0.php | |
14 // | |
15 // You may elect to redistribute this code under either of these licenses. | |
16 // ======================================================================== | |
17 // | |
18 | |
19 package org.eclipse.jetty.util.resource; | |
20 | |
21 import java.io.File; | |
22 import java.io.FileOutputStream; | |
23 import java.io.IOException; | |
24 import java.io.InputStream; | |
25 import java.io.OutputStream; | |
26 import java.net.MalformedURLException; | |
27 import java.net.URI; | |
28 import java.net.URL; | |
29 import java.net.URLConnection; | |
30 import java.text.DateFormat; | |
31 import java.util.Arrays; | |
32 import java.util.Date; | |
33 | |
34 import org.eclipse.jetty.util.B64Code; | |
35 import org.eclipse.jetty.util.IO; | |
36 import org.eclipse.jetty.util.Loader; | |
37 import org.eclipse.jetty.util.StringUtil; | |
38 import org.eclipse.jetty.util.URIUtil; | |
39 import org.eclipse.jetty.util.log.Log; | |
40 import org.eclipse.jetty.util.log.Logger; | |
41 | |
42 | |
43 /* ------------------------------------------------------------ */ | |
44 /** | |
45 * Abstract resource class. | |
46 */ | |
47 public abstract class Resource implements ResourceFactory | |
48 { | |
49 private static final Logger LOG = Log.getLogger(Resource.class); | |
50 public static boolean __defaultUseCaches = true; | |
51 volatile Object _associate; | |
52 | |
53 /* ------------------------------------------------------------ */ | |
54 /** | |
55 * Change the default setting for url connection caches. | |
56 * Subsequent URLConnections will use this default. | |
57 * @param useCaches | |
58 */ | |
59 public static void setDefaultUseCaches (boolean useCaches) | |
60 { | |
61 __defaultUseCaches=useCaches; | |
62 } | |
63 | |
64 /* ------------------------------------------------------------ */ | |
65 public static boolean getDefaultUseCaches () | |
66 { | |
67 return __defaultUseCaches; | |
68 } | |
69 | |
70 /* ------------------------------------------------------------ */ | |
71 /** Construct a resource from a uri. | |
72 * @param uri A URI. | |
73 * @return A Resource object. | |
74 * @throws IOException Problem accessing URI | |
75 */ | |
76 public static Resource newResource(URI uri) | |
77 throws IOException | |
78 { | |
79 return newResource(uri.toURL()); | |
80 } | |
81 | |
82 /* ------------------------------------------------------------ */ | |
83 /** Construct a resource from a url. | |
84 * @param url A URL. | |
85 * @return A Resource object. | |
86 * @throws IOException Problem accessing URL | |
87 */ | |
88 public static Resource newResource(URL url) | |
89 throws IOException | |
90 { | |
91 return newResource(url, __defaultUseCaches); | |
92 } | |
93 | |
94 /* ------------------------------------------------------------ */ | |
95 /** | |
96 * Construct a resource from a url. | |
97 * @param url the url for which to make the resource | |
98 * @param useCaches true enables URLConnection caching if applicable to the type of resource | |
99 * @return | |
100 */ | |
101 static Resource newResource(URL url, boolean useCaches) | |
102 { | |
103 if (url==null) | |
104 return null; | |
105 | |
106 String url_string=url.toExternalForm(); | |
107 if( url_string.startsWith( "file:")) | |
108 { | |
109 try | |
110 { | |
111 FileResource fileResource= new FileResource(url); | |
112 return fileResource; | |
113 } | |
114 catch(Exception e) | |
115 { | |
116 LOG.debug(Log.EXCEPTION,e); | |
117 return new BadResource(url,e.toString()); | |
118 } | |
119 } | |
120 else if( url_string.startsWith( "jar:file:")) | |
121 { | |
122 return new JarFileResource(url, useCaches); | |
123 } | |
124 else if( url_string.startsWith( "jar:")) | |
125 { | |
126 return new JarResource(url, useCaches); | |
127 } | |
128 | |
129 return new URLResource(url,null,useCaches); | |
130 } | |
131 | |
132 | |
133 | |
134 /* ------------------------------------------------------------ */ | |
135 /** Construct a resource from a string. | |
136 * @param resource A URL or filename. | |
137 * @return A Resource object. | |
138 */ | |
139 public static Resource newResource(String resource) | |
140 throws MalformedURLException, IOException | |
141 { | |
142 return newResource(resource, __defaultUseCaches); | |
143 } | |
144 | |
145 /* ------------------------------------------------------------ */ | |
146 /** Construct a resource from a string. | |
147 * @param resource A URL or filename. | |
148 * @param useCaches controls URLConnection caching | |
149 * @return A Resource object. | |
150 */ | |
151 public static Resource newResource (String resource, boolean useCaches) | |
152 throws MalformedURLException, IOException | |
153 { | |
154 URL url=null; | |
155 try | |
156 { | |
157 // Try to format as a URL? | |
158 url = new URL(resource); | |
159 } | |
160 catch(MalformedURLException e) | |
161 { | |
162 if(!resource.startsWith("ftp:") && | |
163 !resource.startsWith("file:") && | |
164 !resource.startsWith("jar:")) | |
165 { | |
166 try | |
167 { | |
168 // It's a file. | |
169 if (resource.startsWith("./")) | |
170 resource=resource.substring(2); | |
171 | |
172 File file=new File(resource).getCanonicalFile(); | |
173 url=Resource.toURL(file); | |
174 | |
175 URLConnection connection=url.openConnection(); | |
176 connection.setUseCaches(useCaches); | |
177 return new FileResource(url,connection,file); | |
178 } | |
179 catch(Exception e2) | |
180 { | |
181 LOG.debug(Log.EXCEPTION,e2); | |
182 throw e; | |
183 } | |
184 } | |
185 else | |
186 { | |
187 LOG.warn("Bad Resource: "+resource); | |
188 throw e; | |
189 } | |
190 } | |
191 | |
192 return newResource(url); | |
193 } | |
194 | |
195 /* ------------------------------------------------------------ */ | |
196 public static Resource newResource (File file) | |
197 throws MalformedURLException, IOException | |
198 { | |
199 file = file.getCanonicalFile(); | |
200 URL url = Resource.toURL(file); | |
201 | |
202 URLConnection connection = url.openConnection(); | |
203 FileResource fileResource = new FileResource(url, connection, file); | |
204 return fileResource; | |
205 } | |
206 | |
207 /* ------------------------------------------------------------ */ | |
208 /** Construct a system resource from a string. | |
209 * The resource is tried as classloader resource before being | |
210 * treated as a normal resource. | |
211 * @param resource Resource as string representation | |
212 * @return The new Resource | |
213 * @throws IOException Problem accessing resource. | |
214 */ | |
215 public static Resource newSystemResource(String resource) | |
216 throws IOException | |
217 { | |
218 URL url=null; | |
219 // Try to format as a URL? | |
220 ClassLoader loader=Thread.currentThread().getContextClassLoader(); | |
221 if (loader!=null) | |
222 { | |
223 try | |
224 { | |
225 url = loader.getResource(resource); | |
226 if (url == null && resource.startsWith("/")) | |
227 url = loader.getResource(resource.substring(1)); | |
228 } | |
229 catch (IllegalArgumentException e) | |
230 { | |
231 // Catches scenario where a bad Windows path like "C:\dev" is | |
232 // improperly escaped, which various downstream classloaders | |
233 // tend to have a problem with | |
234 url = null; | |
235 } | |
236 } | |
237 if (url==null) | |
238 { | |
239 loader=Resource.class.getClassLoader(); | |
240 if (loader!=null) | |
241 { | |
242 url=loader.getResource(resource); | |
243 if (url==null && resource.startsWith("/")) | |
244 url=loader.getResource(resource.substring(1)); | |
245 } | |
246 } | |
247 | |
248 if (url==null) | |
249 { | |
250 url=ClassLoader.getSystemResource(resource); | |
251 if (url==null && resource.startsWith("/")) | |
252 url=ClassLoader.getSystemResource(resource.substring(1)); | |
253 } | |
254 | |
255 if (url==null) | |
256 return null; | |
257 | |
258 return newResource(url); | |
259 } | |
260 | |
261 /* ------------------------------------------------------------ */ | |
262 /** Find a classpath resource. | |
263 */ | |
264 public static Resource newClassPathResource(String resource) | |
265 { | |
266 return newClassPathResource(resource,true,false); | |
267 } | |
268 | |
269 /* ------------------------------------------------------------ */ | |
270 /** Find a classpath resource. | |
271 * The {@link java.lang.Class#getResource(String)} method is used to lookup the resource. If it is not | |
272 * found, then the {@link Loader#getResource(Class, String, boolean)} method is used. | |
273 * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used. | |
274 * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources. | |
275 * @param name The relative name of the resource | |
276 * @param useCaches True if URL caches are to be used. | |
277 * @param checkParents True if forced searching of parent Classloaders is performed to work around | |
278 * loaders with inverted priorities | |
279 * @return Resource or null | |
280 */ | |
281 public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents) | |
282 { | |
283 URL url=Resource.class.getResource(name); | |
284 | |
285 if (url==null) | |
286 url=Loader.getResource(Resource.class,name,checkParents); | |
287 if (url==null) | |
288 return null; | |
289 return newResource(url,useCaches); | |
290 } | |
291 | |
292 /* ------------------------------------------------------------ */ | |
293 public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException | |
294 { | |
295 return r.isContainedIn(containingResource); | |
296 } | |
297 | |
298 /* ------------------------------------------------------------ */ | |
299 @Override | |
300 protected void finalize() | |
301 { | |
302 release(); | |
303 } | |
304 | |
305 /* ------------------------------------------------------------ */ | |
306 public abstract boolean isContainedIn (Resource r) throws MalformedURLException; | |
307 | |
308 | |
309 /* ------------------------------------------------------------ */ | |
310 /** Release any temporary resources held by the resource. | |
311 */ | |
312 public abstract void release(); | |
313 | |
314 | |
315 /* ------------------------------------------------------------ */ | |
316 /** | |
317 * Returns true if the respresened resource exists. | |
318 */ | |
319 public abstract boolean exists(); | |
320 | |
321 | |
322 /* ------------------------------------------------------------ */ | |
323 /** | |
324 * Returns true if the respresenetd resource is a container/directory. | |
325 * If the resource is not a file, resources ending with "/" are | |
326 * considered directories. | |
327 */ | |
328 public abstract boolean isDirectory(); | |
329 | |
330 /* ------------------------------------------------------------ */ | |
331 /** | |
332 * Returns the last modified time | |
333 */ | |
334 public abstract long lastModified(); | |
335 | |
336 | |
337 /* ------------------------------------------------------------ */ | |
338 /** | |
339 * Return the length of the resource | |
340 */ | |
341 public abstract long length(); | |
342 | |
343 | |
344 /* ------------------------------------------------------------ */ | |
345 /** | |
346 * Returns an URL representing the given resource | |
347 */ | |
348 public abstract URL getURL(); | |
349 | |
350 /* ------------------------------------------------------------ */ | |
351 /** | |
352 * Returns an URI representing the given resource | |
353 */ | |
354 public URI getURI() | |
355 { | |
356 try | |
357 { | |
358 return getURL().toURI(); | |
359 } | |
360 catch(Exception e) | |
361 { | |
362 throw new RuntimeException(e); | |
363 } | |
364 } | |
365 | |
366 | |
367 /* ------------------------------------------------------------ */ | |
368 /** | |
369 * Returns an File representing the given resource or NULL if this | |
370 * is not possible. | |
371 */ | |
372 public abstract File getFile() | |
373 throws IOException; | |
374 | |
375 | |
376 /* ------------------------------------------------------------ */ | |
377 /** | |
378 * Returns the name of the resource | |
379 */ | |
380 public abstract String getName(); | |
381 | |
382 | |
383 /* ------------------------------------------------------------ */ | |
384 /** | |
385 * Returns an input stream to the resource | |
386 */ | |
387 public abstract InputStream getInputStream() | |
388 throws java.io.IOException; | |
389 | |
390 /* ------------------------------------------------------------ */ | |
391 /** | |
392 * Returns an output stream to the resource | |
393 */ | |
394 public abstract OutputStream getOutputStream() | |
395 throws java.io.IOException, SecurityException; | |
396 | |
397 /* ------------------------------------------------------------ */ | |
398 /** | |
399 * Deletes the given resource | |
400 */ | |
401 public abstract boolean delete() | |
402 throws SecurityException; | |
403 | |
404 /* ------------------------------------------------------------ */ | |
405 /** | |
406 * Rename the given resource | |
407 */ | |
408 public abstract boolean renameTo( Resource dest) | |
409 throws SecurityException; | |
410 | |
411 /* ------------------------------------------------------------ */ | |
412 /** | |
413 * Returns a list of resource names contained in the given resource | |
414 * The resource names are not URL encoded. | |
415 */ | |
416 public abstract String[] list(); | |
417 | |
418 /* ------------------------------------------------------------ */ | |
419 /** | |
420 * Returns the resource contained inside the current resource with the | |
421 * given name. | |
422 * @param path The path segment to add, which should be encoded by the | |
423 * encode method. | |
424 */ | |
425 public abstract Resource addPath(String path) | |
426 throws IOException,MalformedURLException; | |
427 | |
428 /* ------------------------------------------------------------ */ | |
429 /** Get a resource from withing this resource. | |
430 * <p> | |
431 * This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions. | |
432 * This method satisfied the {@link ResourceFactory} interface. | |
433 * @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String) | |
434 */ | |
435 public Resource getResource(String path) | |
436 { | |
437 try | |
438 { | |
439 return addPath(path); | |
440 } | |
441 catch(Exception e) | |
442 { | |
443 LOG.debug(e); | |
444 return null; | |
445 } | |
446 } | |
447 | |
448 /* ------------------------------------------------------------ */ | |
449 /** Encode according to this resource type. | |
450 * The default implementation calls URI.encodePath(uri) | |
451 * @param uri | |
452 * @return String encoded for this resource type. | |
453 */ | |
454 public String encode(String uri) | |
455 { | |
456 return URIUtil.encodePath(uri); | |
457 } | |
458 | |
459 /* ------------------------------------------------------------ */ | |
460 public Object getAssociate() | |
461 { | |
462 return _associate; | |
463 } | |
464 | |
465 /* ------------------------------------------------------------ */ | |
466 public void setAssociate(Object o) | |
467 { | |
468 _associate=o; | |
469 } | |
470 | |
471 /* ------------------------------------------------------------ */ | |
472 /** | |
473 * @return The canonical Alias of this resource or null if none. | |
474 */ | |
475 public URL getAlias() | |
476 { | |
477 return null; | |
478 } | |
479 | |
480 /* ------------------------------------------------------------ */ | |
481 /** Get the resource list as a HTML directory listing. | |
482 * @param base The base URL | |
483 * @param parent True if the parent directory should be included | |
484 * @return String of HTML | |
485 */ | |
486 public String getListHTML(String base,boolean parent) | |
487 throws IOException | |
488 { | |
489 base=URIUtil.canonicalPath(base); | |
490 if (base==null || !isDirectory()) | |
491 return null; | |
492 | |
493 String[] ls = list(); | |
494 if (ls==null) | |
495 return null; | |
496 Arrays.sort(ls); | |
497 | |
498 String decodedBase = URIUtil.decodePath(base); | |
499 String title = "Directory: "+deTag(decodedBase); | |
500 | |
501 StringBuilder buf=new StringBuilder(4096); | |
502 buf.append("<HTML><HEAD>"); | |
503 buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>"); | |
504 buf.append(title); | |
505 buf.append("</TITLE></HEAD><BODY>\n<H1>"); | |
506 buf.append(title); | |
507 buf.append("</H1>\n<TABLE BORDER=0>\n"); | |
508 | |
509 if (parent) | |
510 { | |
511 buf.append("<TR><TD><A HREF=\""); | |
512 buf.append(URIUtil.addPaths(base,"../")); | |
513 buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n"); | |
514 } | |
515 | |
516 String encodedBase = hrefEncodeURI(base); | |
517 | |
518 DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM, | |
519 DateFormat.MEDIUM); | |
520 for (int i=0 ; i< ls.length ; i++) | |
521 { | |
522 Resource item = addPath(ls[i]); | |
523 | |
524 buf.append("\n<TR><TD><A HREF=\""); | |
525 String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i])); | |
526 | |
527 buf.append(path); | |
528 | |
529 if (item.isDirectory() && !path.endsWith("/")) | |
530 buf.append(URIUtil.SLASH); | |
531 | |
532 // URIUtil.encodePath(buf,path); | |
533 buf.append("\">"); | |
534 buf.append(deTag(ls[i])); | |
535 buf.append(" "); | |
536 buf.append("</A></TD><TD ALIGN=right>"); | |
537 buf.append(item.length()); | |
538 buf.append(" bytes </TD><TD>"); | |
539 buf.append(dfmt.format(new Date(item.lastModified()))); | |
540 buf.append("</TD></TR>"); | |
541 } | |
542 buf.append("</TABLE>\n"); | |
543 buf.append("</BODY></HTML>\n"); | |
544 | |
545 return buf.toString(); | |
546 } | |
547 | |
548 /** | |
549 * Encode any characters that could break the URI string in an HREF. | |
550 * Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a> | |
551 * | |
552 * The above example would parse incorrectly on various browsers as the "<" or '"' characters | |
553 * would end the href attribute value string prematurely. | |
554 * | |
555 * @param raw the raw text to encode. | |
556 * @return the defanged text. | |
557 */ | |
558 private static String hrefEncodeURI(String raw) | |
559 { | |
560 StringBuffer buf = null; | |
561 | |
562 loop: | |
563 for (int i=0;i<raw.length();i++) | |
564 { | |
565 char c=raw.charAt(i); | |
566 switch(c) | |
567 { | |
568 case '\'': | |
569 case '"': | |
570 case '<': | |
571 case '>': | |
572 buf=new StringBuffer(raw.length()<<1); | |
573 break loop; | |
574 } | |
575 } | |
576 if (buf==null) | |
577 return raw; | |
578 | |
579 for (int i=0;i<raw.length();i++) | |
580 { | |
581 char c=raw.charAt(i); | |
582 switch(c) | |
583 { | |
584 case '"': | |
585 buf.append("%22"); | |
586 continue; | |
587 case '\'': | |
588 buf.append("%27"); | |
589 continue; | |
590 case '<': | |
591 buf.append("%3C"); | |
592 continue; | |
593 case '>': | |
594 buf.append("%3E"); | |
595 continue; | |
596 default: | |
597 buf.append(c); | |
598 continue; | |
599 } | |
600 } | |
601 | |
602 return buf.toString(); | |
603 } | |
604 | |
605 private static String deTag(String raw) | |
606 { | |
607 return StringUtil.replace( StringUtil.replace(raw,"<","<"), ">", ">"); | |
608 } | |
609 | |
610 /* ------------------------------------------------------------ */ | |
611 /** | |
612 * @param out | |
613 * @param start First byte to write | |
614 * @param count Bytes to write or -1 for all of them. | |
615 */ | |
616 public void writeTo(OutputStream out,long start,long count) | |
617 throws IOException | |
618 { | |
619 InputStream in = getInputStream(); | |
620 try | |
621 { | |
622 in.skip(start); | |
623 if (count<0) | |
624 IO.copy(in,out); | |
625 else | |
626 IO.copy(in,out,count); | |
627 } | |
628 finally | |
629 { | |
630 in.close(); | |
631 } | |
632 } | |
633 | |
634 /* ------------------------------------------------------------ */ | |
635 public void copyTo(File destination) | |
636 throws IOException | |
637 { | |
638 if (destination.exists()) | |
639 throw new IllegalArgumentException(destination+" exists"); | |
640 writeTo(new FileOutputStream(destination),0,-1); | |
641 } | |
642 | |
643 /* ------------------------------------------------------------ */ | |
644 public String getWeakETag() | |
645 { | |
646 try | |
647 { | |
648 StringBuilder b = new StringBuilder(32); | |
649 b.append("W/\""); | |
650 | |
651 String name=getName(); | |
652 int length=name.length(); | |
653 long lhash=0; | |
654 for (int i=0; i<length;i++) | |
655 lhash=31*lhash+name.charAt(i); | |
656 | |
657 B64Code.encode(lastModified()^lhash,b); | |
658 B64Code.encode(length()^lhash,b); | |
659 b.append('"'); | |
660 return b.toString(); | |
661 } | |
662 catch (IOException e) | |
663 { | |
664 throw new RuntimeException(e); | |
665 } | |
666 } | |
667 | |
668 /* ------------------------------------------------------------ */ | |
669 /** Generate a properly encoded URL from a {@link File} instance. | |
670 * @param file Target file. | |
671 * @return URL of the target file. | |
672 * @throws MalformedURLException | |
673 */ | |
674 public static URL toURL(File file) throws MalformedURLException | |
675 { | |
676 return file.toURI().toURL(); | |
677 } | |
678 } |