JettyでFreeMarkerを使うとテンプレートが見つけられなくなる

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">
  
  <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>
  
</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 の一時ディレクトリ設定の詳細は公式サイトのページで確認できます。

この記事を共有しませんか?