Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/container-dist_zip.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The Dist Zip Container allows applications packaged in [`distZip`-style][] to be
</table>
Tags are printed to standard output by the buildpack detect script

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.
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`.

## Configuration
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].
Expand Down
2 changes: 1 addition & 1 deletion docs/container-java_main.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Command line arguments may optionally be configured.
</table>
Tags are printed to standard output by the buildpack detect script

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.
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`.

## Spring Boot

Expand Down
2 changes: 1 addition & 1 deletion docs/container-spring_boot.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Tags are printed to standard output by the buildpack detect script

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

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.
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`.

## CF Tasks

Expand Down
2 changes: 1 addition & 1 deletion docs/container-spring_boot_cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The Spring Boot CLI Container runs one or more Groovy (i.e. `*.groovy`) files us
</table>
Tags are printed to standard output by the buildpack detect script.

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.
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`.

## Configuration
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].
Expand Down
68 changes: 63 additions & 5 deletions docs/framework-java-cfenv.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,77 @@
# 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 Cloud Foundry 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` <a href="https://github.com/pivotal-cf/java-cfenv">repostitory</a> for more detail.
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.

It also sets the 'cloud' profile for Spring Boot applications, as the Spring AutoReconfiguration framework did.
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.

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) |

<table>
<tr>
<td><strong>Detection Criterion</strong></td>
<td>Existence of a `Spring-Boot-Version: 3.*` manifest entry</td>
<td>No existing `java-cfenv` library found</td>
<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>
<td>No existing <tt>java-cfenv</tt> library found in the application</td>
</tr>
<tr>
<td><strong>Tags</strong></td>
<td><tt>java-cf-env=&lt;version&gt;</tt></td>
</tr>
</table>
Tags are printed to standard output by the buildpack detect script

## How it works

The framework is implemented in `src/java/frameworks/java_cf_env.go`:

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).
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.
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.
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.

## Configuration

The framework can be disabled via the `JBP_CONFIG_JAVA_CF_ENV` environment variable:

```bash
cf set-env <app> JBP_CONFIG_JAVA_CF_ENV '{enabled: false}'
cf restage <app>
```

The buildpack only re-reads this variable during staging, so a `cf restage` is required for the change to take effect.

To re-enable, either set it back to `{enabled: true}` or remove the variable entirely:

```bash
cf unset-env <app> JBP_CONFIG_JAVA_CF_ENV
cf restage <app>
```

