From 7daddcf6f736f60ebc4c533f2412e4aa5c9dc9fe Mon Sep 17 00:00:00 2001 From: Arunkumar K Date: Tue, 28 Jul 2026 13:13:50 +0530 Subject: [PATCH 1/3] Java 27 FAT Co-authored-by-AI: IBM Bob --- .github/workflows/CI.yml | 24 ++- .../scripts/ClassVersionChecker.java | 7 +- io.openliberty.java.internal_fat_27/README.md | 101 +++++++++ .../build.gradle | 55 +++++ .../run/jvm.options | 3 + .../run/server.env | 2 + .../io/openliberty/java/internal/TestApp.java | 17 ++ .../java/internal/TestService.java | 195 ++++++++++++++++++ .../java/internal/package-info.java | 10 + .../src/main/java/module-info.java | 18 ++ .../src/main/liberty/config/server.xml | 6 + settings.gradle | 1 + 12 files changed, 428 insertions(+), 11 deletions(-) create mode 100644 io.openliberty.java.internal_fat_27/README.md create mode 100644 io.openliberty.java.internal_fat_27/build.gradle create mode 100644 io.openliberty.java.internal_fat_27/run/jvm.options create mode 100644 io.openliberty.java.internal_fat_27/run/server.env create mode 100644 io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java create mode 100644 io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java create mode 100644 io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java create mode 100644 io.openliberty.java.internal_fat_27/src/main/java/module-info.java create mode 100644 io.openliberty.java.internal_fat_27/src/main/liberty/config/server.xml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9443a50..06d91bd 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - java-version: [ '17', '18', '19', '20', '21', '22', '23', '24', '25','26' ] + java-version: [ '17', '18', '19', '20', '21', '22', '23', '24', '25','26','27-ea' ] runs-on: ubuntu-latest @@ -30,28 +30,34 @@ jobs: run: | echo "java-version=${JAVA_VERSION%-ea}" >> $GITHUB_OUTPUT - # Gradle 8.X no longer uses a default repository for toolchains. - # Ref: https://docs.gradle.org/current/userguide/upgrading_version_7.html#using_automatic_toolchain_downloading_without_having_a_repository_configured - # Always preload toolchain JDK + # Preload the target JDK. setup-java sets JAVA_HOME to the installed JDK; + # build.gradle for versions not yet in Gradle's toolchain matrix (e.g. 27) + # forks javac directly from JAVA_HOME, so the order of the two setup steps + # below matters: preload the target JDK first, then overwrite JAVA_HOME with + # JDK 21 so that Gradle itself runs on a supported version. - name: Preload JDK ${{ matrix.java-version }} + id: preload uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: java-version: ${{ matrix.java-version }} - distribution: 'oracle' + distribution: 'temurin' - # This is the JDK gradle will use since gradle does not always support the -ea version - - name: Set up JDK 17 + # Gradle itself runs on JDK 21; set it up last so JAVA_HOME points here. + # build.gradle forks javac from the preloaded JDK path captured above. + - name: Set up JDK 21 (Gradle runtime) uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 with: java-version: '21' - distribution: 'oracle' - cache: gradle + distribution: 'temurin' + cache: gradle - name: Toolchain debug run: | ./gradlew -q javaToolchains - name: Build application + env: + JDK27: ${{ steps.preload.outputs.path }} run: | ./gradlew io.openliberty.java.internal_fat_${{ steps.strip.outputs.java-version }}:build diff --git a/.github/workflows/scripts/ClassVersionChecker.java b/.github/workflows/scripts/ClassVersionChecker.java index ec08cf4..75b471a 100755 --- a/.github/workflows/scripts/ClassVersionChecker.java +++ b/.github/workflows/scripts/ClassVersionChecker.java @@ -10,8 +10,10 @@ * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ -import java.io.*; -import java.util.*; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; public class ClassVersionChecker { @@ -50,6 +52,7 @@ public static void main(String[] args) throws IOException { majorCodeMap.put("24", 68); majorCodeMap.put("25", 69); majorCodeMap.put("26", 70); + majorCodeMap.put("27", 71); String filename = args[0]; int expected = majorCodeMap.get(args[1]); diff --git a/io.openliberty.java.internal_fat_27/README.md b/io.openliberty.java.internal_fat_27/README.md new file mode 100644 index 0000000..9980464 --- /dev/null +++ b/io.openliberty.java.internal_fat_27/README.md @@ -0,0 +1,101 @@ +# Repo for quick testing of Liberty apps +Especially useful for new Java versions + + +### Build locally + +Requires a JDK 27 installation. From the root of the repository: + +``` +./gradlew io.openliberty.java.internal_fat_27:build +``` + +Gradle resolves the JDK 27 toolchain automatically via its toolchain support. If Gradle cannot find a local JDK 27, point it at one explicitly: + +``` +./gradlew io.openliberty.java.internal_fat_27:build -P"org.gradle.java.installations.fromEnv=JDK27" +``` + +where `JDK27` is an environment variable set to your JDK 27 home: + +``` +(Mac/Unix) export JDK27="/path/to/jdk-27" +(Win DOS) set JDK27="C:\path\to\jdk-27" +(Win PS) $env:JDK27="C:\path\to\jdk-27" +``` + +### When moving to a new release of Java + +Update the `languageVersion` in **build.gradle**, the `appUrl` in the `ext` block, and add new test coverage to **TestService.java**. Make sure **TestApp.java** is in the same directory. + +--- + +## Java 27 JEP Coverage + +This FAT covers only **finalized (non-preview, non-incubator)** JEPs that ship as standard Java API in Java 27. + +--- + +### JEP 527 — Post-Quantum Hybrid Key Exchange for TLS 1.3 +**Link:** https://openjdk.org/jeps/527 + +Java 27 adds three ML-KEM/ECDHE hybrid named groups to the TLS 1.3 stack: `X25519MLKEM768`, `SecP256r1MLKEM768`, and `SecP384r1MLKEM1024`. `X25519MLKEM768` is placed first in the JDK default preference list so existing code benefits automatically without any change. + +**Test:** `testPostQuantumTLS()` +- Obtains `SSLParameters` from `SSLContext.getDefault().getDefaultSSLParameters()` (not `new SSLParameters()`, which returns `null` for `getNamedGroups()`). +- Asserts `X25519MLKEM768` is the first (most preferred) named group. +- Asserts all three hybrid groups are present in the supported set. + +--- + +### JEP 534 — Compact Object Headers by Default +**Link:** https://openjdk.org/jeps/534 + +Java 27 makes compact object headers the default in HotSpot, reducing object header size from 96 bits (12 bytes) to 64 bits (8 bytes) on 64-bit architectures. Controlled by `-XX:+/-UseCompactObjectHeaders`. + +**Test:** `testCompactObjectHeaders()` +- Reads the `UseCompactObjectHeaders` JVM flag via `HotSpotDiagnosticMXBean`. +- Logs `SUCCESS` if the flag is `true` (the Java 27 default). +- Logs a `NOTICE` (non-fatal) if the flag was explicitly disabled — e.g. Liberty sets `-XX:-UseCompactObjectHeaders` for compatibility. +- Skips gracefully on non-HotSpot JVMs where the MBean is absent. + +--- + +### JEP 536 — JFR In-Process Data Redaction +**Link:** https://openjdk.org/jeps/536 + +JFR now redacts sensitive values from built-in startup events (`jdk.InitialSystemProperty`, `jdk.InitialEnvironmentVariable`, `jdk.JVMInformation`) before writing them to a recording. Redaction is controlled by glob filters via `-XX:FlightRecorderOptions:redact-key/redact-argument`; default filters cover common patterns including `*password*`, `*token*`, `*secret*`. This is a JVM-engine feature — there is no Java annotation API. + +**Test:** `testJFRDataRedaction()` +- Starts a JFR recording with `jdk.InitialSystemProperty` enabled, then reads it back via `RecordingFile`. +- Looks for a property key (`jep536.test.password`) that matches the default `*password*` filter. +- Asserts the recorded value is `[REDACTED]`, not the original plaintext — fails hard if plaintext is found. +- Logs a skip notice if the property was not present at JVM startup (the snapshot is taken once at JVM init; runtime `System.setProperty()` is too late). + +> **To exercise the redaction path**, add the following to `run/jvm.options`: +> ``` +> -Djep536.test.password=sup3rS3cr3t! +> ``` + +--- + +## JEPs Excluded (Preview / Incubator) + +| JEP | Title | Status | +|-----|-------|--------| +| 531 | Lazy Constants | Third Preview — excluded | +| 532 | Primitive Types in Patterns, instanceof, and switch | Fifth Preview — excluded | +| 533 | Structured Concurrency | Seventh Preview — excluded | +| 538 | PEM Encodings of Cryptographic Objects | Third Preview — excluded | +| 537 | Vector API | Twelfth Incubator — excluded | +| 523 | Make G1 the Default GC in All Environments | Final but JVM-internal; no testable API surface | + +--- + +## JVM Options (`run/jvm.options`) + +No special flags are required for JEP 527 or JEP 534. To fully exercise JEP 536 redaction, add: + +``` +-Djep536.test.password=sup3rS3cr3t! +``` diff --git a/io.openliberty.java.internal_fat_27/build.gradle b/io.openliberty.java.internal_fat_27/build.gradle new file mode 100644 index 0000000..0efdfba --- /dev/null +++ b/io.openliberty.java.internal_fat_27/build.gradle @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2027 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +apply plugin: 'war' + +description = "Basic Liberty repo" + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +// Gradle 8.5 does not include Java 27 in its toolchain compatibility matrix, +// so the standard java { toolchain { languageVersion = 27 } } block fails. +// Workaround: fork javac directly using $JDK27 if set, otherwise fall back to +// $JAVA_HOME (which actions/setup-java points at the preloaded JDK 27 in CI). +def jdk27Home = System.getenv('JDK27') ?: System.getenv('JAVA_HOME') + +compileJava { + javaCompiler.set(null as JavaCompiler) + options.fork = true + options.forkOptions.executable = "${jdk27Home}/bin/javac" + + doFirst { + options.compilerArgs = [ + '--module-path', classpath.asPath, + '--release', '27' + ] + classpath = files() + options.warnings = true + options.deprecation = true + options.debug = true + options.incremental = false + } +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.1.1' + compileOnly group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' + compileOnly group: 'javax.enterprise', name: 'cdi-api', version: '2.0' +} + +// This is the URL the test application will be available at +ext { + appUrl = 'http://localhost:9080/io.openliberty.java.internal_fat_27/' +} diff --git a/io.openliberty.java.internal_fat_27/run/jvm.options b/io.openliberty.java.internal_fat_27/run/jvm.options new file mode 100644 index 0000000..a52d208 --- /dev/null +++ b/io.openliberty.java.internal_fat_27/run/jvm.options @@ -0,0 +1,3 @@ +# No preview features are used in this FAT (all tested JEPs are finalized). +# Uncomment the line below if preview features are needed in future: +# --enable-preview diff --git a/io.openliberty.java.internal_fat_27/run/server.env b/io.openliberty.java.internal_fat_27/run/server.env new file mode 100644 index 0000000..aa4e019 --- /dev/null +++ b/io.openliberty.java.internal_fat_27/run/server.env @@ -0,0 +1,2 @@ +# Need to update the JAVA_HOME environment variable to point to your Java 27 JDK +JAVA_HOME=/jdk/temurin/jdk27 \ No newline at end of file diff --git a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java new file mode 100644 index 0000000..e6276b3 --- /dev/null +++ b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2027 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.java.internal; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +@ApplicationPath("/") +public class TestApp extends Application { +} diff --git a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java new file mode 100644 index 0000000..cb6f700 --- /dev/null +++ b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2027 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.java.internal; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.management.ManagementFactory; + +import javax.enterprise.context.ApplicationScoped; +import javax.management.MBeanServer; +import javax.net.ssl.SSLParameters; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; + +@Path("/") +@ApplicationScoped +public class TestService { + + private StringWriter sw = new StringWriter(); + + @GET + public String test() { + try { + log(">>> ENTER"); + doTest(); + log("<<< EXIT SUCCESSFUL"); + } catch (Exception e) { + e.printStackTrace(System.out); + e.printStackTrace(new PrintWriter(sw)); + log("<<< EXIT FAILED"); + } + String result = sw.toString(); + sw = new StringWriter(); + return result; + } + + private void doTest() throws Exception { + log("Beginning Java 27 testing"); + testPostQuantumTLS(); // JEP 527 + testCompactObjectHeaders(); // JEP 534 + testJFRDataRedaction(); // JEP 536 + log("Leaving Java 27 testing"); + } + + // JEP 527: Post-Quantum Hybrid Key Exchange for TLS 1.3 + // https://openjdk.org/jeps/527 + // + // Adds three ML-KEM/ECDHE hybrid named groups to the TLS 1.3 stack. + // X25519MLKEM768 is placed first in the default preference list so that + // existing code benefits automatically without any configuration change. + // Verifies the default named-group list from SSLContext carries all three + // hybrid groups and that X25519MLKEM768 is first. + private void testPostQuantumTLS() throws Exception { + log("Beginning JEP 527 testing: Post-Quantum Hybrid Key Exchange for TLS 1.3"); + + // Must use SSLContext to get the populated default list; + // new SSLParameters() is blank and returns null for getNamedGroups(). + SSLParameters params = javax.net.ssl.SSLContext.getDefault().getDefaultSSLParameters(); + String[] namedGroups = params.getNamedGroups(); + + if (namedGroups == null || namedGroups.length == 0) { + throw new Exception("JEP 527 FAILED: default SSLParameters returned no named groups"); + } + log("Default TLS named groups (" + namedGroups.length + "): " + java.util.Arrays.toString(namedGroups)); + + // X25519MLKEM768 must be first (most preferred) + if (!"X25519MLKEM768".equals(namedGroups[0])) { + throw new Exception("JEP 527 FAILED: expected X25519MLKEM768 first, got: " + namedGroups[0]); + } + log("SUCCESS: X25519MLKEM768 is the most preferred named group"); + + // All three hybrid groups must be present + java.util.Set groups = new java.util.HashSet<>(java.util.Arrays.asList(namedGroups)); + for (String hybrid : new String[]{"X25519MLKEM768", "SecP256r1MLKEM768", "SecP384r1MLKEM1024"}) { + if (!groups.contains(hybrid)) { + throw new Exception("JEP 527 FAILED: hybrid group missing from supported set: " + hybrid); + } + log("Hybrid group present: " + hybrid); + } + + log("Leaving JEP 527 testing"); + } + + // JEP 534: Compact Object Headers by Default + // https://openjdk.org/jeps/534 + // + // Makes compact object headers (64 bits / 8 bytes, down from 96 bits / 12 bytes) + // the default on 64-bit HotSpot. Controlled by -XX:+/-UseCompactObjectHeaders. + // Verifies the flag is true via HotSpotDiagnosticMXBean; logs a notice (not a + // hard failure) if it has been explicitly disabled, e.g. by Liberty. + private void testCompactObjectHeaders() throws Exception { + log("Beginning JEP 534 testing: Compact Object Headers by Default"); + + com.sun.management.HotSpotDiagnosticMXBean diagBean = + ManagementFactory.getPlatformMXBean(com.sun.management.HotSpotDiagnosticMXBean.class); + if (diagBean == null) { + log("HotSpotDiagnosticMXBean not available — skipping JEP 534 check (non-HotSpot JVM)"); + return; + } + + // Ensure the MBean is registered before querying + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + javax.management.ObjectName diagName = + new javax.management.ObjectName("com.sun.management:type=HotSpotDiagnostic"); + if (!server.isRegistered(diagName)) { + log("HotSpotDiagnostic MBean not registered — skipping JEP 534 check"); + return; + } + + String flagValue = diagBean.getVMOption("UseCompactObjectHeaders").getValue(); + log("UseCompactObjectHeaders = " + flagValue); + + if ("true".equalsIgnoreCase(flagValue)) { + log("SUCCESS: Compact object headers are active (UseCompactObjectHeaders=true)"); + } else { + log("NOTICE: UseCompactObjectHeaders is not 'true' — may have been overridden via -XX:-UseCompactObjectHeaders"); + } + + log("Leaving JEP 534 testing"); + } + + // JEP 536: JFR In-Process Data Redaction + // https://openjdk.org/jeps/536 + // + // JFR now redacts sensitive values from built-in startup events + // (jdk.InitialSystemProperty, jdk.InitialEnvironmentVariable, jdk.JVMInformation) + // before writing them to a recording. Redaction is driven by glob filters via + // -XX:FlightRecorderOptions:redact-key/redact-argument; default filters cover + // common patterns including *password*, *token*, *secret*. + // + // To exercise the redaction path, start the server with: + // -Djep536.test.password=sup3rS3cr3t! + // jdk.InitialSystemProperty events are captured once at JVM init, so the + // property must be present at startup — runtime System.setProperty() is too late. + private void testJFRDataRedaction() throws Exception { + log("Beginning JEP 536 testing: JFR In-Process Data Redaction"); + + final String sensitiveKey = "jep536.test.password"; // matches default filter *password* + final String sensitiveValue = "sup3rS3cr3t!"; + + java.util.List events = new java.util.ArrayList<>(); + try (Recording rec = new Recording()) { + rec.enable("jdk.InitialSystemProperty"); + rec.start(); + rec.stop(); + java.nio.file.Path tmp = java.nio.file.Files.createTempFile("jep536-", ".jfr"); + try { + rec.dump(tmp); + events.addAll(RecordingFile.readAllEvents(tmp)); + } finally { + java.nio.file.Files.deleteIfExists(tmp); + } + } + + // Find the recorded value for our sensitive key + String recordedValue = null; + for (RecordedEvent e : events) { + if ("jdk.InitialSystemProperty".equals(e.getEventType().getName()) + && sensitiveKey.equals(e.getString("key"))) { + recordedValue = e.getString("value"); + log("jdk.InitialSystemProperty key=" + sensitiveKey + " value=" + recordedValue); + break; + } + } + + if (recordedValue == null) { + log("NOTE: '" + sensitiveKey + "' not in startup snapshot — re-run with -D" + sensitiveKey + "="); + } else if ("[REDACTED]".equals(recordedValue)) { + log("SUCCESS: JEP 536 redacted '" + sensitiveKey + "' in the JFR recording"); + } else if (sensitiveValue.equals(recordedValue)) { + throw new Exception("JEP 536 FAILED: '" + sensitiveKey + "' was recorded in plain text — expected [REDACTED]"); + } else { + log("NOTE: unexpected recorded value for '" + sensitiveKey + "': " + recordedValue); + } + + log("Leaving JEP 536 testing"); + } + + public void log(String msg) { + System.out.println(msg); + sw.append(msg); + sw.append("
"); + } +} diff --git a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java new file mode 100644 index 0000000..edab5db --- /dev/null +++ b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java @@ -0,0 +1,10 @@ +/******************************************************************************* + * Copyright (c) 2027 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.java.internal; diff --git a/io.openliberty.java.internal_fat_27/src/main/java/module-info.java b/io.openliberty.java.internal_fat_27/src/main/java/module-info.java new file mode 100644 index 0000000..3fe870b --- /dev/null +++ b/io.openliberty.java.internal_fat_27/src/main/java/module-info.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2027 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +open module io.openliberty.java.internal.basic_app { + + requires java.ws.rs; + requires java.net.http; + requires java.management; // JEP 534: Compact Object Headers — HotSpotDiagnosticMXBean + requires jdk.management; // JEP 534: com.sun.management.HotSpotDiagnosticMXBean + requires jdk.jfr; // JEP 536: JFR In-Process Data Redaction + +} diff --git a/io.openliberty.java.internal_fat_27/src/main/liberty/config/server.xml b/io.openliberty.java.internal_fat_27/src/main/liberty/config/server.xml new file mode 100644 index 0000000..269f129 --- /dev/null +++ b/io.openliberty.java.internal_fat_27/src/main/liberty/config/server.xml @@ -0,0 +1,6 @@ + + + servlet-4.0 + jaxrs-2.1 + + diff --git a/settings.gradle b/settings.gradle index bf18884..a95a84b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,3 +22,4 @@ include 'io.openliberty.java.internal_fat_23' include 'io.openliberty.java.internal_fat_24' include 'io.openliberty.java.internal_fat_25' include 'io.openliberty.java.internal_fat_26' +include 'io.openliberty.java.internal_fat_27' From f8126a1af33745f8a589b5c17e9f531d343f4e40 Mon Sep 17 00:00:00 2001 From: Arunkumar K Date: Tue, 4 Aug 2026 11:11:15 +0530 Subject: [PATCH 2/3] Review Comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IBM Vault Radar pattern-matched password=sup3rS3cr3t! — a word that looks like a real password assignment. Changed the value as --- .../build.gradle | 7 +-- .../run/jvm.options | 8 ++++ .../io/openliberty/java/internal/TestApp.java | 2 +- .../java/internal/TestService.java | 47 +++++-------------- .../java/internal/package-info.java | 2 +- .../src/main/java/module-info.java | 2 +- 6 files changed, 26 insertions(+), 42 deletions(-) diff --git a/io.openliberty.java.internal_fat_27/build.gradle b/io.openliberty.java.internal_fat_27/build.gradle index 0efdfba..3b58b40 100644 --- a/io.openliberty.java.internal_fat_27/build.gradle +++ b/io.openliberty.java.internal_fat_27/build.gradle @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2027 IBM Corporation and others. + * Copyright (c) 2026 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -15,10 +15,7 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -// Gradle 8.5 does not include Java 27 in its toolchain compatibility matrix, -// so the standard java { toolchain { languageVersion = 27 } } block fails. -// Workaround: fork javac directly using $JDK27 if set, otherwise fall back to -// $JAVA_HOME (which actions/setup-java points at the preloaded JDK 27 in CI). +// TODO revert to using toolchain once gradle supports JDK 27 def jdk27Home = System.getenv('JDK27') ?: System.getenv('JAVA_HOME') compileJava { diff --git a/io.openliberty.java.internal_fat_27/run/jvm.options b/io.openliberty.java.internal_fat_27/run/jvm.options index a52d208..1c358ec 100644 --- a/io.openliberty.java.internal_fat_27/run/jvm.options +++ b/io.openliberty.java.internal_fat_27/run/jvm.options @@ -1,3 +1,11 @@ # No preview features are used in this FAT (all tested JEPs are finalized). # Uncomment the line below if preview features are needed in future: # --enable-preview + +# JEP 534: It is the Java 27 default, but setting it here makes the FAT +# self-contained and immune to any container-level override. +-XX:+UseCompactObjectHeaders + +# JEP 536: required so jdk.InitialSystemProperty captures this key at JVM startup. +# Value is a clearly-labelled test fixture — not a real credential. +-Djep536.test.password=test-fixture-value diff --git a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java index e6276b3..b86df30 100644 --- a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java +++ b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestApp.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2027 IBM Corporation and others. + * Copyright (c) 2026 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at diff --git a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java index cb6f700..233aa26 100644 --- a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java +++ b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/TestService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2027 IBM Corporation and others. + * Copyright (c) 2026 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -55,12 +55,7 @@ private void doTest() throws Exception { // JEP 527: Post-Quantum Hybrid Key Exchange for TLS 1.3 // https://openjdk.org/jeps/527 - // - // Adds three ML-KEM/ECDHE hybrid named groups to the TLS 1.3 stack. - // X25519MLKEM768 is placed first in the default preference list so that - // existing code benefits automatically without any configuration change. - // Verifies the default named-group list from SSLContext carries all three - // hybrid groups and that X25519MLKEM768 is first. + private void testPostQuantumTLS() throws Exception { log("Beginning JEP 527 testing: Post-Quantum Hybrid Key Exchange for TLS 1.3"); @@ -74,13 +69,7 @@ private void testPostQuantumTLS() throws Exception { } log("Default TLS named groups (" + namedGroups.length + "): " + java.util.Arrays.toString(namedGroups)); - // X25519MLKEM768 must be first (most preferred) - if (!"X25519MLKEM768".equals(namedGroups[0])) { - throw new Exception("JEP 527 FAILED: expected X25519MLKEM768 first, got: " + namedGroups[0]); - } - log("SUCCESS: X25519MLKEM768 is the most preferred named group"); - - // All three hybrid groups must be present + // All three hybrid groups must be present; ordering is vendor-dependent java.util.Set groups = new java.util.HashSet<>(java.util.Arrays.asList(namedGroups)); for (String hybrid : new String[]{"X25519MLKEM768", "SecP256r1MLKEM768", "SecP384r1MLKEM1024"}) { if (!groups.contains(hybrid)) { @@ -95,10 +84,10 @@ private void testPostQuantumTLS() throws Exception { // JEP 534: Compact Object Headers by Default // https://openjdk.org/jeps/534 // - // Makes compact object headers (64 bits / 8 bytes, down from 96 bits / 12 bytes) - // the default on 64-bit HotSpot. Controlled by -XX:+/-UseCompactObjectHeaders. - // Verifies the flag is true via HotSpotDiagnosticMXBean; logs a notice (not a - // hard failure) if it has been explicitly disabled, e.g. by Liberty. + // Reduces object header size from 96 bits (12 bytes) to 64 bits (8 bytes) on + // 64-bit HotSpot. -XX:+UseCompactObjectHeaders is set in jvm.options, so this + // test asserts it as a hard failure rather than a soft notice. + // Skips gracefully on non-HotSpot JVMs where HotSpotDiagnosticMXBean is absent. private void testCompactObjectHeaders() throws Exception { log("Beginning JEP 534 testing: Compact Object Headers by Default"); @@ -121,33 +110,23 @@ private void testCompactObjectHeaders() throws Exception { String flagValue = diagBean.getVMOption("UseCompactObjectHeaders").getValue(); log("UseCompactObjectHeaders = " + flagValue); - if ("true".equalsIgnoreCase(flagValue)) { - log("SUCCESS: Compact object headers are active (UseCompactObjectHeaders=true)"); - } else { - log("NOTICE: UseCompactObjectHeaders is not 'true' — may have been overridden via -XX:-UseCompactObjectHeaders"); + if (!"true".equalsIgnoreCase(flagValue)) { + throw new Exception("JEP 534 FAILED: UseCompactObjectHeaders is not 'true' — " + + "expected it to be enabled via -XX:+UseCompactObjectHeaders in jvm.options"); } + log("SUCCESS: Compact object headers are active (UseCompactObjectHeaders=true)"); log("Leaving JEP 534 testing"); } // JEP 536: JFR In-Process Data Redaction // https://openjdk.org/jeps/536 - // - // JFR now redacts sensitive values from built-in startup events - // (jdk.InitialSystemProperty, jdk.InitialEnvironmentVariable, jdk.JVMInformation) - // before writing them to a recording. Redaction is driven by glob filters via - // -XX:FlightRecorderOptions:redact-key/redact-argument; default filters cover - // common patterns including *password*, *token*, *secret*. - // - // To exercise the redaction path, start the server with: - // -Djep536.test.password=sup3rS3cr3t! - // jdk.InitialSystemProperty events are captured once at JVM init, so the - // property must be present at startup — runtime System.setProperty() is too late. + private void testJFRDataRedaction() throws Exception { log("Beginning JEP 536 testing: JFR In-Process Data Redaction"); final String sensitiveKey = "jep536.test.password"; // matches default filter *password* - final String sensitiveValue = "sup3rS3cr3t!"; + final String sensitiveValue = "test-fixture-value"; java.util.List events = new java.util.ArrayList<>(); try (Recording rec = new Recording()) { diff --git a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java index edab5db..9c06698 100644 --- a/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java +++ b/io.openliberty.java.internal_fat_27/src/main/java/io/openliberty/java/internal/package-info.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2027 IBM Corporation and others. + * Copyright (c) 2026 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at diff --git a/io.openliberty.java.internal_fat_27/src/main/java/module-info.java b/io.openliberty.java.internal_fat_27/src/main/java/module-info.java index 3fe870b..b8874ad 100644 --- a/io.openliberty.java.internal_fat_27/src/main/java/module-info.java +++ b/io.openliberty.java.internal_fat_27/src/main/java/module-info.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2027 IBM Corporation and others. + * Copyright (c) 2026 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at From 3661718886cf9060d2d5745fbb37d93af4bfb4ff Mon Sep 17 00:00:00 2001 From: Arunkumar K Date: Wed, 5 Aug 2026 12:35:42 +0530 Subject: [PATCH 3/3] Updqate CI.yml to resolve merge conflict --- .github/workflows/CI.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 06d91bd..dd0ee30 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout project - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Strip -ea off of java-version for build-time use - name: Strip java-version postfix @@ -37,7 +37,7 @@ jobs: # JDK 21 so that Gradle itself runs on a supported version. - name: Preload JDK ${{ matrix.java-version }} id: preload - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' @@ -45,7 +45,7 @@ jobs: # Gradle itself runs on JDK 21; set it up last so JAVA_HOME points here. # build.gradle forks javac from the preloaded JDK path captured above. - name: Set up JDK 21 (Gradle runtime) - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: java-version: '21' distribution: 'temurin' @@ -69,10 +69,10 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: | ./.github/workflows/scripts/repackageApplication.sh io.openliberty.java.internal_fat_${{ steps.strip.outputs.java-version }}/build/libs/io.openliberty.java.internal_fat_${{ steps.strip.outputs.java-version }}.war .jar - + - name: Upload application if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: io.openliberty.java.internal_fat_${{ steps.strip.outputs.java-version }} path: io.openliberty.java.internal_fat_${{ steps.strip.outputs.java-version }}/build/libs/io.openliberty.java.internal_fat_${{ steps.strip.outputs.java-version }}.jar