From 2f6acf80f30f3cf0b00aded10b89b16fae1770d6 Mon Sep 17 00:00:00 2001 From: Peter Paul Bakker Date: Wed, 1 Jul 2026 09:35:06 +0000 Subject: [PATCH 1/2] feat(cf-env): add Spring Boot 4.x support and improve docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add detection tests for SB 4.0 and 4.1 (BOOT-INF/lib, lib/, WEB-INF/lib, MANIFEST.MF) - Update detection criterion to cover both SB 3.x and 4.x - Add version mapping table: SB 3.x → cfenv 3.x, SB 4.x → cfenv 4.x - Clarify cloud profile is activated by the library at runtime, not the buildpack - Add JBP_CONFIG_JAVA_CF_ENV configuration section with enable/disable examples - Document when to disable (manual VCAP_SERVICES handling, unwanted cloud profile, conflicts) --- docs/framework-java-cfenv.md | 42 ++++++++++++++++++++++--- src/java/frameworks/java_cf_env_test.go | 25 +++++++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/docs/framework-java-cfenv.md b/docs/framework-java-cfenv.md index e9bbb31c71..521b93e0fd 100644 --- a/docs/framework-java-cfenv.md +++ b/docs/framework-java-cfenv.md @@ -1,15 +1,22 @@ # Java CfEnv Framework -The Java CfEnv Framework provides the `java-cfenv` library for Spring Boot 3+ applications. This library sets various Spring Boot properties by parsing CloudFoundry variables such as `VCAP_SERVICES`, allowing Spring Boot's autoconfiguration to kick in. +The Java CfEnv Framework provides the `java-cfenv` library for Spring Boot 3.x and 4.x applications. This library sets various Spring Boot properties by parsing CloudFoundry variables such as `VCAP_SERVICES`, allowing Spring Boot's autoconfiguration to kick in. -This is the recommended replacement for Spring AutoReconfiguration library which is deprecated. See the `java-cfenv` repostitory for more detail. +This is the recommended replacement for Spring AutoReconfiguration library which is deprecated. See the `java-cfenv` repository for more detail. -It also sets the 'cloud' profile for Spring Boot applications, as the Spring AutoReconfiguration framework did. +The included `java-cfenv` library activates the `cloud` Spring profile at runtime when `VCAP_SERVICES` is present, as the Spring AutoReconfiguration framework did. The buildpack itself does not set any Spring profile. + +The buildpack selects the appropriate `java-cfenv` version based on the detected Spring Boot major version: + +| Spring Boot | java-cfenv | +|-------------|------------| +| 3.x | 3.x (latest) | +| 4.x | 4.x (latest) | - - + + @@ -17,3 +24,28 @@ It also sets the 'cloud' profile for Spring Boot applications, as the Spring Aut
Detection CriterionExistence of a `Spring-Boot-Version: 3.*` manifest entryNo existing `java-cfenv` library foundExistence of a spring-boot-3.*.jar or spring-boot-4.*.jar in BOOT-INF/lib, WEB-INF/lib, or lib/; or a Spring-Boot-Version: 3.* / Spring-Boot-Version: 4.* entry in META-INF/MANIFEST.MFNo existing java-cfenv library found in the application
Tags
Tags are printed to standard output by the buildpack detect script + +## Configuration + +The framework can be disabled via the `JBP_CONFIG_JAVA_CF_ENV` environment variable: + +```bash +cf set-env JBP_CONFIG_JAVA_CF_ENV '{enabled: false}' +``` + +To re-enable, either set it back to `{enabled: true}` or remove the variable entirely: + +```bash +cf unset-env JBP_CONFIG_JAVA_CF_ENV +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `JBP_CONFIG_JAVA_CF_ENV` | `{enabled: true}` | Enable or disable the framework | + +Note: if `java-cfenv*.jar` is already present in the application, the buildpack skips injection automatically — no need to disable explicitly for that case. + +Disable when: +- The application handles `VCAP_SERVICES` manually with custom binding logic +- The automatic `cloud` profile activation is unwanted +- Another service binding library conflicts with `java-cfenv` \ No newline at end of file diff --git a/src/java/frameworks/java_cf_env_test.go b/src/java/frameworks/java_cf_env_test.go index b307be606f..dbff69a089 100644 --- a/src/java/frameworks/java_cf_env_test.go +++ b/src/java/frameworks/java_cf_env_test.go @@ -68,10 +68,23 @@ var _ = Describe("Java CF Env", func() { }) }) - Context("with Spring Boot 3.x JAR in lib/", func() { + Context("with Spring Boot 4.1.x JAR in BOOT-INF/lib", func() { + BeforeEach(func() { + Expect(os.MkdirAll(filepath.Join(buildDir, "BOOT-INF", "lib"), 0755)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(buildDir, "BOOT-INF", "lib", "spring-boot-4.1.0.jar"), []byte("fake"), 0644)).To(Succeed()) + }) + + It("returns 'Java CF Env'", func() { + name, err := fw.Detect() + Expect(err).NotTo(HaveOccurred()) + Expect(name).To(Equal("Java CF Env")) + }) + }) + + Context("with Spring Boot 4.1.x JAR in lib/", func() { BeforeEach(func() { Expect(os.MkdirAll(filepath.Join(buildDir, "lib"), 0755)).To(Succeed()) - Expect(os.WriteFile(filepath.Join(buildDir, "lib", "spring-boot-3.1.5.jar"), []byte("fake"), 0644)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(buildDir, "lib", "spring-boot-4.1.0.jar"), []byte("fake"), 0644)).To(Succeed()) }) It("returns 'Java CF Env'", func() { @@ -81,10 +94,10 @@ var _ = Describe("Java CF Env", func() { }) }) - Context("with Spring Boot 3.x JAR in WEB-INF/lib", func() { + Context("with Spring Boot 4.1.x JAR in WEB-INF/lib", func() { BeforeEach(func() { Expect(os.MkdirAll(filepath.Join(buildDir, "WEB-INF", "lib"), 0755)).To(Succeed()) - Expect(os.WriteFile(filepath.Join(buildDir, "WEB-INF", "lib", "spring-boot-3.0.0.jar"), []byte("fake"), 0644)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(buildDir, "WEB-INF", "lib", "spring-boot-4.1.0.jar"), []byte("fake"), 0644)).To(Succeed()) }) It("returns 'Java CF Env'", func() { @@ -94,12 +107,12 @@ var _ = Describe("Java CF Env", func() { }) }) - Context("with Spring-Boot-Version: 3.x in META-INF/MANIFEST.MF", func() { + Context("with Spring-Boot-Version: 4.1.x in META-INF/MANIFEST.MF", func() { BeforeEach(func() { Expect(os.MkdirAll(filepath.Join(buildDir, "META-INF"), 0755)).To(Succeed()) Expect(os.WriteFile( filepath.Join(buildDir, "META-INF", "MANIFEST.MF"), - []byte("Manifest-Version: 1.0\nSpring-Boot-Version: 3.2.0\nMain-Class: org.springframework.boot.loader.JarLauncher\n"), + []byte("Manifest-Version: 1.0\nSpring-Boot-Version: 4.1.0\nMain-Class: org.springframework.boot.loader.JarLauncher\n"), 0644, )).To(Succeed()) }) From 11fce93b656076d75cf6fcf909fb0eb803a20e61 Mon Sep 17 00:00:00 2001 From: Peter Paul Bakker Date: Wed, 1 Jul 2026 19:47:39 +0000 Subject: [PATCH 2/2] fix(supply): show manifest default version label in framework install log - frameworkVersionSuffix now shows '(default: X.Y.Z)' instead of '(X.Y.Z)' making clear this is the manifest default, not necessarily the installed version - Reverts java-cfenv DependencyIdentifier back to 'java-cfenv' (no hack needed) - Removes extra Info log from JavaCfEnvFramework.Supply() Result: single 'Installing Java CF Env (default: 3.5.1)' line; actual installed version visible from libbuildpack install line --- src/java/frameworks/java_cf_env.go | 1 - src/java/frameworks/java_cf_env_test.go | 6 ++++++ src/java/supply/supply.go | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/java/frameworks/java_cf_env.go b/src/java/frameworks/java_cf_env.go index 48bf3f91f2..7a70e04cec 100644 --- a/src/java/frameworks/java_cf_env.go +++ b/src/java/frameworks/java_cf_env.go @@ -71,7 +71,6 @@ func (j *JavaCfEnvFramework) Supply() error { } else { j.context.Log.Debug("Resolved Java CF Env version pattern '%s' to %s", versionPattern, resolvedVersion) } - // Install java-cfenv JAR javaCfEnvDir := filepath.Join(j.context.Stager.DepDir(), "java_cf_env") if err := j.context.Installer.InstallDependency(dep, javaCfEnvDir); err != nil { diff --git a/src/java/frameworks/java_cf_env_test.go b/src/java/frameworks/java_cf_env_test.go index dbff69a089..b10f305699 100644 --- a/src/java/frameworks/java_cf_env_test.go +++ b/src/java/frameworks/java_cf_env_test.go @@ -219,6 +219,12 @@ var _ = Describe("Java CF Env", func() { }) }) + Describe("DependencyIdentifier", func() { + It("returns java-cfenv so supply.go can look up the manifest default version", func() { + Expect(fw.DependencyIdentifier()).To(Equal("java-cfenv")) + }) + }) + Describe("Finalize", func() { Context("when the JAR is present", func() { BeforeEach(func() { diff --git a/src/java/supply/supply.go b/src/java/supply/supply.go index afcd8d0943..bc4d156805 100644 --- a/src/java/supply/supply.go +++ b/src/java/supply/supply.go @@ -181,5 +181,5 @@ func (s *Supplier) frameworkVersionSuffix(framework frameworks.Framework) string return "" } - return fmt.Sprintf(" (%s)", dependency.Version) + return fmt.Sprintf(" (default: %s)", dependency.Version) }