it-swarm.com.de

Wie kann ich eine Java Anwendung neu starten?

Wie kann ich eine Java AWT-Anwendung neu starten? Ich habe eine Schaltfläche, an die ich einen Ereignishandler angefügt habe. Mit welchem ​​Code soll ich die Anwendung neu starten?

Ich möchte das Gleiche tun wie Application.Restart() in einer C # -Anwendung.

90
Azfar Niaz

Natürlich ist es möglich, eine Java Anwendung neu zu starten.

Die folgende Methode zeigt eine Möglichkeit, eine Java -Anwendung neu zu starten:

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

Grundsätzlich macht es folgendes:

  1. Finden Sie die Java ausführbare Datei (Ich habe die Java Binärdatei hier verwendet, aber das hängt von Ihren Anforderungen ab)
  2. Suchen Sie die Anwendung (in meinem Fall eine JAR-Datei, verwenden Sie die Klasse MyClassInTheJar, um den JAR-Speicherort selbst zu ermitteln).
  3. Erstellen Sie einen Befehl, um das jar neu zu starten (in diesem Fall verwenden Sie die Java Binärdatei)
  4. Führ es aus! (und damit die aktuelle Anwendung beenden und neu starten)
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);
    }
}

Allen gewidmet, die sagen, dass es unmöglich ist.

Dieses Programm sammelt alle verfügbaren Informationen, um die ursprüngliche Befehlszeile wiederherzustellen. Anschließend wird es gestartet, und da es sich um denselben Befehl handelt, wird Ihre Anwendung ein zweites Mal gestartet. Dann verlassen wir das ursprüngliche Programm, das untergeordnete Programm läuft weiter (auch unter Linux) und macht genau dasselbe.

[~ # ~] Warnung [~ # ~] : Wenn Sie dies ausführen, beachten Sie, dass es nie endet, neue Prozesse zu erstellen, ähnlich wie bei einem - Gabelbombe .

34
Meinersbur

Im Grunde kann man nicht. Zumindest nicht zuverlässig.

Um ein Java Programm neu zu starten, müssen Sie die JVM neu starten. Um die JVM neu zu starten, müssen Sie

  1. Suchen Sie den verwendeten Starter Java. Sie können es mit System.getProperty("Java.home") versuchen, es gibt jedoch keine Garantie dafür, dass dies tatsächlich auf den Starter verweist, der zum Starten Ihrer Anwendung verwendet wurde. (Der zurückgegebene Wert verweist möglicherweise nicht auf die JRE, mit der die Anwendung gestartet wurde oder wurde möglicherweise von -Djava.home Überschrieben.)

  2. Vermutlich möchten Sie die ursprünglichen Speichereinstellungen usw. (-Xmx, -Xms, ...) berücksichtigen, um herauszufinden, welche Einstellungen zum Starten der ersten JVM verwendet wurden. Sie könnten versuchen, ManagementFactory.getRuntimeMXBean().getInputArguments() zu verwenden, aber es gibt keine Garantie dafür, dass dies die verwendeten Einstellungen widerspiegelt. Dies wird sogar in der Dokumentation dieser Methode geschrieben:

    In der Regel werden nicht alle Befehlszeilenoptionen für den Befehl 'Java' an die virtuelle Maschine Java) übergeben. Daher enthalten die zurückgegebenen Eingabeargumente möglicherweise nicht alle Befehlszeilenoptionen.

  3. Wenn Ihr Programm Eingaben von Standard.in Liest, geht die ursprüngliche Standardeingabe beim Neustart verloren.

  4. Viele dieser Tricks und Hacks schlagen fehl, wenn ein SecurityManager vorhanden ist.


Andererseits: Das solltest du nicht müssen.

Ich empfehle Ihnen, Ihre Anwendung so zu gestalten, dass Sie einfach alles bereinigen und anschließend eine neue Instanz Ihrer "Haupt" -Klasse erstellen können.

Viele Anwendungen sind so konzipiert, dass sie nur eine Instanz in der main-Methode erstellen:

public class MainClass {
    ...
    public static void main(String[] args) {
        new MainClass().launch();
    }
    ...
}

Mit diesem Muster sollte es einfach genug sein, Folgendes zu tun:

public class MainClass {
    ...
    public static void main(String[] args) {
        boolean restart;
        do {
            restart = new MainClass().launch();
        } while (restart);
    }
    ...
}

