/[classpath]/classpath/java/net/URLClassLoader.java
ViewVC logotype

Diff of /classpath/java/net/URLClassLoader.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.6 by mark, Thu Sep 12 16:28:18 2002 UTC revision 1.7 by mark, Sun Oct 27 01:17:56 2002 UTC
# Line 1  Line 1 
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    
# Line 38  exception statement from your version. * Line 38  exception statement from your version. *
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  }  }

Legend:
Removed from v.1.6  
changed lines
  Added in v.1.7

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26