tags:

views:

247

answers:

4

I'd like to write a Java app which can create executable jars at runtime. The "hello world" of what I want to do is write a Java app X that when run, generates an executable jar Y that when run, prints hello world (or perhaps another string not known until after Y is run).

How can I accomplish this?

+2  A: 

Do you have to write it in plain old Java? I'd use Gradle (a Groovy based build tool). You can have a custom task to write out the source files for Y (Groovy makes it really easy to write out templated files). Gradle makes it easy to generate an executable jar.

If you really want to roll your own from scratch, you'd need to use ZipOutStream to zip up the compiled files after calling javac via the Process API to compile the source.

Maybe a bit more info about why you want to do this would help get better answers

cheers

Lee

leebutts
A: 

Step 1: figure out how to do it manually using the command line. Step 2: automate this by calling the program from within Java.

http://devdaily.com/java/edu/pj/pj010016/

For step 1 I would suggest using ant - IDEs are not always automatable. So, either write out all the files from Java, or have some of the ant configurations included as resources n the project.

Hamish Grubijan
But you can do better than this. Both the Java compilation and JAR file creation steps can be done within the current JVM.
Stephen C
That's what I had hoped. I don't want to spawn a new process or depend on the host machine's environment, other than having the JRE installed. Do you have a link to an example, Stephen?
Brian Harris
+1  A: 

To elaborate on Lee's reply, you need to compile the source first. You can use Process or you can use the code from tools.jar directly as explained here. Then write out a MANIFEST.MF file and put it all together using ZipOutputStream as mentioned.

Dan
+1  A: 

The other answers require starting a new process, this is a method that doesn't. Here are 3 class definitions which produce the hello world scenario described in the question.

When you run XMain.main, it generates /tmp/y.jar. Then, when you run this at the command line:

java -jar /tmp/y.jar cool

It prints:

Hello darling Y!
cool

example/YMain.java

package example;

import java.io.IOException;
import java.io.InputStream;

public class YMain {

    public static void main(String[] args) throws IOException {
        // Fetch and print message from X
        InputStream fromx = YMain.class.getClassLoader().getResourceAsStream("fromx.txt");
        System.out.println(new String(Util.toByteArray(fromx)));

        // Print first command line argument
        System.out.println(args[0]);
    }
}

example/XMain.java

package example;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

public class XMain {

    public static void main(String[] args) throws IOException {
        Manifest manifest = new Manifest();
        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
        manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, YMain.class.getName());
        JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream("/tmp/y.jar"), manifest);

        // Add the main class
        addClass(YMain.class, jarOutputStream);

        // Add the Util class; Y uses it to read our secret message
        addClass(Util.class, jarOutputStream);

        // Add a secret message
        jarOutputStream.putNextEntry(new JarEntry("fromx.txt"));
        jarOutputStream.write("Hello darling Y!".getBytes());
        jarOutputStream.closeEntry();

        jarOutputStream.close();
    }

    private static void addClass(Class c, JarOutputStream jarOutputStream) throws IOException
    {
        String path = c.getName().replace('.', '/') + ".class";
        jarOutputStream.putNextEntry(new JarEntry(path));
        jarOutputStream.write(Util.toByteArray(c.getClassLoader().getResourceAsStream(path)));
        jarOutputStream.closeEntry();
    }
}

example/Util.java

package example;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class Util {

    public static byte[] toByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buf = new byte[0x1000];
        while (true) {
            int r = in.read(buf);
            if (r == -1) {
                break;
            }
            out.write(buf, 0, r);
        }
        return out.toByteArray();
    }
}
Brian Harris