und lassen Sie launch() nur dann true zurückgeben, wenn die Anwendung so heruntergefahren wurde, dass sie neu gestartet werden muss.

22
aioobe

Streng genommen kann sich ein Java Programm nicht neu starten, da es dazu die JVM, in der es ausgeführt wird, beenden und dann erneut starten muss, aber sobald die JVM dann nicht mehr ausgeführt wird (beendet wird) Es können keine Maßnahmen ergriffen werden.

Mit benutzerdefinierten Klassenladeprogrammen können Sie einige Tricks ausführen, um die AWT-Komponenten erneut zu laden, zu packen und zu starten. Dies wird jedoch wahrscheinlich viele Kopfschmerzen im Hinblick auf die GUI-Ereignisschleife verursachen.

Abhängig davon, wie die Anwendung gestartet wird, können Sie die JVM in einem Wrapper-Skript starten, das eine do/while-Schleife enthält. Diese Schleife wird fortgesetzt, während die JVM mit einem bestimmten Code beendet wird. Anschließend muss die AWT-App System.exit(RESTART_CODE) aufrufen. . Zum Beispiel beim Scripting von Pseudocode:

DO
  # Launch the awt program
  EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)

Die AWT-App sollte die JVM bei "normaler" Beendigung, die keinen Neustart erfordert, mit etwas anderem als RESTART_CODE beenden.

8
maerics

Eclipse wird normalerweise nach der Installation eines Plugins neu gestartet. Sie tun dies mit einem Wrapper Eclipse.exe (Launcher-App) für Windows. Diese Anwendung führt die zentrale Eclipse-Runner-JAR aus. Wenn die Anwendung Eclipse Java mit einem Neustartcode beendet wird, startet Eclipse.exe die Workbench neu. Sie können ein ähnliches Stück systemeigenen Codes, Shell-Skripts oder einen anderen Java Code-Wrapper erstellen, um den Neustart durchzuführen.

7
whatnick

Wenn Sie Ihre App wirklich neu starten müssen, können Sie eine separate App schreiben, um sie zu starten ...

Diese Seite enthält viele verschiedene Beispiele für verschiedene Szenarien:

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

4
Sean W.

Obwohl diese Frage alt und beantwortet ist, bin ich auf ein Problem mit einigen der Lösungen gestoßen und habe beschlossen, meinen Vorschlag in die Mischung aufzunehmen.

Das Problem bei einigen Lösungen ist, dass sie eine einzelne Befehlszeichenfolge erstellen. Dies führt zu Problemen, wenn einige Parameter Leerzeichen enthalten, insbesondere Java.home .

Zum Beispiel unter Windows die Linie

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

Könnte so etwas zurückgeben: C:\Program Files\Java\jre7\bin\Java

Diese Zeichenfolge muss in Anführungszeichen gesetzt oder durch Leerzeichen in Program Files Maskiert werden. Kein großes Problem, aber etwas nervig und fehleranfällig, insbesondere bei plattformübergreifenden Anwendungen.

Deshalb baut meine Lösung den Befehl als Array von Befehlen auf:

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 um das Skript im minimierten Fenster zu starten

^ & Beenden, um das Cmd-Fenster nach dem Beenden zu schließen

ein Beispiel für ein Cmd-Skript könnte sein

@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

Schlaf 1 Schlaf für 10 Sekunden

4
Amr Lotfy

Hinzufügen von Informationen, die in anderen Antworten nicht vorhanden sind.

Wenn procfs/proc/self/cmdline Verfügbar ist

Wenn Sie in einer Umgebung arbeiten, die procfs bereitstellt und daher das Dateisystem /proc Zur Verfügung hat (was bedeutet, dass dies kein portables System ist) Lösung), können Sie Java read /proc/self/cmdline, um sich neu zu starten, wie folgt:

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

Auf Systemen mit /proc/self/cmdline Ist dies wahrscheinlich die eleganteste Methode, um den aktuellen Java) - Prozess von Java aus neu zu starten Dies erledigt auch alle JVM-Optionen, die an die Binärdatei Java übergeben werden. Die Befehlszeile ist exakt identisch mit der des aktuellen JVM-Prozesses.

