Can I provide runtime compiler access when running with JRE in Java 9+?
Asked Answered
A

1

6

I'm migrating an application to Java 10. Our application normally runs with the JRE, but we allow users to compile bits of their own custom code by bundling tools.jar and using reflection to load a JavacTool instance on demand. Our method looks like:

public static JavaCompiler getJavaCompiler() {
    String toolJarName = "tools.jar";
    File file = getResourceForClass("tools.jar");
    try {
        file = file.getCanonicalFile();
    } catch (IOException ignore) {
    }
    if (!file.exists())
        throw new RuntimeException("Can't find java tools file: '"+file+"'");
    try {
        URL[] urls = new URL[]{ file.toURI().toURL() };
        URLClassLoader cl = URLClassLoader.newInstance(urls);
        return Class.forName("com.sun.tools.javac.api.JavacTool", false, cl).asSubclass(JavaCompiler.class).newInstance();
    } catch (Exception e) {
        throw new RuntimeException("Can't find java compiler from '"+file+"': "+e.getMessage());
    }
}

This is necessary because javax.tools.ToolProvider.getSystemJavaCompiler() returns null when running from the JRE. Our method worked well with Java 8, but tools.jar was removed in Java 9, and the class I need com.sun.tools.javac.api.JavacTool is in the jdk.compiler module which remains part of the JDK, but not the JRE.

Is there any way to load the jdk.compiler module when launching the JRE? I suspect it's not, based on this answer, and my attempts to use --add-module result in: java.lang.module.FindException: JMOD format not supported at execution time

Is there another solution I'm missing?

Arianaariane answered 31/5, 2018 at 15:51 Comment(9)
How about trying to create a custom runtime image using jlink and jmod? Also, thought of using the ToolProvider for your use case?Predestination
@nullpointer Just curious: Does the java.util.spi.ToolProvider in your GitHub sample work even if no JDK is present? I doubt that, which means that this probably won't help the OP.Cimbri
@manouti With my suggestion, I meant creating a custom run-time image and as for the implementation within that, using the ToolProvider instead of tools.jarPredestination
This is really strange question. If the application is running on say JRE 8 then the user needs to have JDK 8 on the system too. Maybe the code in the question isn't about the JRE but rather than tools.jar wasn't on the class path by default? If so then there is no need for this code in JDK 9 and newer as the java.compiler and jdk.compiler modules are resolved by default.Rolph
@Alan Bateman: jdk.compiler isn't available with the Oracle JRE. When running java --list-modules with Java 10 JRE you'll notice jdk.compiler is conspicuously absent.Arianaariane
@nullpointer I'm not totally clear on the advantage of using java.util.spi.ToolProvider rather than, say, javax.tools.ToolProvider.getSystemJavaCompiler(), but a custom runtime image seems like it's the solution (and also way more straightforward than I expected). If you add this as an answer I'll accept it.Arianaariane
The question is still very strange. If you are using JRE 8 then where did it find tools.jar? There must be a JDK on the system and how did you check that it's the same version of the JRE you are running on. If you really want to run on the JRE then I think you will have to create a new VM to run the compiler.Rolph
We bundle tools.jar as a resource. We actually bundle the JRE with our application, so we could ensure it's the same version, but interestingly enough we've been bundling tools.jar from 8u25 and JRE 8u162, unnoticed by all. It worked fine until we migrated to Java 10.Arianaariane
The equivalent with JDK 9 and newer is to run jlink to create a run-time image that includes the jdk.compiler module.Rolph
C
4

IMO the simplest solution to your problem is to keep it simple and require users to download the JDK instead of the JRE:

... but we allow users to compile bits of their own custom code

It should come to no surprise to the users to download a JDK since, well, they are using a JDK feature: compiling code.

If that's not possible, then you may want to try the jlink solution that @nullpointer suggests in the comments.

Cimbri answered 31/5, 2018 at 20:46 Comment(2)
When I say "their own code"... developers aren't our target customers, so most of the custom code snippets they're using are written by third parties (their own IT support, our product forums, etc.) In fact, we bundle the JRE, so replacing it with the JDK bloats our application, but it's definitely something we're considering as a worst case solution.Arianaariane
With Java 11, this solution became really easy, as there is no JRE anymore. Those, who know how to create a smaller runtime environment, are expected to know how to include the compiler.Mexican

© 2022 - 2024 — McMap. All rights reserved.