web-dev-qa-db-ja.com

実行中にJARを更新する

JarがJVM内で実行されている場合、現在実行中のJarをアンロードして、システムから削除することができます。新しいバージョンをダウンロードし、最後のJarと同じ名前に名前を変更してから、新しいJarを初期化して、JVM内でJarのシームレスな更新を作成します。このアクションを実行するようにJVMに指示することも可能ですか?実行中にJarを更新することも可能ですか?

27
James Moore

新しいバージョンをダウンロードし、最後のJarと同じ名前に名前を変更してから、新しいJarを初期化して、JVM内でJarのシームレスな更新を作成します...実行中にJarを更新することも可能ですか?

JARファイルは「実行中」ではなく、JVMが実行中です。 JARファイルには、JVMに役立つ作業を行うためのクラス情報(バイトコード命令)が含まれているだけです。ほとんどの場合、JVMは実際にはJARファイルにシステムロックをかけないため、そのファイルを心ゆくまで置き換えることができます。

もちろん、本当の問題は、JVMがJARをロードすると、ロードしたものとうまく連携し、何度上書きしてもJARファイルから読み取られることはないということです。これはデフォルトのクラスローダーの動作であり、変更することはできませんが、他の人が指摘しているように、デフォルトのクラスローダーを使用する必要はありません。更新されたJARSをファイルシステムからロードするために、Webアプリケーションサーバーが使用するものと同様に、独自の実装を行うことができます。ただし、注意点-独自のクラスローダーを定義することは、自分が何をしているのかを本当に理解していない限り、「悪いアイデア™」と見なされます。あなたはもっと読むことができます ここここ

26
Perception

これは私が以前に何度も行ったことがある(そして自分自身も行ったことがある)ことです。発生する可能性のある問題/解決策のいくつかのポイントを構成しました。

  • 後で使用するJARファイルを上書きすると、JVMはダンプでクラッシュします。
    • 後で言うと、クラスはかなり怠惰にロードされ、プログラムの後半でしかロードされないものもあります
    • JVMにはJARファイルのオープンハンドルがあり、JARとポインタが間違っているとlibは失敗します
    • JARファイルからすべてのクラスとリソースをプリロードすることで確率を下げることができます
    • カスタムクラスローダーがある場合は、自分でハンドルを閉じることができます。
  • クラスのロードがどのように行われるかを知る必要があります。まだコントロールできるほうがいい。
    • JARごとにクラスローダーを作成し、バージョン管理を管理するカスタムクラスローダー
    • アプリケーションがクラスローダーをどのように使用し、新しいJARでどのように機能するかを理解します(たとえば、WARアーカイブを上書きするときにTomcatが何をするかを確認します)
  • Windowsでは、JARファイルはロックされ、上書きすることはできません。あなたがコントロールしているなら、あなたは使用後にそれらのロックを解除することができます(それらを閉じます)。サードパーティシステムの場合、それぞれのフラグを見つける必要があります。たとえば、Tomcatコンテキスト構成でantiJARLockingを確認できます。
  • 同じファイルを上書きすることを避け、バージョン管理を行うことを常にお勧めします

全体として、JARのリロードを実現したいときに遭遇する可能性のある多くの問題があります。幸いなことに、リスクを最小限に抑える方法があります。最も安全な方法は、同じ効果を得るために同様のことを行うことです。最もクリーンなのは、カスタムクラスローダーとJARファイルのバージョン管理です。

24
toomasr

私の知る限り、この動作は公式に定義されていないため、通常、これを行うことはできません。

ただし、jarファイルを使用してクラスローダーを作成することはできますoutside公式のクラスパスを作成し、必要に応じてそこからクラスをロードします。クラスローダーによってロードされたクラスのすべてのインスタンスを破棄することにより、現在のリソースを削除し、新しいjarファイルで新しいクラスローダーをインスタンス化してから、新しいクラスをロードして新しいオブジェクトを作成できます。

これは非常に複雑なので、代わりにjarをOSGiモジュールにして、OSGiローダーを介してプログラムを呼び出すのではないでしょうか。

実行中のjarに書き込むことはできません。書き込み用のgetResourceInputStreamに相当するものはありません。 FileOutputStreamを使用して書き込もうとすると、JVMがそれを使用するため、システムがそれを防ぐため、削除することはできないと思います。

とにかく、さまざまなjarでさまざまなモジュールの更新を提供することはまだ可能です。したがって、アップデーターを含む小さな独立した実行可能なjarファイルを介して更新できるアプリケーションのメインjarファイルがあることを想像できます。

JNLPを使用して、アプリケーションを自動的かつシームレスに更新することもできます。

サーバー側のアプリケーションは、ユーザーへの更新を非表示にするための代替手段でもあります。

よろしく、ステファン

1
Snicolas

答えはJavaクラスローダーです。これらの人は、JARS、.classファイル、またはbyte[]値、URLなどからクラスをロードします。クラスにアクセスするときはいつでも、クラスの適切なインスタンスを提供するために、暗黙的にクラスローダーを使用しています。

選択したクラスローダーを作成し、クラスの「更新」が必要なときにクラスローダーを切り替えるだけです。 Thread.setContextClassLoader メソッドを見てください-これにより、スレッドのクラスローダーが変更されます。

独自のクラスローダーの定義は非常に簡単です。ClassLoaderクラスをサブクラス化し、その findClassメソッド をオーバーライドするだけです。

1
Nick

HotswapAgentを使用してそれを実現できます。広く使用されているいくつかのフレームワークのプラグインをサポートし、新しいカスタムプラグインの作成も容易にします

HotswapAgent - https://github.com/HotswapProjects/HotswapAgent

0
Piyush Katariya

長期のタイムラプス写真を撮る中央サーバーにリンクされたモジュラークライアントの複雑なメッシュである私のソフトウェアスイートでは、ソフトウェアを更新する機能を追加しました。

byte[] file = recieve();

FileOutputStream fos = new FileOutputStream("software.jar");
fos.flush(); fos.write(file); fos.close();

このプロセスが完了した後、クライアントが再起動する前に実行する一連の手順があります。これらのプロセスのうち、長期間のスレッドスリープ、ファイルの読み取りと書き込み、およびネットワークの相互作用が含まれます。

エラーの有無を特定していませんが、場合によっては、hs_err_pid.logでクラッシュが発生することがあります。

また、「SOFTWARE_VERSION」と呼ばれる、finalおよびstaticの変数もあります。この変数を交換した後、ソフトウェアを再起動せずに(サーバーインターフェイスから観察した場合)この変数が更新されることを確認しました。

ただし、慎重に検討した結果、ソフトウェアの更新後すぐにマシンを再起動することにしました(このプログラムは起動時に実行されます)。更新の整合性は信頼できないことが確認されているので、最初からやり直すのが最善だと思いました。次のようなアクションを実行することは可能です(テストされていません)。

Runtime.getRuntime().exec("Sudo Java -jar software.jar");
System.exit(0);

しかし、それがどれほど信頼できるかはわかりません。次のようなものを実行することもできます。

Runtime.getRuntime().exec("run_software.sh")
System.exit(0);

次に、run_software.shで:

sleep 1000
Sudo Java -jar software.jar

それがうまくいくかどうか知りたいです。

お役に立てれば!

0
VocoJax