Как я могу перезапустить приложение Java?

94

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

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

Азфар Ниаз
источник
2
Может я не понимаю твой вопрос. Вы хотите, чтобы в вашем приложении была кнопка, которая перезапускает приложение? Итак, после того, как приложение перестанет работать, оно сможет перезапуститься? Для меня это кажется невозможным.
Джей
Я не спрашиваю, что после остановки JVM я спрашиваю, как я могу возродить свой основной java-фрейм?
Azfar Niaz
2
Не возможно. Я вижу, что верстак eclipse часто перезагружается, даже Windows делает этот трюк после обновлений. Ложное предположение состоит в том, что приложение - это единственное, что работает, под ним ничего нет. Нам понадобится пусковая установка с возможностью перезапуска, черепахи до упора.
whatnick
точно так же, как и в приложении C #, где для этого можно написать System.restart ()?
Azfar Niaz
@aniaz, тогда вам следует обновить вопрос, чтобы указать, что вы хотите показать / скрыть рамку. Приложение НЕ Фрейм.
whatnick

Ответы:

105

Конечно, можно перезапустить приложение 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 класс для поиска самого местоположения банки)
  3. Создайте команду для перезапуска jar (в этом случае используя двоичный файл java)
  4. Выполните это! (и, таким образом, завершение текущего приложения и его повторный запуск)
Veger
источник
5
Разве не существует небольшого временного интервала, в течение которого одновременно работают две версии одного и того же приложения?
Монир
5
Будет ли System.exit (0) завершать дочерний процесс?
Horcrux7
16
@Veger Ответ на вопрос, System.exit(0)завершает ли дочерний процесс, совпадает с тем, действительно ли этот ответ работает и почему. Если вы не можете дать разумное объяснение вместе со своим ответом, вы сделали плохую работу. Ответ, который дает больше вопросов, чем ответов, не является примером исчерпывающего ответа. Хорошие ответы не только показывают код, но и объясняют, как и почему они работают, какие недостатки есть и каковы альтернативы. Вы даже не пытались прикрыть эти вещи.
Томаш Зато - Восстановите Монику
8
Так много комментариев, в которых обсуждается, отвечать на вопрос @Horcrux7 или нет. Вы могли бы просто сказать ему ответ с самого начала, лол. Что ж, я пойду и сделаю это (вроде поздно, я знаю): нет, это не так. Там.
Волдеморт
10
Чтобы ответить на мои вопросы самостоятельно. Не работает образец !!! System.exit (0) немедленно завершает клиентский процесс.
Horcrux7
35
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) и делает то же самое.

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

Meinersbur
источник
Возможное улучшение ManagementFactory.getRuntimeMXBean().getInputArguments() предоставит вам только входные аргументы, переданные JVM. Он пропускает параметры, переданные вашему приложению. например, java -jar start.jar -MISSED_PARAM=true. На Oracle JVM вы можете получить эти параметры, используя System.getProperty("sun.java.command").
Chris2M
1
Родительская виртуальная машина может завершиться, если дочерняя виртуальная машина и родительская виртуальная машина не будут подключены друг к другу с помощью каналов, что происходит при запуске дочерней виртуальной машины. Используя ProcessBuilderи inheritIO(), дочерняя виртуальная машина может быть запущена таким же образом, как и родительская виртуальная машина.
Christian Hujer,
1
У меня есть такая версия. Этот комментарий расскажет вам, как это остановить: переименовать что-нибудь в пути, содержащем java.exe.
Дейл
Строго говоря, это не перезапуск, а запуск новой JVM с теми же аргументами, что и этот.
Thorbjørn Ravn Andersen
4
Какая разница? Есть ли разница между перезагрузкой ПК и выключением ОС + повторной загрузкой?
Meinersbur
28

В принципе, нельзя. По крайней мере, не надежным способом. Однако в этом нет необходимости.

Не может часть

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

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

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

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

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

  4. Многие из этих уловок и уловок не работают при наличии файла SecurityManager.

Не следует необходимость участвовать

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

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

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 тогда и только тогда, когда приложение было закрыто таким образом, что его нужно перезапустить.

