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