--- old/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java 2016-08-01 20:49:56.018916100 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java 2016-08-01 20:49:55.178868100 +0530 @@ -34,6 +34,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UncheckedIOException; @@ -60,6 +61,7 @@ import jdk.tools.jlink.internal.ExecutableImage; import jdk.tools.jlink.plugin.ResourcePool; import jdk.tools.jlink.plugin.ResourcePoolEntry; +import jdk.tools.jlink.plugin.ResourcePoolModule; import jdk.tools.jlink.plugin.PluginException; /** @@ -144,14 +146,12 @@ Files.createDirectories(mdir); } - private void storeFiles(Set modules, Map release) throws IOException { + private void storeFiles(Set modules, Properties release) throws IOException { if (release != null) { - Properties props = new Properties(); - props.putAll(release); - addModules(props, modules); + addModules(release, modules); File r = new File(root.toFile(), "release"); try (FileOutputStream fo = new FileOutputStream(r)) { - props.store(fo, null); + release.store(fo, null); } } } @@ -191,7 +191,7 @@ modules.add(m.name()); } }); - storeFiles(modules, files.releaseProperties()); + storeFiles(modules, releaseProperties(files)); if (Files.getFileStore(root).supportsFileAttributeView(PosixFileAttributeView.class)) { // launchers in the bin directory need execute permission @@ -219,6 +219,27 @@ } } + private Properties releaseProperties(ResourcePool pool) throws IOException { + Properties props = new Properties(); + Optional javaBase = pool.moduleView().findModule("java.base"); + javaBase.ifPresent(mod -> { + // fill release information available from transformed "java.base" module! + ModuleDescriptor desc = mod.descriptor(); + desc.osName().ifPresent(s -> props.setProperty("OS_NAME", s)); + desc.osVersion().ifPresent(s -> props.setProperty("OS_VERSION", s)); + desc.osArch().ifPresent(s -> props.setProperty("OS_ARCH", s)); + }); + + Optional release = pool.findEntry("/java.base/release"); + if (release.isPresent()) { + try (InputStream is = release.get().content()) { + props.load(is); + } + } + + return props; + } + /** * Generates launcher scripts. * @@ -317,6 +338,8 @@ case CONFIG: writeEntry(in, destFile("conf", filename)); break; + case TOP: + break; case OTHER: if (file instanceof SymImageFile) { SymImageFile sym = (SymImageFile) file; --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java 2016-08-01 20:50:00.641180500 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java 2016-08-01 20:49:59.893137700 +0530 @@ -279,6 +279,8 @@ private class LastModule implements ResourcePoolModule { final ResourcePoolModule module; + // lazily initialized + ModuleDescriptor descriptor; LastModule(ResourcePoolModule module) { this.module = module; @@ -297,7 +299,10 @@ @Override public ModuleDescriptor descriptor() { - return module.descriptor(); + if (descriptor == null) { + descriptor = ResourcePoolManager.readModuleDescriptor(this); + } + return descriptor; } @Override @@ -420,11 +425,6 @@ return pool.byteOrder(); } - @Override - public Map releaseProperties() { - return pool.releaseProperties(); - } - private ResourcePoolEntry getUncompressed(ResourcePoolEntry res) { if (res != null) { if (res instanceof ResourcePoolManager.CompressedModuleData) { @@ -458,18 +458,6 @@ throws Exception { Objects.requireNonNull(original); Objects.requireNonNull(transformed); - Optional javaBase = transformed.moduleView().findModule("java.base"); - javaBase.ifPresent(mod -> { - try { - Map release = transformed.releaseProperties(); - // fill release information available from transformed "java.base" module! - ModuleDescriptor desc = mod.descriptor(); - desc.osName().ifPresent(s -> release.put("OS_NAME", s)); - desc.osVersion().ifPresent(s -> release.put("OS_VERSION", s)); - desc.osArch().ifPresent(s -> release.put("OS_ARCH", s)); - } catch (Exception ignored) {} - }); - imageBuilder.storeFiles(new LastPoolManager(transformed).resourcePool()); } --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolManager.java 2016-08-01 20:50:04.576405600 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolManager.java 2016-08-01 20:50:03.823362500 +0530 @@ -50,10 +50,22 @@ * A manager for pool of resources. */ public class ResourcePoolManager { + // utility to read ModuleDescriptor of the given ResourcePoolModule + static ModuleDescriptor readModuleDescriptor(ResourcePoolModule mod) { + String p = "/" + mod.name() + "/module-info.class"; + Optional content = mod.findEntry(p); + if (!content.isPresent()) { + throw new PluginException("No module-info for " + mod.name() + + " module"); + } + ByteBuffer bb = ByteBuffer.wrap(content.get().contentBytes()); + return ModuleDescriptor.read(bb); + } class ResourcePoolModuleImpl implements ResourcePoolModule { final Map moduleContent = new LinkedHashMap<>(); + // lazily initialized private ModuleDescriptor descriptor; final String name; @@ -80,14 +92,7 @@ @Override public ModuleDescriptor descriptor() { if (descriptor == null) { - String p = "/" + name + "/module-info.class"; - Optional content = findEntry(p); - if (!content.isPresent()) { - throw new PluginException("No module-info for " + name - + " module"); - } - ByteBuffer bb = ByteBuffer.wrap(content.get().contentBytes()); - descriptor = ModuleDescriptor.read(bb); + descriptor = readModuleDescriptor(this); } return descriptor; } @@ -166,11 +171,6 @@ return ResourcePoolManager.this.byteOrder(); } - @Override - public Map releaseProperties() { - return ResourcePoolManager.this.releaseProperties(); - } - public StringTable getStringTable() { return ResourcePoolManager.this.getStringTable(); } @@ -214,7 +214,6 @@ private final Map resources = new LinkedHashMap<>(); private final Map modules = new LinkedHashMap<>(); private final ResourcePoolModuleImpl fileCopierModule = new ResourcePoolModuleImpl(FileCopierPlugin.FAKE_MODULE); - private Map releaseProps = new HashMap<>(); private final ByteOrder order; private final StringTable table; private final ResourcePool poolImpl; @@ -391,10 +390,6 @@ return order; } - public Map releaseProperties() { - return releaseProps; - } - public StringTable getStringTable() { return table; } --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java 2016-08-01 20:50:08.643638200 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java 2016-08-01 20:50:07.777588700 +0530 @@ -24,17 +24,23 @@ */ package jdk.tools.jlink.internal.plugins; +import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.module.ModuleDescriptor; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.function.Function; import jdk.tools.jlink.internal.Utils; import jdk.tools.jlink.plugin.ResourcePool; import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.ResourcePoolEntry; +import jdk.tools.jlink.plugin.ResourcePoolModule; import jdk.tools.jlink.plugin.Plugin.Category; import jdk.tools.jlink.plugin.Plugin.State; import jdk.tools.jlink.plugin.Plugin; @@ -119,7 +125,23 @@ @Override public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { - in.releaseProperties().putAll(release); - return in; + in.transformAndCopy(Function.identity(), out); + + // create a TOP level ResourcePoolEntry for "release" file. + out.add(ResourcePoolEntry.create("/java.base/release", + ResourcePoolEntry.Type.TOP, releaseFileContent())); + return out.build(); + } + + private byte[] releaseFileContent() { + Properties props = new Properties(); + props.putAll(release); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + props.store(baos, ""); + return baos.toByteArray(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } } } --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ResourcePool.java 2016-08-01 20:50:13.775786100 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ResourcePool.java 2016-08-01 20:50:12.995785000 +0530 @@ -95,13 +95,6 @@ public ByteOrder byteOrder(); /** - * Release properties such as OS, CPU name, version etc. - * - * @return the release properties - */ - public Map releaseProperties(); - - /** * Visit each ResourcePoolEntry in this ResourcePool to transform it and copy * the transformed ResourcePoolEntry to the output ResourcePoolBuilder. * --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ResourcePoolEntry.java 2016-08-01 20:50:21.411011900 +0530 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ResourcePoolEntry.java 2016-08-01 20:50:19.987947300 +0530 @@ -41,6 +41,7 @@ *
    *
  • For jimage content: /{module name}/{package1}/.../{packageN}/{file * name}
  • + *
  • For top-level files:/{module name}/{file name}
  • *
  • For other files (shared lib, launchers, config, ...):/{module name}/ * {@literal bin|conf|native}/{dir1}/.../{dirN}/{file name}
  • *
@@ -54,6 +55,7 @@ *
    CONFIG: A configuration file.
*
    NATIVE_CMD: A native process launcher.
*
    NATIVE_LIB: A native library.
+ *
    TOP: A top-level file in the jdk run-time image directory.
*
    OTHER: Other kind of file.
* */ @@ -62,6 +64,7 @@ CONFIG, NATIVE_CMD, NATIVE_LIB, + TOP, OTHER }