質問

Java AWTアプリケーションを再起動するにはどうすればよいですか?イベントハンドラーを添付したボタンがあります。アプリケーションを再起動するためにどのコードを使用する必要がありますか?

同じことをしたいです Application.Restart() C#アプリケーションで行います。

役に立ちましたか?

解決

もちろん、Javaアプリケーションを再起動することができます。

次の方法は、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);
}

基本的には次のことを行います。

  1. Java実行可能ファイルを見つけます(ここでJavaバイナリを使用しましたが、それはあなたの要件に依存します)
  2. アプリケーションを見つけます(私の場合は、 MyClassInTheJar JARの位置自体を見つけるためのクラス)
  3. 瓶を再起動するコマンドを作成します(この場合はJavaバイナリを使用)
  4. それを実行してください! (したがって、現在のアプリケーションを終了し、再び開始します)

他のヒント

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

それは不可能だと言うすべての人々に捧げます。

このプログラムは、元のコマンドラインを再構築するために利用可能なすべての情報を収集します。次に、それを起動し、まったく同じコマンドであるため、アプリケーションは2回目に開始されます。次に、元のプログラムを終了します。子供プログラムは実行中(Linuxの下でも)を維持し、まったく同じことを行います。

警告: :これを実行した場合は、と同様に新しいプロセスを作成することが終了しないことに注意してください フォーク爆弾.

基本的に、あなたはできません。少なくとも信頼できる方法ではありません。

Javaプログラムを再起動するには、JVMを再起動する必要があります。必要なJVMを再起動するには

  1. を見つけます java 使用されたランチャー。一緒に試してみることができます System.getProperty("java.home") しかし、これが実際にアプリケーションの起動に使用されたランチャーを指し示すという保証はありません。 (返された値 アプリケーションの起動に使用されていたJREを指すことはないかもしれません または、それによってオーバーライドされた可能性があります -Djava.home.)

  2. おそらく、元のメモリ設定などを称えたいと思うでしょう(-Xmx, -Xms, 、…)したがって、最初のJVMを開始するために使用される設定を把握する必要があります。使用してみてください ManagementFactory.getRuntimeMXBean().getInputArguments() しかし、これが使用される設定を反映するという保証はありません。これはでさえ綴られています ドキュメント その方法の:

    通常、「Java」コマンドのすべてのコマンドラインオプションがJava仮想マシンに渡されるわけではありません。したがって、返された入力引数には、すべてのコマンドラインオプションが含まれない場合があります。

  3. プログラムが入力を読み取る場合 Standard.in 元のstdinは再起動時に紛失されます。

  4. これらのトリックとハックの多くは、 SecurityManager.


一方で: 必要はないはずです。

アプリケーションを設計することをお勧めします。これにより、すべてのものを簡単にクリーンアップでき、その後、「メイン」クラスの新しいインスタンスを作成することをお勧めします。

多くのアプリケーションは、メインメソッドにインスタンスを作成する以外に何もしないように設計されています。

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

このパターンを使用することにより、次のようなことをするのに十分簡単なはずです。

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

とさせてください launch() アプリケーションを再起動する必要がある方法でシャットダウンされた場合にのみTrueを返します。

厳密に言えば、Javaプログラムは、実行中のJVMを殺してから再び開始する必要があるため、再起動できませんが、JVMが実行されなくなったら、アクションを実行できなくなります。

AWTコンポーネントを再度読み込み、梱包し、起動するためのカスタムクラスローダーを使用していくつかのトリックを行うことができますが、これにより、GUIイベントループに関して多くの頭痛を引き起こす可能性があります。

アプリケーションの起動方法に応じて、JVMが特定のコードで終了している間に続くdo/whileループを含むラッパースクリプトでJVMを起動することができます。 System.exit(RESTART_CODE). 。たとえば、Scripting Pseudocodeで:

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

AWTアプリは、再起動を必要としない「通常の」終了時のRestART_Code以外のものを使用してJVMを終了する必要があります。

通常、Eclipseはプラグインがインストールされた後に再起動します。彼らは、Windows用のラッパーEclipse.exe(ランチャーアプリ)を使用してこれを行います。このアプリケーションは、Core Eclipse Runner Jarを実行し、Eclipse Javaアプリケーションがリニューアルコードで終了する場合、Eclipse.exeがワークベンチを再起動します。同様のネイティブコード、シェルスクリプト、または別のJavaコードラッパーを作成して、再起動を実現できます。

本当にアプリを再起動する必要がある場合は、別のアプリを作成することができます...

このページは、さまざまなシナリオのさまざまな例を示しています。

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

ウィンドウズ

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 最小化されたウィンドウでスクリプトを開始します

^& 終了後にCMDウィンドウを閉じるために終了します

サンプルCMDスクリプトはそうかもしれません

@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

睡眠10 10秒間眠ります

この質問は古く、答えられていますが、私はいくつかの解決策の問題に出くわし、私の提案をミックスに追加することにしました。