Viele UNIX-Systeme, einschließlich GNU/Linux (einschließlich Android), haben heutzutage procfs . Bei manchen wie FreeBSD ist dies jedoch veraltet und läuft aus. Mac OS X ist eine Ausnahme in dem Sinne, dass es keine procfs hat. Windows hat auch nicht procfs. Cygwin hat procfs, ist jedoch für Java) unsichtbar, da es nur für Anwendungen sichtbar ist, die die Cygwin-DLLs anstelle von Windows-Systemaufrufen verwenden, und Java) = weiß nichts von Cygwin.

Vergiss nicht ProcessBuilder.inheritIO() zu benutzen

Die Standardeinstellung ist, dass stdin/stdout/stderr (in Java genannt System.in/System.out/System.err) Des gestarteten Prozesses sind auf Pipes gesetzt, wodurch der aktuell laufende Prozess mit dem neu gestarteten Prozess kommunizieren kann. Wenn Sie den aktuellen Prozess neu starten möchten, ist dies - höchstwahrscheinlich nicht das, was Sie wollen. Stattdessen möchten Sie, dass stdin/stdout/stderr mit denen der aktuellen VM identisch sind geerbt. Sie können dies tun, indem Sie inheritIO() Ihrer ProcessBuilder -Instanz aufrufen.

Fallstricke unter Windows

Ein häufiger Anwendungsfall einer restart() -Funktion ist der Neustart der Anwendung nach einem Update. Das letzte Mal, als ich das unter Windows ausprobierte, war das problematisch. Als die .jar - Datei der Anwendung mit der neuen Version überschrieben wurde, begann sich die Anwendung schlecht zu verhalten und es gab Ausnahmen bezüglich der .jar - Datei. Ich sage nur, falls dies Ihr Anwendungsfall ist. Damals löste ich das Problem, indem ich die Anwendung in eine Batch-Datei einbaute und einen magischen Rückgabewert von System.exit() verwendete, den ich in der Batch-Datei abfragte und die Batch-Datei stattdessen die Anwendung neu starten ließ.

3
Christian Hujer

Ich habe das Thema selbst recherchiert, als ich auf diese Frage gestoßen bin.

Ungeachtet der Tatsache, dass die Antwort bereits akzeptiert wird, möchte ich der Vollständigkeit halber noch einen alternativen Ansatz anbieten. Insbesondere diente Apache Ant als sehr flexible Lösung.

Grundsätzlich läuft alles auf eine Ant-Skript-Datei mit einer einzigen Java -Ausführungsaufgabe hinaus (siehe hier und hier ) wird aus einem Java Code aufgerufen (siehe hier ). Dieser Java Code, der eine Methode sein kann launch, könnte Teil der Anwendung sein, die neu gestartet werden muss. Die Anwendung muss von der Apache Ant-Bibliothek (jar) abhängig sein.

Immer wenn eine Anwendung neu gestartet werden muss, sollte sie method launch aufrufen und die VM beenden. Für die Ant-Task Java sollten die Optionen fork und spawn auf true gesetzt sein.

Hier ist ein Beispiel für ein Ant-Skript:

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

Der Code für die launch Methode könnte ungefähr so ​​aussehen:

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

}

Eine sehr praktische Sache ist, dass das gleiche Skript sowohl für den Start der ersten Anwendung als auch für den Neustart verwendet wird.

3
01es

Ähnlich wie bei Yodas ' verbessert ' Antwort, aber mit weiteren Verbesserungen (sowohl funktional als auch lesbar und testbar). Die Ausführung ist jetzt sicher und der Neustart erfolgt so oft wie die Anzahl der angegebenen Programmargumente.

  • Keine Anhäufung von Java_TOOL_OPTIONS Optionen.
  • Findet automatisch die Hauptklasse.
  • Übernimmt die aktuelle stdout/stderr.

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: Nullzeiger wenn Java_TOOL_OPTIONS nicht gesetzt ist


Beispiel:

$ 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

Alte Frage und das alles. Dies ist jedoch eine weitere Möglichkeit, die einige Vorteile bietet.

Unter Windows können Sie den Taskplaner auffordern, Ihre App erneut für Sie zu starten. Dies hat den Vorteil, dass Sie eine bestimmte Zeit warten müssen, bevor die App neu gestartet wird. Sie können zum Task-Manager gehen und den Task löschen. Der Vorgang wird dann nicht mehr wiederholt.

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