diff --git a/spring-boot-parent/pom.xml b/spring-boot-parent/pom.xml index 6173801467..dd1fda958f 100644 --- a/spring-boot-parent/pom.xml +++ b/spring-boot-parent/pom.xml @@ -85,6 +85,11 @@ jopt-simple 4.6 + + org.apache.commons + commons-compress + 1.14 + org.apache.ivy ivy diff --git a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle index accb619d11..23d5ed3dce 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/build.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -38,7 +38,6 @@ dependencies { compile localGroovy() compile gradleApi() testCompile gradleTestKit() - testCompile 'org.apache.commons:commons-compress:1.13' } jar { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/pom.xml b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml index a1dfc94a5a..9ac6671e1d 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/pom.xml +++ b/spring-boot-tools/spring-boot-gradle-plugin/pom.xml @@ -29,6 +29,10 @@ io.spring.gradle dependency-management-plugin + + org.apache.commons + commons-compress + diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java index 003808cd36..a495113644 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java @@ -83,7 +83,7 @@ class BootArchiveSupport { CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), jar.isPreserveFileTimestamps(), isUsingDefaultLoader(jar), this.requiresUnpack.getAsSpec(), this.exclusions.getAsExcludeSpec(), - this.launchScript, this.compressionResolver); + this.launchScript, this.compressionResolver, jar.getMetadataCharset()); if (!jar.isReproducibleFileOrder()) { return copyAction; } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java index 37b4a4c620..c8bffa8557 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java @@ -24,10 +24,11 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Function; import java.util.zip.CRC32; -import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; +import org.apache.commons.compress.archivers.zip.UnixStat; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.gradle.api.GradleException; import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; @@ -65,10 +66,13 @@ class BootZipCopyAction implements CopyAction { private final Function compressionResolver; + private final String encoding; + BootZipCopyAction(File output, boolean preserveFileTimestamps, boolean includeDefaultLoader, Spec requiresUnpack, Spec exclusions, LaunchScriptConfiguration launchScript, - Function compressionResolver) { + Function compressionResolver, + String encoding) { this.output = output; this.preserveFileTimestamps = preserveFileTimestamps; this.includeDefaultLoader = includeDefaultLoader; @@ -76,16 +80,20 @@ class BootZipCopyAction implements CopyAction { this.exclusions = exclusions; this.launchScript = launchScript; this.compressionResolver = compressionResolver; + this.encoding = encoding; } @Override public WorkResult execute(CopyActionProcessingStream stream) { - ZipOutputStream zipStream; + ZipArchiveOutputStream zipStream; Spec loaderEntries; try { FileOutputStream fileStream = new FileOutputStream(this.output); writeLaunchScriptIfNecessary(fileStream); - zipStream = new ZipOutputStream(fileStream); + zipStream = new ZipArchiveOutputStream(fileStream); + if (this.encoding != null) { + zipStream.setEncoding(this.encoding); + } loaderEntries = writeLoaderClassesIfNecessary(zipStream); } catch (IOException ex) { @@ -113,25 +121,26 @@ class BootZipCopyAction implements CopyAction { return Specs.union(loaderEntries, this.exclusions); } - private Spec writeLoaderClassesIfNecessary(ZipOutputStream out) { + private Spec writeLoaderClassesIfNecessary( + ZipArchiveOutputStream out) { if (!this.includeDefaultLoader) { return Specs.satisfyNone(); } return writeLoaderClasses(out); } - private Spec writeLoaderClasses(ZipOutputStream out) { + private Spec writeLoaderClasses(ZipArchiveOutputStream out) { try (ZipInputStream in = new ZipInputStream(getClass() .getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) { Set entries = new HashSet(); - ZipEntry entry; + java.util.zip.ZipEntry entry; while ((entry = in.getNextEntry()) != null) { if (entry.isDirectory() && !entry.getName().startsWith("META-INF/")) { - writeDirectory(entry, out); + writeDirectory(new ZipArchiveEntry(entry), out); entries.add(entry.getName()); } else if (entry.getName().endsWith(".class")) { - writeClass(entry, in, out); + writeClass(new ZipArchiveEntry(entry), in, out); } } return (element) -> { @@ -147,26 +156,29 @@ class BootZipCopyAction implements CopyAction { } } - private void writeDirectory(ZipEntry entry, ZipOutputStream out) throws IOException { - if (!this.preserveFileTimestamps) { - entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); - } - out.putNextEntry(entry); - out.closeEntry(); - } - - private void writeClass(ZipEntry entry, ZipInputStream in, ZipOutputStream out) + private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) throws IOException { if (!this.preserveFileTimestamps) { entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); } - out.putNextEntry(entry); + entry.setUnixMode(UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM); + out.putArchiveEntry(entry); + out.closeArchiveEntry(); + } + + private void writeClass(ZipArchiveEntry entry, ZipInputStream in, + ZipArchiveOutputStream out) throws IOException { + if (!this.preserveFileTimestamps) { + entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); + } + entry.setUnixMode(UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM); + out.putArchiveEntry(entry); byte[] buffer = new byte[4096]; int read; while ((read = in.read(buffer)) > 0) { out.write(buffer, 0, read); } - out.closeEntry(); + out.closeArchiveEntry(); } private void writeLaunchScriptIfNecessary(FileOutputStream fileStream) { @@ -185,7 +197,7 @@ class BootZipCopyAction implements CopyAction { private static final class ZipStreamAction implements CopyActionProcessingStreamAction { - private final ZipOutputStream zipStream; + private final ZipArchiveOutputStream zipStream; private final File output; @@ -197,7 +209,7 @@ class BootZipCopyAction implements CopyAction { private final Function compressionType; - private ZipStreamAction(ZipOutputStream zipStream, File output, + private ZipStreamAction(ZipArchiveOutputStream zipStream, File output, boolean preserveFileTimestamps, Spec requiresUnpack, Spec exclusions, Function compressionType) { @@ -229,29 +241,31 @@ class BootZipCopyAction implements CopyAction { } private void createDirectory(FileCopyDetailsInternal details) throws IOException { - ZipEntry archiveEntry = new ZipEntry( + ZipArchiveEntry archiveEntry = new ZipArchiveEntry( details.getRelativePath().getPathString() + '/'); + archiveEntry.setUnixMode(UnixStat.DIR_FLAG | details.getMode()); archiveEntry.setTime(getTime(details)); - this.zipStream.putNextEntry(archiveEntry); - this.zipStream.closeEntry(); + this.zipStream.putArchiveEntry(archiveEntry); + this.zipStream.closeArchiveEntry(); } private void createFile(FileCopyDetailsInternal details) throws IOException { String relativePath = details.getRelativePath().getPathString(); - ZipEntry archiveEntry = new ZipEntry(relativePath); + ZipArchiveEntry archiveEntry = new ZipArchiveEntry(relativePath); + archiveEntry.setUnixMode(UnixStat.FILE_FLAG | details.getMode()); archiveEntry.setTime(getTime(details)); ZipCompression compression = this.compressionType.apply(details); if (compression == ZipCompression.STORED) { prepareStoredEntry(details, archiveEntry); } - this.zipStream.putNextEntry(archiveEntry); + this.zipStream.putArchiveEntry(archiveEntry); details.copyTo(this.zipStream); - this.zipStream.closeEntry(); + this.zipStream.closeArchiveEntry(); } private void prepareStoredEntry(FileCopyDetailsInternal details, - ZipEntry archiveEntry) throws IOException { - archiveEntry.setMethod(ZipEntry.STORED); + ZipArchiveEntry archiveEntry) throws IOException { + archiveEntry.setMethod(java.util.zip.ZipEntry.STORED); archiveEntry.setSize(details.getSize()); archiveEntry.setCompressedSize(details.getSize()); Crc32OutputStream crcStream = new Crc32OutputStream(); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java index 8e74fa4d7c..4351843aab 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.gradle.api.Project; import org.gradle.api.tasks.bundling.AbstractArchiveTask; import org.gradle.api.tasks.bundling.Jar; @@ -306,6 +308,27 @@ public abstract class AbstractBootArchiveTests { } } + @Test + public void allEntriesUseUnixPlatformAndUtf8NameEncoding() throws IOException { + this.task.setMainClass("com.example.Main"); + this.task.setMetadataCharset("UTF-8"); + File classpathFolder = this.temp.newFolder(); + File resource = new File(classpathFolder, "some-resource.xml"); + resource.getParentFile().mkdirs(); + resource.createNewFile(); + this.task.classpath(classpathFolder); + this.task.execute(); + File archivePath = this.task.getArchivePath(); + try (ZipFile zip = new ZipFile(archivePath)) { + Enumeration entries = zip.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry entry = entries.nextElement(); + assertThat(entry.getPlatform()).isEqualTo(ZipArchiveEntry.PLATFORM_UNIX); + assertThat(entry.getGeneralPurposeBit().usesUTF8ForNames()).isTrue(); + } + } + } + private T configure(T task) throws IOException { AbstractArchiveTask archiveTask = task; archiveTask.setBaseName("test"); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java index fbf71feb09..3f06851ffe 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -30,6 +30,7 @@ import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import io.spring.gradle.dependencymanagement.DependencyManagementPlugin; +import org.apache.commons.compress.archivers.ArchiveEntry; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; import org.junit.rules.TemporaryFolder; @@ -114,7 +115,8 @@ public class GradleBuild implements TestRule { + absolutePath("build/resources/main") + "," + pathOfJarContaining(LaunchScript.class) + "," + pathOfJarContaining(ClassVisitor.class) + "," - + pathOfJarContaining(DependencyManagementPlugin.class); + + pathOfJarContaining(DependencyManagementPlugin.class) + "," + + pathOfJarContaining(ArchiveEntry.class); } private String absolutePath(String path) { diff --git a/spring-boot-tools/spring-boot-loader-tools/pom.xml b/spring-boot-tools/spring-boot-loader-tools/pom.xml index 22926fc79c..276a0a8f7d 100644 --- a/spring-boot-tools/spring-boot-loader-tools/pom.xml +++ b/spring-boot-tools/spring-boot-loader-tools/pom.xml @@ -23,6 +23,10 @@ org.springframework spring-core + + org.apache.commons + commons-compress + org.springframework.boot 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 7ab71cf74f..4cf1d45f8a 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 @@ -37,11 +37,14 @@ import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; -import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.CRC32; import java.util.zip.ZipEntry; +import org.apache.commons.compress.archivers.jar.JarArchiveEntry; +import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.UnixStat; + /** * Writes JAR content, ensuring valid directory entries are always create and duplicate * items are ignored. @@ -55,7 +58,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { private static final int BUFFER_SIZE = 32 * 1024; - private final JarOutputStream jarOutput; + private final JarArchiveOutputStream jarOutput; private final Set writtenEntries = new HashSet<>(); @@ -83,7 +86,8 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { fileOutputStream.write(launchScript.toByteArray()); setExecutableFilePermission(file); } - this.jarOutput = new JarOutputStream(fileOutputStream); + this.jarOutput = new JarArchiveOutputStream(fileOutputStream); + this.jarOutput.setEncoding("UTF-8"); } private void setExecutableFilePermission(File file) { @@ -105,7 +109,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { * @throws IOException of the manifest cannot be written */ public void writeManifest(final Manifest manifest) throws IOException { - JarEntry entry = new JarEntry("META-INF/MANIFEST.MF"); + JarArchiveEntry entry = new JarArchiveEntry("META-INF/MANIFEST.MF"); writeEntry(entry, new EntryWriter() { @Override public void write(OutputStream outputStream) throws IOException { @@ -127,12 +131,12 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { throws IOException { Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); + JarArchiveEntry entry = new JarArchiveEntry(entries.nextElement()); setUpStoredEntryIfNecessary(jarFile, entry); try (ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream( jarFile.getInputStream(entry))) { EntryWriter entryWriter = new InputStreamEntryWriter(inputStream, true); - JarEntry transformedEntry = entryTransformer.transform(entry); + JarArchiveEntry transformedEntry = entryTransformer.transform(entry); if (transformedEntry != null) { writeEntry(transformedEntry, entryWriter); } @@ -140,7 +144,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { } } - private void setUpStoredEntryIfNecessary(JarFile jarFile, JarEntry entry) + private void setUpStoredEntryIfNecessary(JarFile jarFile, JarArchiveEntry entry) throws IOException { try (ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream( jarFile.getInputStream(entry))) { @@ -158,7 +162,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { */ @Override public void writeEntry(String entryName, InputStream inputStream) throws IOException { - JarEntry entry = new JarEntry(entryName); + JarArchiveEntry entry = new JarArchiveEntry(entryName); writeEntry(entry, new InputStreamEntryWriter(inputStream, true)); } @@ -171,7 +175,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { public void writeNestedLibrary(String destination, Library library) throws IOException { File file = library.getFile(); - JarEntry entry = new JarEntry(destination + library.getName()); + JarArchiveEntry entry = new JarArchiveEntry(destination + library.getName()); entry.setTime(getNestedLibraryTime(file)); if (library.isUnpackRequired()) { entry.setComment("UNPACK:" + FileUtils.sha1Hash(file)); @@ -225,7 +229,8 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { JarEntry entry; while ((entry = inputStream.getNextJarEntry()) != null) { if (entry.getName().endsWith(".class")) { - writeEntry(entry, new InputStreamEntryWriter(inputStream, false)); + writeEntry(new JarArchiveEntry(entry), + new InputStreamEntryWriter(inputStream, false)); } } inputStream.close(); @@ -247,24 +252,29 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { * @param entryWriter the entry writer or {@code null} if there is no content * @throws IOException in case of I/O errors */ - private void writeEntry(JarEntry entry, EntryWriter entryWriter) throws IOException { + private void writeEntry(JarArchiveEntry entry, EntryWriter entryWriter) + throws IOException { String parent = entry.getName(); if (parent.endsWith("/")) { parent = parent.substring(0, parent.length() - 1); + entry.setUnixMode(UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM); + } + else { + entry.setUnixMode(UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM); } if (parent.lastIndexOf("/") != -1) { parent = parent.substring(0, parent.lastIndexOf("/") + 1); if (parent.length() > 0) { - writeEntry(new JarEntry(parent), null); + writeEntry(new JarArchiveEntry(parent), null); } } if (this.writtenEntries.add(entry.getName())) { - this.jarOutput.putNextEntry(entry); + this.jarOutput.putArchiveEntry(entry); if (entryWriter != null) { entryWriter.write(this.jarOutput); } - this.jarOutput.closeEntry(); + this.jarOutput.closeArchiveEntry(); } } @@ -393,7 +403,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { } } - public void setupStoredEntry(JarEntry entry) { + public void setupStoredEntry(JarArchiveEntry entry) { entry.setSize(this.size); entry.setCompressedSize(this.size); entry.setCrc(this.crc.getValue()); @@ -408,7 +418,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { */ interface EntryTransformer { - JarEntry transform(JarEntry jarEntry); + JarArchiveEntry transform(JarArchiveEntry jarEntry); } @@ -418,7 +428,7 @@ public class JarWriter implements LoaderClassesWriter, AutoCloseable { private static final class IdentityEntryTransformer implements EntryTransformer { @Override - public JarEntry transform(JarEntry jarEntry) { + public JarArchiveEntry transform(JarArchiveEntry jarEntry) { return jarEntry; } 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 d011dd90ba..f3e26bb6b8 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 @@ -25,10 +25,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; +import org.apache.commons.compress.archivers.jar.JarArchiveEntry; + import org.springframework.boot.loader.tools.JarWriter.EntryTransformer; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.util.Assert; @@ -407,7 +408,7 @@ public class Repackager { } @Override - public JarEntry transform(JarEntry entry) { + public JarArchiveEntry transform(JarArchiveEntry entry) { if (entry.getName().equals("META-INF/INDEX.LIST")) { return null; } @@ -416,7 +417,8 @@ public class Repackager { || entry.getName().startsWith("BOOT-INF/")) { return entry; } - JarEntry renamedEntry = new JarEntry(this.namePrefix + entry.getName()); + JarArchiveEntry renamedEntry = new JarArchiveEntry( + this.namePrefix + entry.getName()); renamedEntry.setTime(entry.getTime()); renamedEntry.setSize(entry.getSize()); renamedEntry.setMethod(entry.getMethod()); 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 67346e445b..3758ee4546 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 @@ -22,12 +22,15 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.attribute.PosixFilePermission; import java.util.Calendar; +import java.util.Enumeration; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.zip.ZipEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -595,6 +598,23 @@ public class RepackagerTests { } } + @Test + public void allEntriesUseUnixPlatformAndUtf8NameEncoding() throws IOException { + this.testJarFile.addClass("A.class", ClassWithMainMethod.class); + File source = this.testJarFile.getFile(); + File dest = this.temporaryFolder.newFile("dest.jar"); + Repackager repackager = new Repackager(source); + repackager.repackage(dest, NO_LIBRARIES); + try (ZipFile zip = new ZipFile(dest)) { + Enumeration entries = zip.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry entry = entries.nextElement(); + assertThat(entry.getPlatform()).isEqualTo(ZipArchiveEntry.PLATFORM_UNIX); + assertThat(entry.getGeneralPurposeBit().usesUTF8ForNames()).isTrue(); + } + } + } + private boolean hasLauncherClasses(File file) throws IOException { return hasEntry(file, "org/springframework/boot/") && hasEntry(file, "org/springframework/boot/loader/JarLauncher.class");