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

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

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

13 ответов


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

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) и делает то же самое.

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


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

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

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

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

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

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

  4. многие из этих трюков и хаков не удастся, в присутствии SecurityManager.


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

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

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

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


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


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


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

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

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


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

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

например, в windows строка

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

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

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

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

всякий раз, когда приложение должно быть перезапущено, оно должно вызвать метод запуск и выйти из виртуальной машины. Задача Ant java должна иметь опции вилки и spawn значение true.

вот пример Ant script:

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

}

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


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 секунд


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

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

многие системы UNIX, включая GNU / Linux (включая Android) в настоящее время имеют procfs однако на некоторых, таких как FreeBSD, он устарел и постепенно прекращается. Mac OS X исключение в том смысле, что он не имеет procfs. окна тоже не есть 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 файл с новой версией, приложение начало плохо себя вести и давать исключения о


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

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

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

  • нет накопления 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 не set


пример:

$ 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]
$

System.err.println("Someone is Restarting me...");
setVisible(false);
try {
    Thread.sleep(600);
} catch (InterruptedException e1) {
    e1.printStackTrace();
}
setVisible(true);

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