import javax.xml.bind.DatatypeConverter;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.util.LinkedList;
import java.util.List;
/**
* Launches an embedded Jar file from within a script.
*
* @author Robert Wapshott
*/
public class Bootstrapper {
public static final String SIGNAL = Bootstrapper.class.getSimpleName();
private static List<String> buffer = new LinkedList<String>();
public static void main(String[] args) {
if (args.length < 1) throw new IllegalStateException("missing Jar argument");
try {
String path = args[0];
log("Reading: " + path);
InputStream stream = locateStream(path);
byte[] data = extractJar(stream);
File jar = writeJar(data);
log("Extracted (" + String.valueOf(jar.length()) + " bytes) to " + jar.getPath());
log("Executing...");
String[] jarArgs = new String[args.length - 1];
System.arraycopy(args, 1, jarArgs, 0, args.length - 1);
int result = launchJar(jar, jarArgs);
log("Completed: " + String.valueOf(result));
if (jar.delete()) {
log("Deleted jar");
}
System.exit(result);
} catch (Throwable e) {
System.err.println(e.getMessage());
for (String line : buffer) {
System.err.println(line);
}
}
}
/**
* Locate the SIGNAL line in a given file and return the input stream that corresponds to
* the location after that line.
*/
public static InputStream locateStream(String path) throws IOException {
// Locate the signal
RandomAccessFile raf = new RandomAccessFile(path, "r");
while (!raf.readLine().equals(SIGNAL));
long total = raf.getFilePointer();
raf.close();
// Seek in the stream
InputStream in = new BufferedInputStream(new FileInputStream(path));
while (total != 0) {
long skipped = in.skip(total);
if (skipped > 0) {
total -= skipped;
}
}
return in;
}
/**
* Extract the contents of a stream to a byte array and decode contents.
*/
public static byte[] extractJar(InputStream in) throws IOException {
ByteArrayOutputStream dout = new ByteArrayOutputStream();
OutputStream out = new BufferedOutputStream(dout);
copyStream(in, out);
String data = new String(dout.toByteArray());
byte[] bytes = DatatypeConverter.parseBase64Binary(data);
return bytes;
}
/**
* Write the data to a Jar.
*/
public static File writeJar(byte[] data) throws IOException {
File jar = File.createTempFile("launcher", ".jar");
ByteArrayInputStream in = new ByteArrayInputStream(data);
OutputStream out = new BufferedOutputStream(new FileOutputStream(jar));
copyStream(in, out);
return jar;
}
/**
* Launch a jar file with the forwarded arguments and prints the output.
*/
public static int launchJar(File jar, String[] jarArgs) throws IOException, InterruptedException {
String cmd = "java -jar " + jar.getAbsolutePath();
// The forwarded arguments.
if (jarArgs.length > 0) {
cmd += " ";
for (String arg : jarArgs) {
cmd += arg + " ";
}
cmd = cmd.substring(0, cmd.length() - " ".length());
}
final Process result = Runtime.getRuntime().exec(cmd);
new Thread(new Runnable(){
@Override
public void run() {
new Printer(result.getInputStream(), System.out);
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
new Printer(result.getErrorStream(), System.err);
}
}).start();
return result.waitFor();
}
/**
* Writes the input to the output.
*/
private static class Printer {
private static final String NL = System.getProperty("line.separator");
public Printer(InputStream in, PrintStream out) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
PrintWriter writer = new PrintWriter(out);
String data;
try {
while ((data = reader.readLine()) != null) {
writer.write(data + NL);
writer.flush();
}
in.close();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
private static long start;
private static void time() {
start = System.currentTimeMillis();
}
private static void timeEnd(String msg) {
long diff = System.currentTimeMillis() - start;
log("--- " + diff + "ms:\t" + msg);
}
private static void log(String message) {
buffer.add(message);
}
private static void copyStream(InputStream in, OutputStream out) {
byte[] buf = new byte[100000];
int read;
try {
while ((read = in.read(buf)) != -1) {
out.write(buf, 0, read);
}
out.flush();
out.close();
in.close();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}