Java AWTアプリケーションを再起動するにはどうすればよいですか?イベントハンドラーをアタッチしたボタンがあります。アプリケーションを再起動するには、どのコードを使用する必要がありますか?
C#アプリケーションでApplication.Restart()
するのと同じことをしたい。
もちろん、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)。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でも)実行されたままで、まったく同じことを行います。
WARNING:これを実行する場合、 fork bomb と同様に、新しいプロセスの作成が終了しないことに注意してください。
基本的にできません。少なくとも信頼できる方法ではありません。
Javaプログラムを再起動するには、JVMを再起動する必要があります。 JVMを再起動するために必要なこと
使用されたJava
ランチャーを見つけます。 System.getProperty("Java.home")
で試すこともできますが、これが実際にアプリケーションの起動に使用されたランチャーを指すという保証はありません。 (返される値は アプリケーションの起動に使用されるJREを指していない可能性があります または-Djava.home
で上書きされている可能性があります。)
おそらく、元のメモリ設定など(-Xmx
、-Xms
、…)を尊重する必要があるため、最初のJVMの起動に使用された設定を把握する必要があります。 ManagementFactory.getRuntimeMXBean().getInputArguments()
を使用してみることもできますが、これが使用される設定を反映するという保証はありません。これは、そのメソッドの ドキュメント で綴られています:
通常、「Java」コマンドのすべてのコマンドラインオプションがJava仮想マシンに渡されるわけではありません。したがって、返される入力引数には、すべてのコマンドラインオプションが含まれているとは限りません。
プログラムがStandard.in
から入力を読み取ると、元の標準入力は再起動時に失われます。
これらのトリックやハッキングの多くは、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を終了する必要があります。
通常、Eclipseはプラグインのインストール後に再起動します。 WindowsのラッパーEclipse.exe(ランチャーアプリ)を使用してこれを行います。このアプリケーションはコアEclipseランナーjarを実行し、Eclipse Javaアプリケーションが再起動コードで終了した場合、Eclipse.exeはワークベンチを再起動します。再起動を実現するために、同様のネイティブコード、シェルスクリプト、または別のJavaコードラッパーをビルドできます。
アプリを本当に再起動する必要がある場合、別のアプリを作成して起動することができます...
このページには、さまざまなシナリオのさまざまな例があります。
この質問は古くて回答がありましたが、いくつかのソリューションの問題に出くわし、私の提案をミックスに追加することにしました。
一部のソリューションの問題は、単一のコマンド文字列を作成することです。これにより、一部のパラメーターにスペースが含まれる場合、特に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
にスペースがあるため、引用符で囲むかエスケープする必要があります。大きな問題ではありませんが、特にクロスプラットフォームアプリケーションでは、多少面倒でエラーが発生しやすくなります。
したがって、私のソリューションは、コマンドをarrayとしてビルドします。
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();
}
}
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最小化されたウィンドウでスクリプトを開始する
^&終了後に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
sleep 1 10秒間スリープ
他の回答にはない情報を追加するだけです。
/proc/self/cmdline
が利用可能な場合 procfs を提供し、したがって/proc
ファイルシステムを使用できる環境で実行している場合(これはポータブルソリューションではないことを意味します) 、次のように自分自身を再起動するためにJava read /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には見えず、JavaはCygwinを知らない。
ProcessBuilder.inheritIO()
の使用を忘れないでくださいデフォルトでは、開始されたプロセスのstdin
/stdout
/stderr
(in Java called System.in
/System.out
/System.err
)はに設定されます。 pipes は、現在実行中のプロセスが新しく開始されたプロセスと通信できるようにします。現在のプロセスを再起動する場合、これはであり、ほとんどの場合は必要なものではありません。代わりに、stdin
/stdout
/stderr
が現在のVMのものと同じであることを望むでしょう。これは inherited と呼ばれます。それには、ProcessBuilder
インスタンスのinheritIO()
を呼び出します。
restart()
関数の頻繁な使用例は、更新後にアプリケーションを再起動することです。 Windowsでこれを最後に試したとき、これは問題がありました。アプリケーションの.jar
ファイルを新しいバージョンで上書きすると、アプリケーションは誤動作を開始し、.jar
ファイルに関する例外が発生しました。これがあなたのユースケースである場合に備えて、私はただ伝えています。当時、アプリケーションをバッチファイルでラップし、System.exit()
からのマジックリターン値を使用して、バッチファイルでクエリを実行し、代わりにバッチファイルにアプリケーションを再起動させることで問題を解決しました。
この質問に出くわしたとき、私は自分で主題を研究していました。
答えがすでに受け入れられているという事実にかかわらず、完全性のための代替アプローチを提供したいと思います。特に、Apache Antは非常に柔軟なソリューションとして機能しました。
基本的に、すべてが1つのJava実行タスクを持つAntスクリプトファイルに要約されます( here および here を参照) Javaコードから呼び出されます( here を参照)。このJavaコードはメソッドlaunchである可能性があり、再起動が必要なアプリケーションの一部である可能性があります。アプリケーションは、Apache Antライブラリー(jar)に依存する必要があります。
アプリケーションを再起動する必要があるときはいつでも、メソッドlaunchを呼び出してVMを終了する必要があります。 Ant Javaタスクにはオプションforkおよびspawnが必要です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);
}
ここで非常に便利なことは、アプリケーションの初期起動と再起動に同じスクリプトが使用されることです。
ヨーダの「 improved 」の回答に似ていますが、さらに改善されています(機能、読みやすさ、テスト容易性の両方)。実行しても安全になり、指定されたプログラム引数の数だけ再起動します。
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]
$
古い質問とそのすべて。しかし、これはいくつかの利点を提供するさらに別の方法です。
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);