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
26 changes: 16 additions & 10 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.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

Expand All @@ -63,7 +69,7 @@ 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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/scripts/ClassVersionChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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]);
Expand Down
101 changes: 101 additions & 0 deletions io.openliberty.java.internal_fat_27/README.md
Original file line number Diff line number Diff line change
@@ -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!
```
52 changes: 52 additions & 0 deletions io.openliberty.java.internal_fat_27/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* 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
* 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'
}

// TODO revert to using toolchain once gradle supports JDK 27
def jdk27Home = System.getenv('JDK27') ?: System.getenv('JAVA_HOME')
Comment thread
KyleAure marked this conversation as resolved.

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/'
}
11 changes: 11 additions & 0 deletions io.openliberty.java.internal_fat_27/run/jvm.options
Original file line number Diff line number Diff line change
@@ -0,0 +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
Comment thread
KyleAure marked this conversation as resolved.

# 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
2 changes: 2 additions & 0 deletions io.openliberty.java.internal_fat_27/run/server.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Need to update the JAVA_HOME environment variable to point to your Java 27 JDK
JAVA_HOME=/jdk/temurin/jdk27
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*******************************************************************************
* 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
* 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 {
}
Loading