How can I start a 'main' in a new process in Java?
Creating a new "java" process from java is not possible since two processes can't share one JVM. (See this question and the accepted answer).
If you can live with creating a new Thread
instead of a Process
you can do it with a custom ClassLoader
. It is as close you can get to a new process. All static and final fields will be reinitialized!
Also note that the "ServerStart
class (for the example below) must be in the class path of the current executing JVM):
public static void main(String args[]) throws Exception {
// start the server
start("ServerStart", "arg1", "arg2");
}
private static void start(final String classToStart, final String... args) {
// start a new thread
new Thread(new Runnable() {
public void run() {
try {
// create the custom class loader
ClassLoader cl = new CustomClassLoader();
// load the class
Class<?> clazz = cl.loadClass(classToStart);
// get the main method
Method main = clazz.getMethod("main", args.getClass());
// and invoke it
main.invoke(null, (Object) args);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
And this is the custom class loader:
private static class CustomClassLoader extends URLClassLoader {
public CustomClassLoader() {
super(new URL[0]);
}
protected java.lang.Class<?> findClass(String name)
throws ClassNotFoundException {
try{
String c = name.replace('.', File.separatorChar) +".class";
URL u = ClassLoader.getSystemResource(c);
String classPath = ((String) u.getFile()).substring(1);
File f = new File(classPath);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte buff[] = new byte[(int) f.length()];
dis.readFully(buff);
dis.close();
return defineClass(name, buff, 0, buff.length, (CodeSource) null);
} catch(Exception e){
throw new ClassNotFoundException(e.getMessage(), e);
}
}
}
Assuming a new thread with a new classloader is not enough (I would vote for this solution though), I understand you need to create a distinct process that invokes a main method in a class without having that declared as "jar main method" in the manifest file -- since you don't have a distinct serverstart.jar anymore.
In this case, you can simply call java -cp $yourClassPath your.package.ServerStart
, as you would do for running any java application when you don't have (or don't want to use) the manifest Main-Class.
I would suggest invoking a shellscript from java and using it to start the new process (if you cant live with just another thread at all).