Skip to content

Commit 60a2e33

Browse files
authored
Merge pull request #1341 from stokpop/fix/cf-env-spring-boot-4x
2 parents b52974a + 78a8680 commit 60a2e33

9 files changed

Lines changed: 156 additions & 30 deletions

docs/container-dist_zip.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The Dist Zip Container allows applications packaged in [`distZip`-style][] to be
1616
</table>
1717
Tags are printed to standard output by the buildpack detect script
1818

19-
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others.
19+
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The [Java CfEnv](framework-java-cfenv.md) framework — the replacement for the deprecated Spring Auto-reconfiguration — activates the `cloud` profile at runtime; you can also add it explicitly with `SPRING_PROFILES_INCLUDE=cloud`.
2020

2121
## Configuration
2222
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].

docs/container-java_main.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Command line arguments may optionally be configured.
1919
</table>
2020
Tags are printed to standard output by the buildpack detect script
2121

22-
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others.
22+
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The [Java CfEnv](framework-java-cfenv.md) framework — the replacement for the deprecated Spring Auto-reconfiguration — activates the `cloud` profile at runtime; you can also add it explicitly with `SPRING_PROFILES_INCLUDE=cloud`.
2323

2424
## Spring Boot
2525

docs/container-spring_boot.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Tags are printed to standard output by the buildpack detect script
1515

1616
The container expects to run the application creating by running [`gradle distZip`][d] in an application built with the Spring Boot Gradle plugin.
1717

18-
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others.
18+
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The [Java CfEnv](framework-java-cfenv.md) framework — the replacement for the deprecated Spring Auto-reconfiguration — activates the `cloud` profile at runtime; you can also add it explicitly with `SPRING_PROFILES_INCLUDE=cloud`.
1919

2020
## CF Tasks
2121

docs/container-spring_boot_cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The Spring Boot CLI Container runs one or more Groovy (i.e. `*.groovy`) files us
1919
</table>
2020
Tags are printed to standard output by the buildpack detect script.
2121

22-
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The Spring Auto-reconfiguration Framework will specify the `cloud` profile in addition to any others.
22+
If the application uses Spring, [Spring profiles][] can be specified by setting the [`SPRING_PROFILES_ACTIVE`][] environment variable. This is automatically detected and used by Spring. The [Java CfEnv](framework-java-cfenv.md) framework — the replacement for the deprecated Spring Auto-reconfiguration — activates the `cloud` profile at runtime; you can also add it explicitly with `SPRING_PROFILES_INCLUDE=cloud`.
2323

2424
## Configuration
2525
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].

docs/framework-java-cfenv.md

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,77 @@
11
# Java CfEnv Framework
2-
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.
2+
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 Cloud Foundry variables such as `VCAP_SERVICES`, allowing Spring Boot's autoconfiguration to kick in.
33

4-
This is the recommended replacement for Spring AutoReconfiguration library which is deprecated. See the `java-cfenv` <a href="https://github.com/pivotal-cf/java-cfenv">repostitory</a> for more detail.
4+
This is the recommended replacement for Spring AutoReconfiguration library which is deprecated. See the `java-cfenv` <a href="https://github.com/pivotal-cf/java-cfenv">repository</a> for more details.
55

6-
It also sets the 'cloud' profile for Spring Boot applications, as the Spring AutoReconfiguration framework did.
6+
The `cloud` Spring profile is activated at runtime by java-cfenv's `CloudProfileApplicationListener`, which ships in the `java-cfenv-all` module. To ensure the profile is active — or to activate it independently of java-cfenv — set it explicitly. Use `SPRING_PROFILES_INCLUDE=cloud` to add `cloud` alongside any other active profiles, or `SPRING_PROFILES_ACTIVE=cloud` to set it as the sole active profile (this replaces any others). The buildpack itself does not set any Spring profile.
7+
8+
The buildpack selects the appropriate `java-cfenv` version based on the detected Spring Boot major version:
9+
10+
| Spring Boot | java-cfenv |
11+
|-------------|------------|
12+
| 3.x | 3.x (latest) |
13+
| 4.x | 4.x (latest) |
714

815
<table>
916
<tr>
1017
<td><strong>Detection Criterion</strong></td>
11-
<td>Existence of a `Spring-Boot-Version: 3.*` manifest entry</td>
12-
<td>No existing `java-cfenv` library found</td>
18+
<td>Existence of a <tt>spring-boot-3.*.jar</tt> or <tt>spring-boot-4.*.jar</tt> in <tt>BOOT-INF/lib</tt>, <tt>WEB-INF/lib</tt>, or <tt>lib/</tt>; or a <tt>Spring-Boot-Version: 3.*</tt> / <tt>Spring-Boot-Version: 4.*</tt> entry in <tt>META-INF/MANIFEST.MF</tt></td>
19+
<td>No existing <tt>java-cfenv</tt> library found in the application</td>
1320
</tr>
1421
<tr>
1522
<td><strong>Tags</strong></td>
1623
<td><tt>java-cf-env=&lt;version&gt;</tt></td>
1724
</tr>
1825
</table>
1926
Tags are printed to standard output by the buildpack detect script
27+
28+
## How it works
29+
30+
The framework is implemented in `src/java/frameworks/java_cf_env.go`:
31+
32+
1. **Detect** — activates only when all of these hold: the framework is enabled (see [Configuration](#configuration)); a Spring Boot 3.x or 4.x marker is found (a `spring-boot-{3,4}.*.jar` under `BOOT-INF/lib`, `WEB-INF/lib`, or `lib/`, or a `Spring-Boot-Version: 3.*` / `4.*` entry in `META-INF/MANIFEST.MF`); and the application does not already bundle a `java-cfenv*.jar` (if it does, the buildpack backs off and uses the application's own copy).
33+
2. **Supply** — selects the java-cfenv version from the detected Spring Boot major (Spring Boot 3 → the manifest's `3.x` line, Spring Boot 4 → the `4.x` line) and installs the `java-cfenv-all` jar into the dependency directory.
34+
3. **Finalize** — appends the installed jar to `CLASSPATH` via a `.profile.d/java_cf_env.sh` script, so it is on the application's runtime classpath.
35+
4. **Runtime** — Spring Boot reads the jar's `META-INF/spring.factories`: the `EnvironmentPostProcessor`s map `VCAP_SERVICES` to Spring properties, and `CloudProfileApplicationListener` (in the `java-cfenv-all` module) activates the `cloud` profile when running in Cloud Foundry.
36+
37+
## Configuration
38+
39+
The framework can be disabled via the `JBP_CONFIG_JAVA_CF_ENV` environment variable:
40+
41+
```bash
42+
cf set-env <app> JBP_CONFIG_JAVA_CF_ENV '{enabled: false}'
43+
cf restage <app>
44+
```
45+
46+
The buildpack only re-reads this variable during staging, so a `cf restage` is required for the change to take effect.
47+
48+
To re-enable, either set it back to `{enabled: true}` or remove the variable entirely:
49+
50+
```bash
51+
cf unset-env <app> JBP_CONFIG_JAVA_CF_ENV
52+
cf restage <app>
53+
```
54+
55+
| Variable | Default | Description |
56+
|----------|---------|-------------|
57+
| `JBP_CONFIG_JAVA_CF_ENV` | `{enabled: true}` | Enable or disable the framework |
58+
59+
Note: if `java-cfenv*.jar` is already present in the application, the buildpack skips injection automatically — no need to disable explicitly for that case.
60+
61+
Disable when:
62+
- The application handles `VCAP_SERVICES` manually with custom binding logic
63+
- The automatic `cloud` profile activation is unwanted
64+
- Another service binding library conflicts with `java-cfenv`
65+
66+
`{enabled: false}` disables the **whole** framework — both the `VCAP_SERVICES` → Spring property mapping and the `cloud` profile activation. There is no option to disable only the `cloud` profile.
67+
68+
For finer control, bundle a java-cfenv artifact in the application yourself. Because the buildpack backs off whenever a `java-cfenv*.jar` is already present, the app's choice wins:
69+
70+
| App bundles | Property mapping | `cloud` profile |
71+
|-------------|------------------|-----------------|
72+
| _(nothing — buildpack injects `java-cfenv-all`)_ | yes | yes |
73+
| `java-cfenv-all` | yes | yes (app pins the version) |
74+
| `java-cfenv-boot` | yes | **no** (no `CloudProfileApplicationListener`) |
75+
| `java-cfenv` (core) | **no** (API only) | **no** |
76+
77+
So an app can include `java-cfenv-boot` to keep property mapping without the `cloud` profile, or the bare `java-cfenv` core to opt out of all automatic behaviour and use the `CfEnv` API directly.

docs/spring-auto-reconfiguration-migration.md

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,45 @@
22

33
This guide provides step-by-step instructions for migrating from the deprecated **Spring Auto-reconfiguration** framework to **java-cfenv**.
44

5+
> **Note — the `cloud` Spring profile**
6+
>
7+
> Spring Auto-reconfiguration activated a Spring profile named `cloud`. java-cfenv only
8+
> activates that profile if the `java-cfenv-all` module is on the classpath (it contains
9+
> `CloudProfileApplicationListener`); the `java-cfenv-boot` module does **not**. If your
10+
> application relies on the `cloud` profile — for example `application-cloud.yml` /
11+
> `application-cloud.properties` or `@Profile("cloud")` beans — either use `java-cfenv-all`,
12+
> or activate it explicitly. If the application already sets other active profiles, use
13+
> `SPRING_PROFILES_INCLUDE` to add `cloud` alongside them:
14+
>
15+
> ```bash
16+
> cf set-env <APP> SPRING_PROFILES_INCLUDE cloud # adds 'cloud' to any existing profiles
17+
> cf restage <APP>
18+
> ```
19+
>
20+
> Use `SPRING_PROFILES_ACTIVE=cloud` only if `cloud` should be the sole active profile (it
21+
> replaces any others).
22+
23+
> **Note — controlling the automatic behaviour**
24+
>
25+
> When the application does not bundle java-cfenv itself, the buildpack injects `java-cfenv-all`
26+
> (property mapping **and** `cloud` profile). To scope this:
27+
> - `cf set-env <APP> JBP_CONFIG_JAVA_CF_ENV '{enabled: false}'` (+ `cf restage`) disables the
28+
> **whole** framework — both property mapping and the `cloud` profile. There is no
29+
> `cloud`-profile-only toggle.
30+
> - Because the buildpack backs off when the app already bundles a `java-cfenv*.jar`, bundling your
31+
> own artifact wins: `java-cfenv-boot` = property mapping without the `cloud` profile;
32+
> `java-cfenv` (core) = neither, use the `CfEnv` API directly.
33+
>
34+
> See [Java CfEnv Framework](framework-java-cfenv.md) for details.
35+
36+
> **Note — backwards compatible with buildpack 4.x (java-buildpack 5.0.6+)**
37+
>
38+
> As of java-buildpack **5.0.6**, the buildpack injects `java-cfenv-all` by default, so the `cloud`
39+
> profile activation and the `VCAP_SERVICES` → Spring property mapping behave the same as under
40+
> java-buildpack 4.x. Apps that relied on either under 4.x keep working after upgrading — no
41+
> application change is required for the `cloud`/VCAP behaviour. (Buildpack 5.0.0–5.0.5 shipped the
42+
> bare `java-cfenv` core module, which activated neither; see #1349.)
43+
544
---
645
746
## Table of Contents
@@ -52,8 +91,9 @@ This guide provides step-by-step instructions for migrating from the deprecated
5291
<!-- Add to your pom.xml -->
5392
<dependency>
5493
<groupId>io.pivotal.cfenv</groupId>
55-
<artifactId>java-cfenv-boot</artifactId>
56-
<version>3.1.4</version>
94+
<artifactId>java-cfenv-all</artifactId>
95+
<!-- match your Spring Boot major: 3.5.1 for Spring Boot 3, 4.0.0 for Spring Boot 4 -->
96+
<version>3.5.1</version>
5797
</dependency>
5898
```
5999
@@ -62,6 +102,7 @@ This guide provides step-by-step instructions for migrating from the deprecated
62102
- Library reads `VCAP_SERVICES` and sets Spring Boot properties
63103
- Spring Boot autoconfiguration uses these properties
64104
- More transparent and Spring Boot native
105+
- `java-cfenv-all` bundles the property post-processors **and** the `cloud` profile listener (`CloudProfileApplicationListener`); use the lighter `java-cfenv-boot` instead only if you do not rely on the `cloud` profile
65106
66107
---
67108
@@ -91,11 +132,11 @@ Check your `pom.xml` or `build.gradle`:
91132
92133
```xml
93134
<dependencies>
94-
<!-- Add this dependency -->
135+
<!-- Add this dependency (match your Spring Boot major: 3.5.1 for Spring Boot 3, 4.0.0 for Spring Boot 4) -->
95136
<dependency>
96137
<groupId>io.pivotal.cfenv</groupId>
97-
<artifactId>java-cfenv-boot</artifactId>
98-
<version>3.1.4</version>
138+
<artifactId>java-cfenv-all</artifactId>
139+
<version>3.5.1</version>
99140
</dependency>
100141
</dependencies>
101142
```
@@ -104,7 +145,7 @@ Check your `pom.xml` or `build.gradle`:
104145
105146
```groovy
106147
dependencies {
107-
implementation 'io.pivotal.cfenv:java-cfenv-boot:3.1.4'
148+
implementation 'io.pivotal.cfenv:java-cfenv-all:3.5.1' // 4.0.0 for Spring Boot 4
108149
}
109150
```
110151
@@ -227,7 +268,7 @@ You should see:
227268
```
228269
Java Buildpack v1.x.x | https://github.com/cloudfoundry/java-buildpack
229270
-----> Supplying frameworks...
230-
java-cf-env=3.1.4
271+
java-cf-env=3.5.1
231272
```
232273
233274
---
@@ -453,16 +494,23 @@ public class CustomConfig {
453494
454495
### Issue: "cloud" profile not active
455496
456-
**Cause**: java-cfenv only activates "cloud" profile on Cloud Foundry
457-
458-
**Solution**: This is expected. Locally, the "cloud" profile won't be active.
497+
**Cause**: The `cloud` profile is activated by `CloudProfileApplicationListener`, which ships
498+
only in the `java-cfenv-all` module. If you migrated with the lighter `java-cfenv-boot` module
499+
instead, it does **not** carry that listener, so the `cloud` profile you had under Spring
500+
Auto-reconfiguration will not be active.
459501
460-
To test cloud profile locally:
502+
**Solution**: Depend on `java-cfenv-all` (as shown in the steps above), or activate the profile
503+
explicitly. If the application already sets other active profiles, add `cloud` alongside them with
504+
`SPRING_PROFILES_INCLUDE`:
461505
462506
```bash
463-
java -jar myapp.jar --spring.profiles.active=cloud
507+
cf set-env <APP> SPRING_PROFILES_INCLUDE cloud # adds 'cloud' to any existing profiles
508+
cf restage <APP>
464509
```
465510
511+
Use `SPRING_PROFILES_ACTIVE=cloud` only if `cloud` should be the sole active profile (it replaces
512+
any others). To activate it locally: `java -jar myapp.jar --spring.profiles.active=cloud`.
513+
466514
---
467515
468516
## Rollback Plan
@@ -502,7 +550,7 @@ If you encounter migration issues:
502550
## Summary Checklist
503551
504552
- [ ] Verify Spring Boot version (2.1+ required, 3.x recommended)
505-
- [ ] Add `java-cfenv-boot` dependency to `pom.xml` or `build.gradle`
553+
- [ ] Add `java-cfenv-all` dependency to `pom.xml` or `build.gradle` (match your Spring Boot major)
506554
- [ ] Remove Spring Cloud Connectors dependencies (if present)
507555
- [ ] Review and simplify custom service configurations
508556
- [ ] Remove `JBP_CONFIG_SPRING_AUTO_RECONFIGURATION` environment variable

src/java/frameworks/java_cf_env.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ func (j *JavaCfEnvFramework) Supply() error {
7171
} else {
7272
j.context.Log.Debug("Resolved Java CF Env version pattern '%s' to %s", versionPattern, resolvedVersion)
7373
}
74-
7574
// Install java-cfenv JAR
7675
javaCfEnvDir := filepath.Join(j.context.Stager.DepDir(), "java_cf_env")
7776
if err := j.context.Installer.InstallDependency(dep, javaCfEnvDir); err != nil {

src/java/frameworks/java_cf_env_test.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,23 @@ var _ = Describe("Java CF Env", func() {
6868
})
6969
})
7070

71-
Context("with Spring Boot 3.x JAR in lib/", func() {
71+
Context("with Spring Boot 4.1.x JAR in BOOT-INF/lib", func() {
72+
BeforeEach(func() {
73+
Expect(os.MkdirAll(filepath.Join(buildDir, "BOOT-INF", "lib"), 0755)).To(Succeed())
74+
Expect(os.WriteFile(filepath.Join(buildDir, "BOOT-INF", "lib", "spring-boot-4.1.0.jar"), []byte("fake"), 0644)).To(Succeed())
75+
})
76+
77+
It("returns 'Java CF Env'", func() {
78+
name, err := fw.Detect()
79+
Expect(err).NotTo(HaveOccurred())
80+
Expect(name).To(Equal("Java CF Env"))
81+
})
82+
})
83+
84+
Context("with Spring Boot 4.1.x JAR in lib/", func() {
7285
BeforeEach(func() {
7386
Expect(os.MkdirAll(filepath.Join(buildDir, "lib"), 0755)).To(Succeed())
74-
Expect(os.WriteFile(filepath.Join(buildDir, "lib", "spring-boot-3.1.5.jar"), []byte("fake"), 0644)).To(Succeed())
87+
Expect(os.WriteFile(filepath.Join(buildDir, "lib", "spring-boot-4.1.0.jar"), []byte("fake"), 0644)).To(Succeed())
7588
})
7689

7790
It("returns 'Java CF Env'", func() {
@@ -81,10 +94,10 @@ var _ = Describe("Java CF Env", func() {
8194
})
8295
})
8396

84-
Context("with Spring Boot 3.x JAR in WEB-INF/lib", func() {
97+
Context("with Spring Boot 4.1.x JAR in WEB-INF/lib", func() {
8598
BeforeEach(func() {
8699
Expect(os.MkdirAll(filepath.Join(buildDir, "WEB-INF", "lib"), 0755)).To(Succeed())
87-
Expect(os.WriteFile(filepath.Join(buildDir, "WEB-INF", "lib", "spring-boot-3.0.0.jar"), []byte("fake"), 0644)).To(Succeed())
100+
Expect(os.WriteFile(filepath.Join(buildDir, "WEB-INF", "lib", "spring-boot-4.1.0.jar"), []byte("fake"), 0644)).To(Succeed())
88101
})
89102

90103
It("returns 'Java CF Env'", func() {
@@ -94,12 +107,12 @@ var _ = Describe("Java CF Env", func() {
94107
})
95108
})
96109

97-
Context("with Spring-Boot-Version: 3.x in META-INF/MANIFEST.MF", func() {
110+
Context("with Spring-Boot-Version: 4.1.x in META-INF/MANIFEST.MF", func() {
98111
BeforeEach(func() {
99112
Expect(os.MkdirAll(filepath.Join(buildDir, "META-INF"), 0755)).To(Succeed())
100113
Expect(os.WriteFile(
101114
filepath.Join(buildDir, "META-INF", "MANIFEST.MF"),
102-
[]byte("Manifest-Version: 1.0\nSpring-Boot-Version: 3.2.0\nMain-Class: org.springframework.boot.loader.JarLauncher\n"),
115+
[]byte("Manifest-Version: 1.0\nSpring-Boot-Version: 4.1.0\nMain-Class: org.springframework.boot.loader.JarLauncher\n"),
103116
0644,
104117
)).To(Succeed())
105118
})
@@ -206,6 +219,12 @@ var _ = Describe("Java CF Env", func() {
206219
})
207220
})
208221

222+
Describe("DependencyIdentifier", func() {
223+
It("returns java-cfenv so supply.go can look up the manifest default version", func() {
224+
Expect(fw.DependencyIdentifier()).To(Equal("java-cfenv"))
225+
})
226+
})
227+
209228
Describe("Finalize", func() {
210229
Context("when the JAR is present", func() {
211230
BeforeEach(func() {

src/java/supply/supply.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,7 @@ func (s *Supplier) frameworkVersionSuffix(framework frameworks.Framework) string
181181
return ""
182182
}
183183

184-
return fmt.Sprintf(" (%s)", dependency.Version)
184+
// "manifest default" — the default version listed in manifest.yml, not necessarily what
185+
// gets installed (e.g. java-cfenv resolves to a Spring-Boot-major-specific version).
186+
return fmt.Sprintf(" (manifest default: %s)", dependency.Version)
185187
}

0 commit comments

Comments
 (0)