Как я могу перезапустить приложение Java? - PullRequest
88 голосов
/ 12 ноября 2010

Как я могу перезапустить приложение Java AWT? У меня есть кнопка, к которой я прикрепил обработчик событий. Какой код я должен использовать для перезапуска приложения?

Я хочу сделать то же самое, что и Application.Restart() в приложении на C #.

Ответы [ 13 ]

100 голосов
/ 16 ноября 2010

Конечно, можно перезапустить приложение Java.

Следующий метод показывает способ перезапуска приложения Java:

public void restartApplication()
{
  final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
  final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI());

  /* is it a jar file? */
  if(!currentJar.getName().endsWith(".jar"))
    return;

  /* Build command: java -jar application.jar */
  final ArrayList<String> command = new ArrayList<String>();
  command.add(javaBin);
  command.add("-jar");
  command.add(currentJar.getPath());

  final ProcessBuilder builder = new ProcessBuilder(command);
  builder.start();
  System.exit(0);
}

По сути, он выполняет следующие действия:

  1. Найдите исполняемый файл Java (здесь я использовал бинарный файл Java, но это зависит от ваших требований)
  2. Найдите приложение (в моем случае банку, используя класс MyClassInTheJar для поискаСамо местоположение jar)
  3. Создайте команду для перезапуска jar (в данном случае используйте двоичный файл java)
  4. Выполните его!(и, таким образом, завершение текущего приложения и запуск его снова)
33 голосов
/ 12 ноября 2010
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        StringBuilder cmd = new StringBuilder();
        cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
        for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
            cmd.append(jvmArg + " ");
        }
        cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
        cmd.append(Main.class.getName()).append(" ");
        for (String arg : args) {
            cmd.append(arg).append(" ");
        }
        Runtime.getRuntime().exec(cmd.toString());
        System.exit(0);
    }
}

Посвящается всем, кто говорит, что это невозможно.

Эта программа собирает всю доступную информацию для восстановления исходной командной строки. Затем он запускает его, и поскольку это та же команда, ваше приложение запускается во второй раз. Затем мы выходим из исходной программы, дочерняя программа продолжает работать (даже под Linux) и делает то же самое.

ПРЕДУПРЕЖДЕНИЕ : если вы запустите это, имейте в виду, что оно никогда не заканчивается созданием новых процессов, подобно fork bomb .

22 голосов
/ 12 ноября 2010

В принципе, вы не можете.По крайней мере, не надежным способом.

Чтобы перезапустить программу Java, вам необходимо перезапустить JVM.Чтобы перезапустить JVM, вам нужно

  1. . Определить местонахождение средства запуска java, которое использовалось.Вы можете попробовать с System.getProperty("java.home"), но нет никакой гарантии, что это на самом деле будет указывать на панель запуска, которая использовалась для запуска вашего приложения.(Возвращаемое значение может не указывать на JRE, используемую для запуска приложения , или оно могло быть переопределено -Djava.home.)

  2. Возможно, вы захотитеучитывайте исходные настройки памяти и т. д. (-Xmx, -Xms,…), поэтому вам необходимо выяснить, какие настройки использовались для запуска первой JVM.Вы можете попробовать использовать ManagementFactory.getRuntimeMXBean().getInputArguments(), но нет гарантии, что это будет отражать используемые настройки.Это даже прописано в документации этого метода:

    Как правило, не все параметры командной строки для команды 'java' передаются на виртуальную машину Java.Таким образом, возвращаемые входные аргументы могут включать не все параметры командной строки.

  3. Если ваша программа считывает ввод с Standard.in, исходный стандарт будет потерян при перезапуске.

  4. Множество этих приемови взломы потерпят неудачу в присутствии SecurityManager.


С другой стороны: Вам не нужно.

Я рекомендую вам спроектировать ваше приложение так, чтобы было легко очистить каждую вещь, а после этого создать новый экземпляр вашего «основного» класса.

Многие приложения предназначены только для создания экземпляра.в методе main:

public class MainClass {
    ...
    public static void main(String[] args) {
        new MainClass().launch();
    }
    ...
}

Используя этот шаблон, должно быть достаточно легко сделать что-то вроде:

public class MainClass {
    ...
    public static void main(String[] args) {
        boolean restart;
        do {
            restart = new MainClass().launch();
        } while (restart);
    }
    ...
}

и пусть launch() вернет true, если и только еслиприложение было закрыто так, что его нужно перезапустить.

