From 88195292dd7464a4392d26c1b0e194bbd43be662 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 20 Jun 2014 08:54:04 -0700 Subject: [PATCH] Reuse previously parsed entries for filtered JARs Update JarFile to reuse the previously parsed entries when creating filtered jars. This saves needing to re-scan the underlying file to recreate a subset of entries. See gh-1119 --- .../boot/loader/jar/JarEntryData.java | 16 +++- .../boot/loader/jar/JarFile.java | 78 +++++++++++-------- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarEntryData.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarEntryData.java index cdf7559273..cce9f96d7a 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarEntryData.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarEntryData.java @@ -55,20 +55,26 @@ public final class JarEntryData { public JarEntryData(JarFile source, byte[] header, InputStream inputStream) throws IOException { - this.source = source; this.header = header; long nameLength = Bytes.littleEndianValue(header, 28, 2); long extraLength = Bytes.littleEndianValue(header, 30, 2); long commentLength = Bytes.littleEndianValue(header, 32, 2); - this.name = new AsciiBytes(Bytes.get(inputStream, nameLength)); this.extra = Bytes.get(inputStream, extraLength); this.comment = new AsciiBytes(Bytes.get(inputStream, commentLength)); - this.localHeaderOffset = Bytes.littleEndianValue(header, 42, 4); } + private JarEntryData(JarEntryData master, JarFile source, AsciiBytes name) { + this.header = master.header; + this.extra = master.extra; + this.comment = master.comment; + this.localHeaderOffset = master.localHeaderOffset; + this.source = source; + this.name = name; + } + void setName(AsciiBytes name) { this.name = name; } @@ -154,6 +160,10 @@ public final class JarEntryData { return this.comment; } + JarEntryData createFilteredCopy(JarFile jarFile, AsciiBytes name) { + return new JarEntryData(this, jarFile, name); + } + /** * Create a new {@link JarEntryData} instance from the specified input stream. * @param source the source {@link JarFile} diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java index 739e6b8051..e1d917a30f 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java @@ -68,18 +68,16 @@ public class JarFile extends java.util.jar.JarFile implements Iterable entries; + private final List entries; private SoftReference> entriesByName; + private boolean signed; + private JarEntryData manifestEntry; private SoftReference manifest; @@ -108,18 +106,25 @@ public class JarFile extends java.util.jar.JarFile implements Iterable entries, JarEntryFilter... filters) throws IOException { + super(rootFile.getFile()); + this.rootFile = rootFile; + this.name = name; + this.data = data; + this.entries = filterEntries(entries, filters); } private RandomAccessData getArchiveData(CentralDirectoryEndRecord endRecord, @@ -131,35 +136,47 @@ public class JarFile extends java.util.jar.JarFile implements Iterable loadJarEntries(CentralDirectoryEndRecord endRecord) + throws IOException { RandomAccessData centralDirectory = endRecord.getCentralDirectory(this.data); int numberOfRecords = endRecord.getNumberOfRecords(); - this.entries = new ArrayList(numberOfRecords); + List entries = new ArrayList(numberOfRecords); InputStream inputStream = centralDirectory.getInputStream(ResourceAccess.ONCE); try { JarEntryData entry = JarEntryData.fromInputStream(this, inputStream); while (entry != null) { - addJarEntry(entry, filters); + entries.add(entry); + processEntry(entry); entry = JarEntryData.fromInputStream(this, inputStream); } } finally { inputStream.close(); } + return entries; } - private void addJarEntry(JarEntryData entry, JarEntryFilter[] filters) { - AsciiBytes name = entry.getName(); - for (JarEntryFilter filter : filters) { - name = (filter == null || name == null ? name : filter.apply(name, entry)); - } - if (name != null) { - entry.setName(name); - this.entries.add(entry); - if (name.startsWith(META_INF)) { - processMetaInfEntry(name, entry); + private List filterEntries(List entries, + JarEntryFilter[] filters) { + List filteredEntries = new ArrayList(entries.size()); + for (JarEntryData entry : entries) { + AsciiBytes name = entry.getName(); + for (JarEntryFilter filter : filters) { + name = (filter == null || name == null ? name : filter.apply(name, entry)); } + if (name != null) { + JarEntryData filteredCopy = entry.createFilteredCopy(this, name); + filteredEntries.add(filteredCopy); + processEntry(filteredCopy); + } + } + return filteredEntries; + } + + private void processEntry(JarEntryData entry) { + AsciiBytes name = entry.getName(); + if (name.startsWith(META_INF)) { + processMetaInfEntry(name, entry); } } @@ -322,8 +339,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable