Source for java.net.URLClassLoader

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