1 |
/* URLConnection.java -- Class loader that loads classes from one or more URLs |
/* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs |
2 |
Copyright (C) 1999, 2001 Free Software Foundation, Inc. |
Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
38 |
package java.net; |
package java.net; |
39 |
|
|
40 |
import java.io.ByteArrayOutputStream; |
import java.io.ByteArrayOutputStream; |
41 |
import java.io.InputStream; |
import java.io.EOFException; |
|
import java.io.IOException; |
|
42 |
import java.io.File; |
import java.io.File; |
43 |
|
import java.io.FileInputStream; |
44 |
|
import java.io.FileNotFoundException; |
45 |
|
import java.io.FilterInputStream; |
46 |
import java.io.FilePermission; |
import java.io.FilePermission; |
47 |
|
import java.io.InputStream; |
48 |
|
import java.io.IOException; |
49 |
|
import java.security.AccessController; |
50 |
|
import java.security.AccessControlContext; |
51 |
import java.security.CodeSource; |
import java.security.CodeSource; |
52 |
import java.security.SecureClassLoader; |
import java.security.SecureClassLoader; |
53 |
|
import java.security.PrivilegedAction; |
54 |
import java.security.PermissionCollection; |
import java.security.PermissionCollection; |
55 |
import java.security.cert.Certificate; |
import java.security.cert.Certificate; |
56 |
import java.util.Enumeration; |
import java.util.Enumeration; |
57 |
import java.util.Vector; |
import java.util.Vector; |
58 |
|
import java.util.HashMap; |
59 |
import java.util.jar.Attributes; |
import java.util.jar.Attributes; |
60 |
|
import java.util.jar.JarEntry; |
61 |
|
import java.util.jar.JarFile; |
62 |
import java.util.jar.Manifest; |
import java.util.jar.Manifest; |
63 |
|
import java.util.zip.ZipException; |
64 |
|
import gnu.java.io.PlatformHelper; |
65 |
|
import gnu.java.net.protocol.jar.JarURLConnection; |
66 |
|
|
67 |
/** |
/** |
68 |
* A secure class loader that can load classes and resources from multiple |
* A secure class loader that can load classes and resources from |
69 |
* locations. Given an array of URLs this class loader will retrieve classes |
* multiple locations. Given an array of <code>URL</code>s this class |
70 |
* and resources by fetching them from possible remote locations. Each URL is |
* loader will retrieve classes and resources by fetching them from |
71 |
* searched in order in which it was added. If the file portion of the URL ends |
* possible remote locations. Each <code>URL</code> is searched in |
72 |
* with a / character then it is interpreted as a base directory, otherwise it |
* order in which it was added. If the file portion of the |
73 |
* is interpreted as a jar file from which the classes/resources are resolved. |
* <code>URL</code> ends with a '/' character then it is interpreted |
74 |
* <p> |
* as a base directory, otherwise it is interpreted as a jar file from |
75 |
* New instances can be created by two static newInstance() methods or by |
* which the classes/resources are resolved. |
76 |
* three public contructors. Both give the option to supply an initial array |
* |
77 |
* of URLs and (optionally) a parent classloader (that is different from the |
* <p>New instances can be created by two static |
78 |
* standard system class loader). |
* <code>newInstance()</code> methods or by three public |
79 |
* Creating a <code>URLClassLoader</code> can always throw a |
* contructors. Both ways give the option to supply an initial array |
80 |
* <code>SecurityException</code> if the checkes performed by the constructor of |
* of <code>URL</code>s and (optionally) a parent classloader (that is |
81 |
* <code>SecureClassLoader</code>, which are called when creating a new |
* different from the standard system class loader).</p> |
82 |
* instance, fail. |
* |
83 |
* Note that only subclasses can add new URLs after the URLClassLoader had been |
* <p>Normally creating a <code>URLClassLoader</code> throws a |
84 |
* created. But it is always possible to get an array of all URLs that the class |
* <code>SecurityException</code> if a <code>SecurityManager</code> is |
85 |
* loader uses to resolve classes and resources by way of the |
* installed and the <code>checkCreateClassLoader()</code> method does |
86 |
* <code>getURLs()</code> method. |
* not return true. But the <code>newInstance()</code> methods may be |
87 |
* <p> |
* used by any code as long as it has permission to acces the given |
88 |
* XXX - TODO - FIXME This implementation is not finished or tested!<br> |
* <code>URL</code>s. <code>URLClassLoaders</code> created by the |
89 |
* Open issues: |
* <code>newInstance()</code> methods also explicitly call the |
90 |
|
* <code>checkPackageAccess()</code> method of |
91 |
|
* <code>SecurityManager</code> if one is installed before trying to |
92 |
|
* load a class. Note that only subclasses of |
93 |
|
* <code>URLClassLoader</code> can add new URLs after the |
94 |
|
* URLClassLoader had been created. But it is always possible to get |
95 |
|
* an array of all URLs that the class loader uses to resolve classes |
96 |
|
* and resources by way of the <code>getURLs()</code> method.</p> |
97 |
|
* |
98 |
|
* <p>Open issues: |
99 |
* <ul> |
* <ul> |
100 |
* <li>Should the URLClassLoader actually add the locations found in the |
* |
101 |
* manifest or is this the responsibility of some other loader/(sub)class? |
* <li>Should the URLClassLoader actually add the locations found in |
102 |
* (see <a href="http://java.sun.com/products/jdk/1.3/docs/guide/extensions/spec.html#bundled">Extension Machanism Architecture - Bundles Extensions</a>) |
* the manifest or is this the responsibility of some other |
103 |
* <li> How does <code>definePackage()</code> and sealing work precisely? |
* loader/(sub)class? (see <a |
104 |
* (Note that the method is currently not even implemented in |
* href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html"> |
105 |
* <code>java.lang.ClassLoader</code>.) |
* Extension Mechanism Architecture - Bundles Extensions</a>)</li> |
106 |
* <li> Should the static <code>newInstance()</code> methods always succeed? Or |
* |
107 |
* are they also restricted by the normal security checks? |
* <li>How does <code>definePackage()</code> and sealing work |
108 |
* <li> We might want to do some caching of URLs, Connections or JarFiles. |
* precisely?</li> |
109 |
* For now we just assume that the (Jar)URLConnection takes care of this. |
* |
110 |
* <li> Is the way we create the "jar" URLs correct? What if it was already a |
* <li>We save and use the security context (when a created by |
111 |
* "jar" URL? |
* <code>newInstance()</code> but do we have to use it in more |
112 |
* <li> The createInstance methods should record the security context and the |
* places?</li> |
113 |
* rest of the methods should use this context to to make doPriveliged() calls. |
* |
114 |
|
* <li>The use of <code>URLStreamHandler</code>s has not been tested.</li> |
115 |
|
* |
116 |
|
* <li>Loading remotely (e.g. http) only works when the target is a jar since |
117 |
|
* the <code>FileURLLoader</code> currently only works on local directories. |
118 |
|
* </li> |
119 |
|
* |
120 |
* </ul> |
* </ul> |
121 |
|
* </p> |
122 |
* |
* |
123 |
* @since 1.2 |
* @since 1.2 |
124 |
|
* |
125 |
* @author Mark Wielaard (mark@klomp.org) |
* @author Mark Wielaard (mark@klomp.org) |
126 |
|
* @author Wu Gansha (gansha.wu@intel.com) |
127 |
*/ |
*/ |
128 |
|
|
129 |
public class URLClassLoader extends SecureClassLoader { |
public class URLClassLoader extends SecureClassLoader { |
130 |
|
|
131 |
// Variables |
// Class Variables |
132 |
|
|
133 |
/** Locations to load classes from */ |
/** |
134 |
private Vector urls = new Vector(); |
* A global cache to store mappings between URLLoader and URL, |
135 |
|
* so we can avoid do all the homework each time the same URL |
136 |
/** Factory used to get the protocol handlers of the URLs */ |
* comes. |
137 |
private URLStreamHandlerFactory factory = null; |
* XXX - Keeps these loaders forever which prevents garbage collection. |
138 |
|
*/ |
139 |
|
private static HashMap urlloaders = new HashMap(); |
140 |
|
|
141 |
|
/** |
142 |
|
* A cache to store mappings between handler factory and its |
143 |
|
* private protocol handler cache (also a HashMap), so we can avoid |
144 |
|
* create handlers each time the same protocol comes. |
145 |
|
*/ |
146 |
|
private static HashMap factoryCache = new HashMap(5); |
147 |
|
|
148 |
|
// Instance variables |
149 |
|
|
150 |
|
/** Locations to load classes from */ |
151 |
|
private final Vector urls = new Vector(); |
152 |
|
|
153 |
|
/** |
154 |
|
* Store pre-parsed information for each url into this vector |
155 |
|
* each element is a URL loader, corresponding to the URL of |
156 |
|
* the same index in "urls" |
157 |
|
*/ |
158 |
|
private final Vector urlinfos = new Vector(); |
159 |
|
|
160 |
|
/** Factory used to get the protocol handlers of the URLs */ |
161 |
|
private final URLStreamHandlerFactory factory; |
162 |
|
|
163 |
// Constructors |
/** |
164 |
|
* The security context when created from <code>newInstance()</code> |
165 |
|
* or null when created through a normal constructor or when no |
166 |
|
* <code>SecurityManager</code> was installed. |
167 |
|
*/ |
168 |
|
private final AccessControlContext securityContext; |
169 |
|
|
170 |
|
// Helper classes |
171 |
|
|
172 |
|
/** |
173 |
|
* A <code>URLLoader</code> contains all logic to load resources from a |
174 |
|
* given base <code>URL</code>. |
175 |
|
*/ |
176 |
|
static abstract class URLLoader |
177 |
|
{ |
178 |
/** |
/** |
179 |
* Creates a URLClassLoader that gets classes from the supplied URLs. |
* Our classloader to get info from if needed. |
|
* To determine if this classloader may be created the constructor of |
|
|
* the super class (<CODE>SecureClassLoader</CODE>) is called first, which |
|
|
* can throw a SecurityException. Then the supplied URLs are added |
|
|
* in the order given to the URLClassLoader which uses these URLs to |
|
|
* load classes and resources (after using the default parent ClassLoader). |
|
|
* @exception SecurityException if the SecurityManager disallows the |
|
|
* creation of a ClassLoader. |
|
|
* @param urls Locations that should be searched by this ClassLoader when |
|
|
* resolving Classes or Resources. |
|
|
* @see SecureClassLoader |
|
180 |
*/ |
*/ |
181 |
public URLClassLoader(URL[] urls) throws SecurityException { |
final URLClassLoader classloader; |
|
super(); |
|
|
addURLs(urls); |
|
|
} |
|
182 |
|
|
183 |
/** |
/** |
184 |
* Creates a URLClassLoader that gets classes from the supplied URLs. |
* The base URL from which all resources are loaded. |
|
* To determine if this classloader may be created the constructor of |
|
|
* the super class (<CODE>SecureClassLoader</CODE>) is called first, which |
|
|
* can throw a SecurityException. Then the supplied URLs are added |
|
|
* in the order given to the URLClassLoader which uses these URLs to |
|
|
* load classes and resources (after using the supplied parent ClassLoader). |
|
|
* @exception SecurityException if the SecurityManager disallows the |
|
|
* creation of a ClassLoader. |
|
|
* @exception SecurityException |
|
|
* @param urls Locations that should be searched by this ClassLoader when |
|
|
* resolving Classes or Resources. |
|
|
* @param parent The parent class loader used before trying this class |
|
|
* loader. |
|
|
* @see SecureClassLoader |
|
185 |
*/ |
*/ |
186 |
public URLClassLoader(URL[] urls, ClassLoader parent) throws |
final URL baseURL; |
|
SecurityException { |
|
|
super(parent); |
|
|
addURLs(urls); |
|
|
} |
|
187 |
|
|
188 |
/** |
/** |
189 |
* Creates a URLClassLoader that gets classes from the supplied URLs. |
* A <code>CodeSource</code> without any associated certificates. |
190 |
* To determine if this classloader may be created the constructor of |
* It is common for classes to not have certificates associated |
191 |
* the super class (<CODE>SecureClassLoader</CODE>) is called first, which |
* with them. If they come from the same <code>URLLoader</code> |
192 |
* can throw a SecurityException. Then the supplied URLs are added |
* then it is safe to share the associated <code>CodeSource</code> |
193 |
* in the order given to the URLClassLoader which uses these URLs to |
* between them since <code>CodeSource</code> is immutable. |
|
* load classes and resources (after using the supplied parent ClassLoader). |
|
|
* It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the |
|
|
* protocol handlers of the supplied URLs. |
|
|
* @exception SecurityException if the SecurityManager disallows the |
|
|
* creation of a ClassLoader. |
|
|
* @exception SecurityException |
|
|
* @param urls Locations that should be searched by this ClassLoader when |
|
|
* resolving Classes or Resources. |
|
|
* @param parent The parent class loader used before trying this class |
|
|
* loader. |
|
|
* @param factory Used to get the protocol handler for the URLs. |
|
|
* @see SecureClassLoader |
|
194 |
*/ |
*/ |
195 |
public URLClassLoader(URL[] urls, |
final CodeSource noCertCodeSource; |
|
ClassLoader parent, |
|
|
URLStreamHandlerFactory factory) throws |
|
|
SecurityException { |
|
|
super(parent); |
|
|
addURLs(urls); |
|
|
this.factory = factory; |
|
|
} |
|
196 |
|
|
197 |
// Methods |
URLLoader(URLClassLoader classloader, URL baseURL) |
198 |
|
{ |
199 |
/** |
this.classloader = classloader; |
200 |
* Adds a new location to the end of the internal URL store. |
this.baseURL = baseURL; |
201 |
* @param newUrl the location to add |
this.noCertCodeSource = new CodeSource(baseURL, null); |
|
*/ |
|
|
protected void addURL(URL newUrl) { |
|
|
urls.add(newUrl); |
|
202 |
} |
} |
203 |
|
|
204 |
/** |
/** |
205 |
* Adds an array of new locations to the end of the internal URL store. |
* Returns a <code>Resource</code> loaded by this |
206 |
* @param newUrls the locations to add |
* <code>URLLoader</code>, or <code>null</code> when no |
207 |
*/ |
* <code>Resource</code> with the given name exists. |
|
private void addURLs(URL[] newUrls) { |
|
|
for (int i = 0; i < newUrls.length; i++) { |
|
|
addURL(newUrls[i]); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Defines a Package based on the given name and the supplied manifest |
|
|
* information. The manifest indicates the tile, version and |
|
|
* vendor information of the specification and implementation and wheter the |
|
|
* package is sealed. If the Manifest indicates that the package is sealed |
|
|
* then the Package will be sealed with respect to the supplied URL. |
|
|
* |
|
|
* @exception IllegalArgumentException If this package name already exists |
|
|
* in this class loader |
|
|
* @param name The name of the package |
|
|
* @param manifest The manifest describing the specification, |
|
|
* implementation and sealing details of the package |
|
|
* @param url the code source url to seal the package |
|
|
* @return the defined Package |
|
208 |
*/ |
*/ |
209 |
protected Package definePackage(String name, Manifest manifest, URL url) |
abstract Resource getResource(String s); |
|
throws IllegalArgumentException { |
|
|
Attributes attr = manifest.getMainAttributes(); |
|
|
String specTitle = |
|
|
attr.getValue(Attributes.Name.SPECIFICATION_TITLE); |
|
|
String specVersion = |
|
|
attr.getValue(Attributes.Name.SPECIFICATION_VERSION); |
|
|
String specVendor = |
|
|
attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); |
|
|
String implTitle = |
|
|
attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); |
|
|
String implVersion = |
|
|
attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); |
|
|
String implVendor = |
|
|
attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); |
|
|
|
|
|
// Look if the Manifest indicates that this package is sealed |
|
|
// XXX - most likely not completely correct! |
|
|
// Shouldn't we also check the sealed attribute of the complete jar? |
|
|
// http://java.sun.com/products/jdk/1.3/docs/guide/extensions/spec.html#bundled |
|
|
// But how do we get that jar manifest here? |
|
|
String sealed = attr.getValue(Attributes.Name.SEALED); |
|
|
if ("false".equals(sealed)) { |
|
|
// make sure that the URL is null so the package is not sealed |
|
|
url = null; |
|
|
} |
|
|
|
|
|
return definePackage(name, specTitle, specVersion, specVendor, |
|
|
implTitle, implVersion, implVendor, url); |
|
|
} |
|
210 |
|
|
211 |
/** |
/** |
212 |
* Finds (the first) class by name from one of the locations. The locations |
* Returns the <code>Manifest</code> associated with the |
213 |
* are searched in the order they were added to the URLClassLoader. |
* <code>Resource</code>s loaded by this <code>URLLoader</code> or |
214 |
* @param className the classname to find |
* <code>null</code> there is no such <code>Manifest</code>. |
|
* @exception ClassNotFoundException when the class could not be found or |
|
|
* loaded |
|
|
* @return a Class object representing the found class |
|
215 |
*/ |
*/ |
216 |
protected Class findClass(String className) throws ClassNotFoundException { |
abstract Manifest getManifest(); |
217 |
// Just try to find the resource by the (almost) same name |
} |
218 |
String resourceName = className.replace('.', '/') + ".class"; |
|
219 |
URL url = findResource(resourceName); |
/** |
220 |
if (url == null) { |
* A <code>Resource</code> represents a resource in some |
221 |
throw new ClassNotFoundException(className); |
* <code>URLLoader</code>. It also contains all information (e.g., |
222 |
} |
* <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and |
223 |
|
* <code>InputStream</code>) that is necessary for loading resources |
224 |
// Try to read the class data, create the CodeSource and construct the |
* and creating classes from a <code>URL</code>. |
225 |
// class (and watch out for those nasty IOExceptions) |
*/ |
226 |
try { |
static abstract class Resource |
227 |
byte [] classData; |
{ |
228 |
URLConnection conn = url.openConnection(); |
final URLLoader loader; |
229 |
InputStream in = conn.getInputStream(); |
final String name; |
|
int length = conn.getContentLength(); |
|
|
if (length != -1) { |
|
|
// We know the length of the data, just read it all in at once |
|
|
classData = new byte[length]; |
|
|
in.read(classData); |
|
|
} else { |
|
|
// We don't know the data length so we have to read it in chunks |
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream(1024); |
|
|
byte b[] = new byte[1024]; |
|
|
int l = 0; |
|
|
while (l != -1) { |
|
|
l = in.read(b); |
|
|
if (l != -1) { |
|
|
out.write(b, 0, l); |
|
|
} |
|
|
} |
|
|
classData = out.toByteArray(); |
|
|
} |
|
|
|
|
|
// Now construct the CodeSource (if loaded from a jar file) |
|
|
CodeSource source = null; |
|
|
if (url.getProtocol().equals("jar")) { |
|
|
Certificate[] certificates = |
|
|
((JarURLConnection)conn).getCertificates(); |
|
|
String u = url.toExternalForm (); |
|
|
u = u.substring (4); //skip "jar:" |
|
|
int i = u.indexOf ('!'); |
|
|
if ( i >= 0) |
|
|
u = u.substring (0, i); |
|
|
url = new URL (u); |
|
|
|
|
|
source = new CodeSource(url, certificates); |
|
|
} |
|
|
else if (url.getProtocol().equals("file")) |
|
|
{ |
|
|
try |
|
|
{ |
|
|
String u = url.toExternalForm(); |
|
|
// Skip "file:" and then get canonical directory name. |
|
|
File f = new File(u.substring(5)); |
|
|
f = f.getCanonicalFile(); |
|
|
url = new URL("file", "", f.getParent()); |
|
|
source = new CodeSource (url, null); |
|
|
} |
|
|
catch (IOException ignore) |
|
|
{ |
|
|
} |
|
|
} |
|
230 |
|
|
231 |
// And finally construct the class! |
Resource(URLLoader loader, String name) |
232 |
return defineClass(className, classData, |
{ |
233 |
0, classData.length, |
this.loader = loader; |
234 |
source); |
this.name = name; |
|
} catch (IOException ioe) { |
|
|
throw new ClassNotFoundException(className + ": " + ioe); |
|
|
} |
|
235 |
} |
} |
236 |
|
|
237 |
/** |
/** |
238 |
* Finds the first occurrence of a resource that can be found. The locations |
* Returns the non-null <code>CodeSource</code> associated with |
239 |
* are searched in the order they were added to the URLClassLoader. |
* this resource. |
|
* @param resourceName the resource name to look for |
|
|
* @return the URL if found, null otherwise |
|
240 |
*/ |
*/ |
241 |
public URL findResource(String resourceName) { |
CodeSource getCodeSource() |
242 |
Enumeration e = urls.elements(); |
{ |
243 |
while (e.hasMoreElements()) { |
Certificate[] certs = getCertificates(); |
244 |
URL url = findResource(resourceName, (URL)e.nextElement()); |
if (certs != null) |
245 |
if (url != null) { |
return loader.noCertCodeSource; |
246 |
// Found the resource |
else |
247 |
return url; |
return new CodeSource(loader.baseURL, certs); |
|
} |
|
|
} |
|
|
// Resource not found |
|
|
return null; |
|
248 |
} |
} |
249 |
|
|
250 |
/** |
/** |
251 |
* Find a resource relative to a base URL. If the base URL ends with a / |
* Returns <code>Certificates</code> associated with this |
252 |
* character then the base URL is interpreted as a directory and the |
* resource, or null when there are none. |
|
* resource name is appended to it, otherwise the base URL is interpreted |
|
|
* as a jar file and a "jar" URL is constructed from the base URL and the |
|
|
* supplied resource name. This method tries to open a connection to the |
|
|
* resulting URL to make sure the resource is actually there. |
|
|
* <p> |
|
|
* XXX - We might want to do some caching of URLs, Connections or JarFiles. |
|
|
* For now we just assume that the (Jar)URLConnection takes care of this. |
|
|
* XXX - Should we also disconnect/close the URLConnection again? And how |
|
|
* would we do that? For now just assume the connection will actually be |
|
|
* used. |
|
|
* @param resourceName the resource name to look for |
|
|
* @param url the base URL |
|
|
* @return the URL if found, null otherwise |
|
|
* @see JarURLConnection |
|
253 |
*/ |
*/ |
254 |
private URL findResource(String resourceName, URL url) { |
abstract Certificate[] getCertificates(); |
|
URL resourceURL; |
|
|
// Get the file portion of the base URL |
|
|
String file = url.getFile(); |
|
|
|
|
|
// Construct the resourceURL |
|
|
if (!file.endsWith(".zip") && !file.endsWith(".jar")) |
|
|
{ |
|
|
// Interpret it as a directory and just append the resource name |
|
|
try { |
|
|
resourceURL = new URL(url, resourceName, |
|
|
createURLStreamHandler(url.getProtocol())); |
|
|
} catch (MalformedURLException e) { |
|
|
return null; |
|
|
} |
|
|
} else { |
|
|
// Interpret it as a jar file and construct a "jar" URL |
|
|
String external = url.toExternalForm(); |
|
|
try { |
|
|
// XXX - Is this correct? Why is there no URL constructor that |
|
|
// takes just a string and a URLStreamHandler? |
|
|
resourceURL = new URL(new URL("jar:" + external + "!/"), |
|
|
resourceName, |
|
|
createURLStreamHandler("jar")); |
|
|
} catch (MalformedURLException e) { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
// Check if the resource is actually at that location |
|
|
// we MUST open the connection to ensure the location gets checked |
|
|
try { |
|
|
URLConnection u = resourceURL.openConnection(); |
|
|
u.connect(); |
|
|
} catch (IOException ioe) { |
|
|
return null; |
|
|
} |
|
|
|
|
|
return resourceURL; |
|
|
} |
|
255 |
|
|
256 |
/** |
/** |
257 |
* If the URLStreamHandlerFactory has been set this return the appropriate |
* Return a <code>URL</code> that can be used to access this resource. |
|
* URLStreamHandler for the given protocol. |
|
|
* @param protocol the protocol for which we need a URLStreamHandler |
|
|
* @return the appropriate URLStreamHandler or null |
|
258 |
*/ |
*/ |
259 |
private URLStreamHandler createURLStreamHandler(String protocol) { |
abstract URL getURL(); |
|
if (factory != null) { |
|
|
return factory.createURLStreamHandler(protocol); |
|
|
} else { |
|
|
return null; |
|
|
} |
|
|
} |
|
260 |
|
|
261 |
/** |
/** |
262 |
* Finds all the resources with a particular name from all the locations. |
* Returns the size of this <code>Resource</code> in bytes or |
263 |
* @exception IOException when an error occurs accessing one of the |
* <code>-1</code> when unknown. |
|
* locations |
|
|
* @param resourceName the name of the resource to lookup |
|
|
* @return a (possible empty) enumeration of URLs where the resource can be |
|
|
* found |
|
264 |
*/ |
*/ |
265 |
public Enumeration findResources(String resourceName) throws IOException { |
abstract int getLength(); |
|
Vector resources = new Vector(); |
|
|
Enumeration e = urls.elements(); |
|
|
while (e.hasMoreElements()) { |
|
|
URL url = findResource(resourceName, (URL)e.nextElement()); |
|
|
if (url != null) { |
|
|
// Found another resource |
|
|
resources.add(url); |
|
|
} |
|
|
} |
|
|
return resources.elements(); |
|
|
} |
|
266 |
|
|
267 |
/** |
/** |
268 |
* Returns the permissions needed to access a particular code source. |
* Returns the non-null <code>InputStream</code> through which |
269 |
* These permissions includes those returned by |
* this resource can be loaded. |
|
* <CODE>SecureClassLoader.getPermissions</CODE> and the actual permissions |
|
|
* to access the objects referenced by the URL of the code source. |
|
|
* The extra permissions added depend on the protocol and file portion of |
|
|
* the URL in the code source. If the URL has the "file" protocol ends with |
|
|
* a / character then it must be a directory and a file Permission to read |
|
|
* everthing in that directory and all subdirectories is added. If the URL |
|
|
* had the "file" protocol and doesn't end with a / character then it must |
|
|
* be a normal file and a file permission to read that file is added. If the |
|
|
* URL has any other protocol then a socket permission to connect and accept |
|
|
* connections from the host portion of the URL is added. |
|
|
* @param source The codesource that needs the permissions to be accessed |
|
|
* @return the collection of permissions needed to access the code resource |
|
|
* @see SecureClassLoader.getPermissions() |
|
270 |
*/ |
*/ |
271 |
protected PermissionCollection getPermissions(CodeSource source) { |
abstract InputStream getInputStream() throws IOException; |
272 |
// XXX - This implementation does exactly as the Javadoc describes. |
} |
|
// But maybe we should/could use URLConnection.getPermissions()? |
|
273 |
|
|
274 |
// First get the permissions that would normally be granted |
/** |
275 |
PermissionCollection permissions = super.getPermissions(source); |
* A <code>JarURLLoader</code> is a type of <code>URLLoader</code> |
276 |
|
* only loading from jar url. |
277 |
// Now add the any extra permissions depending on the URL location |
*/ |
278 |
URL url = source.getLocation(); |
final static class JarURLLoader extends URLLoader |
279 |
String protocol = url.getProtocol(); |
{ |
280 |
if (protocol.equals("file")) { |
final JarFile jarfile; // The canonical jar file for this url |
281 |
String file = url.getFile(); |
final String url_prefix; // url prefix for all resources in this jar url |
|
// If the file end in / it must be an directory |
|
|
if (file.endsWith("/")) { |
|
|
// Grant permission to read everything in that directory and |
|
|
// all subdirectories |
|
|
permissions.add(new FilePermission(file + "-", "read")); |
|
|
} else { // It is a 'normal' file |
|
|
// Grant permission to access that file |
|
|
permissions.add(new FilePermission(file, "read")); |
|
|
} |
|
|
} else { |
|
|
// Grant permission to connect to and accept connections from host |
|
|
String host = url.getHost(); |
|
|
permissions.add(new SocketPermission(host, "connect,accept")); |
|
|
} |
|
282 |
|
|
283 |
return permissions; |
public JarURLLoader(URLClassLoader classloader, URL baseURL) |
284 |
|
{ |
285 |
|
super(classloader, baseURL); |
286 |
|
|
287 |
|
// cache url prefix for all resources in this jar url |
288 |
|
StringBuffer sb = new StringBuffer(PlatformHelper.INITIAL_MAX_PATH); |
289 |
|
sb.append("jar:"); |
290 |
|
sb.append(baseURL.toExternalForm()); |
291 |
|
sb.append("!/"); |
292 |
|
this.url_prefix = sb.toString(); |
293 |
|
JarFile jarfile; |
294 |
|
try |
295 |
|
{ |
296 |
|
jarfile = JarURLConnection.JarFileCache.get(baseURL); |
297 |
|
} |
298 |
|
catch (IOException ioe) |
299 |
|
{ |
300 |
|
jarfile = null; |
301 |
|
} |
302 |
|
this.jarfile = jarfile; |
303 |
} |
} |
304 |
|
|
305 |
/** |
/** get resource with the name "name" in the jar url */ |
306 |
* Returns all the locations that this class loader currently uses the |
Resource getResource(String name) |
307 |
* resolve classes and resource. This includes both the initially supplied |
{ |
308 |
* URLs as any URLs added later by the loader. |
if (jarfile == null) |
309 |
* @return All the currently used URLs |
return null; |
310 |
*/ |
|
311 |
public URL[] getURLs() { |
JarEntry je = jarfile.getJarEntry(name); |
312 |
return (URL[]) urls.toArray(new URL[urls.size()]); |
if(je != null) |
313 |
|
return new JarURLResource(this, name, je); |
314 |
|
else |
315 |
|
return null; |
316 |
|
} |
317 |
|
|
318 |
|
Manifest getManifest() |
319 |
|
{ |
320 |
|
return (jarfile == null) ? null : jarfile.getManifest(); |
321 |
|
} |
322 |
|
|
323 |
|
} |
324 |
|
|
325 |
|
final static class JarURLResource extends Resource |
326 |
|
{ |
327 |
|
private final JarEntry entry; |
328 |
|
|
329 |
|
JarURLResource(JarURLLoader loader, String name, JarEntry entry) |
330 |
|
{ |
331 |
|
super(loader, name); |
332 |
|
this.entry = entry; |
333 |
|
} |
334 |
|
|
335 |
|
InputStream getInputStream() throws IOException |
336 |
|
{ |
337 |
|
return ((JarURLLoader)loader).jarfile.getInputStream(entry); |
338 |
|
} |
339 |
|
|
340 |
|
int getLength() |
341 |
|
{ |
342 |
|
return (int)entry.getSize(); |
343 |
|
} |
344 |
|
|
345 |
|
Certificate[] getCertificates() |
346 |
|
{ |
347 |
|
return entry.getCertificates(); |
348 |
|
} |
349 |
|
|
350 |
|
URL getURL() |
351 |
|
{ |
352 |
|
try |
353 |
|
{ |
354 |
|
StringBuffer sb = new StringBuffer(PlatformHelper.INITIAL_MAX_PATH); |
355 |
|
sb.append(((JarURLLoader)loader).url_prefix); |
356 |
|
sb.append(name); |
357 |
|
return new URL(null, sb.toString(), |
358 |
|
loader.classloader.getURLStreamHandler("jar")); |
359 |
|
} |
360 |
|
catch(MalformedURLException e) |
361 |
|
{ |
362 |
|
throw new InternalError(e.toString()); |
363 |
|
} |
364 |
|
} |
365 |
|
} |
366 |
|
|
367 |
|
/** |
368 |
|
* A <code>FileURLLoader</code> is a type of <code>URLLoader</code> |
369 |
|
* only loading from file url. |
370 |
|
*/ |
371 |
|
final static class FileURLLoader extends URLLoader |
372 |
|
{ |
373 |
|
File dir; //the canonical file for this file url |
374 |
|
|
375 |
|
FileURLLoader(URLClassLoader classloader, URL url) |
376 |
|
{ |
377 |
|
super(classloader, url); |
378 |
|
// Note that this must be a "file" protocol URL. |
379 |
|
dir = new File(url.getFile()); |
380 |
} |
} |
381 |
|
|
382 |
/** |
/** get resource with the name "name" in the file url */ |
383 |
* Creates a new instance of a URLClassLoader that gets classes from the |
Resource getResource(String name) |
384 |
* supplied URLs. This class loader will have as parent the standard |
{ |
385 |
* system class loader. |
File file = new File(dir, name); |
386 |
* @param urls the initial URLs used to resolve classes and resources |
if (file.exists() && !file.isDirectory()) |
387 |
*/ |
return new FileResource(this, name, file); |
388 |
public static URLClassLoader newInstance(URL urls[]) throws |
else |
389 |
SecurityException { |
return null; |
390 |
return new URLClassLoader(urls); |
} |
391 |
|
|
392 |
|
Manifest getManifest() |
393 |
|
{ |
394 |
|
// Manifest not supported for FileURLLoader. |
395 |
|
return null; |
396 |
|
} |
397 |
|
} |
398 |
|
|
399 |
|
final static class FileResource extends Resource |
400 |
|
{ |
401 |
|
final File file; |
402 |
|
|
403 |
|
FileResource(FileURLLoader loader, String name, File file) |
404 |
|
{ |
405 |
|
super(loader, name); |
406 |
|
this.file = file; |
407 |
|
} |
408 |
|
|
409 |
|
InputStream getInputStream() throws IOException |
410 |
|
{ |
411 |
|
return new FileInputStream(file); |
412 |
|
} |
413 |
|
|
414 |
|
public int getLength() |
415 |
|
{ |
416 |
|
return (int)file.length(); |
417 |
|
} |
418 |
|
|
419 |
|
public Certificate[] getCertificates() |
420 |
|
{ |
421 |
|
// Certificates not supported for FileURLLoader. |
422 |
|
return null; |
423 |
|
} |
424 |
|
|
425 |
|
public URL getURL() |
426 |
|
{ |
427 |
|
try |
428 |
|
{ |
429 |
|
return new URL(loader.baseURL, name, |
430 |
|
loader.classloader.getURLStreamHandler("file")); |
431 |
|
} |
432 |
|
catch(MalformedURLException e) |
433 |
|
{ |
434 |
|
throw new InternalError(e.toString()); |
435 |
|
} |
436 |
} |
} |
437 |
|
} |
438 |
|
|
439 |
|
// Constructors |
440 |
|
|
441 |
/** |
/** |
442 |
* Creates a new instance of a URLClassLoader that gets classes from the |
* Creates a URLClassLoader that gets classes from the supplied URLs. |
443 |
* supplied URLs and with the supplied loader as parent class loader. |
* To determine if this classloader may be created the constructor of |
444 |
* @param urls the initial URLs used to resolve classes and resources |
* the super class (<code>SecureClassLoader</code>) is called first, which |
445 |
* @param parent the parent class loader |
* can throw a SecurityException. Then the supplied URLs are added |
446 |
*/ |
* in the order given to the URLClassLoader which uses these URLs to |
447 |
public static URLClassLoader newInstance(URL urls[], |
* load classes and resources (after using the default parent ClassLoader). |
448 |
ClassLoader parent) throws |
* |
449 |
SecurityException { |
* @exception SecurityException if the SecurityManager disallows the |
450 |
return new URLClassLoader(urls, parent); |
* creation of a ClassLoader. |
451 |
|
* @param urls Locations that should be searched by this ClassLoader when |
452 |
|
* resolving Classes or Resources. |
453 |
|
* @see SecureClassLoader |
454 |
|
*/ |
455 |
|
public URLClassLoader(URL[] urls) throws SecurityException |
456 |
|
{ |
457 |
|
super(); |
458 |
|
this.factory = null; |
459 |
|
this.securityContext = null; |
460 |
|
addURLs(urls); |
461 |
|
} |
462 |
|
|
463 |
|
/** |
464 |
|
* Private constructor used by the static |
465 |
|
* <code>newInstance(URL[])</code> method. Creates an |
466 |
|
* <code>URLClassLoader</code> without any <code>URL</code>s |
467 |
|
* yet. This is used to bypass the normal security check for |
468 |
|
* creating classloaders, but remembers the security context which |
469 |
|
* will be used when defining classes. The <code>URL</code>s to |
470 |
|
* load from must be added by the <code>newInstance()</code> method |
471 |
|
* in the security context of the caller. |
472 |
|
* |
473 |
|
* @param securityContext the security context of the unprivileged code. |
474 |
|
*/ |
475 |
|
private URLClassLoader(AccessControlContext securityContext) |
476 |
|
{ |
477 |
|
super(); |
478 |
|
this.factory = null; |
479 |
|
this.securityContext = securityContext; |
480 |
|
} |
481 |
|
|
482 |
|
/** |
483 |
|
* Creates a <code>URLClassLoader</code> that gets classes from the supplied |
484 |
|
* <code>URL</code>s. |
485 |
|
* To determine if this classloader may be created the constructor of |
486 |
|
* the super class (<code>SecureClassLoader</code>) is called first, which |
487 |
|
* can throw a SecurityException. Then the supplied URLs are added |
488 |
|
* in the order given to the URLClassLoader which uses these URLs to |
489 |
|
* load classes and resources (after using the supplied parent ClassLoader). |
490 |
|
* @exception SecurityException if the SecurityManager disallows the |
491 |
|
* creation of a ClassLoader. |
492 |
|
* @exception SecurityException |
493 |
|
* @param urls Locations that should be searched by this ClassLoader when |
494 |
|
* resolving Classes or Resources. |
495 |
|
* @param parent The parent class loader used before trying this class |
496 |
|
* loader. |
497 |
|
* @see SecureClassLoader |
498 |
|
*/ |
499 |
|
public URLClassLoader(URL[] urls, ClassLoader parent) |
500 |
|
throws SecurityException |
501 |
|
{ |
502 |
|
super(parent); |
503 |
|
this.factory = null; |
504 |
|
this.securityContext = null; |
505 |
|
addURLs(urls); |
506 |
|
} |
507 |
|
|
508 |
|
/** |
509 |
|
* Private constructor used by the static |
510 |
|
* <code>newInstance(URL[])</code> method. Creates an |
511 |
|
* <code>URLClassLoader</code> with the given parent but without any |
512 |
|
* <code>URL</code>s yet. This is used to bypass the normal security |
513 |
|
* check for creating classloaders, but remembers the security |
514 |
|
* context which will be used when defining classes. The |
515 |
|
* <code>URL</code>s to load from must be added by the |
516 |
|
* <code>newInstance()</code> method in the security context of the |
517 |
|
* caller. |
518 |
|
* |
519 |
|
* @param securityContext the security context of the unprivileged code. |
520 |
|
*/ |
521 |
|
private URLClassLoader(ClassLoader parent, |
522 |
|
AccessControlContext securityContext) |
523 |
|
{ |
524 |
|
super(parent); |
525 |
|
this.factory = null; |
526 |
|
this.securityContext = securityContext; |
527 |
|
} |
528 |
|
|
529 |
|
/** |
530 |
|
* Creates a URLClassLoader that gets classes from the supplied URLs. |
531 |
|
* To determine if this classloader may be created the constructor of |
532 |
|
* the super class (<CODE>SecureClassLoader</CODE>) is called first, which |
533 |
|
* can throw a SecurityException. Then the supplied URLs are added |
534 |
|
* in the order given to the URLClassLoader which uses these URLs to |
535 |
|
* load classes and resources (after using the supplied parent ClassLoader). |
536 |
|
* It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the |
537 |
|
* protocol handlers of the supplied URLs. |
538 |
|
* @exception SecurityException if the SecurityManager disallows the |
539 |
|
* creation of a ClassLoader. |
540 |
|
* @exception SecurityException |
541 |
|
* @param urls Locations that should be searched by this ClassLoader when |
542 |
|
* resolving Classes or Resources. |
543 |
|
* @param parent The parent class loader used before trying this class |
544 |
|
* loader. |
545 |
|
* @param factory Used to get the protocol handler for the URLs. |
546 |
|
* @see SecureClassLoader |
547 |
|
*/ |
548 |
|
public URLClassLoader(URL[] urls, |
549 |
|
ClassLoader parent, |
550 |
|
URLStreamHandlerFactory factory) |
551 |
|
throws SecurityException |
552 |
|
{ |
553 |
|
super(parent); |
554 |
|
this.securityContext = null; |
555 |
|
this.factory = factory; |
556 |
|
addURLs(urls); |
557 |
|
|
558 |
|
// If this factory is still not in factoryCache, add it, |
559 |
|
// since we only support three protocols so far, 5 is enough |
560 |
|
// for cache initial size |
561 |
|
synchronized(factoryCache) |
562 |
|
{ |
563 |
|
if(factory != null && factoryCache.get(factory) == null) |
564 |
|
factoryCache.put(factory, new HashMap(5)); |
565 |
|
} |
566 |
|
} |
567 |
|
|
568 |
|
// Methods |
569 |
|
|
570 |
|
/** |
571 |
|
* Adds a new location to the end of the internal URL store. |
572 |
|
* @param newUrl the location to add |
573 |
|
*/ |
574 |
|
protected void addURL(URL newUrl) { |
575 |
|
synchronized(urlloaders) |
576 |
|
{ |
577 |
|
if (newUrl == null) |
578 |
|
return; // Silently ignore... |
579 |
|
|
580 |
|
// check global cache to see if there're already url loader |
581 |
|
// for this url |
582 |
|
URLLoader loader = (URLLoader)urlloaders.get(newUrl); |
583 |
|
if (loader == null) |
584 |
|
{ |
585 |
|
String file = newUrl.getFile(); |
586 |
|
if (!file.endsWith("/")) // it's a jar url |
587 |
|
loader = new JarURLLoader(this, newUrl); |
588 |
|
else // it's a file url |
589 |
|
loader = new FileURLLoader(this, newUrl); |
590 |
|
|
591 |
|
//cache it |
592 |
|
urlloaders.put(newUrl, loader); |
593 |
|
} |
594 |
|
|
595 |
|
urls.add(newUrl); |
596 |
|
urlinfos.add(loader); |
597 |
|
} |
598 |
|
} |
599 |
|
|
600 |
|
/** |
601 |
|
* Adds an array of new locations to the end of the internal URL store. |
602 |
|
* @param newUrls the locations to add |
603 |
|
*/ |
604 |
|
private void addURLs(URL[] newUrls) { |
605 |
|
for (int i = 0; i < newUrls.length; i++) { |
606 |
|
addURL(newUrls[i]); |
607 |
|
} |
608 |
|
} |
609 |
|
|
610 |
|
/** |
611 |
|
* Defines a Package based on the given name and the supplied manifest |
612 |
|
* information. The manifest indicates the tile, version and |
613 |
|
* vendor information of the specification and implementation and wheter the |
614 |
|
* package is sealed. If the Manifest indicates that the package is sealed |
615 |
|
* then the Package will be sealed with respect to the supplied URL. |
616 |
|
* |
617 |
|
* @exception IllegalArgumentException If this package name already exists |
618 |
|
* in this class loader |
619 |
|
* @param name The name of the package |
620 |
|
* @param manifest The manifest describing the specification, |
621 |
|
* implementation and sealing details of the package |
622 |
|
* @param url the code source url to seal the package |
623 |
|
* @return the defined Package |
624 |
|
*/ |
625 |
|
protected Package definePackage(String name, Manifest manifest, URL url) |
626 |
|
throws IllegalArgumentException |
627 |
|
{ |
628 |
|
Attributes attr = manifest.getMainAttributes(); |
629 |
|
String specTitle = |
630 |
|
attr.getValue(Attributes.Name.SPECIFICATION_TITLE); |
631 |
|
String specVersion = |
632 |
|
attr.getValue(Attributes.Name.SPECIFICATION_VERSION); |
633 |
|
String specVendor = |
634 |
|
attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); |
635 |
|
String implTitle = |
636 |
|
attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); |
637 |
|
String implVersion = |
638 |
|
attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); |
639 |
|
String implVendor = |
640 |
|
attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); |
641 |
|
|
642 |
|
// Look if the Manifest indicates that this package is sealed |
643 |
|
// XXX - most likely not completely correct! |
644 |
|
// Shouldn't we also check the sealed attribute of the complete jar? |
645 |
|
// http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled |
646 |
|
// But how do we get that jar manifest here? |
647 |
|
String sealed = attr.getValue(Attributes.Name.SEALED); |
648 |
|
if ("false".equals(sealed)) { |
649 |
|
// make sure that the URL is null so the package is not sealed |
650 |
|
url = null; |
651 |
|
} |
652 |
|
|
653 |
|
return definePackage(name, specTitle, specVersion, specVendor, |
654 |
|
implTitle, implVersion, implVendor, url); |
655 |
|
} |
656 |
|
|
657 |
|
/** |
658 |
|
* Finds (the first) class by name from one of the locations. The locations |
659 |
|
* are searched in the order they were added to the URLClassLoader. |
660 |
|
* |
661 |
|
* @param className the classname to find |
662 |
|
* @exception ClassNotFoundException when the class could not be found or |
663 |
|
* loaded |
664 |
|
* @return a Class object representing the found class |
665 |
|
*/ |
666 |
|
protected Class findClass(final String className) |
667 |
|
throws ClassNotFoundException |
668 |
|
{ |
669 |
|
// Just try to find the resource by the (almost) same name |
670 |
|
String resourceName = className.replace('.', '/') + ".class"; |
671 |
|
Resource resource = findURLResource(resourceName); |
672 |
|
if (resource == null) |
673 |
|
throw new ClassNotFoundException(className + " not found in " + urls); |
674 |
|
|
675 |
|
// Try to read the class data, create the CodeSource, Package and |
676 |
|
// construct the class (and watch out for those nasty IOExceptions) |
677 |
|
try |
678 |
|
{ |
679 |
|
byte [] data; |
680 |
|
InputStream in = resource.getInputStream(); |
681 |
|
int length = resource.getLength(); |
682 |
|
if (length != -1) |
683 |
|
{ |
684 |
|
// We know the length of the data. |
685 |
|
// Just try to read it in all at once |
686 |
|
data = new byte[length]; |
687 |
|
int pos = 0; |
688 |
|
while(length - pos > 0) |
689 |
|
{ |
690 |
|
int len = in.read(data, pos, length - pos); |
691 |
|
if (len == -1) |
692 |
|
throw new EOFException("Not enough data reading from: " |
693 |
|
+ in); |
694 |
|
pos += len; |
695 |
|
} |
696 |
|
} |
697 |
|
else |
698 |
|
{ |
699 |
|
// We don't know the data length. |
700 |
|
// Have to read it in chunks. |
701 |
|
ByteArrayOutputStream out = new ByteArrayOutputStream(4096); |
702 |
|
byte b[] = new byte[4096]; |
703 |
|
int l = 0; |
704 |
|
while (l != -1) |
705 |
|
{ |
706 |
|
l = in.read(b); |
707 |
|
if (l != -1) |
708 |
|
out.write(b, 0, l); |
709 |
|
} |
710 |
|
data = out.toByteArray(); |
711 |
|
} |
712 |
|
final byte[] classData = data; |
713 |
|
|
714 |
|
// Now get the CodeSource |
715 |
|
final CodeSource source = resource.getCodeSource(); |
716 |
|
|
717 |
|
|
718 |
|
// Find out package name |
719 |
|
String packageName = null; |
720 |
|
int lastDot = className.lastIndexOf('.'); |
721 |
|
if (lastDot != -1) |
722 |
|
packageName = className.substring(0, lastDot); |
723 |
|
|
724 |
|
if (packageName != null && getPackage(packageName) == null) |
725 |
|
{ |
726 |
|
// define the package |
727 |
|
Manifest manifest = resource.loader.getManifest(); |
728 |
|
if (manifest == null) |
729 |
|
definePackage(packageName, |
730 |
|
null, null, null, null, null, null, null); |
731 |
|
else |
732 |
|
definePackage(packageName, manifest, resource.loader.baseURL); |
733 |
|
} |
734 |
|
|
735 |
|
// And finally construct the class! |
736 |
|
SecurityManager sm = System.getSecurityManager(); |
737 |
|
if (sm != null && securityContext != null) |
738 |
|
{ |
739 |
|
return (Class)AccessController.doPrivileged |
740 |
|
(new PrivilegedAction() |
741 |
|
{ |
742 |
|
public Object run() |
743 |
|
{ |
744 |
|
return defineClass(className, classData, |
745 |
|
0, classData.length, |
746 |
|
source); |
747 |
|
} |
748 |
|
}, securityContext); |
749 |
|
} |
750 |
|
else |
751 |
|
return defineClass(className, classData, |
752 |
|
0, classData.length, |
753 |
|
source); |
754 |
|
} |
755 |
|
catch (IOException ioe) |
756 |
|
{ |
757 |
|
throw (ClassNotFoundException) |
758 |
|
(new ClassNotFoundException(className, ioe)); |
759 |
|
} |
760 |
|
} |
761 |
|
|
762 |
|
/** |
763 |
|
* Finds the first occurrence of a resource that can be found. The locations |
764 |
|
* are searched in the order they were added to the URLClassLoader. |
765 |
|
* |
766 |
|
* @param resourceName the resource name to look for |
767 |
|
* @return the URLResource for the resource if found, null otherwise |
768 |
|
*/ |
769 |
|
private Resource findURLResource(String resourceName) |
770 |
|
{ |
771 |
|
int max = urls.size(); |
772 |
|
for (int i = 0; i < max; i++) |
773 |
|
{ |
774 |
|
URLLoader loader = (URLLoader)urlinfos.elementAt(i); |
775 |
|
if (loader == null) |
776 |
|
continue; |
777 |
|
|
778 |
|
Resource resource = loader.getResource(resourceName); |
779 |
|
if (resource != null) |
780 |
|
return resource; |
781 |
|
} |
782 |
|
return null; |
783 |
|
} |
784 |
|
|
785 |
|
/** |
786 |
|
* Finds the first occurrence of a resource that can be found. |
787 |
|
* |
788 |
|
* @param resourceName the resource name to look for |
789 |
|
* @return the URL if found, null otherwise |
790 |
|
*/ |
791 |
|
public URL findResource(String resourceName) |
792 |
|
{ |
793 |
|
Resource resource = findURLResource(resourceName); |
794 |
|
if (resource != null) |
795 |
|
return resource.getURL(); |
796 |
|
|
797 |
|
// Resource not found |
798 |
|
return null; |
799 |
|
} |
800 |
|
|
801 |
|
/** |
802 |
|
* If the URLStreamHandlerFactory has been set this return the appropriate |
803 |
|
* URLStreamHandler for the given protocol, if not set returns null. |
804 |
|
* |
805 |
|
* @param protocol the protocol for which we need a URLStreamHandler |
806 |
|
* @return the appropriate URLStreamHandler or null |
807 |
|
*/ |
808 |
|
URLStreamHandler getURLStreamHandler(String protocol) |
809 |
|
{ |
810 |
|
if (factory == null) |
811 |
|
return null; |
812 |
|
|
813 |
|
URLStreamHandler handler; |
814 |
|
synchronized (factoryCache) |
815 |
|
{ |
816 |
|
// check if there're handler for the same protocol in cache |
817 |
|
HashMap cache = (HashMap)factoryCache.get(factory); |
818 |
|
handler = (URLStreamHandler)cache.get(protocol); |
819 |
|
if(handler == null) |
820 |
|
{ |
821 |
|
// add it to cache |
822 |
|
handler = factory.createURLStreamHandler(protocol); |
823 |
|
cache.put(protocol, handler); |
824 |
|
} |
825 |
|
} |
826 |
|
return handler; |
827 |
|
} |
828 |
|
|
829 |
|
/** |
830 |
|
* Finds all the resources with a particular name from all the locations. |
831 |
|
* |
832 |
|
* @exception IOException when an error occurs accessing one of the |
833 |
|
* locations |
834 |
|
* @param resourceName the name of the resource to lookup |
835 |
|
* @return a (possible empty) enumeration of URLs where the resource can be |
836 |
|
* found |
837 |
|
*/ |
838 |
|
public Enumeration findResources(String resourceName) throws IOException |
839 |
|
{ |
840 |
|
Vector resources = new Vector(); |
841 |
|
int max = urls.size(); |
842 |
|
for (int i = 0; i < max; i++) |
843 |
|
{ |
844 |
|
URLLoader loader = (URLLoader)urlinfos.elementAt(i); |
845 |
|
Resource resource = loader.getResource(resourceName); |
846 |
|
if (resource != null) |
847 |
|
resources.add(resource.getURL()); |
848 |
|
} |
849 |
|
return resources.elements(); |
850 |
|
} |
851 |
|
|
852 |
|
/** |
853 |
|
* Returns the permissions needed to access a particular code |
854 |
|
* source. These permissions includes those returned by |
855 |
|
* <code>SecureClassLoader.getPermissions()</code> and the actual |
856 |
|
* permissions to access the objects referenced by the URL of the |
857 |
|
* code source. The extra permissions added depend on the protocol |
858 |
|
* and file portion of the URL in the code source. If the URL has |
859 |
|
* the "file" protocol ends with a '/' character then it must be a |
860 |
|
* directory and a file Permission to read everything in that |
861 |
|
* directory and all subdirectories is added. If the URL had the |
862 |
|
* "file" protocol and doesn't end with a '/' character then it must |
863 |
|
* be a normal file and a file permission to read that file is |
864 |
|
* added. If the <code>URL</code> has any other protocol then a |
865 |
|
* socket permission to connect and accept connections from the host |
866 |
|
* portion of the URL is added. |
867 |
|
* |
868 |
|
* @param source The codesource that needs the permissions to be accessed |
869 |
|
* @return the collection of permissions needed to access the code resource |
870 |
|
* @see java.security.SecureClassLoader#getPermissions() |
871 |
|
*/ |
872 |
|
protected PermissionCollection getPermissions(CodeSource source) { |
873 |
|
// XXX - This implementation does exactly as the Javadoc describes. |
874 |
|
// But maybe we should/could use URLConnection.getPermissions()? |
875 |
|
|
876 |
|
// First get the permissions that would normally be granted |
877 |
|
PermissionCollection permissions = super.getPermissions(source); |
878 |
|
|
879 |
|
// Now add the any extra permissions depending on the URL location |
880 |
|
URL url = source.getLocation(); |
881 |
|
String protocol = url.getProtocol(); |
882 |
|
if (protocol.equals("file")) { |
883 |
|
String file = url.getFile(); |
884 |
|
// If the file end in / it must be an directory |
885 |
|
if (file.endsWith("/")) { |
886 |
|
// Grant permission to read everything in that directory and |
887 |
|
// all subdirectories |
888 |
|
permissions.add(new FilePermission(file + "-", "read")); |
889 |
|
} else { // It is a 'normal' file |
890 |
|
// Grant permission to access that file |
891 |
|
permissions.add(new FilePermission(file, "read")); |
892 |
|
} |
893 |
|
} else { |
894 |
|
// Grant permission to connect to and accept connections from host |
895 |
|
String host = url.getHost(); |
896 |
|
permissions.add(new SocketPermission(host, "connect,accept")); |
897 |
} |
} |
898 |
|
|
899 |
|
return permissions; |
900 |
|
} |
901 |
|
|
902 |
|
/** |
903 |
|
* Returns all the locations that this class loader currently uses the |
904 |
|
* resolve classes and resource. This includes both the initially supplied |
905 |
|
* URLs as any URLs added later by the loader. |
906 |
|
* @return All the currently used URLs |
907 |
|
*/ |
908 |
|
public URL[] getURLs() { |
909 |
|
return (URL[]) urls.toArray(new URL[urls.size()]); |
910 |
|
} |
911 |
|
|
912 |
|
/** |
913 |
|
* Creates a new instance of a <code>URLClassLoader</code> that gets |
914 |
|
* classes from the supplied <code>URL</code>s. This class loader |
915 |
|
* will have as parent the standard system class loader. |
916 |
|
* |
917 |
|
* @param urls the initial URLs used to resolve classes and |
918 |
|
* resources |
919 |
|
* |
920 |
|
* @exception SecurityException when the calling code does not have |
921 |
|
* permission to access the given <code>URL</code>s |
922 |
|
*/ |
923 |
|
public static URLClassLoader newInstance(URL urls[]) |
924 |
|
throws SecurityException |
925 |
|
{ |
926 |
|
return newInstance(urls, null); |
927 |
|
} |
928 |
|
|
929 |
|
/** |
930 |
|
* Creates a new instance of a <code>URLClassLoader</code> that gets |
931 |
|
* classes from the supplied <code>URL</code>s and with the supplied |
932 |
|
* loader as parent class loader. |
933 |
|
* |
934 |
|
* @param urls the initial URLs used to resolve classes and |
935 |
|
* resources |
936 |
|
* @param parent the parent class loader |
937 |
|
* |
938 |
|
* @exception SecurityException when the calling code does not have |
939 |
|
* permission to access the given <code>URL</code>s |
940 |
|
*/ |
941 |
|
public static URLClassLoader newInstance(URL urls[], |
942 |
|
final ClassLoader parent) |
943 |
|
throws SecurityException |
944 |
|
{ |
945 |
|
SecurityManager sm = System.getSecurityManager(); |
946 |
|
if (sm == null) |
947 |
|
return new URLClassLoader(urls, parent); |
948 |
|
else |
949 |
|
{ |
950 |
|
final Object securityContext = sm.getSecurityContext(); |
951 |
|
// XXX - What to do with anything else then an AccessControlContext? |
952 |
|
if (!(securityContext instanceof AccessControlContext)) |
953 |
|
throw new SecurityException |
954 |
|
("securityContext must be AccessControlContext: " |
955 |
|
+ securityContext); |
956 |
|
|
957 |
|
URLClassLoader loader = |
958 |
|
(URLClassLoader)AccessController.doPrivileged(new PrivilegedAction() |
959 |
|
{ |
960 |
|
public Object run() |
961 |
|
{ |
962 |
|
return new URLClassLoader |
963 |
|
(parent, (AccessControlContext)securityContext); |
964 |
|
} |
965 |
|
}); |
966 |
|
loader.addURLs(urls); |
967 |
|
return loader; |
968 |
|
} |
969 |
|
} |
970 |
} |
} |