Как я могу запустить 'main' в новом процессе в Java? - PullRequest
12 голосов
/ 13 мая 2011

Вопрос довольно прост.Как я могу запустить основной метод в другом процессе Java?Теперь я делаю это так:

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

Но они попросили меня сделать это не с внешним файлом .jar.В serverstart.jar, очевидно, есть метод main, но можно ли вызвать этот метод main в другом процессе, не вызывая файл .jar?

Я думаю о чем-то вроде этого:

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

Но я не знаю, существует ли что-нибудь подобное.

С уважением,

Ответы [ 5 ]

8 голосов
/ 13 мая 2011

Создание нового процесса "java" из java не возможно, так как два процесса не могут совместно использовать одну JVM . (См. Этот вопрос и принятый ответ ).


Если вы можете жить с созданием нового Thread вместо Process, вы можете сделать это с помощью пользовательского ClassLoader. Как только близко, вы можете перейти к новому процессу . Все статические и конечные поля будут повторно инициализированы!

Также обратите внимание, что класс "ServerStart (для примера ниже) должен находиться в пути к классу текущей исполняющей 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();
}

А это загрузчик пользовательских классов:

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);
        }
    }
}
7 голосов
/ 13 мая 2011

Если предположить, что нового потока с новым загрузчиком классов недостаточно (я бы проголосовал за это решение), я понимаю, что вам нужно создать отдельный процесс, который будет вызывать основной метод в классе, не объявляя его как "метод jar main "в файле манифеста - поскольку у вас больше нет отдельного serverstart.jar.

В этом случае вы можете просто позвонить java -cp $yourClassPath your.package.ServerStart, как если бы вы запускали любое Java-приложение, когда у вас нет (или вы не хотите использовать) манифеста Main-Class.

1 голос
/ 13 мая 2011

Я бы предложил вызвать скрипт из java и использовать его для запуска нового процесса (если вы вообще не можете жить только с другим потоком).

0 голосов
/ 23 октября 2018

Я отвечу здесь, как создать многопроцессное приложение без пружины :).С весной вы можете сделать это с помощью XML-конфигурации. Многопоточность - это еще одна история, это многопроцессный процесс

Создайте класс JavaProces, как показано ниже.Вы можете хранить контрагент XML / JSON этого класса в своей среде.Затем начните процесс с Runtime.getRuntime().exec(processRunnerString);,

Сначала вы должны найти java.exe, vm args, затем установить -classpath, затем mainClass и args соответственно.

Наконец, у вас будет что-то вроде java JRE \ java.exe -classpath.; *; Lib * AClass arg1 - Dprop = val

Вы можете использовать JMX для связи с другим процессом.

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, вы можете собрать экземпляр JavaProcess из файла конфигурации.Я только что создал фиктивный процесс здесь, и в случае ошибки я прекращаю его обратный вызов процесса.

    //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();
    }

Определение местоположения java.exe из java.home env.переменная

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();
    }
0 голосов
/ 13 мая 2011

Вы можете сделать это, используя Reflection (пакет java.lang.reflect).

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