| /* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs |
| Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 |
| Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package java.net; |
| |
| import gnu.java.net.loader.FileURLLoader; |
| import gnu.java.net.loader.JarURLLoader; |
| import gnu.java.net.loader.RemoteURLLoader; |
| import gnu.java.net.loader.Resource; |
| import gnu.java.net.loader.URLLoader; |
| import gnu.java.net.loader.URLStreamHandlerCache; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.EOFException; |
| import java.io.File; |
| import java.io.FilePermission; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.security.AccessControlContext; |
| import java.security.AccessController; |
| import java.security.CodeSource; |
| import java.security.PermissionCollection; |
| import java.security.PrivilegedAction; |
| import java.security.SecureClassLoader; |
| import java.security.cert.Certificate; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.Vector; |
| import java.util.jar.Attributes; |
| import java.util.jar.Manifest; |
| |
| |
| /** |
| * A secure class loader that can load classes and resources from |
| * multiple locations. Given an array of <code>URL</code>s this class |
| * loader will retrieve classes and resources by fetching them from |
| * possible remote locations. Each <code>URL</code> is searched in |
| * order in which it was added. If the file portion of the |
| * <code>URL</code> ends with a '/' character then it is interpreted |
| * as a base directory, otherwise it is interpreted as a jar file from |
| * which the classes/resources are resolved. |
| * |
| * <p>New instances can be created by two static |
| * <code>newInstance()</code> methods or by three public |
| * contructors. Both ways give the option to supply an initial array |
| * of <code>URL</code>s and (optionally) a parent classloader (that is |
| * different from the standard system class loader).</p> |
| * |
| * <p>Normally creating a <code>URLClassLoader</code> throws a |
| * <code>SecurityException</code> if a <code>SecurityManager</code> is |
| * installed and the <code>checkCreateClassLoader()</code> method does |
| * not return true. But the <code>newInstance()</code> methods may be |
| * used by any code as long as it has permission to acces the given |
| * <code>URL</code>s. <code>URLClassLoaders</code> created by the |
| * <code>newInstance()</code> methods also explicitly call the |
| * <code>checkPackageAccess()</code> method of |
| * <code>SecurityManager</code> if one is installed before trying to |
| * load a class. Note that only subclasses of |
| * <code>URLClassLoader</code> can add new URLs after the |
| * URLClassLoader had been created. But it is always possible to get |
| * an array of all URLs that the class loader uses to resolve classes |
| * and resources by way of the <code>getURLs()</code> method.</p> |
| * |
| * <p>Open issues: |
| * <ul> |
| * |
| * <li>Should the URLClassLoader actually add the locations found in |
| * the manifest or is this the responsibility of some other |
| * loader/(sub)class? (see <a |
| * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html"> |
| * Extension Mechanism Architecture - Bundles Extensions</a>)</li> |
| * |
| * <li>How does <code>definePackage()</code> and sealing work |
| * precisely?</li> |
| * |
| * <li>We save and use the security context (when a created by |
| * <code>newInstance()</code> but do we have to use it in more |
| * places?</li> |
| * |
| * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li> |
| * |
| * </ul> |
| * </p> |
| * |
| * @since 1.2 |
| * |
| * @author Mark Wielaard (mark@klomp.org) |
| * @author Wu Gansha (gansha.wu@intel.com) |
| */ |
| public class URLClassLoader extends SecureClassLoader |
| { |
| // Class Variables |
| |
| /** |
| * A cache to store mappings between handler factory and its |
| * private protocol handler cache (also a HashMap), so we can avoid |
| * creating handlers each time the same protocol comes. |
| */ |
| private static URLStreamHandlerCache factoryCache |
| = new URLStreamHandlerCache(); |
| |
| /** |
| * The prefix for URL loaders. |
| */ |
| private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_"; |
| |
| // Instance variables |
| |
| /** Locations to load classes from */ |
| private final Vector urls = new Vector(); |
| |
| /** |
| * Store pre-parsed information for each url into this vector: each |
| * element is a URL loader. A jar file has its own class-path |
| * attribute which adds to the URLs that will be searched, but this |
| * does not add to the list of urls. |
| */ |
| private final Vector urlinfos = new Vector(); |
| |
| /** Factory used to get the protocol handlers of the URLs */ |
| private final URLStreamHandlerFactory factory; |
| |
| /** |
| * The security context when created from <code>newInstance()</code> |
| * or null when created through a normal constructor or when no |
| * <code>SecurityManager</code> was installed. |
| */ |
| private final AccessControlContext securityContext; |
| |
| // Helper classes |
| |
| /** |
| * Creates a URLClassLoader that gets classes from the supplied URLs. |
| * 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). |
| * |
| * @param urls Locations that should be searched by this ClassLoader when |
| * resolving Classes or Resources. |
| * @exception SecurityException if the SecurityManager disallows the |
| * creation of a ClassLoader. |
| * @see SecureClassLoader |
| */ |
| public URLClassLoader(URL[] urls) throws SecurityException |
| { |
| super(); |
| this.factory = null; |
| this.securityContext = null; |
| addURLs(urls); |
| } |
| |
| /** |
| * Creates a <code>URLClassLoader</code> that gets classes from the supplied |
| * <code>URL</code>s. |
| * 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). |
| * @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. |
| * @exception SecurityException if the SecurityManager disallows the |
| * creation of a ClassLoader. |
| * @exception SecurityException |
| * @see SecureClassLoader |
| */ |
| public URLClassLoader(URL[] urls, ClassLoader parent) |
| throws SecurityException |
| { |
| super(parent); |
| this.factory = null; |
| this.securityContext = null; |
| addURLs(urls); |
| } |
| |
| // Package-private to avoid a trampoline constructor. |
| /** |
| * Package-private constructor used by the static |
| * <code>newInstance(URL[])</code> method. Creates an |
| * <code>URLClassLoader</code> with the given parent but without any |
| * <code>URL</code>s yet. This is used to bypass the normal security |
| * check for creating classloaders, but remembers the security |
| * context which will be used when defining classes. The |
| * <code>URL</code>s to load from must be added by the |
| * <code>newInstance()</code> method in the security context of the |
| * caller. |
| * |
| * @param securityContext the security context of the unprivileged code. |
| */ |
| URLClassLoader(ClassLoader parent, AccessControlContext securityContext) |
| { |
| super(parent); |
| this.factory = null; |
| this.securityContext = securityContext; |
| } |
| |
| /** |
| * Creates a URLClassLoader that gets classes from the supplied URLs. |
| * 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). |
| * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the |
| * protocol handlers of the supplied URLs. |
| * @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. |
| * @exception SecurityException if the SecurityManager disallows the |
| * creation of a ClassLoader. |
| * @exception SecurityException |
| * @see SecureClassLoader |
| */ |
| public URLClassLoader(URL[] urls, ClassLoader parent, |
| URLStreamHandlerFactory factory) |
| throws SecurityException |
| { |
| super(parent); |
| this.securityContext = null; |
| this.factory = factory; |
| addURLs(urls); |
| |
| // If this factory is still not in factoryCache, add it. |
| factoryCache.add(factory); |
| } |
| |
| // Methods |
| |
| /** |
| * Adds a new location to the end of the internal URL store. |
| * @param newUrl the location to add |
| */ |
| protected void addURL(URL newUrl) |
| { |
| urls.add(newUrl); |
| addURLImpl(newUrl); |
| } |
| |
| private void addURLImpl(URL newUrl) |
| { |
| synchronized (this) |
| { |
| if (newUrl == null) |
| return; // Silently ignore... |
| |
| // Reset the toString() value. |
| thisString = null; |
| |
| // Create a loader for this URL. |
| URLLoader loader = null; |
| String file = newUrl.getFile(); |
| String protocol = newUrl.getProtocol(); |
| |
| // If we have a file: URL, we want to make it absolute |
| // here, before we decide whether it is really a jar. |
| URL absoluteURL; |
| if ("file".equals (protocol)) |
| { |
| File dir = new File(file); |
| URL absUrl; |
| try |
| { |
| absoluteURL = dir.getCanonicalFile().toURL(); |
| } |
| catch (IOException ignore) |
| { |
| try |
| { |
| absoluteURL = dir.getAbsoluteFile().toURL(); |
| } |
| catch (MalformedURLException _) |
| { |
| // This really should not happen. |
| absoluteURL = newUrl; |
| } |
| } |
| } |
| else |
| { |
| // This doesn't hurt, and it simplifies the logic a |
| // little. |
| absoluteURL = newUrl; |
| } |
| |
| // First see if we can find a handler with the correct name. |
| try |
| { |
| Class handler = Class.forName(URL_LOADER_PREFIX + protocol); |
| Class[] argTypes = new Class[] { URLClassLoader.class, |
| URLStreamHandlerCache.class, |
| URLStreamHandlerFactory.class, |
| URL.class, |
| URL.class }; |
| Constructor k = handler.getDeclaredConstructor(argTypes); |
| loader |
| = (URLLoader) k.newInstance(new Object[] { this, |
| factoryCache, |
| factory, |
| newUrl, |
| absoluteURL }); |
| } |
| catch (ClassNotFoundException ignore) |
| { |
| // Fall through. |
| } |
| catch (NoSuchMethodException nsme) |
| { |
| // Programming error in the class library. |
| InternalError vme |
| = new InternalError("couldn't find URLLoader constructor"); |
| vme.initCause(nsme); |
| throw vme; |
| } |
| catch (InstantiationException inste) |
| { |
| // Programming error in the class library. |
| InternalError vme |
| = new InternalError("couldn't instantiate URLLoader"); |
| vme.initCause(inste); |
| throw vme; |
| } |
| catch (InvocationTargetException ite) |
| { |
| // Programming error in the class library. |
| InternalError vme |
| = new InternalError("error instantiating URLLoader"); |
| vme.initCause(ite); |
| throw vme; |
| } |
| catch (IllegalAccessException illae) |
| { |
| // Programming error in the class library. |
| InternalError vme |
| = new InternalError("invalid access to URLLoader"); |
| vme.initCause(illae); |
| throw vme; |
| } |
| |
| if (loader == null) |
| { |
| // If it is not a directory, use the jar loader. |
| if (! (file.endsWith("/") || file.endsWith(File.separator))) |
| loader = new JarURLLoader(this, factoryCache, factory, |
| newUrl, absoluteURL); |
| else if ("file".equals(protocol)) |
| loader = new FileURLLoader(this, factoryCache, factory, |
| newUrl, absoluteURL); |
| else |
| loader = new RemoteURLLoader(this, factoryCache, factory, |
| newUrl); |
| } |
| |
| urlinfos.add(loader); |
| ArrayList extra = loader.getClassPath(); |
| if (extra != null) |
| urlinfos.addAll(extra); |
| } |
| } |
| |
| /** |
| * Adds an array of new locations to the end of the internal URL |
| * store. Called from the the constructors. Should not call to the |
| * protected addURL() method since that can be overridden and |
| * subclasses are not yet in a good state at this point. |
| * jboss 4.0.3 for example depends on this. |
| * |
| * @param newUrls the locations to add |
| */ |
| private void addURLs(URL[] newUrls) |
| { |
| for (int i = 0; i < newUrls.length; i++) |
| { |
| urls.add(newUrls[i]); |
| addURLImpl(newUrls[i]); |
| } |
| } |
| |
| /** |
| * Look in both Attributes for a given value. The first Attributes |
| * object, if not null, has precedence. |
| */ |
| private String getAttributeValue(Attributes.Name name, Attributes first, |
| Attributes second) |
| { |
| String result = null; |
| if (first != null) |
| result = first.getValue(name); |
| if (result == null) |
| result = second.getValue(name); |
| return result; |
| } |
| |
| /** |
| * Defines a Package based on the given name and the supplied manifest |
| * information. The manifest indicates the title, version and |
| * vendor information of the specification and implementation and whether 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. |
| * |
| * @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 |
| * @throws IllegalArgumentException If this package name already exists |
| * in this class loader |
| */ |
| protected Package definePackage(String name, Manifest manifest, URL url) |
| throws IllegalArgumentException |
| { |
| // Compute the name of the package as it may appear in the |
| // Manifest. |
| StringBuffer xform = new StringBuffer(name); |
| for (int i = xform.length () - 1; i >= 0; --i) |
| if (xform.charAt(i) == '.') |
| xform.setCharAt(i, '/'); |
| xform.append('/'); |
| String xformName = xform.toString(); |
| |
| Attributes entryAttr = manifest.getAttributes(xformName); |
| Attributes attr = manifest.getMainAttributes(); |
| |
| String specTitle |
| = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE, |
| entryAttr, attr); |
| String specVersion |
| = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION, |
| entryAttr, attr); |
| String specVendor |
| = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR, |
| entryAttr, attr); |
| String implTitle |
| = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE, |
| entryAttr, attr); |
| String implVersion |
| = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION, |
| entryAttr, attr); |
| String implVendor |
| = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR, |
| entryAttr, attr); |
| |
| // 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.4/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, specVendor, specVersion, |
| implTitle, implVendor, implVersion, |
| url); |
| } |
| |
| /** |
| * Finds (the first) class by name from one of the locations. The locations |
| * are searched in the order they were added to the URLClassLoader. |
| * |
| * @param className the classname to find |
| * @exception ClassNotFoundException when the class could not be found or |
| * loaded |
| * @return a Class object representing the found class |
| */ |
| protected Class findClass(final String className) |
| throws ClassNotFoundException |
| { |
| // Just try to find the resource by the (almost) same name |
| String resourceName = className.replace('.', '/') + ".class"; |
| int max = urlinfos.size(); |
| Resource resource = null; |
| for (int i = 0; i < max && resource == null; i++) |
| { |
| URLLoader loader = (URLLoader)urlinfos.elementAt(i); |
| if (loader == null) |
| continue; |
| |
| Class k = loader.getClass(className); |
| if (k != null) |
| return k; |
| |
| resource = loader.getResource(resourceName); |
| } |
| if (resource == null) |
| throw new ClassNotFoundException(className + " not found in " + this); |
| |
| // Try to read the class data, create the CodeSource, Package and |
| // construct the class (and watch out for those nasty IOExceptions) |
| try |
| { |
| byte[] data; |
| InputStream in = resource.getInputStream(); |
| try |
| { |
| int length = resource.getLength(); |
| if (length != -1) |
| { |
| // We know the length of the data. |
| // Just try to read it in all at once |
| data = new byte[length]; |
| int pos = 0; |
| while (length - pos > 0) |
| { |
| int len = in.read(data, pos, length - pos); |
| if (len == -1) |
| throw new EOFException("Not enough data reading from: " |
| + in); |
| pos += len; |
| } |
| } |
| else |
| { |
| // We don't know the data length. |
| // Have to read it in chunks. |
| ByteArrayOutputStream out = new ByteArrayOutputStream(4096); |
| byte[] b = new byte[4096]; |
| int l = 0; |
| while (l != -1) |
| { |
| l = in.read(b); |
| if (l != -1) |
| out.write(b, 0, l); |
| } |
| data = out.toByteArray(); |
| } |
| } |
| finally |
| { |
| in.close(); |
| } |
| final byte[] classData = data; |
| |
| // Now get the CodeSource |
| final CodeSource source = resource.getCodeSource(); |
| |
| // Find out package name |
| String packageName = null; |
| int lastDot = className.lastIndexOf('.'); |
| if (lastDot != -1) |
| packageName = className.substring(0, lastDot); |
| |
| if (packageName != null && getPackage(packageName) == null) |
| { |
| // define the package |
| Manifest manifest = resource.getLoader().getManifest(); |
| if (manifest == null) |
| definePackage(packageName, null, null, null, null, null, null, |
| null); |
| else |
| definePackage(packageName, manifest, |
| resource.getLoader().getBaseURL()); |
| } |
| |
| // And finally construct the class! |
| SecurityManager sm = System.getSecurityManager(); |
| Class result = null; |
| if (sm != null && securityContext != null) |
| { |
| result = (Class)AccessController.doPrivileged |
| (new PrivilegedAction() |
| { |
| public Object run() |
| { |
| return defineClass(className, classData, |
| 0, classData.length, |
| source); |
| } |
| }, securityContext); |
| } |
| else |
| result = defineClass(className, classData, 0, classData.length, source); |
| |
| // Avoid NullPointerExceptions. |
| Certificate[] resourceCertificates = resource.getCertificates(); |
| if(resourceCertificates != null) |
| super.setSigners(result, resourceCertificates); |
| |
| return result; |
| } |
| catch (IOException ioe) |
| { |
| ClassNotFoundException cnfe; |
| cnfe = new ClassNotFoundException(className + " not found in " + this); |
| cnfe.initCause(ioe); |
| throw cnfe; |
| } |
| } |
| |
| // Cached String representation of this URLClassLoader |
| private String thisString; |
| |
| /** |
| * Returns a String representation of this URLClassLoader giving the |
| * actual Class name, the URLs that are searched and the parent |
| * ClassLoader. |
| */ |
| public String toString() |
| { |
| synchronized (this) |
| { |
| if (thisString == null) |
| { |
| StringBuffer sb = new StringBuffer(); |
| sb.append(this.getClass().getName()); |
| sb.append("{urls=[" ); |
| URL[] thisURLs = getURLs(); |
| for (int i = 0; i < thisURLs.length; i++) |
| { |
| sb.append(thisURLs[i]); |
| if (i < thisURLs.length - 1) |
| sb.append(','); |
| } |
| sb.append(']'); |
| sb.append(", parent="); |
| sb.append(getParent()); |
| sb.append('}'); |
| thisString = sb.toString(); |
| } |
| return thisString; |
| } |
| } |
| |
| /** |
| * Finds the first occurrence of a resource that can be found. The locations |
| * are searched in the order they were added to the URLClassLoader. |
| * |
| * @param resourceName the resource name to look for |
| * @return the URLResource for the resource if found, null otherwise |
| */ |
| private Resource findURLResource(String resourceName) |
| { |
| int max = urlinfos.size(); |
| for (int i = 0; i < max; i++) |
| { |
| URLLoader loader = (URLLoader) urlinfos.elementAt(i); |
| if (loader == null) |
| continue; |
| |
| Resource resource = loader.getResource(resourceName); |
| if (resource != null) |
| return resource; |
| } |
| return null; |
| } |
| |
| /** |
| * Finds the first occurrence of a resource that can be found. |
| * |
| * @param resourceName the resource name to look for |
| * @return the URL if found, null otherwise |
| */ |
| public URL findResource(String resourceName) |
| { |
| Resource resource = findURLResource(resourceName); |
| if (resource != null) |
| return resource.getURL(); |
| |
| // Resource not found |
| return null; |
| } |
| |
| /** |
| * Finds all the resources with a particular name from all the locations. |
| * |
| * @param resourceName the name of the resource to lookup |
| * @return a (possible empty) enumeration of URLs where the resource can be |
| * found |
| * @exception IOException when an error occurs accessing one of the |
| * locations |
| */ |
| public Enumeration findResources(String resourceName) |
| throws IOException |
| { |
| Vector resources = new Vector(); |
| int max = urlinfos.size(); |
| for (int i = 0; i < max; i++) |
| { |
| URLLoader loader = (URLLoader) urlinfos.elementAt(i); |
| Resource resource = loader.getResource(resourceName); |
| if (resource != null) |
| resources.add(resource.getURL()); |
| } |
| return resources.elements(); |
| } |
| |
| /** |
| * Returns the permissions needed to access a particular code |
| * source. These permissions includes those returned by |
| * <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 everything 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 <code>URL</code> 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 java.security.SecureClassLoader#getPermissions(CodeSource) |
| */ |
| protected PermissionCollection getPermissions(CodeSource source) |
| { |
| // XXX - This implementation does exactly as the Javadoc describes. |
| // But maybe we should/could use URLConnection.getPermissions()? |
| // First get the permissions that would normally be granted |
| PermissionCollection permissions = super.getPermissions(source); |
| |
| // Now add any extra permissions depending on the URL location. |
| URL url = source.getLocation(); |
| String protocol = url.getProtocol(); |
| if (protocol.equals("file")) |
| { |
| String file = url.getFile(); |
| |
| // If the file end in / it must be an directory. |
| if (file.endsWith("/") || file.endsWith(File.separator)) |
| { |
| // 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(); |
| if (host != null) |
| permissions.add(new SocketPermission(host, "connect,accept")); |
| } |
| |
| return permissions; |
| } |
| |
| /** |
| * Returns all the locations that this class loader currently uses the |
| * resolve classes and resource. This includes both the initially supplied |
| * URLs as any URLs added later by the loader. |
| * @return All the currently used URLs |
| */ |
| public URL[] getURLs() |
| { |
| return (URL[]) urls.toArray(new URL[urls.size()]); |
| } |
| |
| /** |
| * Creates a new instance of a <code>URLClassLoader</code> that gets |
| * classes from the supplied <code>URL</code>s. This class loader |
| * will have as parent the standard system class loader. |
| * |
| * @param urls the initial URLs used to resolve classes and |
| * resources |
| * |
| * @return the class loader |
| * |
| * @exception SecurityException when the calling code does not have |
| * permission to access the given <code>URL</code>s |
| */ |
| public static URLClassLoader newInstance(URL[] urls) |
| throws SecurityException |
| { |
| return newInstance(urls, null); |
| } |
| |
| /** |
| * Creates a new instance of a <code>URLClassLoader</code> that gets |
| * classes from the supplied <code>URL</code>s and with the supplied |
| * loader as parent class loader. |
| * |
| * @param urls the initial URLs used to resolve classes and |
| * resources |
| * @param parent the parent class loader |
| * |
| * @return the class loader |
| * |
| * @exception SecurityException when the calling code does not have |
| * permission to access the given <code>URL</code>s |
| */ |
| public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent) |
| throws SecurityException |
| { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm == null) |
| return new URLClassLoader(urls, parent); |
| else |
| { |
| final Object securityContext = sm.getSecurityContext(); |
| |
| // XXX - What to do with anything else then an AccessControlContext? |
| if (! (securityContext instanceof AccessControlContext)) |
| throw new SecurityException("securityContext must be AccessControlContext: " |
| + securityContext); |
| |
| URLClassLoader loader = |
| (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction() |
| { |
| public Object run() |
| { |
| return new URLClassLoader(parent, |
| (AccessControlContext) securityContext); |
| } |
| }); |
| loader.addURLs(urls); |
| return loader; |
| } |
| } |
| } |