'. '


From APIDesign

(Difference between revisions)
Jump to: navigation, search

JaroslavTulach (Talk | contribs)
(New page: I love [http://frgaal.org 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 yo...)
Next diff →

Revision as of 15:44, 21 April 2022

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 Javac for a custom 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;
        public Kind getKind() {
            return Kind.SOURCE;
        public boolean isNameCompatible(String string, Kind kind) {
            return false;
        public NestingKind getNestingKind() {
            return NestingKind.TOP_LEVEL;
        public Modifier getAccessLevel() {
            return Modifier.PUBLIC;
        public URI toUri() {
            try {
                return new URI("memory://Code.java");
            } catch (URISyntaxException ex) {
                throw new IllegalStateException(ex);
        public String getName() {
            return "Code.java";
        public InputStream openInputStream() throws IOException {
            throw new UnsupportedOperationException();
        public OutputStream openOutputStream() throws IOException {
            throw new UnsupportedOperationException();
        public Reader openReader(boolean bln) throws IOException {
            throw new UnsupportedOperationException();
        public CharSequence getCharContent(boolean bln) throws IOException {
            return code;
        public Writer openWriter() throws IOException {
            throw new UnsupportedOperationException();
        public long getLastModified() {
            throw new UnsupportedOperationException();
        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 regular compiler. There is an option --limit-modules in JDK11+ and the idea is to use it to disable standard JavaC and use Frgaal:

$ /jdk11/bin/java --limit-modules java.base ModernJavaCompiler
Exception in thread "main" java.lang.NoClassDefFoundError: javax/tools/ToolProvider

And here is 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!

Personal tools