S-JIS[2009-10-17] 変更履歴

Webアプリ作成の注意点

Javaでウェブアプリケーション(いわゆるサーブレット関係)を作るに当たっての注意点。


MTセーフ

Webアプリ関連のクラスを実装する際は、MTセーフ(マルチスレッドセーフ)になるようにしなければならないものがある。

基本的にサーブレットフィルターは、実行時にはAPサーバーによってインスタンスが1つだけ作られ、マルチスレッドで呼ばれる。(複数スレッドからそのインスタンスのメソッドが同時に呼ばれる)

開発時はたいてい自分一人しか実行しないから、MT非セーフなプログラムであっても、問題が発覚することは無い。
APサーバーに多重のリクエストを長時間投げて、それぞれの処理結果が正しいことをテストして、運が良ければ問題が発生する。
(テスト時に問題が発覚するのは良いことだ。最悪なのは本番稼動してから発覚すること。そしてテスト環境で再現しないこと)

JSPは、実行時に(あるいは事前コンパイルによって)サーブレットのプログラムに変換されるので、サーブレットと同様にMTセーフになるように書かなければならない。
特にスクリプトレット類(中でもJSP宣言<%! %>、場合によってはJSP式<%= %>)はJavaプログラムが自由に書けるので注意が必要。
これは知らない人が意外と多いので、コーディング規約でスクリプトレット類は一律禁止する方が良い。(今はたいていStrutsを使うので、スクリプトレット無しでほとんどの事が出来るはず)

Strutsを使っている場合、ActionクラスもMTセーフにしなければならない。
サーブレットと同様に、実行時には1インスタンスずつしか作られない為。
(Struts2では毎回インスタンスが作られるので、Actionクラス自身はMTセーフである必要は無いらしい)


インスタンスのリセット

APサーバーによっては、インスタンスが使い回されるものがある。そういったオブジェクトでは、初期化のタイミングに注意が必要となる。

JSPのカスタムタグは、JSPが変換されたサーブレットのプログラムの中では、Tagクラスをインスタンス化して属性値をsetterメソッドを使ってセットし、doStartTag()やらdoEndTag()やらを呼び出す。
ここで、Tagクラスのインスタンス化を、毎回行うのか、インスタンスをキャッシュして使い回すのかがAPサーバーによって異なる。
(Tomcatではキャッシュし、WebLogicでは毎回インスタンスを作り直しているようだ。)
Tagのrelease()メソッドはインスタンスが破棄される時に呼ばれるものなので、呼び出されるタイミングには注意。

StrutsのActionFormは、インスタンスの保持期間(スコープ)が変えられる。
デフォルトではsessionなので、その場合はリクエスト毎には初期化されない。
チェックボックスは、チェックされている値しか入ってこない。チェックが外れたことを入力する為には、先に初期化しておく必要がある。
たぶんActionFormのreset()メソッドは、その初期化の為に存在している。


シリアライズ

ファイル等に保存されたり通信で別のJavaVMへ送信されたりするオブジェクトの場合、シリアライズできる必要がある。
(Serializableインターフェースを実装するだけでなく、シリアライズ可能となるように条件を満たす必要がある)

セッションオブジェクトは、レプリケーションやAPサーバー再起動時にファイルにバックアップされたりする時にシリアライズされる。
したがって、セッションに保持する各オブジェクトもシリアライズ可能である必要がある。

StrutsのActionFormもSerializableインターフェースを実装している。
たぶんrequestスコープで使っている場合はシリアライズを気にしなくても大丈夫だが、
sessionスコープで使っている場合はセッションに保持されるので、気にする必要が出てくるだろう。


WEB-INF

WEB-INFというディレクトリーの中のファイルは、外部からのURIで指定された場合でも、参照できない。(セキュリティーの為、そういうルールになっている)
MVC(mode-view-controller)の考え方では、viewであるJSPには外部から直接アクセスできるべきではないので、JSPファイル自体もWEB-INFの下に置いてしまうのがいいかも。

逆に、サーブレットを作る場合は、参照できなくなるようにコーディングする必要がある。
JspClassServletの例


URLエンコード

自分のWebアプリ内のURIを指定する為にHTML上のリンク(href)を生成する場合、URLエンコードを気にする必要がある。

サーブレット関連の場合、URLエンコードという言葉には2つの意味があるようで、
ひとつは全角文字や特殊な記号をURIで扱える文字に変換すること。
もうひとつは、セッションIDを埋め込むこと。

前者はURLEncoder#encode()(逆変換はURLDecoder#decode())を 使用する。

後者はHttpServletResponse#encodeURL()(リダイレクトの場合はencodeRedirectURL())を使用する。

Strutsを使っている場合、html:linkタグを使えば、後者のencodeURL()は自動的に呼ばれる。
StrutsのActionRedirect#addParameter()を使えば前者のエンコードも自動的に行ってくれるが、UTF-8での変換となる。(気をつけないと文字化けの原因となる)


セッションID

セッションIDの保持方法は、接続元のクライアント環境によっても異なる。

特にURLリライティングの為にはHttpServletResponse#encodeURL()(リダイレクトの場合はencodeRedirectURL())を呼んでおかないと、
セッションIDが消える→セッションが成り立たない
という状態になってしまう。

セッションIDはAPサーバーが自動的に割り当てるので、URLリライティングに使われるリクエストパラメーター名は、同一APサーバー上で動いている別Webアプリでも、共通の名前になってしまうと思われる。(設定できるAPサーバーもあるか?)
つまり、別Webアプリ(コンテキストルートが異なるものや異なるサーバー)へ遷移するような作りの場合、自分用のセッションIDがそのまま他アプリへ渡り、勝手に解釈されてしまう可能性がある。(間違ったセッションIDとして破棄されれば良い方で、他人のセッションIDに紐付けられてしまうと最悪)

セッションを使う場合は、自分のWebアプリ内で完結するように設計しなければならない。


サーブレットへ戻る / JavaEEへ戻る / Java目次へ戻る
メールの送信先:ひしだま