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: 
 239:     Resource(URLLoader loader)
 240:     {
 241:       this.loader = loader;
 242:     }
 243: 
 244:     /**
 245:      * Returns the non-null <code>CodeSource</code> associated with
 246:      * this resource.
 247:      */
 248:     CodeSource getCodeSource()
 249:     {
 250:       Certificate[] certs = getCertificates();
 251:       if (certs == null)
 252:         return loader.noCertCodeSource;
 253:       else
 254:         return new CodeSource(loader.baseURL, certs);
 255:     }
 256: 
 257:     /**
 258:      * Returns <code>Certificates</code> associated with this
 259:      * resource, or null when there are none.
 260:      */
 261:     Certificate[] getCertificates()
 262:     {
 263:       return null;
 264:     }
 265: 
 266:     /**
 267:      * Return a <code>URL</code> that can be used to access this resource.
 268:      */
 269:     abstract URL getURL();
 270: 
 271:     /**
 272:      * Returns the size of this <code>Resource</code> in bytes or
 273:      * <code>-1</code> when unknown.
 274:      */
 275:     abstract int getLength();
 276: 
 277:     /**
 278:      * Returns the non-null <code>InputStream</code> through which
 279:      * this resource can be loaded.
 280:      */
 281:     abstract InputStream getInputStream() throws IOException;
 282:   }
 283: 
 284:   /**
 285:    * A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
 286:    * only loading from jar url.
 287:    */
 288:   static final class JarURLLoader extends URLLoader
 289:   {
 290:     final JarFile jarfile; // The jar file for this url
 291:     final URL baseJarURL; // Base jar: url for all resources loaded from jar
 292: 
 293:     Vector classPath;    // The "Class-Path" attribute of this Jar's manifest
 294: 
 295:     public JarURLLoader(URLClassLoader classloader, URL baseURL)
 296:     {
 297:       super(classloader, baseURL);
 298: 
 299:       // Cache url prefix for all resources in this jar url.
 300:       String external = baseURL.toExternalForm();
 301:       StringBuffer sb = new StringBuffer(external.length() + 6);
 302:       sb.append("jar:");
 303:       sb.append(external);
 304:       sb.append("!/");
 305:       String jarURL = sb.toString();
 306: 
 307:       this.classPath = null;
 308:       URL baseJarURL = null;
 309:       JarFile jarfile = null;
 310:       try
 311:     {
 312:       baseJarURL =
 313:         new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
 314:       
 315:       jarfile =
 316:         ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
 317:       
 318:       Manifest manifest;
 319:       Attributes attributes;
 320:       String classPathString;
 321: 
 322:       if ((manifest = jarfile.getManifest()) != null
 323:           && (attributes = manifest.getMainAttributes()) != null
 324:           && ((classPathString 
 325:            = attributes.getValue(Attributes.Name.CLASS_PATH)) 
 326:           != null))
 327:         {
 328:           this.classPath = new Vector();
 329:           
 330:           StringTokenizer st = new StringTokenizer(classPathString, " ");
 331:           while (st.hasMoreElements ()) 
 332:         {  
 333:           String e = st.nextToken ();
 334:           try
 335:             {
 336:               URL url = new URL(baseURL, e);
 337:               this.classPath.add(url);
 338:             } 
 339:           catch (java.net.MalformedURLException xx)
 340:             {
 341:               // Give up
 342:             }
 343:         }
 344:         }
 345:     }
 346:       catch (IOException ioe)
 347:         {
 348:       /* ignored */
 349:         }
 350: 
 351:       this.baseJarURL = baseJarURL;
 352:       this.jarfile = jarfile;
 353:     }
 354: 
 355:     /** get resource with the name "name" in the jar url */
 356:     Resource getResource(String name)
 357:     {
 358:       if (jarfile == null)
 359:         return null;
 360: 
 361:       if (name.startsWith("/"))
 362:         name = name.substring(1);
 363: 
 364:       JarEntry je = jarfile.getJarEntry(name);
 365:       if (je != null)
 366:         return new JarURLResource(this, name, je);
 367:       else
 368:         return null;
 369:     }
 370: 
 371:     Manifest getManifest()
 372:     {
 373:       try
 374:         {
 375:           return (jarfile == null) ? null : jarfile.getManifest();
 376:         }
 377:       catch (IOException ioe)
 378:         {
 379:           return null;
 380:         }
 381:     }
 382: 
 383:     Vector getClassPath()
 384:     {
 385:       return classPath;
 386:     }
 387:   }
 388: 
 389:   static final class JarURLResource extends Resource
 390:   {
 391:     private final JarEntry entry;
 392:     private final String name;
 393: 
 394:     JarURLResource(JarURLLoader loader, String name, JarEntry entry)
 395:     {
 396:       super(loader);
 397:       this.entry = entry;
 398:       this.name = name;
 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);
 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:       try 
 539:      {
 540:        File file = new File(dir, name).getCanonicalFile();
 541:        if (file.exists() && !file.isDirectory())
 542:          return new FileResource(this, file);
 543:      }
 544:       catch (IOException e)
 545:      {
 546:        // Fall through...
 547:      }
 548:       return null;
 549:     }
 550:   }
 551: 
 552:   static final class FileResource extends Resource
 553:   {
 554:     final File file;
 555: 
 556:     FileResource(FileURLLoader loader, File file)
 557:     {
 558:       super(loader);
 559:       this.file = file;
 560:     }
 561: 
 562:     InputStream getInputStream() throws IOException
 563:     {
 564:       return new FileInputStream(file);
 565:     }
 566: 
 567:     public int getLength()
 568:     {
 569:       return (int) file.length();
 570:     }
 571: 
 572:     public URL getURL()
 573:     {
 574:       try
 575:         {
 576:           return file.toURL();
 577:         }
 578:       catch (MalformedURLException e)
 579:         {
 580:           InternalError ie = new InternalError();
 581:           ie.initCause(e);
 582:           throw ie;
 583:         }
 584:     }
 585:   }
 586: 
 587:   // Constructors
 588: 
 589:   /**
 590:    * Creates a URLClassLoader that gets classes from the supplied URLs.
 591:    * To determine if this classloader may be created the constructor of
 592:    * the super class (<code>SecureClassLoader</code>) is called first, which
 593:    * can throw a SecurityException. Then the supplied URLs are added
 594:    * in the order given to the URLClassLoader which uses these URLs to
 595:    * load classes and resources (after using the default parent ClassLoader).
 596:    *
 597:    * @param urls Locations that should be searched by this ClassLoader when
 598:    * resolving Classes or Resources.
 599:    * @exception SecurityException if the SecurityManager disallows the
 600:    * creation of a ClassLoader.
 601:    * @see SecureClassLoader
 602:    */
 603:   public URLClassLoader(URL[] urls) throws SecurityException
 604:   {
 605:     super();
 606:     this.factory = null;
 607:     this.securityContext = null;
 608:     addURLs(urls);
 609:   }
 610: 
 611:   /**
 612:    * Creates a <code>URLClassLoader</code> that gets classes from the supplied
 613:    * <code>URL</code>s.
 614:    * To determine if this classloader may be created the constructor of
 615:    * the super class (<code>SecureClassLoader</code>) is called first, which
 616:    * can throw a SecurityException. Then the supplied URLs are added
 617:    * in the order given to the URLClassLoader which uses these URLs to
 618:    * load classes and resources (after using the supplied parent ClassLoader).
 619:    * @param urls Locations that should be searched by this ClassLoader when
 620:    * resolving Classes or Resources.
 621:    * @param parent The parent class loader used before trying this class
 622:    * loader.
 623:    * @exception SecurityException if the SecurityManager disallows the
 624:    * creation of a ClassLoader.
 625:    * @exception SecurityException
 626:    * @see SecureClassLoader
 627:    */
 628:   public URLClassLoader(URL[] urls, ClassLoader parent)
 629:     throws SecurityException
 630:   {
 631:     super(parent);
 632:     this.factory = null;
 633:     this.securityContext = null;
 634:     addURLs(urls);
 635:   }
 636: 
 637:   // Package-private to avoid a trampoline constructor.
 638:   /**
 639:    * Package-private constructor used by the static
 640:    * <code>newInstance(URL[])</code> method.  Creates an
 641:    * <code>URLClassLoader</code> with the given parent but without any
 642:    * <code>URL</code>s yet. This is used to bypass the normal security
 643:    * check for creating classloaders, but remembers the security
 644:    * context which will be used when defining classes.  The
 645:    * <code>URL</code>s to load from must be added by the
 646:    * <code>newInstance()</code> method in the security context of the
 647:    * caller.
 648:    *
 649:    * @param securityContext the security context of the unprivileged code.
 650:    */
 651:   URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
 652:   {
 653:     super(parent);
 654:     this.factory = null;
 655:     this.securityContext = securityContext;
 656:   }
 657: 
 658:   /**
 659:    * Creates a URLClassLoader that gets classes from the supplied URLs.
 660:    * To determine if this classloader may be created the constructor of
 661:    * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
 662:    * can throw a SecurityException. Then the supplied URLs are added
 663:    * in the order given to the URLClassLoader which uses these URLs to
 664:    * load classes and resources (after using the supplied parent ClassLoader).
 665:    * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
 666:    * protocol handlers of the supplied URLs.
 667:    * @param urls Locations that should be searched by this ClassLoader when
 668:    * resolving Classes or Resources.
 669:    * @param parent The parent class loader used before trying this class
 670:    * loader.
 671:    * @param factory Used to get the protocol handler for the URLs.
 672:    * @exception SecurityException if the SecurityManager disallows the
 673:    * creation of a ClassLoader.
 674:    * @exception SecurityException
 675:    * @see SecureClassLoader
 676:    */
 677:   public URLClassLoader(URL[] urls, ClassLoader parent,
 678:                         URLStreamHandlerFactory factory)
 679:     throws SecurityException
 680:   {
 681:     super(parent);
 682:     this.securityContext = null;
 683:     this.factory = factory;
 684:     addURLs(urls);
 685: 
 686:     // If this factory is still not in factoryCache, add it,
 687:     //   since we only support three protocols so far, 5 is enough
 688:     //   for cache initial size
 689:     synchronized (factoryCache)
 690:       {
 691:         if (factory != null && factoryCache.get(factory) == null)
 692:           factoryCache.put(factory, new HashMap(5));
 693:       }
 694:   }
 695: 
 696:   // Methods
 697: 
 698:   /**
 699:    * Adds a new location to the end of the internal URL store.
 700:    * @param newUrl the location to add
 701:    */
 702:   protected void addURL(URL newUrl)
 703:   {
 704:     urls.add(newUrl);
 705:     addURLImpl(newUrl);
 706:   }
 707: 
 708:   private void addURLImpl(URL newUrl)
 709:   {
 710:     synchronized (this)
 711:       {
 712:         if (newUrl == null)
 713:           return; // Silently ignore...
 714: 
 715:     // Reset the toString() value.
 716:     thisString = null;
 717: 
 718:         // Check global cache to see if there're already url loader
 719:         // for this url.
 720:         URLLoader loader = (URLLoader) urlloaders.get(newUrl);
 721:         if (loader == null)
 722:           {
 723:             String file = newUrl.getFile();
 724:             String protocol = newUrl.getProtocol();
 725: 
 726:             // Check that it is not a directory
 727:             if (! (file.endsWith("/") || file.endsWith(File.separator)))
 728:               loader = new JarURLLoader(this, newUrl);
 729:             else if ("file".equals(protocol))
 730:               loader = new FileURLLoader(this, newUrl);
 731:             else
 732:               loader = new RemoteURLLoader(this, newUrl);
 733: 
 734:             // Cache it.
 735:             urlloaders.put(newUrl, loader);
 736:           }
 737: 
 738:     urlinfos.add(loader);
 739: 
 740:     Vector extraUrls = loader.getClassPath();
 741:     if (extraUrls != null)
 742:       {
 743:         Iterator it = extraUrls.iterator();
 744:         while (it.hasNext())
 745:           {
 746:         URL url = (URL)it.next();
 747:         URLLoader extraLoader = (URLLoader) urlloaders.get(url);
 748:         if (! urlinfos.contains (extraLoader))
 749:           addURLImpl(url);
 750:           }
 751:       }
 752: 
 753:       }
 754:   }
 755: 
 756:   /**
 757:    * Adds an array of new locations to the end of the internal URL
 758:    * store.  Called from the the constructors. Should not call to the
 759:    * protected addURL() method since that can be overridden and
 760:    * subclasses are not yet in a good state at this point.
 761:    * jboss 4.0.3 for example depends on this.
 762:    *
 763:    * @param newUrls the locations to add
 764:    */
 765:   private void addURLs(URL[] newUrls)
 766:   {
 767:     for (int i = 0; i < newUrls.length; i++)
 768:       {
 769:     urls.add(newUrls[i]);
 770:     addURLImpl(newUrls[i]);
 771:       }
 772:   }
 773: 
 774:   /**
 775:    * Look in both Attributes for a given value.  The first Attributes
 776:    * object, if not null, has precedence.
 777:    */
 778:   private String getAttributeValue(Attributes.Name name, Attributes first,
 779:                    Attributes second)
 780:   {
 781:     String result = null;
 782:     if (first != null)
 783:       result = first.getValue(name);
 784:     if (result == null)
 785:       result = second.getValue(name);
 786:     return result;
 787:   }
 788: 
 789:   /**
 790:    * Defines a Package based on the given name and the supplied manifest
 791:    * information. The manifest indicates the title, version and
 792:    * vendor information of the specification and implementation and whether the
 793:    * package is sealed. If the Manifest indicates that the package is sealed
 794:    * then the Package will be sealed with respect to the supplied URL.
 795:    *
 796:    * @param name The name of the package
 797:    * @param manifest The manifest describing the specification,
 798:    * implementation and sealing details of the package
 799:    * @param url the code source url to seal the package
 800:    * @return the defined Package
 801:    * @throws IllegalArgumentException If this package name already exists
 802:    * in this class loader
 803:    */
 804:   protected Package definePackage(String name, Manifest manifest, URL url)
 805:     throws IllegalArgumentException
 806:   {
 807:     // Compute the name of the package as it may appear in the
 808:     // Manifest.
 809:     StringBuffer xform = new StringBuffer(name);
 810:     for (int i = xform.length () - 1; i >= 0; --i)
 811:       if (xform.charAt(i) == '.')
 812:     xform.setCharAt(i, '/');
 813:     xform.append('/');
 814:     String xformName = xform.toString();
 815: 
 816:     Attributes entryAttr = manifest.getAttributes(xformName);
 817:     Attributes attr = manifest.getMainAttributes();
 818: 
 819:     String specTitle
 820:       = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE,
 821:               entryAttr, attr);
 822:     String specVersion
 823:       = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION,
 824:               entryAttr, attr);
 825:     String specVendor
 826:       = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR,
 827:               entryAttr, attr);
 828:     String implTitle
 829:       = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE,
 830:               entryAttr, attr);
 831:     String implVersion
 832:       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION,
 833:               entryAttr, attr);
 834:     String implVendor
 835:       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR,
 836:               entryAttr, attr);
 837: 
 838:     // Look if the Manifest indicates that this package is sealed
 839:     // XXX - most likely not completely correct!
 840:     // Shouldn't we also check the sealed attribute of the complete jar?
 841:     // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
 842:     // But how do we get that jar manifest here?
 843:     String sealed = attr.getValue(Attributes.Name.SEALED);
 844:     if ("false".equals(sealed))
 845:       // make sure that the URL is null so the package is not sealed
 846:       url = null;
 847: 
 848:     return definePackage(name,
 849:              specTitle, specVendor, specVersion,
 850:              implTitle, implVendor, implVersion,
 851:              url);
 852:   }
 853: 
 854:   /**
 855:    * Finds (the first) class by name from one of the locations. The locations
 856:    * are searched in the order they were added to the URLClassLoader.
 857:    *
 858:    * @param className the classname to find
 859:    * @exception ClassNotFoundException when the class could not be found or
 860:    * loaded
 861:    * @return a Class object representing the found class
 862:    */
 863:   protected Class findClass(final String className)
 864:     throws ClassNotFoundException
 865:   {
 866:     // Just try to find the resource by the (almost) same name
 867:     String resourceName = className.replace('.', '/') + ".class";
 868:     Resource resource = findURLResource(resourceName);
 869:     if (resource == null)
 870:       throw new ClassNotFoundException(className + " not found in " + this);
 871: 
 872:     // Try to read the class data, create the CodeSource, Package and
 873:     // construct the class (and watch out for those nasty IOExceptions)
 874:     try
 875:       {
 876:     byte[] data;
 877:     InputStream in = resource.getInputStream();
 878:     try
 879:       {
 880:         int length = resource.getLength();
 881:         if (length != -1)
 882:           {
 883:         // We know the length of the data.
 884:         // Just try to read it in all at once
 885:         data = new byte[length];
 886:         int pos = 0;
 887:         while (length - pos > 0)
 888:           {
 889:             int len = in.read(data, pos, length - pos);
 890:             if (len == -1)
 891:               throw new EOFException("Not enough data reading from: "
 892:                          + in);
 893:             pos += len;
 894:           }
 895:           }
 896:         else
 897:           {
 898:         // We don't know the data length.
 899:         // Have to read it in chunks.
 900:         ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
 901:         byte[] b = new byte[4096];
 902:         int l = 0;
 903:         while (l != -1)
 904:           {
 905:             l = in.read(b);
 906:             if (l != -1)
 907:               out.write(b, 0, l);
 908:           }
 909:         data = out.toByteArray();
 910:           }
 911:       }
 912:     finally
 913:       {
 914:         in.close();
 915:       }
 916:     final byte[] classData = data;
 917: 
 918:         // Now get the CodeSource
 919:         final CodeSource source = resource.getCodeSource();
 920: 
 921:         // Find out package name
 922:         String packageName = null;
 923:         int lastDot = className.lastIndexOf('.');
 924:         if (lastDot != -1)
 925:           packageName = className.substring(0, lastDot);
 926: 
 927:         if (packageName != null && getPackage(packageName) == null)
 928:           {
 929:             // define the package
 930:             Manifest manifest = resource.loader.getManifest();
 931:             if (manifest == null)
 932:               definePackage(packageName, null, null, null, null, null, null,
 933:                             null);
 934:             else
 935:               definePackage(packageName, manifest, resource.loader.baseURL);
 936:           }
 937: 
 938:         // And finally construct the class!
 939:         SecurityManager sm = System.getSecurityManager();
 940:         Class result = null;
 941:         if (sm != null && securityContext != null)
 942:           {
 943:             result = (Class)AccessController.doPrivileged
 944:               (new PrivilegedAction()
 945:                 {
 946:                   public Object run()
 947:                   {
 948:                     return defineClass(className, classData,
 949:                                        0, classData.length,
 950:                                        source);
 951:                   }
 952:                 }, securityContext);
 953:           }
 954:         else
 955:           result = defineClass(className, classData, 0, classData.length, source);
 956: 
 957:         // Avoid NullPointerExceptions.
 958:         Certificate[] resourceCertificates = resource.getCertificates();
 959:         if(resourceCertificates != null)
 960:           super.setSigners(result, resourceCertificates);
 961:         
 962:         return result;
 963:       }
 964:     catch (IOException ioe)
 965:       {
 966:     ClassNotFoundException cnfe;
 967:     cnfe = new ClassNotFoundException(className + " not found in " + this);
 968:     cnfe.initCause(ioe);
 969:     throw cnfe;
 970:       }
 971:   }
 972:   
 973:   // Cached String representation of this URLClassLoader
 974:   private String thisString;
 975:   
 976:   /**
 977:    * Returns a String representation of this URLClassLoader giving the
 978:    * actual Class name, the URLs that are searched and the parent
 979:    * ClassLoader.
 980:    */
 981:   public String toString()
 982:   {
 983:     synchronized (this)
 984:       {
 985:     if (thisString == null)
 986:       {
 987:         StringBuffer sb = new StringBuffer();
 988:         sb.append(this.getClass().getName());
 989:         sb.append("{urls=[" );
 990:         URL[] thisURLs = getURLs();
 991:         for (int i = 0; i < thisURLs.length; i++)
 992:           {
 993:         sb.append(thisURLs[i]);
 994:         if (i < thisURLs.length - 1)
 995:           sb.append(',');
 996:           }
 997:         sb.append(']');
 998:         sb.append(", parent=");
 999:         sb.append(getParent());
1000:         sb.append('}');
1001:         thisString = sb.toString();
1002:       }
1003:     return thisString;
1004:       }
1005:   }
1006: 
1007:   /**
1008:    * Finds the first occurrence of a resource that can be found. The locations
1009:    * are searched in the order they were added to the URLClassLoader.
1010:    *
1011:    * @param resourceName the resource name to look for
1012:    * @return the URLResource for the resource if found, null otherwise
1013:    */
1014:   private Resource findURLResource(String resourceName)
1015:   {
1016:     int max = urlinfos.size();
1017:     for (int i = 0; i < max; i++)
1018:       {
1019:         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1020:         if (loader == null)
1021:           continue;
1022: 
1023:         Resource resource = loader.getResource(resourceName);
1024:         if (resource != null)
1025:           return resource;
1026:       }
1027:     return null;
1028:   }
1029: 
1030:   /**
1031:    * Finds the first occurrence of a resource that can be found.
1032:    *
1033:    * @param resourceName the resource name to look for
1034:    * @return the URL if found, null otherwise
1035:    */
1036:   public URL findResource(String resourceName)
1037:   {
1038:     Resource resource = findURLResource(resourceName);
1039:     if (resource != null)
1040:       return resource.getURL();
1041: 
1042:     // Resource not found
1043:     return null;
1044:   }
1045: 
1046:   /**
1047:    * If the URLStreamHandlerFactory has been set this return the appropriate
1048:    * URLStreamHandler for the given protocol, if not set returns null.
1049:    *
1050:    * @param protocol the protocol for which we need a URLStreamHandler
1051:    * @return the appropriate URLStreamHandler or null
1052:    */
1053:   URLStreamHandler getURLStreamHandler(String protocol)
1054:   {
1055:     if (factory == null)
1056:       return null;
1057: 
1058:     URLStreamHandler handler;
1059:     synchronized (factoryCache)
1060:       {
1061:         // Check if there're handler for the same protocol in cache.
1062:         HashMap cache = (HashMap) factoryCache.get(factory);
1063:         handler = (URLStreamHandler) cache.get(protocol);
1064:         if (handler == null)
1065:           {
1066:             // Add it to cache.
1067:             handler = factory.createURLStreamHandler(protocol);
1068:             cache.put(protocol, handler);
1069:           }
1070:       }
1071:     return handler;
1072:   }
1073: 
1074:   /**
1075:    * Finds all the resources with a particular name from all the locations.
1076:    *
1077:    * @param resourceName the name of the resource to lookup
1078:    * @return a (possible empty) enumeration of URLs where the resource can be
1079:    * found
1080:    * @exception IOException when an error occurs accessing one of the
1081:    * locations
1082:    */
1083:   public Enumeration findResources(String resourceName)
1084:     throws IOException
1085:   {
1086:     Vector resources = new Vector();
1087:     int max = urlinfos.size();
1088:     for (int i = 0; i < max; i++)
1089:       {
1090:         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1091:         Resource resource = loader.getResource(resourceName);
1092:         if (resource != null)
1093:           resources.add(resource.getURL());
1094:       }
1095:     return resources.elements();
1096:   }
1097: 
1098:   /**
1099:    * Returns the permissions needed to access a particular code
1100:    * source.  These permissions includes those returned by
1101:    * <code>SecureClassLoader.getPermissions()</code> and the actual
1102:    * permissions to access the objects referenced by the URL of the
1103:    * code source.  The extra permissions added depend on the protocol
1104:    * and file portion of the URL in the code source. If the URL has
1105:    * the "file" protocol ends with a '/' character then it must be a
1106:    * directory and a file Permission to read everything in that
1107:    * directory and all subdirectories is added. If the URL had the
1108:    * "file" protocol and doesn't end with a '/' character then it must
1109:    * be a normal file and a file permission to read that file is
1110:    * added. If the <code>URL</code> has any other protocol then a
1111:    * socket permission to connect and accept connections from the host
1112:    * portion of the URL is added.
1113:    *
1114:    * @param source The codesource that needs the permissions to be accessed
1115:    * @return the collection of permissions needed to access the code resource
1116:    * @see java.security.SecureClassLoader#getPermissions(CodeSource)
1117:    */
1118:   protected PermissionCollection getPermissions(CodeSource source)
1119:   {
1120:     // XXX - This implementation does exactly as the Javadoc describes.
1121:     // But maybe we should/could use URLConnection.getPermissions()?
1122:     // First get the permissions that would normally be granted
1123:     PermissionCollection permissions = super.getPermissions(source);
1124: 
1125:     // Now add any extra permissions depending on the URL location.
1126:     URL url = source.getLocation();
1127:     String protocol = url.getProtocol();
1128:     if (protocol.equals("file"))
1129:       {
1130:         String file = url.getFile();
1131: 
1132:         // If the file end in / it must be an directory.
1133:         if (file.endsWith("/") || file.endsWith(File.separator))
1134:           {
1135:             // Grant permission to read everything in that directory and
1136:             // all subdirectories.
1137:             permissions.add(new FilePermission(file + "-", "read"));
1138:           }
1139:         else
1140:           {
1141:             // It is a 'normal' file.
1142:             // Grant permission to access that file.
1143:             permissions.add(new FilePermission(file, "read"));
1144:           }
1145:       }
1146:     else
1147:       {
1148:         // Grant permission to connect to and accept connections from host
1149:         String host = url.getHost();
1150:         if (host != null)
1151:           permissions.add(new SocketPermission(host, "connect,accept"));
1152:       }
1153: 
1154:     return permissions;
1155:   }
1156: 
1157:   /**
1158:    * Returns all the locations that this class loader currently uses the
1159:    * resolve classes and resource. This includes both the initially supplied
1160:    * URLs as any URLs added later by the loader.
1161:    * @return All the currently used URLs
1162:    */
1163:   public URL[] getURLs()
1164:   {
1165:     return (URL[]) urls.toArray(new URL[urls.size()]);
1166:   }
1167: 
1168:   /**
1169:    * Creates a new instance of a <code>URLClassLoader</code> that gets
1170:    * classes from the supplied <code>URL</code>s. This class loader
1171:    * will have as parent the standard system class loader.
1172:    *
1173:    * @param urls the initial URLs used to resolve classes and
1174:    * resources
1175:    *
1176:    * @return the class loader
1177:    *
1178:    * @exception SecurityException when the calling code does not have
1179:    * permission to access the given <code>URL</code>s
1180:    */
1181:   public static URLClassLoader newInstance(URL[] urls)
1182:     throws SecurityException
1183:   {
1184:     return newInstance(urls, null);
1185:   }
1186: 
1187:   /**
1188:    * Creates a new instance of a <code>URLClassLoader</code> that gets
1189:    * classes from the supplied <code>URL</code>s and with the supplied
1190:    * loader as parent class loader.
1191:    *
1192:    * @param urls the initial URLs used to resolve classes and
1193:    * resources
1194:    * @param parent the parent class loader
1195:    *
1196:    * @return the class loader
1197:    *
1198:    * @exception SecurityException when the calling code does not have
1199:    * permission to access the given <code>URL</code>s
1200:    */
1201:   public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
1202:     throws SecurityException
1203:   {
1204:     SecurityManager sm = System.getSecurityManager();
1205:     if (sm == null)
1206:       return new URLClassLoader(urls, parent);
1207:     else
1208:       {
1209:         final Object securityContext = sm.getSecurityContext();
1210: 
1211:         // XXX - What to do with anything else then an AccessControlContext?
1212:         if (! (securityContext instanceof AccessControlContext))
1213:           throw new SecurityException("securityContext must be AccessControlContext: "
1214:                                       + securityContext);
1215: 
1216:         URLClassLoader loader =
1217:           (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction()
1218:               {
1219:                 public Object run()
1220:                 {
1221:                   return new URLClassLoader(parent,
1222:                                             (AccessControlContext) securityContext);
1223:                 }
1224:               });
1225:         loader.addURLs(urls);
1226:         return loader;
1227:       }
1228:   }
1229: }