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


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

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

13 87

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.


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

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

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

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

вы можете сделать некоторые трюки с пользовательскими загрузчиками классов, чтобы загрузить, упаковать и снова запустить компоненты 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 код, который может быть методом запуск, может быть частью приложения, которое необходимо перезапустить. Приложение должно иметь зависимость от библиотеки Apache Ant (jar).

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

}

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

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. 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 файл с новой версией, приложение начало плохо себя вести и давать исключения о

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

в 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 не является набор


пример:

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

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