Ahead of Timeコンパイルサポート

目次

はじめに

Tomcatは、GraalVM/Mandrel Native Imageツールを使用して、コンテナを含むネイティブバイナリを生成するのをサポートしています。このドキュメントページでは、そのようなイメージのビルドプロセスについて説明します。

セットアップ

ネイティブイメージツールは単一のJARで使うのがはるかに簡単なので、そのプロセスではMaven shadeプラグインのJARパッケージングを使用します。これは、Tomcat、Webアプリケーション、およびすべての追加依存関係から必要なすべてのクラスを含む単一のJARを生成するという考えです。Tomcatはネイティブイメージをサポートするための互換性修正を受けていますが、他のライブラリは互換性がなく、代替コードが必要になる場合があります(これについてはGraalVMのドキュメントに詳細があります)。

GraalVMまたはMandrelをダウンロードしてインストールします。

Tomcat Stuffedモジュールをhttps://github.com/apache/tomcat/tree/main/modules/stuffedからダウンロードします。利便性のため、環境プロパティを設定できます

export TOMCAT_STUFFED=/absolute...path...to/stuffed
ビルドプロセスには、Apache AntとMavenの両方が必要になります。

パッケージングとビルド

$TOMCAT_STUFFEDフォルダ内では、ディレクトリ構造は通常のTomcatと同じです。主要な設定ファイルはconfフォルダに配置され、デフォルトのserver.xmlを使用している場合は、Webアプリケーションはwebappsフォルダに配置されます。

すべてのWebアプリケーションクラスは、JSP事前コンパイルステップ中にMaven shadeプラグインとコンパイラの両方で利用できるようにする必要があります。/WEB-INF/libにあるすべてのJARは、Mavenの依存関係として利用できるようにする必要があります。webapp-jspc.ant.xmlスクリプトは、Webアプリケーションの/WEB-INF/classesフォルダからクラスをMavenがコンパイルターゲットとして使用するtarget/classesパスにコピーしますが、JSPソースのいずれかがそれらを使用する場合は、代わりにJARとしてパッケージ化する必要があります。

最初のステップは、すべての依存関係を含むシェード付きTomcat JARをビルドすることです。Webアプリケーション内のすべてのJSPは、事前コンパイルされ、パッケージ化されている必要があります(webapps$WEBAPPNAMEというWebアプリケーションが含まれていると仮定します)。

cd $TOMCAT_STUFFED
mvn package
ant -Dwebapp.name=$WEBAPPNAME -f webapp-jspc.ant.xml
Webアプリケーションの依存関係は、メインの$TOMCAT_STUFFED/pom.xmlに追加され、その後シェード付きJARがビルドされます。
mvn package

Ahead of Timeコンパイルでは、可能な限りリフレクションの使用を避けるのが最善であるため、メインのserver.xml設定とコンテキストを構成するために使用されるcontext.xmlファイルからTomcat Embeddedコードを生成およびコンパイルすることをお勧めします。

$JAVA_HOME/bin/java\
        -Dcatalina.base=. -Djava.util.logging.config.file=conf/logging.properties\
        -jar target/tomcat-stuffed-1.0.jar --catalina -generateCode src/main/java
次に、Tomcatを停止し、生成された埋め込みコードを含めるために以下のコマンドを使用します。
mvn package
ここで説明する残りのプロセスでは、このステップが実行され、--catalina -useGeneratedCode引数がコマンドラインに追加されていることを前提とします。そうでない場合は、それらを削除する必要があります。

ネイティブイメージ設定

ネイティブイメージは、記述子で明示的に定義されていない限り、動的なクラスローディングやリフレクションの形式をサポートしません。これらを生成するには、GraalVMのトレーシングエージェントを使用し、場合によっては追加の手動設定が必要です。

GraalVM substrate VMとそのトレースエージェントを使用してTomcatを実行します。

