web.xmlのservlet-mappingの謎〜解決編〜

長かった…。↓のサイトのGetting StartedのPDFのP.19のおかげでやっとわかってきたぞー。
Core JavaServer Faces

こういう具体例を交えた説明が日本語サイトには非常に少ないように思えるけど、みんな理解してるのかな?もしかして、FAQ?
とにかく、わかったことを自分のためにメモ。

FacesServletのservlet-mapping

JSFにおけるファイルの拡張子は.jsp。これが基本。
(変更することもできる。やり方は後述。)

仮想拡張子指定の場合:
    <servlet-mapping>
      <servlet-name>FacesServlet</servlet-name>
      <url-pattern>*.faces</url-pattern>
    </servlet-mapping>

と書くと、

    http://localhost:8080/test/test.faces

というアクセスに対して、

test-project/ (APのルートディレクトリ)
    └test.jsp

のtest.jspに解決される。
これは、URL中の拡張子部分はマッピングのための仮想拡張子*1であり、実際にファイルを探すときには拡張子部分を「.jsp」に暗黙変換することを意味している。
(ブラウザのアドレス欄は.facesのまま)

昨日試した

仮想ディレクトリ指定の場合:
    <servlet-mapping>
      <servlet-name>FacesServlet</servlet-name>
      <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>

も同様。
あくまで「/faces/」というパス部分はマッピングのための仮想ディレクトリであり、実際にはこの部分を無視して詰めたファイル名にバインドすることを意味している。
この場合、実体拡張子は指定したそのままとみなす。つまり、先ほどのディレクトリ構成であれば、

    http://localhost:8080/test/faces/test.jsp

であれば正しくファイルに対応する。もし、

    http://localhost:8080/test/faces/test.jsf

のように書いた場合は、「.jsf」という拡張子のファイルがないため、

HTTPステータス 404 - /test/test.jsf

となりアクセスできない。(実体拡張子を.jspとみなすような暗黙変換はしない)

ここで、

test-project/ (APのルートディレクトリ)
    └test.jsf

というファイルを作成したとすればそのファイルにバインドされはするが、後で書くようにjsfという拡張子でJSPレンダリング処理が行われるように設定しなければ、単にテキストファイルの内容がブラウザに表示されるだけである。


仮想情報として除去・置換されるのは、servlet-mappingのurl-patternで指定したパターンのみであるため、サブディレクトリに配置したJSPファイルにアクセスしたい場合は、

    http://localhost:8080/test/pages/test.faces  (仮想拡張子指定の場合)
    http://localhost:8080/test/faces/pages/test.jsp  (仮想ディレクトリ指定の場合)

というURLにアクセスすれば、

test-project/ (APのルートディレクトリ)
    └pages/
        └test.jsp

のjspファイルに対応する。

拡張子設定の変更

まず、.jsf拡張子のファイルに対してJSPレンダリング処理が行われるようにするための設定は↓の通り。

    <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>

これで、hoge.jsfという実体ファイルにアクセスするとJSPレンダリング処理が行われた結果が表示されるようになる。
これは前節の「仮想ディレクトリ指定の場合」で、

    http://localhost:8080/test/faces/test.jsf

とアクセスした場合に、対応するtest.jsfファイルの内容がJSPとして正しく解釈されるようにするために必要な設定である。


次に、前節の「仮想拡張子指定の場合」では、実体ファイルを探すときに「.jsp」という拡張子であると暗黙変換されていたのを「.jsf」としたい、という場合の設定を示す。
これは、FacesServletへのパラメータとして以下のように指定する。

    <context-param>
      <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
      <param-value>.jsf</param-value>
    </context-param>

これで、

    http://localhost:8080/test/test.faces

というアクセスに対して、

test-project/ (APのルートディレクトリ)
    └test.jsf

というファイルが対応することになる。

先ほどの設定をしておけば、このtest.jsfファイルの内容がJSPとして解釈されることになる。
というかそうしないと意味がないので、仮想拡張子指定の場合で後者の設定をするなら前者の設定は必須。

逆に仮想ディレクトリ指定の場合では、拡張子変換はないので、やるとしても前者の設定のみでOK。

無限ループの考察

これは推測だが、FacesServletにディスパッチされたリクエストはURLから仮想情報を除去・置換して実体に対応するように変換されたURLを使って、forwardするような処理フローになっているのではないか。もしそうだとすると、仮想拡張子=デフォルト実体拡張子という設定にしてしまうと無限再帰ループをするんだけど。
これは自信なしのため、あまり突っ込まないことにする。

まとめ

S2JSFではなく素のJSFを使うなら、ファイル拡張子はデフォルトのまま.jsp、アクセスする仮想拡張子を.html、とするのがきれいな気がする。(一見html風)


ふーーーっ。とりあえずすっきりーーーっ!

*1:便宜的に名づけた名前であり、正式名称ではないことにご注意。