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