Skip to content

Commit dd7e589

Browse files
committed
Add Java 26 early-access support via rewrite-java-next module
Adds a new rewrite-java-next module (copy of rewrite-java-25) to support Java 26 pre-release builds, enabling early validation of the OpenRewrite parser against upcoming JDK versions. Updates version detection cascade in JavaParser and CI workflows to include Java 26.
1 parent f70aad1 commit dd7e589

14 files changed

Lines changed: 5829 additions & 3 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
uses: openrewrite/gh-automation/.github/workflows/ci-gradle.yml@main
2424
with:
2525
java_version: |
26+
26
2627
25
2728
21
2829
secrets:

.github/workflows/publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
uses: openrewrite/gh-automation/.github/workflows/publish-gradle.yml@main
1717
with:
1818
java_version: |
19+
26
1920
25
2021
21
2122
secrets:

rewrite-java-next/build.gradle.kts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
plugins {
4+
id("org.openrewrite.build.language-library")
5+
id("jvm-test-suite")
6+
}
7+
8+
val javaTck = configurations.create("javaTck") {
9+
isTransitive = false
10+
}
11+
12+
dependencies {
13+
api(project(":rewrite-core"))
14+
api(project(":rewrite-java"))
15+
implementation(project(":rewrite-java-lombok"))
16+
17+
compileOnly("org.slf4j:slf4j-api:1.7.+")
18+
19+
implementation("io.micrometer:micrometer-core:1.9.+")
20+
implementation("io.github.classgraph:classgraph:latest.release")
21+
implementation("org.ow2.asm:asm:latest.release")
22+
23+
testImplementation(project(":rewrite-test"))
24+
testImplementation("org.antlr:antlr4-runtime:4.13.2")
25+
"javaTck"(project(":rewrite-java-tck"))
26+
}
27+
28+
java {
29+
toolchain {
30+
languageVersion.set(JavaLanguageVersion.of(26))
31+
}
32+
}
33+
34+
tasks.named("licenseFormat") { enabled = false }
35+
36+
tasks.withType<JavaCompile>().configureEach {
37+
sourceCompatibility = JavaVersion.VERSION_26.toString()
38+
targetCompatibility = JavaVersion.VERSION_26.toString()
39+
40+
options.release.set(null as Int?) // remove `--release 8` set in `org.openrewrite.java-base`
41+
options.compilerArgs.addAll(
42+
listOf(
43+
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
44+
"--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
45+
"--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
46+
"--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
47+
"--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
48+
"--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
49+
)
50+
)
51+
}
52+
53+
//Javadoc compiler will complain about the use of the internal types.
54+
tasks.withType<Javadoc>().configureEach {
55+
exclude(
56+
"**/ReloadableJavaNextJavadocVisitor**",
57+
"**/ReloadableJavaNextParser**",
58+
"**/ReloadableJavaNextParserVisitor**",
59+
"**/ReloadableJavaNextTypeMapping**",
60+
"**/ReloadableJavaNextTypeSignatureBuilder**"
61+
)
62+
}
63+
64+
testing {
65+
suites {
66+
val test by getting(JvmTestSuite::class)
67+
68+
register("compatibilityTest", JvmTestSuite::class) {
69+
dependencies {
70+
implementation(project())
71+
implementation(project(":rewrite-test"))
72+
implementation(project(":rewrite-java-tck"))
73+
implementation(project(":rewrite-java-test"))
74+
implementation("org.assertj:assertj-core:latest.release")
75+
}
76+
77+
targets {
78+
all {
79+
testTask.configure {
80+
useJUnitPlatform()
81+
testClassesDirs += files(javaTck.files.map { zipTree(it) })
82+
jvmArgs = listOf("-XX:+UnlockDiagnosticVMOptions", "-XX:+ShowHiddenFrames")
83+
shouldRunAfter(test)
84+
}
85+
}
86+
}
87+
}
88+
}
89+
}
90+
91+
tasks.named("check") {
92+
dependsOn(testing.suites.named("compatibilityTest"))
93+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java;
17+
18+
import org.jspecify.annotations.Nullable;
19+
import org.openrewrite.ExecutionContext;
20+
import org.openrewrite.SourceFile;
21+
import org.openrewrite.java.internal.JavaTypeCache;
22+
23+
import java.lang.reflect.Constructor;
24+
import java.net.URI;
25+
import java.nio.charset.Charset;
26+
import java.nio.file.Path;
27+
import java.util.Collection;
28+
import java.util.stream.Stream;
29+
30+
public class JavaNextParser implements JavaParser {
31+
private final JavaParser delegate;
32+
33+
JavaNextParser(JavaParser delegate) {
34+
this.delegate = delegate;
35+
}
36+
37+
@Override
38+
public Stream<SourceFile> parseInputs(Iterable<Input> sourceFiles, @Nullable Path relativeTo, ExecutionContext ctx) {
39+
return delegate.parseInputs(sourceFiles, relativeTo, ctx);
40+
}
41+
42+
@Override
43+
public JavaParser reset() {
44+
delegate.reset();
45+
return this;
46+
}
47+
48+
@Override
49+
public JavaParser reset(Collection<URI> cus) {
50+
delegate.reset(cus);
51+
return this;
52+
}
53+
54+
@Override
55+
public void setClasspath(Collection<Path> classpath) {
56+
delegate.setClasspath(classpath);
57+
}
58+
59+
public static Builder builder() {
60+
return new Builder();
61+
}
62+
63+
public static class Builder extends JavaParser.Builder<JavaNextParser, Builder> {
64+
65+
@Nullable
66+
private static ClassLoader moduleClassLoader;
67+
68+
static synchronized void lazyInitClassLoaders() {
69+
if (moduleClassLoader != null) {
70+
return;
71+
}
72+
73+
ClassLoader appClassLoader = JavaNextParser.class.getClassLoader();
74+
moduleClassLoader = new JavaUnrestrictedClassLoader(appClassLoader);
75+
}
76+
77+
@Override
78+
public JavaNextParser build() {
79+
lazyInitClassLoaders();
80+
81+
try {
82+
//Load the parser implementation use the unrestricted module classloader.
83+
Class<?> parserImplementation = Class.forName("org.openrewrite.java.isolated.ReloadableJavaNextParser", true, moduleClassLoader);
84+
85+
Constructor<?> parserConstructor = parserImplementation
86+
.getDeclaredConstructor(Boolean.TYPE, Collection.class, Collection.class, Collection.class, Charset.class,
87+
Collection.class, JavaTypeCache.class);
88+
89+
parserConstructor.setAccessible(true);
90+
91+
JavaParser delegate = (JavaParser) parserConstructor
92+
.newInstance(logCompilationWarningsAndErrors, resolvedClasspath(), classBytesClasspath, dependsOn, charset, styles, javaTypeCache);
93+
94+
return new JavaNextParser(delegate);
95+
} catch (Exception e) {
96+
throw new IllegalStateException("Unable to construct JavaNextParser.", e);
97+
}
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)