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