Truffle on Javaホットスワップ・プラグインAPI

Java on Truffleを使用すると、強化されたHotSwap機能を利用できます。これは、実行中のアプリケーションを再起動することなく、開発時にコードを自然に進化させることが可能です。

コードのリロード(HotSwap)は強力なツールですが、注釈の変更、実装されているサービスやBeanなどのフレームワーク固有の変更など、あらゆる種類の変更を反映するには不十分です。このような場合、変更が実行中のインスタンスに完全に反映される前に、構成またはコンテキストをリロードするためにコードを実行する必要があることがよくあります。この点で、Truffle on Javaホットスワップ・プラグインAPIが役立ちます。Truffle on Javaホットスワップ・プラグインAPIは、適切なフックを設定し、IDEでのソース・コードの編集に対応して変更を反映するフレームワーク開発者向けに設計されています。主な設計原則は、指定されたホットスワップ・イベントで起動する様々なホットスワップ・リスナーを登録できることです。例として、静的なイニシャライザを再実行する機能、一般的なホットスワップ後コールバック、および特定のサービス・プロバイダ変更の実装時のフックなどがあります。ノート: ホットスワップ・プラグインAPIは開発中であり、コミュニティからのリクエストに応じてホットスワップ・リスナーのよりきめ細かい登録が追加される見込みです。コミュニティ・サポート・チャネルを通じて、APIを形成するのに役立つ拡張リクエストを送信することをお薦めします。

Micronautでのより強力なリロード・サポートを有効にする実行中の例をよく検討し、ホットスワップ・プラグインAPIを確認します。

Micronautホットスワップ・プラグイン

