From 2176afb49e04e4c1b636fa168208da1b02209e7f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 16 Apr 2015 21:16:30 -0700 Subject: [PATCH] Set timestamps of nested jars Update JarWriter to set the entry timestamp for nested jars. The timestamp is set to same time as the first (non directory) entry in the nested jar. This change should make it easier to created layered docker images. Fixes gh-2807 --- .../boot/loader/tools/JarWriter.java | 25 ++++++++++++++++++- .../boot/loader/tools/RepackagerTests.java | 19 ++++++++++++-- .../boot/loader/tools/TestJarFile.java | 10 +++++++- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java index f2c365ac6e..aa51cfbb00 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -162,6 +162,7 @@ public class JarWriter { throws IOException { File file = library.getFile(); JarEntry entry = new JarEntry(destination + library.getName()); + entry.setTime(getNestedLibraryTime(file)); if (library.isUnpackRequired()) { entry.setComment("UNPACK:" + FileUtils.sha1Hash(file)); } @@ -169,6 +170,28 @@ public class JarWriter { writeEntry(entry, new InputStreamEntryWriter(new FileInputStream(file), true)); } + private long getNestedLibraryTime(File file) { + try { + JarFile jarFile = new JarFile(file); + try { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + return entry.getTime(); + } + } + } + finally { + jarFile.close(); + } + } + catch (Exception ex) { + // Ignore and just use the source file timestamp + } + return file.lastModified(); + } + /** * Write the required spring-boot-loader classes to the JAR. * @throws IOException diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java index d7394d8c6e..019d74b8be 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.attribute.PosixFilePermission; +import java.util.Calendar; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -58,6 +59,17 @@ public class RepackagerTests { } }; + private static final long JAN_1_1980; + private static final long JAN_1_1985; + static { + Calendar calendar = Calendar.getInstance(); + calendar.set(1980, 0, 1, 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + JAN_1_1980 = calendar.getTime().getTime(); + calendar.set(Calendar.YEAR, 1985); + JAN_1_1985 = calendar.getTime().getTime(); + } + @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -275,7 +287,7 @@ public class RepackagerTests { @Test public void libraries() throws Exception { TestJarFile libJar = new TestJarFile(this.temporaryFolder); - libJar.addClass("a/b/C.class", ClassWithoutMainMethod.class); + libJar.addClass("a/b/C.class", ClassWithoutMainMethod.class, JAN_1_1985); final File libJarFile = libJar.getFile(); final File libJarFileToUnpack = libJar.getFile(); final File libNonJarFile = this.temporaryFolder.newFile(); @@ -284,6 +296,7 @@ public class RepackagerTests { this.testJarFile.addFile("lib/" + libJarFileToUnpack.getName(), libJarFileToUnpack); File file = this.testJarFile.getFile(); + libJarFile.setLastModified(JAN_1_1980); Repackager repackager = new Repackager(file); repackager.repackage(new Libraries() { @Override @@ -297,7 +310,9 @@ public class RepackagerTests { assertThat(hasEntry(file, "lib/" + libJarFile.getName()), equalTo(true)); assertThat(hasEntry(file, "lib/" + libJarFileToUnpack.getName()), equalTo(true)); assertThat(hasEntry(file, "lib/" + libNonJarFile.getName()), equalTo(false)); - JarEntry entry = getEntry(file, "lib/" + libJarFileToUnpack.getName()); + JarEntry entry = getEntry(file, "lib/" + libJarFile.getName()); + assertThat(entry.getTime(), equalTo(JAN_1_1985)); + entry = getEntry(file, "lib/" + libJarFileToUnpack.getName()); assertThat(entry.getComment(), startsWith("UNPACK:")); assertThat(entry.getComment().length(), equalTo(47)); } diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/TestJarFile.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/TestJarFile.java index a20671d726..00cdf10329 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/TestJarFile.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/TestJarFile.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,11 +46,19 @@ public class TestJarFile { } public void addClass(String filename, Class classToCopy) throws IOException { + addClass(filename, classToCopy, null); + } + + public void addClass(String filename, Class classToCopy, Long time) + throws IOException { File file = getFilePath(filename); file.getParentFile().mkdirs(); InputStream inputStream = getClass().getResourceAsStream( "/" + classToCopy.getName().replace(".", "/") + ".class"); copyToFile(inputStream, file); + if (time != null) { + file.setLastModified(time); + } } public void addFile(String filename, File fileToCopy) throws IOException {