Skip to content

Commit 8819529

Browse files
author
Phillip Webb
committed
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
1 parent e9aab1e commit 8819529

File tree

2 files changed

+59
-33
lines changed

2 files changed

+59
-33
lines changed

spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarEntryData.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,26 @@ public final class JarEntryData {
5555

5656
public JarEntryData(JarFile source, byte[] header, InputStream inputStream)
5757
throws IOException {
58-
5958
this.source = source;
6059
this.header = header;
6160
long nameLength = Bytes.littleEndianValue(header, 28, 2);
6261
long extraLength = Bytes.littleEndianValue(header, 30, 2);
6362
long commentLength = Bytes.littleEndianValue(header, 32, 2);
64-
6563
this.name = new AsciiBytes(Bytes.get(inputStream, nameLength));
6664
this.extra = Bytes.get(inputStream, extraLength);
6765
this.comment = new AsciiBytes(Bytes.get(inputStream, commentLength));
68-
6966
this.localHeaderOffset = Bytes.littleEndianValue(header, 42, 4);
7067
}
7168

69+
private JarEntryData(JarEntryData master, JarFile source, AsciiBytes name) {
70+
this.header = master.header;
71+
this.extra = master.extra;
72+
this.comment = master.comment;
73+
this.localHeaderOffset = master.localHeaderOffset;
74+
this.source = source;
75+
this.name = name;
76+
}
77+
7278
void setName(AsciiBytes name) {
7379
this.name = name;
7480
}
@@ -154,6 +160,10 @@ public AsciiBytes getComment() {
154160
return this.comment;
155161
}
156162

163+
JarEntryData createFilteredCopy(JarFile jarFile, AsciiBytes name) {
164+
return new JarEntryData(this, jarFile, name);
165+
}
166+
157167
/**
158168
* Create a new {@link JarEntryData} instance from the specified input stream.
159169
* @param source the source {@link JarFile}

spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,16 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
6868

6969
private final RandomAccessDataFile rootFile;
7070

71-
private final RandomAccessData data;
72-
7371
private final String name;
7472

75-
private final long size;
76-
77-
private boolean signed;
73+
private final RandomAccessData data;
7874

79-
private List<JarEntryData> entries;
75+
private final List<JarEntryData> entries;
8076

8177
private SoftReference<Map<AsciiBytes, JarEntryData>> entriesByName;
8278

79+
private boolean signed;
80+
8381
private JarEntryData manifestEntry;
8482

8583
private SoftReference<Manifest> manifest;
@@ -108,18 +106,25 @@ public JarFile(File file) throws IOException {
108106
* @param rootFile the root jar file
109107
* @param name the name of this file
110108
* @param data the underlying data
111-
* @param filters an optional set of jar entry filters
112109
* @throws IOException
113110
*/
114-
private JarFile(RandomAccessDataFile rootFile, String name, RandomAccessData data,
115-
JarEntryFilter... filters) throws IOException {
111+
private JarFile(RandomAccessDataFile rootFile, String name, RandomAccessData data)
112+
throws IOException {
116113
super(rootFile.getFile());
117114
CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data);
115+
this.rootFile = rootFile;
116+
this.name = name;
118117
this.data = getArchiveData(endRecord, data);
118+
this.entries = loadJarEntries(endRecord);
119+
}
120+
121+
private JarFile(RandomAccessDataFile rootFile, String name, RandomAccessData data,
122+
List<JarEntryData> entries, JarEntryFilter... filters) throws IOException {
123+
super(rootFile.getFile());
119124
this.rootFile = rootFile;
120125
this.name = name;
121-
this.size = data.getSize();
122-
loadJarEntries(endRecord, filters);
126+
this.data = data;
127+
this.entries = filterEntries(entries, filters);
123128
}
124129

125130
private RandomAccessData getArchiveData(CentralDirectoryEndRecord endRecord,
@@ -131,35 +136,47 @@ private RandomAccessData getArchiveData(CentralDirectoryEndRecord endRecord,
131136
return data.getSubsection(offset, data.getSize() - offset);
132137
}
133138

134-
private void loadJarEntries(CentralDirectoryEndRecord endRecord,
135-
JarEntryFilter[] filters) throws IOException {
139+
private List<JarEntryData> loadJarEntries(CentralDirectoryEndRecord endRecord)
140+
throws IOException {
136141
RandomAccessData centralDirectory = endRecord.getCentralDirectory(this.data);
137142
int numberOfRecords = endRecord.getNumberOfRecords();
138-
this.entries = new ArrayList<JarEntryData>(numberOfRecords);
143+
List<JarEntryData> entries = new ArrayList<JarEntryData>(numberOfRecords);
139144
InputStream inputStream = centralDirectory.getInputStream(ResourceAccess.ONCE);
140145
try {
141146
JarEntryData entry = JarEntryData.fromInputStream(this, inputStream);
142147
while (entry != null) {
143-
addJarEntry(entry, filters);
148+
entries.add(entry);
149+
processEntry(entry);
144150
entry = JarEntryData.fromInputStream(this, inputStream);
145151
}
146152
}
147153
finally {
148154
inputStream.close();
149155
}
156+
return entries;
150157
}
151158

152-
private void addJarEntry(JarEntryData entry, JarEntryFilter[] filters) {
153-
AsciiBytes name = entry.getName();
154-
for (JarEntryFilter filter : filters) {
155-
name = (filter == null || name == null ? name : filter.apply(name, entry));
156-
}
157-
if (name != null) {
158-
entry.setName(name);
159-
this.entries.add(entry);
160-
if (name.startsWith(META_INF)) {
161-
processMetaInfEntry(name, entry);
159+
private List<JarEntryData> filterEntries(List<JarEntryData> entries,
160+
JarEntryFilter[] filters) {
161+
List<JarEntryData> filteredEntries = new ArrayList<JarEntryData>(entries.size());
162+
for (JarEntryData entry : entries) {
163+
AsciiBytes name = entry.getName();
164+
for (JarEntryFilter filter : filters) {
165+
name = (filter == null || name == null ? name : filter.apply(name, entry));
162166
}
167+
if (name != null) {
168+
JarEntryData filteredCopy = entry.createFilteredCopy(this, name);
169+
filteredEntries.add(filteredCopy);
170+
processEntry(filteredCopy);
171+
}
172+
}
173+
return filteredEntries;
174+
}
175+
176+
private void processEntry(JarEntryData entry) {
177+
AsciiBytes name = entry.getName();
178+
if (name.startsWith(META_INF)) {
179+
processMetaInfEntry(name, entry);
163180
}
164181
}
165182

@@ -322,8 +339,7 @@ public synchronized JarFile getNestedJarFile(final JarEntryData sourceEntry)
322339
private JarFile getNestedJarFileFromDirectoryEntry(JarEntryData sourceEntry)
323340
throws IOException {
324341
final AsciiBytes sourceName = sourceEntry.getName();
325-
JarEntryFilter[] filtersToUse = new JarEntryFilter[1];
326-
filtersToUse[0] = new JarEntryFilter() {
342+
JarEntryFilter filter = new JarEntryFilter() {
327343
@Override
328344
public AsciiBytes apply(AsciiBytes name, JarEntryData entryData) {
329345
if (name.startsWith(sourceName) && !name.equals(sourceName)) {
@@ -334,7 +350,7 @@ public AsciiBytes apply(AsciiBytes name, JarEntryData entryData) {
334350
};
335351
return new JarFile(this.rootFile, getName() + "!/"
336352
+ sourceEntry.getName().substring(0, sourceName.length() - 1), this.data,
337-
filtersToUse);
353+
this.entries, filter);
338354
}
339355

340356
private JarFile getNestedJarFileFromFileEntry(JarEntryData sourceEntry)
@@ -355,7 +371,7 @@ private JarFile getNestedJarFileFromFileEntry(JarEntryData sourceEntry)
355371
*/
356372
public synchronized JarFile getFilteredJarFile(JarEntryFilter... filters)
357373
throws IOException {
358-
return new JarFile(this.rootFile, getName(), this.data, filters);
374+
return new JarFile(this.rootFile, getName(), this.data, this.entries, filters);
359375
}
360376

361377
private JarEntry getContainedEntry(ZipEntry zipEntry) throws IOException {
@@ -368,7 +384,7 @@ private JarEntry getContainedEntry(ZipEntry zipEntry) throws IOException {
368384

369385
@Override
370386
public int size() {
371-
return (int) this.size;
387+
return (int) this.data.getSize();
372388
}
373389

374390
@Override

0 commit comments

Comments
 (0)