1 /* 2 * Copyright (c) 2015, 2016, 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 jdk.internal.loader; 27 28 import java.io.File; 29 import java.io.FilePermission; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.lang.module.ModuleReference; 33 import java.lang.module.ModuleReader; 34 import java.net.MalformedURLException; 35 import java.net.URI; 36 import java.net.URL; 37 import java.nio.ByteBuffer; 38 import java.security.AccessController; 39 import java.security.CodeSigner; 40 import java.security.CodeSource; 41 import java.security.Permission; 42 import java.security.PermissionCollection; 43 import java.security.PrivilegedAction; 44 import java.security.PrivilegedActionException; 45 import java.security.PrivilegedExceptionAction; 46 import java.security.SecureClassLoader; 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.Enumeration; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Optional; 53 import java.util.concurrent.ConcurrentHashMap; 54 import java.util.jar.Attributes; 55 import java.util.jar.Manifest; 56 57 import jdk.internal.module.ModulePatcher.PatchedModuleReader; 58 import jdk.internal.misc.VM; 59 import sun.misc.URLClassPath; 60 import sun.misc.Resource; 61 62 63 /** 64 * The extension or application class loader. Resources loaded from modules 65 * defined to the boot class loader are also loaded via an instance of this 66 * ClassLoader type. 67 * 68 * <p> This ClassLoader supports loading of classes and resources from modules. 69 * Modules are defined to the ClassLoader by invoking the {@link #loadModule} 70 * method. Defining a module to this ClassLoader has the effect of making the 71 * types in the module visible. </p> 72 * 73 * <p> This ClassLoader also supports loading of classes and resources from a 74 * class path of URLs that are specified to the ClassLoader at construction 75 * time. The class path may expand at runtime (the Class-Path attribute in JAR 76 * files or via instrumentation agents). </p> 77 * 78 * <p> The delegation model used by this ClassLoader differs to the regular 79 * delegation model. When requested to load a class then this ClassLoader first 80 * maps the class name to its package name. If there is a module defined to a 81 * BuiltinClassLoader containing this package then the class loader delegates 82 * directly to that class loader. If there isn't a module containing the 83 * package then it delegates the search to the parent class loader and if not 84 * found in the parent then it searches the class path. The main difference 85 * between this and the usual delegation model is that it allows the extension 86 * class loader to delegate to the application class loader, important with 87 * upgraded modules defined to the extension class loader. 88 */ 89 90 public class BuiltinClassLoader 91 extends SecureClassLoader 92 { 93 static { 94 if (!ClassLoader.registerAsParallelCapable()) 95 throw new InternalError(); 96 } 97 98 // parent ClassLoader 99 private final BuiltinClassLoader parent; 100 101 // the URL class path or null if there is no class path 102 private final URLClassPath ucp; 103 104 105 /** 106 * A module defined/loaded by a built-in class loader. 107 * 108 * A LoadedModule encapsulates a ModuleReference along with its CodeSource 109 * URL to avoid needing to create this URL when define classes. 110 */ 111 private static class LoadedModule { 112 private final BuiltinClassLoader loader; 113 private final ModuleReference mref; 114 private final URL codeSourceURL; // may be null 115 116 LoadedModule(BuiltinClassLoader loader, ModuleReference mref) { 117 URL url = null; 118 if (mref.location().isPresent()) { 119 try { 120 url = mref.location().get().toURL(); 121 } catch (MalformedURLException e) { } 122 } 123 this.loader = loader; 124 this.mref = mref; 125 this.codeSourceURL = url; 126 } 127 128 BuiltinClassLoader loader() { return loader; } 129 ModuleReference mref() { return mref; } 130 String name() { return mref.descriptor().name(); } 131 URL codeSourceURL() { return codeSourceURL; } 132 } 133 134 135 // maps package name to loaded module for modules in the boot layer 136 private static final Map<String, LoadedModule> packageToModule 137 = new ConcurrentHashMap<>(1024); 138 139 // maps a module name to a module reference 140 private final Map<String, ModuleReference> nameToModule; 141 142 // maps a module reference to a module reader 143 private final Map<ModuleReference, ModuleReader> moduleToReader; 144 145 146 /** 147 * Create a new instance. 148 */ 149 BuiltinClassLoader(BuiltinClassLoader parent, URLClassPath ucp) { 150 // ensure getParent() returns null when the parent is the boot loader 151 super(parent == null || parent == ClassLoaders.bootLoader() ? null : parent); 152 153 this.parent = parent; 154 this.ucp = ucp; 155 156 this.nameToModule = new ConcurrentHashMap<>(); 157 this.moduleToReader = new ConcurrentHashMap<>(); 158 } 159 160 /** 161 * Register a module this this class loader. This has the effect of making 162 * the types in the module visible. 163 */ 164 public void loadModule(ModuleReference mref) { 165 String mn = mref.descriptor().name(); 166 if (nameToModule.putIfAbsent(mn, mref) != null) { 167 throw new InternalError(mn + " already defined to this loader"); 168 } 169 170 LoadedModule loadedModule = new LoadedModule(this, mref); 171 for (String pn : mref.descriptor().packages()) { 172 LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule); 173 if (other != null) { 174 throw new InternalError(pn + " in modules " + mn + " and " 175 + other.mref().descriptor().name()); 176 } 177 } 178 } 179 180 /** 181 * Returns the {@code ModuleReference} for the named module defined to 182 * this class loader; or {@code null} if not defined. 183 * 184 * @param name The name of the module to find 185 */ 186 protected ModuleReference findModule(String name) { 187 return nameToModule.get(name); 188 } 189 190 191 // -- finding resources 192 193 /** 194 * Returns a URL to a resource of the given name in a module defined to 195 * this class loader. 196 */ 197 @Override 198 public URL findResource(String mn, String name) throws IOException { 199 ModuleReference mref = nameToModule.get(mn); 200 if (mref == null) 201 return null; // not defined to this class loader 202 203 URL url; 204 205 try { 206 url = AccessController.doPrivileged( 207 new PrivilegedExceptionAction<URL>() { 208 @Override 209 public URL run() throws IOException { 210 URI u = moduleReaderFor(mref).find(name).orElse(null); 211 if (u != null) { 212 try { 213 return u.toURL(); 214 } catch (MalformedURLException e) { } 215 } 216 return null; 217 } 218 }); 219 } catch (PrivilegedActionException pae) { 220 throw (IOException) pae.getCause(); 221 } 222 223 // check access to the URL 224 return checkURL(url); 225 } 226 227 /** 228 * Returns an input stream to a resource of the given name in a module 229 * defined to this class loader. 230 */ 231 public InputStream findResourceAsStream(String mn, String name) 232 throws IOException 233 { 234 // Need URL to resource when running with a security manager so that 235 // the right permission check is done. 236 SecurityManager sm = System.getSecurityManager(); 237 if (sm != null) { 238 239 URL url = findResource(mn, name); 240 return (url != null) ? url.openStream() : null; 241 242 } else { 243 244 ModuleReference mref = nameToModule.get(mn); 245 if (mref == null) 246 return null; // not defined to this class loader 247 248 try { 249 return AccessController.doPrivileged( 250 new PrivilegedExceptionAction<InputStream>() { 251 @Override 252 public InputStream run() throws IOException { 253 return moduleReaderFor(mref).open(name).orElse(null); 254 } 255 }); 256 } catch (PrivilegedActionException pae) { 257 throw (IOException) pae.getCause(); 258 } 259 } 260 } 261 262 /** 263 * Finds the resource with the given name on the class path of this class 264 * loader. 265 */ 266 @Override 267 public URL findResource(String name) { 268 if (ucp != null) { 269 PrivilegedAction<URL> pa = () -> ucp.findResource(name, false); 270 URL url = AccessController.doPrivileged(pa); 271 return checkURL(url); 272 } else { 273 return null; 274 } 275 } 276 277 /** 278 * Returns an enumeration of URL objects to all the resources with the 279 * given name on the class path of this class loader. 280 */ 281 @Override 282 public Enumeration<URL> findResources(String name) throws IOException { 283 if (ucp != null) { 284 List<URL> result = new ArrayList<>(); 285 PrivilegedAction<Enumeration<URL>> pa = () -> ucp.findResources(name, false); 286 Enumeration<URL> e = AccessController.doPrivileged(pa); 287 while (e.hasMoreElements()) { 288 URL url = checkURL(e.nextElement()); 289 if (url != null) { 290 result.add(url); 291 } 292 } 293 return Collections.enumeration(result); // checked URLs 294 } else { 295 return Collections.emptyEnumeration(); 296 } 297 } 298 299 300 // -- finding/loading classes 301 302 /** 303 * Finds the class with the specified binary name. 304 */ 305 @Override 306 protected Class<?> findClass(String cn) throws ClassNotFoundException { 307 // no class loading until VM is fully initialized 308 if (!VM.isModuleSystemInited()) 309 throw new ClassNotFoundException(cn); 310 311 // find the candidate module for this class 312 LoadedModule loadedModule = findLoadedModule(cn); 313 314 Class<?> c = null; 315 if (loadedModule != null) { 316 317 // attempt to load class in module defined to this loader 318 if (loadedModule.loader() == this) { 319 c = findClassInModuleOrNull(loadedModule, cn); 320 } 321 322 } else { 323 324 // search class path 325 if (ucp != null) { 326 c = findClassOnClassPathOrNull(cn); 327 } 328 329 } 330 331 // not found 332 if (c == null) 333 throw new ClassNotFoundException(cn); 334 335 return c; 336 } 337 338 /** 339 * Finds the class with the specified binary name in a given module. 340 * This method returns {@code null} if the class cannot be found. 341 */ 342 @Override 343 protected Class<?> findClass(String mn, String cn) { 344 ModuleReference mref = nameToModule.get(mn); 345 if (mref == null) 346 return null; // not defined to this class loader 347 348 // find the candidate module for this class 349 LoadedModule loadedModule = findLoadedModule(cn); 350 if (loadedModule == null || !loadedModule.name().equals(mn)) { 351 return null; // module name does not match 352 } 353 354 // attempt to load class in module defined to this loader 355 assert loadedModule.loader() == this; 356 return findClassInModuleOrNull(loadedModule, cn); 357 } 358 359 /** 360 * Loads the class with the specified binary name. 361 */ 362 @Override 363 protected Class<?> loadClass(String cn, boolean resolve) 364 throws ClassNotFoundException 365 { 366 Class<?> c = loadClassOrNull(cn, resolve); 367 if (c == null) 368 throw new ClassNotFoundException(cn); 369 return c; 370 } 371 372 /** 373 * A variation of {@code loadCass} to load a class with the specified 374 * binary name. This method returns {@code null} when the class is not 375 * found. 376 */ 377 protected Class<?> loadClassOrNull(String cn, boolean resolve) { 378 synchronized (getClassLoadingLock(cn)) { 379 // check if already loaded 380 Class<?> c = findLoadedClass(cn); 381 382 if (c == null) { 383 384 // find the candidate module for this class 385 LoadedModule loadedModule = findLoadedModule(cn); 386 if (loadedModule != null) { 387 388 // package is in a module 389 BuiltinClassLoader loader = loadedModule.loader(); 390 if (loader == this) { 391 if (VM.isModuleSystemInited()) { 392 c = findClassInModuleOrNull(loadedModule, cn); 393 } 394 } else { 395 // delegate to the other loader 396 c = loader.loadClassOrNull(cn); 397 } 398 399 } else { 400 401 // check parent 402 if (parent != null) { 403 c = parent.loadClassOrNull(cn); 404 } 405 406 // check class path 407 if (c == null && ucp != null && VM.isModuleSystemInited()) { 408 c = findClassOnClassPathOrNull(cn); 409 } 410 } 411 412 } 413 414 if (resolve && c != null) 415 resolveClass(c); 416 417 return c; 418 } 419 } 420 421 /** 422 * A variation of {@code loadCass} to load a class with the specified 423 * binary name. This method returns {@code null} when the class is not 424 * found. 425 */ 426 protected Class<?> loadClassOrNull(String cn) { 427 return loadClassOrNull(cn, false); 428 } 429 430 /** 431 * Find the candidate loaded module for the given class name. 432 * Returns {@code null} if none of the modules defined to this 433 * class loader contain the API package for the class. 434 */ 435 private LoadedModule findLoadedModule(String cn) { 436 int pos = cn.lastIndexOf('.'); 437 if (pos < 0) 438 return null; // unnamed package 439 440 String pn = cn.substring(0, pos); 441 return packageToModule.get(pn); 442 } 443 444 /** 445 * Finds the class with the specified binary name if in a module 446 * defined to this ClassLoader. 447 * 448 * @return the resulting Class or {@code null} if not found 449 */ 450 private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) { 451 PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule); 452 return AccessController.doPrivileged(pa); 453 } 454 455 /** 456 * Finds the class with the specified binary name on the class path. 457 * 458 * @return the resulting Class or {@code null} if not found 459 */ 460 private Class<?> findClassOnClassPathOrNull(String cn) { 461 return AccessController.doPrivileged( 462 new PrivilegedAction<Class<?>>() { 463 public Class<?> run() { 464 String path = cn.replace('.', '/').concat(".class"); 465 Resource res = ucp.getResource(path, false); 466 if (res != null) { 467 try { 468 return defineClass(cn, res); 469 } catch (IOException ioe) { 470 // TBD on how I/O errors should be propagated 471 } 472 } 473 return null; 474 } 475 }); 476 } 477 478 /** 479 * Defines the given binary class name to the VM, loading the class 480 * bytes from the given module. 481 * 482 * @return the resulting Class or {@code null} if an I/O error occurs 483 */ 484 private Class<?> defineClass(String cn, LoadedModule loadedModule) { 485 ModuleReference mref = loadedModule.mref(); 486 ModuleReader reader = moduleReaderFor(mref); 487 488 try { 489 ByteBuffer bb = null; 490 URL csURL = null; 491 492 // locate class file, special handling for patched modules to 493 // avoid locating the resource twice 494 String rn = cn.replace('.', '/').concat(".class"); 495 if (reader instanceof PatchedModuleReader) { 496 Resource r = ((PatchedModuleReader)reader).findResource(rn); 497 if (r != null) { 498 bb = r.getByteBuffer(); 499 csURL = r.getCodeSourceURL(); 500 } 501 } else { 502 bb = reader.read(rn).orElse(null); 503 csURL = loadedModule.codeSourceURL(); 504 } 505 506 if (bb == null) { 507 // class not found 508 return null; 509 } 510 511 CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null); 512 try { 513 // define class to VM 514 return defineClass(cn, bb, cs); 515 516 } finally { 517 reader.release(bb); 518 } 519 520 } catch (IOException ioe) { 521 // TBD on how I/O errors should be propagated 522 return null; 523 } 524 } 525 526 /** 527 * Defines the given binary class name to the VM, loading the class 528 * bytes via the given Resource object. 529 * 530 * @return the resulting Class 531 * @throws IOException if reading the resource fails 532 * @throws SecurityException if there is a sealing violation (JAR spec) 533 */ 534 private Class<?> defineClass(String cn, Resource res) throws IOException { 535 URL url = res.getCodeSourceURL(); 536 537 // if class is in a named package then ensure that the package is defined 538 int pos = cn.lastIndexOf('.'); 539 if (pos != -1) { 540 String pn = cn.substring(0, pos); 541 Manifest man = res.getManifest(); 542 defineOrCheckPackage(pn, man, url); 543 } 544 545 // defines the class to the runtime 546 ByteBuffer bb = res.getByteBuffer(); 547 if (bb != null) { 548 CodeSigner[] signers = res.getCodeSigners(); 549 CodeSource cs = new CodeSource(url, signers); 550 return defineClass(cn, bb, cs); 551 } else { 552 byte[] b = res.getBytes(); 553 CodeSigner[] signers = res.getCodeSigners(); 554 CodeSource cs = new CodeSource(url, signers); 555 return defineClass(cn, b, 0, b.length, cs); 556 } 557 } 558 559 560 // -- packages 561 562 /** 563 * Defines a package in this ClassLoader. If the package is already defined 564 * then its sealing needs to be checked if sealed by the legacy sealing 565 * mechanism. 566 * 567 * @throws SecurityException if there is a sealing violation (JAR spec) 568 */ 569 protected Package defineOrCheckPackage(String pn, Manifest man, URL url) { 570 Package pkg = getAndVerifyPackage(pn, man, url); 571 if (pkg == null) { 572 try { 573 if (man != null) { 574 pkg = definePackage(pn, man, url); 575 } else { 576 pkg = definePackage(pn, null, null, null, null, null, null, null); 577 } 578 } catch (IllegalArgumentException iae) { 579 // defined by another thread so need to re-verify 580 pkg = getAndVerifyPackage(pn, man, url); 581 if (pkg == null) 582 throw new InternalError("Cannot find package: " + pn); 583 } 584 } 585 return pkg; 586 } 587 588 /** 589 * Get the Package with the specified package name. If defined 590 * then verify that it against the manifest and code source. 591 * 592 * @throws SecurityException if there is a sealing violation (JAR spec) 593 */ 594 private Package getAndVerifyPackage(String pn, Manifest man, URL url) { 595 Package pkg = getDefinedPackage(pn); 596 if (pkg != null) { 597 if (pkg.isSealed()) { 598 if (!pkg.isSealed(url)) { 599 throw new SecurityException( 600 "sealing violation: package " + pn + " is sealed"); 601 } 602 } else { 603 // can't seal package if already defined without sealing 604 if ((man != null) && isSealed(pn, man)) { 605 throw new SecurityException( 606 "sealing violation: can't seal package " + pn + 607 ": already defined"); 608 } 609 } 610 } 611 return pkg; 612 } 613 614 /** 615 * Defines a new package in this ClassLoader. The attributes in the specified 616 * Manifest are use to get the package version and sealing information. 617 * 618 * @throws IllegalArgumentException if the package name duplicates an 619 * existing package either in this class loader or one of its ancestors 620 */ 621 private Package definePackage(String pn, Manifest man, URL url) { 622 String specTitle = null; 623 String specVersion = null; 624 String specVendor = null; 625 String implTitle = null; 626 String implVersion = null; 627 String implVendor = null; 628 String sealed = null; 629 URL sealBase = null; 630 631 if (man != null) { 632 Attributes attr = man.getAttributes(pn.replace('.', '/').concat("/")); 633 if (attr != null) { 634 specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE); 635 specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION); 636 specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); 637 implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); 638 implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); 639 implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); 640 sealed = attr.getValue(Attributes.Name.SEALED); 641 } 642 643 attr = man.getMainAttributes(); 644 if (attr != null) { 645 if (specTitle == null) 646 specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE); 647 if (specVersion == null) 648 specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION); 649 if (specVendor == null) 650 specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); 651 if (implTitle == null) 652 implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); 653 if (implVersion == null) 654 implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); 655 if (implVendor == null) 656 implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); 657 if (sealed == null) 658 sealed = attr.getValue(Attributes.Name.SEALED); 659 } 660 661 // package is sealed 662 if ("true".equalsIgnoreCase(sealed)) 663 sealBase = url; 664 } 665 return definePackage(pn, 666 specTitle, 667 specVersion, 668 specVendor, 669 implTitle, 670 implVersion, 671 implVendor, 672 sealBase); 673 } 674 675 /** 676 * Returns {@code true} if the specified package name is sealed according to 677 * the given manifest. 678 */ 679 private boolean isSealed(String pn, Manifest man) { 680 String path = pn.replace('.', '/').concat("/"); 681 Attributes attr = man.getAttributes(path); 682 String sealed = null; 683 if (attr != null) 684 sealed = attr.getValue(Attributes.Name.SEALED); 685 if (sealed == null && (attr = man.getMainAttributes()) != null) 686 sealed = attr.getValue(Attributes.Name.SEALED); 687 return "true".equalsIgnoreCase(sealed); 688 } 689 690 // -- permissions 691 692 /** 693 * Returns the permissions for the given CodeSource. 694 */ 695 @Override 696 protected PermissionCollection getPermissions(CodeSource cs) { 697 PermissionCollection perms = super.getPermissions(cs); 698 699 // add the permission to access the resource 700 URL url = cs.getLocation(); 701 if (url == null) 702 return perms; 703 Permission p = null; 704 try { 705 p = url.openConnection().getPermission(); 706 if (p != null) { 707 // for directories then need recursive access 708 if (p instanceof FilePermission) { 709 String path = p.getName(); 710 if (path.endsWith(File.separator)) { 711 path += "-"; 712 p = new FilePermission(path, "read"); 713 } 714 } 715 perms.add(p); 716 } 717 } catch (IOException ioe) { } 718 719 return perms; 720 } 721 722 723 // -- miscellaneous supporting methods 724 725 /** 726 * Returns the ModuleReader for the given module. 727 */ 728 private ModuleReader moduleReaderFor(ModuleReference mref) { 729 return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref)); 730 } 731 732 /** 733 * Creates a ModuleReader for the given module. 734 */ 735 private ModuleReader createModuleReader(ModuleReference mref) { 736 try { 737 return mref.open(); 738 } catch (IOException e) { 739 // Return a null module reader to avoid a future class load 740 // attempting to open the module again. 741 return new NullModuleReader(); 742 } 743 } 744 745 /** 746 * A ModuleReader that doesn't read any resources. 747 */ 748 private static class NullModuleReader implements ModuleReader { 749 @Override 750 public Optional<URI> find(String name) { 751 return Optional.empty(); 752 } 753 @Override 754 public void close() { 755 throw new InternalError("Should not get here"); 756 } 757 }; 758 759 /** 760 * Checks access to the given URL. We use URLClassPath for consistent 761 * checking with java.net.URLClassLoader. 762 */ 763 private static URL checkURL(URL url) { 764 return URLClassPath.checkURL(url); 765 } 766 }