Comment puis-je redémarrer une application Java?

Comment redémarrer une application Java AWT? J'ai un bouton auquel j'ai attaché un gestionnaire d'événements. Quel code dois-je utiliser pour redémarrer l'application?

je veux faire la même chose que Application.Restart() dans une application C#.

87
demandé sur Jonas 2010-11-12 01:15:33

13 réponses

bien sûr, il est possible de redémarrer une application Java.

la méthode suivante montre un moyen de redémarrer une application 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);
}

fondamentalement, il fait ce qui suit:

  1. trouver l'exécutable java (j'ai utilisé le binaire java ici, mais cela dépend de vos exigences)
  2. trouver l'application (un pot dans mon cas, en utilisant la classe MyClassInTheJar pour trouver l'emplacement du pot elle-même)
  3. construire une commande pour redémarrer le jar (en utilisant le binaire java dans ce cas)
  4. exécutez-le! (et donc mettre fin à la présente demande et la recommencer)
98
répondu Veger 2015-10-10 12:16:05
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);
    }
}

Dédié à tous ceux qui disent que c'est impossible.

ce programme recueille toutes les informations disponibles pour reconstruire la ligne de commande originale. Puis, il se lance et depuis c'est la même commande, votre application démarre une seconde fois. Puis nous sortons du programme original, le programme enfant reste en cours d'exécution (même sous Linux) et fait la même chose.

avertissement : si vous exécutez ceci, soyez conscient qu'il ne cesse jamais de créer de nouveaux processus, similaire à une "bombe fourche .

33
répondu Meinersbur 2010-11-12 00:19:11

en gros, tu ne peux pas. Du moins, pas de façon fiable.

pour redémarrer un programme Java, vous devez redémarrer la JVM. Pour redémarrer la JVM vous avez besoin de

  1. localisez le lanceur java qui a été utilisé. Vous pouvez essayer avec System.getProperty("java.home") mais il n'y a aucune garantie que cela pointera réellement le lanceur qui a été utilisé pour lancer votre application. (La valeur retournée ne peut pas indiquer la JRE utilisée pour lancer l'application ou il aurait pu être remplacé par -Djava.home .)

  2. vous voudriez probablement honorer les paramètres de mémoire d'origine etc ( -Xmx , -Xms , ...) vous devez donc trouver les paramètres utilisés pour démarrer la première JVM. Vous pouvez essayer d'utiliser ManagementFactory.getRuntimeMXBean().getInputArguments() mais il n'y a aucune garantie que cela reflétera les paramètres utilisés. Cela est même précisé dans la documentation de cette méthode:

    typiquement, toutes les options en ligne de commande de la commande 'java' ne sont pas passées à la machine virtuelle Java. Ainsi, les arguments d'entrée retournés peuvent ne pas inclure toutes les options de ligne de commande.

  3. si votre programme lit l'entrée de Standard.in le stdin original sera perdu dans le redémarrage.

  4. beaucoup de ces trucs et les hacks échoueront en présence d'un SecurityManager .


d'un autre côté: vous ne devriez pas avoir besoin.

je vous recommande de concevoir votre application afin qu'il soit facile de nettoyer chaque chose et après cela créer une nouvelle instance de votre classe" principale".

de nombreuses applications sont conçues pour ne rien faire d'autre que de créer une instance dans le principale méthode:

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

en utilisant ce modèle, il devrait être assez facile de faire quelque chose comme:

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

et laisser launch() renvoie true si et seulement si l'application a été arrêté d'une manière qui il doit être redémarré.

22
répondu aioobe 2017-05-23 11:54:40

à proprement parler, un programme Java ne peut pas se redémarrer lui-même car pour ce faire, il doit tuer la JVM dans laquelle il tourne et la redémarrer, mais une fois que la JVM n'est plus en cours d'exécution (tuée), aucune action ne peut être entreprise.

vous pourriez faire quelques trucs avec custom classloaders pour charger, empaqueter et démarrer les composants AWT à nouveau, mais cela causera probablement beaucoup de maux de tête en ce qui concerne la boucle de L'événement GUI.

selon la façon dont l'application si vous lancez la JVM, vous pouvez lancer la JVM dans un script d'enrubannage qui contient une boucle do/while, qui continue pendant que la JVM sort avec un code particulier, alors L'application AWT devra appeler System.exit(RESTART_CODE) . Par exemple, dans le pseudo-code de script:

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

L'application AWT doit quitter la JVM avec quelque chose d'autre que le RESTART_CODE sur la terminaison" normale " qui ne nécessite pas de redémarrage.

8
répondu maerics 2010-11-11 22:23:06

Eclipse redémarre typiquement après l'installation d'un plugin. Ils font cela en utilisant une éclipse d'enveloppe.exe (lanceur d'application) pour windows. Cette application exécute le noyau eclipse runner jar et si l'application Java eclipse se termine par un code de relance, eclipse.l'exe redémarre l'établi. Vous pouvez construire un bit similaire de code natif, de script shell ou d'un autre wrapper de code java pour obtenir le redémarrage.

7
répondu whatnick 2010-11-11 22:29:01

Si vous avez vraiment besoin de redémarrer votre application, vous pouvez écrire une application à part le début...

cette page fournit de nombreux exemples différents pour différents scénarios:

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

4
répondu Sean W. 2010-11-11 22:19:18

bien que cette question soit ancienne et qu'on y réponde, j'ai découvert un problème avec certaines des solutions et j'ai décidé d'ajouter ma suggestion dans le mélange.

le problème avec certaines des solutions est qu'elles construisent une chaîne de commande unique. Cela crée des problèmes lorsque certains paramètres contiennent des espaces, en particulier java.accueil .

par exemple, sur windows ,la ligne

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

