< prev index next > src/java.base/share/classes/jdk/internal/module/ModuleReferences.java
Print this page
/*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
import java.lang.module.ModuleReference;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
+ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+ import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+ import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
import jdk.internal.module.ModuleHashes.HashSupplier;
import sun.net.www.ParseUtil;
-
/**
* A factory for creating ModuleReference implementations where the modules are
* packaged as modular JAR file, JMOD files or where the modules are exploded
* on the file system.
*/
/**
* Creates a ModuleReference to a possibly-patched exploded module.
*/
static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs,
ModulePatcher patcher,
+ boolean previewMode,
Path dir) {
- Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);
+ Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir, previewMode);
return newModule(attrs, dir.toUri(), supplier, patcher, null);
}
/**
/**
* A ModuleReader for an exploded module.
*/
static class ExplodedModuleReader implements ModuleReader {
+ private static final String PREVIEW_PREFIX = "META-INF/preview";
+
private final Path dir;
+ private final Path previewDir;
private volatile boolean closed;
- ExplodedModuleReader(Path dir) {
+ ExplodedModuleReader(Path dir, boolean previewMode) {
this.dir = dir;
+ Path path = dir.resolve("META-INF", "preview");
+ this.previewDir = (previewMode && Files.isDirectory(path)) ? path : null;
}
/**
- * Throws IOException if the module reader is closed;
+ * Throws IOException if the module reader is closed.
*/
private void ensureOpen() throws IOException {
if (closed) throw new IOException("ModuleReader is closed");
}
+ /**
+ * Returns a file path to a resource in the module or null if not found.
+ */
+ private Path toFilePath(String name) throws IOException {
+ if (previewDir != null) {
+ if (isPreviewEntry(name)) {
+ return null;
+ }
+ Path previewPath = Resources.toFilePath(previewDir, name);
+ if (previewPath != null) {
+ return previewPath;
+ }
+ }
+ return Resources.toFilePath(dir, name);
+ }
+
@Override
public Optional<URI> find(String name) throws IOException {
ensureOpen();
- Path path = Resources.toFilePath(dir, name);
+ Path path = toFilePath(name);
if (path != null) {
try {
return Optional.of(path.toUri());
} catch (IOError e) {
throw (IOException) e.getCause();
}
@Override
public Optional<InputStream> open(String name) throws IOException {
ensureOpen();
- Path path = Resources.toFilePath(dir, name);
+ Path path = toFilePath(name);
if (path != null) {
return Optional.of(Files.newInputStream(path));
} else {
return Optional.empty();
}
}
@Override
public Optional<ByteBuffer> read(String name) throws IOException {
ensureOpen();
- Path path = Resources.toFilePath(dir, name);
+ Path path = toFilePath(name);
if (path != null) {
return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));
} else {
return Optional.empty();
}
}
@Override
public Stream<String> list() throws IOException {
ensureOpen();
- return Files.walk(dir, Integer.MAX_VALUE)
- .map(f -> Resources.toResourceName(dir, f))
- .filter(s -> s.length() > 0);
+
+ // not an exploded image, preview features not enabled, or no META-INF/preview
+ if (previewDir == null) {
+ return Files.walk(dir, Integer.MAX_VALUE)
+ .skip(1) // skip root
+ .map(f -> Resources.toResourceName(dir, f));
+ }
+
+ // combine resources from file tree with resources from META-INF/preview
+ var names = new LinkedHashSet<String>();
+ walkAndCollect(dir, rn -> !isPreviewEntry(rn), names);
+ walkAndCollect(previewDir, _ -> true, names);
+ return names.stream();
}
@Override
public void close() {
closed = true;
}
- }
+ /**
+ * Adds the names of resources in the given tree to a collection if the names
+ * are matched by a given predicate.
+ */
+ private static void walkAndCollect(Path root,
+ Predicate<String> matcher,
+ Set<String> names) throws IOException {
+ try (Stream<Path> files = Files.walk(root, Integer.MAX_VALUE)) {
+ files.skip(1) // skip root
+ .map(f -> Resources.toResourceName(root, f))
+ .filter(matcher)
+ .forEach(names::add);
+ }
+ }
+
+ /**
+ * Returns true if a resource is in the META-INF/preview tree.
+ */
+ private static boolean isPreviewEntry(String name) {
+ return name.startsWith(PREVIEW_PREFIX) &&
+ (name.length() == PREVIEW_PREFIX.length()
+ || name.charAt(PREVIEW_PREFIX.length()) == '/');
+ }
+ }
}
< prev index next >