To build an executable war(with jsp) OCI image, when using tomcat-embed-jasper
dependency, the spring-boot-maven-plugin
build-image is OK, but can't run successfully.
I dig a little deeper, during the build-image phase, spring-boot-maven-plugin
using buidpacks add a soft link of spring-cloud-bindings-1.7.1.jar
to /WEB-INF/lib
folder, this jar cause tomcat-embed-jasper
's StandardJarScanner throw a NullPointerException.
I found a solution then,
Add below in application.yml to skip the scan.
server:
tomcat:
additional-tld-skip-patterns:
- spring-cloud-bindings*.jar
The example code is in the repo zhanming/demo
If comment this configuration out, the issue will recur.
Is this soft link of spring-cloud-bindings-1.7.1.jar necessary?
Comment From: wilkinsona
Thanks for the report. Spring Cloud Bindings is added to the image by the Spring Boot Buildpack and is out of Spring Boot's control. There is an environment variable, BPL_SPRING_CLOUD_BINDINGS_ENABLED
, that can be used to control whether or not the bindings are available but it don't believe it has an effect on whether they're contributed to the image. If you'd like such an option to be added, please raise an issue here.
Interestingly, I can't reproduce the problem in an app with an explicit dependency on spring-cloud-bindings
. Tomcat is able to scan the jar when launched in an IDE, using java -jar app.war
, and unpacked using java org.springframework.boot.loader.WarLauncher
. We'll need to investigate further to see what's causing it to fail in an image.
Comment From: zhanming
Thanks for the quick feedback.
I think spring-cloud-bindings
is good. The issue is in spring-boot-maven-plugin
's build-image
phase, as you said Spring Boot Buildpack add a soft link of spring-cloud-bindings
to /WEB-INF/lib
Follow the Whats New in Spring Boot 2.3
BTW: Thanks to Phil Webb, I'm hoping What's New in Spring Boot 2.5
You can use dive to check the image.
First, build the image.
mvn clean spring-boot:build-image
Install dive on macOS
$ brew install dive
Then dive it.
dive demo:0.0.1-SNAPSHOT
In /workspace/WEB-INF/lib
folder, you can see spring-cloud-bindings
is a soft link in there.
I will raise an issue to Spring Boot Buildpack, thanks.
Comment From: wilkinsona
Thanks. It's the fact that it's a symlink that is key. I can reproduce the problem locally with an unpacked war and spring-cloud-bindings
symlinked into WEB-INF/lib
. The same problem also occurs with standalone Tomcat:
wget https://apache.mirrors.nublue.co.uk/tomcat/tomcat-9/v9.0.48/bin/apache-tomcat-9.0.48.tar.gz
tar -xzf apache-tomcat-9.0.48.tar.gz
mkdir -p apache-tomcat-9.0.48/webapps/symlink-problem/WEB-INF/lib
wget https://repo.spring.io/artifactory/libs-release-local/org/springframework/cloud/spring-cloud-bindings/1.7.1/spring-cloud-bindings-1.7.1.jar
ln -s spring-cloud-bindings-1.7.1.jar apache-tomcat-9.0.48/webapps/symlink-problem/WEB-INF/lib
apache-tomcat-9.0.48/bin/catalina.sh run
24-Jun-2021 10:26:55.777 SEVERE [main] org.apache.catalina.startup.HostConfig.deployDirectory Error deploying web application directory [/Users/awilkinson/dev/temp/tomcat-symlink-problem/apache-tomcat-9.0.48/webapps/symlink-problem]
java.lang.IllegalStateException: Error starting child
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:731)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:700)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:696)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1185)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1933)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:1095)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:477)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1618)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:319)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:423)
at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:366)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:948)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:835)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1388)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:921)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:263)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:437)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:934)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.startup.Catalina.start(Catalina.java:772)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:345)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:476)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/symlink-problem]]
at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:440)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:728)
... 37 more
Caused by: java.lang.NullPointerException
at org.apache.tomcat.util.scan.StandardJarScanner.process(StandardJarScanner.java:382)
at org.apache.tomcat.util.scan.StandardJarScanner.scan(StandardJarScanner.java:195)
at org.apache.catalina.startup.ContextConfig.processJarsForWebFragments(ContextConfig.java:2136)
at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1289)
at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:986)
at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:303)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5135)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
... 38 more
@markt-asf This looks like a Tomcat bug to me. If you agree, would you like us to open a Tomcat issue?
Comment From: markt-asf
Generally, symlinks won't work unless allowLinking
is set to true
on Context.getResources()
. This looks like a symptom of using a symlink when that isn't set but it also looks like Tomcat could handle this better. To be consistent with how Tomcat handles this elsewhere, I'd expect Tomcat to behave as if the symlinked JAR wasn't present. Please do open a Tomcat issue for this.
Comment From: wilkinsona
Thanks, Mark. I've opened https://bz.apache.org/bugzilla/show_bug.cgi?id=65397.