pourrait revenir quelque chose comme ceci: C:\Program Files\Java\jre7\bin\java

cette chaîne doit être enveloppée dans des guillemets ou échappée en raison de l'espace dans Program Files . Pas un gros problème, mais quelque peu ennuyeux et sujet aux erreurs, en particulier dans les applications multiplateformes.

donc ma solution construit la commande comme un tableau de commandes:

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
répondu Malt 2016-07-23 10:32:05

je faisais moi-même des recherches sur le sujet quand je suis tombé sur cette question.

, Indépendamment du fait que la réponse est déjà accepté, je voudrais offrir une approche alternative pour l'exhaustivité. Plus précisément, Apache Ant a servi de solution très flexible.

fondamentalement, tout se résume à un fichier script Ant avec une seule tâche D'exécution Java (voir ici et ici ) invoqué à partir d'un code Java (voir ici ). Ce code Java, qui peut être une méthode lancement , pourrait faire partie de l'application qui doit être redémarrée. L'application doit avoir une dépendance sur la bibliothèque Apache Ant (jar).

chaque fois que l'application doit être redémarrée, il doit appeler la méthode lancement et sortir de la VM. La tâche java D'Ant devrait avoir les options fork et spawn la valeur true.

voici un exemple de script 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>

le code pour le launch méthode peut ressembler à quelque chose comme ceci:

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

}

une chose très commode ici est que le même script est utilisé pour le démarrage de l'application initiale aussi bien que pour les redémarrages.

3
répondu 01es 2010-12-12 20:20:14

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 pour démarrer le script dans la fenêtre minimisée

^& sortie pour fermer la fenêtre cmd après finition

un exemple de script cmd pourrait être

@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

dormir 10 dormir 10 Secondes

3
répondu Amr Lotfy 2014-10-16 07:01:40

ajout D'information qui n'est pas présente dans les autres réponses.

si procfs /proc/self/cmdline est disponible

si vous exécutez dans un environnement qui fournit procfs et a donc le système de fichiers /proc disponible (ce qui signifie qu'il ne s'agit pas d'une solution portable), vous pouvez avoir Java lire /proc/self/cmdline afin de se redémarrer, comme ceci:

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

sur les systèmes avec /proc/self/cmdline disponible, c'est probablement la façon la plus élégante de" redémarrer " le processus Java actuel à partir de Java. Pas de JNI impliqué, et pas de devinettes sur les chemins et les choses nécessaires.

beaucoup de systèmes UNIX incluant GNU/Linux (y compris Android) ont aujourd'hui procfs cependant sur certains comme FreeBSD, il est déprécié et est progressivement éliminé. Mac OS X est une exception dans le sens qu'il n'a pas procfs . Windows n'est pas procfs . Cygwin a procfs mais il est invisible pour Java parce qu'il est seulement visible pour les applications utilisant les DLLs de Cygwin au lieu des appels de système de Windows, et Java n'est pas au courant de Cygwin.

N'oubliez pas d'utiliser ProcessBuilder.inheritIO()

par défaut est-ce que stdin / stdout / stderr (en Java appelé System.in / System.out / System.err ) du processus commencé sont définis à pipes qui permettent au processus en cours de communiquer avec le nouveau processus commencé. Si vous souhaitez redémarrer le processus actuel, c'est probablement pas ce que vous voulez . Au lieu de cela, vous voudriez que stdin / stdout / stderr sont les mêmes que ceux de l'actuel VM. C'est ce qu'on appelle hérité . Vous pouvez le faire en appelant inheritIO() de votre instance ProcessBuilder .

Piège sur Windows

un cas d'utilisation fréquente d'une fonction restart() est de redémarrer l'application après une mise à jour. La dernière fois que J'ai essayé ceci sur Windows c'était problématique. Lorsque le fichier .jar de la demande a été réécrit avec la nouvelle version, la demande a commencé à mal se comporter et à donner des exceptions à propos du fichier .jar . Je dis juste, au cas où ce soit votre cas d'utilisation. À l'époque, j'ai résolu le problème en enveloppant l'application dans un fichier batch et en utilisant une valeur de retour magique de System.exit() que j'ai questionnée dans le fichier batch et ai eu le fichier batch redémarrer l'application à la place.

2
répondu Christian Hujer 2015-03-11 22:59:45

vieille question et tout ça. Mais c'est une autre façon qui offre quelques avantages.

sous Windows, vous pouvez demander au planificateur de tâches de redémarrer votre application pour vous. Cela a l'avantage d'attendre un certain temps avant que l'application soit redémarrée. Vous pouvez aller à Gestionnaire des tâches et supprimer la tâche et il cesse de répéter.

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
répondu Dale 2016-05-16 03:44:21

semblable à la réponse de Yoda amélioré , mais avec d'autres améliorations (à la fois fonctionnelle, lisibilité et testabilité). Il est maintenant sûr d'exécuter, et redémarre pour autant de fois que la quantité d'arguments de programme donnés.

  • pas d'accumulation d'options JAVA_TOOL_OPTIONS .
  • trouve automatiquement la classe principale.
  • hérite de l'actuel 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 Bugfix: pointeur null si JAVA_TOOL_OPTIONS n'est pas défini


exemple:

$ 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]
$
1
répondu Mark Jeronimus 2018-02-27 12:31:13
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
    Thread.sleep(600);
} catch (InterruptedException e1) {
    e1.printStackTrace();
}
setVisible(true);

je suppose que vous ne voulez pas vraiment arrêter l'application, mais la" redémarrer". Pour cela, vous pouvez utiliser ceci et ajouter votre "Reset" avant le sommeil et après la fenêtre invisible.

-9
répondu NBStudios 2013-11-03 19:28:33