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 64 65 /** 66 * A class loader that loads classes and resources from a collection of 67 * modules, or from a single module where the class loader is a member 68 * of a pool of class loaders. 69 * 70 * <p> The delegation model used by this ClassLoader differs to the regular 71 * delegation model. When requested to load a class then this ClassLoader first 72 * maps the class name to its package name. If there a module defined to the 73 * Loader containing the package then the class loader attempts to load from 74 * that module. If the package is instead defined to a module in a "remote" 75 * ClassLoader then this class loader delegates directly to that class loader. 76 * The map of package name to remote class loader is created based on the 77 * modules read by modules defined to this class loader. If the package is not 78 * local or remote then this class loader will delegate to the parent class 79 * loader. This allows automatic modules (for example) to link to types in the 80 * unnamed module of the parent class loader. 81 * 82 * @see Layer#defineModulesWithOneLoader 83 * @see Layer#defineModulesWithManyLoaders 84 */ 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 // maps package name to a remote class loader, populated post initialization 105 private final Map<String, ClassLoader> remotePackageToLoader 106 = new ConcurrentHashMap<>(); 107 108 // maps a module reference to a module reader, populated lazily 109 private final Map<ModuleReference, ModuleReader> moduleToReader 110 = new ConcurrentHashMap<>(); 111 112 // ACC used when loading classes and resources */ 113 private final AccessControlContext acc; 114 115 /** 116 * A module defined/loaded to a {@code Loader}. 117 */ 118 private static class LoadedModule { 119 private final ModuleReference mref; 120 private final URL url; // may be null 121 private final CodeSource cs; 122 123 LoadedModule(ModuleReference mref) { 124 URL url = null; 125 if (mref.location().isPresent()) { 126 try { 127 url = mref.location().get().toURL(); 128 } catch (MalformedURLException | IllegalArgumentException e) { } 129 } 130 this.mref = mref; 131 this.url = url; 132 this.cs = new CodeSource(url, (CodeSigner[]) null); 133 } 134 135 ModuleReference mref() { return mref; } 136 String name() { return mref.descriptor().name(); } 137 URL location() { return url; } 138 CodeSource codeSource() { return cs; } 139 } 140 141 142 /** 143 * Creates a {@code Loader} in a loader pool that loads classes/resources 144 * from one module. 145 */ 146 public Loader(ResolvedModule resolvedModule, 147 LoaderPool pool, 148 ClassLoader parent) 149 { 150 super("Loader-" + resolvedModule.name(), parent); 151 152 this.pool = pool; 153 this.parent = parent; 154 155 ModuleReference mref = resolvedModule.reference(); 156 ModuleDescriptor descriptor = mref.descriptor(); 157 String mn = descriptor.name(); 158 this.nameToModule = Map.of(mn, mref); 159 160 Map<String, LoadedModule> localPackageToModule = new HashMap<>(); 161 LoadedModule lm = new LoadedModule(mref); 162 descriptor.packages().forEach(pn -> localPackageToModule.put(pn, lm)); 163 this.localPackageToModule = localPackageToModule; 164 165 this.acc = AccessController.getContext(); 166 } 167 168 /** 169 * Creates a {@code Loader} that loads classes/resources from a collection 170 * of modules. 171 * 172 * @throws IllegalArgumentException 173 * If two or more modules have the same package 174 */ 175 public Loader(Collection<ResolvedModule> modules, ClassLoader parent) { 176 super(parent); 177 178 this.pool = null; 179 this.parent = parent; 180 181 Map<String, ModuleReference> nameToModule = new HashMap<>(); 182 Map<String, LoadedModule> localPackageToModule = new HashMap<>(); 183 for (ResolvedModule resolvedModule : modules) { 184 ModuleReference mref = resolvedModule.reference(); 185 ModuleDescriptor descriptor = mref.descriptor(); 186 nameToModule.put(descriptor.name(), mref); 187 descriptor.packages().forEach(pn -> { 188 LoadedModule lm = new LoadedModule(mref); 189 if (localPackageToModule.put(pn, lm) != null) 190 throw new IllegalArgumentException("Package " 191 + pn + " in more than one module"); 192 }); 193 } 194 this.nameToModule = nameToModule; 195 this.localPackageToModule = localPackageToModule; 196 197 this.acc = AccessController.getContext(); 198 } 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 parentLayers the parent Layers 210 */ 211 public Loader initRemotePackageMap(Configuration cf, 212 List<Layer> parentLayers) 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 Layer layer = parentLayers.stream() 239 .map(parent -> findLayer(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 String target = resolvedModule.name(); 256 ModuleDescriptor descriptor = other.reference().descriptor(); 257 for (ModuleDescriptor.Exports e : descriptor.exports()) { 258 boolean delegate; 259 if (e.isQualified()) { 260 // qualified export in same configuration 261 delegate = (other.configuration() == cf) 262 && e.targets().contains(target); 263 } else { 264 // unqualified 265 delegate = true; 266 } 267 268 if (delegate) { 269 String pn = e.source(); 270 ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader); 271 if (l != null && l != loader) { 272 throw new IllegalArgumentException("Package " 273 + pn + " cannot be imported from multiple loaders"); 274 } 275 } 276 } 277 } 278 279 } 280 281 return this; 282 } 283 284 /** 285 * Find the layer corresponding to the given configuration in the tree 286 * of layers rooted at the given parent. 287 */ 288 private Optional<Layer> findLayer(Layer parent, Configuration cf) { 289 return SharedSecrets.getJavaLangReflectModuleAccess().layers(parent) 290 .filter(l -> l.configuration() == cf) 291 .findAny(); 292 } 293 294 295 /** 296 * Returns the loader pool that this loader is in or {@code null} if this 297 * loader is not in a loader pool. 298 */ 299 public LoaderPool pool() { 300 return pool; 301 } 302 303 304 // -- resources -- 305 306 307 /** 308 * Returns a URL to a resource of the given name in a module defined to 309 * this class loader. 310 */ 311 @Override 312 protected URL findResource(String mn, String name) throws IOException { 313 ModuleReference mref = (mn != null) ? nameToModule.get(mn) : null; 314 if (mref == null) 315 return null; // not defined to this class loader 316 317 // locate resource 318 URL url = null; 319 try { 320 url = AccessController.doPrivileged( 321 new PrivilegedExceptionAction<URL>() { 322 @Override 323 public URL run() throws IOException { 324 Optional<URI> ouri = moduleReaderFor(mref).find(name); 325 if (ouri.isPresent()) { 326 try { 327 return ouri.get().toURL(); 328 } catch (MalformedURLException | 329 IllegalArgumentException e) { } 330 } 331 return null; 332 } 333 }); 334 } catch (PrivilegedActionException pae) { 335 throw (IOException) pae.getCause(); 336 } 337 338 // check access with permissions restricted by ACC 339 if (url != null && System.getSecurityManager() != null) { 340 try { 341 URL urlToCheck = url; 342 url = AccessController.doPrivileged( 343 new PrivilegedExceptionAction<URL>() { 344 @Override 345 public URL run() throws IOException { 346 return URLClassPath.checkURL(urlToCheck); 347 } 348 }, acc); 349 } catch (PrivilegedActionException pae) { 350 url = null; 351 } 352 } 353 354 return url; 355 } 356 357 @Override 358 public URL findResource(String name) { 359 URL url = null; 360 String pn = ResourceHelper.getPackageName(name); 361 LoadedModule module = localPackageToModule.get(pn); 362 if (module != null) { 363 if (name.endsWith(".class") || isOpen(module.mref(), pn)) { 364 try { 365 url = findResource(module.name(), name); 366 } catch (IOException ioe) { 367 // ignore 368 } 369 } 370 } else { 371 for (ModuleReference mref : nameToModule.values()) { 372 try { 373 url = findResource(mref.descriptor().name(), name); 374 if (url != null) 375 break; 376 } catch (IOException ioe) { 377 // ignore 378 } 379 } 380 } 381 return url; 382 } 383 384 @Override 385 public Enumeration<URL> findResources(String name) throws IOException { 386 List<URL> urls = new ArrayList<>(); 387 String pn = ResourceHelper.getPackageName(name); 388 LoadedModule module = localPackageToModule.get(pn); 389 if (module != null) { 390 if (name.endsWith(".class") || isOpen(module.mref(), pn)) { 391 try { 392 URL url = findResource(module.name(), name); 393 if (url != null) 394 urls.add(url); 395 } catch (IOException ioe) { 396 // ignore 397 } 398 } 399 } else { 400 for (ModuleReference mref : nameToModule.values()) { 401 try { 402 URL url = findResource(mref.descriptor().name(), name); 403 if (url != null) 404 urls.add(url); 405 } catch (IOException ioe) { 406 // ignore 407 } 408 } 409 } 410 return Collections.enumeration(urls); 411 } 412 413 414 // -- finding/loading classes 415 416 /** 417 * Finds the class with the specified binary name. 418 */ 419 @Override 420 protected Class<?> findClass(String cn) throws ClassNotFoundException { 421 Class<?> c = null; 422 LoadedModule loadedModule = findLoadedModule(cn); 423 if (loadedModule != null) 424 c = findClassInModuleOrNull(loadedModule, cn); 425 if (c == null) 426 throw new ClassNotFoundException(cn); 427 return c; 428 } 429 430 /** 431 * Finds the class with the specified binary name in a given module. 432 * This method returns {@code null} if the class cannot be found. 433 */ 434 @Override 435 protected Class<?> findClass(String mn, String cn) { 436 Class<?> c = null; 437 LoadedModule loadedModule = findLoadedModule(cn); 438 if (loadedModule != null && loadedModule.name().equals(mn)) 439 c = findClassInModuleOrNull(loadedModule, cn); 440 return c; 441 } 442 443 /** 444 * Loads the class with the specified binary name. 445 */ 446 @Override 447 protected Class<?> loadClass(String cn, boolean resolve) 448 throws ClassNotFoundException 449 { 450 SecurityManager sm = System.getSecurityManager(); 451 if (sm != null) { 452 String pn = packageName(cn); 453 if (!pn.isEmpty()) { 454 sm.checkPackageAccess(pn); 455 } 456 } 457 458 synchronized (getClassLoadingLock(cn)) { 459 // check if already loaded 460 Class<?> c = findLoadedClass(cn); 461 462 if (c == null) { 463 464 LoadedModule loadedModule = findLoadedModule(cn); 465 466 if (loadedModule != null) { 467 468 // class is in module defined to this class loader 469 c = findClassInModuleOrNull(loadedModule, cn); 470 471 } else { 472 473 // type in another module or visible via the parent loader 474 String pn = packageName(cn); 475 ClassLoader loader = remotePackageToLoader.get(pn); 476 if (loader == null) { 477 // type not in a module read by any of the modules 478 // defined to this loader, so delegate to parent 479 // class loader 480 loader = parent; 481 } 482 if (loader == null) { 483 c = BootLoader.loadClassOrNull(cn); 484 } else { 485 c = loader.loadClass(cn); 486 } 487 488 } 489 } 490 491 if (c == null) 492 throw new ClassNotFoundException(cn); 493 494 if (resolve) 495 resolveClass(c); 496 497 return c; 498 } 499 } 500 501 502 /** 503 * Finds the class with the specified binary name if in a module 504 * defined to this ClassLoader. 505 * 506 * @return the resulting Class or {@code null} if not found 507 */ 508 private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) { 509 PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule); 510 return AccessController.doPrivileged(pa, acc); 511 } 512 513 /** 514 * Defines the given binary class name to the VM, loading the class 515 * bytes from the given module. 516 * 517 * @return the resulting Class or {@code null} if an I/O error occurs 518 */ 519 private Class<?> defineClass(String cn, LoadedModule loadedModule) { 520 ModuleReader reader = moduleReaderFor(loadedModule.mref()); 521 522 try { 523 // read class file 524 String rn = cn.replace('.', '/').concat(".class"); 525 ByteBuffer bb = reader.read(rn).orElse(null); 526 if (bb == null) { 527 // class not found 528 return null; 529 } 530 531 try { 532 return defineClass(cn, bb, loadedModule.codeSource()); 533 } finally { 534 reader.release(bb); 535 } 536 537 } catch (IOException ioe) { 538 // TBD on how I/O errors should be propagated 539 return null; 540 } 541 } 542 543 544 // -- permissions 545 546 /** 547 * Returns the permissions for the given CodeSource. 548 */ 549 @Override 550 protected PermissionCollection getPermissions(CodeSource cs) { 551 PermissionCollection perms = super.getPermissions(cs); 552 553 URL url = cs.getLocation(); 554 if (url == null) 555 return perms; 556 557 // add the permission to access the resource 558 try { 559 Permission p = url.openConnection().getPermission(); 560 if (p != null) { 561 // for directories then need recursive access 562 if (p instanceof FilePermission) { 563 String path = p.getName(); 564 if (path.endsWith(File.separator)) { 565 path += "-"; 566 p = new FilePermission(path, "read"); 567 } 568 } 569 perms.add(p); 570 } 571 } catch (IOException ioe) { } 572 573 return perms; 574 } 575 576 577 // -- miscellaneous supporting methods 578 579 /** 580 * Find the candidate module for the given class name. 581 * Returns {@code null} if none of the modules defined to this 582 * class loader contain the API package for the class. 583 */ 584 private LoadedModule findLoadedModule(String cn) { 585 String pn = packageName(cn); 586 return pn.isEmpty() ? null : localPackageToModule.get(pn); 587 } 588 589 /** 590 * Returns the package name for the given class name 591 */ 592 private String packageName(String cn) { 593 int pos = cn.lastIndexOf('.'); 594 return (pos < 0) ? "" : cn.substring(0, pos); 595 } 596 597 598 /** 599 * Returns the ModuleReader for the given module. 600 */ 601 private ModuleReader moduleReaderFor(ModuleReference mref) { 602 return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref)); 603 } 604 605 /** 606 * Creates a ModuleReader for the given module. 607 */ 608 private ModuleReader createModuleReader(ModuleReference mref) { 609 try { 610 return mref.open(); 611 } catch (IOException e) { 612 // Return a null module reader to avoid a future class load 613 // attempting to open the module again. 614 return new NullModuleReader(); 615 } 616 } 617 618 /** 619 * A ModuleReader that doesn't read any resources. 620 */ 621 private static class NullModuleReader implements ModuleReader { 622 @Override 623 public Optional<URI> find(String name) { 624 return Optional.empty(); 625 } 626 @Override 627 public Stream<String> list() { 628 return Stream.empty(); 629 } 630 @Override 631 public void close() { 632 throw new InternalError("Should not get here"); 633 } 634 } 635 636 /** 637 * Returns true if the given module opens the given package 638 * unconditionally. 639 * 640 * @implNote This method currently iterates over each of the open 641 * packages. This will be replaced once the ModuleDescriptor.Opens 642 * API is updated. 643 */ 644 private boolean isOpen(ModuleReference mref, String pn) { 645 ModuleDescriptor descriptor = mref.descriptor(); 646 if (descriptor.isOpen()) 647 return true; 648 for (ModuleDescriptor.Opens opens : descriptor.opens()) { 649 String source = opens.source(); 650 if (!opens.isQualified() && source.equals(pn)) { 651 return true; 652 } 653 } 654 return false; 655 } 656 }