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