GNU Classpath (0.91) | |
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, 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: }
GNU Classpath (0.91) |