8.2.2 サーブレット実装時の注意事項
サーブレットを実装するときの注意事項を示します。
- 〈この項の構成〉
(1) 入出力ストリーム利用時の注意
-
ServletOutputStreamクラスのprint(char c)メソッドの引数には,0x00〜0xFFの範囲で指定してください。範囲外の値を指定するとjava.io.CharConversionExceptionが発生します。
-
ServletInputStreamクラスでは,markメソッドおよびresetメソッドをサポートしていません。また,markSupportedメソッドは常にfalseを返します。
-
ServletInputStreamクラスからデータを読み出したあとに転送(forward)した場合,転送先のServletInputStreamクラスから読み出されるデータは,転送する前に読み出された位置からのものとなります。また,転送する前にServletInputStreamクラスからすべてのデータを読み出した場合,転送先のリクエストパラメタは空となります。
(2) ロケール設定時の注意
ServletResponseクラスのsetLocaleメソッドにLocale.JAPANESEを指定した場合,Content-TypeヘッダのcharsetはShift_JIS(シフトJIS)になります。
(3) URI取得時の注意
HttpServletRequestクラスのgetRequestURIメソッドでは,最適化されたURIが返されます。例えば,「xxx//yyy/zzz」は「xxx/yyy/zzz」に,「xxx/yyy/../zzz」は「xxx/zzz」のように変換されます。
(4) POSTデータの読み込み失敗時の動作について
WebサーバでPOSTデータの読み込みに失敗した場合,Webコンテナで動作するサーブレットでは,ServletRequestクラスの次に示すメソッドの呼び出し時にIllegalStateException例外が発生します。
-
getParameterメソッド
-
getParameterNamesメソッド
-
getParameterValuesメソッド
-
getParameterMapメソッド
また,Content-Typeがmultipart/form-dataのフォームデータを受信した場合は,上記のメソッドまたはHttpServletRequestクラスの次に示すメソッドの呼び出し時に,KDJE39336-E のメッセージの出力を伴うIllegalStateException例外が発生することがあります。この場合,受信したフォームデータのサイズが適切かどうかを確認し,適切なサイズのときはwebserver.connector.limit.max_post_form_dataの設定値を見直してください。
-
getPartメソッド
-
getPartsメソッド
(5) 属性の変更に対するイベント通知時の注意
ServletContextAttributeListenerインタフェース,HttpSessionAttributeListenerインタフェース,およびServletRequestAttributeListenerでは,Webコンテナが内部で使用している属性の追加,削除,更新があった場合にもイベントが通知される場合があります。通知されたイベントの属性名を参照して,Webアプリケーションで使用している属性名以外の場合には無視するようにしてください。
(6) ServletContextインタフェース利用時の注意
-
ServletContextインタフェースのgetResourcePathsメソッドによって得られるjava.util.Setは参照用です。このjava.util.Setに対し,要素の追加,削除の操作をしないでください。add,addAll,clear,remove,およびremoveAllメソッドを使用するとIllegalStateException例外が発生する場合があります。
-
ServletContextインタフェースのgetContextメソッドの引数には,存在するコンテキストルート名を使用したURLを指定してください。存在しないコンテキストルート名を使用したURLを指定した場合の動作は保証されません。
-
ServletContextインタフェースのgetResourceメソッドおよびgetResourceAsStreamメソッドには,該当Webアプリケーションに含まれるリソースを指定してください。該当Webアプリケーション外のリソースを指定した場合の動作は保証されません。
(7) Webアプリケーションに含まれるディレクトリにアクセスするときの注意
Webアプリケーションに含まれるディレクトリにアクセスするときは,クエリ文字列およびPOSTデータがリダイレクト先リソースで取得できないことがあるため,クエリ文字列およびPOSTデータは付けないようにしてください。
(8) ServletRequestインタフェース利用時の注意
ServletRequestインタフェースのgetRemoteHostメソッドは,リクエストを送信したクライアントのホスト名を返しますが,Webサーバがホスト名を解決できないか,解決しないように設定されている場合はIPアドレスを返します。
既定の設定ではWebサーバの設定がされていないため,IPアドレスを返します。ホスト名を取得する場合は,Webサーバの設定を変更する必要があります。ただし,設定を変更すると,ホスト名の解決のため,レスポンスが遅くなる場合があります。Webサーバの設定の変更方法については,マニュアル「HTTP Server」を参照してください。
(9) プロセス内で複数回実行してはならない処理を実装する場合の注意
1プロセス内で複数回実行してはならない処理をサーブレット中に記述する場合,サーブレットの実行とその処理が同期することがないようにしてください。特に,OTMとの通信を開始するための初期化処理の中には,インスタンスを削除しても終了しない常駐スレッドを生成するものがあります。例えば,TPBrokerの初期化関数であるORB.initメソッドは,呼び出されるたびにGC実行のための監視用常駐スレッドを生成し,このスレッドはプロセス終了まで消えません。そのため,1プロセス内でORB.initメソッドを必要以上に実行すると,不要なGC処理が増え,システム全体の性能が著しく低下するなどの悪影響があります。
このような事態を避けるため,プロセス中で1回だけ実行させたい処理をサーブレットに記述する場合には,その処理がプロセス中で実行済みかどうかをあらかじめ判定する必要があります。具体的には,ある処理が実行済みかどうかの状態を格納する条件フラグとしてstatic変数を任意のクラス中で用意します。static変数の値が「未実行」を意味するものである場合にだけ処理を実行し,値を「実行済み」を意味するものに変更することで,その処理の実行回数をプロセス中で1回だけに限定できます。ただし,次の2点に注意してください。
-
任意のクラスでstatic変数を使用する場合は,usrconf.propertiesまたはhitachi_web.propertiesに次の設定をしないでください。
・webserver.context.reloadable=true
・webserver.jsp.recompilable=true
設定した場合,そのクラスのインスタンスが自動的に破棄されて,再生成されることがあるため,それに伴ってstatic変数の値も初期化されてしまうことがあります。usrconf.propertiesとhitachi_web.propertiesの設定については,マニュアル「アプリケーションサーバ リファレンス 定義編(サーバ定義)」を参照してください。
-
あるスレッドが条件フラグであるstatic変数の値を参照してから値を変更するまでの間に,ほかのスレッドが同様の処理を実行することがないよう,この処理を実行するメソッドにsynchronizedキーワードを指定してください。この方法を用いてTPBrokerの初期化関数ORB.initメソッドが1回だけ呼び出されるようにするためのコーディング例を次に示します。
static org.omg.CORBA.ORB _orb=null; public static synchronized org.omg.CORBA.ORB getORB(String[] args, Properties props){ if(_orb==null){ _orb=org.omg.CORBA.ORB.init(args,props); } return _orb; }
(10) ServletContextオブジェクトに登録する製品独自の属性
Webコンテナは,Webアプリケーションの制御に必要な情報をjavax.servlet.ServletContextオブジェクトの属性に登録しています。WebアプリケーションでServletContextインタフェースのgetAttributeNamesメソッドによって取得する属性名には,Webコンテナによって登録された属性の名称も含まれます。
Webアプリケーション内でServletContextオブジェクトに属性を登録する時,次の文字列で開始するキー名称を使用しないでください。
-
org.apache.catalina
-
com.hitachi.software.web
-
jspx
また,ServletContextにはJava EEの仕様で定められた属性も追加されるため,これらと同じキー名称の属性を登録しないでください。
(11) ServletRequestクラスのプロキシ取得用メソッドを使用する場合の注意
次に示すjavax.servlet.ServletRequestクラスのメソッドは,リクエストを送信したクライアント,または最後に通ったプロキシの情報を取得するためのメソッドですが,リバースプロキシを使用した環境では,取得する情報がリバースプロキシの情報となります。
-
getRemoteAddrメソッド
-
getRemoteHostメソッド
-
getRemotePortメソッド
(12) javax.servlet.ServletResponseインタフェースのresetメソッド実行時の注意
javax.servlet.ServletResponseインタフェースのgetWriterメソッドを実行したあとに,resetメソッドを実行した場合,HTTPレスポンスのContent-Typeで指定する文字エンコーディングは,次に示すAPIのどれか(すべてjavax.servlet.ServletResponseインタフェース)を使用して,再度同じ文字エンコーディングを指定してください。
-
setContentTypeメソッド
-
setLocaleメソッド
-
setCharacterEncodingメソッド※
- 注※
-
Servlet 2.4仕様で追加されたメソッドです。
Servlet 2.4仕様以降では,これらのAPIを使用して文字エンコーディングを設定する場合は,getWriterメソッドを実行する前に呼び出す必要があります。ただし,getWriterメソッドを実行したあとにresetメソッドを実行した場合にかぎり,再度getWriterメソッドを呼び出すまではこれらのAPIで文字エンコーディングを設定できます。
(13) setMaxInactiveIntervalメソッドの引数に0を指定した場合の動作
javax.servlet.http.HttpSessionインタフェースのsetMaxInactiveIntervalメソッドの引数に0を指定した場合,セッションがタイムアウトになることはありません。
(14) java.io.BufferedReaderのmark操作について
javax.servlet.ServletRequestのgetReaderメソッドで得られるjava.io.BufferedReaderは,mark操作をサポートしていません。markSupportedメソッドではfalseが返されます。
(15) setVersionメソッドの引数に1を指定した場合の動作
javax.servlet.http.CookieのsetVersionメソッドの引数に1を指定した場合,Set-Cookieヘッダが付加されます。
(16) getRequestDispatcherメソッドでのパスの指定について
javax.servlet.ServletRequestのgetRequestDispatcherメソッドの引数に「/」ではじまらない相対パスを指定した場合,このサーブレットのサーブレットマッピングに指定したURLパターンからの相対パスになります。URLパターンが「/」で終わっている場合は,親のディレクトリからの相対パスになります。
例えば,サーブレットマッピングを"/a/b/"に指定したサーブレットから,"hello.html"を指定してgetRequestDispatcherメソッドを実行すると,"/a/hello.html"が得られます。
(17) setBufferSizeメソッドを使用してバッファサイズを変更する場合の注意
レスポンス送信時に使用されるサーブレットのバッファは,リクエスト処理スレッドごとに保持されます。javax.servlet.ServletResponseのsetBufferSizeメソッドを実行してバッファサイズを変更した場合,変更したバッファサイズは,同一J2EEサーバ上のほかのWebアプリケーションを含め,該当するスレッドが処理するすべてのリクエストに適用されます。javax.servlet.ServletResponseのsetBufferSizeメソッドを使用してバッファサイズを変更する場合は,(バッファサイズ)×(リクエスト処理スレッド数)分のメモリが確保されることを考慮した上で,メモリ使用量を見積もってください。
なお,一度確保されたバッファは,処理スレッドごとにWebアプリケーションからsetBufferSizeメソッドで更新されるまで有効となります。
(18) HTTPレスポンスのContent-Typeヘッダについての注意
サーブレットでは,javax.servlet.ServletResponseクラスのsetContentTypeメソッドで明示的にContent-Typeを設定していない場合,Content-Typeヘッダを作成しません。そのため,HTTPレスポンスの文字エンコーディングをContent-Typeヘッダの”charset=”フィールドから確認することはできません。
(19) javax.servlet.http.HttpSessionクラスのgetIdメソッドについての注意事項
Servlet 2.4仕様以前に準拠したWebアプリケーションで,無効化されたjavax.servlet.http.HttpSessionオブジェクトのgetIdメソッドを呼び出した場合の動作がServlet仕様とアプリケーションサーバとで異なります。それぞれの動作を次に示します。
- Servlet仕様
-
java.lang.IllegalStateException例外をスローする。
- アプリケーションサーバ
-
nullを返す。
(20) javax.servlet.ServletRequestクラスおよびjavax.servlet.http.HttpServletRequestクラスのメソッドについての注意事項
次の表に示すメソッドで取得した情報をレスポンスに出力する場合は,サニタイズする必要があります。
クラス名 |
メソッド名 |
---|---|
javax.servlet.ServletRequest |
getCharacterEncoding() |
getContentType() |
|
getInputStream() |
|
getParameter(java.lang.String name) |
|
getParameterMap() |
|
getParameterNames() |
|
getParameterValues(java.lang.String name) |
|
getProtocol() |
|
getReader() |
|
getServerName() |
|
javax.servlet.http.HttpServletRequest |
getCookies() |
getHeader(java.lang.String name) |
|
getHeaderNames() |
|
getHeaders(java.lang.String name) |
|
getMethod() |
|
getPathInfo() |
|
getPathTranslated() |
|
getQueryString() |
|
getRequestedSessionId() |
|
getRequestURI() |
|
getRequestURL() |
|
getServletPath() |
(21) javax.servlet.ServletRequestクラスのgetLocale(getLocales)メソッドについての注意事項
javax.servlet.ServletRequestクラスのgetLocaleメソッド,またはgetLocalesメソッドで取得できるjava.util.Localeオブジェクトは,HTTPリクエストのAccept-Languageヘッダの値から作成します。
Webコンテナでは,Accept-Languageヘッダの値のロケール(ISO言語コード,ISO国コード,またはバリアント)が英字以外を含むかどうかをチェックします。ロケールが英字以外を含む場合は,不正なロケールと判断して,不正なロケールごとにKDJE39546-Wのメッセージをメッセージログに出力して無視します。
不正なロケールを含むAccept-Languageヘッダを受信した場合は,getLocaleメソッド,またはgetLocalesメソッドは正しいロケールのjava.util.Localeオブジェクトだけを返します。Accept-Languageヘッダに指定されたロケールがすべて不正な場合は,Accept-Languageヘッダがないと見なしてサーバのデフォルトロケールを返します。
(22) javax.servlet.http.HttpServletRequestインタフェースのgetServerNameメソッドの戻り値
javax.servlet.http.HttpServletRequestインタフェースのgetServerNameメソッドの戻り値は,リバースプロキシでHostヘッダを書き換えた場合,HTTPクライアントが設定したHostヘッダの値と異なることがあります。
(23) Servlet 2.4以前のinclude先のサーブレットでのレスポンスヘッダの設定
Servlet 2.4以前では,include先のサーブレットでのレスポンスヘッダの設定はすべて無視される仕様です。ただし,アプリケーションサーバでは,Servlet 2.4を使用した場合でも,getSessionでのレスポンスヘッダの設定は有効になります。
(24) MIMEマッピングがない静的コンテンツのコンテンツ形式
MIMEマッピングがない静的コンテンツの場合,Content-Typeは付与されません。
(25) HTTPセッションのアクセス時刻について
セッションIDの付加されたリクエストがWebコンテナに送信されたとき,HTTPセッションのアクセス時刻は現在時刻で更新されます。ただし,すでにセッションがタイムアウトしたり,無効化したりしている場合には該当しません。
HTTPセッションのアクセス時刻は次で使用されます。
-
javax.servlet.http.HttpSessionインタフェースのgetLastAccessedTime()メソッドの戻り値
javax.servlet.http.HttpSessionインタフェースのgetLastAccessedTime()メソッドは,前回更新時のHTTPセッションのアクセス時刻を返します。
-
HTTPセッションのタイムアウト
現在時刻とHTTPセッションのアクセス時刻の差がタイムアウト時間を超えたとき,HTTPセッションはタイムアウトします。なお,HTTPセッションのタイムアウト監視は30秒間隔で実行されるため,正確なタイムアウト時間にタイムアウトが発生するわけではありません。
(26) Content-Lengthヘッダの値取得時の注意事項
HTTPリクエストにContent-Lengthヘッダが含まれていない場合,javax.servlet.ServletRequestのgetContentLength()メソッドの戻り値,およびjavax.servlet.http.HttpServletRequestのgetIntHeader()メソッドで引数に"Content-Length"を指定した場合の戻り値がServlet仕様とアプリケーションサーバとで異なります。それぞれの戻り値を次に示します。
- Servlet仕様
-
-1を返す。
- アプリケーションサーバ
-
0を返す。