I have a String
that I need to convert to java class, compile and create an instance at run time:
Suppose my String
is:
String s = " public class Test {
public Double add(Double x, Double y){
return (x+y);
}
}"
How can I convert it to a class Test
.class, instantiate it and call the method add(Double x, Double y)
at run time?
I read about Byte
Buddy , but the examples I see has a class already defined. In a situation like the above, could anyone give an example how can I use ByteBuddy or any other libraries through which I can achieve this?
Any inputs or suggestions on how to convert this String
to compilable and instantiable java class would be helpful.
Can we use something like this :
package com.demo;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
public class StringToClass
{
public static void main(String[] args)
{
String s = "import com.demo.FormulaAPI; public class FormulaExecutor" +
" { public Double formula1(FormulaAPI apiReference)" +
" { System.out.println(apiReference.evaluate(\"10.10\")); return apiReference.evaluate(\"10.10\"); } }";
try
{
dynamicClass(s, "FormulaExecutor");
} catch (IOException | NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e)
{
e.printStackTrace();
}
}
static void dynamicClass(String sourceCode, String className) throws IOException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException
{
File parent = new File(System.getProperty("user.dir"));
File sourceFile = new File(parent, className + ".java");
sourceFile.deleteOnExit();
FileWriter writer = new FileWriter(sourceFile);
writer.write(sourceCode);
writer.close();
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);
File parentDir = sourceFile.getParentFile();
standardJavaFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(parentDir));
Iterable<? extends JavaFileObject> compilationUnits = standardJavaFileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
javaCompiler.getTask(null, standardJavaFileManager, null, null, null, compilationUnits).call();
standardJavaFileManager.close();
URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] {parentDir.toURI().toURL()});
Class<?> dynamicClass = urlClassLoader.loadClass(className);
Method formula1 = dynamicClass.getDeclaredMethod("formula1", FormulaAPI.class);
formula1.invoke(dynamicClass.newInstance(), new Object[] {new FormulaAPI()});
}
}
package com.demo;
public class FormulaAPI
{
public Double evaluate(String str)
{
return Double.valueOf(str);
}
}
For Now, Method name is hardcoded
Method addMethod = dynamicClass.getDeclaredMethod("add", Double.class, Double.class);
We can even generate it at runTime using reflection
We can import the class in the source code.
Byte Buddy works on the byte code level and does not process source code. You can use Javassist for this purpose which offers limited processing of source code as it ships its own compiler.
Alternatively, use the Java compiler API as suggested.