Як я можу перезапустити програму Java?


94

Як я можу перезапустити програму Java AWT? У мене є кнопка, до якої я прикріпив обробник подій. Який код слід використовувати для перезапуску програми?

Я хочу зробити те саме, що Application.Restart()і в додатку C #.


2
Можливо, я не розумію вашого запитання. Ви хочете, щоб у вашій програмі була кнопка, яка перезапускає програму? Отже, після того, як програма більше не працює, вона повинна мати можливість перезапуститись сама? Для мене це звучить неможливо.
Джей,

Я не прошу цього після зупинки JVM, я запитую, як я можу відновити свій основний фрейм Java?
Азфар Ніаз,

2
Не неможливо. Я бачу, що робочий стіл eclipse часто перезавантажується, навіть Windows робить цей трюк після оновлень. Помилкове припущення полягає в тому, що додаток - це єдине, що працює, а під ним нічого немає. Нам знадобиться пускова установка з можливістю перезапуску, черепахи аж донизу.
whatnick

точно так само, як і в додатку C #, де u може написати System.restart () для цього?
Азфар Ніаз,

@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. Виконай його! (і, таким чином, завершення поточної програми та запуск її знову)

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) і робить те саме.

ПОПЕРЕДЖЕННЯ . Якщо ви запускаєте це, майте на увазі, що це ніколи не закінчується створенням нових процесів, подібних до вилки-бомби .


Можливе вдосконалення ManagementFactory.getRuntimeMXBean().getInputArguments() дасть вам лише вхідні аргументи, передані JVM. Він пропускає параметри, передані вашому додатку. наприклад, java -jar start.jar -MISSED_PARAM=true. На оракулі jvm ви можете отримати ці параметри за допомогою System.getProperty("sun.java.command").
Chris2M

1
Батьківська ВМ може закінчитися, якщо дочірня ВМ і батьківська ВМ не будуть з’єднані між собою трубами, саме це відбувається під час запуску дочірньої ВМ. Використовуючи ProcessBuilderі inheritIO(), дочірню віртуальну машину можна запустити таким чином, щоб закінчилася батьківська віртуальна машина.
Крістіан Хуєр

1
Я отримав версію цього. Цей коментар повинен розповісти вам, як його зупинити: перейменуйте щось у шляху, що містить java.exe.
Дейл,

Це, строго кажучи, не перезапуск, а запуск нової JVM з тими ж аргументами, що і ця.
Thorbjørn Ravn Andersen

4
Яка різниця? Чи є різниця між перезавантаженням ПК та вимкненням ОС + і його повторним завантаженням?
Майнерсбер

27

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

Не може розлучитися

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


3
+1 для кращої поради щодо дизайну; хоча, іноді це просто неможливо, особливо якщо використовується JNI, наприклад.
maerics

Ну, власна бібліотека може модифікувати глобальний стан, який неможливо змінити за допомогою інтерфейсу JNI, тому не буде ніякого способу «перезапустити» стан програми, окрім як перезапуском процесу. Звичайно, рідна бібліотека повинна бути краще розроблена, але іноді ви залежате від речей, якими ви не можете керувати.
maerics

Гаразд, але маючи ці міркування, ви можете так само мати чисту бібліотеку Java, що модифікує деякі внутрішні статичні змінні. Однак це було б недоліком дизайну і не повинно траплятися в добре написаних бібліотеках.
aioobe

1
Ваша відповідь неправильна, оскільки це цілком можливо навіть без зовнішніх додатків / демонів, як це показує Meinersbur та моя власна відповідь. І для цілей самооновлення перезапуск програми є хорошим рішенням, тому насправді також необхідна перезавантаження програм.
Veger

1
Але ви робите використовувати зовнішню програму: java! Ви забуваєте, що Java - це специфікація мови, а не програма. Що робити, якщо я запускаю вашу програму за допомогою інших jvm, таких як kaffe, наприклад? У будь-якому випадку
оновив

9

Строго кажучи, програма 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, у "звичайному" завершенні, що не вимагає перезапуску.


дуже цікаве рішення. Проблема OSX полягає в тому, що, як правило, програми Java запускаються зі скомпільованого JavaApplicationStub... Не впевнений, чи існує простий спосіб обійти це.
Dan Rosenstark

7

Зазвичай Eclipse перезапускається після встановлення плагіна. Вони роблять це за допомогою обгортки eclipse.exe (програма запуску) для Windows. Ця програма виконує основну jar eclipse runner, і якщо програма eclipse java завершується за допомогою коду перезапуску, eclipse.exe перезапускає робочий стіл. Ви можете створити подібний біт власного коду, сценарію оболонки або іншої обгортки коду Java для перезапуску.


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

/ min, щоб запустити скрипт у згорнутому вікні

^ & вихід, щоб закрити вікно 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).

Кожного разу, коли програму потрібно перезапустити, вона повинна викликати запуск методу та вийти з віртуальної машини. У завданні Java Ant має бути встановлено параметри 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>

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

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

}

Дуже зручно тут те, що той самий сценарій використовується для початкового запуску програми, а також для перезапуску.


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

Думаю, ви насправді не хочете зупиняти програму, а «Перезапустити» її. Для цього ви можете використати це і додати свій "Скинути" перед сном і після невидимого вікна.


4
Користувач попросив перезапустити програму, а не просто приховати та показати вікно.
Amr Lotfy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.