Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to make a Java executable?

To be clear, by executable I do not mean literal bytes ready for the processor. For example a bash script, which is interpreted and not executable, becomes executable when a shebang is added to the top that specifies the script should be run by /bin/bash or /bin/sh or whatever program will be interpreting it.

I was wondering if it's possible to do with Java, which is not technically a scripting language but is definitely not executable. It seems like Java would be hard because the user doesn't actually have the opportunity to add a shebang to the compiled file, and the compiled java cannot come from stdin.

like image 242
William Rosenbloom Avatar asked Feb 25 '16 07:02

William Rosenbloom


People also ask

Can you make a Java executable?

The jar (Java Archive) tool of JDK provides the facility to create the executable jar file. An executable jar file calls the main method of the class if you double click it. To create the executable jar file, you need to create . mf file, also known as manifest file.

How do I make a Java JAR file executable?

Right click on your Java Project and select Export. Select Java -> Runnable JAR file -> Next. Select the Destination folder where you would like to save it and click Finish.


2 Answers

You can certainly create a file:

#!/any/executable/program args
...input goes here...

You could do it with Java

#!/path/bin/java mainclass
...this is System.in...
like image 73
John Hascall Avatar answered Oct 12 '22 02:10

John Hascall


Instead of writing lots of code to have Java be executable in source form, you have a few options:

Use Scala! Did you know that Scala is built off Java? It has an interpreter and compiler. You can run a script, a shell or compile and run it. Scala and Java work seamlessly together. Both compile to the same bytecode and run on a JVM. Yes, the language would feel weird because Scala is like a cross between Java, R and Python, but most of the core language is unchanged and all Java packages are available. Give Scala a try. If you are on Linux, you might as well look at Spark too, even for one machine.

If you insist on using Java only, you can create a hybrid program that does two things (I've done this before): compile code and run it. The executable or even a bash script can do the work of taking source files and making them executable. If you are looking to make a Java shell, then you need to make a dynamic runtime compiler/loader, but you simply need to use what Java/Oracle already gives us. Imagine inserting Java syntax from a file where I put a print statement. You could have anything in there you wanted as long as it compiles. See this example:

package util.injection;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
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 Compiler {

    static final long t0 = System.currentTimeMillis();

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder(64);
        String packageName = "util";
        String className = "HelloWorld";
        sb.append("package util;\n");
        sb.append("public class HelloWorld extends " + Function.class.getName() + " {\n");
        sb.append("    public void test() {\n");
        sb.append("        System.out.println(\"Hello from dynamic function!\");\n");
        sb.append("    }\n");
        sb.append("}\n");
        String code = sb.toString();

        String jarLibraryFile = "target/myprojectname.jar";

        Function dynFunction = code2class(packageName, className, code, jarLibraryFile);
        dynFunction.test();
    }

    public static Function code2class(String packageName, String className, String code, String jarLibraryFile) {
        String wholeClassName = packageName.replace("/", ".") + "." + className;
        String fileName = wholeClassName.replace(".", "/") + ".java";//"testcompile/HelloWorld.java";
        File javaCodeFile = new File(fileName);
        string2file(javaCodeFile, code);
        Function dynFunction = null;
        try {

            boolean success = compile(jarLibraryFile, javaCodeFile);

            /**
             * Load and execute
             * ************************************************************************************************
             */
            System.out.println("Running... " + (System.currentTimeMillis() - t0) + " ms");
            Object obj = load(wholeClassName);
            // Santity check
            if (obj instanceof Function) {
                dynFunction = (Function) obj;
                // Run it 
                //Edit: call dynFunction.test(); to see something
            }
            System.out.println("Finished... " + (System.currentTimeMillis() - t0) + " ms");
        } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) {
            exp.printStackTrace();
        }
        return dynFunction;
    }

    public static boolean compile(String jarLibraryFile, File javaCodeFile) throws IOException {
        /**
         * Compilation Requirements
         * ********************************************************************************************
         */
        System.out.println("Compiling... " + (System.currentTimeMillis() - t0) + " ms");
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        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<>(2);
        optionList.add("-classpath");
        optionList.add(System.getProperty("java.class.path") + ";" + jarLibraryFile);

        Iterable<? extends JavaFileObject> compilationUnit
                = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(javaCodeFile));
        JavaCompiler.CompilationTask task = compiler.getTask(
                null,
                fileManager,
                diagnostics,
                optionList,
                null,
                compilationUnit);
        fileManager.close();

        /**
         * *******************************************************************************************
         * Compilation Requirements *
         */
        if (task.call()) {
            return true;
            /**
             * ***********************************************************************************************
             * 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());
                System.out.printf("Code = %s\nMessage = %s\n", diagnostic.getCode(), diagnostic.getMessage(Locale.US));

            }
        }
        return false;
    }

    public static void string2file(File outputFile, String code) {
        if (outputFile.getParentFile().exists() || outputFile.getParentFile().mkdirs()) {

            try {
                Writer writer = null;
                try {
                    writer = new FileWriter(outputFile);
                    writer.write(code);
                    writer.flush();
                } finally {
                    try {
                        writer.close();
                    } catch (Exception e) {
                    }
                }
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }
    }

    public static Object load(String wholeClassName) throws IllegalAccessException, InstantiationException, ClassNotFoundException, MalformedURLException {
        // 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(wholeClassName);
        // Create a new instance...
        Object obj = loadedClass.newInstance();
        return obj;
    }

}

..

package util.injection;

public class Function {

    private static final long serialVersionUID = 7526472295622776147L;

    public void test() {
                System.out.println("Hello from original Function!");
    }

    public int getID() {
        return -1;
    }

    public void apply(float[] img, int x, int y) {

    }

    public double dot(double[] x, double[] y) {
            return 0;
    }
}
like image 23
ldmtwo Avatar answered Oct 12 '22 03:10

ldmtwo