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