Skip to content

Commit b7a2a2d

Browse files
committed
Set last access and last modified times in layertools extract
Closes gh-28167
1 parent 6d3e173 commit b7a2a2d

File tree

2 files changed

+64
-16
lines changed
  • spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src

2 files changed

+64
-16
lines changed

spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/main/java/org/springframework/boot/jarmode/layertools/ExtractCommand.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.IOException;
2323
import java.io.OutputStream;
2424
import java.nio.file.Files;
25+
import java.nio.file.attribute.BasicFileAttributeView;
2526
import java.util.List;
2627
import java.util.Map;
2728
import java.util.zip.ZipEntry;
@@ -96,7 +97,13 @@ private void write(ZipInputStream zip, ZipEntry entry, File destination) throws
9697
try (OutputStream out = new FileOutputStream(file)) {
9798
StreamUtils.copy(zip, out);
9899
}
99-
Files.setAttribute(file.toPath(), "creationTime", entry.getCreationTime());
100+
try {
101+
Files.getFileAttributeView(file.toPath(), BasicFileAttributeView.class)
102+
.setTimes(entry.getLastModifiedTime(), entry.getLastAccessTime(), entry.getCreationTime());
103+
}
104+
catch (IOException ex) {
105+
// File system does not support setting time attributes. Continue.
106+
}
100107
}
101108

