|
|
|
|
@ -16,6 +16,7 @@
@@ -16,6 +16,7 @@
|
|
|
|
|
|
|
|
|
|
package org.springframework.boot.jarmode.tools; |
|
|
|
|
|
|
|
|
|
import java.io.BufferedOutputStream; |
|
|
|
|
import java.io.File; |
|
|
|
|
import java.io.FileInputStream; |
|
|
|
|
import java.io.FileOutputStream; |
|
|
|
|
@ -26,6 +27,7 @@ import java.io.PrintStream;
@@ -26,6 +27,7 @@ import java.io.PrintStream;
|
|
|
|
|
import java.io.UncheckedIOException; |
|
|
|
|
import java.nio.file.Files; |
|
|
|
|
import java.nio.file.attribute.BasicFileAttributeView; |
|
|
|
|
import java.nio.file.attribute.BasicFileAttributes; |
|
|
|
|
import java.nio.file.attribute.FileTime; |
|
|
|
|
import java.util.EnumSet; |
|
|
|
|
import java.util.Enumeration; |
|
|
|
|
@ -34,6 +36,7 @@ import java.util.List;
@@ -34,6 +36,7 @@ import java.util.List;
|
|
|
|
|
import java.util.Locale; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import java.util.Set; |
|
|
|
|
import java.util.function.Consumer; |
|
|
|
|
import java.util.jar.JarEntry; |
|
|
|
|
import java.util.jar.JarFile; |
|
|
|
|
import java.util.jar.JarOutputStream; |
|
|
|
|
@ -242,10 +245,17 @@ class ExtractCommand extends Command {
@@ -242,10 +245,17 @@ class ExtractCommand extends Command {
|
|
|
|
|
String librariesDirectory = getLibrariesDirectory(options); |
|
|
|
|
Manifest manifest = jarStructure.createLauncherManifest((library) -> librariesDirectory + library); |
|
|
|
|
mkdirs(file.getParentFile()); |
|
|
|
|
try (JarOutputStream output = new JarOutputStream(new FileOutputStream(file), manifest)) { |
|
|
|
|
try (JarOutputStream output = new JarOutputStream(new FileOutputStream(file))) { |
|
|
|
|
ManfiestWriter manfiestWriter = (sourceJarFile) -> { |
|
|
|
|
JarEntry entry = createJarEntry(JarFile.MANIFEST_NAME, |
|
|
|
|
sourceJarFile.getJarEntry(JarFile.MANIFEST_NAME)); |
|
|
|
|
output.putNextEntry(entry); |
|
|
|
|
manifest.write(new BufferedOutputStream(output)); |
|
|
|
|
output.closeEntry(); |
|
|
|
|
}; |
|
|
|
|
EnumSet<Type> allowedTypes = EnumSet.of(Type.APPLICATION_CLASS_OR_RESOURCE, Type.META_INF); |
|
|
|
|
Set<String> writtenEntries = new HashSet<>(); |
|
|
|
|
withJarEntries(this.context.getArchiveFile(), ((stream, jarEntry) -> { |
|
|
|
|
withJarEntries(this.context.getArchiveFile(), manfiestWriter, ((stream, jarEntry) -> { |
|
|
|
|
Entry entry = jarStructure.resolve(jarEntry); |
|
|
|
|
if (entry != null && allowedTypes.contains(entry.type()) && StringUtils.hasLength(entry.location())) { |
|
|
|
|
JarEntry newJarEntry = createJarEntry(entry.location(), jarEntry); |
|
|
|
|
@ -263,6 +273,15 @@ class ExtractCommand extends Command {
@@ -263,6 +273,15 @@ class ExtractCommand extends Command {
|
|
|
|
|
} |
|
|
|
|
})); |
|
|
|
|
} |
|
|
|
|
copyTimestamps(this.context.getArchiveFile(), file); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void copyTimestamps(File source, File destination) throws IOException { |
|
|
|
|
BasicFileAttributes sourceAttributes = Files.getFileAttributeView(source.toPath(), BasicFileAttributeView.class) |
|
|
|
|
.readAttributes(); |
|
|
|
|
Files.getFileAttributeView(destination.toPath(), BasicFileAttributeView.class) |
|
|
|
|
.setTimes(sourceAttributes.lastModifiedTime(), sourceAttributes.lastAccessTime(), |
|
|
|
|
sourceAttributes.creationTime()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private String getApplicationFilename(Map<Option, @Nullable String> options) { |
|
|
|
|
@ -287,6 +306,28 @@ class ExtractCommand extends Command {
@@ -287,6 +306,28 @@ class ExtractCommand extends Command {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void mkdirs(File file) throws IOException { |
|
|
|
|
if (!file.exists() && !file.mkdirs()) { |
|
|
|
|
throw new IOException("Unable to create directory " + file); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static JarEntry createJarEntry(String location, JarEntry originalEntry) { |
|
|
|
|
JarEntry entry = new JarEntry(location); |
|
|
|
|
if (originalEntry != null) { |
|
|
|
|
copyFileTime(getLastModifiedTime(originalEntry), entry::setLastModifiedTime); |
|
|
|
|
copyFileTime(getLastAccessTime(originalEntry), entry::setLastAccessTime); |
|
|
|
|
copyFileTime(getCreationTime(originalEntry), entry::setCreationTime); |
|
|
|
|
} |
|
|
|
|
return entry; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void copyFileTime(@Nullable FileTime fileTime, Consumer<FileTime> setter) { |
|
|
|
|
if (fileTime != null) { |
|
|
|
|
setter.accept(fileTime); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static @Nullable FileTime getCreationTime(JarEntry entry) { |
|
|
|
|
return (entry.getCreationTime() != null) ? entry.getCreationTime() : entry.getLastModifiedTime(); |
|
|
|
|
} |
|
|
|
|
@ -299,31 +340,16 @@ class ExtractCommand extends Command {
@@ -299,31 +340,16 @@ class ExtractCommand extends Command {
|
|
|
|
|
return (entry.getLastModifiedTime() != null) ? entry.getLastModifiedTime() : entry.getCreationTime(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void mkdirs(File file) throws IOException { |
|
|
|
|
if (!file.exists() && !file.mkdirs()) { |
|
|
|
|
throw new IOException("Unable to create directory " + file); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static JarEntry createJarEntry(String location, JarEntry originalEntry) { |
|
|
|
|
JarEntry jarEntry = new JarEntry(location); |
|
|
|
|
FileTime lastModifiedTime = getLastModifiedTime(originalEntry); |
|
|
|
|
if (lastModifiedTime != null) { |
|
|
|
|
jarEntry.setLastModifiedTime(lastModifiedTime); |
|
|
|
|
} |
|
|
|
|
FileTime lastAccessTime = getLastAccessTime(originalEntry); |
|
|
|
|
if (lastAccessTime != null) { |
|
|
|
|
jarEntry.setLastAccessTime(lastAccessTime); |
|
|
|
|
} |
|
|
|
|
FileTime creationTime = getCreationTime(originalEntry); |
|
|
|
|
if (creationTime != null) { |
|
|
|
|
jarEntry.setCreationTime(creationTime); |
|
|
|
|
} |
|
|
|
|
return jarEntry; |
|
|
|
|
private static void withJarEntries(File file, ThrowingConsumer callback) throws IOException { |
|
|
|
|
withJarEntries(file, null, callback); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void withJarEntries(File file, ThrowingConsumer callback) throws IOException { |
|
|
|
|
private static void withJarEntries(File file, @Nullable ManfiestWriter manfiestWriter, ThrowingConsumer callback) |
|
|
|
|
throws IOException { |
|
|
|
|
try (JarFile jarFile = new JarFile(file)) { |
|
|
|
|
if (manfiestWriter != null) { |
|
|
|
|
manfiestWriter.writeManifest(jarFile); |
|
|
|
|
} |
|
|
|
|
Enumeration<JarEntry> entries = jarFile.entries(); |
|
|
|
|
while (entries.hasMoreElements()) { |
|
|
|
|
JarEntry entry = entries.nextElement(); |
|
|
|
|
@ -352,6 +378,13 @@ class ExtractCommand extends Command {
@@ -352,6 +378,13 @@ class ExtractCommand extends Command {
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@FunctionalInterface |
|
|
|
|
private interface ManfiestWriter { |
|
|
|
|
|
|
|
|
|
void writeManifest(JarFile sourceJarFile) throws IOException; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@FunctionalInterface |
|
|
|
|
private interface ThrowingConsumer { |
|
|
|
|
|
|
|
|
|
|