Frgaal
From APIDesign
I love Frgaal and you'll love it too: Wouldn't it be great to have the latest Java language features available on your old JDK? As old as JDK8? Frgaal allows you to do it. There is a Maven plugin, integration with Gradle, a way to use it from command line... read more at http://frgaal.org
Can I use Frgaal via ToolProvider API?
Given following code (that invokes the JavaCompiler via ToolProvider for a custom CODE snippet that uses JDK17 Record:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.lang.model.element.Modifier; import javax.lang.model.element.NestingKind; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; class ModernJavaCompiler { private static final String CODE = "record R (String name) {}"; public static void main(String... args) { JavaCompiler compiler = javax.tools.ToolProvider.getSystemJavaCompiler(); List<String> itrbl = Arrays.asList("-source", "17"); List<String> itrbl1 = null; List<JavaFileObject> itrbl2 = Collections.singletonList(new Src(CODE)); JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, itrbl, itrbl1, itrbl2); Boolean res = task.call(); if (!Boolean.TRUE.equals(res)) { throw new IllegalStateException("Compilation failed: " + res); } else { System.out.println("Compiled OK"); } } static class Src implements JavaFileObject { private final String code; public Src(String code) { this.code = code; } @Override public Kind getKind() { return Kind.SOURCE; } @Override public boolean isNameCompatible(String string, Kind kind) { return false; } @Override public NestingKind getNestingKind() { return NestingKind.TOP_LEVEL; } @Override public Modifier getAccessLevel() { return Modifier.PUBLIC; } @Override public URI toUri() { try { return new URI("memory://Code.java"); } catch (URISyntaxException ex) { throw new IllegalStateException(ex); } } @Override public String getName() { return "Code.java"; } @Override public InputStream openInputStream() throws IOException { throw new UnsupportedOperationException(); } @Override public OutputStream openOutputStream() throws IOException { throw new UnsupportedOperationException(); } @Override public Reader openReader(boolean bln) throws IOException { throw new UnsupportedOperationException(); } @Override public CharSequence getCharContent(boolean bln) throws IOException { return code; } @Override public Writer openWriter() throws IOException { throw new UnsupportedOperationException(); } @Override public long getLastModified() { throw new UnsupportedOperationException(); } @Override public boolean delete() { throw new UnsupportedOperationException(); } } }
This is a Java class that can be compiled using
javac -source 8 -target 8 ModernJavaCompiler.java
Then it can be executed on JDK-17 as
$ /jdk-17/bin/java ModernJavaCompiler Compiled OK
However executing the same code on JDK-11 is going to fail (as the Javac from JDK-11 doesn't support -source 17):
$ /jdk11/bin/java ModernJavaCompiler Exception in thread "main" java.lang.IllegalArgumentException: error: invalid source release: 17 at jdk.compiler/com.sun.tools.javac.main.Arguments.error(Arguments.java:907) at jdk.compiler/com.sun.tools.javac.main.Arguments.doProcessArgs(Arguments.java:383) at jdk.compiler/com.sun.tools.javac.main.Arguments.processArgs(Arguments.java:347) at jdk.compiler/com.sun.tools.javac.main.Arguments.init(Arguments.java:246) at jdk.compiler/com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:185) at jdk.compiler/com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:119) at jdk.compiler/com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:68) at ModernJavaCompiler.main(ModernJavaCompiler.java:23)
The idea would be to replace the standard "compiler tool" with one coming from the frgaal. Let's try to disable the regular compiler first. There is an option --limit-modules in JDK-11+ and the idea is to use it to use it to disable standard JavaC:
$ /jdk11/bin/java --limit-modules java.base ModernJavaCompiler Exception in thread "main" java.lang.NoClassDefFoundError: javax/tools/ToolProvider
Great! The disabling works. Now there is a time to use Frgaal instead. Following script shows how to get it all working:
$ mkdir -p META-INF/services $ echo com.sun.tools.javac.api.JavacTool > META-INF/services/javax.tools.JavaCompiler $ curl -o compiler.jar https://repo1.maven.org/maven2/org/frgaal/compiler/18.0.0/compiler-18.0.0.jar $ /jdk-11/bin/java --limit-modules java.base,jdk.zipfs -cp compiler.jar:. ModernJavaCompiler warning: [options] system modules path not set in conjunction with -source 17 warning: No output directory specified, cannot perform multi-release compilation 2 warnings Compiled OK
E.g. it is necessary to provide extra ServiceLoader registration in META-INF/services path on the class path, but then it seems to run. Embed Frgaal into your own programs and use the latest Java features on any JDK!