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 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: import gnu.gcj.runtime.SharedLibHelper;
  66: import gnu.gcj.Core;
  67: import gnu.java.net.protocol.core.CoreInputStream;
  68: 
  69: /**
  70:  * A secure class loader that can load classes and resources from
  71:  * multiple locations.  Given an array of <code>URL</code>s this class
  72:  * loader will retrieve classes and resources by fetching them from
  73:  * possible remote locations.  Each <code>URL</code> is searched in
  74:  * order in which it was added.  If the file portion of the
  75:  * <code>URL</code> ends with a '/' character then it is interpreted
  76:  * as a base directory, otherwise it is interpreted as a jar file from
  77:  * which the classes/resources are resolved.
  78:  *
  79:  * <p>New instances can be created by two static
  80:  * <code>newInstance()</code> methods or by three public
  81:  * contructors. Both ways give the option to supply an initial array
  82:  * of <code>URL</code>s and (optionally) a parent classloader (that is
  83:  * different from the standard system class loader).</p>
  84:  *
  85:  * <p>Normally creating a <code>URLClassLoader</code> throws a
  86:  * <code>SecurityException</code> if a <code>SecurityManager</code> is
  87:  * installed and the <code>checkCreateClassLoader()</code> method does
  88:  * not return true.  But the <code>newInstance()</code> methods may be
  89:  * used by any code as long as it has permission to acces the given
  90:  * <code>URL</code>s.  <code>URLClassLoaders</code> created by the
  91:  * <code>newInstance()</code> methods also explicitly call the
  92:  * <code>checkPackageAccess()</code> method of
  93:  * <code>SecurityManager</code> if one is installed before trying to
  94:  * load a class.  Note that only subclasses of
  95:  * <code>URLClassLoader</code> can add new URLs after the
  96:  * URLClassLoader had been created. But it is always possible to get
  97:  * an array of all URLs that the class loader uses to resolve classes
  98:  * and resources by way of the <code>getURLs()</code> method.</p>
  99:  *
 100:  * <p>Open issues:
 101:  * <ul>
 102:  *
 103:  * <li>Should the URLClassLoader actually add the locations found in
 104:  * the manifest or is this the responsibility of some other
 105:  * loader/(sub)class?  (see <a
 106:  * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
 107:  * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
 108:  *
 109:  * <li>How does <code>definePackage()</code> and sealing work
 110:  * precisely?</li>
 111:  *
 112:  * <li>We save and use the security context (when a created by
 113:  * <code>newInstance()</code> but do we have to use it in more
 114:  * places?</li>
 115:  *
 116:  * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
 117:  *
 118:  * </ul>
 119:  * </p>
 120:  *
 121:  * @since 1.2
 122:  *
 123:  * @author Mark Wielaard (mark@klomp.org)
 124:  * @author Wu Gansha (gansha.wu@intel.com)
 125:  */
 126: public class URLClassLoader extends SecureClassLoader
 127: {
 128:   // Class Variables
 129: 
 130:   /**
 131:    * A global cache to store mappings between URLLoader and URL,
 132:    * so we can avoid do all the homework each time the same URL
 133:    * comes.
 134:    * XXX - Keeps these loaders forever which prevents garbage collection.
 135:    */
 136:   private static HashMap urlloaders = new HashMap();
 137: 
 138:   /**
 139:    * A cache to store mappings between handler factory and its
 140:    * private protocol handler cache (also a HashMap), so we can avoid
 141:    * create handlers each time the same protocol comes.
 142:    */
 143:   private static HashMap factoryCache = new HashMap(5);
 144: 
 145:   // Instance variables
 146: 
 147:   /** Locations to load classes from */
 148:   private final Vector urls = new Vector();
 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 urlinfos = new Vector();
 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:    * A <code>URLLoader</code> contains all logic to load resources from a
 172:    * given base <code>URL</code>.
 173:    */
 174:   abstract static class URLLoader
 175:   {
 176:     /**
 177:      * Our classloader to get info from if needed.
 178:      */
 179:     final URLClassLoader classloader;
 180: 
 181:     /**
 182:      * The base URL from which all resources are loaded.
 183:      */
 184:     final URL baseURL;
 185: 
 186:     /**
 187:      * A <code>CodeSource</code> without any associated certificates.
 188:      * It is common for classes to not have certificates associated
 189:      * with them.  If they come from the same <code>URLLoader</code>
 190:      * then it is safe to share the associated <code>CodeSource</code>
 191:      * between them since <code>CodeSource</code> is immutable.
 192:      */
 193:     final CodeSource noCertCodeSource;
 194: 
 195:     URLLoader(URLClassLoader classloader, URL baseURL)
 196:     {
 197:       this(classloader, baseURL, baseURL);
 198:     }
 199: 
 200:     URLLoader(URLClassLoader classloader, URL baseURL, URL overrideURL)
 201:     {
 202:       this.classloader = classloader;
 203:       this.baseURL = baseURL;
 204:       this.noCertCodeSource = new CodeSource(overrideURL, null);
 205:     }
 206: 
 207:     /**
 208:      * Returns a <code>Class</code> loaded by this
 209:      * <code>URLLoader</code>, or <code>null</code> when this loader
 210:      * either can't load the class or doesn't know how to load classes
 211:      * at all.
 212:      */
 213:     Class getClass(String className)
 214:     {
 215:       return null;
 216:     }
 217: 
 218:     /**
 219:      * Returns a <code>Resource</code> loaded by this
 220:      * <code>URLLoader</code>, or <code>null</code> when no
 221:      * <code>Resource</code> with the given name exists.
 222:      */
 223:     abstract Resource getResource(String s);
 224: 
 225:     /**
 226:      * Returns the <code>Manifest</code> associated with the
 227:      * <code>Resource</code>s loaded by this <code>URLLoader</code> or
 228:      * <code>null</code> there is no such <code>Manifest</code>.
 229:      */
 230:     Manifest getManifest()
 231:     {
 232:       return null;
 233:     }
 234: 
 235:     Vector getClassPath()
 236:     {
 237:       return null;
 238:     }
 239:   }
 240: 
 241:   /**
 242:    * A <code>Resource</code> represents a resource in some
 243:    * <code>URLLoader</code>. It also contains all information (e.g.,
 244:    * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and
 245:    * <code>InputStream</code>) that is necessary for loading resources
 246:    * and creating classes from a <code>URL</code>.
 247:    */
 248:   abstract static class Resource
 249:   {
 250:     final URLLoader loader;
 251: 
 252:     Resource(URLLoader loader)
 253:     {
 254:       this.loader = loader;
 255:     }
 256: 
 257:     /**
 258:      * Returns the non-null <code>CodeSource</code> associated with
 259:      * this resource.
 260:      */
 261:     CodeSource getCodeSource()
 262:     {
 263:       Certificate[] certs = getCertificates();
 264:       if (certs == null)
 265:         return loader.noCertCodeSource;
 266:       else
 267:         return new CodeSource(loader.baseURL, certs);
 268:     }
 269: 
 270:     /**
 271:      * Returns <code>Certificates</code> associated with this
 272:      * resource, or null when there are none.
 273:      */
 274:     Certificate[] getCertificates()
 275:     {
 276:       return null;
 277:     }
 278: 
 279:     /**
 280:      * Return a <code>URL</code> that can be used to access this resource.
 281:      */
 282:     abstract URL getURL();
 283: 
 284:     /**
 285:      * Returns the size of this <code>Resource</code> in bytes or
 286:      * <code>-1</code> when unknown.
 287:      */
 288:     abstract int getLength();
 289: 
 290:     /**
 291:      * Returns the non-null <code>InputStream</code> through which
 292:      * this resource can be loaded.
 293:      */
 294:     abstract InputStream getInputStream() throws IOException;
 295:   }
 296: 
 297:   /**
 298:    * A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
 299:    * only loading from jar url.
 300:    */
 301:   static final class JarURLLoader extends URLLoader
 302:   {
 303:     final JarFile jarfile; // The jar file for this url
 304:     final URL baseJarURL; // Base jar: url for all resources loaded from jar
 305: 
 306:     Vector classPath;    // The "Class-Path" attribute of this Jar's manifest
 307: 
 308:     public JarURLLoader(URLClassLoader classloader, URL baseURL,
 309:             URL absoluteUrl)
 310:     {
 311:       super(classloader, baseURL, absoluteUrl);
 312: 
 313:       // Cache url prefix for all resources in this jar url.
 314:       String external = baseURL.toExternalForm();
 315:       StringBuffer sb = new StringBuffer(external.length() + 6);
 316:       sb.append("jar:");
 317:       sb.append(external);
 318:       sb.append("!/");
 319:       String jarURL = sb.toString();
 320: 
 321:       this.classPath = null;
 322:       URL baseJarURL = null;
 323:       JarFile jarfile = null;
 324:       try
 325:     {
 326:       baseJarURL =
 327:         new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
 328:       
 329:       jarfile =
 330:         ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
 331:       
 332:       Manifest manifest;
 333:       Attributes attributes;
 334:       String classPathString;
 335: 
 336:       if ((manifest = jarfile.getManifest()) != null
 337:           && (attributes = manifest.getMainAttributes()) != null
 338:           && ((classPathString 
 339:            = attributes.getValue(Attributes.Name.CLASS_PATH)) 
 340:           != null))
 341:         {
 342:           this.classPath = new Vector();
 343:           
 344:           StringTokenizer st = new StringTokenizer(classPathString, " ");
 345:           while (st.hasMoreElements ()) 
 346:         {  
 347:           String e = st.nextToken ();
 348:           try
 349:             {
 350:               URL url = new URL(baseURL, e);
 351:               this.classPath.add(url);
 352:             } 
 353:           catch (java.net.MalformedURLException xx)
 354:             {
 355:               // Give up
 356:             }
 357:         }
 358:         }
 359:     }
 360:       catch (IOException ioe)
 361:         {
 362:       /* ignored */
 363:         }
 364: 
 365:       this.baseJarURL = baseJarURL;
 366:       this.jarfile = jarfile;
 367:     }
 368: 
 369:     /** get resource with the name "name" in the jar url */
 370:     Resource getResource(String name)
 371:     {
 372:       if (jarfile == null)
 373:         return null;
 374: 
 375:       if (name.startsWith("/"))
 376:         name = name.substring(1);
 377: 
 378:       JarEntry je = jarfile.getJarEntry(name);
 379:       if (je != null)
 380:         return new JarURLResource(this, name, je);
 381:       else
 382:         return null;
 383:     }
 384: 
 385:     Manifest getManifest()
 386:     {
 387:       try
 388:         {
 389:           return (jarfile == null) ? null : jarfile.getManifest();
 390:         }
 391:       catch (IOException ioe)
 392:         {
 393:           return null;
 394:         }
 395:     }
 396: 
 397:     Vector getClassPath()
 398:     {
 399:       return classPath;
 400:     }
 401:   }
 402: 
 403:   static final class JarURLResource extends Resource
 404:   {
 405:     private final JarEntry entry;
 406:     private final String name;
 407: 
 408:     JarURLResource(JarURLLoader loader, String name, JarEntry entry)
 409:     {
 410:       super(loader);
 411:       this.entry = entry;
 412:       this.name = name;
 413:     }
 414: 
 415:     InputStream getInputStream() throws IOException
 416:     {
 417:       return ((JarURLLoader) loader).jarfile.getInputStream(entry);
 418:     }
 419: 
 420:     int getLength()
 421:     {
 422:       return (int) entry.getSize();
 423:     }
 424: 
 425:     Certificate[] getCertificates()
 426:     {
 427:       // We have to get the entry from the jar file again, because the
 428:       // certificates will not be available until the entire entry has
 429:       // been read.
 430:       return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name))
 431:         .getCertificates();
 432:     }
 433: 
 434:     URL getURL()
 435:     {
 436:       try
 437:         {
 438:           return new URL(((JarURLLoader) loader).baseJarURL, name,
 439:                          loader.classloader.getURLStreamHandler("jar"));
 440:         }
 441:       catch (MalformedURLException e)
 442:         {
 443:           InternalError ie = new InternalError();
 444:           ie.initCause(e);
 445:           throw ie;
 446:         }
 447:     }
 448:   }
 449: 
 450:   /**
 451:    * Loader for remote directories.
 452:    */
 453:   static final class RemoteURLLoader extends URLLoader
 454:   {
 455:     private final String protocol;
 456: 
 457:     RemoteURLLoader(URLClassLoader classloader, URL url)
 458:     {
 459:       super(classloader, url);
 460:       protocol = url.getProtocol();
 461:     }
 462: 
 463:     /**
 464:      * Get a remote resource.
 465:      * Returns null if no such resource exists.
 466:      */
 467:     Resource getResource(String name)
 468:     {
 469:       try
 470:         {
 471:           URL url =
 472:             new URL(baseURL, name, classloader.getURLStreamHandler(protocol));
 473:           URLConnection connection = url.openConnection();
 474: 
 475:           // Open the connection and check the stream
 476:           // just to be sure it exists.
 477:           int length = connection.getContentLength();
 478:           InputStream stream = connection.getInputStream();
 479: 
 480:           // We can do some extra checking if it is a http request
 481:           if (connection instanceof HttpURLConnection)
 482:             {
 483:               int response =
 484:                 ((HttpURLConnection) connection).getResponseCode();
 485:               if (response / 100 != 2)
 486:                 return null;
 487:             }
 488: 
 489:           if (stream != null)
 490:             return new RemoteResource(this, name, url, stream, length);
 491:           else
 492:             return null;
 493:         }
 494:       catch (IOException ioe)
 495:         {
 496:           return null;
 497:         }
 498:     }
 499:   }
 500: 
 501:   /**
 502:    * A resource from some remote location.
 503:    */
 504:   static final class RemoteResource extends Resource
 505:   {
 506:     private final URL url;
 507:     private final InputStream stream;
 508:     private final int length;
 509: 
 510:     RemoteResource(RemoteURLLoader loader, String name, URL url,
 511:                    InputStream stream, int length)
 512:     {
 513:       super(loader);
 514:       this.url = url;
 515:       this.stream = stream;
 516:       this.length = length;
 517:     }
 518: 
 519:     InputStream getInputStream() throws IOException
 520:     {
 521:       return stream;
 522:     }
 523: 
 524:     public int getLength()
 525:     {
 526:       return length;
 527:     }
 528: 
 529:     public URL getURL()
 530:     {
 531:       return url;
 532:     }
 533:   }
 534: 
 535:   /**
 536:    * A <code>SoURLLoader</code> is a type of <code>URLLoader</code>
 537:    * that loads classes and resources from a shared library.
 538:    */
 539:   final static class SoURLLoader extends URLLoader
 540:   {
 541:     SharedLibHelper helper;
 542: 
 543:     SoURLLoader(URLClassLoader classloader, URL url)
 544:     {
 545:       this(classloader, url, url);
 546:     }
 547: 
 548:     SoURLLoader(URLClassLoader classloader, URL url, URL overrideURL)
 549:     {
 550:       super(classloader, url, overrideURL);
 551:       helper = SharedLibHelper.findHelper(classloader, url.getFile(),
 552:                       noCertCodeSource, true);
 553:     }
 554: 
 555:     Class getClass(String className)
 556:     {
 557:       return helper.findClass(className);
 558:     }
 559: 
 560:     Resource getResource(String name)
 561:     {
 562:       URL url = helper.findResource(name);
 563:       if (url == null)
 564:     return null;
 565:       return new SoResource(this, url);
 566:     }
 567:   }
 568: 
 569:   final static class SoResource extends Resource
 570:   {
 571:     SoResource(SoURLLoader loader, URL url)
 572:     {
 573:       super(loader);
 574:       this.url = url;
 575:     }
 576: 
 577:     InputStream getInputStream() throws IOException
 578:     {
 579:       URLConnection conn = url.openConnection();
 580:       return conn.getInputStream();
 581:     }
 582: 
 583:     public int getLength()
 584:     {
 585:       // FIXME we could find this by asking the core object.
 586:       return -1;
 587:     }
 588: 
 589:     public URL getURL ()
 590:     {
 591:       return url;
 592:     }
 593: 
 594:     final URL url;
 595:   }
 596: 
 597:   /**
 598:    * A <code>FileURLLoader</code> is a type of <code>URLLoader</code>
 599:    * only loading from file url.
 600:    */
 601:   static final class FileURLLoader extends URLLoader
 602:   {
 603:     File dir; //the file for this file url
 604: 
 605:     FileURLLoader(URLClassLoader classloader, URL url, URL absoluteUrl)
 606:     {
 607:       super(classloader, url, absoluteUrl);
 608:       dir = new File(absoluteUrl.getFile());
 609:     }
 610: 
 611:     /** get resource with the name "name" in the file url */
 612:     Resource getResource(String name)
 613:     {
 614:       try 
 615:      {
 616:        File file = new File(dir, name).getCanonicalFile();
 617:        if (file.exists() && !file.isDirectory())
 618:          return new FileResource(this, file);
 619:      }
 620:       catch (IOException e)
 621:      {
 622:        // Fall through...
 623:      }
 624:       return null;
 625:     }
 626:   }
 627: 
 628:   static final class FileResource extends Resource
 629:   {
 630:     final File file;
 631: 
 632:     FileResource(FileURLLoader loader, File file)
 633:     {
 634:       super(loader);
 635:       this.file = file;
 636:     }
 637: 
 638:     InputStream getInputStream() throws IOException
 639:     {
 640:       // Delegate to the URL content handler mechanism to retrieve an
 641:       // HTML representation of the directory listing if a directory
 642:       if (file.isDirectory())
 643:         {
 644:           URL url = getURL();
 645:           return url.openStream();
 646:         }
 647:       // Otherwise simply return a FileInputStream
 648:       return new FileInputStream(file);
 649:     }
 650: 
 651:     public int getLength()
 652:     {
 653:       // Delegate to the URL content handler mechanism to retrieve the
 654:       // length of the HTML representation of the directory listing if
 655:       // a directory, or -1 if an exception occurs opening the directory.
 656:       if (file.isDirectory())
 657:         {
 658:           URL url = getURL();
 659:           try
 660:             {
 661:               URLConnection connection = url.openConnection();
 662:               return connection.getContentLength();
 663:             }
 664:           catch (IOException e)
 665:             {
 666:               return -1;
 667:             }
 668:         }
 669:       // Otherwise simply return the file length
 670:       return (int) file.length();
 671:     }
 672: 
 673:     public URL getURL()
 674:     {
 675:       try
 676:         {
 677:           return file.toURL();
 678:         }
 679:       catch (MalformedURLException e)
 680:         {
 681:           InternalError ie = new InternalError();
 682:           ie.initCause(e);
 683:           throw ie;
 684:         }
 685:     }
 686:   }
 687: 
 688:   /**
 689:    * A <code>CoreURLLoader</code> is a type of <code>URLLoader</code>
 690:    * only loading from core url.
 691:    */
 692:   static final class CoreURLLoader extends URLLoader
 693:   {
 694:     private String dir;
 695: 
 696:     CoreURLLoader(URLClassLoader classloader, URL url)
 697:     {
 698:       super(classloader, url);
 699:       dir = baseURL.getFile();
 700:     }
 701: 
 702:     /** get resource with the name "name" in the core url */
 703:     Resource getResource(String name)
 704:     {
 705:       Core core = Core.find (dir + name);
 706:       if (core != null)
 707:         return new CoreResource(this, name, core);
 708:       return null;
 709:     }
 710:   }
 711: 
 712:   static final class CoreResource extends Resource
 713:   {
 714:     private final Core core;
 715:     private final String name;
 716: 
 717:     CoreResource(CoreURLLoader loader, String name, Core core)
 718:     {
 719:       super(loader);
 720:       this.core = core;
 721:       this.name = name;
 722:     }
 723: 
 724:     InputStream getInputStream() throws IOException
 725:     {
 726:       return new CoreInputStream(core);
 727:     }
 728: 
 729:     public int getLength()
 730:     {
 731:       return core.length;
 732:     }
 733: 
 734:     public URL getURL()
 735:     {
 736:       try
 737:         {
 738:           return new URL(loader.baseURL, name,
 739:                          loader.classloader.getURLStreamHandler("core"));
 740:         }
 741:       catch (MalformedURLException e)
 742:         {
 743:           InternalError ie = new InternalError();
 744:           ie.initCause(e);
 745:           throw ie;
 746:         }
 747:     }
 748:   }
 749: 
 750:   // Constructors
 751: 
 752:   /**
 753:    * Creates a URLClassLoader that gets classes from the supplied URLs.
 754:    * To determine if this classloader may be created the constructor of
 755:    * the super class (<code>SecureClassLoader</code>) is called first, which
 756:    * can throw a SecurityException. Then the supplied URLs are added
 757:    * in the order given to the URLClassLoader which uses these URLs to
 758:    * load classes and resources (after using the default parent ClassLoader).
 759:    *
 760:    * @param urls Locations that should be searched by this ClassLoader when
 761:    * resolving Classes or Resources.
 762:    * @exception SecurityException if the SecurityManager disallows the
 763:    * creation of a ClassLoader.
 764:    * @see SecureClassLoader
 765:    */
 766:   public URLClassLoader(URL[] urls) throws SecurityException
 767:   {
 768:     super();
 769:     this.factory = null;
 770:     this.securityContext = null;
 771:     addURLs(urls);
 772:   }
 773: 
 774:   /**
 775:    * Creates a <code>URLClassLoader</code> that gets classes from the supplied
 776:    * <code>URL</code>s.
 777:    * To determine if this classloader may be created the constructor of
 778:    * the super class (<code>SecureClassLoader</code>) is called first, which
 779:    * can throw a SecurityException. Then the supplied URLs are added
 780:    * in the order given to the URLClassLoader which uses these URLs to
 781:    * load classes and resources (after using the supplied parent ClassLoader).
 782:    * @param urls Locations that should be searched by this ClassLoader when
 783:    * resolving Classes or Resources.
 784:    * @param parent The parent class loader used before trying this class
 785:    * loader.
 786:    * @exception SecurityException if the SecurityManager disallows the
 787:    * creation of a ClassLoader.
 788:    * @exception SecurityException
 789:    * @see SecureClassLoader
 790:    */
 791:   public URLClassLoader(URL[] urls, ClassLoader parent)
 792:     throws SecurityException
 793:   {
 794:     super(parent);
 795:     this.factory = null;
 796:     this.securityContext = null;
 797:     addURLs(urls);
 798:   }
 799: 
 800:   // Package-private to avoid a trampoline constructor.
 801:   /**
 802:    * Package-private constructor used by the static
 803:    * <code>newInstance(URL[])</code> method.  Creates an
 804:    * <code>URLClassLoader</code> with the given parent but without any
 805:    * <code>URL</code>s yet. This is used to bypass the normal security
 806:    * check for creating classloaders, but remembers the security
 807:    * context which will be used when defining classes.  The
 808:    * <code>URL</code>s to load from must be added by the
 809:    * <code>newInstance()</code> method in the security context of the
 810:    * caller.
 811:    *
 812:    * @param securityContext the security context of the unprivileged code.
 813:    */
 814:   URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
 815:   {
 816:     super(parent);
 817:     this.factory = null;
 818:     this.securityContext = securityContext;
 819:   }
 820: 
 821:   /**
 822:    * Creates a URLClassLoader that gets classes from the supplied URLs.
 823:    * To determine if this classloader may be created the constructor of
 824:    * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
 825:    * can throw a SecurityException. Then the supplied URLs are added
 826:    * in the order given to the URLClassLoader which uses these URLs to
 827:    * load classes and resources (after using the supplied parent ClassLoader).
 828:    * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
 829:    * protocol handlers of the supplied URLs.
 830:    * @param urls Locations that should be searched by this ClassLoader when
 831:    * resolving Classes or Resources.
 832:    * @param parent The parent class loader used before trying this class
 833:    * loader.
 834:    * @param factory Used to get the protocol handler for the URLs.
 835:    * @exception SecurityException if the SecurityManager disallows the
 836:    * creation of a ClassLoader.
 837:    * @exception SecurityException
 838:    * @see SecureClassLoader
 839:    */
 840:   public URLClassLoader(URL[] urls, ClassLoader parent,
 841:                         URLStreamHandlerFactory factory)
 842:     throws SecurityException
 843:   {
 844:     super(parent);
 845:     this.securityContext = null;
 846:     this.factory = factory;
 847:     addURLs(urls);
 848: 
 849:     // If this factory is still not in factoryCache, add it,
 850:     //   since we only support three protocols so far, 5 is enough
 851:     //   for cache initial size
 852:     synchronized (factoryCache)
 853:       {
 854:         if (factory != null && factoryCache.get(factory) == null)
 855:           factoryCache.put(factory, new HashMap(5));
 856:       }
 857:   }
 858: 
 859:   // Methods
 860: 
 861:   /**
 862:    * Adds a new location to the end of the internal URL store.
 863:    * @param newUrl the location to add
 864:    */
 865:   protected void addURL(URL newUrl)
 866:   {
 867:     urls.add(newUrl);
 868:     addURLImpl(newUrl);
 869:   }
 870: 
 871:   private void addURLImpl(URL newUrl)
 872:   {
 873:     synchronized (this)
 874:       {
 875:         if (newUrl == null)
 876:           return; // Silently ignore...
 877: 
 878:     // Reset the toString() value.
 879:     thisString = null;
 880: 
 881:         // Check global cache to see if there're already url loader
 882:         // for this url.
 883:         URLLoader loader = (URLLoader) urlloaders.get(newUrl);
 884:         if (loader == null)
 885:           {
 886:             String file = newUrl.getFile();
 887:             String protocol = newUrl.getProtocol();
 888: 
 889:         // If we have a file: URL, we want to make it absolute
 890:         // here, before we decide whether it is really a jar.
 891:         URL absoluteURL;
 892:         if ("file".equals (protocol))
 893:           {
 894:         File dir = new File(file);
 895:         URL absUrl;
 896:         try
 897:           {
 898:             absoluteURL = dir.getCanonicalFile().toURL();
 899:           }
 900:         catch (IOException ignore)
 901:           {
 902:             try
 903:               {
 904:             absoluteURL = dir.getAbsoluteFile().toURL();
 905:               }
 906:             catch (MalformedURLException _)
 907:               {
 908:             // This really should not happen.
 909:             absoluteURL = newUrl;
 910:               }
 911:           }
 912:           }
 913:         else
 914:           {
 915:         // This doesn't hurt, and it simplifies the logic a
 916:         // little.
 917:         absoluteURL = newUrl;
 918:           }
 919: 
 920:             // Check that it is not a directory
 921:         if ("gcjlib".equals(protocol))
 922:           loader = new SoURLLoader(this, newUrl);
 923:         else if (! (file.endsWith("/") || file.endsWith(File.separator)))
 924:               loader = new JarURLLoader(this, newUrl, absoluteURL);
 925:             else if ("file".equals(protocol))
 926:           loader = new FileURLLoader(this, newUrl, absoluteURL);
 927:         else if ("core".equals(protocol))
 928:           loader = new CoreURLLoader(this, newUrl);
 929:             else
 930:               loader = new RemoteURLLoader(this, newUrl);
 931: 
 932:             // Cache it.
 933:             urlloaders.put(newUrl, loader);
 934:           }
 935: 
 936:     urlinfos.add(loader);
 937: 
 938:     Vector extraUrls = loader.getClassPath();
 939:     if (extraUrls != null)
 940:       {
 941:         Iterator it = extraUrls.iterator();
 942:         while (it.hasNext())
 943:           {
 944:         URL url = (URL)it.next();
 945:         URLLoader extraLoader = (URLLoader) urlloaders.get(url);
 946:         if (! urlinfos.contains (extraLoader))
 947:           addURLImpl(url);
 948:           }
 949:       }
 950: 
 951:       }
 952:   }
 953: 
 954:   /**
 955:    * Adds an array of new locations to the end of the internal URL
 956:    * store.  Called from the the constructors. Should not call to the
 957:    * protected addURL() method since that can be overridden and
 958:    * subclasses are not yet in a good state at this point.
 959:    * jboss 4.0.3 for example depends on this.
 960:    *
 961:    * @param newUrls the locations to add
 962:    */
 963:   private void addURLs(URL[] newUrls)
 964:   {
 965:     for (int i = 0; i < newUrls.length; i++)
 966:       {
 967:     urls.add(newUrls[i]);
 968:     addURLImpl(newUrls[i]);
 969:       }
 970:   }
 971: 
 972:   /**
 973:    * Look in both Attributes for a given value.  The first Attributes
 974:    * object, if not null, has precedence.
 975:    */
 976:   private String getAttributeValue(Attributes.Name name, Attributes first,
 977:                    Attributes second)
 978:   {
 979:     String result = null;
 980:     if (first != null)
 981:       result = first.getValue(name);
 982:     if (result == null)
 983:       result = second.getValue(name);
 984:     return result;
 985:   }
 986: 
 987:   /**
 988:    * Defines a Package based on the given name and the supplied manifest
 989:    * information. The manifest indicates the title, version and
 990:    * vendor information of the specification and implementation and whether the
 991:    * package is sealed. If the Manifest indicates that the package is sealed
 992:    * then the Package will be sealed with respect to the supplied URL.
 993:    *
 994:    * @param name The name of the package
 995:    * @param manifest The manifest describing the specification,
 996:    * implementation and sealing details of the package
 997:    * @param url the code source url to seal the package
 998:    * @return the defined Package
 999:    * @throws IllegalArgumentException If this package name already exists
1000:    * in this class loader
1001:    */
1002:   protected Package definePackage(String name, Manifest manifest, URL url)
1003:     throws IllegalArgumentException
1004:   {
1005:     // Compute the name of the package as it may appear in the
1006:     // Manifest.
1007:     StringBuffer xform = new StringBuffer(name);
1008:     for (int i = xform.length () - 1; i >= 0; --i)
1009:       if (xform.charAt(i) == '.')
1010:     xform.setCharAt(i, '/');
1011:     xform.append('/');
1012:     String xformName = xform.toString();
1013: 
1014:     Attributes entryAttr = manifest.getAttributes(xformName);
1015:     Attributes attr = manifest.getMainAttributes();
1016: 
1017:     String specTitle
1018:       = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE,
1019:               entryAttr, attr);
1020:     String specVersion
1021:       = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION,
1022:               entryAttr, attr);
1023:     String specVendor
1024:       = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR,
1025:               entryAttr, attr);
1026:     String implTitle
1027:       = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE,
1028:               entryAttr, attr);
1029:     String implVersion
1030:       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION,
1031:               entryAttr, attr);
1032:     String implVendor
1033:       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR,
1034:               entryAttr, attr);
1035: 
1036:     // Look if the Manifest indicates that this package is sealed
1037:     // XXX - most likely not completely correct!
1038:     // Shouldn't we also check the sealed attribute of the complete jar?
1039:     // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
1040:     // But how do we get that jar manifest here?
1041:     String sealed = attr.getValue(Attributes.Name.SEALED);
1042:     if ("false".equals(sealed))
1043:       // make sure that the URL is null so the package is not sealed
1044:       url = null;
1045: 
1046:     return definePackage(name,
1047:              specTitle, specVendor, specVersion,
1048:              implTitle, implVendor, implVersion,
1049:              url);
1050:   }
1051: 
1052:   /**
1053:    * Finds (the first) class by name from one of the locations. The locations
1054:    * are searched in the order they were added to the URLClassLoader.
1055:    *
1056:    * @param className the classname to find
1057:    * @exception ClassNotFoundException when the class could not be found or
1058:    * loaded
1059:    * @return a Class object representing the found class
1060:    */
1061:   protected Class findClass(final String className)
1062:     throws ClassNotFoundException
1063:   {
1064:     // Just try to find the resource by the (almost) same name
1065:     String resourceName = className.replace('.', '/') + ".class";
1066:     int max = urlinfos.size();
1067:     Resource resource = null;
1068:     for (int i = 0; i < max && resource == null; i++)
1069:       {
1070:     URLLoader loader = (URLLoader)urlinfos.elementAt(i);
1071:     if (loader == null)
1072:       continue;
1073: 
1074:     Class k = loader.getClass(className);
1075:     if (k != null)
1076:       return k;
1077: 
1078:     resource = loader.getResource(resourceName);
1079:       }
1080:     if (resource == null)
1081:       {
1082:     String message = className + " not found";
1083:     if (runtimeInitialized())
1084:       message += " in " + this;
1085:     throw new ClassNotFoundException(message);
1086:       }
1087: 
1088:     // Try to read the class data, create the CodeSource, Package and
1089:     // construct the class (and watch out for those nasty IOExceptions)
1090:     try
1091:       {
1092:     byte[] data;
1093:     InputStream in = resource.getInputStream();
1094:     try
1095:       {
1096:         int length = resource.getLength();
1097:         if (length != -1)
1098:           {
1099:         // We know the length of the data.
1100:         // Just try to read it in all at once
1101:         data = new byte[length];
1102:         int pos = 0;
1103:         while (length - pos > 0)
1104:           {
1105:             int len = in.read(data, pos, length - pos);
1106:             if (len == -1)
1107:               throw new EOFException("Not enough data reading from: "
1108:                          + in);
1109:             pos += len;
1110:           }
1111:           }
1112:         else
1113:           {
1114:         // We don't know the data length.
1115:         // Have to read it in chunks.
1116:         ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
1117:         byte[] b = new byte[4096];
1118:         int l = 0;
1119:         while (l != -1)
1120:           {
1121:             l = in.read(b);
1122:             if (l != -1)
1123:               out.write(b, 0, l);
1124:           }
1125:         data = out.toByteArray();
1126:           }
1127:       }
1128:     finally
1129:       {
1130:         in.close();
1131:       }
1132:     final byte[] classData = data;
1133: 
1134:         // Now get the CodeSource
1135:         final CodeSource source = resource.getCodeSource();
1136: 
1137:         // Find out package name
1138:         String packageName = null;
1139:         int lastDot = className.lastIndexOf('.');
1140:         if (lastDot != -1)
1141:           packageName = className.substring(0, lastDot);
1142: 
1143:         if (packageName != null && getPackage(packageName) == null)
1144:           {
1145:             // define the package
1146:             Manifest manifest = resource.loader.getManifest();
1147:             if (manifest == null)
1148:               definePackage(packageName, null, null, null, null, null, null,
1149:                             null);
1150:             else
1151:               definePackage(packageName, manifest, resource.loader.baseURL);
1152:           }
1153: 
1154:         // And finally construct the class!
1155:         SecurityManager sm = System.getSecurityManager();
1156:         Class result = null;
1157:         if (sm != null && securityContext != null)
1158:           {
1159:             result = (Class)AccessController.doPrivileged
1160:               (new PrivilegedAction()
1161:                 {
1162:                   public Object run()
1163:                   {
1164:                     return defineClass(className, classData,
1165:                                        0, classData.length,
1166:                                        source);
1167:                   }
1168:                 }, securityContext);
1169:           }
1170:         else
1171:           result = defineClass(className, classData, 0, classData.length, source);
1172: 
1173:         // Avoid NullPointerExceptions.
1174:         Certificate[] resourceCertificates = resource.getCertificates();
1175:         if(resourceCertificates != null)
1176:           super.setSigners(result, resourceCertificates);
1177:         
1178:         return result;
1179:       }
1180:     catch (IOException ioe)
1181:       {
1182:     ClassNotFoundException cnfe;
1183:     cnfe = new ClassNotFoundException(className + " not found in " + this);
1184:     cnfe.initCause(ioe);
1185:     throw cnfe;
1186:       }
1187:   }
1188:   
1189:   // Cached String representation of this URLClassLoader
1190:   private String thisString;
1191:   
1192:   /**
1193:    * Returns a String representation of this URLClassLoader giving the
1194:    * actual Class name, the URLs that are searched and the parent
1195:    * ClassLoader.
1196:    */
1197:   public String toString()
1198:   {
1199:     synchronized (this)
1200:       {
1201:     if (thisString == null)
1202:       {
1203:         StringBuffer sb = new StringBuffer();
1204:         sb.append(this.getClass().getName());
1205:         sb.append("{urls=[" );
1206:         URL[] thisURLs = getURLs();
1207:         for (int i = 0; i < thisURLs.length; i++)
1208:           {
1209:         sb.append(thisURLs[i]);
1210:         if (i < thisURLs.length - 1)
1211:           sb.append(',');
1212:           }
1213:         sb.append(']');
1214:         sb.append(", parent=");
1215:         sb.append(getParent());
1216:         sb.append('}');
1217:         thisString = sb.toString();
1218:       }
1219:     return thisString;
1220:       }
1221:   }
1222: 
1223:   /**
1224:    * Finds the first occurrence of a resource that can be found. The locations
1225:    * are searched in the order they were added to the URLClassLoader.
1226:    *
1227:    * @param resourceName the resource name to look for
1228:    * @return the URLResource for the resource if found, null otherwise
1229:    */
1230:   private Resource findURLResource(String resourceName)
1231:   {
1232:     int max = urlinfos.size();
1233:     for (int i = 0; i < max; i++)
1234:       {
1235:         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1236:         if (loader == null)
1237:           continue;
1238: 
1239:         Resource resource = loader.getResource(resourceName);
1240:         if (resource != null)
1241:           return resource;
1242:       }
1243:     return null;
1244:   }
1245: 
1246:   /**
1247:    * Finds the first occurrence of a resource that can be found.
1248:    *
1249:    * @param resourceName the resource name to look for
1250:    * @return the URL if found, null otherwise
1251:    */
1252:   public URL findResource(String resourceName)
1253:   {
1254:     Resource resource = findURLResource(resourceName);
1255:     if (resource != null)
1256:       return resource.getURL();
1257: 
1258:     // Resource not found
1259:     return null;
1260:   }
1261: 
1262:   /**
1263:    * If the URLStreamHandlerFactory has been set this return the appropriate
1264:    * URLStreamHandler for the given protocol, if not set returns null.
1265:    *
1266:    * @param protocol the protocol for which we need a URLStreamHandler
1267:    * @return the appropriate URLStreamHandler or null
1268:    */
1269:   URLStreamHandler getURLStreamHandler(String protocol)
1270:   {
1271:     if (factory == null)
1272:       return null;
1273: 
1274:     URLStreamHandler handler;
1275:     synchronized (factoryCache)
1276:       {
1277:         // Check if there're handler for the same protocol in cache.
1278:         HashMap cache = (HashMap) factoryCache.get(factory);
1279:         handler = (URLStreamHandler) cache.get(protocol);
1280:         if (handler == null)
1281:           {
1282:             // Add it to cache.
1283:             handler = factory.createURLStreamHandler(protocol);
1284:             cache.put(protocol, handler);
1285:           }
1286:       }
1287:     return handler;
1288:   }
1289: 
1290:   /**
1291:    * Finds all the resources with a particular name from all the locations.
1292:    *
1293:    * @param resourceName the name of the resource to lookup
1294:    * @return a (possible empty) enumeration of URLs where the resource can be
1295:    * found
1296:    * @exception IOException when an error occurs accessing one of the
1297:    * locations
1298:    */
1299:   public Enumeration findResources(String resourceName)
1300:     throws IOException
1301:   {
1302:     Vector resources = new Vector();
1303:     int max = urlinfos.size();
1304:     for (int i = 0; i < max; i++)
1305:       {
1306:         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1307:         Resource resource = loader.getResource(resourceName);
1308:         if (resource != null)
1309:           resources.add(resource.getURL());
1310:       }
1311:     return resources.elements();
1312:   }
1313: 
1314:   /**
1315:    * Returns the permissions needed to access a particular code
1316:    * source.  These permissions includes those returned by
1317:    * <code>SecureClassLoader.getPermissions()</code> and the actual
1318:    * permissions to access the objects referenced by the URL of the
1319:    * code source.  The extra permissions added depend on the protocol
1320:    * and file portion of the URL in the code source. If the URL has
1321:    * the "file" protocol ends with a '/' character then it must be a
1322:    * directory and a file Permission to read everything in that
1323:    * directory and all subdirectories is added. If the URL had the
1324:    * "file" protocol and doesn't end with a '/' character then it must
1325:    * be a normal file and a file permission to read that file is
1326:    * added. If the <code>URL</code> has any other protocol then a
1327:    * socket permission to connect and accept connections from the host
1328:    * portion of the URL is added.
1329:    *
1330:    * @param source The codesource that needs the permissions to be accessed
1331:    * @return the collection of permissions needed to access the code resource
1332:    * @see java.security.SecureClassLoader#getPermissions(CodeSource)
1333:    */
1334:   protected PermissionCollection getPermissions(CodeSource source)
1335:   {
1336:     // XXX - This implementation does exactly as the Javadoc describes.
1337:     // But maybe we should/could use URLConnection.getPermissions()?
1338:     // First get the permissions that would normally be granted
1339:     PermissionCollection permissions = super.getPermissions(source);
1340: 
1341:     // Now add any extra permissions depending on the URL location.
1342:     URL url = source.getLocation();
1343:     String protocol = url.getProtocol();
1344:     if (protocol.equals("file"))
1345:       {
1346:         String file = url.getFile();
1347: 
1348:         // If the file end in / it must be an directory.
1349:         if (file.endsWith("/") || file.endsWith(File.separator))
1350:           {
1351:             // Grant permission to read everything in that directory and
1352:             // all subdirectories.
1353:             permissions.add(new FilePermission(file + "-", "read"));
1354:           }
1355:         else
1356:           {
1357:             // It is a 'normal' file.
1358:             // Grant permission to access that file.
1359:             permissions.add(new FilePermission(file, "read"));
1360:           }
1361:       }
1362:     else
1363:       {
1364:         // Grant permission to connect to and accept connections from host
1365:         String host = url.getHost();
1366:         if (host != null)
1367:           permissions.add(new SocketPermission(host, "connect,accept"));
1368:       }
1369: 
1370:     return permissions;
1371:   }
1372: 
1373:   /**
1374:    * Returns all the locations that this class loader currently uses the
1375:    * resolve classes and resource. This includes both the initially supplied
1376:    * URLs as any URLs added later by the loader.
1377:    * @return All the currently used URLs
1378:    */
1379:   public URL[] getURLs()
1380:   {
1381:     return (URL[]) urls.toArray(new URL[urls.size()]);
1382:   }
1383: 
1384:   /**
1385:    * Creates a new instance of a <code>URLClassLoader</code> that gets
1386:    * classes from the supplied <code>URL</code>s. This class loader
1387:    * will have as parent the standard system class loader.
1388:    *
1389:    * @param urls the initial URLs used to resolve classes and
1390:    * resources
1391:    *
1392:    * @return the class loader
1393:    *
1394:    * @exception SecurityException when the calling code does not have
1395:    * permission to access the given <code>URL</code>s
1396:    */
1397:   public static URLClassLoader newInstance(URL[] urls)
1398:     throws SecurityException
1399:   {
1400:     return newInstance(urls, null);
1401:   }
1402: 
1403:   /**
1404:    * Creates a new instance of a <code>URLClassLoader</code> that gets
1405:    * classes from the supplied <code>URL</code>s and with the supplied
1406:    * loader as parent class loader.
1407:    *
1408:    * @param urls the initial URLs used to resolve classes and
1409:    * resources
1410:    * @param parent the parent class loader
1411:    *
1412:    * @return the class loader
1413:    *
1414:    * @exception SecurityException when the calling code does not have
1415:    * permission to access the given <code>URL</code>s
1416:    */
1417:   public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
1418:     throws SecurityException
1419:   {
1420:     SecurityManager sm = System.getSecurityManager();
1421:     if (sm == null)
1422:       return new URLClassLoader(urls, parent);
1423:     else
1424:       {
1425:         final Object securityContext = sm.getSecurityContext();
1426: 
1427:         // XXX - What to do with anything else then an AccessControlContext?
1428:         if (! (securityContext instanceof AccessControlContext))
1429:           throw new SecurityException("securityContext must be AccessControlContext: "
1430:                                       + securityContext);
1431: 
1432:         URLClassLoader loader =
1433:           (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction()
1434:               {
1435:                 public Object run()
1436:                 {
1437:                   return new URLClassLoader(parent,
1438:                                             (AccessControlContext) securityContext);
1439:                 }
1440:               });
1441:         loader.addURLs(urls);
1442:         return loader;
1443:       }
1444:   }
1445: 
1446:   /**
1447:    * Tell whether runtime initialization is complete.
1448:    *
1449:    * @return whether runtime initialization is complete.
1450:    */
1451:   private static native boolean runtimeInitialized();  
1452: }