1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.net; 27 28 import java.io.Closeable; 29 import java.io.File; 30 import java.io.FilePermission; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.security.AccessControlContext; 34 import java.security.AccessController; 35 import java.security.CodeSigner; 36 import java.security.CodeSource; 37 import java.security.Permission; 38 import java.security.PermissionCollection; 39 import java.security.PrivilegedAction; 40 import java.security.PrivilegedExceptionAction; 41 import java.security.SecureClassLoader; 42 import java.util.Enumeration; 43 import java.util.List; 44 import java.util.NoSuchElementException; 45 import java.util.Set; 46 import java.util.WeakHashMap; 47 import java.util.jar.Attributes; 48 import java.util.jar.Attributes.Name; 49 import java.util.jar.JarFile; 50 import java.util.jar.Manifest; 51 52 import jdk.internal.loader.Resource; 53 import jdk.internal.loader.URLClassPath; 54 import jdk.internal.perf.PerfCounter; 55 import sun.net.www.ParseUtil; 56 import sun.security.util.SecurityConstants; 57 58 /** 59 * This class loader is used to load classes and resources from a search 60 * path of URLs referring to both JAR files and directories. Any {@code jar:} 61 * scheme URL (see {@link java.net.JarURLConnection}) is assumed to refer to a 62 * JAR file. Any {@code file:} scheme URL that ends with a '/' is assumed to 63 * refer to a directory. Otherwise, the URL is assumed to refer to a JAR file 64 * which will be opened as needed. 65 * <p> 66 * The AccessControlContext of the thread that created the instance of 67 * URLClassLoader will be used when subsequently loading classes and 68 * resources. 69 * <p> 70 * The classes that are loaded are by default granted permission only to 71 * access the URLs specified when the URLClassLoader was created. 72 * 73 * @author David Connelly 74 * @since 1.2 75 */ 76 public class URLClassLoader extends SecureClassLoader implements Closeable { 77 /* The search path for classes and resources */ 78 private final URLClassPath ucp; 79 80 /* The context to be used when loading classes and resources */ 81 private final AccessControlContext acc; 82 83 /** 84 * Constructs a new URLClassLoader for the given URLs. The URLs will be 85 * searched in the order specified for classes and resources after first 86 * searching in the specified parent class loader. Any {@code jar:} 87 * scheme URL is assumed to refer to a JAR file. Any {@code file:} scheme 88 * URL that ends with a '/' is assumed to refer to a directory. Otherwise, 89 * the URL is assumed to refer to a JAR file which will be downloaded and 90 * opened as needed. 91 * 92 * <p>If there is a security manager, this method first 93 * calls the security manager's {@code checkCreateClassLoader} method 94 * to ensure creation of a class loader is allowed. 95 * 96 * @param urls the URLs from which to load classes and resources 97 * @param parent the parent class loader for delegation 98 * @exception SecurityException if a security manager exists and its 99 * {@code checkCreateClassLoader} method doesn't allow 100 * creation of a class loader. 101 * @exception NullPointerException if {@code urls} is {@code null}. 102 * @see SecurityManager#checkCreateClassLoader 103 */ 104 public URLClassLoader(URL[] urls, ClassLoader parent) { 105 super(parent); 106 // this is to make the stack depth consistent with 1.1 107 SecurityManager security = System.getSecurityManager(); 108 if (security != null) { 109 security.checkCreateClassLoader(); 110 } 111 ucp = new URLClassPath(urls); 112 this.acc = AccessController.getContext(); 113 } 114 115 URLClassLoader(URL[] urls, ClassLoader parent, 116 AccessControlContext acc) { 117 super(parent); 118 // this is to make the stack depth consistent with 1.1 119 SecurityManager security = System.getSecurityManager(); 120 if (security != null) { 121 security.checkCreateClassLoader(); 122 } 123 ucp = new URLClassPath(urls); 124 this.acc = acc; 125 } 126 127 /** 128 * Constructs a new URLClassLoader for the specified URLs using the 129 * default delegation parent {@code ClassLoader}. The URLs will 130 * be searched in the order specified for classes and resources after 131 * first searching in the parent class loader. Any URL that ends with 132 * a '/' is assumed to refer to a directory. Otherwise, the URL is 133 * assumed to refer to a JAR file which will be downloaded and opened 134 * as needed. 135 * 136 * <p>If there is a security manager, this method first 137 * calls the security manager's {@code checkCreateClassLoader} method 138 * to ensure creation of a class loader is allowed. 139 * 140 * @param urls the URLs from which to load classes and resources 141 * 142 * @exception SecurityException if a security manager exists and its 143 * {@code checkCreateClassLoader} method doesn't allow 144 * creation of a class loader. 145 * @exception NullPointerException if {@code urls} is {@code null}. 146 * @see SecurityManager#checkCreateClassLoader 147 */ 148 public URLClassLoader(URL[] urls) { 149 super(); 150 // this is to make the stack depth consistent with 1.1 151 SecurityManager security = System.getSecurityManager(); 152 if (security != null) { 153 security.checkCreateClassLoader(); 154 } 155 ucp = new URLClassPath(urls); 156 this.acc = AccessController.getContext(); 157 } 158 159 URLClassLoader(URL[] urls, AccessControlContext acc) { 160 super(); 161 // this is to make the stack depth consistent with 1.1 162 SecurityManager security = System.getSecurityManager(); 163 if (security != null) { 164 security.checkCreateClassLoader(); 165 } 166 ucp = new URLClassPath(urls); 167 this.acc = acc; 168 } 169 170 /** 171 * Constructs a new URLClassLoader for the specified URLs, parent 172 * class loader, and URLStreamHandlerFactory. The parent argument 173 * will be used as the parent class loader for delegation. The 174 * factory argument will be used as the stream handler factory to 175 * obtain protocol handlers when creating new jar URLs. 176 * 177 * <p>If there is a security manager, this method first 178 * calls the security manager's {@code checkCreateClassLoader} method 179 * to ensure creation of a class loader is allowed. 180 * 181 * @param urls the URLs from which to load classes and resources 182 * @param parent the parent class loader for delegation 183 * @param factory the URLStreamHandlerFactory to use when creating URLs 184 * 185 * @exception SecurityException if a security manager exists and its 186 * {@code checkCreateClassLoader} method doesn't allow 187 * creation of a class loader. 188 * @exception NullPointerException if {@code urls} is {@code null}. 189 * @see SecurityManager#checkCreateClassLoader 190 */ 191 public URLClassLoader(URL[] urls, ClassLoader parent, 192 URLStreamHandlerFactory factory) { 193 super(parent); 194 // this is to make the stack depth consistent with 1.1 195 SecurityManager security = System.getSecurityManager(); 196 if (security != null) { 197 security.checkCreateClassLoader(); 198 } 199 ucp = new URLClassPath(urls, factory); 200 acc = AccessController.getContext(); 201 } 202 203 /* A map (used as a set) to keep track of closeable local resources 204 * (either JarFiles or FileInputStreams). We don't care about 205 * Http resources since they don't need to be closed. 206 * 207 * If the resource is coming from a jar file 208 * we keep a (weak) reference to the JarFile object which can 209 * be closed if URLClassLoader.close() called. Due to jar file 210 * caching there will typically be only one JarFile object 211 * per underlying jar file. 212 * 213 * For file resources, which is probably a less common situation 214 * we have to keep a weak reference to each stream. 215 */ 216 217 private WeakHashMap<Closeable,Void> 218 closeables = new WeakHashMap<>(); 219 220 /** 221 * Returns an input stream for reading the specified resource. 222 * If this loader is closed, then any resources opened by this method 223 * will be closed. 224 * 225 * <p> The search order is described in the documentation for {@link 226 * #getResource(String)}. </p> 227 * 228 * @param name 229 * The resource name 230 * 231 * @return An input stream for reading the resource, or {@code null} 232 * if the resource could not be found 233 * 234 * @since 1.7 235 */ 236 public InputStream getResourceAsStream(String name) { 237 URL url = getResource(name); 238 try { 239 if (url == null) { 240 return null; 241 } 242 URLConnection urlc = url.openConnection(); 243 InputStream is = urlc.getInputStream(); 244 if (urlc instanceof JarURLConnection) { 245 JarURLConnection juc = (JarURLConnection)urlc; 246 JarFile jar = juc.getJarFile(); 247 synchronized (closeables) { 248 if (!closeables.containsKey(jar)) { 249 closeables.put(jar, null); 250 } 251 } 252 } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) { 253 synchronized (closeables) { 254 closeables.put(is, null); 255 } 256 } 257 return is; 258 } catch (IOException e) { 259 return null; 260 } 261 } 262 263 /** 264 * Closes this URLClassLoader, so that it can no longer be used to load 265 * new classes or resources that are defined by this loader. 266 * Classes and resources defined by any of this loader's parents in the 267 * delegation hierarchy are still accessible. Also, any classes or resources 268 * that are already loaded, are still accessible. 269 * <p> 270 * In the case of jar: and file: URLs, it also closes any files 271 * that were opened by it. If another thread is loading a 272 * class when the {@code close} method is invoked, then the result of 273 * that load is undefined. 274 * <p> 275 * The method makes a best effort attempt to close all opened files, 276 * by catching {@link IOException}s internally. Unchecked exceptions 277 * and errors are not caught. Calling close on an already closed 278 * loader has no effect. 279 * 280 * @exception IOException if closing any file opened by this class loader 281 * resulted in an IOException. Any such exceptions are caught internally. 282 * If only one is caught, then it is re-thrown. If more than one exception 283 * is caught, then the second and following exceptions are added 284 * as suppressed exceptions of the first one caught, which is then re-thrown. 285 * 286 * @exception SecurityException if a security manager is set, and it denies 287 * {@link RuntimePermission}{@code ("closeClassLoader")} 288 * 289 * @since 1.7 290 */ 291 public void close() throws IOException { 292 SecurityManager security = System.getSecurityManager(); 293 if (security != null) { 294 security.checkPermission(new RuntimePermission("closeClassLoader")); 295 } 296 List<IOException> errors = ucp.closeLoaders(); 297 298 // now close any remaining streams. 299 300 synchronized (closeables) { 301 Set<Closeable> keys = closeables.keySet(); 302 for (Closeable c : keys) { 303 try { 304 c.close(); 305 } catch (IOException ioex) { 306 errors.add(ioex); 307 } 308 } 309 closeables.clear(); 310 } 311 312 if (errors.isEmpty()) { 313 return; 314 } 315 316 IOException firstex = errors.remove(0); 317 318 // Suppress any remaining exceptions 319 320 for (IOException error: errors) { 321 firstex.addSuppressed(error); 322 } 323 throw firstex; 324 } 325 326 /** 327 * Appends the specified URL to the list of URLs to search for 328 * classes and resources. 329 * <p> 330 * If the URL specified is {@code null} or is already in the 331 * list of URLs, or if this loader is closed, then invoking this 332 * method has no effect. 333 * 334 * @param url the URL to be added to the search path of URLs 335 */ 336 protected void addURL(URL url) { 337 ucp.addURL(url); 338 } 339 340 /** 341 * Returns the search path of URLs for loading classes and resources. 342 * This includes the original list of URLs specified to the constructor, 343 * along with any URLs subsequently appended by the addURL() method. 344 * @return the search path of URLs for loading classes and resources. 345 */ 346 public URL[] getURLs() { 347 return ucp.getURLs(); 348 } 349 350 /** 351 * Finds and loads the class with the specified name from the URL search 352 * path. Any URLs referring to JAR files are loaded and opened as needed 353 * until the class is found. 354 * 355 * @param name the name of the class 356 * @return the resulting class 357 * @exception ClassNotFoundException if the class could not be found, 358 * or if the loader is closed. 359 * @exception NullPointerException if {@code name} is {@code null}. 360 */ 361 protected Class<?> findClass(final String name) 362 throws ClassNotFoundException 363 { 364 final Class<?> result; 365 try { 366 result = AccessController.doPrivileged( 367 new PrivilegedExceptionAction<>() { 368 public Class<?> run() throws ClassNotFoundException { 369 String path = name.replace('.', '/').concat(".class"); 370 Resource res = ucp.getResource(path, false); 371 if (res != null) { 372 try { 373 return defineClass(name, res); 374 } catch (IOException e) { 375 throw new ClassNotFoundException(name, e); 376 } 377 } else { 378 return null; 379 } 380 } 381 }, acc); 382 } catch (java.security.PrivilegedActionException pae) { 383 throw (ClassNotFoundException) pae.getException(); 384 } 385 if (result == null) { 386 throw new ClassNotFoundException(name); 387 } 388 return result; 389 } 390 391 /* 392 * Retrieve the package using the specified package name. 393 * If non-null, verify the package using the specified code 394 * source and manifest. 395 */ 396 private Package getAndVerifyPackage(String pkgname, 397 Manifest man, URL url) { 398 Package pkg = getDefinedPackage(pkgname); 399 if (pkg != null) { 400 // Package found, so check package sealing. 401 if (pkg.isSealed()) { 402 // Verify that code source URL is the same. 403 if (!pkg.isSealed(url)) { 404 throw new SecurityException( 405 "sealing violation: package " + pkgname + " is sealed"); 406 } 407 } else { 408 // Make sure we are not attempting to seal the package 409 // at this code source URL. 410 if ((man != null) && isSealed(pkgname, man)) { 411 throw new SecurityException( 412 "sealing violation: can't seal package " + pkgname + 413 ": already loaded"); 414 } 415 } 416 } 417 return pkg; 418 } 419 420 /* 421 * Defines a Class using the class bytes obtained from the specified 422 * Resource. The resulting Class must be resolved before it can be 423 * used. 424 */ 425 private Class<?> defineClass(String name, Resource res) throws IOException { 426 long t0 = System.nanoTime(); 427 int i = name.lastIndexOf('.'); 428 URL url = res.getCodeSourceURL(); 429 if (i != -1) { 430 String pkgname = name.substring(0, i); 431 // Check if package already loaded. 432 Manifest man = res.getManifest(); 433 if (getAndVerifyPackage(pkgname, man, url) == null) { 434 try { 435 if (man != null) { 436 definePackage(pkgname, man, url); 437 } else { 438 definePackage(pkgname, null, null, null, null, null, null, null); 439 } 440 } catch (IllegalArgumentException iae) { 441 // parallel-capable class loaders: re-verify in case of a 442 // race condition 443 if (getAndVerifyPackage(pkgname, man, url) == null) { 444 // Should never happen 445 throw new AssertionError("Cannot find package " + 446 pkgname); 447 } 448 } 449 } 450 } 451 // Now read the class bytes and define the class 452 java.nio.ByteBuffer bb = res.getByteBuffer(); 453 if (bb != null) { 454 // Use (direct) ByteBuffer: 455 CodeSigner[] signers = res.getCodeSigners(); 456 CodeSource cs = new CodeSource(url, signers); 457 PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); 458 return defineClass(name, bb, cs); 459 } else { 460 byte[] b = res.getBytes(); 461 // must read certificates AFTER reading bytes. 462 CodeSigner[] signers = res.getCodeSigners(); 463 CodeSource cs = new CodeSource(url, signers); 464 PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); 465 return defineClass(name, b, 0, b.length, cs); 466 } 467 } 468 469 /** 470 * Defines a new package by name in this {@code URLClassLoader}. 471 * The attributes contained in the specified {@code Manifest} 472 * will be used to obtain package version and sealing information. 473 * For sealed packages, the additional URL specifies the code source URL 474 * from which the package was loaded. 475 * 476 * @param name the package name 477 * @param man the {@code Manifest} containing package version and sealing 478 * information 479 * @param url the code source url for the package, or null if none 480 * @throws IllegalArgumentException if the package name is 481 * already defined by this class loader 482 * @return the newly defined {@code Package} object 483 */ 484 protected Package definePackage(String name, Manifest man, URL url) { 485 String path = name.replace('.', '/').concat("/"); 486 String specTitle = null, specVersion = null, specVendor = null; 487 String implTitle = null, implVersion = null, implVendor = null; 488 String sealed = null; 489 URL sealBase = null; 490 491 Attributes attr = man.getAttributes(path); 492 if (attr != null) { 493 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 494 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 495 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 496 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 497 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 498 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 499 sealed = attr.getValue(Name.SEALED); 500 } 501 attr = man.getMainAttributes(); 502 if (attr != null) { 503 if (specTitle == null) { 504 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 505 } 506 if (specVersion == null) { 507 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 508 } 509 if (specVendor == null) { 510 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 511 } 512 if (implTitle == null) { 513 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 514 } 515 if (implVersion == null) { 516 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 517 } 518 if (implVendor == null) { 519 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 520 } 521 if (sealed == null) { 522 sealed = attr.getValue(Name.SEALED); 523 } 524 } 525 if ("true".equalsIgnoreCase(sealed)) { 526 sealBase = url; 527 } 528 return definePackage(name, specTitle, specVersion, specVendor, 529 implTitle, implVersion, implVendor, sealBase); 530 } 531 532 /* 533 * Returns true if the specified package name is sealed according to the 534 * given manifest. 535 */ 536 private boolean isSealed(String name, Manifest man) { 537 String path = name.replace('.', '/').concat("/"); 538 Attributes attr = man.getAttributes(path); 539 String sealed = null; 540 if (attr != null) { 541 sealed = attr.getValue(Name.SEALED); 542 } 543 if (sealed == null) { 544 if ((attr = man.getMainAttributes()) != null) { 545 sealed = attr.getValue(Name.SEALED); 546 } 547 } 548 return "true".equalsIgnoreCase(sealed); 549 } 550 551 /** 552 * Finds the resource with the specified name on the URL search path. 553 * 554 * @param name the name of the resource 555 * @return a {@code URL} for the resource, or {@code null} 556 * if the resource could not be found, or if the loader is closed. 557 */ 558 public URL findResource(final String name) { 559 /* 560 * The same restriction to finding classes applies to resources 561 */ 562 URL url = AccessController.doPrivileged( 563 new PrivilegedAction<>() { 564 public URL run() { 565 return ucp.findResource(name, true); 566 } 567 }, acc); 568 569 return url != null ? URLClassPath.checkURL(url) : null; 570 } 571 572 /** 573 * Returns an Enumeration of URLs representing all of the resources 574 * on the URL search path having the specified name. 575 * 576 * @param name the resource name 577 * @exception IOException if an I/O exception occurs 578 * @return an {@code Enumeration} of {@code URL}s 579 * If the loader is closed, the Enumeration will be empty. 580 */ 581 public Enumeration<URL> findResources(final String name) 582 throws IOException 583 { 584 final Enumeration<URL> e = ucp.findResources(name, true); 585 586 return new Enumeration<>() { 587 private URL url = null; 588 589 private boolean next() { 590 if (url != null) { 591 return true; 592 } 593 do { 594 URL u = AccessController.doPrivileged( 595 new PrivilegedAction<>() { 596 public URL run() { 597 if (!e.hasMoreElements()) 598 return null; 599 return e.nextElement(); 600 } 601 }, acc); 602 if (u == null) 603 break; 604 url = URLClassPath.checkURL(u); 605 } while (url == null); 606 return url != null; 607 } 608 609 public URL nextElement() { 610 if (!next()) { 611 throw new NoSuchElementException(); 612 } 613 URL u = url; 614 url = null; 615 return u; 616 } 617 618 public boolean hasMoreElements() { 619 return next(); 620 } 621 }; 622 } 623 624 /** 625 * Returns the permissions for the given codesource object. 626 * The implementation of this method first calls super.getPermissions 627 * and then adds permissions based on the URL of the codesource. 628 * <p> 629 * If the protocol of this URL is "jar", then the permission granted 630 * is based on the permission that is required by the URL of the Jar 631 * file. 632 * <p> 633 * If the protocol is "file" and there is an authority component, then 634 * permission to connect to and accept connections from that authority 635 * may be granted. If the protocol is "file" 636 * and the path specifies a file, then permission to read that 637 * file is granted. If protocol is "file" and the path is 638 * a directory, permission is granted to read all files 639 * and (recursively) all files and subdirectories contained in 640 * that directory. 641 * <p> 642 * If the protocol is not "file", then permission 643 * to connect to and accept connections from the URL's host is granted. 644 * @param codesource the codesource 645 * @exception NullPointerException if {@code codesource} is {@code null}. 646 * @return the permissions granted to the codesource 647 */ 648 protected PermissionCollection getPermissions(CodeSource codesource) 649 { 650 PermissionCollection perms = super.getPermissions(codesource); 651 652 URL url = codesource.getLocation(); 653 654 Permission p; 655 URLConnection urlConnection; 656 657 try { 658 urlConnection = url.openConnection(); 659 p = urlConnection.getPermission(); 660 } catch (java.io.IOException ioe) { 661 p = null; 662 urlConnection = null; 663 } 664 665 if (p instanceof FilePermission) { 666 // if the permission has a separator char on the end, 667 // it means the codebase is a directory, and we need 668 // to add an additional permission to read recursively 669 String path = p.getName(); 670 if (path.endsWith(File.separator)) { 671 path += "-"; 672 p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); 673 } 674 } else if ((p == null) && (url.getProtocol().equals("file"))) { 675 String path = url.getFile().replace('/', File.separatorChar); 676 path = ParseUtil.decode(path); 677 if (path.endsWith(File.separator)) 678 path += "-"; 679 p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); 680 } else { 681 /** 682 * Not loading from a 'file:' URL so we want to give the class 683 * permission to connect to and accept from the remote host 684 * after we've made sure the host is the correct one and is valid. 685 */ 686 URL locUrl = url; 687 if (urlConnection instanceof JarURLConnection) { 688 locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); 689 } 690 String host = locUrl.getHost(); 691 if (host != null && (host.length() > 0)) 692 p = new SocketPermission(host, 693 SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION); 694 } 695 696 // make sure the person that created this class loader 697 // would have this permission 698 699 if (p != null) { 700 final SecurityManager sm = System.getSecurityManager(); 701 if (sm != null) { 702 final Permission fp = p; 703 AccessController.doPrivileged(new PrivilegedAction<>() { 704 public Void run() throws SecurityException { 705 sm.checkPermission(fp); 706 return null; 707 } 708 }, acc); 709 } 710 perms.add(p); 711 } 712 return perms; 713 } 714 715 /** 716 * Creates a new instance of URLClassLoader for the specified 717 * URLs and parent class loader. If a security manager is 718 * installed, the {@code loadClass} method of the URLClassLoader 719 * returned by this method will invoke the 720 * {@code SecurityManager.checkPackageAccess} method before 721 * loading the class. 722 * 723 * @param urls the URLs to search for classes and resources 724 * @param parent the parent class loader for delegation 725 * @exception NullPointerException if {@code urls} is {@code null}. 726 * @return the resulting class loader 727 */ 728 public static URLClassLoader newInstance(final URL[] urls, 729 final ClassLoader parent) { 730 // Save the caller's context 731 final AccessControlContext acc = AccessController.getContext(); 732 // Need a privileged block to create the class loader 733 URLClassLoader ucl = AccessController.doPrivileged( 734 new PrivilegedAction<>() { 735 public URLClassLoader run() { 736 return new FactoryURLClassLoader(urls, parent, acc); 737 } 738 }); 739 return ucl; 740 } 741 742 /** 743 * Creates a new instance of URLClassLoader for the specified 744 * URLs and default parent class loader. If a security manager is 745 * installed, the {@code loadClass} method of the URLClassLoader 746 * returned by this method will invoke the 747 * {@code SecurityManager.checkPackageAccess} before 748 * loading the class. 749 * 750 * @param urls the URLs to search for classes and resources 751 * @exception NullPointerException if {@code urls} is {@code null}. 752 * @return the resulting class loader 753 */ 754 public static URLClassLoader newInstance(final URL[] urls) { 755 // Save the caller's context 756 final AccessControlContext acc = AccessController.getContext(); 757 // Need a privileged block to create the class loader 758 URLClassLoader ucl = AccessController.doPrivileged( 759 new PrivilegedAction<>() { 760 public URLClassLoader run() { 761 return new FactoryURLClassLoader(urls, acc); 762 } 763 }); 764 return ucl; 765 } 766 767 static { 768 ClassLoader.registerAsParallelCapable(); 769 } 770 } 771 772 final class FactoryURLClassLoader extends URLClassLoader { 773 774 static { 775 ClassLoader.registerAsParallelCapable(); 776 } 777 778 FactoryURLClassLoader(URL[] urls, ClassLoader parent, 779 AccessControlContext acc) { 780 super(urls, parent, acc); 781 } 782 783 FactoryURLClassLoader(URL[] urls, AccessControlContext acc) { 784 super(urls, acc); 785 } 786 787 public final Class<?> loadClass(String name, boolean resolve) 788 throws ClassNotFoundException 789 { 790 // First check if we have permission to access the package. This 791 // should go away once we've added support for exported packages. 792 SecurityManager sm = System.getSecurityManager(); 793 if (sm != null) { 794 int i = name.lastIndexOf('.'); 795 if (i != -1) { 796 sm.checkPackageAccess(name.substring(0, i)); 797 } 798 } 799 return super.loadClass(name, resolve); 800 } 801 }