Come posso riavviare un'applicazione Java?


94

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 #.


2
Forse non capisco la tua domanda. Vuoi che la tua applicazione abbia un pulsante che riavvia l'applicazione? Quindi, dopo che l'app non è più in esecuzione, dovrebbe essere in grado di riavviarsi da sola? Mi sembra impossibile.
Jay,

Non lo sto chiedendo dopo che JVM si ferma, sto chiedendo che come posso rigenerare il mio frame java principale?
Azfar Niaz

2
Non impossibile. Vedo che il workbench di eclipse si riavvia frequentemente, anche Windows fa questo trucco dopo gli aggiornamenti. Il falso presupposto è che l'applicazione sia l'unica cosa in esecuzione senza niente sotto. Avremo bisogno di un launcher in grado di riavviare, tartarughe fino in fondo.
whatnick

allo stesso modo dell'applicazione C #, dove puoi scrivere System.restart () per farlo?
Azfar Niaz

@aniaz allora dovresti aggiornare la domanda per indicare che vuoi mostrare / nascondere la cornice. L'applicazione NON è il Frame.
whatnick

Risposte:


105

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:

  1. Trova l'eseguibile java (ho usato il binario java qui, ma dipende dalle tue esigenze)
  2. Trova l'applicazione (un barattolo nel mio caso, usando l'estensione MyClassInTheJar classe per trovare la posizione del jar stesso)
  3. Crea un comando per riavviare il jar (usando il binario java in questo caso)
  4. Eseguilo! (e quindi terminare l'applicazione corrente e riavviarla)

5
Non c'è un breve lasso di tempo in cui due versioni della stessa app vengono eseguite contemporaneamente?
Monir

5
System.exit (0) non terminerà il processo figlio?
Horcrux7

16
@Veger Domanda se 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.
Tomáš Zato - Ripristina Monica

8
Tanti commenti che discutono se rispondere o meno alla domanda di @ Horcrux7. Ragazzi, avreste potuto dirgli la risposta dall'inizio lol. Bene, vado avanti e lo faccio (un po 'tardi lo so): no, non lo fa. Là.
Voldemort

10
Per rispondere alle mie domande. Il campione non funziona !!! System.exit (0) termina il processo client immediatamente.
Horcrux7

35
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 .


Eventuali miglioramenti 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").
Chris2M

1
La VM padre potrebbe terminare se la VM figlia e la VM padre non fossero connesse tra loro con pipe, che è ciò che accade nel modo in cui viene avviata la VM figlia. Utilizzando ProcessBuildere inheritIO(), la VM figlia può essere avviata in modo tale da terminare la VM padre.
Christian Hujer

1
Ho una versione di questo in corso. Questo commento è per dirti come fermarlo: rinomina qualcosa nel percorso che contiene java.exe.
Dale

Questo in senso stretto non è il riavvio ma il lancio di una nuova JVM con gli stessi argomenti di questa.
Thorbjørn Ravn Andersen

4
Qual è la differenza? C'è una differenza tra il riavvio di un PC e lo spegnimento del sistema operativo + il riavvio?
Meinersbur

27

Fondamentalmente non puoi. Almeno non in modo affidabile. Tuttavia, non dovresti averne bisogno.

Il non può separarsi

Per riavviare un programma Java, è necessario riavviare la JVM. Per riavviare la JVM è necessario

  1. Individua il programma di javaavvio 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.)

  2. 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.

  3. Se il tuo programma legge l'input dallo Standard.instdin originale verrà perso al riavvio.

  4. Molti di questi trucchi e hack falliranno in presenza di un file SecurityManager.

Non dovrebbe aver bisogno di una parte

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.


3
+1 per una migliore consulenza sulla progettazione; anche se a volte non è possibile, soprattutto se si utilizza JNI, ad esempio.
maeria

Ebbene, una libreria nativa potrebbe modificare lo stato globale che non può essere modificato dall'interfaccia JNI, quindi non ci sarebbe modo di "riavviare" lo stato del programma se non riavviando il processo. Ovviamente, la libreria nativa dovrebbe essere progettata meglio, ma a volte dipendi da cose che non puoi controllare.
maerie

Ok, ma con questo ragionamento, puoi anche avere una libreria Java pura che modifica alcune variabili statiche interne. Questo sarebbe comunque un difetto di progettazione e non dovrebbe verificarsi in librerie ben scritte.
aioobe

1
La tua risposta non è corretta, poiché è perfettamente possibile anche senza applicazioni / demoni esterni come mostrato da Meinersbur e dalla mia risposta. E per scopi di aggiornamento automatico il riavvio di un'applicazione è una buona soluzione, quindi è effettivamente necessario riavviare anche le applicazioni.
Veger

1
Ma si fa utilizzare un'applicazione esterna: java! Stai dimenticando che Java è una specifica del linguaggio, non un programma. E se eseguo il tuo programma usando qualche altra jvm, come ad esempio kaffe ? Ho comunque aggiornato la mia risposta :-)
aioobe

9

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.


soluzione molto interessante. Il problema su OSX è che, in genere, le app Java vengono eseguite da un compilato JavaApplicationStub... Non sono sicuro che ci sia un modo semplice per aggirare questo.
Dan Rosenstark

7

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.


5

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



4

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

3

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.


3

Basta aggiungere informazioni che non sono presenti in altre risposte.

Se procfs /proc/self/cmdline è disponibile

Se stai eseguendo in un ambiente che fornisce procfs e quindi ha il /procfile system disponibile (il che significa che questa non è una soluzione portatile), puoi fare in modo che Java legga /proc/self/cmdlineper 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/cmdlinedisponibili, 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 javabinario. 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.

Non dimenticare di usare 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 stderrgli stessi della VM corrente. Questo è chiamato ereditato . Puoi farlo chiamando la inheritIO()tua ProcessBuilderistanza.

Trappola su Windows

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 .jarfile dell'applicazione con la nuova versione, l'applicazione ha iniziato a comportarsi in modo anomalo e ha fornito eccezioni sul .jarfile. 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.


2

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

2

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.

  • Nessun accumulo di JAVA_TOOL_OPTIONSopzioni.
  • Trova automaticamente la classe principale.
  • Eredita lo stdout / stderr corrente.

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]
$

-13
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.


4
L'utente ha chiesto di riavviare l'applicazione non solo nascondere e mostrare una finestra.
Amr Lotfy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.