aioobe
источник
3
+1 за лучший совет по дизайну; хотя иногда это просто невозможно, особенно при использовании, например, JNI.
maerics
Ну, собственная библиотека может изменять глобальное состояние, которое нельзя изменить из интерфейса JNI, поэтому не было бы никакого другого способа «перезапустить» состояние программы, кроме как перезапуском процесса. Конечно, нативная библиотека должна быть лучше спроектирована, но иногда вы зависите от того, что не можете контролировать.
maerics
Хорошо, но с такими рассуждениями вы также можете иметь чистую Java-библиотеку, изменяющую некоторые внутренние статические переменные. Однако это было бы недостатком дизайна и не должно происходить в хорошо написанных библиотеках.
aioobe
1
Ваш ответ неверен, поскольку это вполне возможно даже без внешних приложений / демонов, как показано Мейнерсбуром и моим собственным ответом. А для целей самообновления хорошим решением является перезапуск приложения, поэтому на самом деле перезапуск приложений также необходим.
Veger
1
Но вы делаете использовать внешнюю программу: java! Вы забываете, что Java - это спецификация языка, а не программа. Что, если я запустил вашу программу с помощью другого jvm, например, kaffe ? Все равно обновил мой ответ :-)
aioobe
10

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

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

В зависимости от того, как запускается приложение, вы можете запустить 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, при "нормальном" завершении, которое не требует перезапуска.

maerics
источник
очень интересное решение. Проблема OSX в том, что обычно приложения Java запускаются из скомпилированного JavaApplicationStub... Не уверен, есть ли простой способ обойти это.
Дэн Розенстарк,
7

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

Whatnick
источник
5

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

спать 10 спать 10 секунд

Амр Лотфи
источник
4

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

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

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

Шон В.
источник
4

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

Проблема с некоторыми решениями заключается в том, что они создают единую командную строку. Это создает проблемы, когда некоторые параметры содержат пробелы, особенно 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();
        }
    }
Солод
источник
3

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

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

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

Всякий раз, когда приложение необходимо перезапустить, оно должно вызывать метод launch и выходить из виртуальной машины. Java-задача Ant должна иметь вилку параметров и порождение. установлено значение 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>

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

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

}

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

01es
источник
3

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

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

Кристиан Худжер
источник
2

Старый вопрос и все такое. Но это еще один способ, который дает некоторые преимущества.

В Windows вы можете попросить планировщик задач снова запустить ваше приложение. Это дает преимущество в том, что вы ждете определенное время перед перезапуском приложения. Вы можете зайти в диспетчер задач и удалить задачу, и она перестанет повторяться.

SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm");    
Calendar aCal = Calendar.getInstance(); 
aCal.add(Calendar.SECOND, 65);
String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute.
String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"};  
Process proc = Runtime.getRuntime().exec(create, null, null);
System.out.println("Exit Now");
try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better
System.exit(0);
Дол
источник
2

Похож на « улучшенный » ответ Йоды , но с дальнейшими улучшениями (как функциональными, так и удобочитаемыми и тестируемыми). Теперь его можно запускать безопасно, и он будет перезапускаться столько раз, сколько указано в программе.

  • Нет накопления JAVA_TOOL_OPTIONS вариантов.
  • Автоматически находит основной класс.
  • Наследует текущий stdout / stderr.

public static void main(String[] args) throws Exception {
    if (args.length == 0)
        return;
    else
        args = Arrays.copyOf(args, args.length - 1);

    List<String> command = new ArrayList<>(32);
    appendJavaExecutable(command);
    appendVMArgs(command);
    appendClassPath(command);
    appendEntryPoint(command);
    appendArgs(command, args);

    System.out.println(command);
    try {
        new ProcessBuilder(command).inheritIO().start();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

private static void appendJavaExecutable(List<String> cmd) {
    cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}

private static void appendVMArgs(Collection<String> cmd) {
    Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();

    String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
    if (javaToolOptions != null) {
        Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
        vmArguments = new ArrayList<>(vmArguments);
        vmArguments.removeAll(javaToolOptionsList);
    }

    cmd.addAll(vmArguments);
}

private static void appendClassPath(List<String> cmd) {
    cmd.add("-cp");
    cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}

    private static void appendEntryPoint(List<String> cmd) {
    StackTraceElement[] stackTrace          = new Throwable().getStackTrace();
    StackTraceElement   stackTraceElement   = stackTrace[stackTrace.length - 1];
    String              fullyQualifiedClass = stackTraceElement.getClassName();
    String              entryMethod         = stackTraceElement.getMethodName();
    if (!entryMethod.equals("main"))
        throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod);

    cmd.add(fullyQualifiedClass);
}

private static void appendArgs(List<String> cmd, String[] args) {
    cmd.addAll(Arrays.asList(args));
}

V1.1 Исправление: нулевой указатель, если JAVA_TOOL_OPTIONS не установлен


Пример:

$ java -cp Temp.jar Temp a b c d e
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp]
$
Марк Иеронимус
источник
-13
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
    Thread.sleep(600);
} catch (InterruptedException e1) {
    e1.printStackTrace();
}
setVisible(true);

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

NBStudios
источник
4
Пользователь попросил перезапустить приложение, а не просто скрыть, а показать окно.
Амр Лотфи