This starter aims to provide a few custom actuator endpoints:
- GET
/actuator/jars
will list the dependent.jar
files embedded in a Spring Boot application - GET
/actuator/pom
will emit the contents of thepom.xml
file (if the application was built with Maven) - GET
actuator/info
enhanced to provide- a CycloneDX SBOM in JSON format (only if the application packages an sbom.json)
- just the list of dependencies in JSON format (again only if the application packages an sbom.json)
The thinking goes, most developers want to deploy apps to an "app-aware" platform. How might a Spring Boot application register itself and its dependencies with the platform? As well, provide information at runtime about its dependencies.
The first target is Cloud Foundry. But there's nothing that stops us from doing the same on other target runtime platforms (e.g. Kubernetes).
- SDKMan
- JDK 17 or better
- (Optionally) access to a Cloud Foundry foundation with read-only admin credentials
- Your application source's Maven
pom.xml
or Gradlebuild.gradle
file should declare a dependency on Spring Boot 3.2 or better
gh repo clone cf-toolsuite/spring-boot-starter-runtime-metadata
sdk install java 21.0.3-librca
sdk use java 21.0.3-librca
./mvnw clean install
Note: As of
spring-boot-starter-runtime-metadata
0.4.0
, we have a runtime dependency on Java 21 or better. Prior versions have a runtime dependency on Java 17 or better.
Add the following dependency
to your application's pom.xml
file
<dependency>
<groupId>org.cftoolsuite.actuator</groupId>
<artifactId>spring-boot-starter-runtime-metadata</artifactId>
<version>0.3.0</version>
</dependency>
If you want to embed and expose a bill of materials from your artifact, then you'll also want to add this plugin
to your application's pom.xml
file too
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<version>2.8.0</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>makeAggregateBom</goal>
</goals>
</execution>
</executions>
<configuration>
<outputFormat>json</outputFormat>
<outputName>classes/bom</outputName>
</configuration>
</plugin>
Add the following dependency
to your application's build.gradle
file
dependencies {
compile group: 'org.cftoolsuite.actuator', name: 'spring-boot-starter-runtime-metadata', version: '0.3.0'
}
If you want to embed and expose a bill of materials from your artifact, then you'll also want to add this plugin to your application's build.gradle
file too
plugins {
id 'org.cyclonedx.bom' version '1.8.2'
}
tasks.named("cyclonedxBom") {
destination = file("${buildDir}/classes")
}
Note: As of
spring-boot-starter-runtime-metadata
0.4.0
, you will also be able to obtain a software-bill-of-materials from the/actuator/sbom
endpoint, which is now built-in to Spring Boot3.3
.
Add the following dependency
to your application's pom.xml
file
<dependency>
<groupId>org.cftoolsuite.actuator</groupId>
<artifactId>spring-boot-starter-runtime-metadata</artifactId>
<version>0.6.0</version>
</dependency>
If you want to embed and expose a bill of materials from your artifact, then you'll also want to add this plugin
to your application's pom.xml
file too
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
</plugin>
Add the following dependency
to your application's build.gradle
file
dependencies {
compile group: 'org.cftoolsuite.actuator', name: 'spring-boot-starter-runtime-metadata', version: '0.6.0'
}
If you want to embed and expose a bill of materials from your artifact, then you'll also want to add this plugin
to your application's build.gradle
file too
plugins {
id 'org.cyclonedx.bom' version '1.8.2'
}
tasks.named("cyclonedxBom") {
destination = file("${buildDir}/META-INF/sbom")
}
If you use the pack CLI to assemble a container image, then you're probably saying to yourself: "I don't need to add a plugin to my build configuration because pack
will write bill of materials files in various formats to a specific layer that I can later download a copy from".
So, you build your app from source with:
pack build {owner/your-app-name} --path {/path/to/your/app/source} --builder paketobuildpacks/builder-jammy-full
Replace
{owner/your-app-name}
and{/path/to/your/app/source}
with the owner/name of your application and the path to source (i.e., where your build file is) respectively
Verify your image includes bill of materials files in various formats by following these instructions.
Among several sub-directories underneath the layers/sbom/launch
directory, you find:
+- paketo-buildpacks_executable-jar
+- sbom.cdx.json
+- sbom.syft.json
Unfortunately, these files are not available and accessible in the container image at runtime. But what you can do is make a copy of the sbom.cdx.json
file.
Set a variable name.
For applications with dependencies on Spring Boot 3.2, set
SBOM_FILENAME=sbom.json
For applications with dependencies on Spring Boot 3.3 or better, set
SBOM_FILENAME=META-INF/sbom/application.cdx.json
cp -f layers/sbom/launch/paketo-buildpacks_executable-jar/sbom.cdx.json src/main/resources/$SBOM_FILENAME
Then rebuild the container image. (Remember to repeat this process for any change you make to source).
Then make sure to enable the additional contributions to the management.info
endpoint
management:
info:
dependencies:
enabled: true
sbom:
enabled: true
And if you want to expose the /actuator/info
, /actuator/jars
and /actuator/pom
endpoints, you'll need to add (e.g., in application.yml)
endpoints:
web:
exposure:
include: "info,jars,pom"
where
endpoints
above is a sibling of (shares the same indentation as)info
Also, note if you want to expose the /actuator/sbom
endpoint, available since Spring Boot 3.3, you'll need to update the above to be
endpoints:
web:
exposure:
include: "health,info,jars,pom,sbom"
Build your application, then start it up.
Visit the above-mentioned custom actuator endpoints.
So what do you get when you've plumbed everything correctly?
GET /actuator/jars
❯ http :8080/actuator/jars
HTTP/1.1 200 OK
Content-Length: 5438
Content-Type: application/vnd.spring-boot.actuator.v3+json
[
"BOOT-INF/lib/lombok-1.18.30.jar",
"BOOT-INF/lib/jakarta.validation-api-3.0.2.jar",
"BOOT-INF/lib/commons-lang3-3.14.0.jar",
"BOOT-INF/lib/jackson-datatype-jsr310-2.16.1.jar",
"BOOT-INF/lib/jackson-annotations-2.16.1.jar",
"BOOT-INF/lib/jackson-core-2.16.1.jar",
"BOOT-INF/lib/jackson-databind-2.16.1.jar",
"BOOT-INF/lib/jackson-dataformat-csv-2.16.1.jar",
"BOOT-INF/lib/java-uuid-generator-5.0.0.jar",
"BOOT-INF/lib/slf4j-api-2.0.12.jar",
"BOOT-INF/lib/java-cfenv-boot-3.1.5.jar",
"BOOT-INF/lib/java-cfenv-3.1.3.jar",
"BOOT-INF/lib/java-cfenv-jdbc-3.1.5.jar",
"BOOT-INF/lib/spring-boot-3.2.3.jar",
"BOOT-INF/lib/spring-context-6.1.4.jar",
"BOOT-INF/lib/spring-aop-6.1.4.jar",
"BOOT-INF/lib/spring-expression-6.1.4.jar",
"BOOT-INF/lib/json-io-4.19.1.jar",
...
GET /actuator/pom
❯ http :8080/actuator/pom
HTTP/1.1 200 OK
Content-Length: 28052
Content-Type: application/vnd.spring-boot.actuator.v3+json;charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.cftoolsuite.cfapp</groupId>
<artifactId>cf-butler</artifactId>
<version>1.0-SNAPSHOT</version>
<name>cf-butler</name>
<description>My purpose in life is to cleanup stale apps and services on a Cloud Foundry foundation. I can be configured to report on and remove orphaned services and stopped app instances older than a configurable duration. I do many other useful things too.</description>
<scm>
<connection>scm:git:git://github.com/cf-toolsuite/cf-butler.git</connection>
<developerConnection>scm:git:ssh://github.com/cf-toolsuite/cf-butler.git</developerConnection>
<url>https://github.com/cf-toolsuite/cf-butler</url>
</scm>
...
GET /actuator/info
Just a sample of what you can get from the additionally contributed dependencies
section
❯ http :8080/actuator/info | jq .dependencies
[
{
"groupId": "org.projectlombok",
"artifactId": "lombok",
"version": "1.18.30"
},
{
"groupId": "jakarta.validation",
"artifactId": "jakarta.validation-api",
"version": "3.0.2"
},
{
"groupId": "org.apache.commons",
"artifactId": "commons-lang3",
"version": "3.14.0"
},
...
And how you can download a software bill of materials
❯ http :8080/actuator/info | jq .sbom > sbom.json
What if the Java Buildpack could be extended where it would detect if the app to be deployed is a Spring Boot 3.x application? If that is the case, add this dependency to the classpath if not already specified, when assembling the droplet.
- Adapted earlier work by Maciej Walkowiak, here: https://maciejwalkowiak.com/blog/maven-dependencies-spring-boot-actuator-info/.