いくつかのソリューションの問題は、単一のコマンド文字列を構築することです。これにより、いくつかのパラメーターがスペース、特にスペースが含まれている場合に問題が発生します Java.home.

たとえば、Windowsでは、ライン

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

このようなものを返すかもしれません:C:\Program Files\Java\jre7\bin\java

この文字列は、のスペースのために引用符に包まれたり、逃げたりする必要があります Program Files. 。大きな問題ではありませんが、特にクロスプラットフォームアプリケーションでは、やや迷惑でエラーが発生しやすいです。

したがって、私のソリューションは、次のようにコマンドを構築します 配列 コマンドの:

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

この質問に出会ったとき、私は自分で主題を研究していました。

答えがすでに受け入れられているという事実に関係なく、私はまだ完全性のために代替アプローチを提供したいと思います。具体的には、Apache Antは非常に柔軟なソリューションとして機能しました。

基本的に、すべてが1つのJava実行タスクを備えたAntスクリプトファイルに要約されます(参照 ここここ)Javaコードから呼び出された(参照 ここ)。このJavaコードは、方法になる可能性があります 発売, 、再起動する必要があるアプリケーションの一部である可能性があります。アプリケーションは、Apache Ant Library(JAR)に依存する必要があります。

アプリケーションを再起動する必要があるときはいつでも、メソッドを呼び出す必要があります 発売 VMを終了します。 Ant Javaタスクにはオプションが必要です フォークスポーン trueに設定します。

これがアリのスクリプトの例です。

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

のコード 発売 メソッドは次のように見えるかもしれません:

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

}

ここで非常に便利なのは、同じスクリプトが最初のアプリケーションの起動と再起動に使用されることです。

他の回答に存在しない情報を追加するだけです。

もしも procfs /proc/self/cmdline 利用可能です

提供する環境で実行している場合 procfs したがって、 /proc 利用可能なファイルシステム(これはポータブルソリューションではないことを意味します)、Javaに読むことができます /proc/self/cmdline このように再起動するために:

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

でシステムについて /proc/self/cmdline 利用可能な、これはおそらくJavaから現在のJavaプロセスを「再起動」する最もエレガントな方法です。 JNIは関与しておらず、パスやものの推測は必要ありません。これはまた、に渡されたすべてのJVMオプションの世話をします java バイナリ。コマンドラインは、現在のJVMプロセスの1つとまったく同じです。

GNU/Linux(Androidを含む)を含む多くのUNIXシステムが最近持っています procfs ただし、FreeBSDのような一部の場合、それは非推奨であり、段階的に廃止されています。 Mac OS X その意味での例外です procfsはありません. ウィンドウズ また持っていません procfs. 。 Cygwinは持っています procfs しかし、JavaはWindowsシステム呼び出しの代わりにCygwin DLLを使用してアプリケーションにのみ表示されるため、Javaには見えません。JavaはCygwinに気付いていません。

使用することを忘れないでください ProcessBuilder.inheritIO()

デフォルトはそれです stdin / stdout / stderr (Javaが電話をかけました System.in / System.out / System.err)開始プロセスはに設定されています パイプ これにより、現在実行中のプロセスが新しく開始されたプロセスと通信できます。現在のプロセスを再起動したい場合、これは おそらくあなたが望むものではありません. 。代わりに、あなたはそれを望むでしょう stdin / stdout / stderr 現在のVMのものと同じです。これは呼ばれます 遺伝性の. 。あなたは電話してそうすることができます inheritIO() あなたの ProcessBuilder 実例。

Windowsの落とし穴

の頻繁なユースケースa restart() 機能は、更新後にアプリケーションを再起動することです。 Windowsで最後にこれを試したとき、これは問題がありました。アプリケーションを上書きするとき .jar 新しいバージョンにファイルをファイルすると、アプリケーションは誤動作し始め、 .jar ファイル。これがあなたのユースケースである場合に備えて、私はただ言っています。当時、私はアプリケーションをバッチファイルに包み、からのマジックリターン値を使用して問題を解決しました。 System.exit() バッチファイルにクエリをし、代わりにバッチファイルがアプリケーションを再起動したこと。

古い質問とそのすべて。しかし、これはいくつかの利点を提供するさらに別の方法です。

Windowsでは、タスクスケジューラにアプリを再度起動するように依頼することができます。これには、アプリが再起動される前に特定の時間を待つという利点があります。タスクマネージャーに移動してタスクを削除すると、繰り返しが停止します。

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

ヨーダに似ています改善'答え、しかし、さらなる改善があります(機能的、読みやすさ、そしてテスト可能性の両方)。現在、実行しても安全であり、プログラムの引数の量が与えられたと同じくらい数倍に再起動します。

  • の蓄積はありません JAVA_TOOL_OPTIONS オプション。
  • メインクラスを自動的に見つけます。
  • 現在の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:java_tool_optionsが設定されていない場合のnullポインター


例:

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

私はあなたが本当にアプリケーションを停止したくないのではなく、それを「再起動」したいと思います。そのためには、これを使用して、睡眠前と目に見えないウィンドウの後に「リセット」を追加できます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top