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