102109
private void mkParentDirs(File file) throws IOException {

spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,17 @@
2020
import java.io.FileOutputStream;
2121
import java.io.FileWriter;
2222
import java.io.IOException;
23+
import java.nio.file.Files;
24+
import java.nio.file.LinkOption;
25+
import java.nio.file.attribute.BasicFileAttributeView;
26+
import java.nio.file.attribute.BasicFileAttributes;
27+
import java.nio.file.attribute.FileTime;
28+
import java.time.Instant;
29+
import java.time.temporal.ChronoUnit;
2330
import java.util.Arrays;
2431
import java.util.Collections;
2532
import java.util.Iterator;
33+
import java.util.concurrent.TimeUnit;
2634
import java.util.function.Consumer;
2735
import java.util.zip.ZipEntry;
2836
import java.util.zip.ZipOutputStream;
@@ -47,6 +55,12 @@
4755
@ExtendWith(MockitoExtension.class)
4856
class ExtractCommandTests {
4957

58+
private static final FileTime CREATION_TIME = FileTime.from(Instant.now().minus(3, ChronoUnit.DAYS));
59+
60+
private static final FileTime LAST_MODIFIED_TIME = FileTime.from(Instant.now().minus(2, ChronoUnit.DAYS));
61+
62+
private static final FileTime LAST_ACCESS_TIME = FileTime.from(Instant.now().minus(1, ChronoUnit.DAYS));
63+
5064
@TempDir
5165
File temp;
5266

@@ -75,23 +89,42 @@ void runExtractsLayers() throws Exception {
7589
given(this.context.getWorkingDir()).willReturn(this.extract);
7690
this.command.run(Collections.emptyMap(), Collections.emptyList());
7791
assertThat(this.extract.list()).containsOnly("a", "b", "c", "d");
78-
assertThat(new File(this.extract, "a/a/a.jar")).exists();
79-
assertThat(new File(this.extract, "b/b/b.jar")).exists();
80-
assertThat(new File(this.extract, "c/c/c.jar")).exists();
92+
assertThat(new File(this.extract, "a/a/a.jar")).exists().satisfies(this::timeAttributes);
93+
assertThat(new File(this.extract, "b/b/b.jar")).exists().satisfies(this::timeAttributes);
94+
assertThat(new File(this.extract, "c/c/c.jar")).exists().satisfies(this::timeAttributes);
8195
assertThat(new File(this.extract, "d")).isDirectory();
8296
assertThat(new File(this.extract.getParentFile(), "e.jar")).doesNotExist();
8397
}
8498

99+
private void timeAttributes(File file) {
100+
try {
101+
BasicFileAttributes basicAttributes = Files
102+
.getFileAttributeView(file.toPath(), BasicFileAttributeView.class, new LinkOption[0])
103+
.readAttributes();
104+
assertThat(basicAttributes.lastModifiedTime().to(TimeUnit.SECONDS))
105+
.isEqualTo(LAST_MODIFIED_TIME.to(TimeUnit.SECONDS));
106+
assertThat(basicAttributes.creationTime().to(TimeUnit.SECONDS)).satisfiesAnyOf(
107+
(creationTime) -> assertThat(creationTime).isEqualTo(CREATION_TIME.to(TimeUnit.SECONDS)),
108+
// On macOS (at least) the creation time is the last modified time
109+
(creationTime) -> assertThat(creationTime).isEqualTo(LAST_MODIFIED_TIME.to(TimeUnit.SECONDS)));
110+
assertThat(basicAttributes.lastAccessTime().to(TimeUnit.SECONDS))
111+
.isEqualTo(LAST_ACCESS_TIME.to(TimeUnit.SECONDS));
112+
}
113+
catch (IOException ex) {
114+
throw new RuntimeException(ex);
115+
}
116+
}
117+
85118
@Test
86119
void runWhenHasDestinationOptionExtractsLayers() {
87120
given(this.context.getJarFile()).willReturn(this.jarFile);
88121
File out = new File(this.extract, "out");
89122
this.command.run(Collections.singletonMap(ExtractCommand.DESTINATION_OPTION, out.getAbsolutePath()),
90123
Collections.emptyList());
91124
assertThat(this.extract.list()).containsOnly("out");
92-
assertThat(new File(this.extract, "out/a/a/a.jar")).exists();
93-
assertThat(new File(this.extract, "out/b/b/b.jar")).exists();
94-
assertThat(new File(this.extract, "out/c/c/c.jar")).exists();
125+
assertThat(new File(this.extract, "out/a/a/a.jar")).exists().satisfies(this::timeAttributes);
126+
assertThat(new File(this.extract, "out/b/b/b.jar")).exists().satisfies(this::timeAttributes);
127+
assertThat(new File(this.extract, "out/c/c/c.jar")).exists().satisfies(this::timeAttributes);
95128
}
96129

97130
@Test
@@ -100,8 +133,8 @@ void runWhenHasLayerParamsExtractsLimitedLayers() {
100133
given(this.context.getWorkingDir()).willReturn(this.extract);
101134
this.command.run(Collections.emptyMap(), Arrays.asList("a", "c"));
102135
assertThat(this.extract.list()).containsOnly("a", "c");
103-
assertThat(new File(this.extract, "a/a/a.jar")).exists();
104-
assertThat(new File(this.extract, "c/c/c.jar")).exists();
136+
assertThat(new File(this.extract, "a/a/a.jar")).exists().satisfies(this::timeAttributes);
137+
assertThat(new File(this.extract, "c/c/c.jar")).exists().satisfies(this::timeAttributes);
105138
assertThat(new File(this.extract.getParentFile(), "e.jar")).doesNotExist();
106139
}
107140

@@ -143,25 +176,33 @@ private File createJarFile(String name) throws IOException {
143176
private File createJarFile(String name, Consumer<ZipOutputStream> streamHandler) throws IOException {
144177
File file = new File(this.temp, name);
145178
try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file))) {
146-
out.putNextEntry(new ZipEntry("a/"));
179+
out.putNextEntry(entry("a/"));
147180
out.closeEntry();
148-
out.putNextEntry(new ZipEntry("a/a.jar"));
181+
out.putNextEntry(entry("a/a.jar"));
149182
out.closeEntry();
150-
out.putNextEntry(new ZipEntry("b/"));
183+
out.putNextEntry(entry("b/"));
151184
out.closeEntry();
152-
out.putNextEntry(new ZipEntry("b/b.jar"));
185+
out.putNextEntry(entry("b/b.jar"));
153186
out.closeEntry();
154-
out.putNextEntry(new ZipEntry("c/"));
187+
out.putNextEntry(entry("c/"));
155188
out.closeEntry();
156-
out.putNextEntry(new ZipEntry("c/c.jar"));
189+
out.putNextEntry(entry("c/c.jar"));
157190
out.closeEntry();
158-
out.putNextEntry(new ZipEntry("d/"));
191+
out.putNextEntry(entry("d/"));
159192
out.closeEntry();
160193
streamHandler.accept(out);
161194
}
162195
return file;
163196
}
164197

198+
private ZipEntry entry(String path) {
199+
ZipEntry entry = new ZipEntry(path);
200+
entry.setCreationTime(CREATION_TIME);
201+
entry.setLastModifiedTime(LAST_MODIFIED_TIME);
202+
entry.setLastAccessTime(LAST_ACCESS_TIME);
203+
return entry;
204+
}
205+
165206
private static class TestLayers implements Layers {
166207

167208
@Override

0 commit comments

Comments
 (0)