restart ouvrir - Comment puis-je redémarrer une application Java?




fichier jar (12)

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

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


Answers

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 core eclipse runner et si l'application eclipse java se termine par un code de relance, eclipse.exe redémarre le plan de travail. Vous pouvez créer un bit de code natif similaire, un script shell ou un autre wrapper de code Java pour effectuer le redémarrage.


Strictement parlant, un programme Java ne peut pas se relancer puisqu'il doit tuer la JVM dans laquelle il s'exécute et le redémarrer, mais une fois que la JVM n'est plus exécutée (kill), aucune action ne peut être effectuée.

Vous pouvez faire quelques astuces avec les chargeurs de classes personnalisés pour charger, emballer et redémarrer les composants AWT, mais cela causera probablement beaucoup de maux de tête en ce qui concerne la boucle d'événements GUI.

Selon le lancement de l'application, vous pouvez démarrer la JVM dans un script wrapper contenant une boucle do / while, qui se poursuit pendant que la machine virtuelle Java quitte avec un code particulier, puis l'application AWT doit 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 JVM avec autre chose que RESTART_CODE sur une terminaison "normale" qui ne nécessite pas de redémarrage.


Bien que cette question soit ancienne et résolue, j'ai trébuché sur 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.home .

Par exemple, sur les fenêtres, 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 placée entre guillemets ou échappée en raison de l'espace dans Program Files . Pas un gros problème, mais un peu ennuyeux et sujet aux erreurs, en particulier dans les applications multi-plateforme.

Par conséquent 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();
        }
    }

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 d'origine. Puis, il le lance et puisqu'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.

AVERTISSEMENT : Si vous exécutez ceci, soyez conscient qu'il ne finit jamais de créer de nouveaux processus, similaires à une bombe de fourche .


les fenêtres

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 réduite

^ & quitter pour fermer la fenêtre de cmd après la fin

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 sommeil pendant 10 secondes


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 besoins)
  2. Trouver l'application (un pot dans mon cas, en utilisant la classe MyClassInTheJar pour trouver l'emplacement du pot lui-même)
  3. Construire une commande pour redémarrer le jar (en utilisant le binaire java dans ce cas)
  4. L'exécuter! (et ainsi terminer l'application en cours et recommencer)

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

Indépendamment du fait que la réponse est déjà acceptée, je voudrais toujours 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 de script Ant avec une seule tâche d'exécution Java (voir here et here ) invoquée à partir d'un code Java (voir here ). Ce code Java, qui peut être un lancement de méthode, 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, elle doit appeler la méthode lancer et quitter la machine virtuelle. La tâche Ant Java doit avoir les options fork et spawn définies sur 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 de lancement 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.


Similaire à la réponse « improved » de Yoda, mais avec d'autres améliorations (à la fois fonctionnelle, lisibilité et testabilité). Il est maintenant possible de s'exécuter en toute sécurité et redémarre autant de fois que le nombre d'arguments du programme donné.

  • Aucune accumulation d'options JAVA_TOOL_OPTIONS .
  • Recherche automatiquement la classe principale.
  • Hérite de stdout / stderr en cours.
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: null pointeur 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]
$

Il suffit d'ajouter des informations qui ne sont pas présentes dans les 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 disponible (ce qui signifie que ce n'est pas une solution portable), vous pouvez avoir Java /proc/self/cmdline pour 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 disponibles, c'est probablement la façon la plus élégante de "redémarrer" le processus Java actuel à partir de Java. Aucun JNI impliqué, et aucune estimation des chemins et des choses requises.

De nombreux systèmes UNIX, y compris GNU / Linux (y compris Android) ont maintenant procfs Cependant, sur certains comme FreeBSD, il est obsolète et en cours de suppression. Mac OS X est une exception en ce sens qu'il n'a pas de procfs . Windows n'a pas non plus procfs . Cygwin a procfs mais il est invisible pour Java car il n'est visible que pour les applications utilisant les DLL Cygwin au lieu des appels système Windows, et Java n'est pas au courant de Cygwin.

Ne pas oublier d'utiliser ProcessBuilder.inheritIO()

La valeur par défaut est que stdin / stdout / stderr (en Java, appelé System.in / System.out / System.err ) du processus démarré sont définis sur des canaux qui permettent au processus en cours de communiquer avec le processus nouvellement démarré. Si vous voulez redémarrer le processus en cours, ce n'est probablement pas ce que vous voulez . Au lieu de cela, vous voudriez que stdin / stdout / stderr soient les mêmes que ceux de la VM actuelle. Ceci est appelé hérité . Vous pouvez le faire en appelant inheritIO() de votre instance ProcessBuilder .

Piège sur Windows

Un cas d'utilisation fréquent d'une fonction restart() consiste à redémarrer l'application après une mise à jour. La dernière fois que j'ai essayé cela sur Windows c'était problématique. Lors de l'écrasement du fichier .jar l'application avec la nouvelle version, l'application a commencé à se comporter de manière incorrecte et à donner des exceptions au fichier .jar . Je dis juste, au cas où c'est 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 interrogée dans le fichier de commandes et que le fichier batch redémarre l'application à la place.


Fondamentalement, vous ne pouvez pas. Au moins pas d'une 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 utilisé. Vous pouvez essayer avec System.getProperty("java.home") mais il n'y a aucune garantie que cela pointera sur le lanceur qui a été utilisé pour lancer votre application. (La valeur renvoyée peut ne pas pointer vers l'environnement JRE utilisé pour lancer l'application ou elle aurait pu être remplacée par -Djava.home .)

  2. Vous voudrez sans doute honorer 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 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. Ceci est même précisé dans la documentation de cette méthode:

    En règle générale, toutes les options de ligne de commande de la commande 'java' ne sont pas transmises à la machine virtuelle Java. Ainsi, 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 original sera perdu au redémarrage.

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

D'un autre côté: Vous ne devriez pas avoir besoin de.

Je vous recommande de concevoir votre application de sorte qu'il soit facile de tout nettoyer et ensuite créer une nouvelle instance de votre classe "principale".

Beaucoup d'applications sont conçues pour ne rien faire mais créer une instance dans la méthode main:

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é arrêtée de telle sorte qu'elle doit être redémarrée.


Ancienne question et tout ça. Mais c'est encore une autre façon qui offre certains 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 laps de temps avant le redémarrage de l'application. Vous pouvez aller au gestionnaire de 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);

La meilleure façon

//region DESCRIPTION_REGION
int x = 22;
// Comments
String s = "SomeString";
//endregion;

Astuce: Mettez ";" à la fin de la "Endregion"





java restart application-restart