1 /*
2 * Copyright (c) 2015, 2024, 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.util.ArrayDeque;
42 import java.util.Collections;
43 import java.util.Deque;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.Map;
48 import java.util.Objects;
49 import java.util.Optional;
50 import java.util.Set;
51 import java.util.Spliterator;
52 import java.util.function.Consumer;
53 import java.util.function.Supplier;
54 import java.util.stream.Stream;
55 import java.util.stream.StreamSupport;
56
57 import jdk.internal.jimage.ImageLocation;
58 import jdk.internal.jimage.ImageReader;
59 import jdk.internal.jimage.ImageReaderFactory;
60 import jdk.internal.access.JavaNetUriAccess;
61 import jdk.internal.access.SharedSecrets;
62 import jdk.internal.util.StaticProperty;
63 import jdk.internal.module.ModuleHashes.HashSupplier;
64
65 /**
66 * The factory for SystemModules objects and for creating ModuleFinder objects
67 * that find modules in the runtime image.
68 *
69 * This class supports initializing the module system when the runtime is an
70 * images build, an exploded build, or an images build with java.base patched
71 * by an exploded java.base. It also supports a testing mode that re-parses
72 * the module-info.class resources in the run-time image.
73 */
74
75 public final class SystemModuleFinders {
76 private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
77
193 }
194
195 // fall back to parsing the module-info.class files in image
196 if (finder == null) {
197 finder = ofModuleInfos();
198 }
199
200 cachedSystemModuleFinder = finder;
201 return finder;
202
203 }
204
205 // exploded build (do not cache module finder)
206 Path dir = Path.of(home, "modules");
207 if (!Files.isDirectory(dir))
208 throw new InternalError("Unable to detect the run-time image");
209 return ModulePath.of(ModuleBootstrap.patcher(), dir);
210 }
211
212 /**
213 * Parses the module-info.class of all module in the runtime image and
214 * returns a ModuleFinder to find the modules.
215 *
216 * @apiNote The returned ModuleFinder is thread safe.
217 */
218 private static ModuleFinder ofModuleInfos() {
219 // parse the module-info.class in every module
220 Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>();
221 Map<String, byte[]> nameToHash = new HashMap<>();
222 ImageReader reader = SystemImage.reader();
223 for (String mn : reader.getModuleNames()) {
224 ImageLocation loc = reader.findLocation(mn, "module-info.class");
225 ModuleInfo.Attributes attrs
226 = ModuleInfo.read(reader.getResourceBuffer(loc), null);
227
228 nameToAttributes.put(mn, attrs);
229 ModuleHashes hashes = attrs.recordedHashes();
230 if (hashes != null) {
231 for (String name : hashes.names()) {
232 nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name));
233 }
234 }
235 }
236
237 // create a ModuleReference for each module
238 Set<ModuleReference> mrefs = new HashSet<>();
239 Map<String, ModuleReference> nameToModule = new HashMap<>();
240 for (Map.Entry<String, ModuleInfo.Attributes> e : nameToAttributes.entrySet()) {
241 String mn = e.getKey();
242 ModuleInfo.Attributes attrs = e.getValue();
243 HashSupplier hashSupplier = hashSupplier(nameToHash, mn);
244 ModuleReference mref = toModuleReference(attrs.descriptor(),
245 attrs.target(),
246 attrs.recordedHashes(),
247 hashSupplier,
248 attrs.moduleResolution());
249 mrefs.add(mref);
250 nameToModule.put(mn, mref);
251 }
252
253 return new SystemModuleFinder(mrefs, nameToModule);
254 }
255
256 /**
257 * A ModuleFinder that finds module in an array or set of modules.
258 */
259 private static class SystemModuleFinder implements ModuleFinder {
260 final Set<ModuleReference> mrefs;
261 final Map<String, ModuleReference> nameToModule;
262
263 SystemModuleFinder(ModuleReference[] array,
264 Map.Entry<String, ModuleReference>[] map) {
265 this.mrefs = Set.of(array);
266 this.nameToModule = Map.ofEntries(map);
267 }
268
269 SystemModuleFinder(Set<ModuleReference> mrefs,
270 Map<String, ModuleReference> nameToModule) {
271 this.mrefs = Set.copyOf(mrefs);
272 this.nameToModule = Map.copyOf(nameToModule);
273 }
274
275 @Override
365 */
366 private static class SystemImage {
367 static final ImageReader READER = ImageReaderFactory.getImageReader();
368 static ImageReader reader() {
369 return READER;
370 }
371 }
372
373 /**
374 * A ModuleReader for reading resources from a module linked into the
375 * run-time image.
376 */
377 private static class SystemModuleReader implements ModuleReader {
378 private final String module;
379 private volatile boolean closed;
380
381 SystemModuleReader(String module) {
382 this.module = module;
383 }
384
385 /**
386 * Returns the ImageLocation for the given resource, {@code null}
387 * if not found.
388 */
389 private ImageLocation findImageLocation(String name) throws IOException {
390 Objects.requireNonNull(name);
391 if (closed)
392 throw new IOException("ModuleReader is closed");
393 ImageReader imageReader = SystemImage.reader();
394 if (imageReader != null) {
395 return imageReader.findLocation(module, name);
396 } else {
397 // not an images build
398 return null;
399 }
400 }
401
402 /**
403 * Returns {@code true} if the given resource exists, {@code false}
404 * if not found.
405 */
406 private boolean containsImageLocation(String name) throws IOException {
407 Objects.requireNonNull(name);
408 if (closed)
409 throw new IOException("ModuleReader is closed");
410 ImageReader imageReader = SystemImage.reader();
411 if (imageReader != null) {
412 return imageReader.verifyLocation(module, name);
413 } else {
414 // not an images build
415 return false;
416 }
417 }
418
419 @Override
420 public Optional<URI> find(String name) throws IOException {
421 if (containsImageLocation(name)) {
422 URI u = JNUA.create("jrt", "/" + module + "/" + name);
423 return Optional.of(u);
424 } else {
425 return Optional.empty();
426 }
427 }
428
429 @Override
430 public Optional<InputStream> open(String name) throws IOException {
431 return read(name).map(this::toInputStream);
432 }
433
434 private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
435 try {
436 int rem = bb.remaining();
437 byte[] bytes = new byte[rem];
438 bb.get(bytes);
439 return new ByteArrayInputStream(bytes);
440 } finally {
441 release(bb);
442 }
443 }
444
445 @Override
446 public Optional<ByteBuffer> read(String name) throws IOException {
447 ImageLocation location = findImageLocation(name);
448 if (location != null) {
449 return Optional.of(SystemImage.reader().getResourceBuffer(location));
450 } else {
451 return Optional.empty();
452 }
453 }
454
455 @Override
456 public void release(ByteBuffer bb) {
457 Objects.requireNonNull(bb);
458 ImageReader.releaseByteBuffer(bb);
459 }
460
461 @Override
462 public Stream<String> list() throws IOException {
463 if (closed)
464 throw new IOException("ModuleReader is closed");
465
466 Spliterator<String> s = new ModuleContentSpliterator(module);
467 return StreamSupport.stream(s, false);
468 }
469
470 @Override
471 public void close() {
472 // nothing else to do
473 closed = true;
474 }
475 }
476
477 /**
478 * A Spliterator for traversing the resources of a module linked into the
479 * run-time image.
480 */
481 private static class ModuleContentSpliterator implements Spliterator<String> {
482 final String moduleRoot;
483 final Deque<ImageReader.Node> stack;
484 Iterator<ImageReader.Node> iterator;
485
486 ModuleContentSpliterator(String module) throws IOException {
487 moduleRoot = "/modules/" + module;
488 stack = new ArrayDeque<>();
489
490 // push the root node to the stack to get started
491 ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot);
492 if (dir == null || !dir.isDirectory())
493 throw new IOException(moduleRoot + " not a directory");
494 stack.push(dir);
495 iterator = Collections.emptyIterator();
496 }
497
498 /**
499 * Returns the name of the next non-directory node or {@code null} if
500 * there are no remaining nodes to visit.
501 */
502 private String next() throws IOException {
503 for (;;) {
504 while (iterator.hasNext()) {
505 ImageReader.Node node = iterator.next();
506 String name = node.getName();
507 if (node.isDirectory()) {
508 // build node
509 ImageReader.Node dir = SystemImage.reader().findNode(name);
510 assert dir.isDirectory();
511 stack.push(dir);
512 } else {
513 // strip /modules/$MODULE/ prefix
514 return name.substring(moduleRoot.length() + 1);
515 }
516 }
517
518 if (stack.isEmpty()) {
519 return null;
520 } else {
521 ImageReader.Node dir = stack.poll();
522 assert dir.isDirectory();
523 iterator = dir.getChildren().iterator();
524 }
525 }
526 }
527
528 @Override
529 public boolean tryAdvance(Consumer<? super String> action) {
530 String next;
531 try {
532 next = next();
533 } catch (IOException ioe) {
534 throw new UncheckedIOException(ioe);
535 }
536 if (next != null) {
537 action.accept(next);
538 return true;
539 } else {
540 return false;
541 }
542 }
543
|
1 /*
2 * Copyright (c) 2015, 2025, 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.nio.ByteBuffer;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.util.ArrayDeque;
41 import java.util.Collections;
42 import java.util.Deque;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.Iterator;
46 import java.util.Map;
47 import java.util.Objects;
48 import java.util.Optional;
49 import java.util.Set;
50 import java.util.Spliterator;
51 import java.util.function.Consumer;
52 import java.util.function.Supplier;
53 import java.util.stream.Stream;
54 import java.util.stream.StreamSupport;
55
56 import jdk.internal.jimage.ImageReader;
57 import jdk.internal.jimage.ImageReaderFactory;
58 import jdk.internal.access.JavaNetUriAccess;
59 import jdk.internal.access.SharedSecrets;
60 import jdk.internal.util.StaticProperty;
61 import jdk.internal.module.ModuleHashes.HashSupplier;
62
63 /**
64 * The factory for SystemModules objects and for creating ModuleFinder objects
65 * that find modules in the runtime image.
66 *
67 * This class supports initializing the module system when the runtime is an
68 * images build, an exploded build, or an images build with java.base patched
69 * by an exploded java.base. It also supports a testing mode that re-parses
70 * the module-info.class resources in the run-time image.
71 */
72
73 public final class SystemModuleFinders {
74 private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
75
191 }
192
193 // fall back to parsing the module-info.class files in image
194 if (finder == null) {
195 finder = ofModuleInfos();
196 }
197
198 cachedSystemModuleFinder = finder;
199 return finder;
200
201 }
202
203 // exploded build (do not cache module finder)
204 Path dir = Path.of(home, "modules");
205 if (!Files.isDirectory(dir))
206 throw new InternalError("Unable to detect the run-time image");
207 return ModulePath.of(ModuleBootstrap.patcher(), dir);
208 }
209
210 /**
211 * Parses the {@code module-info.class} of all modules in the runtime image and
212 * returns a ModuleFinder to find the modules.
213 *
214 * @apiNote The returned ModuleFinder is thread safe.
215 */
216 private static ModuleFinder ofModuleInfos() {
217 // parse the module-info.class in every module
218 Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>();
219 Map<String, byte[]> nameToHash = new HashMap<>();
220
221 allModuleAttributes().forEach(attrs -> {
222 nameToAttributes.put(attrs.descriptor().name(), attrs);
223 ModuleHashes hashes = attrs.recordedHashes();
224 if (hashes != null) {
225 for (String name : hashes.names()) {
226 nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name));
227 }
228 }
229 });
230
231 // create a ModuleReference for each module
232 Set<ModuleReference> mrefs = new HashSet<>();
233 Map<String, ModuleReference> nameToModule = new HashMap<>();
234 for (Map.Entry<String, ModuleInfo.Attributes> e : nameToAttributes.entrySet()) {
235 String mn = e.getKey();
236 ModuleInfo.Attributes attrs = e.getValue();
237 HashSupplier hashSupplier = hashSupplier(nameToHash, mn);
238 ModuleReference mref = toModuleReference(attrs.descriptor(),
239 attrs.target(),
240 attrs.recordedHashes(),
241 hashSupplier,
242 attrs.moduleResolution());
243 mrefs.add(mref);
244 nameToModule.put(mn, mref);
245 }
246
247 return new SystemModuleFinder(mrefs, nameToModule);
248 }
249
250 /**
251 * Parses the {@code module-info.class} of all modules in the runtime image and
252 * returns a stream of {@link ModuleInfo.Attributes Attributes} for them. The
253 * returned attributes are in no specific order.
254 */
255 private static Stream<ModuleInfo.Attributes> allModuleAttributes() {
256 // System-wide image reader.
257 ImageReader reader = SystemImage.reader();
258 try {
259 return reader.findNode("/modules")
260 .getChildNames()
261 .map(mn -> readModuleAttributes(reader, mn));
262 } catch (IOException e) {
263 throw new Error("Error reading root /modules entry", e);
264 }
265 }
266
267 /**
268 * Returns the module's "module-info", returning a holder for its class file
269 * attributes. Every module is required to have a valid {@code module-info.class}.
270 */
271 private static ModuleInfo.Attributes readModuleAttributes(ImageReader reader, String moduleName) {
272 Exception err = null;
273 try {
274 ImageReader.Node node = reader.findNode(moduleName + "/module-info.class");
275 if (node != null && node.isResource()) {
276 return ModuleInfo.read(reader.getResourceBuffer(node), null);
277 }
278 } catch (IOException | UncheckedIOException e) {
279 err = e;
280 }
281 throw new Error("Missing or invalid module-info.class for module: " + moduleName, err);
282 }
283
284 /**
285 * A ModuleFinder that finds module in an array or set of modules.
286 */
287 private static class SystemModuleFinder implements ModuleFinder {
288 final Set<ModuleReference> mrefs;
289 final Map<String, ModuleReference> nameToModule;
290
291 SystemModuleFinder(ModuleReference[] array,
292 Map.Entry<String, ModuleReference>[] map) {
293 this.mrefs = Set.of(array);
294 this.nameToModule = Map.ofEntries(map);
295 }
296
297 SystemModuleFinder(Set<ModuleReference> mrefs,
298 Map<String, ModuleReference> nameToModule) {
299 this.mrefs = Set.copyOf(mrefs);
300 this.nameToModule = Map.copyOf(nameToModule);
301 }
302
303 @Override
393 */
394 private static class SystemImage {
395 static final ImageReader READER = ImageReaderFactory.getImageReader();
396 static ImageReader reader() {
397 return READER;
398 }
399 }
400
401 /**
402 * A ModuleReader for reading resources from a module linked into the
403 * run-time image.
404 */
405 private static class SystemModuleReader implements ModuleReader {
406 private final String module;
407 private volatile boolean closed;
408
409 SystemModuleReader(String module) {
410 this.module = module;
411 }
412
413 /**
414 * Returns {@code true} if the given resource exists, {@code false}
415 * if not found.
416 */
417 private boolean containsResource(String resourcePath) throws IOException {
418 Objects.requireNonNull(resourcePath);
419 if (closed)
420 throw new IOException("ModuleReader is closed");
421 ImageReader imageReader = SystemImage.reader();
422 if (imageReader != null) {
423 ImageReader.Node node = imageReader.findNode("/modules" + resourcePath);
424 return node != null && node.isResource();
425 } else {
426 // not an images build
427 return false;
428 }
429 }
430
431 @Override
432 public Optional<URI> find(String name) throws IOException {
433 String resourcePath = "/" + module + "/" + name;
434 if (containsResource(resourcePath)) {
435 URI u = JNUA.create("jrt", resourcePath);
436 return Optional.of(u);
437 } else {
438 return Optional.empty();
439 }
440 }
441
442 @Override
443 public Optional<InputStream> open(String name) throws IOException {
444 return read(name).map(this::toInputStream);
445 }
446
447 private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
448 try {
449 int rem = bb.remaining();
450 byte[] bytes = new byte[rem];
451 bb.get(bytes);
452 return new ByteArrayInputStream(bytes);
453 } finally {
454 release(bb);
455 }
456 }
457
458 /**
459 * Returns the node for the given resource if found. If the name references
460 * a non-resource node, then {@code null} is returned.
461 */
462 private ImageReader.Node findResource(ImageReader reader, String name) throws IOException {
463 Objects.requireNonNull(name);
464 if (closed) {
465 throw new IOException("ModuleReader is closed");
466 }
467 String nodeName = "/modules/" + module + "/" + name;
468 ImageReader.Node node = reader.findNode(nodeName);
469 return (node != null && node.isResource()) ? node : null;
470 }
471
472 @Override
473 public Optional<ByteBuffer> read(String name) throws IOException {
474 ImageReader reader = SystemImage.reader();
475 return Optional.ofNullable(findResource(reader, name))
476 .map(reader::getResourceBuffer);
477 }
478
479 @Override
480 public void release(ByteBuffer bb) {
481 Objects.requireNonNull(bb);
482 ImageReader.releaseByteBuffer(bb);
483 }
484
485 @Override
486 public Stream<String> list() throws IOException {
487 if (closed)
488 throw new IOException("ModuleReader is closed");
489
490 Spliterator<String> s = new ModuleContentSpliterator(module);
491 return StreamSupport.stream(s, false);
492 }
493
494 @Override
495 public void close() {
496 // nothing else to do
497 closed = true;
498 }
499 }
500
501 /**
502 * A Spliterator for traversing the resources of a module linked into the
503 * run-time image.
504 */
505 private static class ModuleContentSpliterator implements Spliterator<String> {
506 final String moduleRoot;
507 final Deque<ImageReader.Node> stack;
508 Iterator<String> iterator;
509
510 ModuleContentSpliterator(String module) throws IOException {
511 moduleRoot = "/modules/" + module;
512 stack = new ArrayDeque<>();
513
514 // push the root node to the stack to get started
515 ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot);
516 if (dir == null || !dir.isDirectory())
517 throw new IOException(moduleRoot + " not a directory");
518 stack.push(dir);
519 iterator = Collections.emptyIterator();
520 }
521
522 /**
523 * Returns the name of the next non-directory node or {@code null} if
524 * there are no remaining nodes to visit.
525 */
526 private String next() throws IOException {
527 for (;;) {
528 while (iterator.hasNext()) {
529 String name = iterator.next();
530 ImageReader.Node node = SystemImage.reader().findNode(name);
531 if (node.isDirectory()) {
532 stack.push(node);
533 } else {
534 // strip /modules/$MODULE/ prefix
535 return name.substring(moduleRoot.length() + 1);
536 }
537 }
538
539 if (stack.isEmpty()) {
540 return null;
541 } else {
542 ImageReader.Node dir = stack.poll();
543 assert dir.isDirectory();
544 iterator = dir.getChildNames().iterator();
545 }
546 }
547 }
548
549 @Override
550 public boolean tryAdvance(Consumer<? super String> action) {
551 String next;
552 try {
553 next = next();
554 } catch (IOException ioe) {
555 throw new UncheckedIOException(ioe);
556 }
557 if (next != null) {
558 action.accept(next);
559 return true;
560 } else {
561 return false;
562 }
563 }
564
|