Подтвердить что ты не робот

Выполнение приложения Java в отдельном процессе

Может ли приложение Java загружаться в отдельный процесс, используя его имя, в отличие от его местоположения, независимо от платформы?

Я знаю, что вы можете выполнить программу через...

Process process = Runtime.getRuntime().exec( COMMAND );

... Основная проблема этого метода заключается в том, что такие вызовы являются специфичными для платформы.


В идеале, я бы обернул метод во что-то простое...

EXECUTE.application( CLASS_TO_BE_EXECUTED );

... и передать полное имя класса приложения как CLASS_TO_BE_EXECUTED.

4b9b3361

Ответ 1

Два намека:

System.getProperty("java.home") + "/bin/java" дает вам путь к исполняемому файлу java.

((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURL() помогает вам восстановить ((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURL() текущего приложения.

Тогда ваш EXECUTE.application будет просто (псевдокод):

Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_TO_BE_EXECUTED)

Ответ 2

Это синтез некоторых других ответов, которые были предоставлены. Свойства системы Java предоставляют достаточную информацию, чтобы найти путь к команде java и classpath в том, что, я думаю, является независимым от платформы способом.

public final class JavaProcess {

    private JavaProcess() {}        

    public static int exec(Class klass) throws IOException,
                                               InterruptedException {
        String javaHome = System.getProperty("java.home");
        String javaBin = javaHome +
                File.separator + "bin" +
                File.separator + "java";
        String classpath = System.getProperty("java.class.path");
        String className = klass.getName();

        ProcessBuilder builder = new ProcessBuilder(
                javaBin, "-cp", classpath, className);

        Process process = builder.inheritIO().start();
        process.waitFor();
        return process.exitValue();
    }

}

Вы бы запускали этот метод следующим образом:

int status = JavaProcess.exec(MyClass.class);

Я подумал, что имеет смысл передавать в фактическом классе, а не в строковое представление имени, так как класс должен быть в пути к классам, чтобы это работало.

Ответ 3

Расширяясь на @stepancheg ответ, фактический код будет выглядеть так (в форме теста).

import org.junit.Test;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.stream.Collectors;

public class SpinningUpAJvmTest {
    @Test
    public void shouldRunAJvm() throws Exception {
        String classpath = Arrays.stream(((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURLs())
                .map(URL::getFile)
                .collect(Collectors.joining(File.pathSeparator));
        Process process = new ProcessBuilder(
                System.getProperty("java.home") + "/bin/java",
                "-classpath",
                classpath,
                MyMainClass.class.getName()
                // main class arguments go here
        )
                .inheritIO()
                .start();
        int exitCode = process.waitFor();
        System.out.println("process stopped with exitCode " + exitCode);
    }
}

Ответ 4

Это может быть излишним для вас, но Project Akuma делает то, что вы хотите, и многое другое. Я нашел его через эту запись в Kohsuke (один из программистов Sun Rock), сказочно полезный блог.

Ответ 5

Вам действительно нужно запускать их изначально? Не могли бы вы просто назвать их "основные" методы напрямую? Единственное особенное в главном заключается в том, что запускает VM-пусковая установка, ничто не мешает вам самому называть себя.

Ответ 6

public abstract class EXECUTE {

    private EXECUTE() { /* Procedural Abstract */ }

    public static Process application( final String CLASS_TO_BE_EXECUTED ) {

        final String EXEC_ARGUMENT 
        = new StringBuilder().
              append( java.lang.System.getProperty( "java.home" ) ).
              append( java.io.File.separator ).
              append( "bin" ).
              append( java.io.File.separator ).
              append( "java" ).
              append( " " ).
              append( new java.io.File( "." ).getAbsolutePath() ).
              append( java.io.File.separator ).
              append( CLASS_TO_BE_EXECUTED ).
              toString();

        try {       

            return Runtime.getRuntime().exec( EXEC_ARGUMENT );

        } catch ( final Exception EXCEPTION ) {     

            System.err.println( EXCEPTION.getStackTrace() );
        }

        return null;
    }
}

Ответ 8

Следуя тому, что должен был сказать TofuBeer: Вы уверены, что вам действительно нужно разблокировать другую JVM? JVM имеет действительно хорошую поддержку concurrency в эти дни, поэтому вы можете получить много функциональности относительно дешево, просто открутив новую тему или две (которые могут или не требуют вызова в Foo # main (String [])). Подробнее см. Java.util.concurrent.

Если вы решите развиваться, вы настроились на сложность, связанную с поиском необходимых ресурсов. То есть, если ваше приложение часто меняется и зависит от кучи файлов jar, вам нужно будет отслеживать их все, чтобы они могли быть переданы в argpath классов. Кроме того, такой подход требует вывода как местоположения (в настоящее время исполняемого) JVM (что может быть неточным), так и местоположения текущего пути к классам (что еще менее вероятно, будет точным, в зависимости от того, как нерестится Был вызван поток - jar, jnlp, exploded.classes dir, некоторый контейнер и т.д.).

С другой стороны, привязка к статическим методам #main также имеет свои ловушки. статические модификаторы имеют неприятную склонность к утечке в другой код и, как правило, недовольны дизайнерами.

Ответ 9

Проблема, возникающая при ее запуске из графического интерфейса Java, выполняется в фоновом режиме. Таким образом, вы не видите командную строку вообще.

Чтобы обойти это, вам нужно запустить java.exe через "cmd.exe" и "start". Я не знаю почему, но если вы поместите "cmd/c start" в infront, он покажет приглашение командной строки при запуске.

Однако проблема с "стартом" заключается в том, что если в пути к приложению есть пробел (который путь к java exe обычно имеет, как и в C:\Program Files\Java\jre6\bin\java.exe или аналогичные), то запуск просто завершится неудачей с "не может найти c:\Program"

Итак, вы должны ставить кавычки в C:\Program Files\Java\jre6\bin\java.exe Теперь начните жаловаться на параметры, которые вы передаете java.exe: "Система не может найти файл -cp."

Сброс пробела в "Program Files" с обратной косой чертой также не работает. Поэтому идея состоит в том, чтобы не использовать пространство. Создайте временный файл с расширением bat, а затем введите команду с пробелами и запустить летучую мышь. Однако, запускать летучую мышь через старт, не выходит, когда это делается, поэтому вам нужно поставить "exit" в конце командного файла.

Это все еще кажется yucky.

Итак, ища альтернативы, я обнаружил, что использование цитаты цитаты цитаты в пространстве "Program Files" действительно работает с запуском.

В классе EXECUTE выше измените построитель строк, добавив:

append( "cmd /C start \"Some title\" " ).
append( java.lang.System.getProperty( "java.home" ).replaceAll(" ", "\" \"") ).
append( java.io.File.separator ).
append( "bin" ).
append( java.io.File.separator ).
append( "java" ).
append( " " ).
append( new java.io.File( "." ).getAbsolutePath() ).
append( java.io.File.separator ).
append( CLASS_TO_BE_EXECUTED ).