web-dev-qa-db-fra.com

Comment puis-je redémarrer une application Java?

Comment redémarrer une 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 #.

90
Azfar Niaz

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. Trouvez le Java (j'ai utilisé le binaire Java ici, mais cela dépend de vos besoins))
  2. Trouvez l'application (un bocal dans mon cas, en utilisant la classe MyClassInTheJar pour trouver l'emplacement du bocal lui-même)
  3. Construisez une commande pour redémarrer le jar (en utilisant le binaire Java dans ce cas))
  4. L'exécuter! (et ainsi mettre fin à l'application en cours et la redémarrer)
102
Veger
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 collecte toutes les informations disponibles pour reconstruire la ligne de commande d'origine. Ensuite, il le lance et comme il s’agit de la même commande, votre application démarre une seconde fois. Ensuite, nous quittons le programme d'origine, le programme enfant reste en cours d'exécution (même sous Linux) et fait exactement la même chose.

[~ # ~] warning [~ # ~] : Si vous exécutez cette opération, sachez que la création de nouveaux processus ne se termine jamais, de la même manière qu'un - bombe à la fourche .

34
Meinersbur

En gros, vous ne pouvez pas. Du moins pas de manière fiable.

Pour redémarrer un programme Java, vous devez redémarrer la machine virtuelle Java. Pour redémarrer la machine virtuelle Java, vous devez

  1. Localisez le lanceur Java qui a été utilisé. Vous pouvez essayer avec System.getProperty("Java.home"), mais rien ne garantit que cela pointe réellement sur le programme de lancement utilisé pour lancer votre application. (La valeur renvoyée ne peut pas pointer sur le JRE utilisé pour lancer l'application ou elle aurait pu être remplacée par -Djava.home.)

  2. Vous voudrez probablement respecter les paramètres de mémoire d'origine, etc. (-Xmx, -Xms,…). Vous devez donc déterminer quels paramètres ont été utilisés pour démarrer la première machine virtuelle Java. Vous pouvez essayer d'utiliser ManagementFactory.getRuntimeMXBean().getInputArguments(), mais rien ne garantit que cela reflétera les paramètres utilisés. Ceci est même expliqué dans la documentation de cette méthode:

    En règle générale, toutes les options de ligne de commande associées à la commande 'Java' ne sont pas transmises à la machine virtuelle Java). Par conséquent, les arguments d'entrée renvoyé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 d'origine sera perdu lors du redémarrage.

  4. Beaucoup de ces astuces et astuces échoueront en présence d'un SecurityManager.


D'autre part: Vous ne devriez pas avoir besoin de.

Je vous recommande de concevoir votre application de manière à ce qu'il soit facile de tout nettoyer, puis de créer une nouvelle instance de votre classe "principale".

De nombreuses applications sont conçues pour ne faire que créer une instance dans la méthode principale:

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 laissez launch() renvoyer true si et seulement si l'application a été fermée de manière à ce qu'elle doive être redémarrée.

22
aioobe

Strictement parlant, un programme Java) ne peut pas se redémarrer car il doit pour ce faire tuer la machine virtuelle dans laquelle il s'exécute, puis le redémarrer, mais une fois que la machine virtuelle n'est plus en cours d'exécution (tuée), aucune action ne peut être prise.

Vous pourriez faire quelques astuces avec des chargeurs de classes personnalisés pour charger, compacter et redémarrer les composants AWT, mais cela entraînera probablement beaucoup de maux de tête en ce qui concerne la boucle d'événements de l'interface graphique.

Selon le mode de lancement de l'application, vous pouvez démarrer la machine virtuelle Java dans un script wrapper contenant une boucle Do/While, qui se poursuit pendant la fermeture de la machine virtuelle Java avec un code particulier. L'application AWT doit alors appeler System.exit(RESTART_CODE). . Par exemple, dans le pseudocode 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 machine virtuelle Java avec un élément autre que RESTART_CODE lors d'une terminaison "normale" qui ne nécessite pas de redémarrage.

8
maerics

Eclipse redémarre généralement après l'installation d'un plug-in. Ils le font en utilisant un wrapper Eclipse.exe (application de lancement) pour Windows. Cette application exécute le fichier jar Eclipse Runner et, si l'application Eclipse Java se termine par un code de relancement, Eclipse.exe redémarre le plan de travail. Vous pouvez créer un fragment de code natif, un script Shell ou un autre similaire. Java pour effectuer le redémarrage.

7
whatnick

Si vous avez vraiment besoin de redémarrer votre application, vous pouvez écrire une application distincte pour la démarrer.

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

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

4
Sean W.

Bien que cette question soit ancienne et à laquelle j'ai répondu, je suis tombé sur un problème avec certaines des solutions et j'ai décidé d'ajouter ma suggestion à la liste.

