Keep track of written jar entries to not duplicate them

Closes gh-40903
This commit is contained in:
Moritz Halbritter 2024-05-27 12:01:57 +02:00
parent 82d8222f56
commit 9def6f86c9
3 changed files with 40 additions and 6 deletions

View File

@ -29,6 +29,7 @@ import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileTime;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -251,13 +252,22 @@ class ExtractCommand extends Command {
mkdirs(file.getParentFile());
try (JarOutputStream output = new JarOutputStream(new FileOutputStream(file), manifest)) {
EnumSet<Type> allowedTypes = EnumSet.of(Type.APPLICATION_CLASS_OR_RESOURCE, Type.META_INF);
Set<String> writtenEntries = new HashSet<>();
withJarEntries(this.context.getArchiveFile(), ((stream, jarEntry) -> {
Entry entry = jarStructure.resolve(jarEntry);
if (entry != null && allowedTypes.contains(entry.type()) && StringUtils.hasLength(entry.location())) {
JarEntry newJarEntry = createJarEntry(entry.location(), jarEntry);
output.putNextEntry(newJarEntry);
StreamUtils.copy(stream, output);
output.closeEntry();
if (writtenEntries.add(newJarEntry.getName())) {
output.putNextEntry(newJarEntry);
StreamUtils.copy(stream, output);
output.closeEntry();
}
else {
if (!newJarEntry.isDirectory()) {
throw new IllegalStateException("Duplicate jar entry '%s' from original location '%s'"
.formatted(newJarEntry.getName(), entry.originalLocation()));
}
}
}
}));
}

View File

@ -48,15 +48,17 @@ class ExtractCommandTests extends AbstractJarModeTests {
private static final Instant LAST_ACCESS_TIME = Instant.parse("2022-01-01T00:00:00Z");
private Manifest manifest;
private File archive;
@BeforeEach
void setUp() throws IOException {
Manifest manifest = createManifest("Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx",
this.manifest = createManifest("Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx",
"Spring-Boot-Lib: BOOT-INF/lib/", "Spring-Boot-Classes: BOOT-INF/classes/",
"Start-Class: org.example.Main", "Spring-Boot-Layers-Index: BOOT-INF/layers.idx",
"Some-Attribute: Some-Value");
this.archive = createArchive(manifest, CREATION_TIME, LAST_MODIFIED_TIME, LAST_ACCESS_TIME,
this.archive = createArchive(this.manifest, CREATION_TIME, LAST_MODIFIED_TIME, LAST_ACCESS_TIME,
"BOOT-INF/classpath.idx", "/jar-contents/classpath.idx", "BOOT-INF/layers.idx",
"/jar-contents/layers.idx", "BOOT-INF/lib/dependency-1.jar", "/jar-contents/dependency-1",
"BOOT-INF/lib/dependency-2.jar", "/jar-contents/dependency-2", "BOOT-INF/lib/dependency-3-SNAPSHOT.jar",
@ -86,7 +88,7 @@ class ExtractCommandTests extends AbstractJarModeTests {
catch (IOException ex) {
throw new RuntimeException(ex);
}
};
}
@Nested
class Extract {
@ -216,6 +218,28 @@ class ExtractCommandTests extends AbstractJarModeTests {
assertThat(entryNames).contains("META-INF/build-info.properties");
}
@Test
void shouldNotFailOnDuplicateDirectories() throws IOException {
File file = createArchive(ExtractCommandTests.this.manifest, "BOOT-INF/classpath.idx",
"/jar-contents/classpath.idx", "META-INF/native-image/", "/jar-contents/empty-file",
"BOOT-INF/classes/META-INF/native-image/", "/jar-contents/empty-file");
run(file);
File application = file("test/test.jar");
List<String> entryNames = getJarEntryNames(application);
assertThat(entryNames).containsExactlyInAnyOrder("META-INF/native-image/", "META-INF/MANIFEST.MF");
}
@Test
void shouldFailOnDuplicateFiles() throws IOException {
File file = createArchive(ExtractCommandTests.this.manifest, "BOOT-INF/classpath.idx",
"/jar-contents/classpath.idx", "META-INF/native-image/native-image.properties",
"/jar-contents/empty-file", "BOOT-INF/classes/META-INF/native-image/native-image.properties",
"/jar-contents/empty-file");
assertThatIllegalStateException().isThrownBy(() -> run(file))
.withMessage(
"Duplicate jar entry 'META-INF/native-image/native-image.properties' from original location 'BOOT-INF/classes/META-INF/native-image/native-image.properties'");
}
}
@Nested