Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java
41159 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.module;2627import java.io.File;28import java.io.IOError;29import java.io.IOException;30import java.io.InputStream;31import java.io.UncheckedIOException;32import java.lang.module.ModuleReader;33import java.lang.module.ModuleReference;34import java.net.URI;35import java.nio.ByteBuffer;36import java.nio.file.Files;37import java.nio.file.Path;38import java.util.List;39import java.util.Objects;40import java.util.Optional;41import java.util.concurrent.locks.Lock;42import java.util.concurrent.locks.ReadWriteLock;43import java.util.concurrent.locks.ReentrantReadWriteLock;44import java.util.function.Supplier;45import java.util.jar.JarEntry;46import java.util.jar.JarFile;47import java.util.stream.Stream;48import java.util.zip.ZipFile;4950import jdk.internal.jmod.JmodFile;51import jdk.internal.module.ModuleHashes.HashSupplier;52import sun.net.www.ParseUtil;535455/**56* A factory for creating ModuleReference implementations where the modules are57* packaged as modular JAR file, JMOD files or where the modules are exploded58* on the file system.59*/6061class ModuleReferences {62private ModuleReferences() { }6364/**65* Creates a ModuleReference to a possibly-patched module66*/67private static ModuleReference newModule(ModuleInfo.Attributes attrs,68URI uri,69Supplier<ModuleReader> supplier,70ModulePatcher patcher,71HashSupplier hasher) {72ModuleReference mref = new ModuleReferenceImpl(attrs.descriptor(),73uri,74supplier,75null,76attrs.target(),77attrs.recordedHashes(),78hasher,79attrs.moduleResolution());80if (patcher != null)81mref = patcher.patchIfNeeded(mref);8283return mref;84}8586/**87* Creates a ModuleReference to a possibly-patched module in a modular JAR.88*/89static ModuleReference newJarModule(ModuleInfo.Attributes attrs,90ModulePatcher patcher,91Path file) {92URI uri = file.toUri();93Supplier<ModuleReader> supplier = () -> new JarModuleReader(file, uri);94HashSupplier hasher = (a) -> ModuleHashes.computeHash(supplier, a);95return newModule(attrs, uri, supplier, patcher, hasher);96}9798/**99* Creates a ModuleReference to a module in a JMOD file.100*/101static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) {102URI uri = file.toUri();103Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);104HashSupplier hasher = (a) -> ModuleHashes.computeHash(supplier, a);105return newModule(attrs, uri, supplier, null, hasher);106}107108/**109* Creates a ModuleReference to a possibly-patched exploded module.110*/111static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs,112ModulePatcher patcher,113Path dir) {114Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);115return newModule(attrs, dir.toUri(), supplier, patcher, null);116}117118119/**120* A base module reader that encapsulates machinery required to close the121* module reader safely.122*/123static abstract class SafeCloseModuleReader implements ModuleReader {124125// RW lock to support safe close126private final ReadWriteLock lock = new ReentrantReadWriteLock();127private final Lock readLock = lock.readLock();128private final Lock writeLock = lock.writeLock();129private boolean closed;130131SafeCloseModuleReader() { }132133/**134* Returns a URL to resource. This method is invoked by the find135* method to do the actual work of finding the resource.136*/137abstract Optional<URI> implFind(String name) throws IOException;138139/**140* Returns an input stream for reading a resource. This method is141* invoked by the open method to do the actual work of opening142* an input stream to the resource.143*/144abstract Optional<InputStream> implOpen(String name) throws IOException;145146/**147* Returns a stream of the names of resources in the module. This148* method is invoked by the list method to do the actual work of149* creating the stream.150*/151abstract Stream<String> implList() throws IOException;152153/**154* Closes the module reader. This method is invoked by close to do the155* actual work of closing the module reader.156*/157abstract void implClose() throws IOException;158159@Override160public final Optional<URI> find(String name) throws IOException {161readLock.lock();162try {163if (!closed) {164return implFind(name);165} else {166throw new IOException("ModuleReader is closed");167}168} finally {169readLock.unlock();170}171}172173174@Override175public final Optional<InputStream> open(String name) throws IOException {176readLock.lock();177try {178if (!closed) {179return implOpen(name);180} else {181throw new IOException("ModuleReader is closed");182}183} finally {184readLock.unlock();185}186}187188@Override189public final Stream<String> list() throws IOException {190readLock.lock();191try {192if (!closed) {193return implList();194} else {195throw new IOException("ModuleReader is closed");196}197} finally {198readLock.unlock();199}200}201202@Override203public final void close() throws IOException {204writeLock.lock();205try {206if (!closed) {207closed = true;208implClose();209}210} finally {211writeLock.unlock();212}213}214}215216217/**218* A ModuleReader for a modular JAR file.219*/220static class JarModuleReader extends SafeCloseModuleReader {221private final JarFile jf;222private final URI uri;223224static JarFile newJarFile(Path path) {225try {226return new JarFile(new File(path.toString()),227true, // verify228ZipFile.OPEN_READ,229JarFile.runtimeVersion());230} catch (IOException ioe) {231throw new UncheckedIOException(ioe);232}233}234235JarModuleReader(Path path, URI uri) {236this.jf = newJarFile(path);237this.uri = uri;238}239240private JarEntry getEntry(String name) {241return jf.getJarEntry(Objects.requireNonNull(name));242}243244@Override245Optional<URI> implFind(String name) throws IOException {246JarEntry je = getEntry(name);247if (je != null) {248if (jf.isMultiRelease())249name = je.getRealName();250if (je.isDirectory() && !name.endsWith("/"))251name += "/";252String encodedPath = ParseUtil.encodePath(name, false);253String uris = "jar:" + uri + "!/" + encodedPath;254return Optional.of(URI.create(uris));255} else {256return Optional.empty();257}258}259260@Override261Optional<InputStream> implOpen(String name) throws IOException {262JarEntry je = getEntry(name);263if (je != null) {264return Optional.of(jf.getInputStream(je));265} else {266return Optional.empty();267}268}269270@Override271Stream<String> implList() throws IOException {272// take snapshot to avoid async close273List<String> names = jf.versionedStream()274.map(JarEntry::getName)275.toList();276return names.stream();277}278279@Override280void implClose() throws IOException {281jf.close();282}283}284285286/**287* A ModuleReader for a JMOD file.288*/289static class JModModuleReader extends SafeCloseModuleReader {290private final JmodFile jf;291private final URI uri;292293static JmodFile newJmodFile(Path path) {294try {295return new JmodFile(path);296} catch (IOException ioe) {297throw new UncheckedIOException(ioe);298}299}300301JModModuleReader(Path path, URI uri) {302this.jf = newJmodFile(path);303this.uri = uri;304}305306private JmodFile.Entry getEntry(String name) {307Objects.requireNonNull(name);308return jf.getEntry(JmodFile.Section.CLASSES, name);309}310311@Override312Optional<URI> implFind(String name) {313JmodFile.Entry je = getEntry(name);314if (je != null) {315if (je.isDirectory() && !name.endsWith("/"))316name += "/";317String encodedPath = ParseUtil.encodePath(name, false);318String uris = "jmod:" + uri + "!/" + encodedPath;319return Optional.of(URI.create(uris));320} else {321return Optional.empty();322}323}324325@Override326Optional<InputStream> implOpen(String name) throws IOException {327JmodFile.Entry je = getEntry(name);328if (je != null) {329return Optional.of(jf.getInputStream(je));330} else {331return Optional.empty();332}333}334335@Override336Stream<String> implList() throws IOException {337// take snapshot to avoid async close338List<String> names = jf.stream()339.filter(e -> e.section() == JmodFile.Section.CLASSES)340.map(JmodFile.Entry::name)341.toList();342return names.stream();343}344345@Override346void implClose() throws IOException {347jf.close();348}349}350351352/**353* A ModuleReader for an exploded module.354*/355static class ExplodedModuleReader implements ModuleReader {356private final Path dir;357private volatile boolean closed;358359ExplodedModuleReader(Path dir) {360this.dir = dir;361362// when running with a security manager then check that the caller363// has access to the directory.364@SuppressWarnings("removal")365SecurityManager sm = System.getSecurityManager();366if (sm != null) {367boolean unused = Files.isDirectory(dir);368}369}370371/**372* Throws IOException if the module reader is closed;373*/374private void ensureOpen() throws IOException {375if (closed) throw new IOException("ModuleReader is closed");376}377378@Override379public Optional<URI> find(String name) throws IOException {380ensureOpen();381Path path = Resources.toFilePath(dir, name);382if (path != null) {383try {384return Optional.of(path.toUri());385} catch (IOError e) {386throw (IOException) e.getCause();387}388} else {389return Optional.empty();390}391}392393@Override394public Optional<InputStream> open(String name) throws IOException {395ensureOpen();396Path path = Resources.toFilePath(dir, name);397if (path != null) {398return Optional.of(Files.newInputStream(path));399} else {400return Optional.empty();401}402}403404@Override405public Optional<ByteBuffer> read(String name) throws IOException {406ensureOpen();407Path path = Resources.toFilePath(dir, name);408if (path != null) {409return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));410} else {411return Optional.empty();412}413}414415@Override416public Stream<String> list() throws IOException {417ensureOpen();418return Files.walk(dir, Integer.MAX_VALUE)419.map(f -> Resources.toResourceName(dir, f))420.filter(s -> s.length() > 0);421}422423@Override424public void close() {425closed = true;426}427}428429}430431432