1 /* 2 * Copyright (c) 2015, 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 jdk.internal.loader; 27 28 import java.io.File; 29 import java.io.FilePermission; 30 import java.io.IOException; 31 import java.lang.module.Configuration; 32 import java.lang.module.ModuleDescriptor; 33 import java.lang.module.ModuleReader; 34 import java.lang.module.ModuleReference; 35 import java.lang.module.ResolvedModule; 36 import java.net.MalformedURLException; 37 import java.net.URI; 38 import java.net.URL; 39 import java.nio.ByteBuffer; 40 import java.security.AccessControlContext; 41 import java.security.AccessController; 42 import java.security.CodeSigner; 43 import java.security.CodeSource; 44 import java.security.Permission; 45 import java.security.PermissionCollection; 46 import java.security.PrivilegedAction; 47 import java.security.PrivilegedActionException; 48 import java.security.PrivilegedExceptionAction; 49 import java.security.SecureClassLoader; 50 import java.util.ArrayList; 51 import java.util.Collection; 52 import java.util.Collections; 53 import java.util.Enumeration; 54 import java.util.HashMap; 55 import java.util.Iterator; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.Objects; 59 import java.util.Optional; 60 import java.util.concurrent.ConcurrentHashMap; 61 import java.util.stream.Stream; 62 63 import jdk.internal.misc.SharedSecrets; 64 import jdk.internal.module.Resources; 65 66 /** 67 * A class loader that loads classes and resources from a collection of 68 * modules, or from a single module where the class loader is a member 69 * of a pool of class loaders. 70 * 71 * <p> The delegation model used by this ClassLoader differs to the regular 72 * delegation model. When requested to load a class then this ClassLoader first 73 * maps the class name to its package name. If there a module defined to the 74 * Loader containing the package then the class loader attempts to load from 75 * that module. If the package is instead defined to a module in a "remote" 76 * ClassLoader then this class loader delegates directly to that class loader. 77 * The map of package name to remote class loader is created based on the 78 * modules read by modules defined to this class loader. If the package is not 79 * local or remote then this class loader will delegate to the parent class 80 * loader. This allows automatic modules (for example) to link to types in the 81 * unnamed module of the parent class loader. 82 * 83 * @see ModuleLayer#defineModulesWithOneLoader 84 * @see ModuleLayer#defineModulesWithManyLoaders 85 */ 86 87 public final class Loader extends SecureClassLoader { 88 89 static { 90 ClassLoader.registerAsParallelCapable(); 91 } 92 93 // the pool this loader is a member of; can be null 94 private final LoaderPool pool; 95 96 // parent ClassLoader, can be null 97 private final ClassLoader parent; 98 99 // maps a module name to a module reference 100 private final Map<String, ModuleReference> nameToModule; 101 102 // maps package name to a module loaded by this class loader 103 private final Map<String, LoadedModule> localPackageToModule; 104 105 // maps package name to a remote class loader, populated post initialization 106 private final Map<String, ClassLoader> remotePackageToLoader 107 = new ConcurrentHashMap<>(); 108 109 // maps a module reference to a module reader, populated lazily 110 private final Map<ModuleReference, ModuleReader> moduleToReader 111 = new ConcurrentHashMap<>(); 112 113 // ACC used when loading classes and resources 114 private final AccessControlContext acc; 115 116 /** 117 * A module defined/loaded to a {@code Loader}. 118 */ 119 private static class LoadedModule { 120 private final ModuleReference mref; 121 private final URL url; // may be null 122 private final CodeSource cs; 123 124 LoadedModule(ModuleReference mref) { 125 URL url = null; 126 if (mref.location().isPresent()) { 127 try { 128 url = mref.location().get().toURL(); 129 } catch (MalformedURLException | IllegalArgumentException e) { } 130 } 131 this.mref = mref; 132 this.url = url; 133 this.cs = new CodeSource(url, (CodeSigner[]) null); 134 } 135 136 ModuleReference mref() { return mref; } 137 String name() { return mref.descriptor().name(); } 138 URL location() { return url; } 139 CodeSource codeSource() { return cs; } 140 } 141 142 143 /** 144 * Creates a {@code Loader} in a loader pool that loads classes/resources 145 * from one module. 146 */ 147 public Loader(ResolvedModule resolvedModule, 148 LoaderPool pool, 149 ClassLoader parent) 150 { 151 super("Loader-" + resolvedModule.name(), parent); 152 153 this.pool = pool; 154 this.parent = parent; 155 156 ModuleReference mref = resolvedModule.reference(); 157 ModuleDescriptor descriptor = mref.descriptor(); 158 String mn = descriptor.name(); 159 this.nameToModule = Map.of(mn, mref); 160 161 Map<String, LoadedModule> localPackageToModule = new HashMap<>(); 162 LoadedModule lm = new LoadedModule(mref); 163 descriptor.packages().forEach(pn -> localPackageToModule.put(pn, lm)); 164 this.localPackageToModule = localPackageToModule; 165 166 this.acc = AccessController.getContext(); 167 } 168 169 /** 170 * Creates a {@code Loader} that loads classes/resources from a collection 171 * of modules. 172 * 173 * @throws IllegalArgumentException 174 * If two or more modules have the same package 175 */ 176 public Loader(Collection<ResolvedModule> modules, ClassLoader parent) { 177 super(parent); 178 179 this.pool = null; 180 this.parent = parent; 181 182 Map<String, ModuleReference> nameToModule = new HashMap<>(); 183 Map<String, LoadedModule> localPackageToModule = new HashMap<>(); 184 for (ResolvedModule resolvedModule : modules) { 185 ModuleReference mref = resolvedModule.reference(); 186 ModuleDescriptor descriptor = mref.descriptor(); 187 nameToModule.put(descriptor.name(), mref); 188 descriptor.packages().forEach(pn -> { 189 LoadedModule lm = new LoadedModule(mref); 190 if (localPackageToModule.put(pn, lm) != null) 191 throw new IllegalArgumentException("Package " 192 + pn + " in more than one module"); 193 }); 194 } 195 this.nameToModule = nameToModule; 196 this.localPackageToModule = localPackageToModule; 197 198 this.acc = AccessController.getContext(); 199 } 200 201 /** 202 * Completes initialization of this Loader. This method populates 203 * remotePackageToLoader with the packages of the remote modules, where 204 * "remote modules" are the modules read by modules defined to this loader. 205 * 206 * @param cf the Configuration containing at least modules to be defined to 207 * this class loader 208 * 209 * @param parentModuleLayers the parent ModuleLayers 210 */ 211 public Loader initRemotePackageMap(Configuration cf, 212 List<ModuleLayer> parentModuleLayers) 213 { 214 for (String name : nameToModule.keySet()) { 215 ResolvedModule resolvedModule = cf.findModule(name).get(); 216 assert resolvedModule.configuration() == cf; 217 218 for (ResolvedModule other : resolvedModule.reads()) { 219 String mn = other.name(); 220 ClassLoader loader; 221 222 if (other.configuration() == cf) { 223 224 // The module reads another module in the newly created 225 // layer. If all modules are defined to the same class 226 // loader then the packages are local. 227 if (pool == null) { 228 assert nameToModule.containsKey(mn); 229 continue; 230 } 231 232 loader = pool.loaderFor(mn); 233 assert loader != null; 234 235 } else { 236 237 // find the layer for the target module 238 ModuleLayer layer = parentModuleLayers.stream() 239 .map(parent -> findModuleLayer(parent, other.configuration())) 240 .flatMap(Optional::stream) 241 .findAny() 242 .orElseThrow(() -> 243 new InternalError("Unable to find parent layer")); 244 245 // find the class loader for the module 246 // For now we use the platform loader for modules defined to the 247 // boot loader 248 assert layer.findModule(mn).isPresent(); 249 loader = layer.findLoader(mn); 250 if (loader == null) 251 loader = ClassLoaders.platformClassLoader(); 252 } 253 254 // find the packages that are exported to the target module 255 ModuleDescriptor descriptor = other.reference().descriptor(); 256 if (descriptor.isAutomatic()) { 257 ClassLoader l = loader; 258 descriptor.packages().forEach(pn -> remotePackage(pn, l)); 259 } else { 260 String target = resolvedModule.name(); 261 for (ModuleDescriptor.Exports e : descriptor.exports()) { 262 boolean delegate; 263 if (e.isQualified()) { 264 // qualified export in same configuration 265 delegate = (other.configuration() == cf) 266 && e.targets().contains(target); 267 } else { 268 // unqualified 269 delegate = true; 270 } 271 272 if (delegate) { 273 remotePackage(e.source(), loader); 274 } 275 } 276 } 277 } 278 279 } 280 281 return this; 282 } 283 284 /** 285 * Adds to remotePackageToLoader so that an attempt to load a class in 286 * the package delegates to the given class loader. 287 * 288 * @throws IllegalStateException 289 * if the package is already mapped to a different class loader 290 */ 291 private void remotePackage(String pn, ClassLoader loader) { 292 ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader); 293 if (l != null && l != loader) { 294 throw new IllegalStateException("Package " 295 + pn + " cannot be imported from multiple loaders"); 296 } 297 } 298 299 300 /** 301 * Find the layer corresponding to the given configuration in the tree 302 * of layers rooted at the given parent. 303 */ 304 private Optional<ModuleLayer> findModuleLayer(ModuleLayer parent, Configuration cf) { 305 return SharedSecrets.getJavaLangAccess().layers(parent) 306 .filter(l -> l.configuration() == cf) 307 .findAny(); 308 } 309 310 311 /** 312 * Returns the loader pool that this loader is in or {@code null} if this 313 * loader is not in a loader pool. 314 */ 315 public LoaderPool pool() { 316 return pool; 317 } 318 319 320 // -- resources -- 321 322 /** 323 * Returns a URL to a resource of the given name in a module defined to 324 * this class loader. 325 */ 326 @Override 327 protected URL findResource(String mn, String name) throws IOException { 328 ModuleReference mref = (mn != null) ? nameToModule.get(mn) : null; 329 if (mref == null) 330 return null; // not defined to this class loader 331 332 // locate resource 333 URL url = null; 334 try { 335 url = AccessController.doPrivileged( 336 new PrivilegedExceptionAction<URL>() { 337 @Override 338 public URL run() throws IOException { 339 Optional<URI> ouri = moduleReaderFor(mref).find(name); 340 if (ouri.isPresent()) { 341 try { 342 return ouri.get().toURL(); 343 } catch (MalformedURLException | 344 IllegalArgumentException e) { } 345 } 346 return null; 347 } 348 }); 349 } catch (PrivilegedActionException pae) { 350 throw (IOException) pae.getCause(); 351 } 352 353 // check access with permissions restricted by ACC 354 if (url != null && System.getSecurityManager() != null) { 355 try { 356 URL urlToCheck = url; 357 url = AccessController.doPrivileged( 358 new PrivilegedExceptionAction<URL>() { 359 @Override 360 public URL run() throws IOException { 361 return URLClassPath.checkURL(urlToCheck); 362 } 363 }, acc); 364 } catch (PrivilegedActionException pae) { 365 url = null; 366 } 367 } 368 369 return url; 370 } 371 372 @Override 373 public URL findResource(String name) { 374 String pn = Resources.toPackageName(name); 375 LoadedModule module = localPackageToModule.get(pn); 376 377 if (module != null) { 378 try { 379 URL url = findResource(module.name(), name); 380 if (url != null 381 && (name.endsWith(".class") 382 || url.toString().endsWith("/") 383 || isOpen(module.mref(), pn))) { 384 return url; 385 } 386 } catch (IOException ioe) { 387 // ignore 388 } 389 390 } else { 391 for (ModuleReference mref : nameToModule.values()) { 392 try { 393 URL url = findResource(mref.descriptor().name(), name); 394 if (url != null) return url; 395 } catch (IOException ioe) { 396 // ignore 397 } 398 } 399 } 400 401 return null; 402 } 403 404 @Override 405 public Enumeration<URL> findResources(String name) throws IOException { 406 return Collections.enumeration(findResourcesAsList(name)); 407 } 408 409 @Override 410 public URL getResource(String name) { 411 Objects.requireNonNull(name); 412 413 // this loader 414 URL url = findResource(name); 415 if (url == null) { 416 // parent loader 417 if (parent != null) { 418 url = parent.getResource(name); 419 } else { 420 url = BootLoader.findResource(name); 421 } 422 } 423 return url; 424 } 425 426 @Override 427 public Enumeration<URL> getResources(String name) throws IOException { 428 Objects.requireNonNull(name); 429 430 // this loader 431 List<URL> urls = findResourcesAsList(name); 432 433 // parent loader 434 Enumeration<URL> e; 435 if (parent != null) { 436 e = parent.getResources(name); 437 } else { 438 e = BootLoader.findResources(name); 439 } 440 441 // concat the URLs with the URLs returned by the parent 442 return new Enumeration<>() { 443 final Iterator<URL> iterator = urls.iterator(); 444 @Override 445 public boolean hasMoreElements() { 446 return (iterator.hasNext() || e.hasMoreElements()); 447 } 448 @Override 449 public URL nextElement() { 450 if (iterator.hasNext()) { 451 return iterator.next(); 452 } else { 453 return e.nextElement(); 454 } 455 } 456 }; 457 } 458 459 /** 460 * Finds the resources with the given name in this class loader. 461 */ 462 private List<URL> findResourcesAsList(String name) throws IOException { 463 String pn = Resources.toPackageName(name); 464 LoadedModule module = localPackageToModule.get(pn); 465 if (module != null) { 466 URL url = findResource(module.name(), name); 467 if (url != null 468 && (name.endsWith(".class") 469 || url.toString().endsWith("/") 470 || isOpen(module.mref(), pn))) { 471 return List.of(url); 472 } else { 473 return Collections.emptyList(); 474 } 475 } else { 476 List<URL> urls = new ArrayList<>(); 477 for (ModuleReference mref : nameToModule.values()) { 478 URL url = findResource(mref.descriptor().name(), name); 479 if (url != null) { 480 urls.add(url); 481 } 482 } 483 return urls; 484 } 485 } 486 487 488 // -- finding/loading classes 489 490 /** 491 * Finds the class with the specified binary name. 492 */ 493 @Override 494 protected Class<?> findClass(String cn) throws ClassNotFoundException { 495 Class<?> c = null; 496 LoadedModule loadedModule = findLoadedModule(cn); 497 if (loadedModule != null) 498 c = findClassInModuleOrNull(loadedModule, cn); 499 if (c == null) 500 throw new ClassNotFoundException(cn); 501 return c; 502 } 503 504 /** 505 * Finds the class with the specified binary name in the given module. 506 * This method returns {@code null} if the class cannot be found. 507 */ 508 @Override 509 protected Class<?> findClass(String mn, String cn) { 510 Class<?> c = null; 511 LoadedModule loadedModule = findLoadedModule(cn); 512 if (loadedModule != null && loadedModule.name().equals(mn)) 513 c = findClassInModuleOrNull(loadedModule, cn); 514 return c; 515 } 516 517 /** 518 * Loads the class with the specified binary name. 519 */ 520 @Override 521 protected Class<?> loadClass(String cn, boolean resolve) 522 throws ClassNotFoundException 523 { 524 SecurityManager sm = System.getSecurityManager(); 525 if (sm != null) { 526 String pn = packageName(cn); 527 if (!pn.isEmpty()) { 528 sm.checkPackageAccess(pn); 529 } 530 } 531 532 synchronized (getClassLoadingLock(cn)) { 533 // check if already loaded 534 Class<?> c = findLoadedClass(cn); 535 536 if (c == null) { 537 538 LoadedModule loadedModule = findLoadedModule(cn); 539 540 if (loadedModule != null) { 541 542 // class is in module defined to this class loader 543 c = findClassInModuleOrNull(loadedModule, cn); 544 545 } else { 546 547 // type in another module or visible via the parent loader 548 String pn = packageName(cn); 549 ClassLoader loader = remotePackageToLoader.get(pn); 550 if (loader == null) { 551 // type not in a module read by any of the modules 552 // defined to this loader, so delegate to parent 553 // class loader 554 loader = parent; 555 } 556 if (loader == null) { 557 c = BootLoader.loadClassOrNull(cn); 558 } else { 559 c = loader.loadClass(cn); 560 } 561 562 } 563 } 564 565 if (c == null) 566 throw new ClassNotFoundException(cn); 567 568 if (resolve) 569 resolveClass(c); 570 571 return c; 572 } 573 } 574 575 576 /** 577 * Finds the class with the specified binary name if in a module 578 * defined to this ClassLoader. 579 * 580 * @return the resulting Class or {@code null} if not found 581 */ 582 private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) { 583 PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule); 584 return AccessController.doPrivileged(pa, acc); 585 } 586 587 /** 588 * Defines the given binary class name to the VM, loading the class 589 * bytes from the given module. 590 * 591 * @return the resulting Class or {@code null} if an I/O error occurs 592 */ 593 private Class<?> defineClass(String cn, LoadedModule loadedModule) { 594 ModuleReader reader = moduleReaderFor(loadedModule.mref()); 595 596 try { 597 // read class file 598 String rn = cn.replace('.', '/').concat(".class"); 599 ByteBuffer bb = reader.read(rn).orElse(null); 600 if (bb == null) { 601 // class not found 602 return null; 603 } 604 605 try { 606 return defineClass(cn, bb, loadedModule.codeSource()); 607 } finally { 608 reader.release(bb); 609 } 610 611 } catch (IOException ioe) { 612 // TBD on how I/O errors should be propagated 613 return null; 614 } 615 } 616 617 618 // -- permissions 619 620 /** 621 * Returns the permissions for the given CodeSource. 622 */ 623 @Override 624 protected PermissionCollection getPermissions(CodeSource cs) { 625 PermissionCollection perms = super.getPermissions(cs); 626 627 URL url = cs.getLocation(); 628 if (url == null) 629 return perms; 630 631 // add the permission to access the resource 632 try { 633 Permission p = url.openConnection().getPermission(); 634 if (p != null) { 635 // for directories then need recursive access 636 if (p instanceof FilePermission) { 637 String path = p.getName(); 638 if (path.endsWith(File.separator)) { 639 path += "-"; 640 p = new FilePermission(path, "read"); 641 } 642 } 643 perms.add(p); 644 } 645 } catch (IOException ioe) { } 646 647 return perms; 648 } 649 650 651 // -- miscellaneous supporting methods 652 653 /** 654 * Find the candidate module for the given class name. 655 * Returns {@code null} if none of the modules defined to this 656 * class loader contain the API package for the class. 657 */ 658 private LoadedModule findLoadedModule(String cn) { 659 String pn = packageName(cn); 660 return pn.isEmpty() ? null : localPackageToModule.get(pn); 661 } 662 663 /** 664 * Returns the package name for the given class name 665 */ 666 private String packageName(String cn) { 667 int pos = cn.lastIndexOf('.'); 668 return (pos < 0) ? "" : cn.substring(0, pos); 669 } 670 671 672 /** 673 * Returns the ModuleReader for the given module. 674 */ 675 private ModuleReader moduleReaderFor(ModuleReference mref) { 676 return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref)); 677 } 678 679 /** 680 * Creates a ModuleReader for the given module. 681 */ 682 private ModuleReader createModuleReader(ModuleReference mref) { 683 try { 684 return mref.open(); 685 } catch (IOException e) { 686 // Return a null module reader to avoid a future class load 687 // attempting to open the module again. 688 return new NullModuleReader(); 689 } 690 } 691 692 /** 693 * A ModuleReader that doesn't read any resources. 694 */ 695 private static class NullModuleReader implements ModuleReader { 696 @Override 697 public Optional<URI> find(String name) { 698 return Optional.empty(); 699 } 700 @Override 701 public Stream<String> list() { 702 return Stream.empty(); 703 } 704 @Override 705 public void close() { 706 throw new InternalError("Should not get here"); 707 } 708 } 709 710 /** 711 * Returns true if the given module opens the given package 712 * unconditionally. 713 * 714 * @implNote This method currently iterates over each of the open 715 * packages. This will be replaced once the ModuleDescriptor.Opens 716 * API is updated. 717 */ 718 private boolean isOpen(ModuleReference mref, String pn) { 719 ModuleDescriptor descriptor = mref.descriptor(); 720 if (descriptor.isOpen() || descriptor.isAutomatic()) 721 return true; 722 for (ModuleDescriptor.Opens opens : descriptor.opens()) { 723 String source = opens.source(); 724 if (!opens.isQualified() && source.equals(pn)) { 725 return true; 726 } 727 } 728 return false; 729 } 730 }