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