| 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`

`{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.

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:

| App bundles | Property mapping | `cloud` profile |
|-------------|------------------|-----------------|
| _(nothing — buildpack injects `java-cfenv-all`)_ | yes | yes |
| `java-cfenv-all` | yes | yes (app pins the version) |
| `java-cfenv-boot` | yes | **no** (no `CloudProfileApplicationListener`) |
| `java-cfenv` (core) | **no** (API only) | **no** |

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.
74 changes: 61 additions & 13 deletions docs/spring-auto-reconfiguration-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,45 @@

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

> **Note — the `cloud` Spring profile**
>
> Spring Auto-reconfiguration activated a Spring profile named `cloud`. java-cfenv only
> activates that profile if the `java-cfenv-all` module is on the classpath (it contains
> `CloudProfileApplicationListener`); the `java-cfenv-boot` module does **not**. If your
> application relies on the `cloud` profile — for example `application-cloud.yml` /
> `application-cloud.properties` or `@Profile("cloud")` beans — either use `java-cfenv-all`,
> or activate it explicitly. If the application already sets other active profiles, use
> `SPRING_PROFILES_INCLUDE` to add `cloud` alongside them:
>
> ```bash
> cf set-env <APP> SPRING_PROFILES_INCLUDE cloud # adds 'cloud' to any existing profiles
> cf restage <APP>
> ```
>
> Use `SPRING_PROFILES_ACTIVE=cloud` only if `cloud` should be the sole active profile (it
> replaces any others).

> **Note — controlling the automatic behaviour**
>
> When the application does not bundle java-cfenv itself, the buildpack injects `java-cfenv-all`
> (property mapping **and** `cloud` profile). To scope this:
> - `cf set-env <APP> JBP_CONFIG_JAVA_CF_ENV '{enabled: false}'` (+ `cf restage`) disables the
> **whole** framework — both property mapping and the `cloud` profile. There is no
> `cloud`-profile-only toggle.
> - Because the buildpack backs off when the app already bundles a `java-cfenv*.jar`, bundling your
> own artifact wins: `java-cfenv-boot` = property mapping without the `cloud` profile;
> `java-cfenv` (core) = neither, use the `CfEnv` API directly.
>
> See [Java CfEnv Framework](framework-java-cfenv.md) for details.

> **Note — backwards compatible with buildpack 4.x (java-buildpack 5.0.6+)**
>
> As of java-buildpack **5.0.6**, the buildpack injects `java-cfenv-all` by default, so the `cloud`
> profile activation and the `VCAP_SERVICES` → Spring property mapping behave the same as under
> java-buildpack 4.x. Apps that relied on either under 4.x keep working after upgrading — no
> application change is required for the `cloud`/VCAP behaviour. (Buildpack 5.0.0–5.0.5 shipped the
> bare `java-cfenv` core module, which activated neither; see #1349.)

---

## Table of Contents
Expand Down Expand Up @@ -52,8 +91,9 @@ This guide provides step-by-step instructions for migrating from the deprecated
<!-- Add to your pom.xml -->
<dependency>
<groupId>io.pivotal.cfenv</groupId>
<artifactId>java-cfenv-boot</artifactId>
<version>3.1.4</version>
<artifactId>java-cfenv-all</artifactId>
<!-- match your Spring Boot major: 3.5.1 for Spring Boot 3, 4.0.0 for Spring Boot 4 -->
<version>3.5.1</version>
</dependency>
```

Expand All @@ -62,6 +102,7 @@ This guide provides step-by-step instructions for migrating from the deprecated
- Library reads `VCAP_SERVICES` and sets Spring Boot properties
- Spring Boot autoconfiguration uses these properties
- More transparent and Spring Boot native
- `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

---

Expand Down Expand Up @@ -91,11 +132,11 @@ Check your `pom.xml` or `build.gradle`:

```xml
<dependencies>
<!-- Add this dependency -->
<!-- Add this dependency (match your Spring Boot major: 3.5.1 for Spring Boot 3, 4.0.0 for Spring Boot 4) -->
<dependency>
<groupId>io.pivotal.cfenv</groupId>
<artifactId>java-cfenv-boot</artifactId>
<version>3.1.4</version>
<artifactId>java-cfenv-all</artifactId>
<version>3.5.1</version>
</dependency>
</dependencies>
```
Expand All @@ -104,7 +145,7 @@ Check your `pom.xml` or `build.gradle`:

```groovy
dependencies {
implementation 'io.pivotal.cfenv:java-cfenv-boot:3.1.4'
implementation 'io.pivotal.cfenv:java-cfenv-all:3.5.1' // 4.0.0 for Spring Boot 4
}
```

Expand Down Expand Up @@ -227,7 +268,7 @@ You should see:
```
Java Buildpack v1.x.x | https://github.com/cloudfoundry/java-buildpack
-----> Supplying frameworks...
java-cf-env=3.1.4
java-cf-env=3.5.1
```

---
Expand Down Expand Up @@ -453,16 +494,23 @@ public class CustomConfig {

### Issue: "cloud" profile not active

**Cause**: java-cfenv only activates "cloud" profile on Cloud Foundry

**Solution**: This is expected. Locally, the "cloud" profile won't be active.
**Cause**: The `cloud` profile is activated by `CloudProfileApplicationListener`, which ships
only in the `java-cfenv-all` module. If you migrated with the lighter `java-cfenv-boot` module
instead, it does **not** carry that listener, so the `cloud` profile you had under Spring
Auto-reconfiguration will not be active.

To test cloud profile locally:
**Solution**: Depend on `java-cfenv-all` (as shown in the steps above), or activate the profile
explicitly. If the application already sets other active profiles, add `cloud` alongside them with
`SPRING_PROFILES_INCLUDE`:

```bash
java -jar myapp.jar --spring.profiles.active=cloud
cf set-env <APP> SPRING_PROFILES_INCLUDE cloud # adds 'cloud' to any existing profiles
cf restage <APP>
```

Use `SPRING_PROFILES_ACTIVE=cloud` only if `cloud` should be the sole active profile (it replaces
any others). To activate it locally: `java -jar myapp.jar --spring.profiles.active=cloud`.

---

## Rollback Plan
Expand Down Expand Up @@ -502,7 +550,7 @@ If you encounter migration issues:
## Summary Checklist

- [ ] Verify Spring Boot version (2.1+ required, 3.x recommended)
- [ ] Add `java-cfenv-boot` dependency to `pom.xml` or `build.gradle`
- [ ] Add `java-cfenv-all` dependency to `pom.xml` or `build.gradle` (match your Spring Boot major)
- [ ] Remove Spring Cloud Connectors dependencies (if present)
- [ ] Review and simplify custom service configurations
- [ ] Remove `JBP_CONFIG_SPRING_AUTO_RECONFIGURATION` environment variable
Expand Down
1 change: 0 additions & 1 deletion src/java/frameworks/java_cf_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
31 changes: 25 additions & 6 deletions src/java/frameworks/java_cf_env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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() {
Expand All @@ -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())
})
Expand Down Expand Up @@ -206,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() {
Expand Down
4 changes: 3 additions & 1 deletion src/java/supply/supply.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,7 @@ func (s *Supplier) frameworkVersionSuffix(framework frameworks.Framework) string
return ""
}

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