8 голосов
/ 12 ноября 2010

Строго говоря, Java-программа не может перезапустить себя, поскольку для этого она должна завершить работу JVM, в которой она выполняется, и затем запустить ее снова, но как только JVM больше не работает (не завершается), никакие действия не могут быть предприняты.

Вы можете сделать несколько трюков с пользовательскими загрузчиками классов, чтобы снова загрузить, упаковать и запустить компоненты AWT, но это, вероятно, вызовет много головной боли в связи с циклом событий GUI.

В зависимости от того, какПри запуске приложения вы можете запустить JVM в сценарии-оболочке, который содержит цикл do / while, который продолжается, пока JVM завершает работу с определенным кодом, тогда приложение AWT должно будет вызвать System.exit(RESTART_CODE).Например, в псевдокоде сценариев:

DO
  # Launch the awt program
  EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)

Приложение AWT должно выйти из JVM с чем-то отличным от RESTART_CODE при «нормальном» завершении, которое не требует перезапуска.

7 голосов
/ 12 ноября 2010

Eclipse обычно перезапускается после установки плагина.Они делают это, используя оболочку eclipse.exe (приложение запуска) для Windows.Это приложение запускает основной jar-файл eclipse, и если java-приложение eclipse завершает работу с кодом перезапуска, eclipse.exe перезапускает рабочую среду.Вы можете создать аналогичный фрагмент собственного кода, сценария оболочки или другой оболочки Java-кода для достижения перезапуска.

4 голосов
/ 11 января 2015

Хотя этот вопрос старый и на него дан ответ, я наткнулся на проблему с некоторыми решениями и решил добавить свое предложение в смесь.

Проблема с некоторыми решениями заключается в том, что они создают единую командную строку. Это создает проблемы, когда некоторые параметры содержат пробелы, особенно java.home .

Например, на окнах строка

final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";

Может вернуть что-то вроде этого: C:\Program Files\Java\jre7\bin\java

Эта строка должна быть заключена в кавычки или экранирована из-за пробела в Program Files. Не большая проблема, но несколько раздражающая и подверженная ошибкам, особенно в кроссплатформенных приложениях.

Поэтому моё решение строит команду как массив команд:

