The Cloud Foundry Java Buildpack is designed as a collection of components. These components are divided into three types: Containers, Frameworks, and JREs. The buildpack is implemented in Go and follows Cloud Foundry buildpack conventions.
The buildpack operates in two phases:
- Supply Phase (
bin/supply): Detects components, downloads dependencies, and prepares the application - Finalize Phase (
bin/finalize): Configures runtime settings and generates the launch command
Each component type implements a common interface and is processed in a specific order during these phases.
Container components represent the way that an application will be run. Container types range from traditional application servers and servlet containers to simple Java main() method execution.
Responsibilities:
- Detect which container should be used based on application structure
- Download and install the container runtime (e.g., Tomcat, Jetty)
- Transform the application as needed (e.g., extract JARs, configure servers)
- Generate the command that will be executed by Cloud Foundry at runtime
Implementation:
Container components implement the Container interface defined in src/java/containers/container.go:
type Container interface {
Detect() (string, error) // Returns container name if detected, empty string otherwise
Supply() error
Finalize() error
Release() (string, error)
}Container Types:
- Dist Zip: Distributable archives (ZIP, TAR.GZ) with startup scripts
- Groovy: Standalone Groovy scripts
- Java Main: Executable JARs with
Main-Classmanifest entry - Play Framework: Play 2.x applications
- Spring Boot: Spring Boot executable JARs
- Spring Boot CLI: Spring Boot CLI applications
- Tomcat: WAR files deployed to Tomcat
Detection Order: Only a single container component can run an application. Containers are detected in priority order (most specific to least specific):
- Spring Boot
- Spring Boot CLI
- Tomcat
- Groovy
- Play Framework
- Dist Zip
- Java Main
If more than one container matches, the first one wins. If no container can be used, an error will be raised and application staging will fail.
See Also:
- Implementing Containers Guide - Detailed implementation instructions
- Container Documentation - Individual container guides
Framework components represent additional behavior or transformations used when an application is run. Framework types include monitoring agents (New Relic, AppDynamics), security providers (Luna, Contrast), JDBC JARs for bound services, and automatic Spring reconfiguration.
Responsibilities:
- Detect when the framework is required (via environment variables or bound services)
- Download and install framework components (agents, libraries)
- Transform the application (inject dependencies, modify configuration)
- Contribute JVM options (e.g.,
-javaagent, system properties)
Implementation:
Framework components implement the Framework interface defined in src/java/frameworks/framework.go:
type Framework interface {
Detect() (string, error) // Returns framework name if detected, empty string otherwise
Supply() error
Finalize() error
}Framework Categories:
- Monitoring Agents: AppDynamics, New Relic, Dynatrace, Elastic APM
- Security Providers: Luna, Contrast, Seeker, Protect App
- Profilers: JProfiler, YourKit
- JDBC Drivers: PostgreSQL, MariaDB
- Spring Utilities: Auto-reconfiguration, Cloud Connectors
- Debugging Tools: Debug, JMX, JaCoCo
Detection: Any number of framework components can be used when running an application. Frameworks detect independently based on:
- Environment variables (e.g.,
JBP_CONFIG_NEW_RELIC_AGENT) - Bound services (e.g., VCAP_SERVICES with specific tags)
- Application structure (e.g., presence of configuration files)
See Also:
- Implementing Frameworks Guide - Detailed implementation instructions
- Framework Documentation - Individual framework guides
JRE components represent the Java Runtime Environment that will be used when running an application. JRE types include OpenJDK (default), Zulu, GraalVM, IBM JRE, Oracle JRE, and other vendor-specific distributions.
Responsibilities:
- Detect which JRE should be used (via environment variables or configuration)
- Download and install the JRE
- Install JRE components (Memory Calculator, JVMKill agent)
- Set up JAVA_HOME and PATH for runtime
- Resolve JRE-specific JVM options
Implementation:
JRE components implement the JRE interface defined in src/java/jres/jre.go:
type JRE interface {
Name() string
Detect() (bool, error)
Supply() error
Finalize() error
JavaHome() string
Version() string
}Available JREs:
- OpenJDK: Default JRE, always available
- Zulu: Azul Systems OpenJDK distribution
- GraalVM: High-performance JVM with native image support
- IBM JRE: IBM Java Runtime Environment
- Oracle JRE: Oracle Java SE
- SapMachine: SAP's OpenJDK distribution
- Azul Platform Prime (Zing): Low-latency JVM
Detection Order: Only a single JRE component can be used to run an application. JREs are detected in registry order:
- Explicitly configured JRE (via
JBP_CONFIG_COMPONENTS) - OpenJDK (default/fallback)
If more than one JRE can be used, the first match wins. If no JRE is detected, an error will be raised and application deployment will fail.
JRE Components: Each JRE installation includes:
- Memory Calculator: Computes optimal JVM memory settings based on container limits
- JVMKill Agent: Forcibly terminates JVM on OutOfMemoryError
- Profile.d Scripts: Export JAVA_HOME and PATH at runtime
Version Selection: Users can specify Java version via:
# Simple version
cf set-env myapp BP_JAVA_VERSION 17
# Version pattern
cf set-env myapp BP_JAVA_VERSION "21.*"
# Legacy config
cf set-env myapp JBP_CONFIG_OPEN_JDK_JRE '{jre: {version: 11.+}}'See Also:
- Implementing JREs Guide - Detailed implementation instructions
- JRE Documentation - Individual JRE guides
During the supply phase (bin/supply), the buildpack:
- Detects JRE: Finds the appropriate JRE provider
- Installs JRE: Downloads and extracts the Java runtime
- Detects Frameworks: Identifies required frameworks
- Installs Frameworks: Downloads and installs framework components
- Detects Container: Finds the appropriate container type
- Prepares Container: Downloads and configures the container
Components can write to:
$DEPS_DIR/<idx>/: Dependency installation directory$BUILD_DIR/: Application directory (transformed in-place)$CACHE_DIR/: Persistent cache across builds
During the finalize phase (bin/finalize), the buildpack:
- Finalizes JRE: Configures JVM options, memory calculator
- Finalizes Frameworks: Adds agent paths, system properties
- Finalizes Container: Generates launch command
Components can:
- Read installed dependencies from
$DEPS_DIR/<idx>/ - Write runtime scripts to
.profile.d/ - Generate the final launch command
At runtime, Cloud Foundry:
- Sources
.profile.d/*.shscripts (sets JAVA_HOME, JAVA_OPTS) - Executes the launch command generated by the container
- Runs the application with configured JRE and frameworks
Components are registered in src/java/supply/supply.go and src/java/finalize/finalize.go:
// Register JREs
jreRegistry := jres.NewRegistry(jreCtx)
jreRegistry.Register(jres.NewOpenJDKJRE(jreCtx))
jreRegistry.Register(jres.NewZuluJRE(jreCtx))
// ... more JREs
// Register Frameworks
frameworks := []frameworks.Framework{
frameworks.NewNewRelicAgent(frameworkCtx),
frameworks.NewAppDynamicsAgent(frameworkCtx),
// ... more frameworks
}
// Register Containers
containers := []containers.Container{
containers.NewDistZip(containerCtx),
containers.NewGroovy(containerCtx),
containers.NewJavaMain(containerCtx),
// ... more containers
}The buildpack can be configured via:
Buildpack-wide:
BP_LOG_LEVEL: Logging level (DEBUG, INFO, WARNING, ERROR)BP_JAVA_VERSION: Java version to install (e.g., "17", "21.*")
Component-specific:
JBP_CONFIG_COMPONENTS: Override component selectionJBP_CONFIG_<COMPONENT>: Component-specific configuration (JSON/YAML)
Example:
cf set-env myapp BP_JAVA_VERSION 17
cf set-env myapp JBP_CONFIG_NEW_RELIC_AGENT '{enabled: true}'
cf set-env myapp JBP_CONFIG_COMPONENTS '{jres: ["ZuluJRE"]}'Component defaults are defined in config/*.yml:
config/components.yml: Component detection orderconfig/open_jdk_jre.yml: OpenJDK configurationconfig/tomcat.yml: Tomcat configurationconfig/new_relic_agent.yml: New Relic configuration
The buildpack manifest (manifest.yml) defines available dependencies:
dependencies:
- name: openjdk
version: 17.0.13
uri: https://github.com/.../openjdk-17.0.13.tar.gz
sha256: abc123...
cf_stacks: [cflinuxfs4]
- name: tomcat
version: 9.0.95
uri: https://archive.apache.org/.../tomcat-9.0.95.tar.gz
sha256: def456...
cf_stacks: [cflinuxfs4]Dependencies are:
- Downloaded during supply phase
- Cached for subsequent builds
- Verified with SHA256 checksums
The buildpack can be extended by:
- Adding JREs: Implement the
JREinterface and register in supply/finalize - Adding Frameworks: Implement the
Frameworkinterface and register in supply/finalize - Adding Containers: Implement the
Containerinterface and register in supply/finalize - Forking: Create a custom buildpack based on this codebase
See the implementation guides for detailed instructions:
java-buildpack/
├── bin/
│ ├── compile # Main entry point (supply + finalize)
│ ├── supply # Supply phase binary
│ └── finalize # Finalize phase binary
├── config/
│ ├── components.yml # Component registration
│ ├── *.yml # Component configurations
├── docs/
│ ├── ARCHITECTURE.md # Architecture overview
│ ├── DEVELOPING.md # Development guide
│ ├── IMPLEMENTING_JRES.md # JRE implementation
│ ├── IMPLEMENTING_FRAMEWORKS.md # Framework implementation
│ ├── IMPLEMENTING_CONTAINERS.md # Container implementation
│ └── TESTING.md # Testing guide
├── src/java/
│ ├── containers/ # Container implementations
│ ├── frameworks/ # Framework implementations
│ ├── jres/ # JRE implementations
│ ├── resources/ # Embedded default configuration files
│ │ ├── embed.go # Go embed directive for resources
│ │ └── files/ # Default configuration files
│ │ ├── tomcat/conf/ # Tomcat defaults (server.xml, context.xml, logging.properties)
│ │ ├── app_dynamics_agent/ # AppDynamics defaults
│ │ ├── azure_application_insights_agent/ # Azure AI defaults
│ │ ├── luna_security_provider/ # Luna defaults
│ │ ├── new_relic_agent/ # New Relic defaults
│ │ └── protect_app_security_provider/ # ProtectApp defaults
│ ├── supply/ # Supply phase logic
│ └── finalize/ # Finalize phase logic
└── manifest.yml # Dependency manifest
The buildpack includes default configuration files for various components that are embedded at compile time using Go's embed package. These files are located in src/java/resources/files/ and provide sensible defaults for Cloud Foundry deployments.
The src/java/resources/embed.go file uses the //go:embed directive to include all files from the files/ directory into the compiled binary. This approach:
- Eliminates the need for external file dependencies at runtime
- Ensures configuration files are always available
- Allows operators to customize defaults by forking and modifying the source files
To customize the default configuration files:
- Fork the java-buildpack repository
- Modify the configuration files in
src/java/resources/files/ - Build and package your custom buildpack using
./scripts/package.sh - Upload the custom buildpack to your Cloud Foundry foundation
The embedded defaults are applied first, and user-provided configurations (via environment variables, external configuration URLs, or application-bundled files) are layered on top.
- Language: Go 1.21+
- Libraries:
github.com/cloudfoundry/libbuildpack: Core buildpack utilitiesgithub.com/onsi/ginkgo/v2: BDD testing frameworkgithub.com/onsi/gomega: Matcher librarygithub.com/cloudfoundry/switchblade: Integration testing
- Build Tools:
go build: Compile binariesginkgo: Run testsgofmt,goimports: Code formatting
- Architecture Guide - Detailed architecture and patterns
- Development Guide - Building and testing the buildpack
- Testing Guide - Test framework and best practices
- Contributing Guide - Contribution guidelines