Le problème avec certaines des solutions est qu’elles construisent une seule chaîne de commande. Cela crée des problèmes lorsque certains paramètres contiennent des espaces, en particulier Java.home .

Par exemple, sur Windows, la ligne

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

Peut renvoyer quelque chose comme ceci: C:\Program Files\Java\jre7\bin\Java

Cette chaîne doit être entourée de guillemets ou échappée à cause de l'espace dans Program Files. Pas un gros problème, mais un peu gênant et sujet aux erreurs, en particulier dans les applications multiplates-formes.

Par conséquent, ma solution construit la commande sous la forme d'un tablea 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
Malt

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 une fenêtre réduite

^ & quitte pour fermer la fenêtre cmd après avoir fini

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

sommeil 1 sommeil pendant 10 secondes

4
Amr Lotfy

Il suffit d'ajouter des informations qui ne figurent pas dans d'autres réponses.

Si procfs /proc/self/cmdline Est disponible

Si vous utilisez un environnement qui fournit procfs et que le système de fichiers /proc Est donc disponible ( signifie que ce n'est pas une solution portable), vous pouvez avoir Java lire /proc/self/cmdline pour pouvoir 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 le moyen le plus élégant de "redémarrer" le processus Java actuel à partir de Java. Aucun JNI n’est impliqué, et il n’est pas nécessaire de deviner Cela prendra également en charge toutes les options de la machine virtuelle Java passées au binaire Java La ligne de commande sera exactement identique à celle du processus actuel de la machine virtuelle Java.

De nombreux systèmes UNIX, y compris GNU/Linux (y compris Android), ont de nos jours procfs Cependant, sur certains comme FreeBSD, il est obsolète et éliminé. Mac OS X est une exception dans le sens où il n’a pas procfs . Windows n'a pas non plus procfs . Cygwin a procfs mais il est invisible de Java car il est uniquement visible par les applications utilisant les DLL Cygwin au lieu des appels système Windows et Java n'est pas au courant de Cygwin.

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

La valeur par défaut est que stdin/stdout/stderr (in Java appelé System.in/System.out/System.err) Du processus démarré sont définis sur des tubes qui permettent au processus en cours de communiquer avec le processus récemment démarré. voulez redémarrer le processus en cours, 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 la VM actuelle, elle est appelée , héritée . Vous pouvez le faire en appelant inheritIO() de votre instance ProcessBuilder.

Piège sur Windows

Un cas fréquent d'utilisation d'une fonction restart() consiste à redémarrer l'application après une mise à jour. La dernière fois que j'ai essayé cela sous Windows, c'était problématique. Lorsque le fichier .jar De l'application a été remplacé par la nouvelle version, l'application a mal fonctionné et a donné des exceptions concernant le fichier .jar. Je dis juste, au cas où ce serait votre cas d'utilisation. À l'époque, j'ai résolu le problème en encapsulant l'application dans un fichier de commandes et en utilisant une valeur de retour magique de System.exit() que j'ai interrogée dans le fichier de commandes et que le fichier de commandes a redémarré l'application à la place.

3
Christian Hujer

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

Indépendamment du fait que la réponse est déjà acceptée, je voudrais quand même proposer une approche alternative pour l’exhaustivité. En particulier, Apache Ant était une solution très flexible.

En gros, tout se résume à un fichier de 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 lancée , pourrait faire partie de l’application à redémarrer. L'application doit avoir une dépendance sur la bibliothèque Apache Ant (jar).

Chaque fois que l'application doit être redémarrée, elle doit appeler la méthode , lancer et quitter la machine virtuelle. La tâche Ant Java doit avoir les options fork et spawn mis à 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 de la méthode launch peut ressembler à 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 pratique ici est que le même script est utilisé pour le démarrage initial de l’application ainsi que pour les redémarrages.

3
01es

Similaire à la réponse ' améliorée ' de Yoda, mais avec des améliorations supplémentaires (à la fois sur le plan fonctionnel, sur la lisibilité et sur la testabilité). Il est maintenant sûr de s’exécuter et redémarre autant de fois que nécessaire.

  • Pas d'accumulation de Java_TOOL_OPTIONS Les options.
  • Trouve automatiquement la classe principale.
  • Hérite de la sortie standard/stderr actuelle.

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

Correction de bug V1.1: 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]
$
2
Mark Jeronimus

Vieille question et tout ça. Mais c’est encore une autre manière qui offre certains avantages.

Sous Windows, vous pouvez demander au planificateur de tâches de redémarrer votre application pour vous. Cela présente l'avantage d'attendre un laps de temps spécifique avant le redémarrage de l'application. Vous pouvez aller dans le gestionnaire de tâches et supprimer la tâche qui ne se répète plus.

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
Dale