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