/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/jdk/internal/module/ModuleReferences.java
490 строк
17 KB
David Simms
8389219: Implement JEP 401: Value Objects (Preview)
31 июл 2026, 03:45
31 июл 2026, 03:45
cc278db
Код
Авторство
О чём код?
/* * 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 * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.internal.module; import java.io.File; import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.lang.module.ModuleReader; 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. */ class ModuleReferences { private ModuleReferences() { } /** * Creates a ModuleReference to a possibly-patched module */ private static ModuleReference newModule(ModuleInfo.Attributes attrs, URI uri, Supplier<ModuleReader> supplier, ModulePatcher patcher, HashSupplier hasher) { ModuleReference mref = new ModuleReferenceImpl(attrs.descriptor(), uri, supplier, null, attrs.target(), attrs.recordedHashes(), hasher, attrs.moduleResolution()); if (patcher != null) mref = patcher.patchIfNeeded(mref); return mref; } /** * Creates a ModuleReference to a possibly-patched module in a modular JAR. */ static ModuleReference newJarModule(ModuleInfo.Attributes attrs, ModulePatcher patcher, Path file) { URI uri = file.toUri(); String fileString = file.toString(); Supplier<ModuleReader> supplier = new Supplier<>() { @Override public ModuleReader get() { return new JarModuleReader(fileString, uri); } }; HashSupplier hasher = new HashSupplier() { @Override public byte[] generate(String algorithm) { return ModuleHashes.computeHash(supplier, algorithm); } }; return newModule(attrs, uri, supplier, patcher, hasher); } /** * Creates a ModuleReference to a module in a JMOD file. */ static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) { URI uri = file.toUri(); Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri); HashSupplier hasher = (a) -> ModuleHashes.computeHash(supplier, a); return newModule(attrs, uri, supplier, null, hasher); } /** * 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, previewMode); return newModule(attrs, dir.toUri(), supplier, patcher, null); } /** * A base module reader that encapsulates machinery required to close the * module reader safely. */ abstract static class SafeCloseModuleReader implements ModuleReader { // RW lock to support safe close private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private boolean closed; SafeCloseModuleReader() { } /** * Returns a URL to resource. This method is invoked by the find * method to do the actual work of finding the resource. */ abstract Optional<URI> implFind(String name) throws IOException; /** * Returns an input stream for reading a resource. This method is * invoked by the open method to do the actual work of opening * an input stream to the resource. */ abstract Optional<InputStream> implOpen(String name) throws IOException; /** * Returns a stream of the names of resources in the module. This * method is invoked by the list method to do the actual work of * creating the stream. */ abstract Stream<String> implList() throws IOException; /** * Closes the module reader. This method is invoked by close to do the * actual work of closing the module reader. */ abstract void implClose() throws IOException; @Override public final Optional<URI> find(String name) throws IOException { readLock.lock(); try { if (!closed) { return implFind(name); } else { throw new IOException("ModuleReader is closed"); } } finally { readLock.unlock(); } } @Override public final Optional<InputStream> open(String name) throws IOException { readLock.lock(); try { if (!closed) { return implOpen(name); } else { throw new IOException("ModuleReader is closed"); } } finally { readLock.unlock(); } } @Override public final Stream<String> list() throws IOException { readLock.lock(); try { if (!closed) { return implList(); } else { throw new IOException("ModuleReader is closed"); } } finally { readLock.unlock(); } } @Override public final void close() throws IOException { writeLock.lock(); try { if (!closed) { closed = true; implClose(); } } finally { writeLock.unlock(); } } } /** * A ModuleReader for a modular JAR file. */ static class JarModuleReader extends SafeCloseModuleReader { private final JarFile jf; private final URI uri; static JarFile newJarFile(String path) { try { return new JarFile(new File(path), true, // verify ZipFile.OPEN_READ, JarFile.runtimeVersion()); } catch (IOException ioe) { throw new UncheckedIOException(ioe); } } JarModuleReader(String path, URI uri) { this.jf = newJarFile(path); this.uri = uri; } private JarEntry getEntry(String name) { return jf.getJarEntry(Objects.requireNonNull(name)); } @Override Optional<URI> implFind(String name) throws IOException { JarEntry je = getEntry(name); if (je != null) { if (jf.isMultiRelease()) name = je.getRealName(); if (je.isDirectory() && !name.endsWith("/")) name += "/"; String encodedPath = ParseUtil.encodePath(name, false); String uris = "jar:" + uri + "!/" + encodedPath; return Optional.of(URI.create(uris)); } else { return Optional.empty(); } } @Override Optional<InputStream> implOpen(String name) throws IOException { JarEntry je = getEntry(name); if (je != null) { return Optional.of(jf.getInputStream(je)); } else { return Optional.empty(); } } @Override Stream<String> implList() throws IOException { // take snapshot to avoid async close List<String> names = jf.versionedStream() .map(JarEntry::getName) .toList(); return names.stream(); } @Override void implClose() throws IOException { jf.close(); } } /** * A ModuleReader for a JMOD file. */ static class JModModuleReader extends SafeCloseModuleReader { private final JmodFile jf; private final URI uri; static JmodFile newJmodFile(Path path) { try { return new JmodFile(path); } catch (IOException ioe) { throw new UncheckedIOException(ioe); } } JModModuleReader(Path path, URI uri) { this.jf = newJmodFile(path); this.uri = uri; } private JmodFile.Entry getEntry(String name) { Objects.requireNonNull(name); return jf.getEntry(JmodFile.Section.CLASSES, name); } @Override Optional<URI> implFind(String name) { JmodFile.Entry je = getEntry(name); if (je != null) { if (je.isDirectory() && !name.endsWith("/")) name += "/"; String encodedPath = ParseUtil.encodePath(name, false); String uris = "jmod:" + uri + "!/" + encodedPath; return Optional.of(URI.create(uris)); } else { return Optional.empty(); } } @Override Optional<InputStream> implOpen(String name) throws IOException { JmodFile.Entry je = getEntry(name); if (je != null) { return Optional.of(jf.getInputStream(je)); } else { return Optional.empty(); } } @Override Stream<String> implList() throws IOException { // take snapshot to avoid async close List<String> names = jf.stream() .filter(e -> e.section() == JmodFile.Section.CLASSES) .map(JmodFile.Entry::name) .toList(); return names.stream(); } @Override void implClose() throws IOException { jf.close(); } } /** * 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, 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. */ 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 = toFilePath(name); if (path != null) { try { return Optional.of(path.toUri()); } catch (IOError e) { throw (IOException) e.getCause(); } } else { return Optional.empty(); } } @Override public Optional<InputStream> open(String name) throws IOException { ensureOpen(); 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 = 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(); // 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()) == '/'); } } }