Windows サービスとして実行される Java で記述されたサーバーがあります (Install4J のおかげです)。このサービスが実行元の JAR ファイルの最新バージョンをダウンロードして、新しいコードの実行を開始できるようにしたいと考えています。問題は、Windows サービスを完全に終了させたくないということです。
理想的には、現在のバージョンを停止して新しいバージョンを実行する unix スタイルの exec() 呼び出しによってこれを実現します。どうすればこれを達成できますか?
Windows サービスとして実行される Java で記述されたサーバーがあります (Install4J のおかげです)。このサービスが実行元の JAR ファイルの最新バージョンをダウンロードして、新しいコードの実行を開始できるようにしたいと考えています。問題は、Windows サービスを完全に終了させたくないということです。
理想的には、現在のバージョンを停止して新しいバージョンを実行する unix スタイルの exec() 呼び出しによってこれを実現します。どうすればこれを達成できますか?
これは複雑ですが、移植可能な方法です。
コードを 2 つの jar に分割します。プロセスの起動を管理するためだけに、非常に小さな jar が 1 つあります。クラスパスに他の jar を保持する ClassLoader を作成します。
新しいバージョンをロードする場合は、古い jar からコードを実行しているすべてのスレッドを終了します。古い jar からのクラスのインスタンスへのすべての参照を無効にします。古い jar をロードした ClassLoader へのすべての参照を無効にします。この時点で、何も見逃していなければ、古いクラスと ClassLoader はガベージ コレクションの対象になります。
ここで、新しい jar を指す新しい ClassLoader インスタンスで最初からやり直し、アプリケーション コードを再起動します。
Java Service Wrapperは、これ以上のことを行います。
私の知る限り、Java でこれを行う方法はありません。
Runtime.execJavaまたはProcessBuilderの start() コマンド (新しいプロセスを開始する) を使用して、現在のプロセスを終了させることで回避できると思います... ドキュメントの状態
Process オブジェクトへの参照がなくなった場合、サブプロセスは強制終了されませんが、サブプロセスは非同期で実行を続けます。
親が終了してガベージコレクトされた場合も同じことが当てはまると思います。
問題は、Runtime.exec のプロセスに有効な in、out、および err ストリームがなくなることです。
Tomcat、Jetty、JBoss などの組み込みのリロード機能を使用できます。それらはサービスとして実行でき、Web コンテナーまたは Java EE コンテナーとして使用する必要はありません。
その他のオプションは、OSGi と独自のクラス ロード機能です。
ただし、本番環境でのリロードに注意してください。それが常に最善の解決策であるとは限りません。
一般的方法:
import java.io.BufferedInputStream;
import java.util.Arrays;
public class ProcessSpawner {
public static void main(String[] args) {
//You can change env variables and working directory, and
//have better control over arguments.
//See [ProcessBuilder javadocs][1]
ProcessBuilder builder = new ProcessBuilder("ls", "-l");
try {
Process p = builder.start();
//here we just echo stdout from the process to java's stdout.
//of course, this might not be what you're after.
BufferedInputStream stream =
new BufferedInputStream(p.getInputStream());
byte[] b = new byte[80];
while(stream.available() > 0) {
stream.read(b);
String s = new String(b);
System.out.print(s);
Arrays.fill(b, (byte)0);
}
//exit with the exit code of the spawned process.
System.exit(p.waitFor());
} catch(Exception e) {
System.err.println("Exception: "+e.getMessage());
System.exit(1);
}
}
}