001 /* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs
002 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
003 Free Software Foundation, Inc.
004
005 This file is part of GNU Classpath.
006
007 GNU Classpath is free software; you can redistribute it and/or modify
008 it under the terms of the GNU General Public License as published by
009 the Free Software Foundation; either version 2, or (at your option)
010 any later version.
011
012 GNU Classpath is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of
014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 General Public License for more details.
016
017 You should have received a copy of the GNU General Public License
018 along with GNU Classpath; see the file COPYING. If not, write to the
019 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020 02110-1301 USA.
021
022 Linking this library statically or dynamically with other modules is
023 making a combined work based on this library. Thus, the terms and
024 conditions of the GNU General Public License cover the whole
025 combination.
026
027 As a special exception, the copyright holders of this library give you
028 permission to link this library with independent modules to produce an
029 executable, regardless of the license terms of these independent
030 modules, and to copy and distribute the resulting executable under
031 terms of your choice, provided that you also meet, for each linked
032 independent module, the terms and conditions of the license of that
033 module. An independent module is a module which is not derived from
034 or based on this library. If you modify this library, you may extend
035 this exception to your version of the library, but you are not
036 obligated to do so. If you do not wish to do so, delete this
037 exception statement from your version. */
038
039
040 package java.net;
041
042 import gnu.java.net.loader.FileURLLoader;
043 import gnu.java.net.loader.JarURLLoader;
044 import gnu.java.net.loader.RemoteURLLoader;
045 import gnu.java.net.loader.Resource;
046 import gnu.java.net.loader.URLLoader;
047 import gnu.java.net.loader.URLStreamHandlerCache;
048
049 import java.io.ByteArrayOutputStream;
050 import java.io.EOFException;
051 import java.io.File;
052 import java.io.FilePermission;
053 import java.io.IOException;
054 import java.io.InputStream;
055 import java.lang.reflect.Constructor;
056 import java.lang.reflect.InvocationTargetException;
057 import java.security.AccessControlContext;
058 import java.security.AccessController;
059 import java.security.CodeSource;
060 import java.security.PermissionCollection;
061 import java.security.PrivilegedAction;
062 import java.security.SecureClassLoader;
063 import java.security.cert.Certificate;
064 import java.util.ArrayList;
065 import java.util.Enumeration;
066 import java.util.Vector;
067 import java.util.jar.Attributes;
068 import java.util.jar.Manifest;
069
070
071 /**
072 * A secure class loader that can load classes and resources from
073 * multiple locations. Given an array of <code>URL</code>s this class
074 * loader will retrieve classes and resources by fetching them from
075 * possible remote locations. Each <code>URL</code> is searched in
076 * order in which it was added. If the file portion of the
077 * <code>URL</code> ends with a '/' character then it is interpreted
078 * as a base directory, otherwise it is interpreted as a jar file from
079 * which the classes/resources are resolved.
080 *
081 * <p>New instances can be created by two static
082 * <code>newInstance()</code> methods or by three public
083 * contructors. Both ways give the option to supply an initial array
084 * of <code>URL</code>s and (optionally) a parent classloader (that is
085 * different from the standard system class loader).</p>
086 *
087 * <p>Normally creating a <code>URLClassLoader</code> throws a
088 * <code>SecurityException</code> if a <code>SecurityManager</code> is
089 * installed and the <code>checkCreateClassLoader()</code> method does
090 * not return true. But the <code>newInstance()</code> methods may be
091 * used by any code as long as it has permission to acces the given
092 * <code>URL</code>s. <code>URLClassLoaders</code> created by the
093 * <code>newInstance()</code> methods also explicitly call the
094 * <code>checkPackageAccess()</code> method of
095 * <code>SecurityManager</code> if one is installed before trying to
096 * load a class. Note that only subclasses of
097 * <code>URLClassLoader</code> can add new URLs after the
098 * URLClassLoader had been created. But it is always possible to get
099 * an array of all URLs that the class loader uses to resolve classes
100 * and resources by way of the <code>getURLs()</code> method.</p>
101 *
102 * <p>Open issues:
103 * <ul>
104 *
105 * <li>Should the URLClassLoader actually add the locations found in
106 * the manifest or is this the responsibility of some other
107 * loader/(sub)class? (see <a
108 * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
109 * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
110 *
111 * <li>How does <code>definePackage()</code> and sealing work
112 * precisely?</li>
113 *
114 * <li>We save and use the security context (when a created by
115 * <code>newInstance()</code> but do we have to use it in more
116 * places?</li>
117 *
118 * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
119 *
120 * </ul>
121 * </p>
122 *
123 * @since 1.2
124 *
125 * @author Mark Wielaard (mark@klomp.org)
126 * @author Wu Gansha (gansha.wu@intel.com)
127 */
128 public class URLClassLoader extends SecureClassLoader
129 {
130 // Class Variables
131
132 /**
133 * A cache to store mappings between handler factory and its
134 * private protocol handler cache (also a HashMap), so we can avoid
135 * creating handlers each time the same protocol comes.
136 */
137 private static URLStreamHandlerCache factoryCache
138 = new URLStreamHandlerCache();
139
140 /**
141 * The prefix for URL loaders.
142 */
143 private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_";
144
145 // Instance variables
146
147 /** Locations to load classes from */
148 private final Vector<URL> urls = new Vector<URL>();
149
150 /**
151 * Store pre-parsed information for each url into this vector: each
152 * element is a URL loader. A jar file has its own class-path
153 * attribute which adds to the URLs that will be searched, but this
154 * does not add to the list of urls.
155 */
156 private final Vector<URLLoader> urlinfos = new Vector<URLLoader>();
157
158 /** Factory used to get the protocol handlers of the URLs */
159 private final URLStreamHandlerFactory factory;
160
161 /**
162 * The security context when created from <code>newInstance()</code>
163 * or null when created through a normal constructor or when no
164 * <code>SecurityManager</code> was installed.
165 */
166 private final AccessControlContext securityContext;
167
168 // Helper classes
169
170 /**
171 * Creates a URLClassLoader that gets classes from the supplied URLs.
172 * To determine if this classloader may be created the constructor of
173 * the super class (<code>SecureClassLoader</code>) is called first, which
174 * can throw a SecurityException. Then the supplied URLs are added
175 * in the order given to the URLClassLoader which uses these URLs to
176 * load classes and resources (after using the default parent ClassLoader).
177 *
178 * @param urls Locations that should be searched by this ClassLoader when
179 * resolving Classes or Resources.
180 * @exception SecurityException if the SecurityManager disallows the
181 * creation of a ClassLoader.
182 * @see SecureClassLoader
183 */
184 public URLClassLoader(URL[] urls) throws SecurityException
185 {
186 super();
187 this.factory = null;
188 this.securityContext = null;
189 addURLs(urls);
190 }
191
192 /**
193 * Creates a <code>URLClassLoader</code> that gets classes from the supplied
194 * <code>URL</code>s.
195 * To determine if this classloader may be created the constructor of
196 * the super class (<code>SecureClassLoader</code>) is called first, which
197 * can throw a SecurityException. Then the supplied URLs are added
198 * in the order given to the URLClassLoader which uses these URLs to
199 * load classes and resources (after using the supplied parent ClassLoader).
200 * @param urls Locations that should be searched by this ClassLoader when
201 * resolving Classes or Resources.
202 * @param parent The parent class loader used before trying this class
203 * loader.
204 * @exception SecurityException if the SecurityManager disallows the
205 * creation of a ClassLoader.
206 * @exception SecurityException
207 * @see SecureClassLoader
208 */
209 public URLClassLoader(URL[] urls, ClassLoader parent)
210 throws SecurityException
211 {
212 super(parent);
213 this.factory = null;
214 this.securityContext = null;
215 addURLs(urls);
216 }
217
218 // Package-private to avoid a trampoline constructor.
219 /**
220 * Package-private constructor used by the static
221 * <code>newInstance(URL[])</code> method. Creates an
222 * <code>URLClassLoader</code> with the given parent but without any
223 * <code>URL</code>s yet. This is used to bypass the normal security
224 * check for creating classloaders, but remembers the security
225 * context which will be used when defining classes. The
226 * <code>URL</code>s to load from must be added by the
227 * <code>newInstance()</code> method in the security context of the
228 * caller.
229 *
230 * @param securityContext the security context of the unprivileged code.
231 */
232 URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
233 {
234 super(parent);
235 this.factory = null;
236 this.securityContext = securityContext;
237 }
238
239 /**
240 * Creates a URLClassLoader that gets classes from the supplied URLs.
241 * To determine if this classloader may be created the constructor of
242 * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
243 * can throw a SecurityException. Then the supplied URLs are added
244 * in the order given to the URLClassLoader which uses these URLs to
245 * load classes and resources (after using the supplied parent ClassLoader).
246 * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
247 * protocol handlers of the supplied URLs.
248 * @param urls Locations that should be searched by this ClassLoader when
249 * resolving Classes or Resources.
250 * @param parent The parent class loader used before trying this class
251 * loader.
252 * @param factory Used to get the protocol handler for the URLs.
253 * @exception SecurityException if the SecurityManager disallows the
254 * creation of a ClassLoader.
255 * @exception SecurityException
256 * @see SecureClassLoader
257 */
258 public URLClassLoader(URL[] urls, ClassLoader parent,
259 URLStreamHandlerFactory factory)
260 throws SecurityException
261 {
262 super(parent);
263 this.securityContext = null;
264 this.factory = factory;
265 // If this factory is not yet in factoryCache, add it.
266 factoryCache.add(factory);
267 addURLs(urls);
268 }
269
270 // Methods
271
272 /**
273 * Adds a new location to the end of the internal URL store.
274 * @param newUrl the location to add
275 */
276 protected void addURL(URL newUrl)
277 {
278 urls.add(newUrl);
279 addURLImpl(newUrl);
280 }
281
282 private void addURLImpl(URL newUrl)
283 {
284 synchronized (this)
285 {
286 if (newUrl == null)
287 return; // Silently ignore...
288
289 // Reset the toString() value.
290 thisString = null;
291
292 // Create a loader for this URL.
293 URLLoader loader = null;
294 String file = newUrl.getFile();
295 String protocol = newUrl.getProtocol();
296
297 // If we have a file: URL, we want to make it absolute
298 // here, before we decide whether it is really a jar.
299 URL absoluteURL;
300 if ("file".equals (protocol))
301 {
302 File dir = new File(file);
303 try
304 {
305 absoluteURL = dir.getCanonicalFile().toURL();
306 }
307 catch (IOException ignore)
308 {
309 try
310 {
311 absoluteURL = dir.getAbsoluteFile().toURL();
312 }
313 catch (MalformedURLException _)
314 {
315 // This really should not happen.
316 absoluteURL = newUrl;
317 }
318 }
319 }
320 else
321 {
322 // This doesn't hurt, and it simplifies the logic a
323 // little.
324 absoluteURL = newUrl;
325 }
326
327 // First see if we can find a handler with the correct name.
328 try
329 {
330 Class<?> handler = Class.forName(URL_LOADER_PREFIX + protocol);
331 Class<?>[] argTypes = new Class<?>[] { URLClassLoader.class,
332 URLStreamHandlerCache.class,
333 URLStreamHandlerFactory.class,
334 URL.class,
335 URL.class };
336 Constructor k = handler.getDeclaredConstructor(argTypes);
337 loader
338 = (URLLoader) k.newInstance(new Object[] { this,
339 factoryCache,
340 factory,
341 newUrl,
342 absoluteURL });
343 }
344 catch (ClassNotFoundException ignore)
345 {
346 // Fall through.
347 }
348 catch (NoSuchMethodException nsme)
349 {
350 // Programming error in the class library.
351 InternalError vme
352 = new InternalError("couldn't find URLLoader constructor");
353 vme.initCause(nsme);
354 throw vme;
355 }
356 catch (InstantiationException inste)
357 {
358 // Programming error in the class library.
359 InternalError vme
360 = new InternalError("couldn't instantiate URLLoader");
361 vme.initCause(inste);
362 throw vme;
363 }
364 catch (InvocationTargetException ite)
365 {
366 // Programming error in the class library.
367 InternalError vme
368 = new InternalError("error instantiating URLLoader");
369 vme.initCause(ite);
370 throw vme;
371 }
372 catch (IllegalAccessException illae)
373 {
374 // Programming error in the class library.
375 InternalError vme
376 = new InternalError("invalid access to URLLoader");
377 vme.initCause(illae);
378 throw vme;
379 }
380
381 if (loader == null)
382 {
383 // If it is not a directory, use the jar loader.
384 if (! (file.endsWith("/") || file.endsWith(File.separator)))
385 loader = new JarURLLoader(this, factoryCache, factory,
386 newUrl, absoluteURL);
387 else if ("file".equals(protocol))
388 loader = new FileURLLoader(this, factoryCache, factory,
389 newUrl, absoluteURL);
390 else
391 loader = new RemoteURLLoader(this, factoryCache, factory,
392 newUrl);
393 }
394
395 urlinfos.add(loader);
396 ArrayList<URLLoader> extra = loader.getClassPath();
397 if (extra != null)
398 urlinfos.addAll(extra);
399 }
400 }
401
402 /**
403 * Adds an array of new locations to the end of the internal URL
404 * store. Called from the the constructors. Should not call to the
405 * protected addURL() method since that can be overridden and
406 * subclasses are not yet in a good state at this point.
407 * jboss 4.0.3 for example depends on this.
408 *
409 * @param newUrls the locations to add
410 */
411 private void addURLs(URL[] newUrls)
412 {
413 for (int i = 0; i < newUrls.length; i++)
414 {
415 urls.add(newUrls[i]);
416 addURLImpl(newUrls[i]);
417 }
418 }
419
420 /**
421 * Look in both Attributes for a given value. The first Attributes
422 * object, if not null, has precedence.
423 */
424 private String getAttributeValue(Attributes.Name name, Attributes first,
425 Attributes second)
426 {
427 String result = null;
428 if (first != null)
429 result = first.getValue(name);
430 if (result == null)
431 result = second.getValue(name);
432 return result;
433 }
434
435 /**
436 * Defines a Package based on the given name and the supplied manifest
437 * information. The manifest indicates the title, version and
438 * vendor information of the specification and implementation and whether the
439 * package is sealed. If the Manifest indicates that the package is sealed
440 * then the Package will be sealed with respect to the supplied URL.
441 *
442 * @param name The name of the package
443 * @param manifest The manifest describing the specification,
444 * implementation and sealing details of the package
445 * @param url the code source url to seal the package
446 * @return the defined Package
447 * @throws IllegalArgumentException If this package name already exists
448 * in this class loader
449 */
450 protected Package definePackage(String name, Manifest manifest, URL url)
451 throws IllegalArgumentException
452 {
453 // Compute the name of the package as it may appear in the
454 // Manifest.
455 StringBuffer xform = new StringBuffer(name);
456 for (int i = xform.length () - 1; i >= 0; --i)
457 if (xform.charAt(i) == '.')
458 xform.setCharAt(i, '/');
459 xform.append('/');
460 String xformName = xform.toString();
461
462 Attributes entryAttr = manifest.getAttributes(xformName);
463 Attributes attr = manifest.getMainAttributes();
464
465 String specTitle
466 = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE,
467 entryAttr, attr);
468 String specVersion
469 = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION,
470 entryAttr, attr);
471 String specVendor
472 = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR,
473 entryAttr, attr);
474 String implTitle
475 = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE,
476 entryAttr, attr);
477 String implVersion
478 = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION,
479 entryAttr, attr);
480 String implVendor
481 = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR,
482 entryAttr, attr);
483
484 // Look if the Manifest indicates that this package is sealed
485 // XXX - most likely not completely correct!
486 // Shouldn't we also check the sealed attribute of the complete jar?
487 // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
488 // But how do we get that jar manifest here?
489 String sealed = attr.getValue(Attributes.Name.SEALED);
490 if ("false".equals(sealed))
491 // make sure that the URL is null so the package is not sealed
492 url = null;
493
494 return definePackage(name,
495 specTitle, specVendor, specVersion,
496 implTitle, implVendor, implVersion,
497 url);
498 }
499
500 /**
501 * Finds (the first) class by name from one of the locations. The locations
502 * are searched in the order they were added to the URLClassLoader.
503 *
504 * @param className the classname to find
505 * @exception ClassNotFoundException when the class could not be found or
506 * loaded
507 * @return a Class object representing the found class
508 */
509 protected Class<?> findClass(final String className)
510 throws ClassNotFoundException
511 {
512 // Just try to find the resource by the (almost) same name
513 String resourceName = className.replace('.', '/') + ".class";
514 int max = urlinfos.size();
515 Resource resource = null;
516 for (int i = 0; i < max && resource == null; i++)
517 {
518 URLLoader loader = (URLLoader)urlinfos.elementAt(i);
519 if (loader == null)
520 continue;
521
522 Class k = loader.getClass(className);
523 if (k != null)
524 return k;
525
526 resource = loader.getResource(resourceName);
527 }
528 if (resource == null)
529 throw new ClassNotFoundException(className + " not found in " + this);
530
531 // Try to read the class data, create the CodeSource, Package and
532 // construct the class (and watch out for those nasty IOExceptions)
533 try
534 {
535 byte[] data;
536 InputStream in = resource.getInputStream();
537 try
538 {
539 int length = resource.getLength();
540 if (length != -1)
541 {
542 // We know the length of the data.
543 // Just try to read it in all at once
544 data = new byte[length];
545 int pos = 0;
546 while (length - pos > 0)
547 {
548 int len = in.read(data, pos, length - pos);
549 if (len == -1)
550 throw new EOFException("Not enough data reading from: "
551 + in);
552 pos += len;
553 }
554 }
555 else
556 {
557 // We don't know the data length.
558 // Have to read it in chunks.
559 ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
560 byte[] b = new byte[4096];
561 int l = 0;
562 while (l != -1)
563 {
564 l = in.read(b);
565 if (l != -1)
566 out.write(b, 0, l);
567 }
568 data = out.toByteArray();
569 }
570 }
571 finally
572 {
573 in.close();
574 }
575 final byte[] classData = data;
576
577 // Now get the CodeSource
578 final CodeSource source = resource.getCodeSource();
579
580 // Find out package name
581 String packageName = null;
582 int lastDot = className.lastIndexOf('.');
583 if (lastDot != -1)
584 packageName = className.substring(0, lastDot);
585
586 if (packageName != null && getPackage(packageName) == null)
587 {
588 // define the package
589 Manifest manifest = resource.getLoader().getManifest();
590 if (manifest == null)
591 definePackage(packageName, null, null, null, null, null, null,
592 null);
593 else
594 definePackage(packageName, manifest,
595 resource.getLoader().getBaseURL());
596 }
597
598 // And finally construct the class!
599 SecurityManager sm = System.getSecurityManager();
600 Class result = null;
601 if (sm != null && securityContext != null)
602 {
603 result = AccessController.doPrivileged
604 (new PrivilegedAction<Class>()
605 {
606 public Class run()
607 {
608 return defineClass(className, classData,
609 0, classData.length,
610 source);
611 }
612 }, securityContext);
613 }
614 else
615 result = defineClass(className, classData, 0, classData.length, source);
616
617 // Avoid NullPointerExceptions.
618 Certificate[] resourceCertificates = resource.getCertificates();
619 if(resourceCertificates != null)
620 super.setSigners(result, resourceCertificates);
621
622 return result;
623 }
624 catch (IOException ioe)
625 {
626 throw new ClassNotFoundException(className + " not found in " + this, ioe);
627 }
628 }
629
630 // Cached String representation of this URLClassLoader
631 private String thisString;
632
633 /**
634 * Returns a String representation of this URLClassLoader giving the
635 * actual Class name, the URLs that are searched and the parent
636 * ClassLoader.
637 */
638 public String toString()
639 {
640 synchronized (this)
641 {
642 if (thisString == null)
643 {
644 StringBuffer sb = new StringBuffer();
645 sb.append(this.getClass().getName());
646 sb.append("{urls=[" );
647 URL[] thisURLs = getURLs();
648 for (int i = 0; i < thisURLs.length; i++)
649 {
650 sb.append(thisURLs[i]);
651 if (i < thisURLs.length - 1)
652 sb.append(',');
653 }
654 sb.append(']');
655 sb.append(", parent=");
656 sb.append(getParent());
657 sb.append('}');
658 thisString = sb.toString();
659 }
660 return thisString;
661 }
662 }
663
664 /**
665 * Finds the first occurrence of a resource that can be found. The locations
666 * are searched in the order they were added to the URLClassLoader.
667 *
668 * @param resourceName the resource name to look for
669 * @return the URLResource for the resource if found, null otherwise
670 */
671 private Resource findURLResource(String resourceName)
672 {
673 int max = urlinfos.size();
674 for (int i = 0; i < max; i++)
675 {
676 URLLoader loader = (URLLoader) urlinfos.elementAt(i);
677 if (loader == null)
678 continue;
679
680 Resource resource = loader.getResource(resourceName);
681 if (resource != null)
682 return resource;
683 }
684 return null;
685 }
686
687 /**
688 * Finds the first occurrence of a resource that can be found.
689 *
690 * @param resourceName the resource name to look for
691 * @return the URL if found, null otherwise
692 */
693 public URL findResource(String resourceName)
694 {
695 Resource resource = findURLResource(resourceName);
696 if (resource != null)
697 return resource.getURL();
698
699 // Resource not found
700 return null;
701 }
702
703 /**
704 * Finds all the resources with a particular name from all the locations.
705 *
706 * @param resourceName the name of the resource to lookup
707 * @return a (possible empty) enumeration of URLs where the resource can be
708 * found
709 * @exception IOException when an error occurs accessing one of the
710 * locations
711 */
712 public Enumeration<URL> findResources(String resourceName)
713 throws IOException
714 {
715 Vector<URL> resources = new Vector<URL>();
716 int max = urlinfos.size();
717 for (int i = 0; i < max; i++)
718 {
719 URLLoader loader = (URLLoader) urlinfos.elementAt(i);
720 Resource resource = loader.getResource(resourceName);
721 if (resource != null)
722 resources.add(resource.getURL());
723 }
724 return resources.elements();
725 }
726
727 /**
728 * Returns the permissions needed to access a particular code
729 * source. These permissions includes those returned by
730 * <code>SecureClassLoader.getPermissions()</code> and the actual
731 * permissions to access the objects referenced by the URL of the
732 * code source. The extra permissions added depend on the protocol
733 * and file portion of the URL in the code source. If the URL has
734 * the "file" protocol ends with a '/' character then it must be a
735 * directory and a file Permission to read everything in that
736 * directory and all subdirectories is added. If the URL had the
737 * "file" protocol and doesn't end with a '/' character then it must
738 * be a normal file and a file permission to read that file is
739 * added. If the <code>URL</code> has any other protocol then a
740 * socket permission to connect and accept connections from the host
741 * portion of the URL is added.
742 *
743 * @param source The codesource that needs the permissions to be accessed
744 * @return the collection of permissions needed to access the code resource
745 * @see java.security.SecureClassLoader#getPermissions(CodeSource)
746 */
747 protected PermissionCollection getPermissions(CodeSource source)
748 {
749 // XXX - This implementation does exactly as the Javadoc describes.
750 // But maybe we should/could use URLConnection.getPermissions()?
751 // First get the permissions that would normally be granted
752 PermissionCollection permissions = super.getPermissions(source);
753
754 // Now add any extra permissions depending on the URL location.
755 URL url = source.getLocation();
756 String protocol = url.getProtocol();
757 if (protocol.equals("file"))
758 {
759 String file = url.getFile();
760
761 // If the file end in / it must be an directory.
762 if (file.endsWith("/") || file.endsWith(File.separator))
763 {
764 // Grant permission to read everything in that directory and
765 // all subdirectories.
766 permissions.add(new FilePermission(file + "-", "read"));
767 }
768 else
769 {
770 // It is a 'normal' file.
771 // Grant permission to access that file.
772 permissions.add(new FilePermission(file, "read"));
773 }
774 }
775 else
776 {
777 // Grant permission to connect to and accept connections from host
778 String host = url.getHost();
779 if (host != null)
780 permissions.add(new SocketPermission(host, "connect,accept"));
781 }
782
783 return permissions;
784 }
785
786 /**
787 * Returns all the locations that this class loader currently uses the
788 * resolve classes and resource. This includes both the initially supplied
789 * URLs as any URLs added later by the loader.
790 * @return All the currently used URLs
791 */
792 public URL[] getURLs()
793 {
794 return (URL[]) urls.toArray(new URL[urls.size()]);
795 }
796
797 /**
798 * Creates a new instance of a <code>URLClassLoader</code> that gets
799 * classes from the supplied <code>URL</code>s. This class loader
800 * will have as parent the standard system class loader.
801 *
802 * @param urls the initial URLs used to resolve classes and
803 * resources
804 *
805 * @return the class loader
806 *
807 * @exception SecurityException when the calling code does not have
808 * permission to access the given <code>URL</code>s
809 */
810 public static URLClassLoader newInstance(URL[] urls)
811 throws SecurityException
812 {
813 return newInstance(urls, null);
814 }
815
816 /**
817 * Creates a new instance of a <code>URLClassLoader</code> that gets
818 * classes from the supplied <code>URL</code>s and with the supplied
819 * loader as parent class loader.
820 *
821 * @param urls the initial URLs used to resolve classes and
822 * resources
823 * @param parent the parent class loader
824 *
825 * @return the class loader
826 *
827 * @exception SecurityException when the calling code does not have
828 * permission to access the given <code>URL</code>s
829 */
830 public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
831 throws SecurityException
832 {
833 SecurityManager sm = System.getSecurityManager();
834 if (sm == null)
835 return new URLClassLoader(urls, parent);
836 else
837 {
838 final Object securityContext = sm.getSecurityContext();
839
840 // XXX - What to do with anything else then an AccessControlContext?
841 if (! (securityContext instanceof AccessControlContext))
842 throw new SecurityException("securityContext must be AccessControlContext: "
843 + securityContext);
844
845 URLClassLoader loader =
846 AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>()
847 {
848 public URLClassLoader run()
849 {
850 return new URLClassLoader(parent,
851 (AccessControlContext) securityContext);
852 }
853 });
854 loader.addURLs(urls);
855 return loader;
856 }
857 }
858 }