From e21930811036b2c070019eda53d937e6604f3c29 Mon Sep 17 00:00:00 2001 From: jpai Date: Thu, 9 Sep 2021 20:32:00 +0530 Subject: [PATCH 01/12] 8258117: jar tool sets the time stamp of module-info.class entries to the current time --- .../share/classes/sun/tools/jar/Main.java | 77 ++++-- ...olModuleDescriptorReproducibilityTest.java | 259 ++++++++++++++++++ 2 files changed, 317 insertions(+), 19 deletions(-) create mode 100644 test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 4458cd00b445e..fdeb9a4bdb6c8 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -120,7 +120,7 @@ public int hashCode() { Set entries = new LinkedHashSet<>(); // module-info.class entries need to be added/updated. - Map moduleInfos = new HashMap<>(); + Map moduleInfos = new HashMap<>(); // A paths Set for each version, where each Set contains directories // specified by the "-C" operation. @@ -801,7 +801,9 @@ private void expand(File dir, String[] files, Set cpaths, int version) if (f.isFile()) { Entry e = new Entry(f, name, false); if (isModuleInfoEntry(name)) { - moduleInfos.putIfAbsent(name, Files.readAllBytes(f.toPath())); + Long lastModified = f.lastModified() == 0 ? null : f.lastModified(); + moduleInfos.putIfAbsent(name, + new StreamedModuleInfoEntry(name, Files.readAllBytes(f.toPath()), lastModified)); if (uflag) entryMap.put(name, e); } else if (entries.add(e)) { @@ -895,7 +897,7 @@ private boolean equalsIgnoreCase(String s, String upper) { */ boolean update(InputStream in, OutputStream out, InputStream newManifest, - Map moduleInfos, + Map moduleInfos, JarIndex jarIndex) throws IOException { ZipInputStream zis = new ZipInputStream(in); @@ -944,7 +946,8 @@ boolean update(InputStream in, OutputStream out, return false; } } else if (moduleInfos != null && isModuleInfoEntry) { - moduleInfos.putIfAbsent(name, zis.readAllBytes()); + Long lastModified = e.getTime() == -1 ? null : e.getTime(); + moduleInfos.putIfAbsent(name, new StreamedModuleInfoEntry(name, zis.readAllBytes(), lastModified)); } else { boolean isDir = e.isDirectory(); if (!entryMap.containsKey(name)) { // copy the old stuff @@ -1028,15 +1031,16 @@ private void addIndex(JarIndex index, ZipOutputStream zos) zos.closeEntry(); } - private void updateModuleInfo(Map moduleInfos, ZipOutputStream zos) + private void updateModuleInfo(Map moduleInfos, ZipOutputStream zos) throws IOException { String fmt = uflag ? "out.update.module-info": "out.added.module-info"; - for (Map.Entry mi : moduleInfos.entrySet()) { + for (Map.Entry mi : moduleInfos.entrySet()) { String name = mi.getKey(); - byte[] bytes = mi.getValue(); + byte[] bytes = mi.getValue().readAllBytes(); ZipEntry e = new ZipEntry(name); - e.setTime(System.currentTimeMillis()); + Long lastModified = mi.getValue().getLastModifiedTime(); + e.setTime(lastModified == null ? System.currentTimeMillis() : lastModified); if (flag0) { crc32ModuleInfo(e, bytes); } @@ -1731,12 +1735,23 @@ private File createTemporaryFile(String tmpbase, String suffix) { /** * Associates a module descriptor's zip entry name along with its - * bytes and an optional URI. Used when describing modules. + * bytes and an optional URI. */ interface ModuleInfoEntry { String name(); Optional uriString(); InputStream bytes() throws IOException; + /** + * @return Returns the last modified time of the module descriptor. + * Returns null if the last modified time is unknown or cannot be + * determined. + */ + Long getLastModifiedTime(); + default byte[] readAllBytes() throws IOException { + try (InputStream is = bytes()) { + return is.readAllBytes(); + } + } } static class ZipFileModuleInfoEntry implements ModuleInfoEntry { @@ -1750,6 +1765,12 @@ static class ZipFileModuleInfoEntry implements ModuleInfoEntry { @Override public InputStream bytes() throws IOException { return zipFile.getInputStream(entry); } + + @Override + public Long getLastModifiedTime() { + return entry.getTime() == -1 ? null : entry.getTime(); + } + /** Returns an optional containing the effective URI. */ @Override public Optional uriString() { String uri = (Paths.get(zipFile.getName())).toUri().toString(); @@ -1761,14 +1782,28 @@ static class ZipFileModuleInfoEntry implements ModuleInfoEntry { static class StreamedModuleInfoEntry implements ModuleInfoEntry { private final String name; private final byte[] bytes; - StreamedModuleInfoEntry(String name, byte[] bytes) { + private final Long lastModifiedTime; + + StreamedModuleInfoEntry(String name, byte[] bytes, Long lastModifiedTime) { this.name = name; this.bytes = bytes; + this.lastModifiedTime = lastModifiedTime; } @Override public String name() { return name; } @Override public InputStream bytes() throws IOException { return new ByteArrayInputStream(bytes); } + + @Override + public byte[] readAllBytes() throws IOException { + return bytes; + } + + @Override + public Long getLastModifiedTime() { + return lastModifiedTime; + } + /** Returns an empty optional. */ @Override public Optional uriString() { return Optional.empty(); // no URI can be derived @@ -1820,7 +1855,8 @@ private boolean describeModuleFromStream(FileInputStream fis) while ((e = zis.getNextEntry()) != null) { String ename = e.getName(); if (isModuleInfoEntry(ename)) { - infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes())); + Long lastModified = e.getTime() == -1 ? null : e.getTime(); + infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes(), lastModified)); } } } @@ -2033,14 +2069,14 @@ static String toBinaryName(String classname) { return (classname.replace('.', '/')) + ".class"; } - private boolean checkModuleInfo(byte[] moduleInfoBytes, Set entries) + private boolean checkModuleInfo(ModuleInfoEntry moduleInfoEntry, Set entries) throws IOException { boolean ok = true; - if (moduleInfoBytes != null) { // no root module-info.class if null + if (moduleInfoEntry != null) { // no root module-info.class if null try { // ModuleDescriptor.read() checks open/exported pkgs vs packages - ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes)); + ModuleDescriptor md = ModuleDescriptor.read(moduleInfoEntry.bytes()); // A module must have the implementation class of the services it 'provides'. if (md.provides().stream().map(Provides::providers).flatMap(List::stream) .filter(p -> !entries.contains(toBinaryName(p))) @@ -2058,15 +2094,18 @@ private boolean checkModuleInfo(byte[] moduleInfoBytes, Set entries) /** * Adds extended modules attributes to the given module-info's. The given - * Map values are updated in-place. Returns false if an error occurs. + * Map values are updated in-place. */ - private void addExtendedModuleAttributes(Map moduleInfos, + private void addExtendedModuleAttributes(Map moduleInfos, Set packages) throws IOException { - for (Map.Entry e: moduleInfos.entrySet()) { - ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue())); - e.setValue(extendedInfoBytes(md, e.getValue(), packages)); + for (Map.Entry e: moduleInfos.entrySet()) { + byte[] bytes = e.getValue().readAllBytes(); + ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(bytes)); + byte[] extended = extendedInfoBytes(md, bytes, packages); + // replace the entry value with the extended bytes + e.setValue(new StreamedModuleInfoEntry(e.getValue().name(), extended, e.getValue().getLastModifiedTime())); } } diff --git a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java new file mode 100644 index 0000000000000..b81dcf8b5027a --- /dev/null +++ b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2021, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.module.ModuleDescriptor; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.spi.ToolProvider; + +/** + * @test + * @bug 8258117 + * @library /test/lib + * @summary Tests that the content generated for module-info.class, using the jar command, is reproducible + * @run driver JarToolModuleDescriptorReproducibilityTest + */ +public class JarToolModuleDescriptorReproducibilityTest { + + private static final String MODULE_NAME = "foo"; + private static final String MODULE_VERSION = "1.2.3"; + private static final String UPDATED_MODULE_VERSION = "1.2.4"; + private static final String MAIN_CLASS = "jdk.test.foo.Foo"; + private static final Path MODULE_CLASSES_DIR = Paths.get("8258117-module-classes", MODULE_NAME).toAbsolutePath(); + + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") + .orElseThrow(() + -> new RuntimeException("jar tool not found") + ); + private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac") + .orElseThrow(() + -> new RuntimeException("javac tool not found") + ); + + public static void main(final String[] args) throws Exception { + // compile the module classes once + compileModuleClasses(); + // jar --create tests + testJarCreate(); + // jar --update tests + testJarUpdate(); + } + + /** + * Launches a "jar --create" process multiple times to create a module-info.class module descriptor + * from the same content and then expects that the modular jar created by each of these processes + * has the exact same bytes. + */ + private static void testJarCreate() throws Exception { + final List jarFiles = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + final Path tmpFile = Files.createTempFile(Path.of("."), "8258117-jar-create", ".jar"); + jarFiles.add(tmpFile); + final ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( + CreateJar.class.getName(), + tmpFile.toString()); + if (i > 0) { + // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed + // down to SECONDS unit. So we make sure that there's at least a second + // gap between the jar file creations, to be sure that the jar file + // was indeed generated at "different times" + Thread.sleep(1000); + } + executeJavaProcess(processBuilder); + } + assertAllFileContentsAreSame(jarFiles); + } + + /** + * Launches a "jar --update" process multiple times to update the module-info.class + * descriptor with the same content and then expects that the modular jar created by + * each of these processes has the exact same bytes. + */ + private static void testJarUpdate() throws Exception { + final List jarFiles = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + final Path tmpFile = Files.createTempFile(Path.of("."), "8258117-jar-update", ".jar"); + jarFiles.add(tmpFile); + final ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( + UpdateJar.class.getName(), + tmpFile.toString()); + if (i > 0) { + // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed + // down to SECONDS unit. So we make sure that there's at least a second + // gap between the jar file creations, to be sure that the jar file + // was indeed generated at "different times" + Thread.sleep(1000); + } + executeJavaProcess(processBuilder); + } + assertAllFileContentsAreSame(jarFiles); + } + + // compiles using javac tool the classes used in the test module + private static void compileModuleClasses() throws Exception { + Path sourcePath = Paths.get(System.getProperty("test.src", "."), + "src", MODULE_NAME); + List sourceFiles = new ArrayList<>(); + Files.walkFileTree(sourcePath, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { + if (file.toString().endsWith(".java")) { + sourceFiles.add(file.toString()); + } + return FileVisitResult.CONTINUE; + } + }); + Path classesDir = Files.createDirectories(MODULE_CLASSES_DIR); + List javacArgs = new ArrayList<>(); + javacArgs.add("-d"); + javacArgs.add(classesDir.toString()); + sourceFiles.forEach((f) -> javacArgs.add(f)); + System.out.println("Launching javac command with args: " + javacArgs); + StringWriter sw = new StringWriter(); + try (PrintWriter pw = new PrintWriter(sw)) { + int exitCode = JAVAC_TOOL.run(pw, pw, javacArgs.toArray(new String[0])); + if (exitCode != 0) { + throw new RuntimeException("Module compilation failed: " + sw.toString()); + } + } + System.out.println("Module classes successfully compiled to directory " + classesDir); + } + + // runs the "jar" command passing it the "jarArgs" and verifying that the command + // execution didn't fail + private static void runJarCommand(String... jarArgs) throws Exception { + StringWriter sw = new StringWriter(); + System.out.println("Launching jar command with args: " + Arrays.toString(jarArgs)); + try (PrintWriter pw = new PrintWriter(sw)) { + int exitCode = JAR_TOOL.run(pw, pw, jarArgs); + if (exitCode != 0) { + throw new RuntimeException("jar command execution failed: " + sw.toString()); + } + } + } + + // launches the java process and waits for it to exit. throws an exception if exit value is non-zero + private static void executeJavaProcess(ProcessBuilder pb) throws Exception { + final OutputAnalyzer outputAnalyzer = ProcessTools.executeProcess(pb); + try { + outputAnalyzer.shouldHaveExitValue(0); + } finally { + // print out any stdout/err that was generated in the launched program + outputAnalyzer.reportDiagnosticSummary(); + } + } + + // verifies the byte equality of the contents in each of the files + private static void assertAllFileContentsAreSame(final List files) throws Exception { + final byte[] file1Contents = Files.readAllBytes(files.get(0)); + for (int i = 1; i < files.size(); i++) { + final byte[] otherFileContents = Files.readAllBytes(files.get(i)); + if (!Arrays.equals(file1Contents, otherFileContents)) { + throw new RuntimeException("Content in file " + files.get(i) + + " isn't the same as in file " + files.get(0)); + } + } + } + + // verifies that a module-info.class is present in the jar and the module name and version are the expected + // ones + private static void assertExpectedModuleInfo(Path jar, String expectedModuleVersion) throws Exception { + try (JarInputStream jaris = new JarInputStream(Files.newInputStream(jar))) { + JarEntry moduleInfoEntry = null; + JarEntry entry = null; + while ((entry = jaris.getNextJarEntry()) != null) { + if (entry.getName().equals("module-info.class")) { + moduleInfoEntry = entry; + break; + } + } + if (moduleInfoEntry == null) { + throw new RuntimeException("module-info.class is missing from jar " + jar); + } + ModuleDescriptor md = ModuleDescriptor.read(jaris); + if (!md.name().equals(MODULE_NAME)) { + throw new RuntimeException("Unexpected module name " + md.name() + ", expected " + MODULE_NAME); + } + if (md.rawVersion().isEmpty()) { + throw new RuntimeException("Module version missing from descriptor"); + } + String actualVersion = md.rawVersion().get(); + if (!actualVersion.equals(expectedModuleVersion)) { + throw new RuntimeException("Unexpected module version " + actualVersion + ", expected " + expectedModuleVersion); + } + System.out.println(moduleInfoEntry.getName() + " has a timestamp of " + + moduleInfoEntry.getTime() + " for version " + actualVersion); + } + } + + static class CreateJar { + public static void main(final String[] args) throws Exception { + Path targetJar = Path.of(args[0]); + // create a modular jar + runJarCommand("--create", + "--file=" + targetJar, + "--main-class=" + MAIN_CLASS, + "--module-version=" + MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "."); + // verify the module descriptor in the jar + assertExpectedModuleInfo(targetJar, MODULE_VERSION); + } + } + + static class UpdateJar { + public static void main(final String[] args) throws Exception { + Path targetJar = Path.of(args[0]); + // create the modular jar + runJarCommand("--create", + "--file=" + targetJar, + "--module-version=" + MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "."); + assertExpectedModuleInfo(targetJar, MODULE_VERSION); + // update the same modular jar + runJarCommand("--update", + "--file=" + targetJar, + "--module-version=" + UPDATED_MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "module-info.class"); + // verify the module descriptor in the jar + assertExpectedModuleInfo(targetJar, UPDATED_MODULE_VERSION); + } + } +} From 57e1753f4a74f59eb371101e100b2c62c3591546 Mon Sep 17 00:00:00 2001 From: jpai Date: Fri, 5 Nov 2021 18:37:19 +0530 Subject: [PATCH 02/12] convert test to testng --- ...olModuleDescriptorReproducibilityTest.java | 116 +++++++----------- 1 file changed, 42 insertions(+), 74 deletions(-) diff --git a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java index b81dcf8b5027a..093d5effc9d6e 100644 --- a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java +++ b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java @@ -21,8 +21,8 @@ * questions. */ -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; import java.io.IOException; import java.io.PrintWriter; @@ -44,9 +44,8 @@ /** * @test * @bug 8258117 - * @library /test/lib * @summary Tests that the content generated for module-info.class, using the jar command, is reproducible - * @run driver JarToolModuleDescriptorReproducibilityTest + * @run testng JarToolModuleDescriptorReproducibilityTest */ public class JarToolModuleDescriptorReproducibilityTest { @@ -65,28 +64,23 @@ public class JarToolModuleDescriptorReproducibilityTest { -> new RuntimeException("javac tool not found") ); - public static void main(final String[] args) throws Exception { - // compile the module classes once + + @BeforeClass + public static void setup() throws Exception { compileModuleClasses(); - // jar --create tests - testJarCreate(); - // jar --update tests - testJarUpdate(); } /** - * Launches a "jar --create" process multiple times to create a module-info.class module descriptor - * from the same content and then expects that the modular jar created by each of these processes - * has the exact same bytes. + * Launches a "jar --create" command multiple times with a module-info.class. The module-info.class + * is internally updated by the jar tool to add additional data. Expects that each such generated + * jar has the exact same bytes. */ - private static void testJarCreate() throws Exception { + @Test + public void testJarCreate() throws Exception { final List jarFiles = new ArrayList<>(); for (int i = 0; i < 3; i++) { - final Path tmpFile = Files.createTempFile(Path.of("."), "8258117-jar-create", ".jar"); - jarFiles.add(tmpFile); - final ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( - CreateJar.class.getName(), - tmpFile.toString()); + final Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-create", ".jar"); + jarFiles.add(targetJar); if (i > 0) { // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed // down to SECONDS unit. So we make sure that there's at least a second @@ -94,7 +88,15 @@ private static void testJarCreate() throws Exception { // was indeed generated at "different times" Thread.sleep(1000); } - executeJavaProcess(processBuilder); + // create a modular jar + runJarCommand("--create", + "--file=" + targetJar, + "--main-class=" + MAIN_CLASS, + "--module-version=" + MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "."); + // verify the module descriptor in the jar + assertExpectedModuleInfo(targetJar, MODULE_VERSION); } assertAllFileContentsAreSame(jarFiles); } @@ -104,14 +106,12 @@ private static void testJarCreate() throws Exception { * descriptor with the same content and then expects that the modular jar created by * each of these processes has the exact same bytes. */ - private static void testJarUpdate() throws Exception { + @Test + public void testJarUpdate() throws Exception { final List jarFiles = new ArrayList<>(); for (int i = 0; i < 3; i++) { - final Path tmpFile = Files.createTempFile(Path.of("."), "8258117-jar-update", ".jar"); - jarFiles.add(tmpFile); - final ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( - UpdateJar.class.getName(), - tmpFile.toString()); + final Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-update", ".jar"); + jarFiles.add(targetJar); if (i > 0) { // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed // down to SECONDS unit. So we make sure that there's at least a second @@ -119,7 +119,21 @@ private static void testJarUpdate() throws Exception { // was indeed generated at "different times" Thread.sleep(1000); } - executeJavaProcess(processBuilder); + // first create the modular jar + runJarCommand("--create", + "--file=" + targetJar, + "--module-version=" + MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "."); + assertExpectedModuleInfo(targetJar, MODULE_VERSION); + // now update the same modular jar + runJarCommand("--update", + "--file=" + targetJar, + "--module-version=" + UPDATED_MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "module-info.class"); + // verify the module descriptor in the jar + assertExpectedModuleInfo(targetJar, UPDATED_MODULE_VERSION); } assertAllFileContentsAreSame(jarFiles); } @@ -167,17 +181,6 @@ private static void runJarCommand(String... jarArgs) throws Exception { } } - // launches the java process and waits for it to exit. throws an exception if exit value is non-zero - private static void executeJavaProcess(ProcessBuilder pb) throws Exception { - final OutputAnalyzer outputAnalyzer = ProcessTools.executeProcess(pb); - try { - outputAnalyzer.shouldHaveExitValue(0); - } finally { - // print out any stdout/err that was generated in the launched program - outputAnalyzer.reportDiagnosticSummary(); - } - } - // verifies the byte equality of the contents in each of the files private static void assertAllFileContentsAreSame(final List files) throws Exception { final byte[] file1Contents = Files.readAllBytes(files.get(0)); @@ -220,40 +223,5 @@ private static void assertExpectedModuleInfo(Path jar, String expectedModuleVers + moduleInfoEntry.getTime() + " for version " + actualVersion); } } - - static class CreateJar { - public static void main(final String[] args) throws Exception { - Path targetJar = Path.of(args[0]); - // create a modular jar - runJarCommand("--create", - "--file=" + targetJar, - "--main-class=" + MAIN_CLASS, - "--module-version=" + MODULE_VERSION, - "--no-manifest", - "-C", MODULE_CLASSES_DIR.toString(), "."); - // verify the module descriptor in the jar - assertExpectedModuleInfo(targetJar, MODULE_VERSION); - } - } - - static class UpdateJar { - public static void main(final String[] args) throws Exception { - Path targetJar = Path.of(args[0]); - // create the modular jar - runJarCommand("--create", - "--file=" + targetJar, - "--module-version=" + MODULE_VERSION, - "--no-manifest", - "-C", MODULE_CLASSES_DIR.toString(), "."); - assertExpectedModuleInfo(targetJar, MODULE_VERSION); - // update the same modular jar - runJarCommand("--update", - "--file=" + targetJar, - "--module-version=" + UPDATED_MODULE_VERSION, - "--no-manifest", - "-C", MODULE_CLASSES_DIR.toString(), "module-info.class"); - // verify the module descriptor in the jar - assertExpectedModuleInfo(targetJar, UPDATED_MODULE_VERSION); - } - } } + From 5c0d89e470a5025bb2b4ab997b7b6440ded0b7e2 Mon Sep 17 00:00:00 2001 From: jpai Date: Fri, 5 Nov 2021 18:59:08 +0530 Subject: [PATCH 03/12] Remove "final" usage from test --- ...rToolModuleDescriptorReproducibilityTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java index 093d5effc9d6e..c280116e132b9 100644 --- a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java +++ b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java @@ -77,9 +77,9 @@ public static void setup() throws Exception { */ @Test public void testJarCreate() throws Exception { - final List jarFiles = new ArrayList<>(); + List jarFiles = new ArrayList<>(); for (int i = 0; i < 3; i++) { - final Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-create", ".jar"); + Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-create", ".jar"); jarFiles.add(targetJar); if (i > 0) { // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed @@ -108,9 +108,9 @@ public void testJarCreate() throws Exception { */ @Test public void testJarUpdate() throws Exception { - final List jarFiles = new ArrayList<>(); + List jarFiles = new ArrayList<>(); for (int i = 0; i < 3; i++) { - final Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-update", ".jar"); + Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-update", ".jar"); jarFiles.add(targetJar); if (i > 0) { // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed @@ -145,7 +145,7 @@ private static void compileModuleClasses() throws Exception { List sourceFiles = new ArrayList<>(); Files.walkFileTree(sourcePath, new SimpleFileVisitor<>() { @Override - public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file.toString().endsWith(".java")) { sourceFiles.add(file.toString()); } @@ -182,10 +182,10 @@ private static void runJarCommand(String... jarArgs) throws Exception { } // verifies the byte equality of the contents in each of the files - private static void assertAllFileContentsAreSame(final List files) throws Exception { - final byte[] file1Contents = Files.readAllBytes(files.get(0)); + private static void assertAllFileContentsAreSame(List files) throws Exception { + byte[] file1Contents = Files.readAllBytes(files.get(0)); for (int i = 1; i < files.size(); i++) { - final byte[] otherFileContents = Files.readAllBytes(files.get(i)); + byte[] otherFileContents = Files.readAllBytes(files.get(i)); if (!Arrays.equals(file1Contents, otherFileContents)) { throw new RuntimeException("Content in file " + files.get(i) + " isn't the same as in file " + files.get(0)); From 2c0246f936a3f142f32f09cf2ee7932c062e7f21 Mon Sep 17 00:00:00 2001 From: jpai Date: Fri, 5 Nov 2021 19:07:05 +0530 Subject: [PATCH 04/12] use testng asserts --- ...olModuleDescriptorReproducibilityTest.java | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java index c280116e132b9..b0c8831b5c2bd 100644 --- a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java +++ b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java @@ -41,6 +41,10 @@ import java.util.jar.JarInputStream; import java.util.spi.ToolProvider; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; + /** * @test * @bug 8258117 @@ -161,35 +165,28 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO StringWriter sw = new StringWriter(); try (PrintWriter pw = new PrintWriter(sw)) { int exitCode = JAVAC_TOOL.run(pw, pw, javacArgs.toArray(new String[0])); - if (exitCode != 0) { - throw new RuntimeException("Module compilation failed: " + sw.toString()); - } + assertEquals(exitCode, 0, "Module compilation failed: " + sw.toString()); } System.out.println("Module classes successfully compiled to directory " + classesDir); } // runs the "jar" command passing it the "jarArgs" and verifying that the command // execution didn't fail - private static void runJarCommand(String... jarArgs) throws Exception { + private static void runJarCommand(String... jarArgs) { StringWriter sw = new StringWriter(); System.out.println("Launching jar command with args: " + Arrays.toString(jarArgs)); try (PrintWriter pw = new PrintWriter(sw)) { int exitCode = JAR_TOOL.run(pw, pw, jarArgs); - if (exitCode != 0) { - throw new RuntimeException("jar command execution failed: " + sw.toString()); - } + assertEquals(exitCode, 0, "jar command execution failed: " + sw.toString()); } } // verifies the byte equality of the contents in each of the files private static void assertAllFileContentsAreSame(List files) throws Exception { - byte[] file1Contents = Files.readAllBytes(files.get(0)); + Path firstFile = files.get(0); for (int i = 1; i < files.size(); i++) { - byte[] otherFileContents = Files.readAllBytes(files.get(i)); - if (!Arrays.equals(file1Contents, otherFileContents)) { - throw new RuntimeException("Content in file " + files.get(i) - + " isn't the same as in file " + files.get(0)); - } + assertEquals(Files.mismatch(firstFile, files.get(i)), -1, + "Content in file " + files.get(i) + " isn't the same as in file " + firstFile); } } @@ -205,20 +202,15 @@ private static void assertExpectedModuleInfo(Path jar, String expectedModuleVers break; } } - if (moduleInfoEntry == null) { - throw new RuntimeException("module-info.class is missing from jar " + jar); - } + assertNotNull(moduleInfoEntry, "module-info.class is missing from jar " + jar); + ModuleDescriptor md = ModuleDescriptor.read(jaris); - if (!md.name().equals(MODULE_NAME)) { - throw new RuntimeException("Unexpected module name " + md.name() + ", expected " + MODULE_NAME); - } - if (md.rawVersion().isEmpty()) { - throw new RuntimeException("Module version missing from descriptor"); - } + assertEquals(md.name(), MODULE_NAME, "Unexpected module name"); + assertFalse(md.rawVersion().isEmpty(), "Module version missing from descriptor"); + String actualVersion = md.rawVersion().get(); - if (!actualVersion.equals(expectedModuleVersion)) { - throw new RuntimeException("Unexpected module version " + actualVersion + ", expected " + expectedModuleVersion); - } + assertEquals(actualVersion, expectedModuleVersion, "Unexpected module version"); + System.out.println(moduleInfoEntry.getName() + " has a timestamp of " + moduleInfoEntry.getTime() + " for version " + actualVersion); } From 1196acc6904cb087c2f7b3828a4426e8a02c5798 Mon Sep 17 00:00:00 2001 From: jpai Date: Mon, 22 Nov 2021 14:09:41 +0530 Subject: [PATCH 05/12] review suggestion - store e.getValue() and reuse the stored value --- src/jdk.jartool/share/classes/sun/tools/jar/Main.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index fdeb9a4bdb6c8..3d2196584ef1f 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -2101,11 +2101,12 @@ private void addExtendedModuleAttributes(Map moduleInfo throws IOException { for (Map.Entry e: moduleInfos.entrySet()) { - byte[] bytes = e.getValue().readAllBytes(); + ModuleInfoEntry mie = e.getValue(); + byte[] bytes = mie.readAllBytes(); ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(bytes)); byte[] extended = extendedInfoBytes(md, bytes, packages); // replace the entry value with the extended bytes - e.setValue(new StreamedModuleInfoEntry(e.getValue().name(), extended, e.getValue().getLastModifiedTime())); + e.setValue(new StreamedModuleInfoEntry(mie.name(), extended, mie.getLastModifiedTime())); } } From 4f6b6dfebc899a7d38b6e2b85e830061e5796f98 Mon Sep 17 00:00:00 2001 From: jpai Date: Mon, 22 Nov 2021 14:11:36 +0530 Subject: [PATCH 06/12] review suggestion - Use Path.of instead of Paths.get in testcase --- .../JarToolModuleDescriptorReproducibilityTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java index b0c8831b5c2bd..376b261799854 100644 --- a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java +++ b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java @@ -31,7 +31,6 @@ import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; @@ -57,7 +56,7 @@ public class JarToolModuleDescriptorReproducibilityTest { private static final String MODULE_VERSION = "1.2.3"; private static final String UPDATED_MODULE_VERSION = "1.2.4"; private static final String MAIN_CLASS = "jdk.test.foo.Foo"; - private static final Path MODULE_CLASSES_DIR = Paths.get("8258117-module-classes", MODULE_NAME).toAbsolutePath(); + private static final Path MODULE_CLASSES_DIR = Path.of("8258117-module-classes", MODULE_NAME).toAbsolutePath(); private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") .orElseThrow(() @@ -144,7 +143,7 @@ public void testJarUpdate() throws Exception { // compiles using javac tool the classes used in the test module private static void compileModuleClasses() throws Exception { - Path sourcePath = Paths.get(System.getProperty("test.src", "."), + Path sourcePath = Path.of(System.getProperty("test.src", "."), "src", MODULE_NAME); List sourceFiles = new ArrayList<>(); Files.walkFileTree(sourcePath, new SimpleFileVisitor<>() { From 6fb7f95ee2a5359fa31d2bb37f49a06d3c05031c Mon Sep 17 00:00:00 2001 From: jpai Date: Mon, 22 Nov 2021 14:16:00 +0530 Subject: [PATCH 07/12] review suggestion - split into multiple statements to make it easily readable --- src/jdk.jartool/share/classes/sun/tools/jar/Main.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 3d2196584ef1f..7e33ed5ccb95a 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -802,8 +802,9 @@ private void expand(File dir, String[] files, Set cpaths, int version) Entry e = new Entry(f, name, false); if (isModuleInfoEntry(name)) { Long lastModified = f.lastModified() == 0 ? null : f.lastModified(); - moduleInfos.putIfAbsent(name, - new StreamedModuleInfoEntry(name, Files.readAllBytes(f.toPath()), lastModified)); + byte[] fileContent = Files.readAllBytes(f.toPath()); + ModuleInfoEntry mie = new StreamedModuleInfoEntry(name, fileContent, lastModified); + moduleInfos.putIfAbsent(name, mie); if (uflag) entryMap.put(name, e); } else if (entries.add(e)) { From 8ff7583575a1167decd7bade9f167a8c55309352 Mon Sep 17 00:00:00 2001 From: jpai Date: Mon, 22 Nov 2021 14:22:50 +0530 Subject: [PATCH 08/12] review suggestion - use FileTime instead of Long to prevent any potential NPEs during unboxing --- .../share/classes/sun/tools/jar/Main.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 7e33ed5ccb95a..189357ff8f42c 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -44,6 +44,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.FileTime; import java.text.MessageFormat; import java.util.*; import java.util.function.Consumer; @@ -801,7 +802,7 @@ private void expand(File dir, String[] files, Set cpaths, int version) if (f.isFile()) { Entry e = new Entry(f, name, false); if (isModuleInfoEntry(name)) { - Long lastModified = f.lastModified() == 0 ? null : f.lastModified(); + FileTime lastModified = f.lastModified() == 0 ? null : FileTime.fromMillis(f.lastModified()); byte[] fileContent = Files.readAllBytes(f.toPath()); ModuleInfoEntry mie = new StreamedModuleInfoEntry(name, fileContent, lastModified); moduleInfos.putIfAbsent(name, mie); @@ -947,7 +948,7 @@ boolean update(InputStream in, OutputStream out, return false; } } else if (moduleInfos != null && isModuleInfoEntry) { - Long lastModified = e.getTime() == -1 ? null : e.getTime(); + FileTime lastModified = e.getTime() == -1 ? null : FileTime.fromMillis(e.getTime()); moduleInfos.putIfAbsent(name, new StreamedModuleInfoEntry(name, zis.readAllBytes(), lastModified)); } else { boolean isDir = e.isDirectory(); @@ -1040,8 +1041,8 @@ private void updateModuleInfo(Map moduleInfos, ZipOutpu String name = mi.getKey(); byte[] bytes = mi.getValue().readAllBytes(); ZipEntry e = new ZipEntry(name); - Long lastModified = mi.getValue().getLastModifiedTime(); - e.setTime(lastModified == null ? System.currentTimeMillis() : lastModified); + FileTime lastModified = mi.getValue().getLastModifiedTime(); + e.setTime(lastModified == null ? System.currentTimeMillis() : lastModified.toMillis()); if (flag0) { crc32ModuleInfo(e, bytes); } @@ -1747,7 +1748,7 @@ interface ModuleInfoEntry { * Returns null if the last modified time is unknown or cannot be * determined. */ - Long getLastModifiedTime(); + FileTime getLastModifiedTime(); default byte[] readAllBytes() throws IOException { try (InputStream is = bytes()) { return is.readAllBytes(); @@ -1768,8 +1769,8 @@ static class ZipFileModuleInfoEntry implements ModuleInfoEntry { } @Override - public Long getLastModifiedTime() { - return entry.getTime() == -1 ? null : entry.getTime(); + public FileTime getLastModifiedTime() { + return entry.getTime() == -1 ? null : FileTime.fromMillis(entry.getTime()); } /** Returns an optional containing the effective URI. */ @@ -1783,9 +1784,9 @@ public Long getLastModifiedTime() { static class StreamedModuleInfoEntry implements ModuleInfoEntry { private final String name; private final byte[] bytes; - private final Long lastModifiedTime; + private final FileTime lastModifiedTime; - StreamedModuleInfoEntry(String name, byte[] bytes, Long lastModifiedTime) { + StreamedModuleInfoEntry(String name, byte[] bytes, FileTime lastModifiedTime) { this.name = name; this.bytes = bytes; this.lastModifiedTime = lastModifiedTime; @@ -1801,7 +1802,7 @@ public byte[] readAllBytes() throws IOException { } @Override - public Long getLastModifiedTime() { + public FileTime getLastModifiedTime() { return lastModifiedTime; } @@ -1856,7 +1857,7 @@ private boolean describeModuleFromStream(FileInputStream fis) while ((e = zis.getNextEntry()) != null) { String ename = e.getName(); if (isModuleInfoEntry(ename)) { - Long lastModified = e.getTime() == -1 ? null : e.getTime(); + FileTime lastModified = e.getTime() == -1 ? null : FileTime.fromMillis(e.getTime()); infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes(), lastModified)); } } From 9ab5ad8a1af3f87744fa3f0d1c9def3ed7e0640d Mon Sep 17 00:00:00 2001 From: jpai Date: Mon, 22 Nov 2021 20:01:52 +0530 Subject: [PATCH 09/12] use proper FileTime centric APIs --- .../share/classes/sun/tools/jar/Main.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 189357ff8f42c..9378b7cf22762 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -802,9 +802,9 @@ private void expand(File dir, String[] files, Set cpaths, int version) if (f.isFile()) { Entry e = new Entry(f, name, false); if (isModuleInfoEntry(name)) { - FileTime lastModified = f.lastModified() == 0 ? null : FileTime.fromMillis(f.lastModified()); - byte[] fileContent = Files.readAllBytes(f.toPath()); - ModuleInfoEntry mie = new StreamedModuleInfoEntry(name, fileContent, lastModified); + Path path = f.toPath(); + byte[] fileContent = Files.readAllBytes(path); + ModuleInfoEntry mie = new StreamedModuleInfoEntry(name, fileContent, Files.getLastModifiedTime(path)); moduleInfos.putIfAbsent(name, mie); if (uflag) entryMap.put(name, e); @@ -948,8 +948,7 @@ boolean update(InputStream in, OutputStream out, return false; } } else if (moduleInfos != null && isModuleInfoEntry) { - FileTime lastModified = e.getTime() == -1 ? null : FileTime.fromMillis(e.getTime()); - moduleInfos.putIfAbsent(name, new StreamedModuleInfoEntry(name, zis.readAllBytes(), lastModified)); + moduleInfos.putIfAbsent(name, new StreamedModuleInfoEntry(name, zis.readAllBytes(), e.getLastModifiedTime())); } else { boolean isDir = e.isDirectory(); if (!entryMap.containsKey(name)) { // copy the old stuff @@ -1039,10 +1038,11 @@ private void updateModuleInfo(Map moduleInfos, ZipOutpu String fmt = uflag ? "out.update.module-info": "out.added.module-info"; for (Map.Entry mi : moduleInfos.entrySet()) { String name = mi.getKey(); - byte[] bytes = mi.getValue().readAllBytes(); + ModuleInfoEntry mie = mi.getValue(); + byte[] bytes = mie.readAllBytes(); ZipEntry e = new ZipEntry(name); - FileTime lastModified = mi.getValue().getLastModifiedTime(); - e.setTime(lastModified == null ? System.currentTimeMillis() : lastModified.toMillis()); + FileTime lastModified = mie.getLastModifiedTime(); + e.setLastModifiedTime(lastModified != null ? lastModified : FileTime.fromMillis(System.currentTimeMillis())); if (flag0) { crc32ModuleInfo(e, bytes); } @@ -1770,7 +1770,7 @@ static class ZipFileModuleInfoEntry implements ModuleInfoEntry { @Override public FileTime getLastModifiedTime() { - return entry.getTime() == -1 ? null : FileTime.fromMillis(entry.getTime()); + return entry.getLastModifiedTime(); } /** Returns an optional containing the effective URI. */ @@ -1857,8 +1857,7 @@ private boolean describeModuleFromStream(FileInputStream fis) while ((e = zis.getNextEntry()) != null) { String ename = e.getName(); if (isModuleInfoEntry(ename)) { - FileTime lastModified = e.getTime() == -1 ? null : FileTime.fromMillis(e.getTime()); - infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes(), lastModified)); + infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes(), e.getLastModifiedTime())); } } } From e1893382fd35eca3309b250101eaf3942dba48a5 Mon Sep 17 00:00:00 2001 From: jpai Date: Wed, 24 Nov 2021 20:08:36 +0530 Subject: [PATCH 10/12] review suggestion - use if/else instead of ternary operator --- src/jdk.jartool/share/classes/sun/tools/jar/Main.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index f095b885cb948..6144bc0ac46c6 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -1054,7 +1054,11 @@ private void updateModuleInfo(Map moduleInfos, ZipOutpu byte[] bytes = mie.readAllBytes(); ZipEntry e = new ZipEntry(name); FileTime lastModified = mie.getLastModifiedTime(); - e.setLastModifiedTime(lastModified != null ? lastModified : FileTime.fromMillis(System.currentTimeMillis())); + if (lastModified != null) { + e.setLastModifiedTime(lastModified); + } else { + e.setLastModifiedTime(FileTime.fromMillis(System.currentTimeMillis())); + } if (flag0) { crc32ModuleInfo(e, bytes); } From e668ef767f4eeb7a5cb010d37bb1fdee102fc155 Mon Sep 17 00:00:00 2001 From: jpai Date: Wed, 24 Nov 2021 20:11:13 +0530 Subject: [PATCH 11/12] review suggestion - change javadoc to mention module-info.class instead of module descriptor --- src/jdk.jartool/share/classes/sun/tools/jar/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 6144bc0ac46c6..076cfb898b2c3 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -1760,7 +1760,7 @@ interface ModuleInfoEntry { Optional uriString(); InputStream bytes() throws IOException; /** - * @return Returns the last modified time of the module descriptor. + * @return Returns the last modified time of the module-info.class. * Returns null if the last modified time is unknown or cannot be * determined. */ From bdc741caf83225102e1fcceb3f440b84c9535108 Mon Sep 17 00:00:00 2001 From: jpai Date: Wed, 24 Nov 2021 20:14:16 +0530 Subject: [PATCH 12/12] review suggestion - format code in ModuleInfoEntry to use 4 space indentation --- .../share/classes/sun/tools/jar/Main.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 076cfb898b2c3..4e6df7e39bf40 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -1756,20 +1756,20 @@ private File createTemporaryFile(String tmpbase, String suffix) { * bytes and an optional URI. */ interface ModuleInfoEntry { - String name(); - Optional uriString(); - InputStream bytes() throws IOException; + String name(); + Optional uriString(); + InputStream bytes() throws IOException; /** * @return Returns the last modified time of the module-info.class. * Returns null if the last modified time is unknown or cannot be * determined. */ - FileTime getLastModifiedTime(); - default byte[] readAllBytes() throws IOException { + FileTime getLastModifiedTime(); + default byte[] readAllBytes() throws IOException { try (InputStream is = bytes()) { return is.readAllBytes(); } - } + } } static class ZipFileModuleInfoEntry implements ModuleInfoEntry {