$JAVA_HOME/bin/java\
        -agentlib:native-image-agent=config-output-dir=$TOMCAT_STUFFED/target/\
        -Dorg.graalvm.nativeimage.imagecode=agent\
        -Dcatalina.base=. -Djava.util.logging.config.file=conf/logging.properties\
        -jar target/tomcat-stuffed-1.0.jar --catalina -useGeneratedCode

これで、Webアプリケーションからの動的クラスローディングにつながるすべてのパス(例:サーブレットアクセス、WebSocketなど)は、Webアプリケーションを実行するスクリプトを使用してアクセスされる必要があります。サーブレットは、実際のアクセスを必要とせずに起動時にロードされる場合があります。リスナーも起動時に追加のクラスをロードするために使用される場合があります。それが完了したら、Tomcatを停止できます。

記述子はエージェントの出力ディレクトリに生成されました。この時点で、トレースされない項目(ベースインターフェース、リソースバンドル、BeanInfoベースのリフレクションなど)を追加するために、さらに設定を行う必要があります。このプロセスに関する詳細については、Graalのドキュメントを参照してください。

使用されるすべてのクラスはAOTでネイティブイメージにコンパイルされる必要がありますが、Webアプリケーションは変更せずに、必要なすべてのクラスとJARをWEB-INFフォルダに含め続ける必要があります。これらのクラスは実際に実行またはロードされませんが、それらへのアクセスが必要です。

ネイティブイメージのビルド

すべてが適切に行われた場合、ネイティブイメージはnative-imageツールを使用してビルドできます。

$JAVA_HOME/bin/native-image --report-unsupported-elements-at-runtime\
        --enable-http --enable-https --enable-url-protocols=http,https,jar,jrt\
        --initialize-at-build-time=org.eclipse.jdt,org.apache.el.parser.SimpleNode,jakarta.servlet.jsp.JspFactory,org.apache.jasper.servlet.JasperInitializer,org.apache.jasper.runtime.JspFactoryImpl\
        -H:+UnlockExperimentalVMOptions\
        -H:+JNI -H:+ReportExceptionStackTraces\
        -H:ConfigurationFileDirectories=$TOMCAT_STUFFED/target/\
        -H:ReflectionConfigurationFiles=$TOMCAT_STUFFED/tomcat-reflection.json\
        -H:ResourceConfigurationFiles=$TOMCAT_STUFFED/tomcat-resource.json\
        -H:JNIConfigurationFiles=$TOMCAT_STUFFED/tomcat-jni.json\
        -jar $TOMCAT_STUFFED/target/tomcat-stuffed-1.0.jar
追加の--staticパラメータにより、生成されたバイナリ内でglibc、zlib、およびlibstd++の静的リンクが可能になります。

ネイティブイメージの実行は次のようになります。

./tomcat-stuffed-1.0 -Dcatalina.base=. -Djava.util.logging.config.file=conf/logging.properties --catalina -useGeneratedCode

互換性

サーブレット、JSP、EL、WebSocket、Tomcatコンテナ、tomcat-native、HTTP/2はすべて、ネイティブイメージでそのままサポートされます。

このドキュメントの執筆時点では、JULIはサポートされていません。ログマネージャーの設定プロパティがGraalでサポートされていないことに加え、いくつかの静的イニシャライザの問題があるため、代わりに通常のjava.util.loggingロガーと実装を使用する必要があります。

デフォルトのserver.xmlファイルを使用している場合、JMXリスナー(JMXはサポートされていません)やリーク防止リスナー(Graalに存在しない内部コードの使用)など、ネイティブイメージと互換性のない一部のサーバーリスナーは設定から削除する必要があります。

Tomcatの機能向上に不足している項目

  • java.util.logging LogManager: システムプロパティによる設定は実装されていないため、JULIの代わりに標準のjava.util.loggingを使用する必要があります。
  • 静的リンク設定: tomcat-nativeは静的にリンクできません。