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