Our application is split into two JARs (The first contains the spring boot launcher and our classes. The second file contains just 3rd party libraries) to be possible to share libraries between multiple applications to mitigate data transfer etc.:

jar lite.jar:

+BOOT-INF
| + classes (our classes and resources)
| + lib (our libraries - not the 3rd party)
| + classpath.idx
| + layers.idx
+ META-INF
| + MANIFEST.MF
+ org (spring boot launcher)

jar lite-lib.jar

+ BOOT-INF
| + lib (3rd party libraries)
+ META-INF
  + MANIFEST.MF (empty manifest)

We run this application with command like java -cp lite.jar -Dloader.path=lite-lib.jar org.springframework.boot.loader.PropertiesLauncher

Our JDK:

java version "1.8.0_321"
Java(TM) SE Runtime Environment (build 8.0.7.5 - pmz6480sr7fp5-20220208_01(SR7 FP5))
IBM J9 VM (build 2.9, JRE 1.8.0 z/OS s390x-64-Bit Compressed References 20220104_19630 (JIT enabled, AOT enabled)
OpenJ9   - 2d4c7d9
OMR      - 59845b7
IBM      - 3c151c1)
JCL - 20220120_01 based on Oracle jdk8u321-b07

Since version 2.6.9 we randomly got this exception:

java.lang.reflect.InvocationTargetException
.at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
.at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90)
.at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
.at java.lang.reflect.Method.invoke(Method.java:508)
.at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
.at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
.at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
.at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:467)
Caused by: java.lang.IllegalStateException: zip file closed
.at org.springframework.boot.loader.jar.JarFile.ensureOpen(JarFile.java:383)
.at org.springframework.boot.loader.jar.JarFile.getEntry(JarFile.java:269)
.at org.springframework.boot.loader.jar.JarFileWrapper.getEntry(JarFileWrapper.java:82)
.at org.springframework.boot.loader.LaunchedURLClassLoader.lambda$definePackage$0(LaunchedURLClassLoader.java:225)
.at org.springframework.boot.loader.LaunchedURLClassLoader$$Lambda$10/0x00000000d8525970.run(Unknown Source)
.at java.security.AccessController.doPrivileged(AccessController.java:774)
.at org.springframework.boot.loader.LaunchedURLClassLoader.definePackage(LaunchedURLClassLoader.java:217)
.at org.springframework.boot.loader.LaunchedURLClassLoader.definePackageIfNecessary(LaunchedURLClassLoader.java:199)
.at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:140)
.at java.lang.ClassLoader.loadClass(ClassLoader.java:873)
....
.at ch.qos.logback.classic.spi.TurboFilterList.getTurboFilterChainDecision(TurboFilterList.java:60)
.at ch.qos.logback.classic.LoggerContext.getTurboFilterChainDecision_0_3OrMore(LoggerContext.java:269)
.at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:373)
.at ch.qos.logback.classic.Logger.error(Logger.java:522)
....

It seems that the reason is the commit https://github.com/spring-projects/spring-boot/commit/b42f056ddbfd5041ef80d2d909dd2f5e51ec3ff0:

It collects references of nested JARs and then closes their streams if the main is closing, see:

https://github.com/spring-projects/spring-boot/blob/24c2ed3c788452260d15e592671224f9f347d1e8/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java#L99

https://github.com/spring-projects/spring-boot/blob/24c2ed3c788452260d15e592671224f9f347d1e8/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java#L335-L347

https://github.com/spring-projects/spring-boot/blob/24c2ed3c788452260d15e592671224f9f347d1e8/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java#L362-L379

I guess the problem is in synchronization between threads. The code contains synchronization blocks for elementary operation, but cannot detect if a different thread is trying to read data meanwhile.

Once I remove this part of the code (closing of nestedJar) I was not able to simulate this case again.

https://github.com/spring-projects/spring-boot/blob/24c2ed3c788452260d15e592671224f9f347d1e8/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java#L374-L376

If I am right, those streams shouldn't be closed. One way could be to use ie. ByteArrayInputStream as a buffer and takes care just of the input streams reading the data.

Comment From: wilkinsona

This may be a duplicate of https://github.com/spring-projects/spring-boot/issues/31853.

Comment From: philwebb

Having dug into #31853 a bit more I'm pretty sure this is a duplicate. @pj892031 If at all possible, once build 1110 if green, could you try the latest SNAPSHOT build and see if the issue is fixed?

Comment From: pj892031

I tested that with the snapshot version and could not reproduce the bug. It seems to be solved. Thank you.