Выполнение приложения Java в отдельном процессе
Может ли приложение Java загружаться в отдельный процесс, используя его имя, в отличие от его местоположения, независимо от платформы?
Я знаю, что вы можете выполнить программу через...
Process process = Runtime.getRuntime().exec( COMMAND );
... Основная проблема этого метода заключается в том, что такие вызовы являются специфичными для платформы.
В идеале, я бы обернул метод во что-то простое...
EXECUTE.application( CLASS_TO_BE_EXECUTED );
... и передать полное имя класса приложения как CLASS_TO_BE_EXECUTED
.
Ответы
Ответ 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;
}
}
Ответ 7
Вы проверили API ProcessBuilder? Он доступен с 1,5
http://java.sun.com/javase/6/docs/api/java/lang/ProcessBuilder.html
Ответ 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 ).