Micronautホットスワップ・プラグインの実装例は、Micronaut-coreのフォークとしてホストされます。次の手順はmacOS Xの設定に基づいており、Windowsではわずかな変更が必要です。開始するには:

  1. リポジトリをクローニングします:
      git clone git@github.com:javeleon/micronaut-core.git
    

  2. ビルドして、ローカルMavenリポジトリに公開します:
      cd micronaut-core
      ./gradlew publishMavenPublicationToMavenLocal
    

    これで、ホットスワップに対応しているMicronautのバージョンができました。Micronautの拡張バージョンを使用するサンプル・アプリケーションを設定する前に、プラグインの機能を確認してください。対象となるクラスはMicronautHotSwapPluginで、アプリケーション・ソース・コードに対して特定の変更が行われた場合にリロードできるアプリケーション・コンテキストを保持します。クラスは次のようになります:

    final class MicronautHotSwapPlugin implements HotSwapPlugin {
    ​
     private final ApplicationContext context;
     private boolean needsBeenRefresh = false;
    ​
     MicronautHotSwapPlugin(ApplicationContext context) {
         this.context = context;
         // register class re-init for classes that provide annotation metadata
         EspressoHotSwap.registerClassInitHotSwap(
                 AnnotationMetadataProvider.class,
                 true,
                 () -> needsBeenRefresh = true);
         // register ServiceLoader listener for declared bean definitions
         EspressoHotSwap.registerMetaInfServicesListener(
                 BeanDefinitionReference.class,
                 context.getClassLoader(),
                 () -> reloadContext());
         EspressoHotSwap.registerMetaInfServicesListener(
                 BeanIntrospectionReference.class,
                 context.getClassLoader(),
                 () -> reloadContext());
     }
    ​
     @Override
     public String getName() {
         return "Micronaut HotSwap Plugin";
     }
    ​
     @Override
     public void postHotSwap(Class<?>[] changedClasses) {
         if (needsBeenRefresh) {
             reloadContext();
         }
         needsBeenRefresh = false;
     }
    ​
     private void reloadContext() {
         if (Micronaut.LOG.isInfoEnabled()) {
             Micronaut.LOG.info("Reloading app context");
         }
         context.stop();
         context.flushBeanCaches();
         context.start();
    ​
         // fetch new embedded application bean which will re-wire beans
         Optional<EmbeddedApplication> bean = context.findBean(EmbeddedApplication.class);
         // now restart the embedded app/server
         bean.ifPresent(ApplicationContextLifeCycle::start);
     }
    }
    

    ​ ホットスワップAPIに関するロジックは、このクラスのコンストラクタにあります。Micronautは、注釈メタデータが収集され、生成されたクラスの静的フィールドに格納されるコンパイル時注釈処理を中心に設計されています。開発者がMicronaut注釈付きクラスに変更を加えるたびに、対応するメタデータ・クラスが再生成されます。標準ホットスワップは静的イニシャライザを再実行しない(およびするべきではない)ため、ホットスワップ・プラグインでは静的イニシャライザはメタデータ(Micronaut生成クラス)を提供するすべてのクラスに対して再実行されます。したがって、このAPIメソッドEspressoHotSwap.registerClassInitHotSwapが使用されます:

    public static boolean registerClassInitHotSwap(Class<?> klass, boolean onChange, HotSwapAction action)
    

    これにより、特定のクラス、さらにそれらのサブクラスに対するクラス変更にリスナーが登録されます。onChange変数は、含まれるコードが変更された場合のみ静的イニシャライザを再実行するように指示します。actionパラメータは、静的イニシャライザが再実行されたときに特定のアクションを起動するフックです。ここでは、静的イニシャライザが再実行されるたびに、needsBeenRefreshフィールドをtrueに設定するための関数を渡します。ホットスワップ・アクションが完了すると、プラグインはpostHotSwapコールを受信し、trueのneedsBeenRefreshに応じて、reloadContextメソッドでアプリケーション・コンテキストをリロードするためにMicronaut固有のコードを実行します。

    新しいクラスの検出およびインジェクション

    ​ ホットスワップは、実行中のアプリケーションでクラスをホットスワップできるように設計されています。ただし、開発者がまったく新しいクラス(Micronautの新しい@Controller クラスなど)を導入した場合、ホットスワップは新しいクラスを魔法のように注入しません。これは、最低限の内部クラスロード・ロジックに関する知識を必要とするためです。クラスがフレームワークによって検出される標準的な方法は、ServiceLoaderメカニズムを使用する方法です。Truffle on JavaホットスワップAPIには、メソッドEspressoHotSwap.registerMetaInfServicesListenerを使用してサービス実装変更リスナーを登録するための組込みサポートがあります:

    public static boolean registerMetaInfServicesListener(Class<?> serviceType, ClassLoader loader, HotSwapAction action)
    

    現在のサポートは、META-INF/servicesのクラス・パス・ベースのサービス・デプロイメントに関する実装変更のリスニングに限定されます。登録済クラス・タイプに対する一連のサービス実装が変更されるたびに、actionが起動されます。Micronautホットスワップ・プラグインでは、reloadContextが実行され、変更が自動的に取得されます。ノート: サービス実装の変更によって発生するホットスワップ・アクションは、ホットスワップとは独立して起動されます。開発者として、実行中のアプリケーションの新しい機能を表示するには、IDEからホットスワップを実行する必要はありません。

    Micronautの次のレベルのホットスワップ

    ​ Micronautホットスワップ・プラグインの動作がわかったので、この機能を実際のアプリケーションで使用してください。最初のMicronaut Graalアプリケーションの作成のチュートリアルから作成されたサンプル・アプリケーションを次に示します。例のソースは、ここから既製のGradleプロジェクトとしてダウンロードできます。IDEでプロジェクトをダウンロードして解凍し、開きます。

続行する前に、Java on Truffleがインストール済であることを確認し、GraalVMをプロジェクトSDKとして設定してください。

  1. IDEで、サンプル・プロジェクト内のルートbuild.gradleに移動します。追加します:
  run.jvmArgs+="-truffle"
  1. また、拡張されたMicronautフレームワークを以前に公開したMavenローカル・リポジトリを追加します。たとえば: ​
      repositories {
       mavenLocal()
       ...
      }
    

  2. gradle.propertiesで、公開したMicronautのバージョンを更新します。たとえば: ​
      micronautVersion=2.5.8-SNAPSHOT
    

    これで、すべての設定が行われました。

  3. assembleタスクを実行し、定義されたrun Gradleタスクを使用して実行構成を作成します。
  4. 「デバッグ」ボタンを押してアプリケーションをデバッグ・モードで起動します。これにより、拡張されたホットスワップ・サポートが可能になります。

  5. アプリケーションが起動したら、http://localhost:8080/conferences/randomに移動してConferenceControllerからレスポンスを取得していることを確認します。
  6. サンプル・アプリケーション内のクラスに様々な変更を試行し(たとえば、@Controllerマッピングを別の値に変更する、または新しい@Get注釈付きメソッドを追加するなど)、ホットスワップを適用してその優れた処理を確認します。新しい@Controllerクラスを定義した場合、必要なのはクラスのコンパイルのみであり、ファイル・システムの監視によって変更が取得されると、明示的にホットスワップを使用する必要がなくリロードを確認できます。