diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java index 97da6ffc71b..0ff3f14a40f 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java @@ -20,7 +20,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.jar.JarFile; import java.util.jar.Manifest; @@ -154,29 +156,30 @@ public class Repackager { private void repackage(JarFile sourceJar, File destination, Libraries libraries) throws IOException { - final JarWriter writer = new JarWriter(destination); + JarWriter writer = new JarWriter(destination); try { - final Set seen = new HashSet(); - writer.writeManifest(buildManifest(sourceJar)); + final List unpackLibraries = new ArrayList(); + final List standardLibraries = new ArrayList(); + libraries.doWithLibraries(new LibraryCallback() { @Override public void library(Library library) throws IOException { File file = library.getFile(); if (isZip(file)) { - String destination = Repackager.this.layout - .getLibraryDestination(library.getName(), - library.getScope()); - if (destination != null) { - if (!seen.add(destination + library.getName())) { - throw new IllegalStateException("Duplicate library " - + library.getName()); - } - writer.writeNestedLibrary(destination, library); + if (library.isUnpackRequired()) { + unpackLibraries.add(library); + } + else { + standardLibraries.add(library); } } } }); + writer.writeManifest(buildManifest(sourceJar)); + Set seen = new HashSet(); + writeNestedLibraries(unpackLibraries, seen, writer); writer.writeEntries(sourceJar); + writeNestedLibraries(standardLibraries, seen, writer); if (this.layout.isExecutable()) { writer.writeLoaderClasses(); } @@ -191,6 +194,21 @@ public class Repackager { } } + private void writeNestedLibraries(List libraries, Set alreadySeen, + JarWriter writer) throws IOException { + for (Library library : libraries) { + String destination = Repackager.this.layout.getLibraryDestination( + library.getName(), library.getScope()); + if (destination != null) { + if (!alreadySeen.add(destination + library.getName())) { + throw new IllegalStateException("Duplicate library " + + library.getName()); + } + writer.writeNestedLibrary(destination, library); + } + } + } + private boolean isZip(File file) { try { FileInputStream fileInputStream = new FileInputStream(file); 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 a8a92de05ea..8b3a91ad5d0 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 @@ -32,6 +32,7 @@ import org.junit.rules.TemporaryFolder; import org.springframework.boot.loader.tools.sample.ClassWithMainMethod; import org.springframework.boot.loader.tools.sample.ClassWithoutMainMethod; import org.springframework.util.FileCopyUtils; +import org.zeroturnaround.zip.ZipUtil; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; @@ -393,6 +394,69 @@ public class RepackagerTests { } } + @Test + public void unpackLibrariesTakePrecedenceOverExistingSourceEntries() throws Exception { + final TestJarFile nested = new TestJarFile(this.temporaryFolder); + nested.addClass("a/b/C.class", ClassWithoutMainMethod.class); + final File nestedFile = nested.getFile(); + this.testJarFile.addFile("lib/" + nestedFile.getName(), nested.getFile()); + this.testJarFile.addClass("A.class", ClassWithMainMethod.class); + File file = this.testJarFile.getFile(); + Repackager repackager = new Repackager(file); + repackager.repackage(new Libraries() { + + @Override + public void doWithLibraries(LibraryCallback callback) throws IOException { + callback.library(new Library(nestedFile, LibraryScope.COMPILE, true)); + } + + }); + + JarFile jarFile = new JarFile(file); + try { + assertThat(jarFile.getEntry("lib/" + nestedFile.getName()).getComment(), + startsWith("UNPACK:")); + } + finally { + jarFile.close(); + } + } + + @Test + public void existingSourceEntriesTakePrecedenceOverStandardLibraries() + throws Exception { + final TestJarFile nested = new TestJarFile(this.temporaryFolder); + nested.addClass("a/b/C.class", ClassWithoutMainMethod.class); + final File nestedFile = nested.getFile(); + this.testJarFile.addFile("lib/" + nestedFile.getName(), nested.getFile()); + this.testJarFile.addClass("A.class", ClassWithMainMethod.class); + File file = this.testJarFile.getFile(); + Repackager repackager = new Repackager(file); + + long sourceLength = nestedFile.length(); + + repackager.repackage(new Libraries() { + + @Override + public void doWithLibraries(LibraryCallback callback) throws IOException { + nestedFile.delete(); + File toZip = RepackagerTests.this.temporaryFolder.newFile(); + ZipUtil.packEntry(toZip, nestedFile); + callback.library(new Library(nestedFile, LibraryScope.COMPILE)); + } + + }); + + JarFile jarFile = new JarFile(file); + try { + assertThat(jarFile.getEntry("lib/" + nestedFile.getName()).getSize(), + equalTo(sourceLength)); + } + finally { + jarFile.close(); + } + } + private boolean hasLauncherClasses(File file) throws IOException { return hasEntry(file, "org/springframework/boot/") && hasEntry(file, "org/springframework/boot/loader/JarLauncher.class");