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.Collection; 52 import java.util.HashMap; 53 import java.util.Map; 54 import java.util.Optional; 55 import java.util.concurrent.ConcurrentHashMap; 56 57 58 /** 59 * A class loader that loads classes and resources from a collection of 60 * modules, or from a single module where the class loader is a member 61 * of a pool of class loaders. 62 * 63 * <p> The delegation model used by this ClassLoader differs to the regular 64 * delegation model. When requested to load a class then this ClassLoader first 65 * maps the class name to its package name. If there a module defined to the 66 * Loader containing the package then the class loader attempts to load from 67 * that module. If the package is instead defined to a module in a "remote" 68 * ClassLoader then this class loader delegates directly to that class loader. 69 * The map of package name to remote class loader is created based on the 70 * modules read by modules defined to this class loader. If the package is not 71 * local or remote then this class loader will delegate to the parent class 72 * loader. This allows automatic modules (for example) to link to types in the 73 * unnamed module of the parent class loader. 74 * 75 * @see Layer#createWithOneLoader 76 * @see Layer#createWithManyLoaders 77 */ 78 79 public final class Loader extends SecureClassLoader { 80 81 static { 82 ClassLoader.registerAsParallelCapable(); 83 } 84 85 // the loader pool is in a pool, can be null 86 private final LoaderPool pool; 87 88 // parent ClassLoader, can be null 89 private final ClassLoader parent; 90 91 // maps a module name to a module reference 92 private final Map<String, ModuleReference> nameToModule; 93 94 // maps package name to a module loaded by this class loader 95 private final Map<String, LoadedModule> localPackageToModule; 96 97 // maps package name to a remote class loader, populated post initialization 98 private final Map<String, ClassLoader> remotePackageToLoader 99 = new ConcurrentHashMap<>(); 100 101 // maps a module reference to a module reader, populated lazily 102 private final Map<ModuleReference, ModuleReader> moduleToReader 103 = new ConcurrentHashMap<>(); 104 105 // ACC used when loading classes and resources */ 106 private final AccessControlContext acc; 107 108 /** 109 * A module defined/loaded to a {@code Loader}. 110 */ 111 private static class LoadedModule { 112 private final ModuleReference mref; 113 private final URL url; // may be null 114 private final CodeSource cs; 115 116 LoadedModule(ModuleReference mref) { 117 URL url = null; 118 if (mref.location().isPresent()) { 119 try { 120 url = mref.location().get().toURL(); 121 } catch (MalformedURLException e) { } 122 } 123 this.mref = mref; 124 this.url = url; 125 this.cs = new CodeSource(url, (CodeSigner[]) null); 126 } 127 128 ModuleReference mref() { return mref; } 129 String name() { return mref.descriptor().name(); } 130 URL location() { return url; } 131 CodeSource codeSource() { return cs; } 132 } 133 134 135 /** 136 * Creates a {@code Loader} in a loader pool that loads classes/resources 137 * from one module. 138 */ 139 public Loader(ResolvedModule resolvedModule, 140 LoaderPool pool, 141 ClassLoader parent) 142 { 143 super(parent); 144 145 this.pool = pool; 146 this.parent = parent; 147 148 ModuleReference mref = resolvedModule.reference(); 149 ModuleDescriptor descriptor = mref.descriptor(); 150 String mn = descriptor.name(); 151 this.nameToModule = Map.of(mn, mref); 152 153 Map<String, LoadedModule> localPackageToModule = new HashMap<>(); 154 LoadedModule lm = new LoadedModule(mref); 155 descriptor.packages().forEach(pn -> localPackageToModule.put(pn, lm)); 156 this.localPackageToModule = localPackageToModule; 157 158 this.acc = AccessController.getContext(); 159 } 160 161 /** 162 * Creates a {@code Loader} that loads classes/resources from a collection 163 * of modules. 164 * 165 * @throws IllegalArgumentException 166 * If two or more modules have the same package 167 */ 168 public Loader(Collection<ResolvedModule> modules, ClassLoader parent) { 169 super(parent); 170 171 this.pool = null; 172 this.parent = parent; 173 174 Map<String, ModuleReference> nameToModule = new HashMap<>(); 175 Map<String, LoadedModule> localPackageToModule = new HashMap<>(); 176 for (ResolvedModule resolvedModule : modules) { 177 ModuleReference mref = resolvedModule.reference(); 178 ModuleDescriptor descriptor = mref.descriptor(); 179 nameToModule.put(descriptor.name(), mref); 180 descriptor.packages().forEach(pn -> { 181 LoadedModule lm = new LoadedModule(mref); 182 if (localPackageToModule.put(pn, lm) != null) 183 throw new IllegalArgumentException("Package " 184 + pn + " in more than one module"); 185 }); 186 } 187 this.nameToModule = nameToModule; 188 this.localPackageToModule = localPackageToModule; 189 190 this.acc = AccessController.getContext(); 191 } 192 193 194 /** 195 * Completes initialization of this Loader. This method populates 196 * remotePackageToLoader with the packages of the remote modules, where 197 * "remote modules" are the modules read by modules defined to this loader. 198 * 199 * @param cf the Configuration containing at least modules to be defined to 200 * this class loader 201 * 202 * @param parentLayer the parent Layer 203 */ 204 public Loader initRemotePackageMap(Configuration cf, Layer parentLayer) { 205 206 for (String name : nameToModule.keySet()) { 207 208 ResolvedModule resolvedModule = cf.findModule(name).get(); 209 assert resolvedModule.configuration() == cf; 210 211 for (ResolvedModule other : resolvedModule.reads()) { 212 String mn = other.name(); 213 ClassLoader loader; 214 215 if (other.configuration() == cf) { 216 217 // The module reads another module in the newly created 218 // layer. If all modules are defined to the same class 219 // loader then the packages are local. 220 if (pool == null) { 221 assert nameToModule.containsKey(mn); 222 continue; 223 } 224 225 loader = pool.loaderFor(mn); 226 assert loader != null; 227 228 } else { 229 230 // find the layer contains the module that is read 231 Layer layer = parentLayer; 232 while (layer != null) { 233 if (layer.configuration() == other.configuration()) { 234 break; 235 } 236 layer = layer.parent().orElse(null); 237 } 238 assert layer != null; 239 240 // find the class loader for the module in the layer 241 // For now we use the platform loader for modules defined to the 242 // boot loader 243 assert layer.findModule(mn).isPresent(); 244 loader = layer.findLoader(mn); 245 if (loader == null) 246 loader = ClassLoaders.platformClassLoader(); 247 } 248 249 // find the packages that are exported to the target module 250 String target = resolvedModule.name(); 251 ModuleDescriptor descriptor = other.reference().descriptor(); 252 for (ModuleDescriptor.Exports e : descriptor.exports()) { 253 boolean delegate; 254 if (e.isQualified()) { 255 // qualified export in same configuration 256 delegate = (other.configuration() == cf) 257 && e.targets().contains(target); 258 } else { 259 // unqualified 260 delegate = true; 261 } 262 263 if (delegate) { 264 String pn = e.source(); 265 ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader); 266 if (l != null && l != loader) { 267 throw new IllegalArgumentException("Package " 268 + pn + " cannot be imported from multiple loaders"); 269 } 270 271 } 272 } 273 } 274 275 } 276 277 return this; 278 } 279 280 /** 281 * Returns the loader pool that this loader is in or {@code null} if this 282 * loader is not in a loader pool. 283 */ 284 public LoaderPool pool() { 285 return pool; 286 } 287 288 289 // -- resources -- 290 291 292 /** 293 * Returns a URL to a resource of the given name in a module defined to 294 * this class loader. 295 */ 296 @Override 297 protected URL findResource(String mn, String name) throws IOException { 298 ModuleReference mref = nameToModule.get(mn); 299 if (mref == null) 300 return null; // not defined to this class loader 301 302 try { 303 return AccessController.doPrivileged( 304 new PrivilegedExceptionAction<URL>() { 305 @Override 306 public URL run() throws IOException { 307 Optional<URI> ouri = moduleReaderFor(mref).find(name); 308 if (ouri.isPresent()) { 309 try { 310 return ouri.get().toURL(); 311 } catch (MalformedURLException e) { } 312 } 313 return null; 314 } 315 }, acc); 316 } catch (PrivilegedActionException pae) { 317 throw (IOException) pae.getCause(); 318 } 319 } 320 321 322 // -- finding/loading classes 323 324 /** 325 * Finds the class with the specified binary name. 326 */ 327 @Override 328 protected Class<?> findClass(String cn) throws ClassNotFoundException { 329 Class<?> c = null; 330 LoadedModule loadedModule = findLoadedModule(cn); 331 if (loadedModule != null) 332 c = findClassInModuleOrNull(loadedModule, cn); 333 if (c == null) 334 throw new ClassNotFoundException(cn); 335 return c; 336 } 337 338 /** 339 * Finds the class with the specified binary name in a given module. 340 * This method returns {@code null} if the class cannot be found. 341 */ 342 @Override 343 protected Class<?> findClass(String mn, String cn) { 344 Class<?> c = null; 345 LoadedModule loadedModule = findLoadedModule(cn); 346 if (loadedModule != null && loadedModule.name().equals(mn)) 347 c = findClassInModuleOrNull(loadedModule, cn); 348 return c; 349 } 350 351 /** 352 * Loads the class with the specified binary name. 353 */ 354 @Override 355 protected Class<?> loadClass(String cn, boolean resolve) 356 throws ClassNotFoundException 357 { 358 SecurityManager sm = System.getSecurityManager(); 359 if (sm != null) { 360 String pn = packageName(cn); 361 if (!pn.isEmpty()) { 362 sm.checkPackageAccess(pn); 363 } 364 } 365 366 synchronized (getClassLoadingLock(cn)) { 367 // check if already loaded 368 Class<?> c = findLoadedClass(cn); 369 370 if (c == null) { 371 372 LoadedModule loadedModule = findLoadedModule(cn); 373 374 if (loadedModule != null) { 375 376 // class is in module defined to this class loader 377 c = findClassInModuleOrNull(loadedModule, cn); 378 379 } else { 380 381 // type in another module or visible via the parent loader 382 String pn = packageName(cn); 383 ClassLoader loader = remotePackageToLoader.get(pn); 384 if (loader == null) { 385 // type not in a module read by any of the modules 386 // defined to this loader, so delegate to parent 387 // class loader 388 loader = parent; 389 } 390 if (loader == null) { 391 c = BootLoader.loadClassOrNull(cn); 392 } else { 393 c = loader.loadClass(cn); 394 } 395 396 } 397 } 398 399 if (c == null) 400 throw new ClassNotFoundException(cn); 401 402 if (resolve) 403 resolveClass(c); 404 405 return c; 406 } 407 } 408 409 410 /** 411 * Finds the class with the specified binary name if in a module 412 * defined to this ClassLoader. 413 * 414 * @return the resulting Class or {@code null} if not found 415 */ 416 private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) { 417 PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule); 418 return AccessController.doPrivileged(pa, acc); 419 } 420 421 /** 422 * Defines the given binary class name to the VM, loading the class 423 * bytes from the given module. 424 * 425 * @return the resulting Class or {@code null} if an I/O error occurs 426 */ 427 private Class<?> defineClass(String cn, LoadedModule loadedModule) { 428 ModuleReader reader = moduleReaderFor(loadedModule.mref()); 429 430 try { 431 // read class file 432 String rn = cn.replace('.', '/').concat(".class"); 433 ByteBuffer bb = reader.read(rn).orElse(null); 434 if (bb == null) { 435 // class not found 436 return null; 437 } 438 439 try { 440 return defineClass(cn, bb, loadedModule.codeSource()); 441 } finally { 442 reader.release(bb); 443 } 444 445 } catch (IOException ioe) { 446 // TBD on how I/O errors should be propagated 447 return null; 448 } 449 } 450 451 452 // -- permissions 453 454 /** 455 * Returns the permissions for the given CodeSource. 456 */ 457 @Override 458 protected PermissionCollection getPermissions(CodeSource cs) { 459 PermissionCollection perms = super.getPermissions(cs); 460 461 URL url = cs.getLocation(); 462 if (url == null) 463 return perms; 464 465 // add the permission to access the resource 466 try { 467 Permission p = url.openConnection().getPermission(); 468 if (p != null) { 469 // for directories then need recursive access 470 if (p instanceof FilePermission) { 471 String path = p.getName(); 472 if (path.endsWith(File.separator)) { 473 path += "-"; 474 p = new FilePermission(path, "read"); 475 } 476 } 477 perms.add(p); 478 } 479 } catch (IOException ioe) { } 480 481 return perms; 482 } 483 484 485 // -- miscellaneous supporting methods 486 487 /** 488 * Find the candidate module for the given class name. 489 * Returns {@code null} if none of the modules defined to this 490 * class loader contain the API package for the class. 491 */ 492 private LoadedModule findLoadedModule(String cn) { 493 String pn = packageName(cn); 494 return pn.isEmpty() ? null : localPackageToModule.get(pn); 495 } 496 497 /** 498 * Returns the package name for the given class name 499 */ 500 private String packageName(String cn) { 501 int pos = cn.lastIndexOf('.'); 502 return (pos < 0) ? "" : cn.substring(0, pos); 503 } 504 505 506 /** 507 * Returns the ModuleReader for the given module. 508 */ 509 private ModuleReader moduleReaderFor(ModuleReference mref) { 510 return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref)); 511 } 512 513 /** 514 * Creates a ModuleReader for the given module. 515 */ 516 private ModuleReader createModuleReader(ModuleReference mref) { 517 try { 518 return mref.open(); 519 } catch (IOException e) { 520 // Return a null module reader to avoid a future class load 521 // attempting to open the module again. 522 return new NullModuleReader(); 523 } 524 } 525 526 /** 527 * A ModuleReader that doesn't read any resources. 528 */ 529 private static class NullModuleReader implements ModuleReader { 530 @Override 531 public Optional<URI> find(String name) { 532 return Optional.empty(); 533 } 534 @Override 535 public void close() { 536 throw new InternalError("Should not get here"); 537 } 538 } 539 540 }