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
26 package jdk.internal.module;
27
28 import java.io.File;
29 import java.io.IOError;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.UncheckedIOException;
33 import java.lang.module.ModuleReader;
34 import java.lang.module.ModuleReference;
35 import java.net.URI;
36 import java.nio.ByteBuffer;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.Optional;
42 import java.util.concurrent.locks.Lock;
43 import java.util.concurrent.locks.ReadWriteLock;
44 import java.util.concurrent.locks.ReentrantReadWriteLock;
45 import java.util.function.Supplier;
46 import java.util.jar.JarEntry;
47 import java.util.jar.JarFile;
48 import java.util.stream.Stream;
49 import java.util.zip.ZipFile;
50
51 import jdk.internal.jmod.JmodFile;
52 import jdk.internal.module.ModuleHashes.HashSupplier;
53 import sun.net.www.ParseUtil;
54
55
56 /**
57 * A factory for creating ModuleReference implementations where the modules are
58 * packaged as modular JAR file, JMOD files or where the modules are exploded
59 * on the file system.
60 */
61
62 class ModuleReferences {
63 private ModuleReferences() { }
64
65 /**
66 * Creates a ModuleReference to a possibly-patched module
67 */
68 private static ModuleReference newModule(ModuleInfo.Attributes attrs,
69 URI uri,
70 Supplier<ModuleReader> supplier,
71 ModulePatcher patcher,
72 HashSupplier hasher) {
73 ModuleReference mref = new ModuleReferenceImpl(attrs.descriptor(),
74 uri,
75 supplier,
105 }
106 };
107 return newModule(attrs, uri, supplier, patcher, hasher);
108 }
109
110 /**
111 * Creates a ModuleReference to a module in a JMOD file.
112 */
113 static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) {
114 URI uri = file.toUri();
115 Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
116 HashSupplier hasher = (a) -> ModuleHashes.computeHash(supplier, a);
117 return newModule(attrs, uri, supplier, null, hasher);
118 }
119
120 /**
121 * Creates a ModuleReference to a possibly-patched exploded module.
122 */
123 static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs,
124 ModulePatcher patcher,
125 Path dir) {
126 Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);
127 return newModule(attrs, dir.toUri(), supplier, patcher, null);
128 }
129
130
131 /**
132 * A base module reader that encapsulates machinery required to close the
133 * module reader safely.
134 */
135 abstract static class SafeCloseModuleReader implements ModuleReader {
136
137 // RW lock to support safe close
138 private final ReadWriteLock lock = new ReentrantReadWriteLock();
139 private final Lock readLock = lock.readLock();
140 private final Lock writeLock = lock.writeLock();
141 private boolean closed;
142
143 SafeCloseModuleReader() { }
144
145 /**
146 * Returns a URL to resource. This method is invoked by the find
349 // take snapshot to avoid async close
350 List<String> names = jf.stream()
351 .filter(e -> e.section() == JmodFile.Section.CLASSES)
352 .map(JmodFile.Entry::name)
353 .toList();
354 return names.stream();
355 }
356
357 @Override
358 void implClose() throws IOException {
359 jf.close();
360 }
361 }
362
363
364 /**
365 * A ModuleReader for an exploded module.
366 */
367 static class ExplodedModuleReader implements ModuleReader {
368 private final Path dir;
369 private volatile boolean closed;
370
371 ExplodedModuleReader(Path dir) {
372 this.dir = dir;
373 }
374
375 /**
376 * Throws IOException if the module reader is closed;
377 */
378 private void ensureOpen() throws IOException {
379 if (closed) throw new IOException("ModuleReader is closed");
380 }
381
382 @Override
383 public Optional<URI> find(String name) throws IOException {
384 ensureOpen();
385 Path path = Resources.toFilePath(dir, name);
386 if (path != null) {
387 try {
388 return Optional.of(path.toUri());
389 } catch (IOError e) {
390 throw (IOException) e.getCause();
391 }
392 } else {
393 return Optional.empty();
394 }
395 }
396
397 @Override
398 public Optional<InputStream> open(String name) throws IOException {
399 ensureOpen();
400 Path path = Resources.toFilePath(dir, name);
401 if (path != null) {
402 return Optional.of(Files.newInputStream(path));
403 } else {
404 return Optional.empty();
405 }
406 }
407
408 @Override
409 public Optional<ByteBuffer> read(String name) throws IOException {
410 ensureOpen();
411 Path path = Resources.toFilePath(dir, name);
412 if (path != null) {
413 return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));
414 } else {
415 return Optional.empty();
416 }
417 }
418
419 @Override
420 public Stream<String> list() throws IOException {
421 ensureOpen();
422 return Files.walk(dir, Integer.MAX_VALUE)
423 .map(f -> Resources.toResourceName(dir, f))
424 .filter(s -> s.length() > 0);
425 }
426
427 @Override
428 public void close() {
429 closed = true;
430 }
431 }
432
433 }
|
1 /*
2 * Copyright (c) 2015, 2026, 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.module;
27
28 import java.io.File;
29 import java.io.IOError;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.UncheckedIOException;
33 import java.lang.module.ModuleReader;
34 import java.lang.module.ModuleReference;
35 import java.net.URI;
36 import java.nio.ByteBuffer;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.util.LinkedHashSet;
40 import java.util.List;
41 import java.util.Objects;
42 import java.util.Optional;
43 import java.util.Set;
44 import java.util.concurrent.locks.Lock;
45 import java.util.concurrent.locks.ReadWriteLock;
46 import java.util.concurrent.locks.ReentrantReadWriteLock;
47 import java.util.function.Supplier;
48 import java.util.jar.JarEntry;
49 import java.util.jar.JarFile;
50 import java.util.stream.Stream;
51 import java.util.zip.ZipFile;
52
53 import jdk.internal.jmod.JmodFile;
54 import jdk.internal.module.ModuleHashes.HashSupplier;
55 import sun.net.www.ParseUtil;
56
57 /**
58 * A factory for creating ModuleReference implementations where the modules are
59 * packaged as modular JAR file, JMOD files or where the modules are exploded
60 * on the file system.
61 */
62
63 class ModuleReferences {
64 private ModuleReferences() { }
65
66 /**
67 * Creates a ModuleReference to a possibly-patched module
68 */
69 private static ModuleReference newModule(ModuleInfo.Attributes attrs,
70 URI uri,
71 Supplier<ModuleReader> supplier,
72 ModulePatcher patcher,
73 HashSupplier hasher) {
74 ModuleReference mref = new ModuleReferenceImpl(attrs.descriptor(),
75 uri,
76 supplier,
106 }
107 };
108 return newModule(attrs, uri, supplier, patcher, hasher);
109 }
110
111 /**
112 * Creates a ModuleReference to a module in a JMOD file.
113 */
114 static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) {
115 URI uri = file.toUri();
116 Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
117 HashSupplier hasher = (a) -> ModuleHashes.computeHash(supplier, a);
118 return newModule(attrs, uri, supplier, null, hasher);
119 }
120
121 /**
122 * Creates a ModuleReference to a possibly-patched exploded module.
123 */
124 static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs,
125 ModulePatcher patcher,
126 boolean previewMode,
127 Path dir) {
128 Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir, previewMode);
129 return newModule(attrs, dir.toUri(), supplier, patcher, null);
130 }
131
132
133 /**
134 * A base module reader that encapsulates machinery required to close the
135 * module reader safely.
136 */
137 abstract static class SafeCloseModuleReader implements ModuleReader {
138
139 // RW lock to support safe close
140 private final ReadWriteLock lock = new ReentrantReadWriteLock();
141 private final Lock readLock = lock.readLock();
142 private final Lock writeLock = lock.writeLock();
143 private boolean closed;
144
145 SafeCloseModuleReader() { }
146
147 /**
148 * Returns a URL to resource. This method is invoked by the find
351 // take snapshot to avoid async close
352 List<String> names = jf.stream()
353 .filter(e -> e.section() == JmodFile.Section.CLASSES)
354 .map(JmodFile.Entry::name)
355 .toList();
356 return names.stream();
357 }
358
359 @Override
360 void implClose() throws IOException {
361 jf.close();
362 }
363 }
364
365
366 /**
367 * A ModuleReader for an exploded module.
368 */
369 static class ExplodedModuleReader implements ModuleReader {
370 private final Path dir;
371 private final Path previewDir;
372 private volatile boolean closed;
373
374 ExplodedModuleReader(Path dir, boolean previewMode) {
375 this.dir = dir;
376 Path path = dir.resolve("META-INF", "preview");
377 this.previewDir = (previewMode && Files.isDirectory(path)) ? path : null;
378 }
379
380 /**
381 * Throws IOException if the module reader is closed;
382 */
383 private void ensureOpen() throws IOException {
384 if (closed) throw new IOException("ModuleReader is closed");
385 }
386
387 private Path toFilePath(String name) throws IOException {
388 if (previewDir != null) {
389 if (isPreviewEntry(name)) {
390 return null;
391 }
392 Path previewPath = Resources.toFilePath(previewDir, name);
393 if (previewPath != null && Files.exists(previewPath)) {
394 return previewPath;
395 }
396 }
397 return Resources.toFilePath(dir, name);
398 }
399
400 @Override
401 public Optional<URI> find(String name) throws IOException {
402 ensureOpen();
403 Path path = toFilePath(name);
404 if (path != null) {
405 try {
406 return Optional.of(path.toUri());
407 } catch (IOError e) {
408 throw (IOException) e.getCause();
409 }
410 } else {
411 return Optional.empty();
412 }
413 }
414
415 @Override
416 public Optional<InputStream> open(String name) throws IOException {
417 ensureOpen();
418 Path path = toFilePath(name);
419 if (path != null) {
420 return Optional.of(Files.newInputStream(path));
421 } else {
422 return Optional.empty();
423 }
424 }
425
426 @Override
427 public Optional<ByteBuffer> read(String name) throws IOException {
428 ensureOpen();
429 Path path = toFilePath(name);
430 if (path != null) {
431 return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));
432 } else {
433 return Optional.empty();
434 }
435 }
436
437 @Override
438 public Stream<String> list() throws IOException {
439 ensureOpen();
440 LinkedHashSet<String> names = new LinkedHashSet<>();
441 collectNames(dir, names);
442 if (previewDir != null) {
443 collectNames(previewDir, names);
444 }
445 return names.stream();
446 }
447
448 private void collectNames(Path dir, Set<String> dest) throws IOException {
449 try (Stream<Path> files = Files.walk(dir, Integer.MAX_VALUE)) {
450 files.map(f -> Resources.toResourceName(dir, f))
451 .filter(s -> !s.isEmpty())
452 .filter(s -> previewDir == null || !isPreviewEntry(s))
453 .forEach(dest::add);
454 }
455 }
456
457 @Override
458 public void close() {
459 closed = true;
460 }
461
462 // Names do not have a leading '/'.
463 private static final String PREVIEW_PREFIX = "META-INF/preview";
464
465 private static boolean isPreviewEntry(String name) {
466 return name.startsWith(PREVIEW_PREFIX) &&
467 (name.length() == PREVIEW_PREFIX.length()
468 || name.charAt(PREVIEW_PREFIX.length()) == '/');
469 }
470 }
471 }
|