Java AWTアプリケーションを再起動するにはどうすればよいですか?イベントハンドラーをアタッチしたボタンがあります。アプリケーションを再起動するにはどのコードを使用すればよいですか?
Application.Restart()
C#アプリケーションと同じことをしたいのですが。
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);
}
基本的には次のことを行います。
MyClassInTheJar
はjar 、クラスを使用してjarの場所自体を見つける)System.exit(0)
子プロセスを終了するかどうかの質問には、この回答が実際に機能するかどうか、およびその理由と同じ回答があります。あなたの答えと一緒に賢明な説明を提供できない場合、あなたは悪い仕事をしました。回答よりも多くの質問を提供する回答は、完全な回答の例ではありません。良い答えは、コードを示すだけでなく、それらがどのようにそしてなぜ機能するか、欠点は何であり、代替策は何かについても説明します。あなたはこれらのものをカバーしようとさえしませんでした。
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")
。
ProcessBuilder
およびを使用するとinheritIO()
、親VMが終了するように子VMを起動できます。
基本的にはできません。少なくとも信頼できる方法ではありません。ただし、必要はありません。
Javaプログラムを再起動するには、JVMを再起動する必要があります。JVMを再起動するには、次のことを行う必要があります。
java
使用されたランチャーを見つけます。試すこともできますがSystem.getProperty("java.home")
、これが実際にアプリケーションの起動に使用されたランチャーを指しているという保証はありません。(返された値は、アプリケーションの起動に使用されたJREを指していないか、またはによってオーバーライドされている可能性があります-Djava.home
。)
あなたはおそらく(元のメモリ設定などを尊重したい-Xmx
、-Xms
あなたが設定は最初のJVMを起動するために使用され把握する必要があるので...)。使用してみることができますがManagementFactory.getRuntimeMXBean().getInputArguments()
、これが使用された設定を反映することを保証するものではありません。これは、そのメソッドのドキュメントでも詳しく説明されています。
通常、「java」コマンドのすべてのコマンドラインオプションがJava仮想マシンに渡されるわけではありません。したがって、返される入力引数には、すべてのコマンドラインオプションが含まれていない場合があります。
プログラムStandard.in
が元のstdin から入力を読み取る場合、再起動時に失われます。
これらのトリックやハックの多くは、の存在下では失敗し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を返します。
厳密に言うと、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を終了する必要があります。
JavaApplicationStub
ことです。簡単な方法があるかどうかはわかりません。
ウィンドウズ
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は非常に柔軟なソリューションとして機能しました。
基本的に、すべてが単一の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);
}
ここで非常に便利なことは、同じスクリプトがアプリケーションの初回起動時と再起動時に使用されることです。
他の回答にはない情報を追加するだけです。
/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
インスタンス。
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
オプションの蓄積なし。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]
$
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
Thread.sleep(600);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
setVisible(true);
本当にアプリケーションを停止するのではなく、「再起動」する必要があると思います。そのためには、これを使用して、スリープ前と非表示ウィンドウの後に「リセット」を追加します。