docs community blog github
Edit

How to Build Java Apps with Paketo Buildpacks

This documentation explains how to use the Paketo buildpacks to build Java applications for several common use-cases. For more in-depth description of the buildpacks' behavior and configuration see the Paketo Java Buildpack and Paketo Java Native Image Buildpack reference documentation.

About the Examples

All Java Buildpack examples will use the Paketo sample applications.

Examples assume that the root of this repository is the working directory:

git clone https://github.com/paketo-buildpacks/samples
cd samples
copy to clipboard
Copied!

The pack CLI is used throughout the examples. pack is just one of several Cloud Native Buildpack platforms than can execute builds with the Java Buildpacks. For example, Spring Boot developers may want to explore the Spring Boot Maven Plugin or Spring Boot Gradle Plugin .

Examples assume that the Paketo Base builder is the default builder:

pack config default-builder paketobuildpacks/builder:base
copy to clipboard
Copied!

All java example images should return {"status":"UP"} from the actuator health endpoint.

docker run --rm --tty --publish 8080:8080 samples/java
curl -s http://localhost:8080/actuator/health | jq .
copy to clipboard
Copied!

Build an App as a Traditional Java WAR or JAR

Build from Source

The Java Buildpack can build from source using any of the following build tools:

The correct build tool to use will be detected based on the contents of the application directory.

The build should produce one the of supported artifact formats. After building, the buildpack will replace provided application source code with the exploded archive. The build will proceed as described in Building from a Compiled Artifact.

Example: Building with Maven

The following command creates an image from source with maven.

pack build samples/java \
  --path java/maven
copy to clipboard
Copied!

Configure the Build Tool

Note: The following set of configuration options are not comprehensive, see the homepage for the relevant component buildpacks for a full-set of configuration options.

Select a Module or Artifact

