Skip to content

Commit 4ff2632

Browse files
Merge pull request #74 from Arunkumar-Kallyodan/FAT_26_New
Java 26 FAT
2 parents 42d319c + 542215d commit 4ff2632

File tree

13 files changed

+385
-4
lines changed

13 files changed

+385
-4
lines changed

.github/workflows/CI.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
fail-fast: false
1616
matrix:
17-
java-version: [ '17', '18', '19', '20', '21', '22', '23', '24', '25' ]
17+
java-version: [ '17', '18', '19', '20', '21', '22', '23', '24', '25','26-ea' ]
1818

1919
runs-on: ubuntu-latest
2020

@@ -37,14 +37,14 @@ jobs:
3737
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
3838
with:
3939
java-version: ${{ matrix.java-version }}
40-
distribution: 'oracle'
40+
distribution: 'temurin'
4141

4242
# This is the JDK gradle will use since gradle does not always support the -ea version
4343
- name: Set up JDK 17
4444
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
4545
with:
4646
java-version: '21'
47-
distribution: 'oracle'
47+
distribution: 'temurin'
4848
cache: gradle
4949

5050
- name: Toolchain debug

.github/workflows/scripts/ClassVersionChecker.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2023,2025 IBM Corporation and others.
2+
* Copyright (c) 2023,2026 IBM Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License 2.0
55
* which accompanies this distribution, and is available at
@@ -49,6 +49,7 @@ public static void main(String[] args) throws IOException {
4949
majorCodeMap.put("23", 67);
5050
majorCodeMap.put("24", 68);
5151
majorCodeMap.put("25", 69);
52+
majorCodeMap.put("26", 70);
5253

5354
String filename = args[0];
5455
int expected = majorCodeMap.get(args[1]);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.gradle/
2+
/bin/
3+
/build/
4+
/generated/
5+
/wlp
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Repo for quick testing of Liberty apps
2+
Especially useful for new Java versions
3+
4+
5+
### For versions of Java that already have gradle support
6+
To build the WAR file locally, from the root directory of the open-liberty-misc repository run:
7+
8+
```
9+
./gradlew io.openliberty.java.internal_fat_26:build
10+
```
11+
12+
13+
### For versions of Java that do not have gradle support yet
14+
Make the following updates in the following files (example given here as if you were working with pre-gradle support for Java 26):
15+
16+
- In **build.gradle**, set:
17+
18+
```
19+
languageVersion = JavaLanguageVersion.of(26)
20+
```
21+
22+
- And then set the environment variable JDK26 to your Java 26 JDK home, for example:
23+
24+
```
25+
(Mac) export JDK26="/path/to/your/jdk26/home"
26+
(Unix) JDK26="/path/to/your/jdk26/home"
27+
(Win DOS) set JDK26="C:\path\to\your\jdk26\home"
28+
(Win PS) $env:JDK26="C:\path\to\your\jdk26\home"
29+
```
30+
31+
- To build the WAR file locally, from the root directory of the open-liberty-misc repository run:
32+
33+
```
34+
./gradlew io.openliberty.java.internal_fat_26:build -P"org.gradle.java.installations.fromEnv=JDK26"
35+
```
36+
where **JDK26** is a system environment variable that reflects the location of your Java 26 JDK to use to compile the source (see last step).
37+
38+
39+
### When moving to a new release of Java
40+
- **build.gradle**
41+
42+
```
43+
appUrl = 'http://localhost:9080/io.openliberty.java.internal_fat_26/'
44+
45+
<Make sure to add any new dependencies or update existing ones to the proper levels>
46+
```
47+
48+
Then add new code to **TestServices.java** either directly or via another class and make sure **TestApp.java** is in the same directory.
49+
50+
---
51+
52+
## Java 26 Specific: JEP 500 Testing
53+
54+
This FAT includes tests for **JEP 500: Prepare to Make Final Mean Final**.
55+
56+
### JVM Options Configuration
57+
58+
The test behavior is controlled by the `run/jvm.options` file:
59+
60+
**Current Configuration (Default Mode):**
61+
```
62+
--enable-preview
63+
# --illegal-final-field-mutation=deny
64+
```
65+
66+
This tests the **Java 26 default behavior** where deep reflection attempts on final fields:
67+
- Issue **WARNING** messages to console/logs
68+
- Do NOT throw exceptions (preparation phase)
69+
- FAT validation should check for warning messages in `console.log` or `messages.log`
70+
71+
**Strict Mode (Optional):**
72+
73+
To test the future default behavior, uncomment the flag in `run/jvm.options`:
74+
```
75+
--enable-preview
76+
--illegal-final-field-mutation=deny
77+
```
78+
79+
With this flag enabled:
80+
- Deep reflection attempts will throw `IllegalAccessException`
81+
- Mutations are blocked immediately
82+
- Test code catches the exception and logs it
83+
84+
### FAT Validation
85+
86+
When running the FAT test:
87+
1. **Default mode**:
88+
- Check server logs for WARNING messages about illegal reflection
89+
- Verify the field value was or was not mutated by checking the logged value
90+
- Expected: Mutation will succeed with warnings logged
91+
92+
2. **Strict mode**:
93+
- Verify test catches `IllegalAccessException` and logs it
94+
- Verify the field value remains unchanged (should be "Original")
95+
- Expected: Mutation blocked, exception thrown
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 IBM Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*******************************************************************************/
10+
// To create this WAR file (ex: ./gradlew build...) for newer versions of Java (typically early access ones) before gradle supports it
11+
// See the README.md file
12+
13+
apply plugin: 'war'
14+
15+
description = "Basic Liberty repo"
16+
17+
tasks.withType(JavaCompile) {
18+
options.encoding = 'UTF-8'
19+
}
20+
21+
compileJava {
22+
doFirst {
23+
options.compilerArgs = [
24+
'--module-path', classpath.asPath,
25+
'--enable-preview'
26+
]
27+
classpath = files()
28+
options.warnings = true
29+
options.deprecation = true
30+
options.debug = true
31+
options.incremental = false
32+
}
33+
}
34+
35+
java {
36+
toolchain {
37+
languageVersion = JavaLanguageVersion.of(26)
38+
}
39+
}
40+
41+
repositories {
42+
mavenCentral()
43+
}
44+
45+
dependencies {
46+
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.1.1'
47+
compileOnly group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
48+
compileOnly group: 'javax.enterprise', name: 'cdi-api', version: '2.0'
49+
}
50+
51+
// This is the URL the test application will be available at
52+
ext {
53+
appUrl = 'http://localhost:9080/io.openliberty.java.internal_fat_26/'
54+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Enable Java preview features
2+
--enable-preview
3+
4+
# JEP 500: Prepare to Make Final Mean Final
5+
# In Java 26, the default behavior is to issue warnings (not deny)
6+
# The FAT test should check for warning messages in console.log/messages.log
7+
# To test strict enforcement mode, uncomment the line below:
8+
# --illegal-final-field-mutation=deny
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Need to update the JAVA_HOME environment variable to point to your Java 26 JDK
2+
JAVA_HOME=/jdk/temurin/jdk26
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 IBM Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*******************************************************************************/
10+
package io.openliberty.java.internal;
11+
12+
import javax.ws.rs.ApplicationPath;
13+
import javax.ws.rs.core.Application;
14+
15+
@ApplicationPath("/")
16+
public class TestApp extends Application {
17+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 IBM Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*******************************************************************************/
10+
package io.openliberty.java.internal;
11+
12+
import module java.base;
13+
import javax.enterprise.context.ApplicationScoped;
14+
import javax.ws.rs.GET;
15+
import javax.ws.rs.Path;
16+
import java.net.http.HttpClient;
17+
import java.lang.reflect.Field;
18+
import java.lang.reflect.Modifier;
19+
import java.io.StringWriter;
20+
import java.io.PrintWriter;
21+
22+
@Path("/")
23+
@ApplicationScoped
24+
public class TestService {
25+
26+
static class FirstName {
27+
private final String value;
28+
29+
public FirstName(String value) {
30+
this.value = value;
31+
}
32+
33+
public String getValue() {
34+
return value;
35+
}
36+
37+
@Override
38+
public String toString() {
39+
return value;
40+
}
41+
}
42+
43+
static class Person {
44+
public final FirstName name = new FirstName("Original");
45+
}
46+
47+
private StringWriter sw = new StringWriter();
48+
49+
@GET
50+
public String test() {
51+
try {
52+
log(">>> ENTER");
53+
doTest();
54+
log("<<< EXIT SUCCESSFUL");
55+
} catch (Exception e) {
56+
e.printStackTrace(System.out);
57+
e.printStackTrace(new PrintWriter(sw));
58+
log("<<< EXIT FAILED");
59+
}
60+
String result = sw.toString();
61+
sw = new StringWriter();
62+
return result;
63+
}
64+
65+
private void doTest() throws Exception {
66+
log("Beginning Java 26 testing");
67+
68+
// Test Final Mean Final (JEP 500)
69+
testFinalMeanFinal();
70+
71+
// Test HTTP/3 Support (JEP 517)
72+
testHTTP3Support();
73+
74+
log("Leaving testing");
75+
}
76+
77+
// Prepare to Make Final Mean Final : JEP 500 -> https://openjdk.org/jeps/500
78+
// Prepare to Make Final Mean Final : JEP 500 -> https://openjdk.org/jeps/500
79+
private void testFinalMeanFinal() throws Exception {
80+
log("Beginning JEP 500 testing: Prepare to Make Final Mean Final");
81+
log("Testing final field mutation protection");
82+
83+
Person p = new Person();
84+
log("Before mutation attempt: " + p.name);
85+
86+
boolean mutationBlocked = false;
87+
String mode = null;
88+
89+
try {
90+
// Use Java 12+ approach to attempt final field mutation
91+
Field f = Person.class.getDeclaredField("name");
92+
f.setAccessible(true);
93+
94+
// Attempt to set the final field directly
95+
log("Attempting direct mutation of final field...");
96+
f.set(p, new FirstName("Mutated"));
97+
98+
// If we reach here, mutation succeeded (WARN mode)
99+
mode = "WARN";
100+
log("Mutation succeeded - running in WARN mode");
101+
log("Field value after mutation: " + p.name);
102+
log("Check server logs for JEP 500 warning messages");
103+
104+
} catch (IllegalAccessException e) {
105+
// Mutation was blocked (DENY mode) -Future Java 26 Behaviour
106+
mutationBlocked = true;
107+
mode = "DENY";
108+
log("SUCCESS: IllegalAccessException caught - running in DENY mode");
109+
log("Exception message: " + e.getMessage());
110+
log("Final field mutation was blocked as expected");
111+
}
112+
113+
// Verify results based on mode
114+
if (mode.equals("DENY")) {
115+
if (!"Original".equals(p.name.getValue())) {
116+
throw new Exception("JEP 500 test FAILED: Final field was mutated despite exception. Value: " + p.name);
117+
}
118+
log("RESULT: Final field remained immutable (value: " + p.name + ")");
119+
} else {
120+
// WARN mode - mutation may succeed but should log warnings - Current Java 26 Behaviour
121+
if ("Mutated".equals(p.name.getValue())) {
122+
log("RESULT: Mutation succeeded in WARN mode - field value changed to: " + p.name);
123+
} else {
124+
log("RESULT: Mutation attempted in WARN mode but value unchanged: " + p.name);
125+
}
126+
log("Note: Check console.log/messages.log for JEP 500 warnings");
127+
128+
}
129+
130+
log("JEP 500 Test Summary:");
131+
log("Mode detected: " + mode);
132+
log("Mutation blocked: " + mutationBlocked);
133+
log("Final field value: " + p.name);
134+
log("Leaving JEP 500 testing");
135+
}
136+
137+
// HTTP/3 for the HTTP Client API : JEP 517 -> https://openjdk.org/jeps/517
138+
private void testHTTP3Support() {
139+
log("Testing HTTP/3 Support (JEP 517)");
140+
141+
try {
142+
// Create an HTTP client with HTTP/3 support
143+
HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_3) // HTTP/3 support
144+
.build();
145+
146+
log("HTTP Client created with HTTP/3 support");
147+
// This demonstrates that the API is available
148+
log("HTTP/3 version: " + HttpClient.Version.HTTP_3);
149+
150+
// Note: Actual HTTP/3 requests would require a server that supports HTTP/3
151+
log("HTTP/3 is now available in the standard HttpClient API");
152+
153+
} catch (Exception e) {
154+
log("HTTP/3 test note: " + e.getMessage());
155+
log("HTTP/3 support is available in Java 26 HttpClient API");
156+
}
157+
log("Leaving JEP 517 testing");
158+
}
159+
160+
public void log(String msg) {
161+
System.out.println(msg);
162+
sw.append(msg);
163+
sw.append("<br/>");
164+
}
165+
166+
}

0 commit comments

Comments
 (0)