'. '

Frgaal

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(Can I use Frgaal via {{JDK|javax/tools|ToolProvider}} API?)
(11 intermediate revisions not shown.)
Line 1: Line 1:
-
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 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
+
I love [http://frgaal.org Frgaal] and you'll love it too: Wouldn't it be great to have '''record'''s and other latest [[Java]] language features available on your old [[JDK]]s? As old as [[JDK]]8? [[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 {{JDK|javax/tools|ToolProvider}} [[API]]? ===
=== Can I use [[Frgaal]] via {{JDK|javax/tools|ToolProvider}} [[API]]? ===
-
Given following code (that invokes the {{JDK|javax/tool|JavaCompiler}} via {{JDK|javax/tool|ToolProvider}} for a custom ''CODE'' snippet that uses [[JDK]]17 {{JDK|java/lang|Record}}:
+
Let's imagine we have a following code invoking the {{JDK|javax/tool|JavaCompiler}} via {{JDK|javax/tool|ToolProvider}} for a custom ''CODE'' snippet defining {{JDK|java/lang|Record}} with main method:
<source lang="java">
<source lang="java">
-
 
import java.io.IOException;
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.URI;
import java.net.URISyntaxException;
import java.net.URISyntaxException;
Line 18: Line 13:
import java.util.Collections;
import java.util.Collections;
import java.util.List;
import java.util.List;
-
import javax.lang.model.element.Modifier;
 
-
import javax.lang.model.element.NestingKind;
 
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject;
-
 
+
import javax.tools.SimpleJavaFileObject;
 +
class ModernJavaCompiler {
class ModernJavaCompiler {
-
private static final String CODE = "record R (String name) {}";
+
private static final String CODE = ""
-
 
+
+ "record R (String name) {\n"
-
public static void main(String... args) {
+
+ " public static void main(String... args) {\n"
 +
+ " System.out.println(new R(\"Hello World!\"));\n"
 +
+ " }\n"
 +
+ "}\n";
 +
 +
public static void main(String... args) throws Exception {
JavaCompiler compiler = javax.tools.ToolProvider.getSystemJavaCompiler();
JavaCompiler compiler = javax.tools.ToolProvider.getSystemJavaCompiler();
-
List<String> itrbl = Arrays.asList("-source", "17");
+
List<String> arguments = Arrays.asList(args);
-
List<String> itrbl1 = null;
+
List<JavaFileObject> sources = Collections.singletonList(new Src(CODE));
-
List<JavaFileObject> itrbl2 = Collections.singletonList(new Src(CODE));
+
JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, arguments, null, sources);
-
JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, itrbl, itrbl1, itrbl2);
+
Boolean res = task.call();
Boolean res = task.call();
if (!Boolean.TRUE.equals(res)) {
if (!Boolean.TRUE.equals(res)) {
Line 39: Line 37:
}
}
}
}
-
+
-
static class Src implements JavaFileObject {
+
static class Src extends SimpleJavaFileObject {
private final String code;
private final String code;
-
 
+
-
public Src(String code) {
+
public Src(String code) throws URISyntaxException {
 +
super(new URI("memory://Code.java"), Kind.SOURCE);
this.code = 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
@Override
public String getName() {
public String getName() {
return "Code.java";
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
@Override
public CharSequence getCharContent(boolean bln) throws IOException {
public CharSequence getCharContent(boolean bln) throws IOException {
return code;
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();
 
-
}
 
-
 
}
}
}
}
</source>
</source>
-
This is a Java class that can be compiled using
+
This is a regular Java source file that can be compiled using standard [[Javac]] compiler:
<source lang="bash">
<source lang="bash">
-
javac -source 8 -target 8 ModernJavaCompiler.java
+
$ javac -source 8 -target 8 ModernJavaCompiler.java
</source>
</source>
-
Then it can be executed on [[JDK]]-17 as
+
The ''ModernJavaCompiler'' source code then uses {{JDK|javax/tool|JavaCompiler}} on its own to generate class ''R''.
 +
It can be executed [[JDK]]-17 as
<source lang="bash">
<source lang="bash">
$ /jdk-17/bin/java ModernJavaCompiler
$ /jdk-17/bin/java ModernJavaCompiler
Compiled OK
Compiled OK
 +
$ /jdk-17/bin/java R
 +
R[name=Hello World!]
</source>
</source>
-
However executing the same code on [[JDK]]-11 is going to fail (as the [[Javac]] from [[JDK]]-11 doesn't support -source 17):
+
However executing the same steps with [[JDK]]-11 is going to fail as the [[Javac]] from [[JDK]]-11 doesn't support '''record''' keyword:
<source lang="bash">
<source lang="bash">
$ /jdk11/bin/java ModernJavaCompiler
$ /jdk11/bin/java ModernJavaCompiler
-
Exception in thread "main" java.lang.IllegalArgumentException: error: invalid source release: 17
+
Code.java:1: error: class, interface, or enum expected
-
at jdk.compiler/com.sun.tools.javac.main.Arguments.error(Arguments.java:907)
+
record R (String name) {
-
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)
+
</source>
</source>
-
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]]:
+
Can we fix that? Sure...
 +
 
 +
The idea would be to replace the standard "compiler tool" with the one coming from the [[frgaal]] project. 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 disable standard [[JavaC]]:
<source lang="bash">
<source lang="bash">
Line 155: Line 94:
</source>
</source>
-
Great! The disabling works. The "compiler tool" is completely missing. Now there is a time to inject [[Frgaal]] instead. Following script shows how to get it all working:
+
Great! Disabling works. The "compiler tool" is completely missing. Now there is a time to inject [[Frgaal]] instead. Following script shows how to get it all working:
<source lang="bash">
<source lang="bash">
Line 166: Line 105:
2 warnings
2 warnings
Compiled OK
Compiled OK
 +
/jdk-11/bin/java R
 +
R[name=Hello World!]
</source>
</source>
-
E.g. it is necessary to provide extra {{JDK|java/util|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]]!
+
Hey, using '''record''' on JDK-11!
 +
 
 +
Having '''record''' (and other [[JDK]]-18 language features) available on old JDKs is great, isn't it? All that is necessary is to provide extra {{JDK|java/util|ServiceLoader}} registration in ''META-INF/services'' path on the class path. Then the [[Frgaal]] compiler gets picked up and is used for compilation even on older [[JDK]]s.
 +
 
 +
 
 +
Embed [[Frgaal]] into your own programs and use the latest [[Java]] features on any [[JDK]]!

Revision as of 16:55, 25 April 2022

I love Frgaal and you'll love it too: Wouldn't it be great to have records and other latest Java language features available on your old JDKs? 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?

Let's imagine we have a following code invoking the JavaCompiler via ToolProvider for a custom CODE snippet defining Record with main method:

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
 
class ModernJavaCompiler {
  private static final String CODE = ""
          + "record R (String name) {\n"
          + "  public static void main(String... args) {\n"
          + "    System.out.println(new R(\"Hello World!\"));\n"
          + "  }\n"
          + "}\n";
 
  public static void main(String... args) throws Exception {
      JavaCompiler compiler = javax.tools.ToolProvider.getSystemJavaCompiler();
      List<String> arguments = Arrays.asList(args);
      List<JavaFileObject> sources = Collections.singletonList(new Src(CODE));
      JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, arguments, null, sources);
      Boolean res = task.call();
      if (!Boolean.TRUE.equals(res)) {
          throw new IllegalStateException("Compilation failed: " + res);
      } else {
          System.out.println("Compiled OK");
      }
  }
 
  static class Src extends SimpleJavaFileObject {
        private final String code;
 
        public Src(String code) throws URISyntaxException {
            super(new URI("memory://Code.java"), Kind.SOURCE);
            this.code = code;
        }
 
        @Override
        public String getName() {
            return "Code.java";
        }
 
 
        @Override
        public CharSequence getCharContent(boolean bln) throws IOException {
            return code;
        }
  }
}

This is a regular Java source file that can be compiled using standard Javac compiler:

$ javac -source 8 -target 8 ModernJavaCompiler.java

The ModernJavaCompiler source code then uses JavaCompiler on its own to generate class R. It can be executed JDK-17 as

$ /jdk-17/bin/java ModernJavaCompiler
Compiled OK
$ /jdk-17/bin/java R                                                                                                                     
R[name=Hello World!]

However executing the same steps with JDK-11 is going to fail as the Javac from JDK-11 doesn't support record keyword:

$ /jdk11/bin/java ModernJavaCompiler
Code.java:1: error: class, interface, or enum expected
record R (String name) {
^

Can we fix that? Sure...

The idea would be to replace the standard "compiler tool" with the one coming from the frgaal project. 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 disable standard JavaC:

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

Great! Disabling works. The "compiler tool" is completely missing. Now there is a time to inject 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
/jdk-11/bin/java R
R[name=Hello World!]

Hey, using record on JDK-11!

Having record (and other JDK-18 language features) available on old JDKs is great, isn't it? All that is necessary is to provide extra ServiceLoader registration in META-INF/services path on the class path. Then the Frgaal compiler gets picked up and is used for compilation even on older JDKs.


Embed Frgaal into your own programs and use the latest Java features on any JDK!

Personal tools
buy