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.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.lang.reflect.Layer; 37 import java.net.MalformedURLException; 38 import java.net.URI; 39 import java.net.URL; 40 import java.nio.ByteBuffer; 41 import java.security.AccessControlContext; 42 import java.security.AccessController; 43 import java.security.CodeSigner; 44 import java.security.CodeSource; 45 import java.security.Permission; 46 import java.security.PermissionCollection; 47 import java.security.PrivilegedAction; 48 import java.security.PrivilegedActionException; 49 import java.security.PrivilegedExceptionAction; 50 import java.security.SecureClassLoader; 51 import java.util.ArrayList; 52 import java.util.Collection; 53 import java.util.Collections; 54 import java.util.Enumeration; 55 import java.util.HashMap; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.Optional; 59 import java.util.concurrent.ConcurrentHashMap; 60 import java.util.stream.Stream; 61 62 import jdk.internal.misc.SharedSecrets; 63 import jdk.internal.module.Resources; 64 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 Layer#defineModulesWithOneLoader 84 * @see Layer#defineModulesWithManyLoaders 85 */ 86 87 public final class Loader extends SecureClassLoader { 88 89 static { 90 ClassLoader.registerAsParallelCapable(); 91 } 92 93 // the loader pool is in a pool, 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 /** 203 * Completes initialization of this Loader. This method populates 204 * remotePackageToLoader with the packages of the remote modules, where 205 * "remote modules" are the modules read by modules defined to this loader. 206 * 207 * @param cf the Configuration containing at least modules to be defined to 208 * this class loader 209 * 210 * @param parentLayers the parent Layers 211 */ 212 public Loader initRemotePackageMap(Configuration cf, 213 List<Layer> parentLayers) 214 { 215 for (String name : nameToModule.keySet()) { 216 ResolvedModule resolvedModule = cf.findModule(name).get(); 217 assert resolvedModule.configuration() == cf; 218 219 for (ResolvedModule other : resolvedModule.reads()) { 220 String mn = other.name(); 221 ClassLoader loader; 222 223 if (other.configuration() == cf) { 224 225 // The module reads another module in the newly created 226 // layer. If all modules are defined to the same class 227 // loader then the packages are local. 228 if (pool == null) { 229 assert nameToModule.containsKey(mn); 230 continue; 231 } 232 233 loader = pool.loaderFor(mn); 234 assert loader != null; 235 236 } else { 237 238 // find the layer for the target module 239 Layer layer = parentLayers.stream() 240 .map(parent -> findLayer(parent, other.configuration())) 241 .flatMap(Optional::stream) 242 .findAny() 243 .orElseThrow(() -> 244 new InternalError("Unable to find parent layer")); 245 246 // find the class loader for the module 247 // For now we use the platform loader for modules defined to the 248 // boot loader 249 assert layer.findModule(mn).isPresent(); 250 loader = layer.findLoader(mn); 251 if (loader == null) 252 loader = ClassLoaders.platformClassLoader(); 253 } 254 255 // find the packages that are exported to the target module 256 String target = resolvedModule.name(); 257 ModuleDescriptor descriptor = other.reference().descriptor(); 258 for (ModuleDescriptor.Exports e : descriptor.exports()) { 259 boolean delegate; 260 if (e.isQualified()) { 261 // qualified export in same configuration 262 delegate = (other.configuration() == cf) 263 && e.targets().contains(target); 264 } else { 265 // unqualified 266 delegate = true; 267 } 268 269 if (delegate) { 270 String pn = e.source(); 271 ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader); 272 if (l != null && l != loader) { 273 throw new IllegalArgumentException("Package " 274 + pn + " cannot be imported from multiple loaders"); 275 } 276 } 277 } 278 } 279 280 } 281 282 return this; 283 } 284 285 /** 286 * Find the layer corresponding to the given configuration in the tree 287 * of layers rooted at the given parent. 288 */ 289 private Optional<Layer> findLayer(Layer parent, Configuration cf) { 290 return SharedSecrets.getJavaLangReflectModuleAccess().layers(parent) 291 .filter(l -> l.configuration() == cf) 292 .findAny(); 293 } 294 295 296 /** 297 * Returns the loader pool that this loader is in or {@code null} if this 298 * loader is not in a loader pool. 299 */ 300 public LoaderPool pool() { 301 return pool; 302 } 303 304 305 // -- resources -- 306 307 308 /** 309 * Returns a URL to a resource of the given name in a module defined to 310 * this class loader. 311 */ 312 @Override 313 protected URL findResource(String mn, String name) throws IOException { 314 ModuleReference mref = (mn != null) ? nameToModule.get(mn) : null; 315 if (mref == null) 316 return null; // not defined to this class loader 317 318 // locate resource 319 URL url = null; 320 try { 321 url = AccessController.doPrivileged( 322 new PrivilegedExceptionAction<URL>() { 323 @Override 324 public URL run() throws IOException { 325 Optional<URI> ouri = moduleReaderFor(mref).find(name); 326 if (ouri.isPresent()) { 327 try { 328 return ouri.get().toURL(); 329 } catch (MalformedURLException | 330 IllegalArgumentException e) { } 331 } 332 return null; 333 } 334 }); 335 } catch (PrivilegedActionException pae) { 336 throw (IOException) pae.getCause(); 337 } 338 339 // check access with permissions restricted by ACC 340 if (url != null && System.getSecurityManager() != null) { 341 try { 342 URL urlToCheck = url; 343 url = AccessController.doPrivileged( 344 new PrivilegedExceptionAction<URL>() { 345 @Override 346 public URL run() throws IOException { 347 return URLClassPath.checkURL(urlToCheck); 348 } 349 }, acc); 350 } catch (PrivilegedActionException pae) { 351 url = null; 352 } 353 } 354 355 return url; 356 } 357 358 @Override 359 public URL findResource(String name) { 360 String pn = Resources.toPackageName(name); 361 LoadedModule module = localPackageToModule.get(pn); 362 363 if (module != null) { 364 try { 365 URL url = findResource(module.name(), name); 366 if (url != null 367 && (name.endsWith(".class") 368 || url.toString().endsWith("/") 369 || isOpen(module.mref(), pn))) { 370 return url; 371 } 372 } catch (IOException ioe) { 373 // ignore 374 } 375 376 } else { 377 for (ModuleReference mref : nameToModule.values()) { 378 try { 379 URL url = findResource(mref.descriptor().name(), name); 380 if (url != null) return url; 381 } catch (IOException ioe) { 382 // ignore 383 } 384 } 385 } 386 387 return null; 388 } 389 390 @Override 391 public Enumeration<URL> findResources(String name) throws IOException { 392 List<URL> urls = new ArrayList<>(); 393 String pn = Resources.toPackageName(name); 394 LoadedModule module = localPackageToModule.get(pn); 395 if (module != null) { 396 try { 397 URL url = findResource(module.name(), name); 398 if (url != null 399 && (name.endsWith(".class") 400 || url.toString().endsWith("/") 401 || isOpen(module.mref(), pn))) { 402 urls.add(url); 403 } 404 } catch (IOException ioe) { 405 // ignore 406 } 407 } else { 408 for (ModuleReference mref : nameToModule.values()) { 409 try { 410 URL url = findResource(mref.descriptor().name(), name); 411 if (url != null) 412 urls.add(url); 413 } catch (IOException ioe) { 414 // ignore 415 } 416 } 417 } 418 return Collections.enumeration(urls); 419 } 420 421 422 // -- finding/loading classes 423 424 /** 425 * Finds the class with the specified binary name. 426 */ 427 @Override 428 protected Class<?> findClass(String cn) throws ClassNotFoundException { 429 Class<?> c = null; 430 LoadedModule loadedModule = findLoadedModule(cn); 431 if (loadedModule != null) 432 c = findClassInModuleOrNull(loadedModule, cn); 433 if (c == null) 434 throw new ClassNotFoundException(cn); 435 return c; 436 } 437 438 /** 439 * Finds the class with the specified binary name in a given module. 440 * This method returns {@code null} if the class cannot be found. 441 */ 442 @Override 443 protected Class<?> findClass(String mn, String cn) { 444 Class<?> c = null; 445 LoadedModule loadedModule = findLoadedModule(cn); 446 if (loadedModule != null && loadedModule.name().equals(mn)) 447 c = findClassInModuleOrNull(loadedModule, cn); 448 return c; 449 } 450 451 /** 452 * Loads the class with the specified binary name. 453 */ 454 @Override 455 protected Class<?> loadClass(String cn, boolean resolve) 456 throws ClassNotFoundException 457 { 458 SecurityManager sm = System.getSecurityManager(); 459 if (sm != null) { 460 String pn = packageName(cn); 461 if (!pn.isEmpty()) { 462 sm.checkPackageAccess(pn); 463 } 464 } 465 466 synchronized (getClassLoadingLock(cn)) { 467 // check if already loaded 468 Class<?> c = findLoadedClass(cn); 469 470 if (c == null) { 471 472 LoadedModule loadedModule = findLoadedModule(cn); 473 474 if (loadedModule != null) { 475 476 // class is in module defined to this class loader 477 c = findClassInModuleOrNull(loadedModule, cn); 478 479 } else { 480 481 // type in another module or visible via the parent loader 482 String pn = packageName(cn); 483 ClassLoader loader = remotePackageToLoader.get(pn); 484 if (loader == null) { 485 // type not in a module read by any of the modules 486 // defined to this loader, so delegate to parent 487 // class loader 488 loader = parent; 489 } 490 if (loader == null) { 491 c = BootLoader.loadClassOrNull(cn); 492 } else { 493 c = loader.loadClass(cn); 494 } 495 496 } 497 } 498 499 if (c == null) 500 throw new ClassNotFoundException(cn); 501 502 if (resolve) 503 resolveClass(c); 504 505 return c; 506 } 507 } 508 509 510 /** 511 * Finds the class with the specified binary name if in a module 512 * defined to this ClassLoader. 513 * 514 * @return the resulting Class or {@code null} if not found 515 */ 516 private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) { 517 PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule); 518 return AccessController.doPrivileged(pa, acc); 519 } 520 521 /** 522 * Defines the given binary class name to the VM, loading the class 523 * bytes from the given module. 524 * 525 * @return the resulting Class or {@code null} if an I/O error occurs 526 */ 527 private Class<?> defineClass(String cn, LoadedModule loadedModule) { 528 ModuleReader reader = moduleReaderFor(loadedModule.mref()); 529 530 try { 531 // read class file 532 String rn = cn.replace('.', '/').concat(".class"); 533 ByteBuffer bb = reader.read(rn).orElse(null); 534 if (bb == null) { 535 // class not found 536 return null; 537 } 538 539 try { 540 return defineClass(cn, bb, loadedModule.codeSource()); 541 } finally { 542 reader.release(bb); 543 } 544 545 } catch (IOException ioe) { 546 // TBD on how I/O errors should be propagated 547 return null; 548 } 549 } 550 551 552 // -- permissions 553 554 /** 555 * Returns the permissions for the given CodeSource. 556 */ 557 @Override 558 protected PermissionCollection getPermissions(CodeSource cs) { 559 PermissionCollection perms = super.getPermissions(cs); 560 561 URL url = cs.getLocation(); 562 if (url == null) 563 return perms; 564 565 // add the permission to access the resource 566 try { 567 Permission p = url.openConnection().getPermission(); 568 if (p != null) { 569 // for directories then need recursive access 570 if (p instanceof FilePermission) { 571 String path = p.getName(); 572 if (path.endsWith(File.separator)) { 573 path += "-"; 574 p = new FilePermission(path, "read"); 575 } 576 } 577 perms.add(p); 578 } 579 } catch (IOException ioe) { } 580 581 return perms; 582 } 583 584 585 // -- miscellaneous supporting methods 586 587 /** 588 * Find the candidate module for the given class name. 589 * Returns {@code null} if none of the modules defined to this 590 * class loader contain the API package for the class. 591 */ 592 private LoadedModule findLoadedModule(String cn) { 593 String pn = packageName(cn); 594 return pn.isEmpty() ? null : localPackageToModule.get(pn); 595 } 596 597 /** 598 * Returns the package name for the given class name 599 */ 600 private String packageName(String cn) { 601 int pos = cn.lastIndexOf('.'); 602 return (pos < 0) ? "" : cn.substring(0, pos); 603 } 604 605 606 /** 607 * Returns the ModuleReader for the given module. 608 */ 609 private ModuleReader moduleReaderFor(ModuleReference mref) { 610 return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref)); 611 } 612 613 /** 614 * Creates a ModuleReader for the given module. 615 */ 616 private ModuleReader createModuleReader(ModuleReference mref) { 617 try { 618 return mref.open(); 619 } catch (IOException e) { 620 // Return a null module reader to avoid a future class load 621 // attempting to open the module again. 622 return new NullModuleReader(); 623 } 624 } 625 626 /** 627 * A ModuleReader that doesn't read any resources. 628 */ 629 private static class NullModuleReader implements ModuleReader { 630 @Override 631 public Optional<URI> find(String name) { 632 return Optional.empty(); 633 } 634 @Override 635 public Stream<String> list() { 636 return Stream.empty(); 637 } 638 @Override 639 public void close() { 640 throw new InternalError("Should not get here"); 641 } 642 } 643 644 /** 645 * Returns true if the given module opens the given package 646 * unconditionally. 647 * 648 * @implNote This method currently iterates over each of the open 649 * packages. This will be replaced once the ModuleDescriptor.Opens 650 * API is updated. 651 */ 652 private boolean isOpen(ModuleReference mref, String pn) { 653 ModuleDescriptor descriptor = mref.descriptor(); 654 if (descriptor.isOpen() || descriptor.isAutomatic()) 655 return true; 656 for (ModuleDescriptor.Opens opens : descriptor.opens()) { 657 String source = opens.source(); 658 if (!opens.isQualified() && source.equals(pn)) { 659 return true; 660 } 661 } 662 return false; 663 } 664 }