Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile java files programmatically

I know this has been asked and answered a lot but I still don't have a good solution and I still don't understand some parts. So I have the requirement to compile *.java files programmatically.

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

is what I'm using and (as expected) the compiler is null. Now, I do know that I have to use the JDK and not the JRE as a "runtime", but here is something I don't understand: isn't it enough to just put tools.jar in the classpath of the application and then have access to the Java Compiler API? If this is true, is there (I think there is) difference between a stand-alone java application and web-based application. Actually, I am trying to summon the JavaCompiler from a PlayFramework webapp so I figured out, maybe this solutions (with including the tools.jar) only works for stand-alone applications?

I also tried to create a custom ClassLoader and invoke the above mentioned method with reflection but all I am getting is null for the compiler object:

ClassLoader classloader = app.classloader();
File file = new File("lib/tools.jar");          
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader newCL = new URLClassLoader(urls, classloader) {
};
Class<?> loadClass = newCL.loadClass("javax.tools.ToolProvider");
Method method = loadClass.getMethod("getSystemJavaCompiler", null);             
Object object = method.invoke(null);
System.out.println("Object: " + object); // NULL

Explanation for the code above:

  • I haven't included try/catches for the sake of simplicity.
  • app.classloader() is a Play-method which returns the ClassLoader of the App
  • tools.jar is included in my lib folder of the Play project (this implies that it is on the project's classpath - according to the Play documentation)

I am pretty sure there is something else I need to do before Play is able to load the Java Compiler class I just don't know what I am missing.

Options like Runtime.exec("javac myFile.java") and the Eclipse JDT compiler are known to me, but that's not what I am looking for.

Oh, and something like System.setProperty("java.home", "PATH_TO_YOUR_JDK"); and then ToolProvider.getSystemJavaCompiler(); is working but I find this solution so ugly.

Best regards

EDIT: (to give additional information and reflect the last status) This is a basic representation of web-app-structure:

myApp
|-conf\...
|-lib\MyJar.jar
|-lib\tools.jar
|-logs\...
|-...

MyJar.jar has now a META-INF/MANIFEST.MF file with the following content:

Manifest-Version: 1.0
Sealed: true
Main-Class: here.comes.my.main.class
Class-Path: tools.jar

Without starting the Play app, I am trying (in the lib folder): java -jar MyJar.jar - my simple main method tries to invoke the Compiler (ToolProvider.getSystemJavaCompiler();) and returns null. So this leads me to believe, that the problem has nothing to do with Play - I can't even get a Compiler when normally running my Jar!

like image 425
user2229298 Avatar asked Mar 31 '13 13:03

user2229298


1 Answers

There is no difference between web applications and standalone applications.

You should not package the tools.jar into your webapp classpath. The classloader in Java generally only works bottom up. Thus the ToolProvider that is part of the jdk won't see your tools.jar in the webapp classpath (it cannot look down into the webapp classpath).

Solution: Use a JDK and make sure you point to it or put the tools.jar in an Java ext dir. You may override the ext dir by setting the property -Djava.ext.dir to any directory you like.

like image 105
Chrstian Beutenmueller Avatar answered Oct 16 '22 12:10

Chrstian Beutenmueller