< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JRTArchive.java

Print this page
@@ -1,7 +1,8 @@
  /*
   * Copyright (c) 2024, Red Hat, Inc.
+  * Copyright (c) 2025, 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

@@ -30,11 +31,10 @@
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.UncheckedIOException;
  import java.lang.module.ModuleFinder;
- import java.lang.module.ModuleReference;
  import java.nio.charset.StandardCharsets;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.security.MessageDigest;

@@ -49,10 +49,12 @@
  import java.util.function.Function;
  import java.util.function.Predicate;
  import java.util.stream.Collectors;
  import java.util.stream.Stream;
  
+ import jdk.internal.jimage.ResourceEntries;
+ import jdk.internal.jimage.SystemImageReader;
  import jdk.internal.util.OperatingSystem;
  import jdk.tools.jlink.internal.Archive.Entry.EntryType;
  import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
  import jdk.tools.jlink.plugin.ResourcePoolEntry;
  import jdk.tools.jlink.plugin.ResourcePoolEntry.Type;

@@ -61,14 +63,13 @@
   * An archive implementation based on the JDK's run-time image. That is, classes
   * and resources from the modules image (lib/modules, or jimage) and other
   * associated files from the filesystem of the JDK installation.
   */
  public class JRTArchive implements Archive {
- 
      private final String module;
      private final Path path;
-     private final ModuleReference ref;
+     private final ResourceEntries imageResources;
      // The collection of files of this module
      private final List<JRTFile> files = new ArrayList<>();
      // Files not part of the lib/modules image of the JDK install.
      // Thus, native libraries, binaries, legal files, etc.
      private final List<String> otherRes;

@@ -97,16 +98,15 @@
                 List<ResourceDiff> perModDiff,
                 TaskHelper taskHelper,
                 Set<String> upgradeableFiles) {
          this.module = module;
          this.path = path;
-         this.ref = ModuleFinder.ofSystem()
-                                .find(module)
-                                .orElseThrow(() ->
-                                     new IllegalArgumentException(
-                                             "Module " + module +
-                                             " not part of the JDK install"));
+         ModuleFinder.ofSystem()
+                 .find(module)
+                 .orElseThrow(() -> new IllegalArgumentException(
+                         "Module " + module + " not part of the JDK install"));
+         this.imageResources = SystemImageReader.getResourceEntries();
          this.errorOnModifiedFile = errorOnModifiedFile;
          this.otherRes = readModuleResourceFile(module);
          this.resDiff = Objects.requireNonNull(perModDiff).stream()
                              .collect(Collectors.toMap(ResourceDiff::getName, Function.identity()));
          this.taskHelper = taskHelper;

@@ -157,56 +157,39 @@
          return (obj instanceof JRTArchive other &&
                     Objects.equals(module, other.module) &&
                     Objects.equals(path, other.path));
      }
  
