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 #.
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 #.
Réponses:
Bien entendu, 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);
}
En gros, il fait ce qui suit:
MyClassInTheJar
classe pour trouver l'emplacement du pot lui-même)System.exit(0)
termine le processus enfant a la même réponse que si cette réponse fonctionne vraiment et pourquoi. Si vous ne pouvez pas fournir une explication raisonnable avec votre réponse, vous avez fait un mauvais travail. Une réponse qui fournit plus de questions qu'elle ne répond n'est pas un exemple de réponse approfondie. Les bonnes réponses ne montrent pas seulement le code, mais expliquent également comment et pourquoi ils fonctionnent, quels sont les inconvénients et quelles sont les alternatives. Vous n'avez même pas essayé de couvrir ces choses.
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.
AVERTISSEMENT : si vous exécutez ceci, sachez qu'il ne s'arrête jamais de créer de nouveaux processus, similaires à une bombe à fourche .
ManagementFactory.getRuntimeMXBean().getInputArguments()
ne vous donnera que les arguments d'entrée passés à la JVM. Il manque les paramètres passés à votre application. par exemple java -jar start.jar -MISSED_PARAM=true
. Sur un jvm oracle, vous pouvez récupérer ces paramètres à l'aide de System.getProperty("sun.java.command")
.
ProcessBuilder
et inheritIO()
, la machine virtuelle enfant peut être démarrée de manière à ce que la machine virtuelle parente se termine.
En gros, vous ne pouvez pas. Du moins pas de manière fiable. Cependant, vous ne devriez pas en avoir besoin.
Pour redémarrer un programme Java, vous devez redémarrer la JVM. Pour redémarrer la JVM, vous devez
Localisez le java
lanceur utilisé. Vous pouvez essayer avec System.getProperty("java.home")
mais il n'y a aucune garantie que cela pointera réellement vers le lanceur qui a été utilisé pour lancer votre application. (La valeur renvoyée peut ne pas pointer vers le JRE utilisé pour lancer l'application ou elle peut avoir été remplacée par -Djava.home
.)
Vous voulez sans doute pour honorer la mémoire des paramètres d' origine etc ( -Xmx
, -Xms
...) de sorte que vous devez déterminer les paramètres où utilisé 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 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.
Si votre programme lit l'entrée du Standard.in
stdin d'origine, il sera perdu lors du redémarrage.
Beaucoup de ces astuces et hacks échoueront en présence d'un fichier SecurityManager
.
Je vous recommande de concevoir votre application de manière à ce qu'il soit facile de tout nettoyer et ensuite de créer une nouvelle instance de votre classe "principale".
De nombreuses applications sont conçues pour ne rien faire d'autre que 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()
return true si et seulement si l'application a été arrêtée de telle sorte qu'elle doit être redémarrée.
À proprement parler, un programme Java ne peut pas redémarrer lui-même car pour ce faire, il doit tuer la JVM dans laquelle il est en cours d'exécution, puis 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 pouvez faire quelques astuces avec des 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 de l'interface graphique.
En fonction de la façon dont l'application est lancée, vous pouvez démarrer la JVM dans un script wrapper qui contient une boucle do / while, qui se poursuit pendant que la JVM se ferme avec un code particulier, puis l'application AWT devra 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 lors d'une terminaison "normale" qui ne nécessite pas de redémarrage.
JavaApplicationStub
... Je ne sais pas s'il existe un moyen facile de contourner cela.
Eclipse redémarre généralement après l'installation d'un plugin. Ils le font à l'aide d'un wrapper eclipse.exe (application de lancement) pour Windows. Cette application exécute le jar principal de l'exécuteur eclipse et si l'application java eclipse se termine par un code de relance, eclipse.exe redémarre l'atelier. Vous pouvez créer un morceau similaire de code natif, de script shell ou d'un autre wrapper de code java pour effectuer le redémarrage.
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 cmd après avoir terminé
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 pendant 10 secondes
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:
Bien que cette question soit ancienne et répondue, je suis tombé 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 créent 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 placée entre 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.
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();
}
}
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 toujours proposer 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 ici et ici ) appelée à partir d'un code Java (voir ici ). 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 le lancement de méthode 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.
Il suffit d'ajouter des informations qui ne sont pas présentes dans d'autres réponses.
/proc/self/cmdline
est disponibleSi vous exécutez dans un environnement qui fournit procfs et que le /proc
système de fichiers est donc disponible (ce qui signifie que ce n'est pas une solution portable), vous pouvez faire lire Java /proc/self/cmdline
afin de redémarrer lui-même, 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 manière 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. Cela prendra également en charge toutes les options JVM transmises au java
binaire. La ligne de commande sera exactement identique à celle du processus JVM actuel.
De nombreux systèmes UNIX, y compris GNU / Linux (y compris Android) ont de nos jours des procfs. Cependant, sur certains comme FreeBSD, il est obsolète et est en cours de suppression. Mac OS X est une exception dans le sens où 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 ne connaît pas Cygwin.
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é est défini sur des canaux qui permettent au processus en cours d'exécution de communiquer avec le processus nouvellement démarré. Si vous souhaitez redémarrer le processus en cours, ce n'est probablement pas ce que vous souhaitez . Au lieu de cela, vous voudriez que stdin
/ stdout
/ stderr
soient les mêmes que ceux de la VM actuelle. C'est ce qu'on appelle hérité . Vous pouvez le faire en appelant inheritIO()
votre ProcessBuilder
instance.
Un cas d'utilisation fréquent d'une restart()
fonction est de redémarrer l'application après une mise à jour. La dernière fois que j'ai essayé cela sous Windows, c'était problématique. Lorsqu'elle a remplacé le .jar
fichier de l'application par la nouvelle version, l'application a commencé à se comporter mal et à donner des exceptions au .jar
fichier. Je dis juste, au cas où ce serait votre cas d'utilisation. À l'époque, j'ai résolu le problème en enveloppant l'application dans un fichier de commandes et en utilisant une valeur de retour magique à partir de System.exit()
laquelle j'ai interrogé dans le fichier de commandes et que le fichier de commandes redémarre l'application à la place.
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 présente l'avantage d'attendre un laps de temps spécifique avant de redémarrer l'application. Vous pouvez accéder au gestionnaire de tâches et supprimer la tâche et elle cesse de se 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);
Similaire à la réponse ` ` améliorée ' ' de Yoda , mais avec de nouvelles améliorations (à la fois fonctionnelles, lisibles et testables). Il est maintenant sûr de s'exécuter et redémarre autant de fois que le nombre d'arguments du programme donné.
JAVA_TOOL_OPTIONS
options.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 nul 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]
$
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.