Javaアプリケーションを再起動するにはどうすればよいですか?


94

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

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


2
多分私はあなたの質問を理解していません。アプリケーションに、アプリケーションを再起動するボタンを付けたいですか?それで、アプリが実行されなくなったら、それ自体を再起動できるはずですか?それは私には不可能に思えます。
Jay

JVMが停止した後で、メインのJavaフレームをどのように再生成できるかを尋ねているのではありませんか?
Azfar Niaz、2010

2
不可能ではない。私はeclipseワークベンチが頻繁に再起動することを確認しています。更新後もWindowsでこのトリックが実行されます。誤った仮定は、アプリケーションはその下に何もない状態で実行される唯一のものであることです。私たちは再起動可能なランチャーが必要です、カメはずっと下にあります。
whatnick

C#アプリケーションと同じように、uはSystem.restart()を書いてそれを行うことができますか?
Azfar Niaz 2010

@aniaz次に、質問を更新して、フレームを表示/非表示にすることを指摘する必要があります。アプリケーションはフレームではありません。
whatnick

回答:


104

もちろん、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 、クラスを使用してjarの場所自体を見つける)
  3. jarを再起動するコマンドを作成します(この場合はjavaバイナリを使用します)
  4. 実行してください!(したがって、現在のアプリケーションを終了して再起動します)

5
同じアプリの2つのバージョンが同時に実行される短い時間枠はありませんか?
Monir

5
System.exit(0)は子プロセスを終了しませんか?
Horcrux7 2015

16
@Veger System.exit(0)子プロセスを終了するかどうかの質問には、この回答が実際に機能するかどうか、およびその理由と同じ回答があります。あなたの答えと一緒に賢明な説明を提供できない場合、あなたは悪い仕事をしました。回答よりも多くの質問を提供する回答は、完全な回答の例ではありません。良い答えは、コードを示すだけでなく、それらがどのようにそしてなぜ機能するか、欠点は何であり、代替策は何かについても説明します。あなたはこれらのものをカバーしようとさえしませんでした。
トマシュザト-2015年

8
@ Horcrux7の質問に回答するかどうかについて議論するコメントがたくさんあります。君たちは最初から答えを彼に言っただけかもしれない笑 ええと、私は先に進み、それを実行します(少し遅れて知っています):いいえ、それはしません。そこ。
Voldemort 2015

10
私の質問に自分で答える。サンプルは動作しません!!! System.exit(0)は、クライアントプロセスをすぐに終了します。
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);
    }
}

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

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

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


可能な改善 ManagementFactory.getRuntimeMXBean().getInputArguments()は、JVMに渡される入力引数のみを提供します。アプリケーションに渡されたパラメーターがありません。例えば、java -jar start.jar -MISSED_PARAM=true。oracle jvmでは、を使用してこれらのパラメーターを取得できますSystem.getProperty("sun.java.command")
Chris2M、2014

1
子VMと親VMがパイプで相互に接続されていない場合、親VMが終了する可能性があります。これが、子VMの起動方法です。ProcessBuilderおよびを使用するとinheritIO()、親VMが終了するように子VMを起動できます。
Christian Hujer 2015年

1
私はこれのバージョンを手に入れた。このコメントは、それを停止する方法を説明するためのものです。java.exeを含むパスの名前を変更してください。
2016年

これは厳密には再起動ではなく、これと同じ引数で新しいJVMを起動することです。
–ThorbjørnRavn Andersen 2017

4
違いは何ですか?PCの再起動とOSのシャットダウンとそれを再起動することの間に違いはありますか?
Meinersbur 2017年

27

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

できない部分

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ます。

必要はありません一部を

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

多くのアプリケーションは、mainメソッドでインスタンスを作成するだけです。

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を返します。


3
より良い設計アドバイスのための+1。ただし、特にJNIを使​​用している場合などは、それが不可能な場合もあります。
maerics 2010

まあ、ネイティブライブラリは、JNIインターフェイスからは変更できないグローバル状態を変更する可能性があるため、プロセスを再起動する以外にプログラムの状態を「再起動」する方法はありません。もちろん、ネイティブライブラリの方が優れているはずですが、制御できないものに依存している場合があります。
maerics 2010

わかりましたが、その理由により、いくつかの内部静的変数を変更する純粋なJavaライブラリを使用することもできます。ただし、これは設計上の欠陥であり、適切に作成されたライブラリでは発生しません。
aioobe 2010

