Skip to content

Commit 2176afb

Browse files
committed
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 spring-projectsgh-2807
1 parent a664499 commit 2176afb

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2013 the original author or authors.
2+
* Copyright 2012-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -162,13 +162,36 @@ public void writeNestedLibrary(String destination, Library library)
162162
throws IOException {
163163
File file = library.getFile();
164164
JarEntry entry = new JarEntry(destination + library.getName());
165+
entry.setTime(getNestedLibraryTime(file));
165166
if (library.isUnpackRequired()) {
166167
entry.setComment("UNPACK:" + FileUtils.sha1Hash(file));
167168
}
168169
new CrcAndSize(file).setupStoredEntry(entry);
169170
writeEntry(entry, new InputStreamEntryWriter(new FileInputStream(file), true));
170171
}
171172

173+
private long getNestedLibraryTime(File file) {
174+
try {
175+
JarFile jarFile = new JarFile(file);
176+
try {
177+
Enumeration<JarEntry> entries = jarFile.entries();
178+
while (entries.hasMoreElements()) {
179+
JarEntry entry = entries.nextElement();
180+
if (!entry.isDirectory()) {
181+
return entry.getTime();
182+
}
183+
}
184+
}
185+
finally {
186+
jarFile.close();
187+
}
188+
}
189+
catch (Exception ex) {
190+
// Ignore and just use the source file timestamp
191+
}
192+
return file.lastModified();
193+
}
194+
172195
/**
173196
* Write the required spring-boot-loader classes to the JAR.
174197
* @throws IOException

spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.IOException;
2121
import java.nio.file.Files;
2222
import java.nio.file.attribute.PosixFilePermission;
23+
import java.util.Calendar;
2324
import java.util.jar.Attributes;
2425
import java.util.jar.JarEntry;
2526
import java.util.jar.JarFile;
@@ -58,6 +59,17 @@ public void doWithLibraries(LibraryCallback callback) throws IOException {
5859
}
5960
};
6061

62+
private static final long JAN_1_1980;
63+
private static final long JAN_1_1985;
64+
static {
65+
Calendar calendar = Calendar.getInstance();
66+
calendar.set(1980, 0, 1, 0, 0, 0);
67+
calendar.set(Calendar.MILLISECOND, 0);
68+
JAN_1_1980 = calendar.getTime().getTime();
69+
calendar.set(Calendar.YEAR, 1985);
70+
JAN_1_1985 = calendar.getTime().getTime();
71+
}
72+
6173
@Rule
6274
public TemporaryFolder temporaryFolder = new TemporaryFolder();
6375

@@ -275,7 +287,7 @@ public void nullLibraries() throws Exception {
275287
@Test
276288
public void libraries() throws Exception {
277289
TestJarFile libJar = new TestJarFile(this.temporaryFolder);
278-
libJar.addClass("a/b/C.class", ClassWithoutMainMethod.class);
290+
libJar.addClass("a/b/C.class", ClassWithoutMainMethod.class, JAN_1_1985);
279291
final File libJarFile = libJar.getFile();
280292
final File libJarFileToUnpack = libJar.getFile();
281293
final File libNonJarFile = this.temporaryFolder.newFile();
@@ -284,6 +296,7 @@ public void libraries() throws Exception {
284296
this.testJarFile.addFile("lib/" + libJarFileToUnpack.getName(),
285297
libJarFileToUnpack);
286298
File file = this.testJarFile.getFile();
299+
libJarFile.setLastModified(JAN_1_1980);
287300
Repackager repackager = new Repackager(file);
288301
repackager.repackage(new Libraries() {
289302
@Override
@@ -297,7 +310,9 @@ public void doWithLibraries(LibraryCallback callback) throws IOException {
297310
assertThat(hasEntry(file, "lib/" + libJarFile.getName()), equalTo(true));
298311
assertThat(hasEntry(file, "lib/" + libJarFileToUnpack.getName()), equalTo(true));
299312
assertThat(hasEntry(file, "lib/" + libNonJarFile.getName()), equalTo(false));
300-
JarEntry entry = getEntry(file, "lib/" + libJarFileToUnpack.getName());
313+
JarEntry entry = getEntry(file, "lib/" + libJarFile.getName());
314+
assertThat(entry.getTime(), equalTo(JAN_1_1985));
315+
entry = getEntry(file, "lib/" + libJarFileToUnpack.getName());
301316
assertThat(entry.getComment(), startsWith("UNPACK:"));
302317
assertThat(entry.getComment().length(), equalTo(47));
303318
}

spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/TestJarFile.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2013 the original author or authors.
2+
* Copyright 2012-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,11 +46,19 @@ public TestJarFile(TemporaryFolder temporaryFolder) throws IOException {
4646
}
4747

4848
public void addClass(String filename, Class<?> classToCopy) throws IOException {
49+
addClass(filename, classToCopy, null);
50+
}
51+
52+
public void addClass(String filename, Class<?> classToCopy, Long time)
53+
throws IOException {
4954
File file = getFilePath(filename);
5055
file.getParentFile().mkdirs();
5156
InputStream inputStream = getClass().getResourceAsStream(
5257
"/" + classToCopy.getName().replace(".", "/") + ".class");
5358
copyToFile(inputStream, file);
59+
if (time != null) {
60+
file.setLastModified(time);
61+
}
5462
}
5563

5664
public void addFile(String filename, File fileToCopy) throws IOException {

0 commit comments

Comments
 (0)