For a given build <TOOL>, where <TOOL> is one of MAVEN, GRADLE, LEIN or SBT, the selected artifact can be configured with one of the following environment variable at build-time:

  • BP_<TOOL>_BUILT_MODULE
    • Defaults to the root module.
    • Configures the module in a multi-module build from which the buildpack will select the application artifact.
    • Example: Given BP_MAVEN_BUILT_MODULE=api, Paketo Maven Buildpack will look for the application artifact with the file pattern target/api/*.[jw]ar.
  • BP_<TOOL>_BUILT_ARTIFACT
    • Defaults to a tool-specific pattern (e.g. target/*.[jw]ar for Maven, build/libs/*.[jw]ar for gradle). See component buildpack homepage for details.
    • Configures the built application artifact path, using Bash Pattern Matching.
    • Supercedes BP_<TOOL>_BUILT_MODULE if set to a non-default value.
    • Example: Given BP_MAVEN_BUILT_ARTIFACT=out/api-*.jar, the Paketo Maven Buildpack will select a file with name out/api-1.0.0.jar.
Specify the Build Command

For a given build <TOOL>, where <TOOL> is one of MAVEN, GRADLE, LEIN or SBT, the build command can be configured with the following environment variable at build-time:

  • BP_<TOOL>_BUILD_ARGUMENTS
    • Defaults to a tool-specific value (e.g. -Dmaven.test.skip=true package for Maven, --no-daemon assemble for Gradle). See component buildpack homepage for details.
    • Configures the arguments to pass to the build tool.
    • Example: Given BP_GRADLE_BUILD_ARGUMENTS=war, the Paketo Gradle Buildpack will execute ./gradlew war or gradle war (depending on the presence of the gradle wrapper).
Connect to a Private Maven Repository

A binding with type maven and key settings.xml can be used to provide custom Maven settings.

<binding-name>
├── settings.xml
└── type

The value of settings.xml file may contain the credentials needed to connect to a private Maven repository.

Example: Providing Maven Settings

The following steps demonstrate how to use a settings.xml file from your workstation with pack.

  1. Create a directory to contain the binding.

    mkdir java/maven/binding
    copy to clipboard
    Copied!

  2. Indicate that the binding is of type maven with a file called type inside the binding, containing the value maven.

    echo -n "maven" > java/maven/binding/type
    copy to clipboard
    Copied!

  3. Copy the settings.xml file from the workstation to the binding.

    cp ~/.m2/settings.xml java/maven/binding/settings.xml
    copy to clipboard
    Copied!

  4. Provide the binding to pack build.

    pack build samples/java \
       --path java/maven \
       --volume $(pwd)/java/maven/binding:/platform/bindings/my-maven-settings
    copy to clipboard
    Copied!

Build from a Compiled Artifact

An application developer may build an image from following archive formats:

The Java Buildpack expects the application directory to contain the extracted contents of the archive (e.g. an exploded JAR). Most platforms will automatically extract any provided archives.

If a WAR is detect the Java Buildpack will install Apache Tomcat. For exact set of supported Tomcat versions can be found in the Java Buildpack releases notes. For tomcat configuration options see the Apache Tomcat Buildpack.

The component buildpack for the provided artifact format will contribute a start command to the image.

Note: All three of the Apache Tomcat Buildpack, Executable Jar Buildpack, and DistZip Buildpack may opt-in during detection. However, only one of these buildpacks will actually contribute to the final image. This happens because the artifact type may be unknown during detection, if for example a previous buildpack compiles the artifact.

Example: Building from an Executable JAR

The following command uses Maven to compile an executable JAR and then uses pack to build an image from the JAR.

cd java/maven
./mvnw package
pack build samples/java \
   --path /target/demo-0.0.1-SNAPSHOT.jar
copy to clipboard
Copied!

The resulting application image will be identical to that built in the Building with Maven example.

Inspect the JVM Version

The exact JRE version that was contributed to a given image can be read from the Bill-of-Materials.

Example Inspecting the JRE Version

Given an image named samples/java built from one of examples above, the following command should print the exact version of the installed JRE.

pack inspect-image samples/app --bom | jq '.local[] | select(.name=="jre") | .metadata.version'
copy to clipboard
Copied!

Install a Specific JVM Version

The following environment variable configures the JVM version at build-time.

  • BP_JVM_VERSION
    • Defaults to the latest LTS version at the time of release.
    • Configures a specific JDK or JRE version.
    • Example: Given BP_JVM_VERSION=8 or BP_JVM_VERSION=8.* the buildpack will install the latest patch releases of the Java 8 JDK and JRE.

Configure the JVM at Runtime

The Java Buildpack configures the JVM by setting JAVA_TOOL_OPTIONS in the JVM environment.

The runtime JVM can be configured in two ways:

  1. Buildpack-provided runtime components including the Memory Calculator accept semantically named environment variables which are then used to derive JAVA_TOOL_OPTIONS flags. Examples include:
    • BPL_JVM_HEAD_ROOM
    • BPL_JVM_LOADED_CLASS_COUNT
    • BPL_JVM_THREAD_COUNT
  2. Flags can be set directly at runtime with the JAVA_TOOL_OPTIONS environment variable. User-provided flags will be appended to buildpack-provided flags. If the user and a buildpack set the same flag, user-provided flags take precedence.

See the homepage for the Bellsoft Liberica Buildpack for a full set of configuration options.

Use an Alternative JVM

By default, the Paketo Java buildpack will use the Liberica JVM. The following Paketo JVM buildpacks may be used to substitute alternate JVM implemenations in place of Liberica’s JVM.

JVM Buildpack
Adoptium 1 Paketo Adoptium Buildpack
Alibaba Dragonwell 2 Paketo Alibaba Dragonwell Buildpack
Amazon Corretto 2 Paketo Amazon Corretto Buildpack
Azul Zulu Paketo Azul Zulu Buildpack
BellSoft Liberica Paketo BellSoft Liberica Buildpack - Default
Eclipse OpenJ9 Paketo Eclipse OpenJ9 Buildpack
GraalVM 2 Paketo GraalVM Buildpack
Microsoft OpenJDK 2 Paketo Microsoft OpenJDK Buildpack
SapMachine Paketo SapMachine Buildpack
  1. Only provides JRE and JDK releases for Java 8 and 11, Java 16+ is JDK only
  2. Only provides JDK releases

To use an alternative JVM, you will need to set two --buildpack arguments to pack build, one for the alternative JVM buildpack you’d like to use and one for the Paketo Java buildpack (in that order). This works because while you end up with two JVM buildpacks, the first one, the one you’re specifying will claim the build plan entries so the second one will end up being a noop and doing nothing.

This example will switch in the Azul Zulu buildpack:

pack build samples/jar --buildpack gcr.io/paketo-buildpacks/azul-zulu --buildpack paketo-buildpacks/java`
copy to clipboard
Copied!

There is one drawback to this approach. When using the method above to specify an alternative JVM vendor buildpack, this alternate buildpack ends up running before the CA certs buildpack and therefore traffic from the alternate JVM vendor buildpack won’t trust any additional CA certs. This is not expected to impact many users because JVM buildpacks should reach out to URLs that have a cert signed by a known authority with a CA in the default system truststore.

If you have customized your JVM buildpack to download the JVM from a URL that uses a certificate not signed by a well-known CA, you can workaround this by specifying the CA certs buildpack to run first. This works because while you will end up with the CA certificates buildpack specified twice, the lifecycle is smart enough to drop the second one.

For example:

pack build samples/jar --buildpack paketo-buildpacks/ca-certificates --buildpack gcr.io/paketo-buildpacks/azul-zulu --buildpack paketo-buildpacks/java`
copy to clipboard
Copied!

It does not hurt to use this command for all situations, it is just more verbose and most users can get away without specifying the CA certificates buildpack to be first.

Build an App as a GraalVM Native Image Application

The Paketo Java Native Image Buildpack allows users to create an image containing a GraalVM native image application.

The Java Native Buildpack is a composite buildpack and each step in a build is handled by one of its components. The following docs describe common build configurations. For a full set of configuration options and capabilities see the homepages of the component buildpacks.

Build From Source

The Java Native Image Buildpack supports the same build tools and configuration options as the Java Buildpack. The build must produce an executable jarexecutable jar.

After compiling and packaging, the buildpack will replace provided application source code with the exploded JAR and proceed as described in [Building from an Executable Jar][building-from-an-executable-jar].

Example: Building a Native image with Maven

The following command creates an image from source with maven.

pack build samples/java-native \
  --env BP_NATIVE_IMAGE=true
  --path java/native-image/java-native-image-sample
copy to clipboard
Copied!

Build From an Executable JAR

An application developer may build an image from an exploded executable JAR. Most platforms will automatically extract provided archives.

Example: Building a Native image from an Executable JAR

The following command uses Maven directly to compile an executable JAR and then uses the pack CLI to build an image from the JAR.

cd samples/java/native-image
./mvnw package
pack build samples/java-native \
  --env BP_NATIVE_IMAGE=true
  --path java/native-image/java-native-image-sample/target/demo-0.0.1-SNAPSHOT.jar
copy to clipboard
Copied!

The resulting application image will be identical to that built in the “Building a Native image with Maven” example.

Inspect the Native Image Tools Version

The exact substrate VM version that was contributed to a given image can be read from the Bill-of-Materials.

Example Inspecting the JRE Version

Given an image named samples/java-native built from one of examples above, the following command will print the exact version of the installed substrate VM.

pack inspect-image samples/java-native --bom | jq '.local[] | select(.name=="native-image-svm") | .metadata.version'
copy to clipboard
Copied!

Configure the GraalVM Version

Because GraalVM is evolving rapidly you may on occasion need to, for compatibility reasons, select a sepecific version of the GraalVM and associated tools to use when building an image. This is not a directly configurable option like the JVM version, however, you can pick a specific version by changing the version of the Java Native Image Buildpack you use.

The following table documents the versions available.

GraalVM Version Java Native Image Buildpack Version
21.2 5.5.0
21.1 5.4.0
21.0 5.3.0

For example, to select GraalVM 21.1:

pack build samples/native -e BP_NATIVE_IMAGE=true --buildpack gcr.io/paketo-buildpacks/ca-certificates --buildpack gcr.io/paketo-buildpacks/java-native-image:5.4.0
copy to clipboard
Copied!

Use an Alternative Java Native Image Toolkit

By default, the Paketo Java Native Image buildpack will use the GraalVM Native Image Toolkit. The following Paketo JVM buildpacks may be used to substitute alternate Native Image Toolkit implemenations in place of the default.

JVM Buildpack
Bellsoft Liberica Paketo Bellsoft Liberica Buildpack

To use an alternative Java Native Image Toolkit, you will need to set two --buildpack arguments to pack build, one for the alternative Java Native Image Toolkit buildpack you’d like to use and one for the Paketo Java Native Image buildpack (in that order). This works because while you end up with two Java Native Image Toolkit buildpacks, the first one, the one you’re specifying will claim the build plan entries so the second one will end up being a noop and doing nothing.

This example will switch in the Bellsoft Liberica buildpack:

pack build samples/native-image --buildpack paketo-buildpacks/bellsoft-liberica --buildpack paketo-buildpacks/java-native-image`
copy to clipboard
Copied!

There is one drawback to this approach. When using the method above to specify an alternative Java Native Image Toolkit vendor buildpack, this alternate buildpack ends up running before the CA certs buildpack and therefore traffic from the alternate Java Native Image Toolkit vendor buildpack won’t trust any additional CA certs. This is not expected to impact many users because Java Native Image Toolkit buildpacks should reach out to URLs that have a cert signed by a known authority with a CA in the default system truststore.

If you have customized your Java Native Image Toolkit buildpack to download the Java Native Image Toolkit from a URL that uses a certificate not signed by a well-known CA, you can workaround this by specifying the CA certs buildpack to run first. This works because while you will end up with the CA certificates buildpack specified twice, the lifecycle is smart enough to drop the second one.

For example:

pack build samples/jar --buildpack paketo-buildpacks/ca-certificates --buildpack paketo-buildpacks/bellsoft-liberica --buildpack paketo-buildpacks/java-native-image`
copy to clipboard
Copied!

It does not hurt to use this command for all situations, it is just more verbose and most users can get away without specifying the CA certificates buildpack to be first.

Build a Spring Boot Application

Inspect Spring Boot Application Dependencies

The following command uses pack to list every dependency of a sample application.

pack inspect-image samples/java --bom | jq '.local[] | select(.name=="dependencies") | .metadata.dependencies[].name'
copy to clipboard
Copied!

Disable Spring Boot Auto-Configuration

The Spring Boot Buildpack adds Spring Cloud Bindings to the application class path. Spring Cloud Bindings will auto-configure the application to connect to an external service when a binding of a supported type provides credentials and connection information at runtime. Runtime auto-configuration is enabled by default but can be disabled with the BPL_SPRING_CLOUD_BINDINGS_ENABLED environment variable.

Connect to an APM

The Java Buildpack supports the following APM integrations:

APM integrations are enabled with bindings. If a binding of the correct type is provided at build-time the corresponding java agent will be contributed to the application image. Connection credentials will be read from the binding at runtime.

Example: Connecting to Azure Application Insights

The following command builds an image with the Azure Application Insights Java Agent

pack build samples/java --volume "$(pwd)/java/application-insights/binding:/platform/bindings/application-insights"
copy to clipboard
Copied!

To connect to Azure Applicaiton Insights at runtime a valid Instrumentation Key is required.

echo "<Instrumentation Key>" > java/application-insights/binding/InstrumentationKey
docker run --rm --tty \
  --env SERVICE_BINDING_ROOT=/bindings \
  --volume "$(pwd)/java/application-insights/binding:/bindings/app-insights" \
  samples/java
copy to clipboard
Copied!

Enable Remote Debugging

If BP_DEBUG_ENABLED is set at build-time and BPL_DEBUG_ENABLED is set at runtime the Debug Buildpack will configure the application to accept debugger connections. The debug port defaults to 8000 and can be configured with BPL_DEBUG_PORT at runtime. If BPL_DEBUG_SUSPEND is set at runtime, the JVM will suspend execution until a debugger has attached.

Example: Remote Debugging

The following commands builds a debug-enabled image.

pack build samples/java \
  --path java/jar \
  --env BP_DEBUG_ENABLED=true
copy to clipboard
Copied!

To run the image with the debug port published:

docker run --env BPL_DEBUG_ENABLED=true --publish 8000:8000 samples/java
copy to clipboard
Copied!

Connect your IDE debugger to connect to the published port. Eclipse Remote Debug Configuration

Enable JMX

If BP_JMX_ENABLED is set at build-time and BPL_JMX_ENABLED is set at runtime, the JMX Buildpack will enable JMX. The JMX connector will listen on port 5000 by default. The port can be configured with the BPL_JMX_PORT environment variable at runtime.

Example: Enabling JMX

The following commands builds a JMX enabled image.

pack build samples/java \
  --path java/jar \
  --env BP_JMX_ENABLED=true
copy to clipboard
Copied!

To run the image with the JMX port published:

docker run --env BPL_JMX_ENABLED=true --publish 5000:5000 samples/java
copy to clipboard
Copied!

Connect JConsole to the published port. JConsole

Append Arguments to the App’s Start Command

Additional arguments can be provided to the application using the container CMD. In Kubernetes set CMD using the args field on the container resource.

Example: Setting the Server Port

Execute the following command passes an additional argument to application start command, setting the port to 8081.

docker run --rm --publish 8081:8081 samples/java --server.port=8081
curl -s http://localhost:8081/actuator/health
copy to clipboard
Copied!

Provide a Custom Start Command at Launch

To override the buildpack-provided start command with a custom command, set the container ENTRYPOINT

Example: Starting an Interactive Shell

The following command runs Bash interactively:

docker run --rm --entrypoint bash samples/java
copy to clipboard
Copied!

Execute a Custom Command in the Buildpack-Provided Environment

Every buildpack-generated image contains an executable called the launcher which can be used to execute a custom command in an environment containing buildpack-provided environment variables. The launcher will execute any buildpack provided profile scripts before running to provided command, in order to set environment variables with values that should be calculated dynamically at runtime.

To run a custom start command in the buildpack-provided environment set the ENTRYPOINT to launcher and provide the command using the container CMD.

Example: Inspecting the Buildpack-Provided JAVA_TOOL_OPTIONS The following command will print value of $JAVA_TOOL_OPTIONS set by the buildpack:

docker run --rm --entrypoint launcher samples/java echo 'JAVA_TOOL_OPTIONS: $JAVA_TOOL_OPTIONS'
copy to clipboard
Copied!

Each argument provided to the launcher will be evaluated by the shell prior to execution and the original tokenization will be preserved. Note that, in the example above 'JAVA_TOOL_OPTIONS: $JAVA_TOOL_OPTIONS' is single quoted so that $JAVA_TOOL_OPTIONS is evaluated in the container, rather than by the host shell.

Edit

Last modified: September 21, 2021