Source code: java/net/URLClassLoader.java
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.
4
5 This file is part of GNU Classpath.
6
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.
11
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.
16
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., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
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.
26
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. */
38
39
40 package java.net;
41
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
66
67 /**
68 * A secure class loader that can load classes and resources from
69 * multiple locations. Given an array of <code>URL</code>s this class
70 * loader will retrieve classes and resources by fetching them from
71 * possible remote locations. Each <code>URL</code> is searched in
72 * order in which it was added. If the file portion of the
73 * <code>URL</code> ends with a '/' character then it is interpreted
74 * as a base directory, otherwise it is interpreted as a jar file from
75 * which the classes/resources are resolved.
76 *
77 * <p>New instances can be created by two static
78 * <code>newInstance()</code> methods or by three public
79 * contructors. Both ways give the option to supply an initial array
80 * of <code>URL</code>s and (optionally) a parent classloader (that is
81 * different from the standard system class loader).</p>
82 *
83 * <p>Normally creating a <code>URLClassLoader</code> throws a
84 * <code>SecurityException</code> if a <code>SecurityManager</code> is
85 * installed and the <code>checkCreateClassLoader()</code> method does
86 * not return true. But the <code>newInstance()</code> methods may be
87 * used by any code as long as it has permission to acces the given
88 * <code>URL</code>s. <code>URLClassLoaders</code> created by the
89 * <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>
100 *
101 * <li>Should the URLClassLoader actually add the locations found in
102 * the manifest or is this the responsibility of some other
103 * loader/(sub)class? (see <a
104 * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
105 * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
106 *
107 * <li>How does <code>definePackage()</code> and sealing work
108 * precisely?</li>
109 *
110 * <li>We save and use the security context (when a created by
111 * <code>newInstance()</code> but do we have to use it in more
112 * places?</li>
113 *
114 * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
115 *
116 * </ul>
117 * </p>
118 *
119 * @since 1.2
120 *
121 * @author Mark Wielaard (mark@klomp.org)
122 * @author Wu Gansha (gansha.wu@intel.com)
123 */
124 public class URLClassLoader extends SecureClassLoader
125 {
126 // Class Variables
127
128 /**
129 * A global cache to store mappings between URLLoader and URL,
130 * so we can avoid do all the homework each time the same URL
131 * comes.
132 * XXX - Keeps these loaders forever which prevents garbage collection.
133 */
134 private static HashMap urlloaders = new HashMap();
135
136 /**
137 * A cache to store mappings between handler factory and its
138 * private protocol handler cache (also a HashMap), so we can avoid
139 * create handlers each time the same protocol comes.
140 */
141 private static HashMap factoryCache = new HashMap(5);
142
143 // Instance variables
144
145 /** Locations to load classes from */
146 private final Vector urls = new Vector();
147
148 /**
149 * Store pre-parsed information for each url into this vector: each
150 * element is a URL loader. A jar file has its own class-path
151 * attribute which adds to the URLs that will be searched, but this
152 * does not add to the list of urls.
153 */
154 private final Vector urlinfos = new Vector();
155
156 /** Factory used to get the protocol handlers of the URLs */
157 private final URLStreamHandlerFactory factory;
158
159 /**
160 * The security context when created from <code>newInstance()</code>
161 * or null when created through a normal constructor or when no
162 * <code>SecurityManager</code> was installed.
163 */
164 private final AccessControlContext securityContext;
165
166 // Helper classes
167
168 /**
169 * A <code>URLLoader</code> contains all logic to load resources from a
170 * given base <code>URL</code>.
171 */
172 abstract static class URLLoader
173 {
174 /**
175 * Our classloader to get info from if needed.
176 */
177 final URLClassLoader classloader;
178
179 /**
180 * The base URL from which all resources are loaded.
181 */
182 final URL baseURL;
183
184 /**
185 * A <code>CodeSource</code> without any associated certificates.
186 * It is common for classes to not have certificates associated
187 * with them. If they come from the same <code>URLLoader</code>
188 * then it is safe to share the associated <code>CodeSource</code>
189 * between them since <code>CodeSource</code> is immutable.
190 */
191 final CodeSource noCertCodeSource;
192
193 URLLoader(URLClassLoader classloader, URL baseURL)
194 {
195 this(classloader, baseURL, baseURL);
196 }
197
198 URLLoader(URLClassLoader classloader, URL baseURL, URL overrideURL)
199 {
200 this.classloader = classloader;
201 this.baseURL = baseURL;
202 this.noCertCodeSource = new CodeSource(overrideURL, null);
203 }
204
205 /**
206 * Returns a <code>Resource</code> loaded by this
207 * <code>URLLoader</code>, or <code>null</code> when no
208 * <code>Resource</code> with the given name exists.
209 */
210 abstract Resource getResource(String s);
211
212 /**
213 * Returns the <code>Manifest</code> associated with the
214 * <code>Resource</code>s loaded by this <code>URLLoader</code> or
215 * <code>null</code> there is no such <code>Manifest</code>.
216 */
217 Manifest getManifest()
218 {
219 return null;
220 }
221
222 Vector getClassPath()
223 {
224 return null;
225 }
226 }
227
228 /**
229 * A <code>Resource</code> represents a resource in some
230 * <code>URLLoader</code>. It also contains all information (e.g.,
231 * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and
232 * <code>InputStream</code>) that is necessary for loading resources
233 * and creating classes from a <code>URL</code>.
234 */
235 abstract static class Resource
236 {
237 final URLLoader loader;
238 final String name;
239
240 Resource(URLLoader loader, String name)
241 {
242 this.loader = loader;
243 this.name = name;
244 }
245
246 /**
247 * Returns the non-null <code>CodeSource</code> associated with
248 * this resource.
249 */
250 CodeSource getCodeSource()
251 {
252 Certificate[] certs = getCertificates();
253 if (certs == null)
254 return loader.noCertCodeSource;
255 else
256 return new CodeSource(loader.baseURL, certs);
257 }
258
259 /**
260 * Returns <code>Certificates</code> associated with this
261 * resource, or null when there are none.
262 */
263 Certificate[] getCertificates()
264 {
265 return null;
266 }
267
268 /**
269 * Return a <code>URL</code> that can be used to access this resource.
270 */
271 abstract URL getURL();
272
273 /**
274 * Returns the size of this <code>Resource</code> in bytes or
275 * <code>-1</code> when unknown.
276 */
277 abstract int getLength();
278
279 /**
280 * Returns the non-null <code>InputStream</code> through which
281 * this resource can be loaded.
282 */
283 abstract InputStream getInputStream() throws IOException;
284 }
285
286 /**
287 * A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
288 * only loading from jar url.
289 */
290 static final class JarURLLoader extends URLLoader
291 {
292 final JarFile jarfile; // The jar file for this url
293 final URL baseJarURL; // Base jar: url for all resources loaded from jar
294
295 Vector classPath; // The "Class-Path" attribute of this Jar's manifest
296
297 public JarURLLoader(URLClassLoader classloader, URL baseURL)
298 {
299 super(classloader, baseURL);
300
301 // Cache url prefix for all resources in this jar url.
302 String external = baseURL.toExternalForm();
303 StringBuffer sb = new StringBuffer(external.length() + 6);
304 sb.append("jar:");
305 sb.append(external);
306 sb.append("!/");
307 String jarURL = sb.toString();
308
309 this.classPath = null;
310 URL baseJarURL = null;
311 JarFile jarfile = null;
312 try
313 {
314 baseJarURL =
315 new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
316
317 jarfile =
318 ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
319
320 Manifest manifest;
321 Attributes attributes;
322 String classPathString;
323
324 if ((manifest = jarfile.getManifest()) != null
325 && (attributes = manifest.getMainAttributes()) != null
326 && ((classPathString
327 = attributes.getValue(Attributes.Name.CLASS_PATH))
328 != null))
329 {
330 this.classPath = new Vector();
331
332 StringTokenizer st = new StringTokenizer(classPathString, " ");
333 while (st.hasMoreElements ())
334 {
335 String e = st.nextToken ();
336 try
337 {
338 URL url = new URL(baseURL, e);
339 this.classPath.add(url);
340 }
341 catch (java.net.MalformedURLException xx)
342 {
343 // Give up
344 }
345 }
346 }
347 }
348 catch (IOException ioe)
349 {
350 /* ignored */
351 }
352
353 this.baseJarURL = baseJarURL;
354 this.jarfile = jarfile;
355 }
356
357 /** get resource with the name "name" in the jar url */
358 Resource getResource(String name)
359 {
360 if (jarfile == null)
361 return null;
362
363 if (name.startsWith("/"))
364 name = name.substring(1);
365
366 JarEntry je = jarfile.getJarEntry(name);
367 if (je != null)
368 return new JarURLResource(this, name, je);
369 else
370 return null;
371 }
372
373 Manifest getManifest()
374 {
375 try
376 {
377 return (jarfile == null) ? null : jarfile.getManifest();
378 }
379 catch (IOException ioe)
380 {
381 return null;
382 }
383 }
384
385 Vector getClassPath()
386 {
387 return classPath;
388 }
389 }
390
391 static final class JarURLResource extends Resource
392 {
393 private final JarEntry entry;
394
395 JarURLResource(JarURLLoader loader, String name, JarEntry entry)
396 {
397 super(loader, name);
398 this.entry = entry;
399 }
400
401 InputStream getInputStream() throws IOException
402 {
403 return ((JarURLLoader) loader).jarfile.getInputStream(entry);
404 }
405
406 int getLength()
407 {
408 return (int) entry.getSize();
409 }
410
411 Certificate[] getCertificates()
412 {
413 // We have to get the entry from the jar file again, because the
414 // certificates will not be available until the entire entry has
415 // been read.
416 return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name))
417 .getCertificates();
418 }
419
420 URL getURL()
421 {
422 try
423 {
424 return new URL(((JarURLLoader) loader).baseJarURL, name,
425 loader.classloader.getURLStreamHandler("jar"));
426 }
427 catch (MalformedURLException e)
428 {
429 InternalError ie = new InternalError();
430 ie.initCause(e);
431 throw ie;
432 }
433 }
434 }
435
436 /**
437 * Loader for remote directories.
438 */
439 static final class RemoteURLLoader extends URLLoader
440 {
441 private final String protocol;
442
443 RemoteURLLoader(URLClassLoader classloader, URL url)
444 {
445 super(classloader, url);
446 protocol = url.getProtocol();
447 }
448
449 /**
450 * Get a remote resource.
451 * Returns null if no such resource exists.
452 */
453 Resource getResource(String name)
454 {
455 try
456 {
457 URL url =
458 new URL(baseURL, name, classloader.getURLStreamHandler(protocol));
459 URLConnection connection = url.openConnection();
460
461 // Open the connection and check the stream
462 // just to be sure it exists.
463 int length = connection.getContentLength();
464 InputStream stream = connection.getInputStream();
465
466 // We can do some extra checking if it is a http request
467 if (connection instanceof HttpURLConnection)
468 {
469 int response =
470 ((HttpURLConnection) connection).getResponseCode();
471 if (response / 100 != 2)
472 return null;
473 }
474
475 if (stream != null)
476 return new RemoteResource(this, name, url, stream, length);
477 else
478 return null;
479 }
480 catch (IOException ioe)
481 {
482 return null;
483 }
484 }
485 }
486
487 /**
488 * A resource from some remote location.
489 */
490 static final class RemoteResource extends Resource
491 {
492 private final URL url;
493 private final InputStream stream;
494 private final int length;
495
496 RemoteResource(RemoteURLLoader loader, String name, URL url,
497 InputStream stream, int length)
498 {
499 super(loader, name);
500 this.url = url;
501 this.stream = stream;
502 this.length = length;
503 }
504
505 InputStream getInputStream() throws IOException
506 {
507 return stream;
508 }
509
510 public int getLength()
511 {
512 return length;
513 }
514
515 public URL getURL()
516 {
517 return url;
518 }
519 }
520
521 /**
522 * A <code>FileURLLoader</code> is a type of <code>URLLoader</code>
523 * only loading from file url.
524 */
525 static final class FileURLLoader extends URLLoader
526 {
527 File dir; //the file for this file url
528
529 FileURLLoader(URLClassLoader classloader, URL url)
530 {
531 super(classloader, url);
532 dir = new File(baseURL.getFile());
533 }
534
535 /** get resource with the name "name" in the file url */
536 Resource getResource(String name)
537 {
538 File file = new File(dir, name);
539 if (file.exists() && !file.isDirectory())
540 return new FileResource(this, name, file);
541 return null;
542 }
543 }
544
545 static final class FileResource extends Resource
546 {
547 final File file;
548
549 FileResource(FileURLLoader loader, String name, File file)
550 {
551 super(loader, name);
552 this.file = file;
553 }
554
555 InputStream getInputStream() throws IOException
556 {
557 return new FileInputStream(file);
558 }
559
560 public int getLength()
561 {
562 return (int) file.length();
563 }
564
565 public URL getURL()
566 {
567 try
568 {
569 return new URL(loader.baseURL, name,
570 loader.classloader.getURLStreamHandler("file"));
571 }
572 catch (MalformedURLException e)
573 {
574 InternalError ie = new InternalError();
575 ie.initCause(e);
576 throw ie;
577 }
578 }
579 }
580
581 // Constructors
582
583 /**
584 * Creates a URLClassLoader that gets classes from the supplied URLs.
585 * To determine if this classloader may be created the constructor of
586 * the super class (<code>SecureClassLoader</code>) is called first, which
587 * can throw a SecurityException. Then the supplied URLs are added
588 * in the order given to the URLClassLoader which uses these URLs to
589 * load classes and resources (after using the default parent ClassLoader).
590 *
591 * @param urls Locations that should be searched by this ClassLoader when
592 * resolving Classes or Resources.
593 * @exception SecurityException if the SecurityManager disallows the
594 * creation of a ClassLoader.
595 * @see SecureClassLoader
596 */
597 public URLClassLoader(URL[] urls) throws SecurityException
598 {
599 super();
600 this.factory = null;
601 this.securityContext = null;
602 addURLs(urls);
603 }
604
605 /**
606 * Creates a <code>URLClassLoader</code> that gets classes from the supplied
607 * <code>URL</code>s.
608 * To determine if this classloader may be created the constructor of
609 * the super class (<code>SecureClassLoader</code>) is called first, which
610 * can throw a SecurityException. Then the supplied URLs are added
611 * in the order given to the URLClassLoader which uses these URLs to
612 * load classes and resources (after using the supplied parent ClassLoader).
613 * @param urls Locations that should be searched by this ClassLoader when
614 * resolving Classes or Resources.
615 * @param parent The parent class loader used before trying this class
616 * loader.
617 * @exception SecurityException if the SecurityManager disallows the
618 * creation of a ClassLoader.
619 * @exception SecurityException
620 * @see SecureClassLoader
621 */
622 public URLClassLoader(URL[] urls, ClassLoader parent)
623 throws SecurityException
624 {
625 super(parent);
626 this.factory = null;
627 this.securityContext = null;
628 addURLs(urls);
629 }
630
631 // Package-private to avoid a trampoline constructor.
632 /**
633 * Package-private constructor used by the static
634 * <code>newInstance(URL[])</code> method. Creates an
635 * <code>URLClassLoader</code> with the given parent but without any
636 * <code>URL</code>s yet. This is used to bypass the normal security
637 * check for creating classloaders, but remembers the security
638 * context which will be used when defining classes. The
639 * <code>URL</code>s to load from must be added by the
640 * <code>newInstance()</code> method in the security context of the
641 * caller.
642 *
643 * @param securityContext the security context of the unprivileged code.
644 */
645 URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
646 {
647 super(parent);
648 this.factory = null;
649 this.securityContext = securityContext;
650 }
651
652 /**
653 * Creates a URLClassLoader that gets classes from the supplied URLs.
654 * To determine if this classloader may be created the constructor of
655 * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
656 * can throw a SecurityException. Then the supplied URLs are added
657 * in the order given to the URLClassLoader which uses these URLs to
658 * load classes and resources (after using the supplied parent ClassLoader).
659 * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
660 * protocol handlers of the supplied URLs.
661 * @param urls Locations that should be searched by this ClassLoader when
662 * resolving Classes or Resources.
663 * @param parent The parent class loader used before trying this class
664 * loader.
665 * @param factory Used to get the protocol handler for the URLs.
666 * @exception SecurityException if the SecurityManager disallows the
667 * creation of a ClassLoader.
668 * @exception SecurityException
669 * @see SecureClassLoader
670 */
671 public URLClassLoader(URL[] urls, ClassLoader parent,
672 URLStreamHandlerFactory factory)
673 throws SecurityException
674 {
675 super(parent);
676 this.securityContext = null;
677 this.factory = factory;
678 addURLs(urls);
679
680 // If this factory is still not in factoryCache, add it,
681 // since we only support three protocols so far, 5 is enough
682 // for cache initial size
683 synchronized (factoryCache)
684 {
685 if (factory != null && factoryCache.get(factory) == null)
686 factoryCache.put(factory, new HashMap(5));
687 }
688 }
689
690 // Methods
691
692 /**
693 * Adds a new location to the end of the internal URL store.
694 * @param newUrl the location to add
695 */
696 protected void addURL(URL newUrl)
697 {
698 urls.add(newUrl);
699 addURLImpl(newUrl);
700 }
701
702 private void addURLImpl(URL newUrl)
703 {
704 synchronized (urlloaders)
705 {
706 if (newUrl == null)
707 return; // Silently ignore...
708
709 // Reset the toString() value.
710 thisString = null;
711
712 // Check global cache to see if there're already url loader
713 // for this url.
714 URLLoader loader = (URLLoader) urlloaders.get(newUrl);
715 if (loader == null)
716 {
717 String file = newUrl.getFile();
718 String protocol = newUrl.getProtocol();
719
720 // Check that it is not a directory
721 if (! (file.endsWith("/") || file.endsWith(File.separator)))
722 loader = new JarURLLoader(this, newUrl);
723 else if ("file".equals(protocol))
724 loader = new FileURLLoader(this, newUrl);
725 else
726 loader = new RemoteURLLoader(this, newUrl);
727
728 // Cache it.
729 urlloaders.put(newUrl, loader);
730 }
731
732 urlinfos.add(loader);
733
734 Vector extraUrls = loader.getClassPath();
735 if (extraUrls != null)
736 {
737 Iterator it = extraUrls.iterator();
738 while (it.hasNext())
739 {
740 URL url = (URL)it.next();
741 URLLoader extraLoader = (URLLoader) urlloaders.get(url);
742 if (! urlinfos.contains (extraLoader))
743 addURLImpl(url);
744 }
745 }
746
747 }
748 }
749
750 /**
751 * Adds an array of new locations to the end of the internal URL store.
752 * @param newUrls the locations to add
753 */
754 private void addURLs(URL[] newUrls)
755 {
756 for (int i = 0; i < newUrls.length; i++)
757 addURL(newUrls[i]);
758 }
759
760 /**
761 * Defines a Package based on the given name and the supplied manifest
762 * information. The manifest indicates the tile, version and
763 * vendor information of the specification and implementation and wheter the
764 * package is sealed. If the Manifest indicates that the package is sealed
765 * then the Package will be sealed with respect to the supplied URL.
766 *
767 * @param name The name of the package
768 * @param manifest The manifest describing the specification,
769 * implementation and sealing details of the package
770 * @param url the code source url to seal the package
771 * @exception IllegalArgumentException If this package name already exists
772 * in this class loader
773 * @return the defined Package
774 */
775 protected Package definePackage(String name, Manifest manifest, URL url)
776 throws IllegalArgumentException
777 {
778 Attributes attr = manifest.getMainAttributes();
779 String specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
780 String specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
781 String specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
782 String implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
783 String implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
784 String implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
785
786 // Look if the Manifest indicates that this package is sealed
787 // XXX - most likely not completely correct!
788 // Shouldn't we also check the sealed attribute of the complete jar?
789 // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
790 // But how do we get that jar manifest here?
791 String sealed = attr.getValue(Attributes.Name.SEALED);
792 if ("false".equals(sealed))
793 // make sure that the URL is null so the package is not sealed
794 url = null;
795
796 return definePackage(name, specTitle, specVersion, specVendor, implTitle,
797 implVersion, implVendor, url);
798 }
799
800 /**
801 * Finds (the first) class by name from one of the locations. The locations
802 * are searched in the order they were added to the URLClassLoader.
803 *
804 * @param className the classname to find
805 * @exception ClassNotFoundException when the class could not be found or
806 * loaded
807 * @return a Class object representing the found class
808 */
809 protected Class findClass(final String className)
810 throws ClassNotFoundException
811 {
812 // Just try to find the resource by the (almost) same name
813 String resourceName = className.replace('.', '/') + ".class";
814 Resource resource = findURLResource(resourceName);
815 if (resource == null)
816 throw new ClassNotFoundException(className + " not found in " + this);
817
818 // Try to read the class data, create the CodeSource, Package and
819 // construct the class (and watch out for those nasty IOExceptions)
820 try
821 {
822 byte[] data;
823 InputStream in = resource.getInputStream();
824 try
825 {
826 int length = resource.getLength();
827 if (length != -1)
828 {
829 // We know the length of the data.
830 // Just try to read it in all at once
831 data = new byte[length];
832 int pos = 0;
833 while (length - pos > 0)
834 {
835 int len = in.read(data, pos, length - pos);
836 if (len == -1)
837 throw new EOFException("Not enough data reading from: "
838 + in);
839 pos += len;
840 }
841 }
842 else
843 {
844 // We don't know the data length.
845 // Have to read it in chunks.
846 ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
847 byte[] b = new byte[4096];
848 int l = 0;
849 while (l != -1)
850 {
851 l = in.read(b);
852 if (l != -1)
853 out.write(b, 0, l);
854 }
855 data = out.toByteArray();
856 }
857 }
858 finally
859 {
860 in.close();
861 }
862 final byte[] classData = data;
863
864 // Now get the CodeSource
865 final CodeSource source = resource.getCodeSource();
866
867 // Find out package name
868 String packageName = null;
869 int lastDot = className.lastIndexOf('.');
870 if (lastDot != -1)
871 packageName = className.substring(0, lastDot);
872
873 if (packageName != null && getPackage(packageName) == null)
874 {
875 // define the package
876 Manifest manifest = resource.loader.getManifest();
877 if (manifest == null)
878 definePackage(packageName, null, null, null, null, null, null,
879 null);
880 else
881 definePackage(packageName, manifest, resource.loader.baseURL);
882 }
883
884 // And finally construct the class!
885 SecurityManager sm = System.getSecurityManager();
886 Class result = null;
887 if (sm != null && securityContext != null)
888 {
889 result = (Class)AccessController.doPrivileged
890 (new PrivilegedAction()
891 {
892 public Object run()
893 {
894 return defineClass(className, classData,
895 0, classData.length,
896 source);
897 }
898 }, securityContext);
899 }
900 else
901 result = defineClass(className, classData, 0, classData.length, source);
902
903 // Avoid NullPointerExceptions.
904 Certificate[] resourceCertificates = resource.getCertificates();
905 if(resourceCertificates != null)
906 super.setSigners(result, resourceCertificates);
907
908 return result;
909 }
910 catch (IOException ioe)
911 {
912 ClassNotFoundException cnfe;
913 cnfe = new ClassNotFoundException(className + " not found in " + this);
914 cnfe.initCause(ioe);
915 throw cnfe;
916 }
917 }
918
919 // Cached String representation of this URLClassLoader
920 private String thisString;
921
922 /**
923 * Returns a String representation of this URLClassLoader giving the
924 * actual Class name, the URLs that are searched and the parent
925 * ClassLoader.
926 */
927 public String toString()
928 {
929 synchronized (urlloaders)
930 {
931 if (thisString == null)
932 {
933 StringBuffer sb = new StringBuffer();
934 sb.append(this.getClass().getName());
935 sb.append("{urls=[" );
936 URL[] thisURLs = getURLs();
937 for (int i = 0; i < thisURLs.length; i++)
938 {
939 sb.append(thisURLs[i]);
940 if (i < thisURLs.length - 1)
941 sb.append(',');
942 }
943 sb.append(']');
944 sb.append(", parent=");
945 sb.append(getParent());
946 sb.append('}');
947 thisString = sb.toString();
948 }
949 return thisString;
950 }
951 }
952
953 /**
954 * Finds the first occurrence of a resource that can be found. The locations
955 * are searched in the order they were added to the URLClassLoader.
956 *
957 * @param resourceName the resource name to look for
958 * @return the URLResource for the resource if found, null otherwise
959 */
960 private Resource findURLResource(String resourceName)
961 {
962 int max = urlinfos.size();
963 for (int i = 0; i < max; i++)
964 {
965 URLLoader loader = (URLLoader) urlinfos.elementAt(i);
966 if (loader == null)
967 continue;
968
969 Resource resource = loader.getResource(resourceName);
970 if (resource != null)
971 return resource;
972 }
973 return null;
974 }
975
976 /**
977 * Finds the first occurrence of a resource that can be found.
978 *
979 * @param resourceName the resource name to look for
980 * @return the URL if found, null otherwise
981 */
982 public URL findResource(String resourceName)
983 {
984 Resource resource = findURLResource(resourceName);
985 if (resource != null)
986 return resource.getURL();
987
988 // Resource not found
989 return null;
990 }
991
992 /**
993 * If the URLStreamHandlerFactory has been set this return the appropriate
994 * URLStreamHandler for the given protocol, if not set returns null.
995 *
996 * @param protocol the protocol for which we need a URLStreamHandler
997 * @return the appropriate URLStreamHandler or null
998 */
999 URLStreamHandler getURLStreamHandler(String protocol)
1000 {
1001 if (factory == null)
1002 return null;
1003
1004 URLStreamHandler handler;
1005 synchronized (factoryCache)
1006 {
1007 // Check if there're handler for the same protocol in cache.
1008 HashMap cache = (HashMap) factoryCache.get(factory);
1009 handler = (URLStreamHandler) cache.get(protocol);
1010 if (handler == null)
1011 {
1012 // Add it to cache.
1013 handler = factory.createURLStreamHandler(protocol);
1014 cache.put(protocol, handler);
1015 }
1016 }
1017 return handler;
1018 }
1019
1020 /**
1021 * Finds all the resources with a particular name from all the locations.
1022 *
1023 * @param resourceName the name of the resource to lookup
1024 * @return a (possible empty) enumeration of URLs where the resource can be
1025 * found
1026 * @exception IOException when an error occurs accessing one of the
1027 * locations
1028 */
1029 public Enumeration findResources(String resourceName)
1030 throws IOException
1031 {
1032 Vector resources = new Vector();
1033 int max = urlinfos.size();
1034 for (int i = 0; i < max; i++)
1035 {
1036 URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1037 Resource resource = loader.getResource(resourceName);
1038 if (resource != null)
1039 resources.add(resource.getURL());
1040 }
1041 return resources.elements();
1042 }
1043
1044 /**
1045 * Returns the permissions needed to access a particular code
1046 * source. These permissions includes those returned by
1047 * <code>SecureClassLoader.getPermissions()</code> and the actual
1048 * permissions to access the objects referenced by the URL of the
1049 * code source. The extra permissions added depend on the protocol
1050 * and file portion of the URL in the code source. If the URL has
1051 * the "file" protocol ends with a '/' character then it must be a
1052 * directory and a file Permission to read everything in that
1053 * directory and all subdirectories is added. If the URL had the
1054 * "file" protocol and doesn't end with a '/' character then it must
1055 * be a normal file and a file permission to read that file is
1056 * added. If the <code>URL</code> has any other protocol then a
1057 * socket permission to connect and accept connections from the host
1058 * portion of the URL is added.
1059 *
1060 * @param source The codesource that needs the permissions to be accessed
1061 * @return the collection of permissions needed to access the code resource
1062 * @see java.security.SecureClassLoader#getPermissions(CodeSource)
1063 */
1064 protected PermissionCollection getPermissions(CodeSource source)
1065 {
1066 // XXX - This implementation does exactly as the Javadoc describes.
1067 // But maybe we should/could use URLConnection.getPermissions()?
1068 // First get the permissions that would normally be granted
1069 PermissionCollection permissions = super.getPermissions(source);
1070
1071 // Now add any extra permissions depending on the URL location.
1072 URL url = source.getLocation();
1073 String protocol = url.getProtocol();
1074 if (protocol.equals("file"))
1075 {
1076 String file = url.getFile();
1077
1078 // If the file end in / it must be an directory.
1079 if (file.endsWith("/") || file.endsWith(File.separator))
1080 {
1081 // Grant permission to read everything in that directory and
1082 // all subdirectories.
1083 permissions.add(new FilePermission(file + "-", "read"));
1084 }
1085 else
1086 {
1087 // It is a 'normal' file.
1088 // Grant permission to access that file.
1089 permissions.add(new FilePermission(file, "read"));
1090 }
1091 }
1092 else
1093 {
1094 // Grant permission to connect to and accept connections from host
1095 String host = url.getHost();
1096 if (host != null)
1097 permissions.add(new SocketPermission(host, "connect,accept"));
1098 }
1099
1100 return permissions;
1101 }
1102
1103 /**
1104 * Returns all the locations that this class loader currently uses the
1105 * resolve classes and resource. This includes both the initially supplied
1106 * URLs as any URLs added later by the loader.
1107 * @return All the currently used URLs
1108 */
1109 public URL[] getURLs()
1110 {
1111 return (URL[]) urls.toArray(new URL[urls.size()]);
1112 }
1113
1114 /**
1115 * Creates a new instance of a <code>URLClassLoader</code> that gets
1116 * classes from the supplied <code>URL</code>s. This class loader
1117 * will have as parent the standard system class loader.
1118 *
1119 * @param urls the initial URLs used to resolve classes and
1120 * resources
1121 *
1122 * @return the class loader
1123 *
1124 * @exception SecurityException when the calling code does not have
1125 * permission to access the given <code>URL</code>s
1126 */
1127 public static URLClassLoader newInstance(URL[] urls)
1128 throws SecurityException
1129 {
1130 return newInstance(urls, null);
1131 }
1132
1133 /**
1134 * Creates a new instance of a <code>URLClassLoader</code> that gets
1135 * classes from the supplied <code>URL</code>s and with the supplied
1136 * loader as parent class loader.
1137 *
1138 * @param urls the initial URLs used to resolve classes and
1139 * resources
1140 * @param parent the parent class loader
1141 *
1142 * @return the class loader
1143 *
1144 * @exception SecurityException when the calling code does not have
1145 * permission to access the given <code>URL</code>s
1146 */
1147 public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
1148 throws SecurityException
1149 {
1150 SecurityManager sm = System.getSecurityManager();
1151 if (sm == null)
1152 return new URLClassLoader(urls, parent);
1153 else
1154 {
1155 final Object securityContext = sm.getSecurityContext();
1156
1157 // XXX - What to do with anything else then an AccessControlContext?
1158 if (! (securityContext instanceof AccessControlContext))
1159 throw new SecurityException("securityContext must be AccessControlContext: "
1160 + securityContext);
1161
1162 URLClassLoader loader =
1163 (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction()
1164 {
1165 public Object run()
1166 {
1167 return new URLClassLoader(parent,
1168 (AccessControlContext) securityContext);
1169 }
1170 });
1171 loader.addURLs(urls);
1172 return loader;
1173 }
1174 }
1175}