< prev index next >

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

Print this page
*** 1,7 ***
  /*
!  * Copyright (c) 2015, 2024, 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
--- 1,7 ---
  /*
!  * 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

*** 34,13 ***
--- 34,15 ---
  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.Supplier;
  import java.util.jar.JarEntry;

*** 50,11 ***
  
  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.
   */
--- 52,10 ---

*** 120,12 ***
      /**
       * Creates a ModuleReference to a possibly-patched exploded module.
       */
      static ModuleReference newExplodedModule(ModuleInfo.Attributes attrs,
                                               ModulePatcher patcher,
                                               Path dir) {
!         Supplier<ModuleReader> supplier = () -> new ExplodedModuleReader(dir);
          return newModule(attrs, dir.toUri(), supplier, patcher, null);
      }
  
  
      /**
--- 121,13 ---
      /**
       * 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);
      }
  
  
      /**

*** 364,27 ***
      /**
       * A ModuleReader for an exploded module.
       */
      static class ExplodedModuleReader implements ModuleReader {
          private final Path dir;
          private volatile boolean closed;
  
!         ExplodedModuleReader(Path dir) {
              this.dir = dir;
          }
  
          /**
           * Throws IOException if the module reader is closed;
           */
          private void ensureOpen() throws IOException {
              if (closed) throw new IOException("ModuleReader is closed");
          }
  
          @Override
          public Optional<URI> find(String name) throws IOException {
              ensureOpen();
!             Path path = Resources.toFilePath(dir, name);
              if (path != null) {
                  try {
                      return Optional.of(path.toUri());
                  } catch (IOError e) {
                      throw (IOException) e.getCause();
--- 366,43 ---
      /**
       * A ModuleReader for an exploded module.
       */
      static class ExplodedModuleReader implements ModuleReader {
          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");
          }
  
+         private Path toFilePath(String name) throws IOException {
+             if (previewDir != null) {
+                 if (isPreviewEntry(name)) {
+                     return null;
+                 }
+                 Path previewPath = Resources.toFilePath(previewDir, name);
+                 if (previewPath != null && Files.exists(previewPath)) {
+                     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();

*** 395,39 ***
          }
  
          @Override
          public Optional<InputStream> open(String name) throws IOException {
              ensureOpen();
!             Path path = Resources.toFilePath(dir, 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);
              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);
          }
  
          @Override
          public void close() {
              closed = true;
          }
-     }
  
  }
--- 413,59 ---
          }
  
          @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();
!             LinkedHashSet<String> names = new LinkedHashSet<>();
!             collectNames(dir, names);
!             if (previewDir != null) {
+                 collectNames(previewDir, names);
+             }
+             return names.stream();
+         }
+ 
+         private void collectNames(Path dir, Set<String> dest) throws IOException {
+             try (Stream<Path> files = Files.walk(dir, Integer.MAX_VALUE)) {
+                 files.map(f -> Resources.toResourceName(dir, f))
+                         .filter(s -> !s.isEmpty())
+                         .filter(s -> previewDir == null || !isPreviewEntry(s))
+                         .forEach(dest::add);
+             }
          }
  
          @Override
          public void close() {
              closed = true;
          }
  
+         // Names do not have a leading '/'.
+         private static final String PREVIEW_PREFIX = "META-INF/preview";
+ 
+         private static boolean isPreviewEntry(String name) {
+             return name.startsWith(PREVIEW_PREFIX) &&
+                     (name.length() == PREVIEW_PREFIX.length()
+                             || name.charAt(PREVIEW_PREFIX.length()) == '/');
+         }
+     }
  }
< prev index next >