The presence of an analog of python exec in java
Is there a function in Java that is similar to the exec function in Python? I can't find a clear answer anywhere - there is or not. If there is, then it would be good to name it.
The exec function in python "executes" a string as if it were written in the program as a piece of code, not a string. For example:
exec("a = func()")
Assigns the result of the func function to the variable a
2 answers
In java 9 there is a tool like java shell
. It is quite suitable for your task
An example of usage can be found in this article
Or so in the unix
environment:
String sourceCode = "1 + 2"
List<String> commands = new ArrayList<String>();
commands.add("/bin/sh");
commands.add("-c");
commands.add("echo \"" + sourceCode + "\"" | jshell");
SystemCommandExecutor commandExecutor = new SystemCommandExecutor(commands);
int result = commandExecutor.executeCommand();
StringBuilder stdout = commandExecutor.getStandardOutputFromCommand();
StringBuilder stderr = commandExecutor.getStandardErrorFromCommand();
System.out.println("STDOUT");
System.out.println(stdout);
System.out.println("STDERR");
System.out.println(stderr);
There is no direct analogue of this function, but it is possible to knock on the compiler from java code and feed it the source code, after loading a new class into the class loader. After that, you can use the Reflection api to run the methods of this class. There are examples in the English version: 1 , 2.
I wrote a small utility that compiles and then runs the script to the input of which you need to submit only the script itself and the returned one type:
package ru.stackoverflow.question910353;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
public class ScriptRunner {
private static final AtomicLong COUNTER = new AtomicLong(0);
private static File root;
private static String packageNamePath;
private static String packageName;
private static File packageNameFolder;
private static URLClassLoader classLoader;
static {
try {
root = Files.createTempDirectory("dynamicClasses").toFile();
packageName = "ru.stackoverflow.question910353";
packageNamePath = packageName.replaceAll("\\.", "/");
packageNameFolder = new File(root, packageNamePath);
packageNameFolder.mkdirs();
classLoader = URLClassLoader.newInstance(new URL[]{root.toURI().toURL()});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> T run(String script, Class<T> returnType) throws Exception {
String className = "Test" + COUNTER.incrementAndGet();
String source = "package " + packageName + "; public class " + className + " implements java.util.concurrent.Callable<" + returnType.getName() + "> { " +
" public " + returnType.getName() + " call() throws Exception { " + script + " } }";
File sourceFile = new File(packageNameFolder, className + ".java");
sourceFile.getParentFile().mkdirs();
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());
Class<?> cls = Class.forName(packageName + "." + className, true, classLoader);
Callable<T> instance = (Callable<T>) cls.newInstance();
return instance.call();
}
}
And a test for it, which proves the work of this utility:
package ru.stackoverflow.question910353;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class DynamicClassCompileTest {
@Test
public void testClassLoad() throws Exception {
String result = ScriptRunner.run("return \"test\";", String.class);
System.out.println("result " + result);
Assertions.assertEquals("test", result);
Integer resultInt = ScriptRunner.run("" +
"int a=5;" +
"int b=10;" +
"return a+b;", Integer.class);
System.out.println("resultInt " + resultInt);
Assertions.assertEquals(Integer.valueOf(15), resultInt);
Assertions.assertThrows(Exception.class, () -> {
ScriptRunner.run("invalid code",Void.class);
});
}
}
In fact, bytecode generation is more commonly used. There are special libraries for this: