When assembling a big jar file, SpringBoot breaks with a IndexOutOfBoundsException.
I have put the example that creates this bug at:
https://github.com/jfarjona/bugs-spring-boot-1
It breaks with different versions.
Exception details:
Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: java.lang.IndexOutOfBoundsException
at org.springframework.boot.loader.PropertiesLauncher.<init>(PropertiesLauncher.java:158)
at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:465)
Caused by: java.lang.IllegalStateException: java.lang.IndexOutOfBoundsException
at org.springframework.boot.loader.PropertiesLauncher.getHomeDirectory(PropertiesLauncher.java:167)
at org.springframework.boot.loader.PropertiesLauncher.<init>(PropertiesLauncher.java:152)
... 1 more
Caused by: java.lang.IndexOutOfBoundsException
at org.springframework.boot.loader.jar.AsciiBytes.<init>(AsciiBytes.java:73)
at org.springframework.boot.loader.jar.CentralDirectoryFileHeader.load(CentralDirectoryFileHeader.java:96)
at org.springframework.boot.loader.jar.CentralDirectoryParser.parseEntries(CentralDirectoryParser.java:68)
at org.springframework.boot.loader.jar.CentralDirectoryParser.parse(CentralDirectoryParser.java:57)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:137)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:123)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:109)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:100)
at org.springframework.boot.loader.archive.JarFileArchive.<init>(JarFileArchive.java:73)
at org.springframework.boot.loader.archive.JarFileArchive.<init>(JarFileArchive.java:69)
at org.springframework.boot.loader.Launcher.createArchive(Launcher.java:163)
at org.springframework.boot.loader.PropertiesLauncher.getProperty(PropertiesLauncher.java:442)
at org.springframework.boot.loader.PropertiesLauncher.getPropertyWithDefault(PropertiesLauncher.java:403)
at org.springframework.boot.loader.PropertiesLauncher.getHomeDirectory(PropertiesLauncher.java:164)
... 2 more
Juan
Comment From: philwebb
The sample application cannot be built on OSX but I did manage to replicate the problem on Linux. The jar file in question is 8.4G 😱
Comment From: philwebb
I think the problem is out zip64 detection logic. We currently check to see if the "number of records" entry contains 0xFFFF
. With the jar from your sample we have an EOCD that looks like this:
504B0506 signature
0000 number of disk
0000 disk where cd starts
CA01 number of records
CA01 total num of rec
D3AE0000 size of CD
FFFFFFFF offset of start of central directory
0000 comment length
The number of records in this file is 0xCA01
not 0xFFFF
. Only the offset has a 0xFFFFFFFF
value.
Looking at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT I think this is valid:
The number of this disk, which contains central directory end record. If an archive is in ZIP64 format and the value in this field is 0xFFFF, the size will be in the corresponding 4 byte zip64 end of central directory field.
The important part being "if ... the value in this field is 0xFFFF". I think this means that it doesn't need to be 0xFFFF, but it can be 0xFFFF.
I think we need to refine CentralDirectoryEndRecord.isZip64
.
Comment From: wilkinsona
With a fix for the Zip64-detection in place, another failure occurs:
Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: java.lang.IndexOutOfBoundsException
at org.springframework.boot.loader.PropertiesLauncher.<init>(PropertiesLauncher.java:158)
at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:465)
Caused by: java.lang.IllegalStateException: java.lang.IndexOutOfBoundsException
at org.springframework.boot.loader.PropertiesLauncher.getHomeDirectory(PropertiesLauncher.java:167)
at org.springframework.boot.loader.PropertiesLauncher.<init>(PropertiesLauncher.java:152)
... 1 more
Caused by: java.lang.IndexOutOfBoundsException
at org.springframework.boot.loader.data.RandomAccessDataFile.getSubsection(RandomAccessDataFile.java:83)
at org.springframework.boot.loader.jar.CentralDirectoryEndRecord$Zip64End.getCentralDirectory(CentralDirectoryEndRecord.java:188)
at org.springframework.boot.loader.jar.CentralDirectoryEndRecord$Zip64End.access$300(CentralDirectoryEndRecord.java:149)
at org.springframework.boot.loader.jar.CentralDirectoryEndRecord.getCentralDirectory(CentralDirectoryEndRecord.java:118)
at org.springframework.boot.loader.jar.CentralDirectoryParser.parse(CentralDirectoryParser.java:55)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:139)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:123)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:109)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:100)
at org.springframework.boot.loader.archive.JarFileArchive.<init>(JarFileArchive.java:73)
at org.springframework.boot.loader.archive.JarFileArchive.<init>(JarFileArchive.java:69)
at org.springframework.boot.loader.Launcher.createArchive(Launcher.java:163)
at org.springframework.boot.loader.PropertiesLauncher.getProperty(PropertiesLauncher.java:442)
at org.springframework.boot.loader.PropertiesLauncher.getPropertyWithDefault(PropertiesLauncher.java:403)
at org.springframework.boot.loader.PropertiesLauncher.getHomeDirectory(PropertiesLauncher.java:164)
... 2 more
Comment From: wilkinsona
And another one after that:
Exception in thread "main" java.lang.IllegalStateException: Failed to get nested archive for entry BOOT-INF/lib/cuda-11.2-8.1-1.5.5-windows-x86_64-redist.jar
at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:120)
at org.springframework.boot.loader.archive.JarFileArchive$NestedArchiveIterator.adapt(JarFileArchive.java:274)
at org.springframework.boot.loader.archive.JarFileArchive$NestedArchiveIterator.adapt(JarFileArchive.java:265)
at org.springframework.boot.loader.archive.JarFileArchive$AbstractIterator.next(JarFileArchive.java:226)
at org.springframework.boot.loader.PropertiesLauncher$ClassPathArchives.addNestedEntries(PropertiesLauncher.java:651)
at org.springframework.boot.loader.PropertiesLauncher$ClassPathArchives.<init>(PropertiesLauncher.java:542)
at org.springframework.boot.loader.PropertiesLauncher.getClassPathArchivesIterator(PropertiesLauncher.java:458)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:55)
at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:467)
Caused by: java.io.IOException: Unable to open nested jar file 'BOOT-INF/lib/cuda-11.2-8.1-1.5.5-windows-x86_64-redist.jar'
at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:295)
at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:281)
at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:116)
... 8 more
Caused by: java.io.IOException: Unable to find ZIP central directory records after reading 65792 bytes
at org.springframework.boot.loader.jar.CentralDirectoryEndRecord.<init>(CentralDirectoryEndRecord.java:68)
at org.springframework.boot.loader.jar.CentralDirectoryParser.parse(CentralDirectoryParser.java:51)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:139)
at org.springframework.boot.loader.jar.JarFile.<init>(JarFile.java:123)
at org.springframework.boot.loader.jar.JarFile.createJarFileFromFileEntry(JarFile.java:326)
at org.springframework.boot.loader.jar.JarFile.createJarFileFromEntry(JarFile.java:303)
at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:292)
... 10 more
Comment From: wilkinsona
I have a fix for this. I'm a little bit concerned about the increased memory footprint due to JarFileEntries.centralDirectoryOffsets
changing from int[]
to long[]
. That'll consume in additional 4 bytes per entry (top-level and nested).