Come posso riavviare un'applicazione Java AWT? Ho un pulsante a cui ho collegato un gestore di eventi. Quale codice devo usare per riavviare l'applicazione?
Voglio fare la stessa cosa che si Application.Restart()
fa in un'applicazione C #.
Come posso riavviare un'applicazione Java AWT? Ho un pulsante a cui ho collegato un gestore di eventi. Quale codice devo usare per riavviare l'applicazione?
Voglio fare la stessa cosa che si Application.Restart()
fa in un'applicazione C #.
Risposte:
Ovviamente è possibile riavviare un'applicazione Java.
Il metodo seguente mostra un modo per riavviare un'applicazione 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);
}
Fondamentalmente fa quanto segue:
MyClassInTheJar
classe per trovare la posizione del jar stesso)System.exit(0)
termina il processo figlio ha la stessa risposta di se questa risposta funziona davvero e perché. Se non puoi fornire una spiegazione ragionevole insieme alla tua risposta, hai fatto un pessimo lavoro. La risposta che fornisce più domande di quante ne risponda non è un esempio di risposta esauriente. Le buone risposte non mostrano solo il codice, ma spiegano anche come e perché funzionano, che ci sono gli svantaggi e quali sono le alternative. Non hai nemmeno provato a coprire queste cose.
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);
}
}
Dedicato a tutti coloro che dicono che è impossibile.
Questo programma raccoglie tutte le informazioni disponibili per ricostruire la riga di comando originale. Quindi, lo avvia e poiché è lo stesso comando, l'applicazione si avvia una seconda volta. Quindi usciamo dal programma originale, il programma figlio rimane in esecuzione (anche sotto Linux) e fa la stessa cosa.
ATTENZIONE : se lo esegui, tieni presente che non finisce mai di creare nuovi processi, simili a una fork bomb .
ManagementFactory.getRuntimeMXBean().getInputArguments()
forniranno solo gli argomenti di input passati alla JVM. Mancano i parametri passati alla tua applicazione. es java -jar start.jar -MISSED_PARAM=true
. Su una jvm Oracle, puoi recuperare quei parametri usando System.getProperty("sun.java.command")
.
ProcessBuilder
e inheritIO()
, la VM figlia può essere avviata in modo tale da terminare la VM padre.
Fondamentalmente non puoi. Almeno non in modo affidabile. Tuttavia, non dovresti averne bisogno.
Per riavviare un programma Java, è necessario riavviare la JVM. Per riavviare la JVM è necessario
Individua il programma di java
avvio utilizzato. Puoi provare con, System.getProperty("java.home")
ma non c'è garanzia che questo punti effettivamente al programma di avvio utilizzato per avviare l'applicazione. (Il valore restituito potrebbe non puntare al JRE utilizzato per avviare l'applicazione o potrebbe essere stato sovrascritto da -Djava.home
.)
Si potrebbe presumibilmente vuole onorare la memoria originale impostazioni ecc ( -Xmx
, -Xms
, ...) quindi è necessario capire quali impostazioni in cui utilizzato per avviare il primo JVM. Puoi provare a utilizzare ManagementFactory.getRuntimeMXBean().getInputArguments()
ma non c'è alcuna garanzia che questo rifletterà le impostazioni utilizzate. Questo è anche spiegato nella documentazione di quel metodo:
In genere, non tutte le opzioni della riga di comando per il comando "java" vengono passate alla macchina virtuale Java. Pertanto, gli argomenti di input restituiti potrebbero non includere tutte le opzioni della riga di comando.
Se il tuo programma legge l'input dallo Standard.in
stdin originale verrà perso al riavvio.
Molti di questi trucchi e hack falliranno in presenza di un file SecurityManager
.
Ti consiglio di progettare la tua applicazione in modo che sia facile ripulire ogni cosa e successivamente creare una nuova istanza della tua classe "principale".
Molte applicazioni sono progettate per non fare altro che creare un'istanza nel metodo principale:
public class MainClass {
...
public static void main(String[] args) {
new MainClass().launch();
}
...
}
Utilizzando questo modello, dovrebbe essere abbastanza facile fare qualcosa come:
public class MainClass {
...
public static void main(String[] args) {
boolean restart;
do {
restart = new MainClass().launch();
} while (restart);
}
...
}
e lasciate launch()
restituire true se e solo se l'applicazione è stata chiusa in modo tale da dover essere riavviata.
A rigor di termini, un programma Java non può riavviarsi da solo poiché per farlo deve uccidere la JVM in cui è in esecuzione e quindi riavviarla, ma una volta che la JVM non è più in esecuzione (uccisa), non è possibile intraprendere alcuna azione.
Potresti fare alcuni trucchi con classloader personalizzati per caricare, imballare e avviare nuovamente i componenti AWT, ma questo probabilmente causerà molti mal di testa per quanto riguarda il ciclo di eventi della GUI.
A seconda di come viene avviata l'applicazione, è possibile avviare la JVM in uno script wrapper che contiene un ciclo do / while, che continua mentre la JVM esce con un codice particolare, quindi l'app AWT dovrebbe chiamare System.exit(RESTART_CODE)
. Ad esempio, nello scripting pseudocodice:
DO
# Launch the awt program
EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)
L'app AWT dovrebbe uscire dalla JVM con qualcosa di diverso da RESTART_CODE alla terminazione "normale" che non richiede il riavvio.
JavaApplicationStub
... Non sono sicuro che ci sia un modo semplice per aggirare questo.
Eclipse in genere si riavvia dopo l'installazione di un plug-in. Lo fanno utilizzando un wrapper eclipse.exe (app launcher) per Windows. Questa applicazione esegue il jar runner di eclipse principale e se l'applicazione java di eclipse termina con un codice di riavvio, eclipse.exe riavvia il workbench. È possibile creare un bit simile di codice nativo, script di shell o un altro wrapper di codice java per ottenere il riavvio.
finestre
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 per avviare lo script nella finestra ridotta a icona
^ & esci per chiudere la finestra di cmd al termine
uno script cmd di esempio potrebbe essere
@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
dormire 10 dormire per 10 secondi
Se hai davvero bisogno di riavviare la tua app, potresti scrivere un'app separata per avviarla ...
Questa pagina fornisce molti esempi diversi per diversi scenari:
Sebbene questa domanda sia vecchia e abbia ricevuto risposta, mi sono imbattuto in un problema con alcune delle soluzioni e ho deciso di aggiungere il mio suggerimento al mix.
Il problema con alcune delle soluzioni è che creano una singola stringa di comando. Ciò crea problemi quando alcuni parametri contengono spazi, in particolare java.home .
Ad esempio, su Windows, il file line
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
Potrebbe restituire qualcosa del genere:C:\Program Files\Java\jre7\bin\java
Questa stringa deve essere racchiusa tra virgolette o fatta di escape a causa dello spazio in Program Files
. Non è un problema enorme, ma un po 'fastidioso e soggetto a errori, specialmente nelle applicazioni multipiattaforma.
Pertanto la mia soluzione crea il comando come un array di comandi:
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();
}
}
Stavo studiando l'argomento io stesso quando mi sono imbattuto in questa domanda.
Indipendentemente dal fatto che la risposta sia già accettata, vorrei comunque offrire un approccio alternativo per completezza. In particolare, Apache Ant è stata una soluzione molto flessibile.
Fondamentalmente, tutto si riduce a un file di script Ant con una singola attività di esecuzione Java (fare riferimento qui e qui ) invocata da un codice Java (vedere qui ). Questo codice Java, che può essere l' avvio di un metodo , potrebbe essere una parte dell'applicazione che deve essere riavviata. L'applicazione deve avere una dipendenza dalla libreria Apache Ant (jar).
Ogni volta che l'applicazione deve essere riavviata, dovrebbe chiamare il metodo launch e uscire dalla VM. L'attività Ant java dovrebbe avere opzioni fork e spawn impostate su true.
Ecco un esempio di uno 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>
Il codice per il metodo di avvio potrebbe essere simile a questo:
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);
}
Una cosa molto comoda qui è che lo stesso script viene utilizzato per l'avvio iniziale dell'applicazione e per i riavvii.
Basta aggiungere informazioni che non sono presenti in altre risposte.
/proc/self/cmdline
è disponibileSe stai eseguendo in un ambiente che fornisce procfs e quindi ha il /proc
file system disponibile (il che significa che questa non è una soluzione portatile), puoi fare in modo che Java legga /proc/self/cmdline
per riavviarsi, in questo modo:
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();
}
}
Sui sistemi /proc/self/cmdline
disponibili, questo è probabilmente il modo più elegante per "riavviare" il processo Java corrente da Java. Nessun JNI coinvolto e nessuna indovinazione di percorsi e cose richieste. Questo si occuperà anche di tutte le opzioni JVM passate al java
binario. La riga di comando sarà esattamente identica a quella del processo JVM corrente.
Molti sistemi UNIX incluso GNU / Linux (incluso Android) oggigiorno hanno procf Tuttavia su alcuni come FreeBSD, è deprecato ed è in fase di eliminazione. Mac OS X è un'eccezione nel senso che non ha procfs . Windows inoltre non ha procfs . Cygwin ha procfs ma è invisibile a Java perché è visibile solo alle applicazioni che utilizzano le DLL Cygwin invece delle chiamate di sistema di Windows, e Java non è a conoscenza di Cygwin.
ProcessBuilder.inheritIO()
L'impostazione predefinita è che stdin
/ stdout
/ stderr
(in Java chiamato System.in
/ System.out
/ System.err
) del processo avviato sono impostati su pipe che consentono al processo attualmente in esecuzione di comunicare con il processo appena avviato. Se vuoi riavviare il processo corrente, molto probabilmente non è quello che vuoi . Invece vorresti che stdin
/ stdout
/ fossero stderr
gli stessi della VM corrente. Questo è chiamato ereditato . Puoi farlo chiamando la inheritIO()
tua ProcessBuilder
istanza.
Un caso d'uso frequente di una restart()
funzione è riavviare l'applicazione dopo un aggiornamento. L'ultima volta che l'ho provato su Windows è stato problematico. Quando ha sovrascritto il .jar
file dell'applicazione con la nuova versione, l'applicazione ha iniziato a comportarsi in modo anomalo e ha fornito eccezioni sul .jar
file. Sto solo dicendo, nel caso questo sia il tuo caso d'uso. Allora ho risolto il problema avvolgendo l'applicazione in un file batch e utilizzando un valore di ritorno magico da System.exit()
quello che ho interrogato nel file batch e invece il file batch ha riavviato l'applicazione.
Vecchia domanda e tutto il resto. Ma questo è ancora un altro modo che offre alcuni vantaggi.
Su Windows, potresti chiedere all'utilità di pianificazione di riavviare la tua app per te. Ciò ha il vantaggio di attendere un determinato periodo di tempo prima che l'app venga riavviata. Puoi andare al task manager ed eliminare l'attività e smette di ripetersi.
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);
Simile alla risposta " migliorata " di Yoda , ma con ulteriori miglioramenti (sia funzionali, leggibilità e testabilità). Ora è sicuro da eseguire e si riavvia tante volte quante sono gli argomenti del programma forniti.
JAVA_TOOL_OPTIONS
opzioni.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: puntatore nullo se JAVA_TOOL_OPTIONS non è impostato
Esempio:
$ 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);
Immagino che tu non voglia davvero interrompere l'applicazione, ma "Riavvia". Per questo, puoi usare questo e aggiungere il tuo "Reset" prima del sonno e dopo la finestra invisibile.