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

Author: Anton Shchyrov, 2018-11-22

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);
 1
Author: Artem Konovalov, 2018-11-23 07:42:45

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:

  1. cglib
  2. byte butty
 2
Author: Темка тоже, 2018-11-23 06:34:03