001/* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs 002 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 003 Free Software Foundation, Inc. 004 005This file is part of GNU Classpath. 006 007GNU Classpath is free software; you can redistribute it and/or modify 008it under the terms of the GNU General Public License as published by 009the Free Software Foundation; either version 2, or (at your option) 010any later version. 011 012GNU Classpath is distributed in the hope that it will be useful, but 013WITHOUT ANY WARRANTY; without even the implied warranty of 014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015General Public License for more details. 016 017You should have received a copy of the GNU General Public License 018along with GNU Classpath; see the file COPYING. If not, write to the 019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02002110-1301 USA. 021 022Linking this library statically or dynamically with other modules is 023making a combined work based on this library. Thus, the terms and 024conditions of the GNU General Public License cover the whole 025combination. 026 027As a special exception, the copyright holders of this library give you 028permission to link this library with independent modules to produce an 029executable, regardless of the license terms of these independent 030modules, and to copy and distribute the resulting executable under 031terms of your choice, provided that you also meet, for each linked 032independent module, the terms and conditions of the license of that 033module. An independent module is a module which is not derived from 034or based on this library. If you modify this library, you may extend 035this exception to your version of the library, but you are not 036obligated to do so. If you do not wish to do so, delete this 037exception statement from your version. */ 038 039 040package java.net; 041 042import gnu.java.lang.CPStringBuilder; 043 044import gnu.java.net.loader.FileURLLoader; 045import gnu.java.net.loader.JarURLLoader; 046import gnu.java.net.loader.RemoteURLLoader; 047import gnu.java.net.loader.Resource; 048import gnu.java.net.loader.URLLoader; 049import gnu.java.net.loader.URLStreamHandlerCache; 050 051import java.io.ByteArrayOutputStream; 052import java.io.EOFException; 053import java.io.File; 054import java.io.FilePermission; 055import java.io.IOException; 056import java.io.InputStream; 057import java.lang.reflect.Constructor; 058import java.lang.reflect.InvocationTargetException; 059import java.security.AccessControlContext; 060import java.security.AccessController; 061import java.security.CodeSource; 062import java.security.PermissionCollection; 063import java.security.PrivilegedAction; 064import java.security.SecureClassLoader; 065import java.security.cert.Certificate; 066import java.util.ArrayList; 067import java.util.Enumeration; 068import java.util.Vector; 069import java.util.jar.Attributes; 070import java.util.jar.Manifest; 071 072 073/** 074 * A secure class loader that can load classes and resources from 075 * multiple locations. Given an array of <code>URL</code>s this class 076 * loader will retrieve classes and resources by fetching them from 077 * possible remote locations. Each <code>URL</code> is searched in 078 * order in which it was added. If the file portion of the 079 * <code>URL</code> ends with a '/' character then it is interpreted 080 * as a base directory, otherwise it is interpreted as a jar file from 081 * which the classes/resources are resolved. 082 * 083 * <p>New instances can be created by two static 084 * <code>newInstance()</code> methods or by three public 085 * contructors. Both ways give the option to supply an initial array 086 * of <code>URL</code>s and (optionally) a parent classloader (that is 087 * different from the standard system class loader).</p> 088 * 089 * <p>Normally creating a <code>URLClassLoader</code> throws a 090 * <code>SecurityException</code> if a <code>SecurityManager</code> is 091 * installed and the <code>checkCreateClassLoader()</code> method does 092 * not return true. But the <code>newInstance()</code> methods may be 093 * used by any code as long as it has permission to acces the given 094 * <code>URL</code>s. <code>URLClassLoaders</code> created by the 095 * <code>newInstance()</code> methods also explicitly call the 096 * <code>checkPackageAccess()</code> method of 097 * <code>SecurityManager</code> if one is installed before trying to 098 * load a class. Note that only subclasses of 099 * <code>URLClassLoader</code> can add new URLs after the 100 * URLClassLoader had been created. But it is always possible to get 101 * an array of all URLs that the class loader uses to resolve classes 102 * and resources by way of the <code>getURLs()</code> method.</p> 103 * 104 * <p>Open issues: 105 * <ul> 106 * 107 * <li>Should the URLClassLoader actually add the locations found in 108 * the manifest or is this the responsibility of some other 109 * loader/(sub)class? (see <a 110 * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html"> 111 * Extension Mechanism Architecture - Bundles Extensions</a>)</li> 112 * 113 * <li>How does <code>definePackage()</code> and sealing work 114 * precisely?</li> 115 * 116 * <li>We save and use the security context (when a created by 117 * <code>newInstance()</code> but do we have to use it in more 118 * places?</li> 119 * 120 * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li> 121 * 122 * </ul> 123 * </p> 124 * 125 * @since 1.2 126 * 127 * @author Mark Wielaard (mark@klomp.org) 128 * @author Wu Gansha (gansha.wu@intel.com) 129 */ 130public class URLClassLoader extends SecureClassLoader 131{ 132 // Class Variables 133 134 /** 135 * A cache to store mappings between handler factory and its 136 * private protocol handler cache (also a HashMap), so we can avoid 137 * creating handlers each time the same protocol comes. 138 */ 139 private static URLStreamHandlerCache factoryCache 140 = new URLStreamHandlerCache(); 141 142 /** 143 * The prefix for URL loaders. 144 */ 145 private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_"; 146 147 // Instance variables 148 149 /** Locations to load classes from */ 150 private final Vector<URL> urls = new Vector<URL>(); 151 152 /** 153 * Store pre-parsed information for each url into this vector: each 154 * element is a URL loader. A jar file has its own class-path 155 * attribute which adds to the URLs that will be searched, but this 156 * does not add to the list of urls. 157 */ 158 private final Vector<URLLoader> urlinfos = new Vector<URLLoader>(); 159 160 /** Factory used to get the protocol handlers of the URLs */ 161 private final URLStreamHandlerFactory factory; 162 163 /** 164 * The security context when created from <code>newInstance()</code> 165 * or null when created through a normal constructor or when no 166 * <code>SecurityManager</code> was installed. 167 */ 168 private final AccessControlContext securityContext; 169 170 // Helper classes 171 172 /** 173 * Creates a URLClassLoader that gets classes from the supplied URLs. 174 * To determine if this classloader may be created the constructor of 175 * the super class (<code>SecureClassLoader</code>) is called first, which 176 * can throw a SecurityException. Then the supplied URLs are added 177 * in the order given to the URLClassLoader which uses these URLs to 178 * load classes and resources (after using the default parent ClassLoader). 179 * 180 * @param urls Locations that should be searched by this ClassLoader when 181 * resolving Classes or Resources. 182 * @exception SecurityException if the SecurityManager disallows the 183 * creation of a ClassLoader. 184 * @see SecureClassLoader 185 */ 186 public URLClassLoader(URL[] urls) throws SecurityException 187 { 188 super(); 189 this.factory = null; 190 this.securityContext = null; 191 addURLs(urls); 192 } 193 194 /** 195 * Creates a <code>URLClassLoader</code> that gets classes from the supplied 196 * <code>URL</code>s. 197 * To determine if this classloader may be created the constructor of 198 * the super class (<code>SecureClassLoader</code>) is called first, which 199 * can throw a SecurityException. Then the supplied URLs are added 200 * in the order given to the URLClassLoader which uses these URLs to 201 * load classes and resources (after using the supplied parent ClassLoader). 202 * @param urls Locations that should be searched by this ClassLoader when 203 * resolving Classes or Resources. 204 * @param parent The parent class loader used before trying this class 205 * loader. 206 * @exception SecurityException if the SecurityManager disallows the 207 * creation of a ClassLoader. 208 * @exception SecurityException 209 * @see SecureClassLoader 210 */ 211 public URLClassLoader(URL[] urls, ClassLoader parent) 212 throws SecurityException 213 { 214 super(parent); 215 this.factory = null; 216 this.securityContext = null; 217 addURLs(urls); 218 } 219 220 // Package-private to avoid a trampoline constructor. 221 /** 222 * Package-private constructor used by the static 223 * <code>newInstance(URL[])</code> method. Creates an 224 * <code>URLClassLoader</code> with the given parent but without any 225 * <code>URL</code>s yet. This is used to bypass the normal security 226 * check for creating classloaders, but remembers the security 227 * context which will be used when defining classes. The 228 * <code>URL</code>s to load from must be added by the 229 * <code>newInstance()</code> method in the security context of the 230 * caller. 231 * 232 * @param securityContext the security context of the unprivileged code. 233 */ 234 URLClassLoader(ClassLoader parent, AccessControlContext securityContext) 235 { 236 super(parent); 237 this.factory = null; 238 this.securityContext = securityContext; 239 } 240 241 /** 242 * Creates a URLClassLoader that gets classes from the supplied URLs. 243 * To determine if this classloader may be created the constructor of 244 * the super class (<CODE>SecureClassLoader</CODE>) is called first, which 245 * can throw a SecurityException. Then the supplied URLs are added 246 * in the order given to the URLClassLoader which uses these URLs to 247 * load classes and resources (after using the supplied parent ClassLoader). 248 * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the 249 * protocol handlers of the supplied URLs. 250 * @param urls Locations that should be searched by this ClassLoader when 251 * resolving Classes or Resources. 252 * @param parent The parent class loader used before trying this class 253 * loader. 254 * @param factory Used to get the protocol handler for the URLs. 255 * @exception SecurityException if the SecurityManager disallows the 256 * creation of a ClassLoader. 257 * @exception SecurityException 258 * @see SecureClassLoader 259 */ 260 public URLClassLoader(URL[] urls, ClassLoader parent, 261 URLStreamHandlerFactory factory) 262 throws SecurityException 263 { 264 super(parent); 265 this.securityContext = null; 266 this.factory = factory; 267 // If this factory is not yet in factoryCache, add it. 268 factoryCache.add(factory); 269 addURLs(urls); 270 } 271 272 // Methods 273 274 /** 275 * Adds a new location to the end of the internal URL store. 276 * @param newUrl the location to add 277 */ 278 protected void addURL(URL newUrl) 279 { 280 urls.add(newUrl); 281 addURLImpl(newUrl); 282 } 283 284 private void addURLImpl(URL newUrl) 285 { 286 synchronized (this) 287 { 288 if (newUrl == null) 289 return; // Silently ignore... 290 291 // Reset the toString() value. 292 thisString = null; 293 294 // Create a loader for this URL. 295 URLLoader loader = null; 296 String file = newUrl.getFile(); 297 String protocol = newUrl.getProtocol(); 298 299 // If we have a file: URL, we want to make it absolute 300 // here, before we decide whether it is really a jar. 301 URL absoluteURL; 302 if ("file".equals (protocol)) 303 { 304 File dir = new File(file); 305 try 306 { 307 absoluteURL = dir.getCanonicalFile().toURL(); 308 } 309 catch (IOException ignore) 310 { 311 try 312 { 313 absoluteURL = dir.getAbsoluteFile().toURL(); 314 } 315 catch (MalformedURLException _) 316 { 317 // This really should not happen. 318 absoluteURL = newUrl; 319 } 320 } 321 } 322 else 323 { 324 // This doesn't hurt, and it simplifies the logic a 325 // little. 326 absoluteURL = newUrl; 327 } 328 329 // First see if we can find a handler with the correct name. 330 try 331 { 332 Class<?> handler = Class.forName(URL_LOADER_PREFIX + protocol); 333 Class<?>[] argTypes = new Class<?>[] { URLClassLoader.class, 334 URLStreamHandlerCache.class, 335 URLStreamHandlerFactory.class, 336 URL.class, 337 URL.class }; 338 Constructor k = handler.getDeclaredConstructor(argTypes); 339 loader 340 = (URLLoader) k.newInstance(new Object[] { this, 341 factoryCache, 342 factory, 343 newUrl, 344 absoluteURL }); 345 } 346 catch (ClassNotFoundException ignore) 347 { 348 // Fall through. 349 } 350 catch (NoSuchMethodException nsme) 351 { 352 // Programming error in the class library. 353 InternalError vme 354 = new InternalError("couldn't find URLLoader constructor"); 355 vme.initCause(nsme); 356 throw vme; 357 } 358 catch (InstantiationException inste) 359 { 360 // Programming error in the class library. 361 InternalError vme 362 = new InternalError("couldn't instantiate URLLoader"); 363 vme.initCause(inste); 364 throw vme; 365 } 366 catch (InvocationTargetException ite) 367 { 368 // Programming error in the class library. 369 InternalError vme 370 = new InternalError("error instantiating URLLoader"); 371 vme.initCause(ite); 372 throw vme; 373 } 374 catch (IllegalAccessException illae) 375 { 376 // Programming error in the class library. 377 InternalError vme 378 = new InternalError("invalid access to URLLoader"); 379 vme.initCause(illae); 380 throw vme; 381 } 382 383 if (loader == null) 384 { 385 // If it is not a directory, use the jar loader. 386 if (! (file.endsWith("/") || file.endsWith(File.separator))) 387 loader = new JarURLLoader(this, factoryCache, factory, 388 newUrl, absoluteURL); 389 else if ("file".equals(protocol)) 390 loader = new FileURLLoader(this, factoryCache, factory, 391 newUrl, absoluteURL); 392 else 393 loader = new RemoteURLLoader(this, factoryCache, factory, 394 newUrl); 395 } 396 397 urlinfos.add(loader); 398 ArrayList<URLLoader> extra = loader.getClassPath(); 399 if (extra != null) 400 urlinfos.addAll(extra); 401 } 402 } 403 404 /** 405 * Adds an array of new locations to the end of the internal URL 406 * store. Called from the the constructors. Should not call to the 407 * protected addURL() method since that can be overridden and 408 * subclasses are not yet in a good state at this point. 409 * jboss 4.0.3 for example depends on this. 410 * 411 * @param newUrls the locations to add 412 */ 413 private void addURLs(URL[] newUrls) 414 { 415 for (int i = 0; i < newUrls.length; i++) 416 { 417 urls.add(newUrls[i]); 418 addURLImpl(newUrls[i]); 419 } 420 } 421 422 /** 423 * Look in both Attributes for a given value. The first Attributes 424 * object, if not null, has precedence. 425 */ 426 private String getAttributeValue(Attributes.Name name, Attributes first, 427 Attributes second) 428 { 429 String result = null; 430 if (first != null) 431 result = first.getValue(name); 432 if (result == null) 433 result = second.getValue(name); 434 return result; 435 } 436 437 /** 438 * Defines a Package based on the given name and the supplied manifest 439 * information. The manifest indicates the title, version and 440 * vendor information of the specification and implementation and whether the 441 * package is sealed. If the Manifest indicates that the package is sealed 442 * then the Package will be sealed with respect to the supplied URL. 443 * 444 * @param name The name of the package 445 * @param manifest The manifest describing the specification, 446 * implementation and sealing details of the package 447 * @param url the code source url to seal the package 448 * @return the defined Package 449 * @throws IllegalArgumentException If this package name already exists 450 * in this class loader 451 */ 452 protected Package definePackage(String name, Manifest manifest, URL url) 453 throws IllegalArgumentException 454 { 455 // Compute the name of the package as it may appear in the 456 // Manifest. 457 CPStringBuilder xform = new CPStringBuilder(name); 458 for (int i = xform.length () - 1; i >= 0; --i) 459 if (xform.charAt(i) == '.') 460 xform.setCharAt(i, '/'); 461 xform.append('/'); 462 String xformName = xform.toString(); 463 464 Attributes entryAttr = manifest.getAttributes(xformName); 465 Attributes attr = manifest.getMainAttributes(); 466 467 String specTitle 468 = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE, 469 entryAttr, attr); 470 String specVersion 471 = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION, 472 entryAttr, attr); 473 String specVendor 474 = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR, 475 entryAttr, attr); 476 String implTitle 477 = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE, 478 entryAttr, attr); 479 String implVersion 480 = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION, 481 entryAttr, attr); 482 String implVendor 483 = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR, 484 entryAttr, attr); 485 486 // Look if the Manifest indicates that this package is sealed 487 // XXX - most likely not completely correct! 488 // Shouldn't we also check the sealed attribute of the complete jar? 489 // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled 490 // But how do we get that jar manifest here? 491 String sealed = attr.getValue(Attributes.Name.SEALED); 492 if ("false".equals(sealed)) 493 // make sure that the URL is null so the package is not sealed 494 url = null; 495 496 return definePackage(name, 497 specTitle, specVendor, specVersion, 498 implTitle, implVendor, implVersion, 499 url); 500 } 501 502 /** 503 * Finds (the first) class by name from one of the locations. The locations 504 * are searched in the order they were added to the URLClassLoader. 505 * 506 * @param className the classname to find 507 * @exception ClassNotFoundException when the class could not be found or 508 * loaded 509 * @return a Class object representing the found class 510 */ 511 protected Class<?> findClass(final String className) 512 throws ClassNotFoundException 513 { 514 // Just try to find the resource by the (almost) same name 515 String resourceName = className.replace('.', '/') + ".class"; 516 int max = urlinfos.size(); 517 Resource resource = null; 518 for (int i = 0; i < max && resource == null; i++) 519 { 520 URLLoader loader = (URLLoader)urlinfos.elementAt(i); 521 if (loader == null) 522 continue; 523 524 Class k = loader.getClass(className); 525 if (k != null) 526 return k; 527 528 resource = loader.getResource(resourceName); 529 } 530 if (resource == null) 531 throw new ClassNotFoundException(className + " not found in " + this); 532 533 // Try to read the class data, create the CodeSource, Package and 534 // construct the class (and watch out for those nasty IOExceptions) 535 try 536 { 537 byte[] data; 538 InputStream in = resource.getInputStream(); 539 try 540 { 541 int length = resource.getLength(); 542 if (length != -1) 543 { 544 // We know the length of the data. 545 // Just try to read it in all at once 546 data = new byte[length]; 547 int pos = 0; 548 while (length - pos > 0) 549 { 550 int len = in.read(data, pos, length - pos); 551 if (len == -1) 552 throw new EOFException("Not enough data reading from: " 553 + in); 554 pos += len; 555 } 556 } 557 else 558 { 559 // We don't know the data length. 560 // Have to read it in chunks. 561 ByteArrayOutputStream out = new ByteArrayOutputStream(4096); 562 byte[] b = new byte[4096]; 563 int l = 0; 564 while (l != -1) 565 { 566 l = in.read(b); 567 if (l != -1) 568 out.write(b, 0, l); 569 } 570 data = out.toByteArray(); 571 } 572 } 573 finally 574 { 575 in.close(); 576 } 577 final byte[] classData = data; 578 579 // Now get the CodeSource 580 final CodeSource source = resource.getCodeSource(); 581 582 // Find out package name 583 String packageName = null; 584 int lastDot = className.lastIndexOf('.'); 585 if (lastDot != -1) 586 packageName = className.substring(0, lastDot); 587 588 if (packageName != null && getPackage(packageName) == null) 589 { 590 // define the package 591 Manifest manifest = resource.getLoader().getManifest(); 592 if (manifest == null) 593 definePackage(packageName, null, null, null, null, null, null, 594 null); 595 else 596 definePackage(packageName, manifest, 597 resource.getLoader().getBaseURL()); 598 } 599 600 // And finally construct the class! 601 SecurityManager sm = System.getSecurityManager(); 602 Class result = null; 603 if (sm != null && securityContext != null) 604 { 605 result = AccessController.doPrivileged 606 (new PrivilegedAction<Class>() 607 { 608 public Class run() 609 { 610 return defineClass(className, classData, 611 0, classData.length, 612 source); 613 } 614 }, securityContext); 615 } 616 else 617 result = defineClass(className, classData, 0, classData.length, source); 618 619 // Avoid NullPointerExceptions. 620 Certificate[] resourceCertificates = resource.getCertificates(); 621 if(resourceCertificates != null) 622 super.setSigners(result, resourceCertificates); 623 624 return result; 625 } 626 catch (IOException ioe) 627 { 628 throw new ClassNotFoundException(className + " not found in " + this, ioe); 629 } 630 } 631 632 // Cached String representation of this URLClassLoader 633 private String thisString; 634 635 /** 636 * Returns a String representation of this URLClassLoader giving the 637 * actual Class name, the URLs that are searched and the parent 638 * ClassLoader. 639 */ 640 public String toString() 641 { 642 synchronized (this) 643 { 644 if (thisString == null) 645 { 646 CPStringBuilder sb = new CPStringBuilder(); 647 sb.append(this.getClass().getName()); 648 sb.append("{urls=[" ); 649 URL[] thisURLs = getURLs(); 650 for (int i = 0; i < thisURLs.length; i++) 651 { 652 sb.append(thisURLs[i]); 653 if (i < thisURLs.length - 1) 654 sb.append(','); 655 } 656 sb.append(']'); 657 sb.append(", parent="); 658 sb.append(getParent()); 659 sb.append('}'); 660 thisString = sb.toString(); 661 } 662 return thisString; 663 } 664 } 665 666 /** 667 * Finds the first occurrence of a resource that can be found. The locations 668 * are searched in the order they were added to the URLClassLoader. 669 * 670 * @param resourceName the resource name to look for 671 * @return the URLResource for the resource if found, null otherwise 672 */ 673 private Resource findURLResource(String resourceName) 674 { 675 int max = urlinfos.size(); 676 for (int i = 0; i < max; i++) 677 { 678 URLLoader loader = (URLLoader) urlinfos.elementAt(i); 679 if (loader == null) 680 continue; 681 682 Resource resource = loader.getResource(resourceName); 683 if (resource != null) 684 return resource; 685 } 686 return null; 687 } 688 689 /** 690 * Finds the first occurrence of a resource that can be found. 691 * 692 * @param resourceName the resource name to look for 693 * @return the URL if found, null otherwise 694 */ 695 public URL findResource(String resourceName) 696 { 697 Resource resource = findURLResource(resourceName); 698 if (resource != null) 699 return resource.getURL(); 700 701 // Resource not found 702 return null; 703 } 704 705 /** 706 * Finds all the resources with a particular name from all the locations. 707 * 708 * @param resourceName the name of the resource to lookup 709 * @return a (possible empty) enumeration of URLs where the resource can be 710 * found 711 * @exception IOException when an error occurs accessing one of the 712 * locations 713 */ 714 public Enumeration<URL> findResources(String resourceName) 715 throws IOException 716 { 717 Vector<URL> resources = new Vector<URL>(); 718 int max = urlinfos.size(); 719 for (int i = 0; i < max; i++) 720 { 721 URLLoader loader = (URLLoader) urlinfos.elementAt(i); 722 Resource resource = loader.getResource(resourceName); 723 if (resource != null) 724 resources.add(resource.getURL()); 725 } 726 return resources.elements(); 727 } 728 729 /** 730 * Returns the permissions needed to access a particular code 731 * source. These permissions includes those returned by 732 * <code>SecureClassLoader.getPermissions()</code> and the actual 733 * permissions to access the objects referenced by the URL of the 734 * code source. The extra permissions added depend on the protocol 735 * and file portion of the URL in the code source. If the URL has 736 * the "file" protocol ends with a '/' character then it must be a 737 * directory and a file Permission to read everything in that 738 * directory and all subdirectories is added. If the URL had the 739 * "file" protocol and doesn't end with a '/' character then it must 740 * be a normal file and a file permission to read that file is 741 * added. If the <code>URL</code> has any other protocol then a 742 * socket permission to connect and accept connections from the host 743 * portion of the URL is added. 744 * 745 * @param source The codesource that needs the permissions to be accessed 746 * @return the collection of permissions needed to access the code resource 747 * @see java.security.SecureClassLoader#getPermissions(CodeSource) 748 */ 749 protected PermissionCollection getPermissions(CodeSource source) 750 { 751 // XXX - This implementation does exactly as the Javadoc describes. 752 // But maybe we should/could use URLConnection.getPermissions()? 753 // First get the permissions that would normally be granted 754 PermissionCollection permissions = super.getPermissions(source); 755 756 // Now add any extra permissions depending on the URL location. 757 URL url = source.getLocation(); 758 String protocol = url.getProtocol(); 759 if (protocol.equals("file")) 760 { 761 String file = url.getFile(); 762 763 // If the file end in / it must be an directory. 764 if (file.endsWith("/") || file.endsWith(File.separator)) 765 { 766 // Grant permission to read everything in that directory and 767 // all subdirectories. 768 permissions.add(new FilePermission(file + "-", "read")); 769 } 770 else 771 { 772 // It is a 'normal' file. 773 // Grant permission to access that file. 774 permissions.add(new FilePermission(file, "read")); 775 } 776 } 777 else 778 { 779 // Grant permission to connect to and accept connections from host 780 String host = url.getHost(); 781 if (host != null) 782 permissions.add(new SocketPermission(host, "connect,accept")); 783 } 784 785 return permissions; 786 } 787 788 /** 789 * Returns all the locations that this class loader currently uses the 790 * resolve classes and resource. This includes both the initially supplied 791 * URLs as any URLs added later by the loader. 792 * @return All the currently used URLs 793 */ 794 public URL[] getURLs() 795 { 796 return (URL[]) urls.toArray(new URL[urls.size()]); 797 } 798 799 /** 800 * Creates a new instance of a <code>URLClassLoader</code> that gets 801 * classes from the supplied <code>URL</code>s. This class loader 802 * will have as parent the standard system class loader. 803 * 804 * @param urls the initial URLs used to resolve classes and 805 * resources 806 * 807 * @return the class loader 808 * 809 * @exception SecurityException when the calling code does not have 810 * permission to access the given <code>URL</code>s 811 */ 812 public static URLClassLoader newInstance(URL[] urls) 813 throws SecurityException 814 { 815 return newInstance(urls, null); 816 } 817 818 /** 819 * Creates a new instance of a <code>URLClassLoader</code> that gets 820 * classes from the supplied <code>URL</code>s and with the supplied 821 * loader as parent class loader. 822 * 823 * @param urls the initial URLs used to resolve classes and 824 * resources 825 * @param parent the parent class loader 826 * 827 * @return the class loader 828 * 829 * @exception SecurityException when the calling code does not have 830 * permission to access the given <code>URL</code>s 831 */ 832 public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent) 833 throws SecurityException 834 { 835 SecurityManager sm = System.getSecurityManager(); 836 if (sm == null) 837 return new URLClassLoader(urls, parent); 838 else 839 { 840 final Object securityContext = sm.getSecurityContext(); 841 842 // XXX - What to do with anything else then an AccessControlContext? 843 if (! (securityContext instanceof AccessControlContext)) 844 throw new SecurityException("securityContext must be AccessControlContext: " 845 + securityContext); 846 847 URLClassLoader loader = 848 AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() 849 { 850 public URLClassLoader run() 851 { 852 return new URLClassLoader(parent, 853 (AccessControlContext) securityContext); 854 } 855 }); 856 loader.addURLs(urls); 857 return loader; 858 } 859 } 860}