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
パッケージングとビルド
$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
$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
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は静的にリンクできません。