It's possible at the moment using a little bit of manual configuration:
bootBuildImage {
jar = bootWar.archiveFile
}
The jar
property is unfortunately named in this case. archive
or archiveFile
may be better as it would cover both jars and wars. We could also react to the war plugin being applied and automatically make the configuration change above.
Comment From: wilkinsona
Here's the output from bootBuildImage
when configured to use a war file as its "jar":
> Task :bootBuildImage
Building image 'docker.io/library/build-image-from-war:0.0.1-SNAPSHOT'
> Pulling builder image 'docker.io/paketobuildpacks/builder:base' ..................................................
> Pulled builder image 'paketobuildpacks/builder@sha256:bae0a6b7ec5d51867eb49ac598775bb5fa8788bba458d2edfda314611be554a8'
> Pulling run image 'docker.io/paketobuildpacks/run:base-cnb' ..................................................
> Pulled run image 'paketobuildpacks/run@sha256:155ee4b4389fa88b8b9bd5444601417218b3da44e491b1aad8c8b6c2365e7909'
> Executing lifecycle version v0.9.3
> Using build cache volume 'pack-cache-9462ad59044b.build'
> Running creator
[creator] ===> DETECTING
[creator] 6 of 18 buildpacks participating
[creator] paketo-buildpacks/ca-certificates 1.0.1
[creator] paketo-buildpacks/bellsoft-liberica 5.2.0
[creator] paketo-buildpacks/executable-jar 3.1.3
[creator] paketo-buildpacks/apache-tomcat 2.4.1
[creator] paketo-buildpacks/dist-zip 2.2.2
[creator] paketo-buildpacks/spring-boot 3.4.0
[creator] ===> ANALYZING
[creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:helper" from app image
[creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:java-security-properties" from app image
[creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jre" from app image
[creator] Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jvmkill" from app image
[creator] Restoring metadata for "paketo-buildpacks/executable-jar:class-path" from app image
[creator] Restoring metadata for "paketo-buildpacks/apache-tomcat:catalina-base" from app image
[creator] Restoring metadata for "paketo-buildpacks/apache-tomcat:helper" from app image
[creator] Restoring metadata for "paketo-buildpacks/apache-tomcat:tomcat" from app image
[creator] Restoring metadata for "paketo-buildpacks/spring-boot:helper" from app image
[creator] Restoring metadata for "paketo-buildpacks/spring-boot:spring-cloud-bindings" from app image
[creator] Restoring metadata for "paketo-buildpacks/spring-boot:web-application-type" from app image
[creator] ===> RESTORING
[creator] ===> BUILDING
[creator]
[creator] Paketo CA Certificates Buildpack 1.0.1
[creator] https://github.com/paketo-buildpacks/ca-certificates
[creator] Launch Helper: Contributing to layer
[creator] Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
[creator] Writing profile.d/helper
[creator]
[creator] Paketo BellSoft Liberica Buildpack 5.2.0
[creator] https://github.com/paketo-buildpacks/bellsoft-liberica
[creator] Build Configuration:
[creator] $BP_JVM_VERSION 11.* the Java version
[creator] Launch Configuration:
[creator] $BPL_JVM_HEAD_ROOM 0 the headroom in memory calculation
[creator] $BPL_JVM_LOADED_CLASS_COUNT 35% of classes the number of loaded classes in memory calculation
[creator] $BPL_JVM_THREAD_COUNT 250 the number of threads in memory calculation
[creator] $JAVA_TOOL_OPTIONS the JVM launch flags
[creator] BellSoft Liberica JRE 11.0.9: Contributing to layer
[creator] Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.9.1+1/bellsoft-jre11.0.9.1+1-linux-amd64.tar.gz
[creator] Verifying checksum
[creator] Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
[creator] Adding 138 container CA certificates to JVM truststore
[creator] Writing env.launch/BPI_APPLICATION_PATH.default
[creator] Writing env.launch/BPI_JVM_CACERTS.default
[creator] Writing env.launch/BPI_JVM_CLASS_COUNT.default
[creator] Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
[creator] Writing env.launch/JAVA_HOME.default
[creator] Writing env.launch/MALLOC_ARENA_MAX.default
[creator] Launch Helper: Contributing to layer
[creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/active-processor-count
[creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts
[creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/link-local-dns
[creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator
[creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/openssl-certificate-loader
[creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-configurer
[creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-classpath-9
[creator] Writing profile.d/helper
[creator] JVMKill Agent 1.16.0: Reusing cached layer
[creator] Java Security Properties: Contributing to layer
[creator] Writing env.launch/JAVA_SECURITY_PROPERTIES.default
[creator] Writing env.launch/JAVA_TOOL_OPTIONS.append
[creator] Writing env.launch/JAVA_TOOL_OPTIONS.delim
[creator]
[creator] Paketo Executable JAR Buildpack 3.1.3
[creator] https://github.com/paketo-buildpacks/executable-jar
[creator] Process types:
[creator] executable-jar: java org.springframework.boot.loader.WarLauncher
[creator] task: java org.springframework.boot.loader.WarLauncher
[creator] web: java org.springframework.boot.loader.WarLauncher
[creator]
[creator] Paketo Apache Tomcat Buildpack 2.4.1
[creator] https://github.com/paketo-buildpacks/apache-tomcat
[creator] Build Configuration:
[creator] $BP_TOMCAT_CONTEXT_PATH the application context path
[creator] $BP_TOMCAT_EXT_CONF_SHA256 the SHA256 hash of the external Tomcat configuration archive
[creator] $BP_TOMCAT_EXT_CONF_STRIP 0 the number of directory components to strip from the external Tomcat configuration archive
[creator] $BP_TOMCAT_EXT_CONF_URI the download location of the external Tomcat configuration
[creator] $BP_TOMCAT_EXT_CONF_VERSION the version of the external Tomcat configuration
[creator] $BP_TOMCAT_VERSION 9.* the Tomcat version
[creator] Launch Configuration:
[creator] $BPL_TOMCAT_ACCESS_LOGGING_ENABLED the Tomcat access logging state
[creator] Apache Tomcat 9.0.39: Contributing to layer
[creator] Downloading from https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.39/bin/apache-tomcat-9.0.39.tar.gz
[creator] Verifying checksum
[creator] Expanding to /layers/paketo-buildpacks_apache-tomcat/tomcat
[creator] Writing env.launch/CATALINA_HOME.default
[creator] Launch Helper: Contributing to layer
[creator] Creating /layers/paketo-buildpacks_apache-tomcat/helper/exec.d/access-logging-support
[creator] Writing profile.d/helper
[creator] Apache Tomcat Support: Reusing cached layer
[creator] Process types:
[creator] task: catalina.sh run
[creator] tomcat: catalina.sh run
[creator] web: catalina.sh run
[creator]
[creator] Paketo Spring Boot Buildpack 3.4.0
[creator] https://github.com/paketo-buildpacks/spring-boot
[creator] Launch Helper: Contributing to layer
[creator] Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
[creator] Writing profile.d/helper
[creator] Web Application Type: Reusing cached layer
[creator] Spring Cloud Bindings 1.6.0: Reusing cached layer
[creator] Image labels:
[creator] org.springframework.boot.spring-configuration-metadata.json
[creator] org.springframework.boot.version
[creator] ===> EXPORTING
[creator] Adding layer 'paketo-buildpacks/ca-certificates:helper'
[creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:helper'
[creator] Reusing layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
[creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
[creator] Reusing layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
[creator] Reusing layer 'paketo-buildpacks/executable-jar:class-path'
[creator] Reusing layer 'paketo-buildpacks/apache-tomcat:catalina-base'
[creator] Adding layer 'paketo-buildpacks/apache-tomcat:helper'
[creator] Adding layer 'paketo-buildpacks/apache-tomcat:tomcat'
[creator] Adding layer 'paketo-buildpacks/spring-boot:helper'
[creator] Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
[creator] Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
[creator] Reusing 1/1 app layer(s)
[creator] Adding layer 'launcher'
[creator] Adding layer 'config'
[creator] Adding label 'io.buildpacks.lifecycle.metadata'
[creator] Adding label 'io.buildpacks.build.metadata'
[creator] Adding label 'io.buildpacks.project.metadata'
[creator] Adding label 'org.springframework.boot.spring-configuration-metadata.json'
[creator] Adding label 'org.springframework.boot.version'
[creator] *** Images (55754fd6d792):
[creator] docker.io/library/build-image-from-war:0.0.1-SNAPSHOT
Successfully built image 'docker.io/library/build-image-from-war:0.0.1-SNAPSHOT'
Things seem to get a bit confused as the executable jar, Tomcat, and Spring Boot buildpacks all get involved. It's not clear to me from the output which will win and, therefore, how the application will be launched.
We can run the image and see what happens. Here's the output from doing so:
Setting Active Processor Count to 8
WARNING: Container memory limit unset. Configuring JVM for 1G container.
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx659378K -XX:MaxMetaspaceSize=81997K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 50, Loaded Class Count: 12063, Headroom: 0%)
Adding 138 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Using CATALINA_BASE: /layers/paketo-buildpacks_apache-tomcat/catalina-base
Using CATALINA_HOME: /layers/paketo-buildpacks_apache-tomcat/tomcat
Using CATALINA_TMPDIR: /layers/paketo-buildpacks_apache-tomcat/catalina-base/temp
Using JRE_HOME: /layers/paketo-buildpacks_bellsoft-liberica/jre
Using CLASSPATH: /layers/paketo-buildpacks_apache-tomcat/catalina-base/bin/tomcat-logging-support-3.3.0.RELEASE.jar:/layers/paketo-buildpacks_apache-tomcat/tomcat/bin/bootstrap.jar:/layers/paketo-buildpacks_apache-tomcat/tomcat/bin/tomcat-juli.jar
Using CATALINA_OPTS:
NOTE: Picked up JDK_JAVA_OPTIONS: --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=8 -XX:MaxDirectMemorySize=10M -Xmx659378K -XX:MaxMetaspaceSize=81997K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true
[CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Initializing ProtocolHandler ["http-nio-8080"]
[CONTAINER] org.apache.catalina.startup.Catalina INFO Server initialization in [404] milliseconds
[CONTAINER] org.apache.catalina.core.StandardService INFO Starting service [Catalina]
[CONTAINER] org.apache.catalina.core.StandardEngine INFO Starting Servlet engine: [Apache Tomcat/9.0.39]
[CONTAINER] org.apache.catalina.startup.HostConfig INFO Deploying web application directory [/layers/paketo-buildpacks_apache-tomcat/catalina-base/webapps/ROOT]
[CONTAINER] org.apache.jasper.servlet.TldScanner INFO At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[CONTAINER] lina.core.ContainerBase.[Catalina].[localhost].[/] INFO 2 Spring WebApplicationInitializers detected on classpath
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.0-M4)
2020-11-10 10:41:26.193 INFO 1 --- [ main] c.e.b.ServletInitializer : Starting ServletInitializer using Java 11.0.9.1 on 9f715d4be99a with PID 1 (/workspace/WEB-INF/classes started by cnb in /workspace)
2020-11-10 10:41:26.196 INFO 1 --- [ main] c.e.b.ServletInitializer : No active profile set, falling back to default profiles: default
[CONTAINER] lina.core.ContainerBase.[Catalina].[localhost].[/] INFO Initializing Spring embedded WebApplicationContext
2020-11-10 10:41:26.660 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 422 ms
2020-11-10 10:41:26.954 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-10 10:41:27.078 INFO 1 --- [ main] c.e.b.ServletInitializer : Started ServletInitializer in 1.239 seconds (JVM running for 2.699)
[CONTAINER] org.apache.catalina.startup.HostConfig INFO Deployment of web application directory [/layers/paketo-buildpacks_apache-tomcat/catalina-base/webapps/ROOT] has finished in [2,148] ms
[CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Starting ProtocolHandler ["http-nio-8080"]
[CONTAINER] org.apache.catalina.startup.Catalina INFO Server startup in [2183] milliseconds
It's a traditional war deployment with our war file being deployed to Tomcat. If a Spring Boot user is building a war file purely to use JSPs (the only reason to use war packaging), a traditional war deployment may not be what they want. In many cases they'll want their executable war to be launched as an executable jar would have been. Among other things, launching an executable war will ensure that the app's choice of embedded container (it may not be Tomcat) and the configuration of it is honoured.
@nebhale What's the current state of the art with Paketo and building an executable war-based image? Is it possible today with some config options, or would some builder changes be required for it to support either executable war or traditional war deployments?
Comment From: nebhale
@wilkinsona The executable WAR thing is really problematic for us because it’s not immediately obvious what the user was intending. In general we have to do some serious surgery (note the lack of the plugin) on an application to get a coherent sample. That sample is the outcome of many people asking why, when they changed a Boot application to <packaging>war</packaging>
, it didn’t run in a contributed Tomcat.
The good news is that we’re in a better situation with CNBs than we were with CF buildpacks, because applications don’t need to have be unambiguously one type or another. They can define the same process types in a last-wins priority, they can define multiple process types allowing the user to decide which command to run, etc.
I see a couple of options and welcome your opinion on what you’d like to see:
- Change the priority so that the application is run as an executable JAR if there is a
Main-Class
regardless of packaging. Tomcat would be contributed, even though it’d likely rarely be used. - Change the detection logic so that
apache-tomcat
would not participate if there was aMain-Class
even if it was a WAR. Tomcat would not be contributed, even though it might be desired for some applications. - Something we haven’t thought of yet...
/cc @ekcasey
Comment From: wilkinsona
Thanks, Ben.
In an ideal world, I think we'd make two things possible:
- Build an image without a Servlet container that runs the war as an executable jar
- Build an image with a Servlet container that deploys the war to the container
Generally speaking, I think these two are compatible with you second option. In fact, from a general CNB perspective, I think they're completely compatible. From a Spring Boot perspective things may be a bit trickier, particularly with Maven where I think you may have to jump through some hoops to build a war file without a Main-Class
while still being able to make use of the build-image
goal. It's more straightforward with Gradle where you'd either use the output of the war
task or the bootWar
task depending on the sort of image that you wanted.
Comment From: nebhale
@wilkinsona this has been released and is propagating out to the various places that buildpacks live.
Comment From: scottfrederick
Without the bootBuildImage.jar
configuration suggested in this issue, running the bootBuildImage
task will always result in a jar file being built and used in the image creation, even though the packaging type is set to war. Re-classifying this as a bug since that behavior isn't what the user would expect.
Comment From: scottfrederick
After further discussion, #24521 has been created to replace the unexpected behavior with an error message for current releases. Proper support for image building with war packaging will be a future enhancement.
Comment From: philwebb
Possibly related to #22195