Skip to content

Commit 7b46997

Browse files
authored
Merge pull request cloudfoundry#1288 from stokpop/issue-1264-base-jre-refactor
refactor: extract BaseJRE to eliminate duplication across standard JRE providers
2 parents 3548d1b + bf158c3 commit 7b46997

14 files changed

Lines changed: 720 additions & 1349 deletions

adoption-migration-details.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,30 @@ There are two main aspects to consider when migrating to the Go-based Java Build
142142
2. Plan migration before EOL dates
143143
3. Monitor EOL announcements for your versions
144144

145-
## Additional Resources
145+
## Behavioral Differences vs Ruby Buildpack
146+
147+
This section documents Go buildpack behaviors that differ from the Ruby buildpack, including new features and known fixes applied during the migration.
148+
149+
### `JAVA_HOME` is now set during staging
150+
151+
The Ruby buildpack never set `JAVA_HOME`. The Go buildpack writes it as an env file (sourced between supply steps) and in `profile.d/java.sh` at runtime. This is a **positive new feature** — apps and tasks that previously hard-coded `.java-buildpack/open_jdk_jre/bin/java` can now use `$JAVA_HOME/bin/java` instead.
152+
153+
See also: [issue #1151](https://github.com/cloudfoundry/java-buildpack/issues/1151).
154+
155+
### Tomcat version auto-selection based on Java version
156+
157+
The Ruby buildpack always used the manifest default (`9.+`) unless the user explicitly set a Tomcat version. The Go buildpack adds a new convenience feature: when `JAVA_HOME` is available and readable it auto-selects:
158+
159+
- **Java 11+** → Tomcat 10.x (Jakarta EE 9+)
160+
- **Java ≤ 10** → Tomcat 9.x (Java EE 8)
161+
162+
When Java version detection fails (e.g. unreadable `release` file, non-standard JRE layout), the buildpack falls back to `DefaultVersion("tomcat")` from the manifest — matching the Ruby buildpack's behaviour in all cases. An explicitly configured `JBP_CONFIG_TOMCAT` version is always honoured.
163+
164+
### `-XX:ActiveProcessorCount` is HotSpot-only
165+
166+
The Ruby buildpack only added `-XX:ActiveProcessorCount=$(nproc)` in the OpenJDK-like JRE. The Go buildpack matches this: only HotSpot-based JREs (OpenJDK, Oracle, SapMachine, Zulu, GraalVM) receive this flag. IBM JRE continues to use `-Xtune:virtualized -Xshareclasses:none` as in Ruby. Note that IBM OpenJ9 does support `-XX:ActiveProcessorCount` (see [OpenJ9 docs](https://eclipse.dev/openj9/docs/xxactiveprocessorcount/)), but it is deliberately omitted here to stay compatible with the Ruby buildpack behaviour.
167+
168+
146169

147170
- [OpenRewrite: JavaxMigrationToJakarta Recipe](https://docs.openrewrite.org/recipes/java/migrate/jakarta/javaxmigrationtojakarta)
148171
- [Apache Tomcat Jakarta EE Migration Tool](https://github.com/apache/tomcat-jakartaee-migration)

src/java/common/context.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,7 @@ func DetermineJavaVersion(javaHome string) (int, error) {
6666
releaseFile := filepath.Join(javaHome, "release")
6767
content, err := os.ReadFile(releaseFile)
6868
if err != nil {
69-
// Default to Java 17 if release file is missing
70-
if os.IsNotExist(err) {
71-
return 17, nil
72-
}
73-
return 0, fmt.Errorf("failed to read release file: %w", err)
69+
return 17, fmt.Errorf("failed to read release file: %w", err)
7470
}
7571

7672
// Parse JAVA_VERSION from release file
@@ -103,7 +99,7 @@ func DetermineJavaVersion(javaHome string) (int, error) {
10399
}
104100
}
105101

106-
return 0, fmt.Errorf("unable to parse Java version from release file")
102+
return 17, fmt.Errorf("unable to parse Java version from release file")
107103
}
108104

109105
// GetJavaMajorVersion returns the Java major version from the JAVA_HOME environment variable.

src/java/containers/tomcat.go

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,45 +63,22 @@ func (t *TomcatContainer) Supply() error {
6363
}
6464

6565
if javaHome != "" {
66-
javaMajorVersion, versionErr := common.DetermineJavaVersion(javaHome)
67-
if versionErr == nil {
68-
tomcatVersion := DetermineTomcatVersion(t.config.Tomcat.Version)
69-
t.context.Log.Debug("Detected Java major version: %d", javaMajorVersion)
70-
71-
// Select Tomcat version pattern based on Java version
72-
var versionPattern string
73-
if tomcatVersion == "" {
74-
t.context.Log.Info("Tomcat version not specified")
75-
if javaMajorVersion >= 11 {
76-
// Java 11+: Use Tomcat 10.x (Jakarta EE 9+)
77-
versionPattern = "10.x"
78-
t.context.Log.Info("Using Tomcat 10.x for Java %d", javaMajorVersion)
79-
} else {
80-
// Java 8-10: Use Tomcat 9.x (Java EE 8)
81-
versionPattern = "9.x"
82-
t.context.Log.Info("Using Tomcat 9.x for Java %d", javaMajorVersion)
83-
}
84-
} else {
85-
versionPattern = tomcatVersion
86-
t.context.Log.Info("Using Tomcat %s for Java %d", versionPattern, javaMajorVersion)
87-
}
88-
89-
if strings.HasPrefix(versionPattern, "10.") && javaMajorVersion < 11 {
90-
return fmt.Errorf("Tomcat 10.x requires Java 11+, but Java %d detected", javaMajorVersion)
91-
}
66+
versionPattern, versionErr := SelectTomcatVersionPattern(javaHome, DetermineTomcatVersion(t.config.Tomcat.Version))
67+
if versionErr != nil {
68+
return versionErr
69+
}
9270

93-
// Resolve the version pattern to actual version using libbuildpack
71+
if versionPattern != "" {
9472
allVersions := t.context.Manifest.AllDependencyVersions("tomcat")
9573
resolvedVersion, err := libbuildpack.FindMatchingVersion(versionPattern, allVersions)
9674
if err != nil {
9775
return fmt.Errorf("tomcat version resolution error for pattern %q: %w", versionPattern, err)
9876
}
99-
10077
dep.Name = "tomcat"
10178
dep.Version = resolvedVersion
10279
t.context.Log.Debug("Resolved Tomcat version pattern '%s' to %s", versionPattern, resolvedVersion)
10380
} else {
104-
t.context.Log.Warning("Unable to determine Java version: %s", versionErr.Error())
81+
t.context.Log.Warning("Unable to determine Java version from JAVA_HOME, falling back to manifest default Tomcat version")
10582
}
10683
}
10784

@@ -462,7 +439,37 @@ func getKeys(m map[string]string) []string {
462439
return keys
463440
}
464441

465-
// DetermineTomcatVersion determines the version of the tomcat
442+
// SelectTomcatVersionPattern determines the Tomcat version pattern to use based on the
443+
// detected Java version and any user-configured Tomcat version.
444+
// Returns ("", nil) when Java version cannot be determined — the caller should fall back
445+
// to the manifest default (matching Ruby buildpack behaviour).
446+
func SelectTomcatVersionPattern(javaHome, configVersion string) (string, error) {
447+
if javaHome == "" {
448+
return "", nil
449+
}
450+
451+
javaMajorVersion, err := common.DetermineJavaVersion(javaHome)
452+
if err != nil {
453+
if configVersion != "" {
454+
return configVersion, nil
455+
}
456+
return "", nil
457+
}
458+
459+
if configVersion != "" {
460+
if strings.HasPrefix(configVersion, "10.") && javaMajorVersion < 11 {
461+
return "", fmt.Errorf("Tomcat 10.x requires Java 11+, but Java %d detected", javaMajorVersion)
462+
}
463+
return configVersion, nil
464+
}
465+
466+
if javaMajorVersion >= 11 {
467+
return "10.x", nil
468+
}
469+
return "9.x", nil
470+
}
471+
472+
466473
// based on the JBP_CONFIG_TOMCAT field from manifest.
467474
// It looks for a tomcat block with a version of the form "<major>.+" (e.g. "9.+", "10.+", "10.1.+").
468475
// Returns the pattern with "+" replaced by "*" (e.g. "9.*", "10.*", "10.1.*") so libbuildpack can resolve it.

src/java/containers/tomcat_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,62 @@ var _ = Describe("Tomcat Container", func() {
197197
})
198198
})
199199

200+
Describe("SelectTomcatVersionPattern", func() {
201+
var javaHome string
202+
203+
BeforeEach(func() {
204+
var err error
205+
javaHome, err = os.MkdirTemp("", "javahome")
206+
Expect(err).NotTo(HaveOccurred())
207+
})
208+
209+
AfterEach(func() {
210+
os.RemoveAll(javaHome)
211+
})
212+
213+
writeReleaseFile := func(content string) {
214+
err := os.WriteFile(filepath.Join(javaHome, "release"), []byte(content), 0644)
215+
Expect(err).NotTo(HaveOccurred())
216+
}
217+
218+
Context("when release file is missing", func() {
219+
It("returns empty pattern to fall back to manifest default, not assume Java 17", func() {
220+
pattern, err := containers.SelectTomcatVersionPattern(javaHome, "")
221+
Expect(err).NotTo(HaveOccurred())
222+
Expect(pattern).To(Equal(""))
223+
})
224+
225+
It("still honours an explicitly configured tomcat version", func() {
226+
pattern, err := containers.SelectTomcatVersionPattern(javaHome, "9.*")
227+
Expect(err).NotTo(HaveOccurred())
228+
Expect(pattern).To(Equal("9.*"))
229+
})
230+
})
231+
232+
Context("happy path version selection", func() {
233+
It("selects Tomcat 10.x for Java 11+", func() {
234+
writeReleaseFile("JAVA_VERSION=\"11.0.20\"\n")
235+
pattern, err := containers.SelectTomcatVersionPattern(javaHome, "")
236+
Expect(err).NotTo(HaveOccurred())
237+
Expect(pattern).To(Equal("10.x"))
238+
})
239+
240+
It("selects Tomcat 9.x for Java 8", func() {
241+
writeReleaseFile("JAVA_VERSION=\"1.8.0_372\"\n")
242+
pattern, err := containers.SelectTomcatVersionPattern(javaHome, "")
243+
Expect(err).NotTo(HaveOccurred())
244+
Expect(pattern).To(Equal("9.x"))
245+
})
246+
247+
It("errors when Tomcat 10.x is requested but Java 8 detected", func() {
248+
writeReleaseFile("JAVA_VERSION=\"1.8.0_372\"\n")
249+
_, err := containers.SelectTomcatVersionPattern(javaHome, "10.*")
250+
Expect(err).To(HaveOccurred())
251+
Expect(err.Error()).To(ContainSubstring("Java 11+"))
252+
})
253+
})
254+
})
255+
200256
Describe("determineTomcatVersion", func() {
201257
It("returns empty string when JBP_CONFIG_TOMCAT is empty", func() {
202258
v := containers.DetermineTomcatVersion("")

0 commit comments

Comments
 (0)