Skip to content

Commit 2d12fa0

Browse files
committed
Merge branch '3.2.x'
Closes gh-39071
2 parents 6a083f6 + e5f489f commit 2d12fa0

File tree

5 files changed

+120
-11
lines changed

5 files changed

+120
-11
lines changed

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ public InputStream getRawZipDataInputStream() throws IOException {
154154
@Override
155155
public Manifest getManifest() throws IOException {
156156
try {
157-
return this.resources.zipContent().getInfo(ManifestInfo.class, this::getManifestInfo).getManifest();
157+
return this.resources.zipContentForManifest()
158+
.getInfo(ManifestInfo.class, this::getManifestInfo)
159+
.getManifest();
158160
}
159161
catch (UncheckedIOException ex) {
160162
throw ex.getCause();

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

+31
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import org.springframework.boot.loader.ref.Cleaner;
3232
import org.springframework.boot.loader.zip.ZipContent;
33+
import org.springframework.boot.loader.zip.ZipContent.Kind;
3334

3435
/**
3536
* Resources created managed and cleaned by a {@link NestedJarFile} instance and suitable
@@ -43,6 +44,8 @@ class NestedJarFileResources implements Runnable {
4344

4445
private ZipContent zipContent;
4546

47+
private ZipContent zipContentForManifest;
48+
4649
private final Set<InputStream> inputStreams = Collections.newSetFromMap(new WeakHashMap<>());
4750

4851
private Deque<Inflater> inflaterCache = new ArrayDeque<>();
@@ -55,6 +58,8 @@ class NestedJarFileResources implements Runnable {
5558
*/
5659
NestedJarFileResources(File file, String nestedEntryName) throws IOException {
5760
this.zipContent = ZipContent.open(file.toPath(), nestedEntryName);
61+
this.zipContentForManifest = (this.zipContent.getKind() != Kind.NESTED_DIRECTORY) ? null
62+
: ZipContent.open(file.toPath());
5863
}
5964

6065
/**
@@ -65,6 +70,15 @@ ZipContent zipContent() {
6570
return this.zipContent;
6671
}
6772

73+
/**
74+
* Return the underlying {@link ZipContent} that should be used to load manifest
75+
* content.
76+
* @return the zip content to use when loading the manifest
77+
*/
78+
ZipContent zipContentForManifest() {
79+
return (this.zipContentForManifest != null) ? this.zipContentForManifest : this.zipContent;
80+
}
81+
6882
/**
6983
* Add a managed input stream resource.
7084
* @param inputStream the input stream
@@ -144,6 +158,7 @@ private void releaseAll() {
144158
exceptionChain = releaseInflators(exceptionChain);
145159
exceptionChain = releaseInputStreams(exceptionChain);
146160
exceptionChain = releaseZipContent(exceptionChain);
161+
exceptionChain = releaseZipContentForManifest(exceptionChain);
147162
if (exceptionChain != null) {
148163
throw new UncheckedIOException(exceptionChain);
149164
}
@@ -195,6 +210,22 @@ private IOException releaseZipContent(IOException exceptionChain) {
195210
return exceptionChain;
196211
}
197212

213+
private IOException releaseZipContentForManifest(IOException exceptionChain) {
214+
ZipContent zipContentForManifest = this.zipContentForManifest;
215+
if (zipContentForManifest != null) {
216+
try {
217+
zipContentForManifest.close();
218+
}
219+
catch (IOException ex) {
220+
exceptionChain = addToExceptionChain(exceptionChain, ex);
221+
}
222+
finally {
223+
this.zipContentForManifest = null;
224+
}
225+
}
226+
return exceptionChain;
227+
}
228+
198229
private IOException addToExceptionChain(IOException exceptionChain, IOException ex) {
199230
if (exceptionChain != null) {
200231
exceptionChain.addSuppressed(ex);

spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java

+46-10
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ public final class ZipContent implements Closeable {
7272

7373
private final Source source;
7474

75+
private final Kind kind;
76+
7577
private final FileChannelDataBlock data;
7678

7779
private final long centralDirectoryPos;
@@ -94,10 +96,11 @@ public final class ZipContent implements Closeable {
9496

9597
private SoftReference<Map<Class<?>, Object>> info;
9698

97-
private ZipContent(Source source, FileChannelDataBlock data, long centralDirectoryPos, long commentPos,
99+
private ZipContent(Source source, Kind kind, FileChannelDataBlock data, long centralDirectoryPos, long commentPos,
98100
long commentLength, int[] lookupIndexes, int[] nameHashLookups, int[] relativeCentralDirectoryOffsetLookups,
99101
NameOffsetLookups nameOffsetLookups, boolean hasJarSignatureFile) {
100102
this.source = source;
103+
this.kind = kind;
101104
this.data = data;
102105
this.centralDirectoryPos = centralDirectoryPos;
103106
this.commentPos = commentPos;
@@ -109,6 +112,15 @@ private ZipContent(Source source, FileChannelDataBlock data, long centralDirecto
109112
this.hasJarSignatureFile = hasJarSignatureFile;
110113
}
111114

115+
/**
116+
* Return the kind of content that was loaded.
117+
* @return the content kind
118+
* @since 3.2.2
119+
*/
120+
public Kind getKind() {
121+
return this.kind;
122+
}
123+
112124
/**
113125
* Open a {@link DataBlock} containing the raw zip data. For container zip files, this
114126
* may be smaller than the original file since additional bytes are permitted at the
@@ -380,6 +392,30 @@ private static ZipContent open(Source source) throws IOException {
380392
return zipContent;
381393
}
382394

395+
/**
396+
* Zip content kinds.
397+
*
398+
* @since 3.2.2
399+
*/
400+
public enum Kind {
401+
402+
/**
403+
* Content from a standard zip file.
404+
*/
405+
ZIP,
406+
407+
/**
408+
* Content from nested zip content.
409+
*/
410+
NESTED_ZIP,
411+
412+
/**
413+
* Content from a nested zip directory.
414+
*/
415+
NESTED_DIRECTORY
416+
417+
}
418+
383419
/**
384420
* The source of {@link ZipContent}. Used as a cache key.
385421
*
@@ -451,7 +487,7 @@ private void add(ZipCentralDirectoryFileHeaderRecord centralRecord, long pos, bo
451487
this.cursor++;
452488
}
453489

454-
private ZipContent finish(long commentPos, long commentLength, boolean hasJarSignatureFile) {
490+
private ZipContent finish(Kind kind, long commentPos, long commentLength, boolean hasJarSignatureFile) {
455491
if (this.cursor != this.nameHashLookups.length) {
456492
this.nameHashLookups = Arrays.copyOf(this.nameHashLookups, this.cursor);
457493
this.relativeCentralDirectoryOffsetLookups = Arrays.copyOf(this.relativeCentralDirectoryOffsetLookups,
@@ -463,7 +499,7 @@ private ZipContent finish(long commentPos, long commentLength, boolean hasJarSig
463499
for (int i = 0; i < size; i++) {
464500
lookupIndexes[this.index[i]] = i;
465501
}
466-
return new ZipContent(this.source, this.data, this.centralDirectoryPos, commentPos, commentLength,
502+
return new ZipContent(this.source, kind, this.data, this.centralDirectoryPos, commentPos, commentLength,
467503
lookupIndexes, this.nameHashLookups, this.relativeCentralDirectoryOffsetLookups,
468504
this.nameOffsetLookups, hasJarSignatureFile);
469505
}
@@ -525,7 +561,7 @@ static ZipContent load(Source source) throws IOException {
525561

526562
private static ZipContent loadNonNested(Source source) throws IOException {
527563
debug.log("Loading non-nested zip '%s'", source.path());
528-
return openAndLoad(source, new FileChannelDataBlock(source.path()));
564+
return openAndLoad(source, Kind.ZIP, new FileChannelDataBlock(source.path()));
529565
}
530566

531567
private static ZipContent loadNestedZip(Source source, Entry entry) throws IOException {
@@ -534,21 +570,21 @@ private static ZipContent loadNestedZip(Source source, Entry entry) throws IOExc
534570
.formatted(source.nestedEntryName(), source.path()));
535571
}
536572
debug.log("Loading nested zip entry '%s' from '%s'", source.nestedEntryName(), source.path());
537-
return openAndLoad(source, entry.getContent());
573+
return openAndLoad(source, Kind.NESTED_ZIP, entry.getContent());
538574
}
539575

540-
private static ZipContent openAndLoad(Source source, FileChannelDataBlock data) throws IOException {
576+
private static ZipContent openAndLoad(Source source, Kind kind, FileChannelDataBlock data) throws IOException {
541577
try {
542578
data.open();
543-
return loadContent(source, data);
579+
return loadContent(source, kind, data);
544580
}
545581
catch (IOException | RuntimeException ex) {
546582
data.close();
547583
throw ex;
548584
}
549585
}
550586

551-
private static ZipContent loadContent(Source source, FileChannelDataBlock data) throws IOException {
587+
private static ZipContent loadContent(Source source, Kind kind, FileChannelDataBlock data) throws IOException {
552588
ZipEndOfCentralDirectoryRecord.Located locatedEocd = ZipEndOfCentralDirectoryRecord.load(data);
553589
ZipEndOfCentralDirectoryRecord eocd = locatedEocd.endOfCentralDirectoryRecord();
554590
long eocdPos = locatedEocd.pos();
@@ -585,7 +621,7 @@ private static ZipContent loadContent(Source source, FileChannelDataBlock data)
585621
pos += centralRecord.size();
586622
}
587623
long commentPos = locatedEocd.pos() + ZipEndOfCentralDirectoryRecord.COMMENT_OFFSET;
588-
return loader.finish(commentPos, eocd.commentLength(), hasJarSignatureFile);
624+
return loader.finish(kind, commentPos, eocd.commentLength(), hasJarSignatureFile);
589625
}
590626

591627
/**
@@ -642,7 +678,7 @@ private static ZipContent loadNestedDirectory(Source source, ZipContent zip, Ent
642678
}
643679
}
644680
}
645-
return loader.finish(zip.commentPos, zip.commentLength, zip.hasJarSignatureFile);
681+
return loader.finish(Kind.NESTED_DIRECTORY, zip.commentPos, zip.commentLength, zip.hasJarSignatureFile);
646682
}
647683
catch (IOException | RuntimeException ex) {
648684
zip.data.close();

spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/NestedJarFileTests.java

+20
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,26 @@ void createWhenNestedJarDirectoryOpensJar() throws IOException {
110110
}
111111
}
112112

113+
@Test
114+
void getManifestWhenNestedJarReturnsManifestOfNestedJar() throws Exception {
115+
try (JarFile jar = new JarFile(this.file)) {
116+
try (NestedJarFile nestedJar = new NestedJarFile(this.file, "nested.jar")) {
117+
Manifest manifest = nestedJar.getManifest();
118+
assertThat(manifest).isNotEqualTo(jar.getManifest());
119+
assertThat(manifest.getMainAttributes().getValue("Built-By")).isEqualTo("j2");
120+
}
121+
}
122+
}
123+
124+
@Test
125+
void getManifestWhenNestedJarDirectoryReturnsManifestOfParent() throws Exception {
126+
try (JarFile jar = new JarFile(this.file)) {
127+
try (NestedJarFile nestedJar = new NestedJarFile(this.file, "d/")) {
128+
assertThat(nestedJar.getManifest()).isEqualTo(jar.getManifest());
129+
}
130+
}
131+
}
132+
113133
@Test
114134
void createWhenJarHasFrontMatterOpensJar() throws IOException {
115135
File file = new File(this.tempDir, "frontmatter.jar");

spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipContentTests.java

+20
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
import org.springframework.boot.loader.testsupport.TestJar;
5050
import org.springframework.boot.loader.zip.ZipContent.Entry;
51+
import org.springframework.boot.loader.zip.ZipContent.Kind;
5152
import org.springframework.util.FileCopyUtils;
5253
import org.springframework.util.StreamUtils;
5354

@@ -168,6 +169,25 @@ void getEntryAsCreatesCompatibleEntries() throws IOException {
168169
}
169170
}
170171

172+
@Test
173+
void getKindWhenZipReturnsZip() {
174+
assertThat(this.zipContent.getKind()).isEqualTo(Kind.ZIP);
175+
}
176+
177+
@Test
178+
void getKindWhenNestedZipReturnsNestedZip() throws IOException {
179+
try (ZipContent nested = ZipContent.open(this.file.toPath(), "nested.jar")) {
180+
assertThat(nested.getKind()).isEqualTo(Kind.NESTED_ZIP);
181+
}
182+
}
183+
184+
@Test
185+
void getKindWhenNestedDirectoryReturnsNestedDirectory() throws IOException {
186+
try (ZipContent nested = ZipContent.open(this.file.toPath(), "d/")) {
187+
assertThat(nested.getKind()).isEqualTo(Kind.NESTED_DIRECTORY);
188+
}
189+
}
190+
171191
private void assertThatFieldsAreEqual(ZipEntry actual, ZipEntry expected) {
172192
assertThat(actual.getName()).isEqualTo(expected.getName());
173193
assertThat(actual.getTime()).isEqualTo(expected.getTime());

0 commit comments

Comments
 (0)