Merge from the pain train
[official-gcc.git] / libjava / java / net / URLClassLoader.java
blob919fc9de0f61a0b211777121dc404243aa200647
1 /* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs
2 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA.
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
40 package java.net;
42 import java.io.ByteArrayOutputStream;
43 import java.io.EOFException;
44 import java.io.File;
45 import java.io.FileInputStream;
46 import java.io.FilePermission;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.security.AccessControlContext;
50 import java.security.AccessController;
51 import java.security.CodeSource;
52 import java.security.PermissionCollection;
53 import java.security.PrivilegedAction;
54 import java.security.SecureClassLoader;
55 import java.security.cert.Certificate;
56 import java.util.Enumeration;
57 import java.util.HashMap;
58 import java.util.Iterator;
59 import java.util.StringTokenizer;
60 import java.util.Vector;
61 import java.util.jar.Attributes;
62 import java.util.jar.JarEntry;
63 import java.util.jar.JarFile;
64 import java.util.jar.Manifest;
65 import gnu.gcj.runtime.SharedLibHelper;
68 /**
69 * A secure class loader that can load classes and resources from
70 * multiple locations. Given an array of <code>URL</code>s this class
71 * loader will retrieve classes and resources by fetching them from
72 * possible remote locations. Each <code>URL</code> is searched in
73 * order in which it was added. If the file portion of the
74 * <code>URL</code> ends with a '/' character then it is interpreted
75 * as a base directory, otherwise it is interpreted as a jar file from
76 * which the classes/resources are resolved.
78 * <p>New instances can be created by two static
79 * <code>newInstance()</code> methods or by three public
80 * contructors. Both ways give the option to supply an initial array
81 * of <code>URL</code>s and (optionally) a parent classloader (that is
82 * different from the standard system class loader).</p>
84 * <p>Normally creating a <code>URLClassLoader</code> throws a
85 * <code>SecurityException</code> if a <code>SecurityManager</code> is
86 * installed and the <code>checkCreateClassLoader()</code> method does
87 * not return true. But the <code>newInstance()</code> methods may be
88 * used by any code as long as it has permission to acces the given
89 * <code>URL</code>s. <code>URLClassLoaders</code> created by the
90 * <code>newInstance()</code> methods also explicitly call the
91 * <code>checkPackageAccess()</code> method of
92 * <code>SecurityManager</code> if one is installed before trying to
93 * load a class. Note that only subclasses of
94 * <code>URLClassLoader</code> can add new URLs after the
95 * URLClassLoader had been created. But it is always possible to get
96 * an array of all URLs that the class loader uses to resolve classes
97 * and resources by way of the <code>getURLs()</code> method.</p>
99 * <p>Open issues:
100 * <ul>
102 * <li>Should the URLClassLoader actually add the locations found in
103 * the manifest or is this the responsibility of some other
104 * loader/(sub)class? (see <a
105 * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
106 * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
108 * <li>How does <code>definePackage()</code> and sealing work
109 * precisely?</li>
111 * <li>We save and use the security context (when a created by
112 * <code>newInstance()</code> but do we have to use it in more
113 * places?</li>
115 * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
117 * </ul>
118 * </p>
120 * @since 1.2
122 * @author Mark Wielaard (mark@klomp.org)
123 * @author Wu Gansha (gansha.wu@intel.com)
125 public class URLClassLoader extends SecureClassLoader
127 // Class Variables
130 * A global cache to store mappings between URLLoader and URL,
131 * so we can avoid do all the homework each time the same URL
132 * comes.
133 * XXX - Keeps these loaders forever which prevents garbage collection.
135 private static HashMap urlloaders = new HashMap();
138 * A cache to store mappings between handler factory and its
139 * private protocol handler cache (also a HashMap), so we can avoid
140 * create handlers each time the same protocol comes.
142 private static HashMap factoryCache = new HashMap(5);
144 // Instance variables
146 /** Locations to load classes from */
147 private final Vector urls = new Vector();
150 * Store pre-parsed information for each url into this vector: each
151 * element is a URL loader. A jar file has its own class-path
152 * attribute which adds to the URLs that will be searched, but this
153 * does not add to the list of urls.
155 private final Vector urlinfos = new Vector();
157 /** Factory used to get the protocol handlers of the URLs */
158 private final URLStreamHandlerFactory factory;
161 * The security context when created from <code>newInstance()</code>
162 * or null when created through a normal constructor or when no
163 * <code>SecurityManager</code> was installed.
165 private final AccessControlContext securityContext;
167 // Helper classes
170 * A <code>URLLoader</code> contains all logic to load resources from a
171 * given base <code>URL</code>.
173 abstract static class URLLoader
176 * Our classloader to get info from if needed.
178 final URLClassLoader classloader;
181 * The base URL from which all resources are loaded.
183 final URL baseURL;
186 * A <code>CodeSource</code> without any associated certificates.
187 * It is common for classes to not have certificates associated
188 * with them. If they come from the same <code>URLLoader</code>
189 * then it is safe to share the associated <code>CodeSource</code>
190 * between them since <code>CodeSource</code> is immutable.
192 final CodeSource noCertCodeSource;
194 URLLoader(URLClassLoader classloader, URL baseURL)
196 this(classloader, baseURL, baseURL);
199 URLLoader(URLClassLoader classloader, URL baseURL, URL overrideURL)
201 this.classloader = classloader;
202 this.baseURL = baseURL;
203 this.noCertCodeSource = new CodeSource(overrideURL, null);
207 * Returns a <code>Class</code> loaded by this
208 * <code>URLLoader</code>, or <code>null</code> when this loader
209 * either can't load the class or doesn't know how to load classes
210 * at all.
212 Class getClass(String className)
214 return null;
218 * Returns a <code>Resource</code> loaded by this
219 * <code>URLLoader</code>, or <code>null</code> when no
220 * <code>Resource</code> with the given name exists.
222 abstract Resource getResource(String s);
225 * Returns the <code>Manifest</code> associated with the
226 * <code>Resource</code>s loaded by this <code>URLLoader</code> or
227 * <code>null</code> there is no such <code>Manifest</code>.
229 Manifest getManifest()
231 return null;
234 Vector getClassPath()
236 return null;
241 * A <code>Resource</code> represents a resource in some
242 * <code>URLLoader</code>. It also contains all information (e.g.,
243 * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and
244 * <code>InputStream</code>) that is necessary for loading resources
245 * and creating classes from a <code>URL</code>.
247 abstract static class Resource
249 final URLLoader loader;
250 final String name;
252 Resource(URLLoader loader, String name)
254 this.loader = loader;
255 this.name = name;
259 * Returns the non-null <code>CodeSource</code> associated with
260 * this resource.
262 CodeSource getCodeSource()
264 Certificate[] certs = getCertificates();
265 if (certs == null)
266 return loader.noCertCodeSource;
267 else
268 return new CodeSource(loader.baseURL, certs);
272 * Returns <code>Certificates</code> associated with this
273 * resource, or null when there are none.
275 Certificate[] getCertificates()
277 return null;
281 * Return a <code>URL</code> that can be used to access this resource.
283 abstract URL getURL();
286 * Returns the size of this <code>Resource</code> in bytes or
287 * <code>-1</code> when unknown.
289 abstract int getLength();
292 * Returns the non-null <code>InputStream</code> through which
293 * this resource can be loaded.
295 abstract InputStream getInputStream() throws IOException;
299 * A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
300 * only loading from jar url.
302 static final class JarURLLoader extends URLLoader
304 final JarFile jarfile; // The jar file for this url
305 final URL baseJarURL; // Base jar: url for all resources loaded from jar
307 Vector classPath; // The "Class-Path" attribute of this Jar's manifest
309 public JarURLLoader(URLClassLoader classloader, URL baseURL)
311 super(classloader, baseURL);
313 // Cache url prefix for all resources in this jar url.
314 String external = baseURL.toExternalForm();
315 StringBuffer sb = new StringBuffer(external.length() + 6);
316 sb.append("jar:");
317 sb.append(external);
318 sb.append("!/");
319 String jarURL = sb.toString();
321 this.classPath = null;
322 URL baseJarURL = null;
323 JarFile jarfile = null;
326 baseJarURL =
327 new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
329 jarfile =
330 ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
332 Manifest manifest;
333 Attributes attributes;
334 String classPathString;
336 if ((manifest = jarfile.getManifest()) != null
337 && (attributes = manifest.getMainAttributes()) != null
338 && ((classPathString
339 = attributes.getValue(Attributes.Name.CLASS_PATH))
340 != null))
342 this.classPath = new Vector();
344 StringTokenizer st = new StringTokenizer(classPathString, " ");
345 while (st.hasMoreElements ())
347 String e = st.nextToken ();
350 URL url = new URL(baseURL, e);
351 this.classPath.add(url);
353 catch (java.net.MalformedURLException xx)
355 // Give up
360 catch (IOException ioe)
362 /* ignored */
365 this.baseJarURL = baseJarURL;
366 this.jarfile = jarfile;
369 /** get resource with the name "name" in the jar url */
370 Resource getResource(String name)
372 if (jarfile == null)
373 return null;
375 if (name.startsWith("/"))
376 name = name.substring(1);
378 JarEntry je = jarfile.getJarEntry(name);
379 if (je != null)
380 return new JarURLResource(this, name, je);
381 else
382 return null;
385 Manifest getManifest()
389 return (jarfile == null) ? null : jarfile.getManifest();
391 catch (IOException ioe)
393 return null;
397 Vector getClassPath()
399 return classPath;
403 static final class JarURLResource extends Resource
405 private final JarEntry entry;
407 JarURLResource(JarURLLoader loader, String name, JarEntry entry)
409 super(loader, name);
410 this.entry = entry;
413 InputStream getInputStream() throws IOException
415 return ((JarURLLoader) loader).jarfile.getInputStream(entry);
418 int getLength()
420 return (int) entry.getSize();
423 Certificate[] getCertificates()
425 // We have to get the entry from the jar file again, because the
426 // certificates will not be available until the entire entry has
427 // been read.
428 return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name))
429 .getCertificates();
432 URL getURL()
436 return new URL(((JarURLLoader) loader).baseJarURL, name,
437 loader.classloader.getURLStreamHandler("jar"));
439 catch (MalformedURLException e)
441 InternalError ie = new InternalError();
442 ie.initCause(e);
443 throw ie;
449 * Loader for remote directories.
451 static final class RemoteURLLoader extends URLLoader
453 private final String protocol;
455 RemoteURLLoader(URLClassLoader classloader, URL url)
457 super(classloader, url);
458 protocol = url.getProtocol();
462 * Get a remote resource.
463 * Returns null if no such resource exists.
465 Resource getResource(String name)
469 URL url =
470 new URL(baseURL, name, classloader.getURLStreamHandler(protocol));
471 URLConnection connection = url.openConnection();
473 // Open the connection and check the stream
474 // just to be sure it exists.
475 int length = connection.getContentLength();
476 InputStream stream = connection.getInputStream();
478 // We can do some extra checking if it is a http request
479 if (connection instanceof HttpURLConnection)
481 int response =
482 ((HttpURLConnection) connection).getResponseCode();
483 if (response / 100 != 2)
484 return null;
487 if (stream != null)
488 return new RemoteResource(this, name, url, stream, length);
489 else
490 return null;
492 catch (IOException ioe)
494 return null;
500 * A resource from some remote location.
502 static final class RemoteResource extends Resource
504 private final URL url;
505 private final InputStream stream;
506 private final int length;
508 RemoteResource(RemoteURLLoader loader, String name, URL url,
509 InputStream stream, int length)
511 super(loader, name);
512 this.url = url;
513 this.stream = stream;
514 this.length = length;
517 InputStream getInputStream() throws IOException
519 return stream;
522 public int getLength()
524 return length;
527 public URL getURL()
529 return url;
534 * A <code>SoURLLoader</code> is a type of <code>URLLoader</code>
535 * that loads classes and resources from a shared library.
537 final static class SoURLLoader extends URLLoader
539 SharedLibHelper helper;
541 SoURLLoader(URLClassLoader classloader, URL url)
543 this(classloader, url, url);
546 SoURLLoader(URLClassLoader classloader, URL url, URL overrideURL)
548 super(classloader, url, overrideURL);
549 helper = SharedLibHelper.findHelper(classloader, url.getFile(),
550 noCertCodeSource, true);
553 Class getClass(String className)
555 return helper.findClass(className);
558 Resource getResource(String name)
560 URL url = helper.findResource(name);
561 if (url == null)
562 return null;
563 return new SoResource(this, name, url);
567 final static class SoResource extends Resource
569 SoResource(SoURLLoader loader, String name, URL url)
571 super(loader, name);
572 this.url = url;
575 InputStream getInputStream() throws IOException
577 URLConnection conn = url.openConnection();
578 return conn.getInputStream();
581 public int getLength()
583 // FIXME we could find this by asking the core object.
584 return -1;
587 public URL getURL ()
589 return url;
592 final URL url;
596 * A <code>FileURLLoader</code> is a type of <code>URLLoader</code>
597 * only loading from file url.
599 static final class FileURLLoader extends URLLoader
601 File dir; //the file for this file url
603 FileURLLoader(URLClassLoader classloader, URL url)
605 super(classloader, url);
606 dir = new File(baseURL.getFile());
609 /** get resource with the name "name" in the file url */
610 Resource getResource(String name)
612 File file = new File(dir, name);
613 if (file.exists() && ! file.isDirectory())
614 return new FileResource(this, name, file);
615 return null;
619 static final class FileResource extends Resource
621 final File file;
623 FileResource(FileURLLoader loader, String name, File file)
625 super(loader, name);
626 this.file = file;
629 InputStream getInputStream() throws IOException
631 return new FileInputStream(file);
634 public int getLength()
636 return (int) file.length();
639 public URL getURL()
643 return new URL(loader.baseURL, name,
644 loader.classloader.getURLStreamHandler("file"));
646 catch (MalformedURLException e)
648 InternalError ie = new InternalError();
649 ie.initCause(e);
650 throw ie;
655 // Constructors
658 * Creates a URLClassLoader that gets classes from the supplied URLs.
659 * To determine if this classloader may be created the constructor of
660 * the super class (<code>SecureClassLoader</code>) is called first, which
661 * can throw a SecurityException. Then the supplied URLs are added
662 * in the order given to the URLClassLoader which uses these URLs to
663 * load classes and resources (after using the default parent ClassLoader).
665 * @exception SecurityException if the SecurityManager disallows the
666 * creation of a ClassLoader.
667 * @param urls Locations that should be searched by this ClassLoader when
668 * resolving Classes or Resources.
669 * @see SecureClassLoader
671 public URLClassLoader(URL[] urls) throws SecurityException
673 super();
674 this.factory = null;
675 this.securityContext = null;
676 addURLs(urls);
680 * Private constructor used by the static
681 * <code>newInstance(URL[])</code> method. Creates an
682 * <code>URLClassLoader</code> without any <code>URL</code>s
683 * yet. This is used to bypass the normal security check for
684 * creating classloaders, but remembers the security context which
685 * will be used when defining classes. The <code>URL</code>s to
686 * load from must be added by the <code>newInstance()</code> method
687 * in the security context of the caller.
689 * @param securityContext the security context of the unprivileged code.
691 private URLClassLoader(AccessControlContext securityContext)
693 super();
694 this.factory = null;
695 this.securityContext = securityContext;
699 * Creates a <code>URLClassLoader</code> that gets classes from the supplied
700 * <code>URL</code>s.
701 * To determine if this classloader may be created the constructor of
702 * the super class (<code>SecureClassLoader</code>) is called first, which
703 * can throw a SecurityException. Then the supplied URLs are added
704 * in the order given to the URLClassLoader which uses these URLs to
705 * load classes and resources (after using the supplied parent ClassLoader).
706 * @exception SecurityException if the SecurityManager disallows the
707 * creation of a ClassLoader.
708 * @exception SecurityException
709 * @param urls Locations that should be searched by this ClassLoader when
710 * resolving Classes or Resources.
711 * @param parent The parent class loader used before trying this class
712 * loader.
713 * @see SecureClassLoader
715 public URLClassLoader(URL[] urls, ClassLoader parent)
716 throws SecurityException
718 super(parent);
719 this.factory = null;
720 this.securityContext = null;
721 addURLs(urls);
724 // Package-private to avoid a trampoline constructor.
726 * Package-private constructor used by the static
727 * <code>newInstance(URL[])</code> method. Creates an
728 * <code>URLClassLoader</code> with the given parent but without any
729 * <code>URL</code>s yet. This is used to bypass the normal security
730 * check for creating classloaders, but remembers the security
731 * context which will be used when defining classes. The
732 * <code>URL</code>s to load from must be added by the
733 * <code>newInstance()</code> method in the security context of the
734 * caller.
736 * @param securityContext the security context of the unprivileged code.
738 URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
740 super(parent);
741 this.factory = null;
742 this.securityContext = securityContext;
746 * Creates a URLClassLoader that gets classes from the supplied URLs.
747 * To determine if this classloader may be created the constructor of
748 * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
749 * can throw a SecurityException. Then the supplied URLs are added
750 * in the order given to the URLClassLoader which uses these URLs to
751 * load classes and resources (after using the supplied parent ClassLoader).
752 * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
753 * protocol handlers of the supplied URLs.
754 * @exception SecurityException if the SecurityManager disallows the
755 * creation of a ClassLoader.
756 * @exception SecurityException
757 * @param urls Locations that should be searched by this ClassLoader when
758 * resolving Classes or Resources.
759 * @param parent The parent class loader used before trying this class
760 * loader.
761 * @param factory Used to get the protocol handler for the URLs.
762 * @see SecureClassLoader
764 public URLClassLoader(URL[] urls, ClassLoader parent,
765 URLStreamHandlerFactory factory)
766 throws SecurityException
768 super(parent);
769 this.securityContext = null;
770 this.factory = factory;
771 addURLs(urls);
773 // If this factory is still not in factoryCache, add it,
774 // since we only support three protocols so far, 5 is enough
775 // for cache initial size
776 synchronized (factoryCache)
778 if (factory != null && factoryCache.get(factory) == null)
779 factoryCache.put(factory, new HashMap(5));
783 // Methods
786 * Adds a new location to the end of the internal URL store.
787 * @param newUrl the location to add
789 protected void addURL(URL newUrl)
791 urls.add(newUrl);
792 addURLImpl(newUrl);
795 private void addURLImpl(URL newUrl)
797 synchronized (urlloaders)
799 if (newUrl == null)
800 return; // Silently ignore...
802 // Check global cache to see if there're already url loader
803 // for this url.
804 URLLoader loader = (URLLoader) urlloaders.get(newUrl);
805 if (loader == null)
807 String file = newUrl.getFile();
808 String protocol = newUrl.getProtocol();
810 // Check that it is not a directory
811 if ("gcjlib".equals(protocol))
812 loader = new SoURLLoader(this, newUrl);
813 else if (! (file.endsWith("/") || file.endsWith(File.separator)))
814 loader = new JarURLLoader(this, newUrl);
815 else if ("file".equals(protocol))
816 loader = new FileURLLoader(this, newUrl);
817 else
818 loader = new RemoteURLLoader(this, newUrl);
820 // Cache it.
821 urlloaders.put(newUrl, loader);
824 urlinfos.add(loader);
826 Vector extraUrls = loader.getClassPath();
827 if (extraUrls != null)
829 Iterator it = extraUrls.iterator();
830 while (it.hasNext())
832 URL url = (URL)it.next();
833 URLLoader extraLoader = (URLLoader) urlloaders.get(url);
834 if (! urlinfos.contains (extraLoader))
835 addURLImpl(url);
843 * Adds an array of new locations to the end of the internal URL store.
844 * @param newUrls the locations to add
846 private void addURLs(URL[] newUrls)
848 for (int i = 0; i < newUrls.length; i++)
849 addURL(newUrls[i]);
853 * Defines a Package based on the given name and the supplied manifest
854 * information. The manifest indicates the tile, version and
855 * vendor information of the specification and implementation and wheter the
856 * package is sealed. If the Manifest indicates that the package is sealed
857 * then the Package will be sealed with respect to the supplied URL.
859 * @exception IllegalArgumentException If this package name already exists
860 * in this class loader
861 * @param name The name of the package
862 * @param manifest The manifest describing the specification,
863 * implementation and sealing details of the package
864 * @param url the code source url to seal the package
865 * @return the defined Package
867 protected Package definePackage(String name, Manifest manifest, URL url)
868 throws IllegalArgumentException
870 Attributes attr = manifest.getMainAttributes();
871 String specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
872 String specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
873 String specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
874 String implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
875 String implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
876 String implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
878 // Look if the Manifest indicates that this package is sealed
879 // XXX - most likely not completely correct!
880 // Shouldn't we also check the sealed attribute of the complete jar?
881 // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
882 // But how do we get that jar manifest here?
883 String sealed = attr.getValue(Attributes.Name.SEALED);
884 if ("false".equals(sealed))
885 // make sure that the URL is null so the package is not sealed
886 url = null;
888 return definePackage(name, specTitle, specVersion, specVendor, implTitle,
889 implVersion, implVendor, url);
893 * Finds (the first) class by name from one of the locations. The locations
894 * are searched in the order they were added to the URLClassLoader.
896 * @param className the classname to find
897 * @exception ClassNotFoundException when the class could not be found or
898 * loaded
899 * @return a Class object representing the found class
901 protected Class findClass(final String className)
902 throws ClassNotFoundException
904 // Just try to find the resource by the (almost) same name
905 String resourceName = className.replace('.', '/') + ".class";
906 int max = urlinfos.size();
907 Resource resource = null;
908 for (int i = 0; i < max && resource == null; i++)
910 URLLoader loader = (URLLoader)urlinfos.elementAt(i);
911 if (loader == null)
912 continue;
914 Class k = loader.getClass(className);
915 if (k != null)
916 return k;
918 resource = loader.getResource(resourceName);
920 if (resource == null)
921 throw new ClassNotFoundException(className + " not found in " + this);
923 // Try to read the class data, create the CodeSource, Package and
924 // construct the class (and watch out for those nasty IOExceptions)
927 byte[] data;
928 InputStream in = resource.getInputStream();
929 int length = resource.getLength();
930 if (length != -1)
932 // We know the length of the data.
933 // Just try to read it in all at once
934 data = new byte[length];
935 int pos = 0;
936 while (length - pos > 0)
938 int len = in.read(data, pos, length - pos);
939 if (len == -1)
940 throw new EOFException("Not enough data reading from: "
941 + in);
942 pos += len;
945 else
947 // We don't know the data length.
948 // Have to read it in chunks.
949 ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
950 byte[] b = new byte[4096];
951 int l = 0;
952 while (l != -1)
954 l = in.read(b);
955 if (l != -1)
956 out.write(b, 0, l);
958 data = out.toByteArray();
960 final byte[] classData = data;
962 // Now get the CodeSource
963 final CodeSource source = resource.getCodeSource();
965 // Find out package name
966 String packageName = null;
967 int lastDot = className.lastIndexOf('.');
968 if (lastDot != -1)
969 packageName = className.substring(0, lastDot);
971 if (packageName != null && getPackage(packageName) == null)
973 // define the package
974 Manifest manifest = resource.loader.getManifest();
975 if (manifest == null)
976 definePackage(packageName, null, null, null, null, null, null,
977 null);
978 else
979 definePackage(packageName, manifest, resource.loader.baseURL);
982 // And finally construct the class!
983 SecurityManager sm = System.getSecurityManager();
984 Class result = null;
985 if (sm != null && securityContext != null)
987 result = (Class)AccessController.doPrivileged
988 (new PrivilegedAction()
990 public Object run()
992 return defineClass(className, classData,
993 0, classData.length,
994 source);
996 }, securityContext);
998 else
999 result = defineClass(className, classData, 0, classData.length, source);
1001 super.setSigners(result, resource.getCertificates());
1002 return result;
1004 catch (IOException ioe)
1006 ClassNotFoundException cnfe;
1007 cnfe = new ClassNotFoundException(className + " not found in " + this);
1008 cnfe.initCause(ioe);
1009 throw cnfe;
1013 // Cached String representation of this URLClassLoader
1014 private String thisString;
1017 * Returns a String representation of this URLClassLoader giving the
1018 * actual Class name, the URLs that are searched and the parent
1019 * ClassLoader.
1021 public String toString()
1023 if (thisString == null)
1025 StringBuffer sb = new StringBuffer();
1026 sb.append(this.getClass().getName());
1027 sb.append("{urls=[" );
1028 URL[] thisURLs = getURLs();
1029 for (int i = 0; i < thisURLs.length; i++)
1031 sb.append(thisURLs[i]);
1032 if (i < thisURLs.length - 1)
1033 sb.append(',');
1035 sb.append(']');
1036 sb.append(", parent=");
1037 sb.append(getParent());
1038 sb.append('}');
1039 thisString = sb.toString();
1041 return thisString;
1045 * Finds the first occurrence of a resource that can be found. The locations
1046 * are searched in the order they were added to the URLClassLoader.
1048 * @param resourceName the resource name to look for
1049 * @return the URLResource for the resource if found, null otherwise
1051 private Resource findURLResource(String resourceName)
1053 int max = urlinfos.size();
1054 for (int i = 0; i < max; i++)
1056 URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1057 if (loader == null)
1058 continue;
1060 Resource resource = loader.getResource(resourceName);
1061 if (resource != null)
1062 return resource;
1064 return null;
1068 * Finds the first occurrence of a resource that can be found.
1070 * @param resourceName the resource name to look for
1071 * @return the URL if found, null otherwise
1073 public URL findResource(String resourceName)
1075 Resource resource = findURLResource(resourceName);
1076 if (resource != null)
1077 return resource.getURL();
1079 // Resource not found
1080 return null;
1084 * If the URLStreamHandlerFactory has been set this return the appropriate
1085 * URLStreamHandler for the given protocol, if not set returns null.
1087 * @param protocol the protocol for which we need a URLStreamHandler
1088 * @return the appropriate URLStreamHandler or null
1090 URLStreamHandler getURLStreamHandler(String protocol)
1092 if (factory == null)
1093 return null;
1095 URLStreamHandler handler;
1096 synchronized (factoryCache)
1098 // Check if there're handler for the same protocol in cache.
1099 HashMap cache = (HashMap) factoryCache.get(factory);
1100 handler = (URLStreamHandler) cache.get(protocol);
1101 if (handler == null)
1103 // Add it to cache.
1104 handler = factory.createURLStreamHandler(protocol);
1105 cache.put(protocol, handler);
1108 return handler;
1112 * Finds all the resources with a particular name from all the locations.
1114 * @exception IOException when an error occurs accessing one of the
1115 * locations
1116 * @param resourceName the name of the resource to lookup
1117 * @return a (possible empty) enumeration of URLs where the resource can be
1118 * found
1120 public Enumeration findResources(String resourceName)
1121 throws IOException
1123 Vector resources = new Vector();
1124 int max = urlinfos.size();
1125 for (int i = 0; i < max; i++)
1127 URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1128 Resource resource = loader.getResource(resourceName);
1129 if (resource != null)
1130 resources.add(resource.getURL());
1132 return resources.elements();
1136 * Returns the permissions needed to access a particular code
1137 * source. These permissions includes those returned by
1138 * <code>SecureClassLoader.getPermissions()</code> and the actual
1139 * permissions to access the objects referenced by the URL of the
1140 * code source. The extra permissions added depend on the protocol
1141 * and file portion of the URL in the code source. If the URL has
1142 * the "file" protocol ends with a '/' character then it must be a
1143 * directory and a file Permission to read everything in that
1144 * directory and all subdirectories is added. If the URL had the
1145 * "file" protocol and doesn't end with a '/' character then it must
1146 * be a normal file and a file permission to read that file is
1147 * added. If the <code>URL</code> has any other protocol then a
1148 * socket permission to connect and accept connections from the host
1149 * portion of the URL is added.
1151 * @param source The codesource that needs the permissions to be accessed
1152 * @return the collection of permissions needed to access the code resource
1153 * @see java.security.SecureClassLoader#getPermissions()
1155 protected PermissionCollection getPermissions(CodeSource source)
1157 // XXX - This implementation does exactly as the Javadoc describes.
1158 // But maybe we should/could use URLConnection.getPermissions()?
1159 // First get the permissions that would normally be granted
1160 PermissionCollection permissions = super.getPermissions(source);
1162 // Now add any extra permissions depending on the URL location.
1163 URL url = source.getLocation();
1164 String protocol = url.getProtocol();
1165 if (protocol.equals("file"))
1167 String file = url.getFile();
1169 // If the file end in / it must be an directory.
1170 if (file.endsWith("/") || file.endsWith(File.separator))
1172 // Grant permission to read everything in that directory and
1173 // all subdirectories.
1174 permissions.add(new FilePermission(file + "-", "read"));
1176 else
1178 // It is a 'normal' file.
1179 // Grant permission to access that file.
1180 permissions.add(new FilePermission(file, "read"));
1183 else
1185 // Grant permission to connect to and accept connections from host
1186 String host = url.getHost();
1187 if (host != null)
1188 permissions.add(new SocketPermission(host, "connect,accept"));
1191 return permissions;
1195 * Returns all the locations that this class loader currently uses the
1196 * resolve classes and resource. This includes both the initially supplied
1197 * URLs as any URLs added later by the loader.
1198 * @return All the currently used URLs
1200 public URL[] getURLs()
1202 return (URL[]) urls.toArray(new URL[urls.size()]);
1206 * Creates a new instance of a <code>URLClassLoader</code> that gets
1207 * classes from the supplied <code>URL</code>s. This class loader
1208 * will have as parent the standard system class loader.
1210 * @param urls the initial URLs used to resolve classes and
1211 * resources
1213 * @return the class loader
1215 * @exception SecurityException when the calling code does not have
1216 * permission to access the given <code>URL</code>s
1218 public static URLClassLoader newInstance(URL[] urls)
1219 throws SecurityException
1221 return newInstance(urls, null);
1225 * Creates a new instance of a <code>URLClassLoader</code> that gets
1226 * classes from the supplied <code>URL</code>s and with the supplied
1227 * loader as parent class loader.
1229 * @param urls the initial URLs used to resolve classes and
1230 * resources
1231 * @param parent the parent class loader
1233 * @return the class loader
1235 * @exception SecurityException when the calling code does not have
1236 * permission to access the given <code>URL</code>s
1238 public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
1239 throws SecurityException
1241 SecurityManager sm = System.getSecurityManager();
1242 if (sm == null)
1243 return new URLClassLoader(urls, parent);
1244 else
1246 final Object securityContext = sm.getSecurityContext();
1248 // XXX - What to do with anything else then an AccessControlContext?
1249 if (! (securityContext instanceof AccessControlContext))
1250 throw new SecurityException("securityContext must be AccessControlContext: "
1251 + securityContext);
1253 URLClassLoader loader =
1254 (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction()
1256 public Object run()
1258 return new URLClassLoader(parent,
1259 (AccessControlContext) securityContext);
1262 loader.addURLs(urls);
1263 return loader;