FreeMarker テンプレートエンジンを使った Web アプリケーションを Jetty アプリケーションサーバーで数日稼働させていると以下の例外が発生することがありました。
freemarker.template.TemplateNotFoundException: Template not found for name "index.ftl".
なぜかテンプレート ・ ファイルが見つけられなくなってしまうのです。その原因は Jetty と Windows にありました。
WARファイルは一時ディレクトリーに展開される
Jetty はデフォルトで WAR ファイルを Java システムプロパティ java.io.tmpdir
で指定されている場所に展開します。Windows の場合、 java.io.tmpdir
の既定値は TMP 環境変数と同じになります。
ユーザーの場合は TMP 環境変数の既定値は %USERPROFILE%\AppData\Local\Temp
です。Jetty を Windows サービスとして NT AUTHORITY¥System
などのビルトイン ・ システムアカウントで実行している場合は、 ユーザー環境変数ではなくシステム環境変数が参照されます。システム環境変数 TMP の既定値は %SystemRoot%\TEMP
(展開すると C:\WINDOWS\TEMP
など) になります。
つまり、 Jetty の Web アプリケーションは一時ディレクトリーに展開したファイルを参照して動くことになります。Web アプリケーション稼働中に一時ディレクトリーのファイルが削除されたら困ったことになりそうです。
ストレージ センサー
Windows 10 にはストレージ センサーという一時ファイルを削除する機能があります。
- ストレージ センサーの設定を確認するには?
- 「スタート」 → 「設定」 → 「システム」 → 「ストレージ」 でストレージ センサーの状態を確認できます。「空き領域を自動的に増やす方法を変更する」 をクリックすると詳細な設定ができます。
このストレージ センサーによって一時ファイルが削除されてしまうと、 Jetty Web アプリケーションが影響を受けてしまいます。ストレージ センサーを使わなくても、 ユーザーが手動でディスクのクリーンアップを実行して一時ファイルを削除してしまう可能性もあります。
WARファイルの展開ディレクトリを変更する
Jetty には WAR ファイルの展開ディレクトリを変更する方法がいくつか用意されています。
方法 1. ベースディレクトリを指定する
以下のように XML を記述をすることで一時ディレクトリを指定することができます。(下記は demo-base\webapps\test.xml
の設定例です。)
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<div class="vspace" data-length="0" style="margin-block-start:-1px;height:1px"></div>
<Set name="contextPath">/test</Set>
<Set name="war">webapps/test.war</Set>
<Call name="setAttribute">
<Arg>org.eclipse.jetty.webapp.basetempdir</Arg>
<Arg>D:/temp/jetty</Arg>
</Call>
<div class="vspace" data-length="0" style="margin-block-start:-1px;height:1px"></div>
</Configure>
指定するベースディレクトリ (D:/temp/jetty
) はあらかじめ作成しておく必要があります。自動的には作成されません。
この設定をすると、 D:/temp/jetty
以下に Web アプリケーションの一時ディレクトリが作成されるようになります。Web アプリケーションの一時ディレクトリの名前は以下のようになります。
jetty-0.0.0.0-8080-test.war-_test-any-16761492621178986490.dir
一時フォルダー名はホスト名、 ポート番号、 WAR ファイル名、 さらにランダムな数値を含みます。そのため、 適切にクリーンアップしないと Jetty を起動する度に一時フォルダーが増えて続けてしまう可能性があります。
これは Server
レベルの設定ではなく WebAppContext
レベルの設定であるため、 Web アプリケーションごとの XML に個別に設定を記述しなければならず、 手間も掛かります。
方法 2. workディレクトリーを作成する
${jetty.base}
に work
という名前のディレクトリーを作成すると、 自動的に WAR ファイルを展開する一時ディレクトリとして使用されるようになります。
- demo-base の場合であれば
demo-base\work
を作成します。
これはサーバー全体に影響し、 すべての WAR ファイルが work
ディレクトリ下位に展開されるようになるのでお手軽です。ただし、 Web アプリケーションごとに作成される一時ディレクトリ名は前述したものと同様にランダムであるため、 適切にクリーンアップしないと増え続けてしまうおそれがあります。
方法 3. 一時ディレクトリを完全に指定する
方法 1.に似ていますが、 設定する属性名が basetempdir
ではなく tempdir
になっています。ベースとなる親ディレクトリを指定するのではなく、 Web アプリケーションの展開ディレクトリを明確に指定します。
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">/test</Set>
<Set name="war">webapps/test.war</Set>
<Call name="setAttribute">
<Arg>javax.servlet.context.tempdir</Arg>
<Arg>D:/temp/jetty/test-war</Arg>
</Call>
</Configure>
もしくは以下のように書くこともできます。(どちらの書き方でも効果は同じです。)
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">/test</Set>
<Set name="war">webapps/test.war</Set>
<Set name="tempDirectory">D:/temp/jetty/test-war</Set>
</Configure>
D:/temp/jetty
まであらかじめ作成しておく必要があります。末尾のtest-war
ディレクトリは自動的に作成されます。
このように指定すると test.war
は常に D:/temp/jetty/test-war
に展開されるようになります。Web アプリケーションの一時ディレクトリ名がランダムではなく指定した値 (test-war
) に固定されるので、 一時ディレクトリが増え続けてしまう心配がありません。
Jetty の一時ディレクトリ設定の詳細は公式サイトのページで確認できます。