ADDITIONAL SYSTEM INFORMATION : Macbook Pro 15-inch 2016 Mac OS X High Sierra 10.13.5 OpenJDK jdk-10.0.1.jdk & jdk-11.jdk A DESCRIPTION OF THE PROBLEM : I have observed that when building a Jar using JarOutputStream, when adding a JarEntry if I call JarEntry.setLastModifiedTime prior to calling setTime, the Jar is built incorrectly and reading it using java.util.jar.JarFile will throw an exception. java.util.zip.ZipException: invalid CEN header (bad header size) Expected Results: Regardless of order, or calls to these methods should produce a valid Jar file or exception while writing the Jar file to indicate the error. REGRESSION : Last worked in version 8u171 STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : 1. Open a JarOutputStream 2. Create a single JarEntry 3. setCreationTime 4. setLastAccessTime 5. setLastModifiedTime 6. setTime 7. JarOutputStream.putNextEntry EXPECTED VERSUS ACTUAL BEHAVIOR : EXPECTED - Expect the Jar to be built correctly or for the JarOutputStream to fail if it is unable to be built correctly. This previously worked on Oracle JRE jdk1.8.0_171 and earlier. ACTUAL - The JarOutputStream does not fail, but the jar is not built correctly, and then reading the jar using java.util.jar.JarFile will throw java.util.zip.ZipException: invalid CEN header (bad header size). I can recreate these results on Open JDK 10.0.1 and 11. ---------- BEGIN SOURCE ---------- import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; import java.util.function.Consumer; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.ZipException; /** * @author Daniel DeGroff */ public class JarOutputStreamTest { public static void main(String[] args) throws IOException { // Create a test file to include in the jar. Files.deleteIfExists(Paths.get("./foo")); Path file = Files.createFile(Paths.get("./foo")); FileTime creationTime = (FileTime) Files.getAttribute(file, "creationTime"); FileTime lastAccessTime = (FileTime) Files.getAttribute(file, "lastAccessTime"); FileTime lastModifiedTime = Files.getLastModifiedTime(file); File jarFile = new File("testcase"); jarFile.deleteOnExit(); // 1. Passes, order is Ok. runTestCase(jarFile, file, entry -> { entry.setCreationTime(creationTime); entry.setLastAccessTime(lastAccessTime); // Calling setTime prior to setLastModifiedTime is Ok. entry.setTime(lastModifiedTime.toMillis()); entry.setLastModifiedTime(lastModifiedTime); }); // 2. Passes, omit the call to setTime runTestCase(jarFile, file, entry -> { entry.setCreationTime(creationTime); entry.setLastAccessTime(lastAccessTime); // Omitting the call to setTime is Ok. entry.setLastModifiedTime(lastModifiedTime); }); // 3. Passes, omit setCreationTime and setLastAccessTime then order does not matter runTestCase(jarFile, file, entry -> { // Calling these two in either order is ok when we don't call setCreationTime and setLastAccessTime entry.setTime(lastModifiedTime.toMillis()); entry.setLastModifiedTime(lastModifiedTime); }); // 4. Passes, omit setCreationTime and setLastAccessTime then order does not matter runTestCase(jarFile, file, entry -> { // Calling these two in either order is ok when we don't call setCreationTime and setLastAccessTime entry.setLastModifiedTime(lastModifiedTime); entry.setTime(lastModifiedTime.toMillis()); }); // 5. Fails runTestCase(jarFile, file, entry -> { entry.setCreationTime(creationTime); entry.setLastAccessTime(lastAccessTime); // Calling setLastModifiedTime prior to setTime when also calling setCreationTime and setLastAccessTime fails. entry.setLastModifiedTime(lastModifiedTime); entry.setTime(lastModifiedTime.toMillis()); }); } private static void runTestCase(File jarFile, Path file, Consumer<JarEntry> consumer) throws IOException { try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(jarFile.toPath()), new Manifest())) { JarEntry entry = new JarEntry(file.toString()); consumer.accept(entry); entry.setSize((Long) Files.getAttribute(file, "size")); jos.putNextEntry(entry); jos.flush(); jos.closeEntry(); } try { new JarFile(jarFile); System.out.println("Success!"); } catch (ZipException e) { // Throws java.util.zip.ZipException: invalid CEN header (bad header size) System.out.println("Fail. " + e.getClass().getCanonicalName() + ": " + e.getMessage()); } } } ---------- END SOURCE ---------- CUSTOMER SUBMITTED WORKAROUND : Workaround 1: Call JarEntry.setTime before JarEntry.setLastModifiedTime Workaround 2: Leave setLastModifiedTime before setTime but omit setCreationTime and setLastAccessTime Workaround 3: Do not call setTime, instead only call setCreationTime, setLastAccessTime, and setLastModifiedTime FREQUENCY : always
|