Source code: edu/emory/mathcs/util/classloader/URIClassLoader.java
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is the Emory Utilities.
15 *
16 * The Initial Developer of the Original Code is
17 * The Distributed Computing Laboratory, Emory University.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Alternatively, the contents of this file may be used under the terms of
22 * either the GNU General Public License Version 2 or later (the "GPL"), or
23 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24 * in which case the provisions of the GPL or the LGPL are applicable instead
25 * of those above. If you wish to allow use of your version of this file only
26 * under the terms of either the GPL or the LGPL, and not to allow others to
27 * use your version of this file under the terms of the MPL, indicate your
28 * decision by deleting the provisions above and replace them with the notice
29 * and other provisions required by the GPL or the LGPL. If you do not delete
30 * the provisions above, a recipient may use your version of this file under
31 * the terms of any one of the MPL, the GPL or the LGPL.
32 *
33 * ***** END LICENSE BLOCK ***** */
34
35 package edu.emory.mathcs.util.classloader;
36
37 import java.net.*;
38 import java.util.*;
39
40 /**
41 * Equivalent of java.net.URLClassloader but without bugs related to ill-formed
42 * URLs and with customizable JAR caching policy. The standard URLClassLoader
43 * accepts URLs containing spaces and other characters which are forbidden in
44 * the URI syntax, according to the RFC 2396.
45 * As a workaround to this problem, Java escapes and un-escapes URLs in various
46 * arbitrary places; however, this is inconsistent and leads to numerous
47 * problems with URLs referring to local files with spaces in the path. SUN
48 * acknowledges the problem, see but refuses to modify the behavior for
49 * compatibility reasons; see Java Bug Parade 4273532, 4466485.
50 * <p>
51 * Additionally, the JAR caching policy used by
52 * URLClassLoader is system-wide and inflexible: once downloaded JAR files are
53 * never re-downloaded, even if one creates a fresh instance of the class
54 * loader that happens to have the same URL in its search path. In fact, that
55 * policy is a security vulnerability: it is possible to crash any URL class
56 * loader, thus affecting potentially separate part of the system, by creating
57 * URL connection to one of the URLs of that class loader search path and
58 * closing the associated JAR file.
59 * See Java Bug Parade 4405789, 4388666, 4639900.
60 * <p>
61 * This class avoids these problems by 1) using URIs instead of URLs for the
62 * search path (thus enforcing strict syntax conformance and defining precise
63 * escaping semantics), and 2) using custom URLStreamHandler which ensures
64 * per-classloader JAR caching policy.
65 *
66 * @author Dawid Kurzyniec
67 * @version 1.0
68 *
69 * @see File#toURL
70 * @see File#toURI
71 */
72 public class URIClassLoader extends GenericClassLoader {
73
74 /**
75 * Creates URIClassLoader with the specified search path.
76 * @param uris the search path
77 */
78 public URIClassLoader(URI[] uris) {
79 super(new URIResourceFinder(uris));
80 }
81
82 /**
83 * Creates URIClassLoader with the specified search path and parent class
84 * loader.
85 * @param uris the search path
86 * @param parent the parent class loader.
87 */
88 public URIClassLoader(URI[] uris, ClassLoader parent) {
89 super(new URIResourceFinder(uris), parent);
90 }
91
92 /**
93 * Add specified URI at the end of the search path.
94 * @param uri the URI to add
95 */
96 public void addURI(URI uri) {
97 ((URIResourceFinder)finder).addURI(uri);
98 }
99
100 private static class URIResourceFinder implements ResourceFinder {
101 URL[] urls;
102 final ResourceLoader loader = new ResourceLoader();
103 public URIResourceFinder(URI[] uris) {
104 try {
105 URL[] urls = new URL[uris.length];
106 for (int i = 0; i < uris.length; i++) {
107 urls[i] = uris[i].toURL();
108 }
109 this.urls = urls;
110 }
111 catch (MalformedURLException e) {
112 throw new IllegalArgumentException(e.getMessage());
113 }
114 }
115 public synchronized void addURI(URI uri) {
116 try {
117 URL url = uri.toURL();
118 int len = this.urls.length;
119 URL[] urls = new URL[len + 1];
120 System.arraycopy(this.urls, 0, urls, 0, len);
121 urls[len] = url;
122 this.urls = urls;
123 }
124 catch (MalformedURLException e) {
125 throw new IllegalArgumentException(e.getMessage());
126 }
127 }
128 private synchronized final URL[] getUrls() {
129 return urls;
130 }
131 public ResourceHandle getResource(String name) {
132 return loader.getResource(getUrls(), name);
133 }
134 public Enumeration getResources(String name) {
135 return loader.getResources(getUrls(), name);
136 }
137 public URL findResource(String name) {
138 return loader.findResource(getUrls(), name);
139 }
140 public Enumeration findResources(String name) {
141 return loader.findResources(getUrls(), name);
142 }
143 }
144 }