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 package jdk.internal.module; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.UncheckedIOException; 31 import java.lang.module.ModuleDescriptor; 32 import java.lang.module.ModuleFinder; 33 import java.lang.module.ModuleReader; 34 import java.lang.module.ModuleReference; 35 import java.lang.reflect.Constructor; 36 import java.net.URI; 37 import java.net.URLConnection; 38 import java.nio.ByteBuffer; 39 import java.nio.file.Files; 40 import java.nio.file.Path; 41 import java.security.AccessController; 42 import java.security.PrivilegedAction; 43 import java.util.ArrayDeque; 44 import java.util.Collections; 45 import java.util.Deque; 46 import java.util.HashMap; 47 import java.util.HashSet; 48 import java.util.Iterator; 49 import java.util.Map; 50 import java.util.Objects; 51 import java.util.Optional; 52 import java.util.Set; 53 import java.util.Spliterator; 54 import java.util.function.Consumer; 55 import java.util.function.Supplier; 56 import java.util.stream.Stream; 57 import java.util.stream.StreamSupport; 58 59 import jdk.internal.jimage.ImageLocation; 60 import jdk.internal.jimage.ImageReader; 61 import jdk.internal.jimage.ImageReaderFactory; 62 import jdk.internal.misc.JavaNetUriAccess; 63 import jdk.internal.misc.SharedSecrets; 64 import jdk.internal.util.StaticProperty; 65 import jdk.internal.module.ModuleHashes.HashSupplier; 66 67 /** 68 * The factory for SystemModules objects and for creating ModuleFinder objects 69 * that find modules in the runtime image. 70 * 71 * This class supports initializing the module system when the runtime is an 72 * images build, an exploded build, or an images build with java.base patched 73 * by an exploded java.base. It also supports a testing mode that re-parses 74 * the module-info.class resources in the run-time image. 75 */ 76 77 public final class SystemModuleFinders { 78 private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess(); 79 80 private static final boolean USE_FAST_PATH; 81 static { 82 String value = System.getProperty("jdk.system.module.finder.disableFastPath"); 83 if (value == null) { 84 USE_FAST_PATH = true; 85 } else { 86 USE_FAST_PATH = !value.isEmpty() && !Boolean.parseBoolean(value); 87 } 88 } 89 90 // cached ModuleFinder returned from ofSystem 91 private static volatile ModuleFinder cachedSystemModuleFinder; 92 93 private SystemModuleFinders() { } 94 95 /** 96 * Returns the SystemModules object to reconstitute all modules. Returns 97 * null if this is an exploded build or java.base is patched by an exploded 98 * build. 99 */ 100 static SystemModules allSystemModules() { 101 if (USE_FAST_PATH) { 102 return SystemModulesMap.allSystemModules(); 103 } else { 104 return null; 105 } 106 } 107 108 /** 109 * Returns a SystemModules object to reconstitute the modules for the 110 * given initial module. If the initial module is null then return the 111 * SystemModules object to reconstitute the default modules. 112 * 113 * Return null if there is no SystemModules class for the initial module, 114 * this is an exploded build, or java.base is patched by an exploded build. 115 */ 116 static SystemModules systemModules(String initialModule) { 117 if (USE_FAST_PATH) { 118 if (initialModule == null) { 119 return SystemModulesMap.defaultSystemModules(); 120 } 121 122 String[] initialModules = SystemModulesMap.moduleNames(); 123 for (int i = 0; i < initialModules.length; i++) { 124 String moduleName = initialModules[i]; 125 if (initialModule.equals(moduleName)) { 126 String cn = SystemModulesMap.classNames()[i]; 127 try { 128 // one-arg Class.forName as java.base may not be defined 129 Constructor<?> ctor = Class.forName(cn).getConstructor(); 130 return (SystemModules) ctor.newInstance(); 131 } catch (Exception e) { 132 throw new InternalError(e); 133 } 134 } 135 } 136 } 137 return null; 138 } 139 140 /** 141 * Returns a ModuleFinder that is backed by the given SystemModules object. 142 * 143 * @apiNote The returned ModuleFinder is thread safe. 144 */ 145 static ModuleFinder of(SystemModules systemModules) { 146 ModuleDescriptor[] descriptors = systemModules.moduleDescriptors(); 147 ModuleTarget[] targets = systemModules.moduleTargets(); 148 ModuleHashes[] recordedHashes = systemModules.moduleHashes(); 149 ModuleResolution[] moduleResolutions = systemModules.moduleResolutions(); 150 151 int moduleCount = descriptors.length; 152 ModuleReference[] mrefs = new ModuleReference[moduleCount]; 153 @SuppressWarnings(value = {"rawtypes", "unchecked"}) 154 Map.Entry<String, ModuleReference>[] map 155 = (Map.Entry<String, ModuleReference>[])new Map.Entry[moduleCount]; 156 157 Map<String, byte[]> nameToHash = generateNameToHash(recordedHashes); 158 159 for (int i = 0; i < moduleCount; i++) { 160 String name = descriptors[i].name(); 161 HashSupplier hashSupplier = hashSupplier(nameToHash, name); 162 ModuleReference mref = toModuleReference(descriptors[i], 163 targets[i], 164 recordedHashes[i], 165 hashSupplier, 166 moduleResolutions[i]); 167 mrefs[i] = mref; 168 map[i] = Map.entry(name, mref); 169 } 170 171 return new SystemModuleFinder(mrefs, map); 172 } 173 174 /** 175 * Returns the ModuleFinder to find all system modules. Supports both 176 * images and exploded builds. 177 * 178 * @apiNote Used by ModuleFinder.ofSystem() 179 */ 180 public static ModuleFinder ofSystem() { 181 ModuleFinder finder = cachedSystemModuleFinder; 182 if (finder != null) { 183 return finder; 184 } 185 186 // probe to see if this is an images build 187 String home = StaticProperty.javaHome(); 188 Path modules = Path.of(home, "lib", "modules"); 189 if (Files.isRegularFile(modules)) { 190 if (USE_FAST_PATH) { 191 SystemModules systemModules = allSystemModules(); 192 if (systemModules != null) { 193 finder = of(systemModules); 194 } 195 } 196 197 // fall back to parsing the module-info.class files in image 198 if (finder == null) { 199 finder = ofModuleInfos(); 200 } 201 202 cachedSystemModuleFinder = finder; 203 return finder; 204 205 } 206 207 // exploded build (do not cache module finder) 208 Path dir = Path.of(home, "modules"); 209 if (!Files.isDirectory(dir)) 210 throw new InternalError("Unable to detect the run-time image"); 211 ModuleFinder f = ModulePath.of(ModuleBootstrap.patcher(), dir); 212 return new ModuleFinder() { 213 @Override 214 public Optional<ModuleReference> find(String name) { 215 PrivilegedAction<Optional<ModuleReference>> pa = () -> f.find(name); 216 return AccessController.doPrivileged(pa); 217 } 218 @Override 219 public Set<ModuleReference> findAll() { 220 PrivilegedAction<Set<ModuleReference>> pa = f::findAll; 221 return AccessController.doPrivileged(pa); 222 } 223 }; 224 } 225 226 /** 227 * Parses the module-info.class of all module in the runtime image and 228 * returns a ModuleFinder to find the modules. 229 * 230 * @apiNote The returned ModuleFinder is thread safe. 231 */ 232 private static ModuleFinder ofModuleInfos() { 233 // parse the module-info.class in every module 234 Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>(); 235 Map<String, byte[]> nameToHash = new HashMap<>(); 236 ImageReader reader = SystemImage.reader(); 237 for (String mn : reader.getModuleNames()) { 238 ImageLocation loc = reader.findLocation(mn, "module-info.class"); 239 ModuleInfo.Attributes attrs 240 = ModuleInfo.read(reader.getResourceBuffer(loc), null); 241 242 nameToAttributes.put(mn, attrs); 243 ModuleHashes hashes = attrs.recordedHashes(); 244 if (hashes != null) { 245 for (String name : hashes.names()) { 246 nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name)); 247 } 248 } 249 } 250 251 // create a ModuleReference for each module 252 Set<ModuleReference> mrefs = new HashSet<>(); 253 Map<String, ModuleReference> nameToModule = new HashMap<>(); 254 for (Map.Entry<String, ModuleInfo.Attributes> e : nameToAttributes.entrySet()) { 255 String mn = e.getKey(); 256 ModuleInfo.Attributes attrs = e.getValue(); 257 HashSupplier hashSupplier = hashSupplier(nameToHash, mn); 258 ModuleReference mref = toModuleReference(attrs.descriptor(), 259 attrs.target(), 260 attrs.recordedHashes(), 261 hashSupplier, 262 attrs.moduleResolution()); 263 mrefs.add(mref); 264 nameToModule.put(mn, mref); 265 } 266 267 return new SystemModuleFinder(mrefs, nameToModule); 268 } 269 270 /** 271 * A ModuleFinder that finds module in an array or set of modules. 272 */ 273 private static class SystemModuleFinder implements ModuleFinder { 274 final Set<ModuleReference> mrefs; 275 final Map<String, ModuleReference> nameToModule; 276 277 SystemModuleFinder(ModuleReference[] array, 278 Map.Entry<String, ModuleReference>[] map) { 279 this.mrefs = Set.of(array); 280 this.nameToModule = Map.ofEntries(map); 281 } 282 283 SystemModuleFinder(Set<ModuleReference> mrefs, 284 Map<String, ModuleReference> nameToModule) { 285 this.mrefs = Collections.unmodifiableSet(mrefs); 286 this.nameToModule = Collections.unmodifiableMap(nameToModule); 287 } 288 289 @Override 290 public Optional<ModuleReference> find(String name) { 291 Objects.requireNonNull(name); 292 return Optional.ofNullable(nameToModule.get(name)); 293 } 294 295 @Override 296 public Set<ModuleReference> findAll() { 297 return mrefs; 298 } 299 } 300 301 /** 302 * Creates a ModuleReference to the system module. 303 */ 304 static ModuleReference toModuleReference(ModuleDescriptor descriptor, 305 ModuleTarget target, 306 ModuleHashes recordedHashes, 307 HashSupplier hasher, 308 ModuleResolution mres) { 309 String mn = descriptor.name(); 310 URI uri = JNUA.create("jrt", "/".concat(mn)); 311 312 Supplier<ModuleReader> readerSupplier = new Supplier<>() { 313 @Override 314 public ModuleReader get() { 315 return new SystemModuleReader(mn, uri); 316 } 317 }; 318 319 ModuleReference mref = new ModuleReferenceImpl(descriptor, 320 uri, 321 readerSupplier, 322 null, 323 target, 324 recordedHashes, 325 hasher, 326 mres); 327 328 // may need a reference to a patched module if --patch-module specified 329 mref = ModuleBootstrap.patcher().patchIfNeeded(mref); 330 331 return mref; 332 } 333 334 /** 335 * Generates a map of module name to hash value. 336 */ 337 static Map<String, byte[]> generateNameToHash(ModuleHashes[] recordedHashes) { 338 Map<String, byte[]> nameToHash = null; 339 340 boolean secondSeen = false; 341 // record the hashes to build HashSupplier 342 for (ModuleHashes mh : recordedHashes) { 343 if (mh != null) { 344 // if only one module contain ModuleHashes, use it 345 if (nameToHash == null) { 346 nameToHash = mh.hashes(); 347 } else { 348 if (!secondSeen) { 349 nameToHash = new HashMap<>(nameToHash); 350 secondSeen = true; 351 } 352 nameToHash.putAll(mh.hashes()); 353 } 354 } 355 } 356 return (nameToHash != null) ? nameToHash : Collections.emptyMap(); 357 } 358 359 /** 360 * Returns a HashSupplier that returns the hash of the given module. 361 */ 362 static HashSupplier hashSupplier(Map<String, byte[]> nameToHash, String name) { 363 byte[] hash = nameToHash.get(name); 364 if (hash != null) { 365 // avoid lambda here 366 return new HashSupplier() { 367 @Override 368 public byte[] generate(String algorithm) { 369 return hash; 370 } 371 }; 372 } else { 373 return null; 374 } 375 } 376 377 /** 378 * Holder class for the ImageReader 379 * 380 * @apiNote This class must be loaded before a security manager is set. 381 */ 382 private static class SystemImage { 383 static final ImageReader READER = ImageReaderFactory.getImageReader(); 384 static ImageReader reader() { 385 return READER; 386 } 387 } 388 389 /** 390 * A ModuleReader for reading resources from a module linked into the 391 * run-time image. 392 */ 393 private static class SystemModuleReader implements ModuleReader { 394 private final String module; 395 private volatile boolean closed; 396 397 /** 398 * If there is a security manager set then check permission to 399 * connect to the run-time image. 400 */ 401 private static void checkPermissionToConnect(URI uri) { 402 SecurityManager sm = System.getSecurityManager(); 403 if (sm != null) { 404 try { 405 URLConnection uc = uri.toURL().openConnection(); 406 sm.checkPermission(uc.getPermission()); 407 } catch (IOException ioe) { 408 throw new UncheckedIOException(ioe); 409 } 410 } 411 } 412 413 SystemModuleReader(String module, URI uri) { 414 checkPermissionToConnect(uri); 415 this.module = module; 416 } 417 418 /** 419 * Returns the ImageLocation for the given resource, {@code null} 420 * if not found. 421 */ 422 private ImageLocation findImageLocation(String name) throws IOException { 423 Objects.requireNonNull(name); 424 if (closed) 425 throw new IOException("ModuleReader is closed"); 426 ImageReader imageReader = SystemImage.reader(); 427 if (imageReader != null) { 428 return imageReader.findLocation(module, name); 429 } else { 430 // not an images build 431 return null; 432 } 433 } 434 435 @Override 436 public Optional<URI> find(String name) throws IOException { 437 ImageLocation location = findImageLocation(name); 438 if (location != null) { 439 URI u = URI.create("jrt:/" + module + "/" + name); 440 return Optional.of(u); 441 } else { 442 return Optional.empty(); 443 } 444 } 445 446 @Override 447 public Optional<InputStream> open(String name) throws IOException { 448 return read(name).map(this::toInputStream); 449 } 450 451 private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer? 452 try { 453 int rem = bb.remaining(); 454 byte[] bytes = new byte[rem]; 455 bb.get(bytes); 456 return new ByteArrayInputStream(bytes); 457 } finally { 458 release(bb); 459 } 460 } 461 462 @Override 463 public Optional<ByteBuffer> read(String name) throws IOException { 464 ImageLocation location = findImageLocation(name); 465 if (location != null) { 466 return Optional.of(SystemImage.reader().getResourceBuffer(location)); 467 } else { 468 return Optional.empty(); 469 } 470 } 471 472 @Override 473 public void release(ByteBuffer bb) { 474 Objects.requireNonNull(bb); 475 ImageReader.releaseByteBuffer(bb); 476 } 477 478 @Override 479 public Stream<String> list() throws IOException { 480 if (closed) 481 throw new IOException("ModuleReader is closed"); 482 483 Spliterator<String> s = new ModuleContentSpliterator(module); 484 return StreamSupport.stream(s, false); 485 } 486 487 @Override 488 public void close() { 489 // nothing else to do 490 closed = true; 491 } 492 } 493 494 /** 495 * A Spliterator for traversing the resources of a module linked into the 496 * run-time image. 497 */ 498 private static class ModuleContentSpliterator implements Spliterator<String> { 499 final String moduleRoot; 500 final Deque<ImageReader.Node> stack; 501 Iterator<ImageReader.Node> iterator; 502 503 ModuleContentSpliterator(String module) throws IOException { 504 moduleRoot = "/modules/" + module; 505 stack = new ArrayDeque<>(); 506 507 // push the root node to the stack to get started 508 ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot); 509 if (dir == null || !dir.isDirectory()) 510 throw new IOException(moduleRoot + " not a directory"); 511 stack.push(dir); 512 iterator = Collections.emptyIterator(); 513 } 514 515 /** 516 * Returns the name of the next non-directory node or {@code null} if 517 * there are no remaining nodes to visit. 518 */ 519 private String next() throws IOException { 520 for (;;) { 521 while (iterator.hasNext()) { 522 ImageReader.Node node = iterator.next(); 523 String name = node.getName(); 524 if (node.isDirectory()) { 525 // build node 526 ImageReader.Node dir = SystemImage.reader().findNode(name); 527 assert dir.isDirectory(); 528 stack.push(dir); 529 } else { 530 // strip /modules/$MODULE/ prefix 531 return name.substring(moduleRoot.length() + 1); 532 } 533 } 534 535 if (stack.isEmpty()) { 536 return null; 537 } else { 538 ImageReader.Node dir = stack.poll(); 539 assert dir.isDirectory(); 540 iterator = dir.getChildren().iterator(); 541 } 542 } 543 } 544 545 @Override 546 public boolean tryAdvance(Consumer<? super String> action) { 547 String next; 548 try { 549 next = next(); 550 } catch (IOException ioe) { 551 throw new UncheckedIOException(ioe); 552 } 553 if (next != null) { 554 action.accept(next); 555 return true; 556 } else { 557 return false; 558 } 559 } 560 561 @Override 562 public Spliterator<String> trySplit() { 563 return null; 564 } 565 566 @Override 567 public int characteristics() { 568 return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE; 569 } 570 571 @Override 572 public long estimateSize() { 573 return Long.MAX_VALUE; 574 } 575 } 576 }