public static void restart(String[] args) {

        ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
        List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();

        // Java
        commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");

        // Jvm arguments
        for (String jvmArg : jvmArgs) {
            commands.add(jvmArg);
        }

        // Classpath
        commands.add("-cp");
        commands.add(ManagementFactory.getRuntimeMXBean().getClassPath());

        // Class to be executed
        commands.add(BGAgent.class.getName());

        // Command line arguments
        for (String arg : args) {
            commands.add(arg);
        }

        File workingDir = null; // Null working dir means that the child uses the same working directory

        String[] env = null; // Null env means that the child uses the same environment

        String[] commandArray = new String[commands.size()];
        commandArray = commands.toArray(commandArray);

        try {
            Runtime.getRuntime().exec(commandArray, env, workingDir);
            System.exit(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
4 голосов
/ 01 октября 2014

Windows

public void restartApp(){

    // This launches a new instance of application dirctly, 
    // remember to add some sleep to the start of the cmd file to make sure current instance is
    // completely terminated, otherwise 2 instances of the application can overlap causing strange
    // things:)

    new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start();
    System.exit(0);
}

/ мин для запуска скрипта в свернутом окне

^ & выход для закрытияокно cmd после финиша

пример сценария cmd может быть

@echo off
rem add some sleep (e.g. 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start
sleep 10   
set path=C:\someFolder\application_lib\libs;%path%
java -jar application.jar

sleep 10 sleep в течение 10 секунд

4 голосов
/ 12 ноября 2010

Если вам действительно нужно перезапустить ваше приложение, вы можете написать отдельное приложение для его запуска ...

На этой странице приведено много разных примеров для различных сценариев:

http://www.rgagnon.com/javadetails/java-0014.html

3 голосов
/ 12 марта 2015

Просто добавление информации, которой нет в других ответах.

Если доступно procfs /proc/self/cmdline

Если вы работаете в среде, которая предоставляет procfs и, следовательно, имеет файловую систему /proc (что означает, что это не переносимое решение), вы можете иметь Java для чтения /proc/self/cmdline, чтобы перезапустить себя, например так:

public static void restart() throws IOException {
    new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start();
}
public static String[] getMyOwnCmdLine() throws IOException {
    return readFirstLine("/proc/self/cmdline").split("\u0000");
}
public static String readFirstLine(final String filename) throws IOException {
    try (final BufferedReader in = new BufferedReader(new FileReader(filename))) {
        return in.readLine();
    }
}

В системах с /proc/self/cmdline это, вероятно, самый элегантный способ «перезапустить» текущий процесс Java из Java.Никакой JNI не задействован, и не нужно угадывать пути и прочее.Это также позаботится обо всех параметрах JVM, переданных в двоичный файл java.Командная строка будет точно такой же, как и у текущего процесса JVM.

Многие UNIX-системы, включая GNU / Linux (включая Android), в настоящее время имеют procfs Однако нанекоторые, такие как FreeBSD, устарели и постепенно исчезают. Mac OS X является исключением в том смысле, что не имеет procfs . Windows также не имеет procfs .У Cygwin есть procfs , но он невидим для Java, потому что он виден только приложениям, использующим библиотеки Cygwin вместо системных вызовов Windows, а Java не знает о Cygwin.

Не забудьте использовать ProcessBuilder.inheritIO()

По умолчанию stdin / stdout / stderr (в Java называется System.in / System.out / System.err) запущенного процесса установлены на трубы, которые позволяют текущему процессу взаимодействовать с недавно запущенным процессом.Если вы хотите перезапустить текущий процесс, это , скорее всего, не то, что вы хотите .Вместо этого вы бы хотели, чтобы stdin / stdout / stderr были такими же, как у текущей виртуальной машины.Это называется наследуется .Вы можете сделать это, вызвав inheritIO() вашего ProcessBuilder экземпляра.

Подводный камень в Windows

Частым случаем использования функции restart() является перезапуск приложения после обновления.В прошлый раз, когда я попробовал это на Windows, это было проблематично.Когда файл новой версии .jar был перезаписан новой версией, приложение начало плохо себя вести и выдавало исключения для файла .jar.Я просто говорю, на случай, если это ваш вариант использования.Тогда я решил эту проблему, поместив приложение в пакетный файл и использовав магическое возвращаемое значение из System.exit(), которое я запросил в пакетном файле, и вместо этого пакетный файл перезапустил приложение.

3 голосов
/ 12 декабря 2010

Я сам исследовал предмет, когда натолкнулся на этот вопрос.

Независимо от того, что ответ уже принят, я все же хотел бы предложить альтернативный подход для полноты.В частности, Apache Ant послужил очень гибким решением.

По сути, все сводится к файлу сценария Ant с одной задачей выполнения Java (см. здесь и здесь ), вызванной из кода Java (см. здесь ).Этот Java-код, который может быть методом launch , может быть частью приложения, которое необходимо перезапустить.Приложение должно иметь зависимость от библиотеки Apache Ant (jar).

Когда необходимо перезапустить приложение, оно должно вызывать метод launch и выход из виртуальной машины.Задача Ant java должна иметь параметры fork и spawn со значением true.

Вот пример сценария Ant:

<project name="applaucher" default="launch" basedir=".">
<target name="launch">
    <java classname="package.MasinClass" fork="true" spawn="true">
        <jvmarg value="-splash:splash.jpg"/>
        <jvmarg value="-D other VM params"/>
        <classpath>
            <pathelement location="lib-1.jar" />
            ...
            <pathelement location="lib-n.jar" />
        </classpath>
    </java>
</target>
</project>

Код для метода launch может выглядеть примерно так:

public final void launch(final String antScriptFile) {
 /* configure Ant and execute the task */
   final File buildFile = new File(antScriptFile);
   final Project p = new Project();
   p.setUserProperty("ant.file", buildFile.getAbsolutePath());

   final DefaultLogger consoleLogger = new DefaultLogger();
   consoleLogger.setErrorPrintStream(System.err);
   consoleLogger.setOutputPrintStream(System.out);
   consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
   p.addBuildListener(consoleLogger);

   try {
       p.fireBuildStarted();
       p.init();
       final ProjectHelper helper = ProjectHelper.getProjectHelper();
       p.addReference("ant.projectHelper", helper);
       helper.parse(p, buildFile);
       p.executeTarget(p.getDefaultTarget());
       p.fireBuildFinished(null);
   } catch (final BuildException e) {
       p.fireBuildFinished(e);
   }

   /* exit the current VM */
   System.exit(0);

}

Здесь очень удобно то, что один и тот же скрипт используется для первоначального запуска приложениядо перезапуска.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...