Spring Boot Version
3.2.0-RC1
Description of Bug/Issue
We use an embedded tomcat to bring up a server that still uses Java Server Pages (jsp), and still needs to scan for taglibs. This has normally not causes any issues. But when trying to migrate to Spring Boot 3.2.0, we've seen logs like these upon startup:
ZipException trying to scan `WEB-INF/classes
2023-10-25 11:55:26,387 INFO [] org.apache.catalina.core.StandardEngine: Starting Servlet engine: [Apache Tomcat/10.1.15]
2023-10-25 11:55:26,676 WARN [] org.apache.tomcat.util.scan.StandardJarScanner: Failed to scan [jar:nested:/Users/dconard/projects/web/backend/app/build/libs/app.war/!WEB-INF/classes/!/] from classloader hierarchy
java.util.zip.ZipException: invalid entry size (expected 2531524616 but got 0 bytes)
at java.base/java.util.zip.ZipInputStream.readEnd(ZipInputStream.java:399)
at java.base/java.util.zip.ZipInputStream.read(ZipInputStream.java:198)
at java.base/java.util.jar.JarInputStream.read(JarInputStream.java:194)
at java.base/java.util.zip.ZipInputStream.closeEntry(ZipInputStream.java:142)
at java.base/java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:120)
at java.base/java.util.jar.JarInputStream.<init>(JarInputStream.java:84)
at java.base/java.util.jar.JarInputStream.<init>(JarInputStream.java:61)
at org.apache.tomcat.util.scan.NonClosingJarInputStream.<init>(NonClosingJarInputStream.java:37)
at org.apache.tomcat.util.scan.UrlJar.createJarInputStream(UrlJar.java:47)
at org.apache.tomcat.util.scan.AbstractInputStreamJar.reset(AbstractInputStreamJar.java:160)
at org.apache.tomcat.util.scan.AbstractInputStreamJar.getManifest(AbstractInputStreamJar.java:151)
at org.apache.tomcat.util.scan.StandardJarScanner.processManifest(StandardJarScanner.java:444)
at org.apache.tomcat.util.scan.StandardJarScanner.process(StandardJarScanner.java:399)
at org.apache.tomcat.util.scan.StandardJarScanner.processURLs(StandardJarScanner.java:332)
at org.apache.tomcat.util.scan.StandardJarScanner.doScanClassPath(StandardJarScanner.java:275)
at org.apache.tomcat.util.scan.StandardJarScanner.scan(StandardJarScanner.java:238)
at org.apache.jasper.servlet.TldScanner.scanJars(TldScanner.java:262)
at org.apache.jasper.servlet.TldScanner.scan(TldScanner.java:104)
at org.apache.jasper.servlet.JasperInitializer.onStartup(JasperInitializer.java:83)
Failure to scan jars
2023-10-25 11:55:26,686 WARN [] org.apache.tomcat.util.scan.StandardJarScanner: Failed to scan [jar:nested:/Users/dconard/projects/web/backend/app/build/libs/app.war/!WEB-INF/lib/jakarta.servlet.jsp.jstl-3.0.1.jar!/] from classloader hierarchy
java.io.IOException: no entry name specified
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.getInputStream(JarUrlConnection.java:179)
at org.apache.catalina.webresources.war.WarURLConnection.getInputStream(WarURLConnection.java:52)
at java.base/java.net.URL.openStream(URL.java:1161)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFileFactory.createJarFileForStream(UrlJarFileFactory.java:89)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFileFactory.createJarFile(UrlJarFileFactory.java:57)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFiles.getOrCreate(UrlJarFiles.java:72)
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.connect(JarUrlConnection.java:277)
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.getInputStream(JarUrlConnection.java:189)
at java.base/java.net.URL.openStream(URL.java:1161)
at org.apache.tomcat.util.descriptor.tld.TldResourcePath.openStream(TldResourcePath.java:127)
at org.apache.tomcat.util.descriptor.tld.TldParser.parse(TldParser.java:62)
at org.apache.jasper.servlet.TldScanner.parseTld(TldScanner.java:275)
2023-10-25 11:55:26,718 WARN [] org.apache.tomcat.util.scan.StandardJarScanner: Failed to scan [jar:nested:/Users/dconard/projects/web/backend/app/build/libs/app.war/!WEB-INF/lib/spring-webmvc-6.1.0-RC1.jar!/] from classloader hierarchy
java.io.IOException: no entry name specified
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.getInputStream(JarUrlConnection.java:179)
at org.apache.catalina.webresources.war.WarURLConnection.getInputStream(WarURLConnection.java:52)
at java.base/java.net.URL.openStream(URL.java:1161)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFileFactory.createJarFileForStream(UrlJarFileFactory.java:89)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFileFactory.createJarFile(UrlJarFileFactory.java:57)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFiles.getOrCreate(UrlJarFiles.java:72)
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.connect(JarUrlConnection.java:277)
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.getInputStream(JarUrlConnection.java:189)
at java.base/java.net.URL.openStream(URL.java:1161)
at org.apache.tomcat.util.descriptor.tld.TldResourcePath.openStream(TldResourcePath.java:127)
at org.apache.tomcat.util.descriptor.tld.TldParser.parse(TldParser.java:62)
at org.apache.jasper.servlet.TldScanner.parseTld(TldScanner.java:275)
2023-10-25 11:55:26,725 WARN [] org.apache.tomcat.util.scan.StandardJarScanner: Failed to scan [jar:nested:/Users/dconard/projects/web/backend/app/build/libs/app.war/!WEB-INF/lib/struts-taglib-1.3.8.jar!/] from classloader hierarchy
java.io.IOException: no entry name specified
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.getInputStream(JarUrlConnection.java:179)
at org.apache.catalina.webresources.war.WarURLConnection.getInputStream(WarURLConnection.java:52)
at java.base/java.net.URL.openStream(URL.java:1161)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFileFactory.createJarFileForStream(UrlJarFileFactory.java:89)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFileFactory.createJarFile(UrlJarFileFactory.java:57)
at org.springframework.boot.loader.net.protocol.jar.UrlJarFiles.getOrCreate(UrlJarFiles.java:72)
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.connect(JarUrlConnection.java:277)
at org.springframework.boot.loader.net.protocol.jar.JarUrlConnection.getInputStream(JarUrlConnection.java:189)
at java.base/java.net.URL.openStream(URL.java:1161)
at org.apache.tomcat.util.descriptor.tld.TldResourcePath.openStream(TldResourcePath.java:127)
at org.apache.tomcat.util.descriptor.tld.TldParser.parse(TldParser.java:62)
at org.apache.jasper.servlet.TldScanner.parseTld(TldScanner.java:275)
The documentation here mentioned that the loaderImplementation has changed. I was able to resolve the issue by changing to the CLASSIC implementation. But I wanted to raise an issue posterity's sake.
Comment From: philwebb
Thanks for the report @dconard. I've managed to replicate the problem which is caused by org.apache.tomcat.util.scan.JarFactory picking a different implementation than before along with a bug in our nested: URL support.
I think we can fix the URL bug by using a similar technique to the one we use in org.springframework.boot.loader.nio.file.NestedFileSystem.
Longer term we might want to develop our own JarScanner that can handle nested URLs directly. I've opened #38049 to consider that.
Comment From: philwebb
The bug with our own code is related to data descriptor records. I think I know how to fix that one.
Comment From: philwebb
@dconard Thanks very much for trying the RC and reporting this one. Once this build is green, I wonder if you could give the latest 3.2.0-SNAPSHOT a try just to confirm that the issue is fixed?
Comment From: dconard
@philwebb , I tried the snapshot this morning and it worked perfectly. Thanks so much for getting that fix in so quickly!