Hi I am trying to compile a java code(which contains classes from third party jars i.e; dependencies) dynamically and execute the same but its not working for me.
Ex : If i want to execute a simple java code (System.out.println) it works fine but if i add any third party jar files (ex: selenium). It throws error that symbol not found.
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class InlineCompiler {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder(64);
sb.append("package testcompile;\n");
sb.append("import org.sikuli.script.*;\n");
sb.append("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff {\n");
sb.append(" public void doStuff() {\n");
sb.append("Screen s = new Screen();\n");
sb.append(" System.out.println(\"Hello world\");\n");
sb.append(" }\n");
sb.append("}\n");
File helloWorldJava = new File("testcompile/HelloWorld.java");
if (helloWorldJava.getParentFile().exists() || helloWorldJava.getParentFile().mkdirs()) {
try {
Writer writer = null;
try {
writer = new FileWriter(helloWorldJava);
writer.write(sb.toString());
writer.flush();
} finally {
try {
writer.close();
} catch (Exception e) {
}
}
/** Compilation Requirements *********************************************************************************************/
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
// This sets up the class path that the compiler will use.
// I've added the .jar file that contains the DoStuff interface within in it...
List<String> optionList = new ArrayList<String>();
optionList.add("-classpath");
optionList.add(System.getProperty("java.class.path") + ";sikuli.jar");
Iterable<? extends JavaFileObject> compilationUnit
= fileManager.getJavaFileObjectsFromFiles(Arrays.asList(helloWorldJava));
JavaCompiler.CompilationTask task = compiler.getTask(
null,
fileManager,
diagnostics,
optionList,
null,
compilationUnit);
/********************************************************************************************* Compilation Requirements **/
if (task.call()) {
/** Load and execute *************************************************************************************************/
System.out.println("Yipe");
// Create a new custom class loader, pointing to the directory that contains the compiled
// classes, this should point to the top of the package structure!
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("./").toURI().toURL()});
// Load the class from the classloader by name....
Class<?> loadedClass = classLoader.loadClass("testcompile.HelloWorld");
// Create a new instance...
Object obj = loadedClass.newInstance();
// Santity check
if (obj instanceof DoStuff) {
// Cast to the DoStuff interface
DoStuff stuffToDo = (DoStuff)obj;
// Run it baby
stuffToDo.doStuff();
}
/************************************************************************************************* Load and execute **/
} else {
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
System.out.format("Error on line %d in %s%n",
diagnostic.getLineNumber(),
diagnostic.getSource().toUri());
}
}
fileManager.close();
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) {
exp.printStackTrace();
}
}
}
public static interface DoStuff {
public void doStuff();
}
}
the code should run but it may through error symbol not found.
First of all, I suggest that when printing the diagnostics, add the message. Change the part that prints the error message to this:
This will help with understanding what the compiler thought went wrong.
On Linux or Mac OS, we now see a problem: Even with a
sikuli.jarcontainig aScreen.classin the right place, that classScreenis not found. Why? Beause Java isn't looking for asikuli.jarfile. Why? Because the wrong path concatenation character is used. The path concatenation character is;on Windows and:on POSIX-y systems. Instead of hard-coding;, we should useSystem.getProperty("path.separator").I could now see 3 compiler errors (and one message that says "Error" but is actually a warning):
So, change the line that builds the classpath entry in the
optionListto this:After this change, the code works.
Here's the directory structure that I used:
And the commands:
Which means if it doesn't work, also check your directory structure.
Last but not least,
java.lang.Compileris deprecated and scheduled for removal. You might want to switch tojavax.tools.JavaCompiler. You can obtain an instance using: