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