How can I start a 'main' in a new process

2019-04-05 03:21发布

The question is rather simple. How can I start a main method in another java process? Now I do it like this:

startOptions = new String[] {"java", "-jar", "serverstart.jar"};
new ProcessBuilder(startOptions).start();

But they asked me to do it not with an external .jar file. The serverstart.jar obviously has a main method, but it it possible to call that main method in another process, without calling the .jar file?

I'm thinking of something like this:

new ProcessBuilder(ServerStart.main(startOptions)).start();

But I don't know if anything like that exists.

Kind regards,

5条回答
放我归山
2楼-- · 2019-04-05 04:10

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).

查看更多
3楼-- · 2019-04-05 04:11

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.

查看更多
孤傲高冷的网名
4楼-- · 2019-04-05 04:12

You can do this using Reflection (java.lang.reflect package).

public static void main(String[] args) throws Exception {
    Class c = Class.forName("ServerStart");
    Class[] argTypes = { args.getClass() };
    Method m = c.getMethod("main", argTypes);
    Object passedArgv[] = { args };
    m.invoke(null, passedArgv);
}
查看更多
劫难
5楼-- · 2019-04-05 04:18

I'll answer here how to create multi process application without spring :). With spring you can do this by xml config. Multithread is another story, this is multi-process

Create a JavaProces class as seen below. You can store a counterparter XML/JSON of this class in your environment. Then start your process with Runtime.getRuntime().exec(processRunnerString);,

You should first find java.exe , vm args, then set -classpath then mainClass and args respectively.

Finally you will have something like java JRE\java.exe -classpath .;*;lib* AClass arg1 - Dprop=val

You can use JMX to communicate with other process.

import java.util.Dictionary;
import java.util.List;

public class JavaProcess {

    private String mainClass;
    private Dictionary<String, String> vmParameters;
    private List<String> classPath;
    private List<String> mainArgs;

    public String getMainClass() {
        return mainClass;
    }

    public void setMainClass(String mainClass) {
        this.mainClass = mainClass;
    }

    public Dictionary<String, String> getVmParameters() {
        return vmParameters;
    }

    public void setVmParameters(Dictionary<String, String> vmParameters) {
        this.vmParameters = vmParameters;
    }

    public List<String> getClassPath() {
        return classPath;
    }

    public void setClassPath(List<String> classPath) {
        this.classPath = classPath;
    }

    public List<String> getMainArgs() {
        return mainArgs;
    }

    public void setMainArgs(List<String> mainArgs) {
        this.mainArgs = mainArgs;
    }

}

MainRunner Application, you can gather JavaProcess instance from a configuration file. I just created a dummy Process here and in case of error I stop it from callback of the process.

    //process
    JavaProcess jp = new JavaProcess();

    //java class
    jp.setMainClass("com.hmg.vidapter.run.DriverLauncher");

    //main args[]
    List<String> mainArgsList = new ArrayList<String>();
    mainArgsList.add("ABC1 ARG2 ARG3 ARGN");
    jp.setMainArgs(mainArgsList);

    //-classpath
    List<String> classPath = new ArrayList<String>();
    classPath.add("*");
    classPath.add("libs\\*");
    classPath.add("repo\\*");
    jp.setClassPath(classPath);

    //-Dvm args.
    Dictionary<String, String> vmArgs = new Hashtable<String, String>();
    vmArgs.put("-Dcom.sun.management.jmxremote", "");
    vmArgs.put("-Dcom.sun.management.jmxremote.authenticate=false", "");
    vmArgs.put("-Dcom.sun.management.jmxremote.port=1453", "");
    vmArgs.put("-Dcom.sun.management.jmxremote.ssl=false", "");
    jp.setVmParameters(vmArgs);

    String params = JSONUtils.convertToJSON(jp);
    System.out.println(params);

    StringBuilder sb = new StringBuilder("\"" + getJavaExecutablePath()+ "\"");

    sb.append(" ");

    Enumeration<String> vmaEnum = vmArgs.keys();
    while (vmaEnum.hasMoreElements()) {
        String key = vmaEnum.nextElement();
        sb.append(key + " ");
        String val=vmArgs.get(key);
        if(val!=null && !val.isEmpty())
        {
            sb.append(val + " ");
        }
    }

    sb.append(" -classpath ");
    List<String> cps = jp.getClassPath();
    for (String cp : cps) {
        sb.append(cp+";");
    }
    sb.append(" ");             
    sb.append(jp.getMainClass());
    sb.append(" ");

    List<String> mainArgs = jp.getMainArgs();
    for (String ma : mainArgs) {
        sb.append(ma+" ");
    }

    System.out.println(sb.toString());
    Process p = Runtime.getRuntime().exec(sb.toString());

    //write output
    InputStreamReader isrO = new InputStreamReader(p.getInputStream());
    BufferedReader brO = new BufferedReader(isrO);
    String callBackO = brO.readLine();
    if (callBackO!=null)
    {
        System.out.println("Application Output: " + callBackO);         
    }

    //write errput
    InputStreamReader isr = new InputStreamReader(p.getErrorStream());
    BufferedReader br = new BufferedReader(isr);
    String callBack = br.readLine();
    if (callBack!=null)
    {
        System.err.println("Application Error: "+ callBack);
        //you can do whatever you want if you don't wanna stop it
        p.destroyForcibly();
    }

Determine location of java.exe from java.home env. variable

private static String getJavaExecutablePath(){
        String javaHome = System.getProperty("java.home");
        File f = new File(javaHome);
        f = new File(f, "bin");
        f = new File(f, "java.exe");
        return f.getAbsolutePath();
    }
查看更多
贼婆χ
6楼-- · 2019-04-05 04:19

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);
        }
    }
}
查看更多
登录 后发表回答