< prev index next >

src/java.base/share/classes/jdk/internal/module/ModuleReferences.java

Print this page

  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 }
< prev index next >