< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java

Print this page




  41 import java.lang.module.ModuleDescriptor;
  42 import java.nio.charset.StandardCharsets;
  43 import java.nio.file.FileAlreadyExistsException;
  44 import java.nio.file.Files;
  45 import java.nio.file.Path;
  46 import java.nio.file.Paths;
  47 import java.nio.file.StandardOpenOption;
  48 import java.nio.file.attribute.PosixFilePermission;
  49 import java.util.Collections;
  50 import java.util.HashMap;
  51 import java.util.HashSet;
  52 import java.util.List;
  53 import java.util.Map;
  54 import java.util.Objects;
  55 import java.util.Optional;
  56 import java.util.Properties;
  57 import java.util.Set;
  58 import static java.util.stream.Collectors.*;
  59 
  60 import jdk.tools.jlink.internal.BasicImageWriter;
  61 import jdk.tools.jlink.internal.plugins.FileCopierPlugin.SymImageFile;
  62 import jdk.tools.jlink.internal.ExecutableImage;
  63 import jdk.tools.jlink.plugin.ResourcePool;
  64 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  65 import jdk.tools.jlink.plugin.ResourcePoolEntry.Type;
  66 import jdk.tools.jlink.plugin.ResourcePoolModule;
  67 import jdk.tools.jlink.plugin.PluginException;
  68 
  69 /**
  70  *
  71  * Default Image Builder. This builder creates the default runtime image layout.
  72  */
  73 public final class DefaultImageBuilder implements ImageBuilder {
  74     // Top-level directory names in a modular runtime image
  75     public static final String BIN_DIRNAME      = "bin";
  76     public static final String CONF_DIRNAME     = "conf";
  77     public static final String INCLUDE_DIRNAME  = "include";
  78     public static final String LIB_DIRNAME      = "lib";
  79     public static final String LEGAL_DIRNAME    = "legal";
  80     public static final String MAN_DIRNAME      = "man";
  81 


 132 
 133     private final Path root;
 134     private final Path mdir;
 135     private final Set<String> modules = new HashSet<>();
 136     private String targetOsName;
 137 
 138     /**
 139      * Default image builder constructor.
 140      *
 141      * @param root The image root directory.
 142      * @throws IOException
 143      */
 144     public DefaultImageBuilder(Path root) throws IOException {
 145         Objects.requireNonNull(root);
 146 
 147         this.root = root;
 148         this.mdir = root.resolve("lib");
 149         Files.createDirectories(mdir);
 150     }
 151 
 152     private void storeRelease(ResourcePool pool) throws IOException {
 153         Properties props = new Properties();
 154         Optional<ResourcePoolEntry> release = pool.findEntry("/java.base/release");
 155         if (release.isPresent()) {
 156             try (InputStream is = release.get().content()) {
 157                 props.load(is);
 158             }
 159         }
 160         File r = new File(root.toFile(), "release");
 161         try (FileOutputStream fo = new FileOutputStream(r)) {
 162             props.store(fo, null);
 163         }
 164     }
 165 
 166     @Override
 167     public void storeFiles(ResourcePool files) {
 168         try {
 169             // populate targetOsName field up-front because it's used elsewhere.
 170             Optional<ResourcePoolModule> javaBase = files.moduleView().findModule("java.base");
 171             javaBase.ifPresent(mod -> {
 172                 // fill release information available from transformed "java.base" module!
 173                 ModuleDescriptor desc = mod.descriptor();
 174                 desc.osName().ifPresent(s -> {
 175                     this.targetOsName = s;
 176                 });
 177             });
 178 
 179             if (this.targetOsName == null) {
 180                 throw new PluginException("TargetPlatform attribute is missing for java.base module");
 181             }
 182 
 183             // store 'release' file
 184             storeRelease(files);
 185 
 186             Path bin = root.resolve(BIN_DIRNAME);
 187 
 188             // check any duplicated resource files
 189             Map<Path, Set<String>> duplicates = new HashMap<>();
 190             files.entries()
 191                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
 192                 .collect(groupingBy(this::entryToImagePath,
 193                          mapping(ResourcePoolEntry::moduleName, toSet())))
 194                 .entrySet()
 195                 .stream()
 196                 .filter(e -> e.getValue().size() > 1)
 197                 .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
 198 
 199             // write non-classes resource files to the image
 200             files.entries()
 201                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
 202                 .forEach(f -> {
 203                     try {
 204                         accept(f);
 205                     } catch (FileAlreadyExistsException e) {


 356     /**
 357      * Returns the path of the given entry to be written in the image
 358      */
 359     private Path entryToImagePath(ResourcePoolEntry entry) {
 360         switch (entry.type()) {
 361             case NATIVE_LIB:
 362                 String filename = entryToFileName(entry);
 363                 return Paths.get(nativeDir(filename), filename);
 364             case NATIVE_CMD:
 365                 return Paths.get(BIN_DIRNAME, entryToFileName(entry));
 366             case CONFIG:
 367                 return Paths.get(CONF_DIRNAME, entryToFileName(entry));
 368             case HEADER_FILE:
 369                 return Paths.get(INCLUDE_DIRNAME, entryToFileName(entry));
 370             case MAN_PAGE:
 371                 return Paths.get(MAN_DIRNAME, entryToFileName(entry));
 372             case LEGAL_NOTICE:
 373                 return Paths.get(LEGAL_DIRNAME, entryToFileName(entry));
 374             case TOP:
 375                 return Paths.get(entryToFileName(entry));
 376             case OTHER:
 377                 return Paths.get("other", entryToFileName(entry));
 378             default:
 379                 throw new IllegalArgumentException("invalid type: " + entry);
 380         }
 381     }
 382 
 383     private void accept(ResourcePoolEntry file) throws IOException {
 384         if (file.linkedTarget() != null && file.type() != Type.LEGAL_NOTICE) {
 385             throw new UnsupportedOperationException("symbolic link not implemented: " + file);
 386         }
 387 
 388         try (InputStream in = file.content()) {
 389             switch (file.type()) {
 390                 case NATIVE_LIB:
 391                     Path dest = root.resolve(entryToImagePath(file));
 392                     writeEntry(in, dest);
 393                     break;
 394                 case NATIVE_CMD:
 395                     Path p = root.resolve(entryToImagePath(file));
 396                     writeEntry(in, p);
 397                     p.toFile().setExecutable(true);
 398                     break;
 399                 case CONFIG:
 400                 case HEADER_FILE:
 401                 case MAN_PAGE:
 402                     writeEntry(in, root.resolve(entryToImagePath(file)));
 403                     break;
 404                 case LEGAL_NOTICE:
 405                     Path source = entryToImagePath(file);
 406                     if (file.linkedTarget() == null) {
 407                         writeEntry(in, root.resolve(source));
 408                     } else {
 409                         Path target = entryToImagePath(file.linkedTarget());
 410                         Path relPath = source.getParent().relativize(target);
 411                         writeSymLinkEntry(root.resolve(source), relPath);
 412                     }
 413                     break;
 414                 case TOP:
 415                     break;
 416                 case OTHER:
 417                     String filename = entryToFileName(file);
 418                     if (file instanceof SymImageFile) {
 419                         SymImageFile sym = (SymImageFile) file;
 420                         Path target = root.resolve(sym.getTargetPath());
 421                         if (!Files.exists(target)) {
 422                             throw new IOException("Sym link target " + target
 423                                     + " doesn't exist");
 424                         }
 425                         writeSymEntry(root.resolve(filename), target);
 426                     } else {
 427                         writeEntry(in, root.resolve(filename));
 428                     }
 429                     break;
 430                 default:
 431                     throw new InternalError("unexpected entry: " + file.path());
 432             }
 433         }
 434     }
 435 
 436     private void writeEntry(InputStream in, Path dstFile) throws IOException {
 437         Objects.requireNonNull(in);
 438         Objects.requireNonNull(dstFile);
 439         Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
 440         Files.copy(in, dstFile);
 441     }
 442 
 443     private void writeSymEntry(Path dstFile, Path target) throws IOException {
 444         Objects.requireNonNull(dstFile);
 445         Objects.requireNonNull(target);
 446         Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
 447         Files.createLink(dstFile, target);




  41 import java.lang.module.ModuleDescriptor;
  42 import java.nio.charset.StandardCharsets;
  43 import java.nio.file.FileAlreadyExistsException;
  44 import java.nio.file.Files;
  45 import java.nio.file.Path;
  46 import java.nio.file.Paths;
  47 import java.nio.file.StandardOpenOption;
  48 import java.nio.file.attribute.PosixFilePermission;
  49 import java.util.Collections;
  50 import java.util.HashMap;
  51 import java.util.HashSet;
  52 import java.util.List;
  53 import java.util.Map;
  54 import java.util.Objects;
  55 import java.util.Optional;
  56 import java.util.Properties;
  57 import java.util.Set;
  58 import static java.util.stream.Collectors.*;
  59 
  60 import jdk.tools.jlink.internal.BasicImageWriter;

  61 import jdk.tools.jlink.internal.ExecutableImage;
  62 import jdk.tools.jlink.plugin.ResourcePool;
  63 import jdk.tools.jlink.plugin.ResourcePoolEntry;
  64 import jdk.tools.jlink.plugin.ResourcePoolEntry.Type;
  65 import jdk.tools.jlink.plugin.ResourcePoolModule;
  66 import jdk.tools.jlink.plugin.PluginException;
  67 
  68 /**
  69  *
  70  * Default Image Builder. This builder creates the default runtime image layout.
  71  */
  72 public final class DefaultImageBuilder implements ImageBuilder {
  73     // Top-level directory names in a modular runtime image
  74     public static final String BIN_DIRNAME      = "bin";
  75     public static final String CONF_DIRNAME     = "conf";
  76     public static final String INCLUDE_DIRNAME  = "include";
  77     public static final String LIB_DIRNAME      = "lib";
  78     public static final String LEGAL_DIRNAME    = "legal";
  79     public static final String MAN_DIRNAME      = "man";
  80 


 131 
 132     private final Path root;
 133     private final Path mdir;
 134     private final Set<String> modules = new HashSet<>();
 135     private String targetOsName;
 136 
 137     /**
 138      * Default image builder constructor.
 139      *
 140      * @param root The image root directory.
 141      * @throws IOException
 142      */
 143     public DefaultImageBuilder(Path root) throws IOException {
 144         Objects.requireNonNull(root);
 145 
 146         this.root = root;
 147         this.mdir = root.resolve("lib");
 148         Files.createDirectories(mdir);
 149     }
 150 














 151     @Override
 152     public void storeFiles(ResourcePool files) {
 153         try {
 154             // populate targetOsName field up-front because it's used elsewhere.
 155             Optional<ResourcePoolModule> javaBase = files.moduleView().findModule("java.base");
 156             javaBase.ifPresent(mod -> {
 157                 // fill release information available from transformed "java.base" module!
 158                 ModuleDescriptor desc = mod.descriptor();
 159                 desc.osName().ifPresent(s -> {
 160                     this.targetOsName = s;
 161                 });
 162             });
 163 
 164             if (this.targetOsName == null) {
 165                 throw new PluginException("TargetPlatform attribute is missing for java.base module");
 166             }
 167 



 168             Path bin = root.resolve(BIN_DIRNAME);
 169 
 170             // check any duplicated resource files
 171             Map<Path, Set<String>> duplicates = new HashMap<>();
 172             files.entries()
 173                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
 174                 .collect(groupingBy(this::entryToImagePath,
 175                          mapping(ResourcePoolEntry::moduleName, toSet())))
 176                 .entrySet()
 177                 .stream()
 178                 .filter(e -> e.getValue().size() > 1)
 179                 .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
 180 
 181             // write non-classes resource files to the image
 182             files.entries()
 183                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
 184                 .forEach(f -> {
 185                     try {
 186                         accept(f);
 187                     } catch (FileAlreadyExistsException e) {


 338     /**
 339      * Returns the path of the given entry to be written in the image
 340      */
 341     private Path entryToImagePath(ResourcePoolEntry entry) {
 342         switch (entry.type()) {
 343             case NATIVE_LIB:
 344                 String filename = entryToFileName(entry);
 345                 return Paths.get(nativeDir(filename), filename);
 346             case NATIVE_CMD:
 347                 return Paths.get(BIN_DIRNAME, entryToFileName(entry));
 348             case CONFIG:
 349                 return Paths.get(CONF_DIRNAME, entryToFileName(entry));
 350             case HEADER_FILE:
 351                 return Paths.get(INCLUDE_DIRNAME, entryToFileName(entry));
 352             case MAN_PAGE:
 353                 return Paths.get(MAN_DIRNAME, entryToFileName(entry));
 354             case LEGAL_NOTICE:
 355                 return Paths.get(LEGAL_DIRNAME, entryToFileName(entry));
 356             case TOP:
 357                 return Paths.get(entryToFileName(entry));


 358             default:
 359                 throw new IllegalArgumentException("invalid type: " + entry);
 360         }
 361     }
 362 
 363     private void accept(ResourcePoolEntry file) throws IOException {
 364         if (file.linkedTarget() != null && file.type() != Type.LEGAL_NOTICE) {
 365             throw new UnsupportedOperationException("symbolic link not implemented: " + file);
 366         }
 367 
 368         try (InputStream in = file.content()) {
 369             switch (file.type()) {
 370                 case NATIVE_LIB:
 371                     Path dest = root.resolve(entryToImagePath(file));
 372                     writeEntry(in, dest);
 373                     break;
 374                 case NATIVE_CMD:
 375                     Path p = root.resolve(entryToImagePath(file));
 376                     writeEntry(in, p);
 377                     p.toFile().setExecutable(true);
 378                     break;
 379                 case CONFIG:
 380                 case HEADER_FILE:
 381                 case MAN_PAGE:
 382                     writeEntry(in, root.resolve(entryToImagePath(file)));
 383                     break;
 384                 case LEGAL_NOTICE:
 385                     Path source = entryToImagePath(file);
 386                     if (file.linkedTarget() == null) {
 387                         writeEntry(in, root.resolve(source));
 388                     } else {
 389                         Path target = entryToImagePath(file.linkedTarget());
 390                         Path relPath = source.getParent().relativize(target);
 391                         writeSymLinkEntry(root.resolve(source), relPath);
 392                     }
 393                     break;
 394                 case TOP:
 395                     // Copy TOP files of the "java.base" module (only)
 396                     if ("java.base".equals(file.moduleName())) {
 397                         writeEntry(in, root.resolve(entryToImagePath(file)));










 398                     }
 399                     break;
 400                 default:
 401                     throw new InternalError("unexpected entry: " + file.path());
 402             }
 403         }
 404     }
 405 
 406     private void writeEntry(InputStream in, Path dstFile) throws IOException {
 407         Objects.requireNonNull(in);
 408         Objects.requireNonNull(dstFile);
 409         Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
 410         Files.copy(in, dstFile);
 411     }
 412 
 413     private void writeSymEntry(Path dstFile, Path target) throws IOException {
 414         Objects.requireNonNull(dstFile);
 415         Objects.requireNonNull(target);
 416         Files.createDirectories(Objects.requireNonNull(dstFile.getParent()));
 417         Files.createLink(dstFile, target);


< prev index next >