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