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.net.loader.FileURLLoader;
  43: import gnu.java.net.loader.JarURLLoader;
  44: import gnu.java.net.loader.RemoteURLLoader;
  45: import gnu.java.net.loader.Resource;
  46: import gnu.java.net.loader.URLLoader;
  47: import gnu.java.net.loader.URLStreamHandlerCache;
  48: 
  49: import java.io.ByteArrayOutputStream;
  50: import java.io.EOFException;
  51: import java.io.File;
  52: import java.io.FilePermission;
  53: import java.io.IOException;
  54: import java.io.InputStream;
  55: import java.lang.reflect.Constructor;
  56: import java.lang.reflect.InvocationTargetException;
  57: import java.security.AccessControlContext;
  58: import java.security.AccessController;
  59: import java.security.CodeSource;
  60: import java.security.PermissionCollection;
  61: import java.security.PrivilegedAction;
  62: import java.security.SecureClassLoader;
  63: import java.security.cert.Certificate;
  64: import java.util.ArrayList;
  65: import java.util.Enumeration;
  66: import java.util.Vector;
  67: import java.util.jar.Attributes;
  68: import java.util.jar.Manifest;
  69: 
  70: 
  71: /**
  72:  * A secure class loader that can load classes and resources from
  73:  * multiple locations.  Given an array of <code>URL</code>s this class
  74:  * loader will retrieve classes and resources by fetching them from
  75:  * possible remote locations.  Each <code>URL</code> is searched in
  76:  * order in which it was added.  If the file portion of the
  77:  * <code>URL</code> ends with a '/' character then it is interpreted
  78:  * as a base directory, otherwise it is interpreted as a jar file from
  79:  * which the classes/resources are resolved.
  80:  *
  81:  * <p>New instances can be created by two static
  82:  * <code>newInstance()</code> methods or by three public
  83:  * contructors. Both ways give the option to supply an initial array
  84:  * of <code>URL</code>s and (optionally) a parent classloader (that is
  85:  * different from the standard system class loader).</p>
  86:  *
  87:  * <p>Normally creating a <code>URLClassLoader</code> throws a
  88:  * <code>SecurityException</code> if a <code>SecurityManager</code> is
  89:  * installed and the <code>checkCreateClassLoader()</code> method does
  90:  * not return true.  But the <code>newInstance()</code> methods may be
  91:  * used by any code as long as it has permission to acces the given
  92:  * <code>URL</code>s.  <code>URLClassLoaders</code> created by the
  93:  * <code>newInstance()</code> methods also explicitly call the
  94:  * <code>checkPackageAccess()</code> method of
  95:  * <code>SecurityManager</code> if one is installed before trying to
  96:  * load a class.  Note that only subclasses of
  97:  * <code>URLClassLoader</code> can add new URLs after the
  98:  * URLClassLoader had been created. But it is always possible to get
  99:  * 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:     StringBuilder xform = new StringBuilder(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:         StringBuilder sb = new StringBuilder();
 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: }