1
Meinersburと私の答えが示すように、外部アプリケーション/デーモンがなくても完全に可能であるため、あなたの答えは正しくありません。また、自己更新を目的とする場合、アプリケーションの再起動は優れたソリューションであるため、実際にはアプリケーションの再起動も必要です。
Veger 2010年

1
しかし、あなたが行う外部アプリケーションを使用しますjava!Javaはプログラムではなく言語仕様であることを忘れています。たとえばkaffeなどの他のjvmを使用してプログラムを実行するとどうなりますか?とにかく私の答えを更新しました:-)
aioobe

9

厳密に言うと、Javaプログラムは、それが実行されているJVMを強制終了してから再起動する必要があるため、再起動できません。ただし、JVMが実行されなくなった(強制終了した)場合、アクションは実行できません。

カスタムクラスローダーを使用して、AWTコンポーネントを再度ロード、パック、および起動するいくつかのトリックを実行できますが、これにより、GUIイベントループに関して多くの問題が発生する可能性があります。

アプリケーションの起動方法に応じて、do / whileループを含むラッパースクリプトでJVMを起動できます。このループは、JVMが特定のコードで終了するまで続行され、AWTアプリはを呼び出す必要がありますSystem.exit(RESTART_CODE)。たとえば、スクリプトの擬似コードでは:

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

AWTアプリは、再起動を必要としない「通常」の終了時にRESTART_CODE以外の何かでJVMを終了する必要があります。


非常に興味深い解決策。OSXでの問題は、通常、Javaアプリはコンパイルされたアプリケーションから実行されるJavaApplicationStubことです。簡単な方法があるかどうかはわかりません。
Dan Rosenstark、2012

7

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


5

ウィンドウズ

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秒間スリープ



4

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

一部のソリューションの問題は、単一のコマンド文字列を作成することです。これにより、一部のパラメータにスペースが含まれる場合、特に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();
        }
    }

3

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

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

基本的に、すべてが単一のJava実行タスクでのAntスクリプトファイルに沸く(参照してくださいここここに Javaコードから呼び出された)(参照ここ)。メソッドの起動である可能性があるこのJavaコードは、再起動する必要があるアプリケーションの一部である可能性があります。アプリケーションは、Apache Antライブラリ(jar)に依存している必要があります。

アプリケーションを再起動する必要がある場合は常に、メソッドlaunchを呼び出してVMを終了する必要があります。Ant Javaタスクには、フォークスポーンのオプションが必要ですをtrueに設定する。

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>

launchメソッドのコードは次のようになります。

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

}

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


3

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

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は関与せず、パスやその他のものを推測する必要もありません。これにより、javaバイナリに渡されるすべてのJVMオプションも処理されます。コマンドラインは、現在のJVMプロセスとまったく同じです。

現在、GNU / Linux(Androidを含む)を含む多くのUNIXシステムにはprocfsがありますが、FreeBSDのような一部のシステムでは非推奨になり、段階的に廃止されます。Mac OS Xは、procfsがないという点で例外です。Windowsにprocfsはありません。Cygwinにはprocfsがありますがあり、Windowsシステムコールの代わりにCygwin DLLを使用するアプリケーションからしか見えず、JavaはCygwinを認識しないため、Javaからは見えません。

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

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

Windowsの落とし穴

restart()関数の頻繁な使用例は、更新後にアプリケーションを再起動することです。私が最後にWindowsでこれを試したとき、これは問題がありました。アプリケーションの.jarファイルを新しいバージョンで上書きすると、アプリケーションは正常に動作しなくなり、.jarファイルに関する例外が発生しました。これがあなたのユースケースである場合のために、私はちょうど言っています。当時、私はアプリケーションをバッチファイルでラップし、バッチファイルでSystem.exit()照会した魔法の戻り値を使用して問題を解決し、代わりにバッチファイルでアプリケーションを再起動しました。


2

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

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

2

ヨーダの「改善された」答えに似ていますが、さらに改善されています(機能、読みやすさ、およびテスト容易性の両方)。これで安全に実行でき、指定されたプログラム引数の数だけ再起動します。

  • 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バグ修正: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]
$

-13
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
    Thread.sleep(600);
} catch (InterruptedException e1) {
    e1.printStackTrace();
}
setVisible(true);

本当にアプリケーションを停止するのではなく、「再起動」する必要があると思います。そのためには、これを使用して、スリープ前と非表示ウィンドウの後に「リセット」を追加します。


4
ユーザーは、ウィンドウを非表示にして表示するだけでなく、アプリケーションを再起動するように求めました。
Amr Lotfy 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.