+     private boolean isNormalOrModifiedDiff(String name) {
+         ResourceDiff rd = resDiff.get(name);
+         // Filter all resources with a resource diff of kind MODIFIED.
+         // Note that REMOVED won't happen since in that case the module listing
+         // won't have the resource anyway.
+         // Note as well that filter removes files of kind ADDED. Those files are
+         // not in the packaged modules, so ought not to get returned from the
+         // pipeline.
+         return (rd == null || rd.getKind() == ResourceDiff.Kind.MODIFIED);
+     }
+ 
      private void collectFiles() throws IOException {
          if (files.isEmpty()) {
              addNonClassResources();
+ 
              // Add classes/resources from the run-time image,
              // patched with the run-time image diff
-             files.addAll(ref.open().list()
-                                    .filter(i -> {
-                                            String lookupKey = String.format("/%s/%s", module, i);
-                                            ResourceDiff rd = resDiff.get(lookupKey);
-                                            // Filter all resources with a resource diff
-                                            // that are of kind MODIFIED.
-                                            // Note that REMOVED won't happen since in
-                                            // that case the module listing won't have
-                                            // the resource anyway.
-                                            // Note as well that filter removes files
-                                            // of kind ADDED. Those files are not in
-                                            // the packaged modules, so ought not to
-                                            // get returned from the pipeline.
-                                            return (rd == null ||
-                                                    rd.getKind() == ResourceDiff.Kind.MODIFIED);
-                                    })
-                                    .map(s -> {
-                                            String lookupKey = String.format("/%s/%s", module, s);
-                                            return new JRTArchiveFile(JRTArchive.this, s,
-                                                            EntryType.CLASS_OR_RESOURCE,
-                                                            null /* hashOrTarget */,
-                                                            false /* symlink */,
-                                                            resDiff.get(lookupKey));
-                                    })
-                                    .toList());
+             imageResources.getEntryNames(module)
+                     .filter(this::isNormalOrModifiedDiff)
+                     .sorted()
+                     .map(name -> new JrtModuleFile(this, name, resDiff.get(name)))
+                     .forEach(files::add);
+ 
              // Finally add all files only present in the resource diff
              // That is, removed items in the run-time image.
              files.addAll(resDiff.values().stream()
-                                          .filter(rd -> rd.getKind() == ResourceDiff.Kind.REMOVED)
-                                          .map(s -> {
-                                                  int secondSlash = s.getName().indexOf("/", 1);
-                                                  assert secondSlash != -1;
-                                                  String pathWithoutModule = s.getName().substring(secondSlash + 1);
-                                                  return new JRTArchiveFile(JRTArchive.this,
-                                                          pathWithoutModule,
-                                                          EntryType.CLASS_OR_RESOURCE,
-                                                          null  /* hashOrTarget */,
-                                                          false /* symlink */,
-                                                          s);
-                                          })
-                                          .toList());
+                     .filter(rd -> rd.getKind() == ResourceDiff.Kind.REMOVED)
+                     .map(rd -> new JrtModuleFile(this, rd.getName(), rd))
+                     .toList());
          }
      }
  
      /*
       * no need to keep track of the warning produced since this is eagerly

@@ -232,19 +215,14 @@
                              } else {
                                  taskHelper.warning("err.runtime.link.modified.file", path.toString());
                              }
                          }
  
-                         return new JRTArchiveFile(JRTArchive.this,
-                                                   m.resPath,
-                                                   toEntryType(m.resType),
-                                                   m.hashOrTarget,
-                                                   m.symlink,
-                                                   /* diff only for resources */
-                                                   null);
-                  })
-                  .toList());
+                         return new JrtOtherFile(
+                                 this, m.resPath, toEntryType(m.resType), m.hashOrTarget, m.symlink);
+                     })
+                     .toList());
          }
      }
  
      /**
       * Certain files in a module are considered upgradeable. That is,

@@ -321,15 +299,15 @@
                                   symlink ? 1 : 0,
                                   hashOrTarget,
                                   resPath);
          }
  
-         /**
+         /*
           *  line: <int>|<int>|<hashOrTarget>|<path>
           *
-          *  Take the integer before '|' convert it to a Type. The second
-          *  token is an integer representing symlinks (or not). The third token is
+          *  Take the integer before '|' convert it to a Type. The second token
+          *  is an integer representing symlinks (or not). The third token is
           *  a hash sum (sha512) of the file denoted by the fourth token (path).
           */
          static ResourceFileEntry decodeFromString(String line) {
              assert !line.isEmpty();
  

@@ -435,80 +413,88 @@
  
      interface JRTFile {
          Entry toEntry();
      }
  
-     record JRTArchiveFile(Archive archive,
-                           String resPath,
-                           EntryType resType,
-                           String sha,
-                           boolean symlink,
-                           ResourceDiff diff) implements JRTFile {
+     record JrtModuleFile(
+             JRTArchive archive,
+             String resPath,
+             ResourceDiff diff) implements JRTFile {
+         @Override
+         public Entry toEntry() {
+             assert resPath.startsWith("/" + archive.moduleName() + "/");
+             String resName = resPath.substring(archive.moduleName().length() + 2);
+ 
+             // If the resource has a diff to the packaged modules, use the diff.
+             // Diffs of kind ADDED have been filtered out in collectFiles();
+             if (diff != null) {
+                 assert diff.getKind() != ResourceDiff.Kind.ADDED;
+                 assert diff.getName().equals(resPath);
+ 
+                 return new Entry(archive, resPath, resName, EntryType.CLASS_OR_RESOURCE) {
+                     @Override
+                     public long size() {
+                         return diff.getResourceBytes().length;
+                     }
+                     @Override
+                     public InputStream stream() {
+                         return new ByteArrayInputStream(diff.getResourceBytes());
+                     }
+                 };
+             } else {
+                 return new Entry(archive, resPath, resName, EntryType.CLASS_OR_RESOURCE) {
+                     @Override
+                     public long size() {
+                         return archive.imageResources.getSize(resPath);
+                     }
+ 
+                     @Override
+                     public InputStream stream() {
+                         // Byte content could be cached in the entry if needed.
+                         return new ByteArrayInputStream(archive.imageResources.getBytes(resPath));
+                     }
+                 };
+             }
+         }
+     }
+ 
+     record JrtOtherFile(
+             JRTArchive archive,
+             String resPath,
+             EntryType resType,
+             String sha,
+             boolean symlink) implements JRTFile {
+ 
+         // Read from the base JDK image, special casing
+         // symlinks, which have the link target in the
+         // hashOrTarget field.
+         Path targetPath() {
+             return BASE.resolve(symlink ? sha : resPath);
+         }
+ 
          public Entry toEntry() {
-             return new Entry(archive,
-                              String.format("/%s/%s",
-                                            archive.moduleName(),
-                                            resPath),
-                              resPath,
-                              resType) {
+             assert resType != EntryType.CLASS_OR_RESOURCE;
+ 
+             return new Entry(
+                     archive,
+                     String.format("/%s/%s", archive.moduleName(), resPath),
+                     resPath,
+                     resType) {
+ 
                  @Override
                  public long size() {
                      try {
-                         if (resType != EntryType.CLASS_OR_RESOURCE) {
-                             // Read from the base JDK image, special casing
-                             // symlinks, which have the link target in the
-                             // hashOrTarget field
-                             if (symlink) {
-                                 return Files.size(BASE.resolve(sha));
-                             }
-                             return Files.size(BASE.resolve(resPath));
-                         } else {
-                             if (diff != null) {
-                                 // If the resource has a diff to the
-                                 // packaged modules, use the diff. Diffs of kind
-                                 // ADDED have been filtered out in collectFiles();
-                                 assert diff.getKind() != ResourceDiff.Kind.ADDED;
-                                 assert diff.getName().equals(String.format("/%s/%s",
-                                                                            archive.moduleName(),
-                                                                            resPath));
-                                 return diff.getResourceBytes().length;
-                             }
-                             // Read from the module image. This works, because
-                             // the underlying base path is a JrtPath with the
-                             // JrtFileSystem underneath which is able to handle
-                             // this size query.
-                             return Files.size(archive.getPath().resolve(resPath));
-                         }
+                         return Files.size(targetPath());
                      } catch (IOException e) {
                          throw new UncheckedIOException(e);
                      }
                  }
  
                  @Override
                  public InputStream stream() throws IOException {
-                     if (resType != EntryType.CLASS_OR_RESOURCE) {
-                         // Read from the base JDK image.
-                         Path path = symlink ? BASE.resolve(sha) : BASE.resolve(resPath);
-                         return Files.newInputStream(path);
-                     } else {
-                         // Read from the module image. Use the diff to the
-                         // packaged modules if we have one. Diffs of kind
-                         // ADDED have been filtered out in collectFiles();
-                         if (diff != null) {
-                             assert diff.getKind() != ResourceDiff.Kind.ADDED;
-                             assert diff.getName().equals(String.format("/%s/%s",
-                                                                        archive.moduleName(),
-                                                                        resPath));
-                             return new ByteArrayInputStream(diff.getResourceBytes());
-                         }
-                         String module = archive.moduleName();
-                         ModuleReference mRef = ModuleFinder.ofSystem()
-                                                     .find(module).orElseThrow();
-                         return mRef.open().open(resPath).orElseThrow();
-                     }
+                     return Files.newInputStream(targetPath());
                  }
- 
              };
          }
      }
  
      private static List<String> readModuleResourceFile(String modName) {
< prev index next >