例外ハンドリング¶Caution本バージョンの内容は既に古くなっています。最新のガイドラインはこちらからご参照ください。例外オブジェクトの属性名HTTPレスポンスのキャッシュ制御有無HandlerExceptionResolverLoggingInterceptorの設定項目についてログ出力対象から除外する例外クラスの一覧DefaultHandlerExceptionResolverで設定されるHTTPレスポンスコードについて本ガイドラインで作成する、Webアプリケーションの例外ハンドリング指針について説明する。MVC配下の処理で発生する例外のハンドリングについて説明する。説明対象は、以下の通りである。例外の分類¶アプリケーション実行時に発生する例外は、以下3つに分類される。種類(1)オペレータの再操作(入力値の変更など)によって、発生原因が解消できる例外オペレータの再操作によって、発生原因が解消できる例外は、アプリケーションコードで例外をハンドリングし、例外処理を行う。正常稼働時に発生するライブラリ例外(2)オペレータの再操作によって、発生原因が解消できない例外オペレータの再操作によって、発生原因が解消できない例外は、フレームワークで例外をハンドリングし、例外処理を行う。致命的なエラー(3)クライアントからの不正リクエストにより発生する例外クライアントからの不正リクエストにより発生する例外は、フレームワークで例外をハンドリングし、例外処理を行う。リクエスト不正時に発生するフレームワーク例外Note誰が、例外を意識する必要があるのか?(1)はアプリケーション開発者が意識する例外となる。(2)と(3)はアプリケーションアーキテクトが意識する例外となる。例外のハンドリング方法¶アプリケーション実行時に発生する例外は、以下4つの方法でハンドリングを行う。ハンドリング方法毎のハンドリングフローの詳細は、例外ハンドリングの基本フローを参照されたい。例外ハンドリングのパターン(1)アプリケーションコードにて、try-catchを使い、例外ハンドリングを行う。リクエスト(Controllerのメソッド)単位に、例外をハンドリングする場合に使用する。詳細は、リクエスト単位でControllerクラスがハンドリングする場合の基本フローを参照されたい。ユースケースの一部やり直し(途中からのやり直し)を促す場合(2)@ExceptionHandlerアノテーションを使い、アプリケーションコードで例外ハンドリングを行う。ユースケース(Controller)単位に、例外をハンドリングする場合に使用する。詳細は、ユースケース単位でControllerクラスがハンドリングする場合の基本フローを参照されたい。ユースケースのやり直し(先頭からのやり直し)を促す場合(3)フレームワークから提供されているHandlerExceptionResolverの仕組みを使い、例外ハンドリングを行う。サーブレット単位に、例外をハンドリングする場合に使用する。HandlerExceptionResolverは、<mvc:annotation-driven>を指定した際に、自動的に、登録されるクラスと、共通ライブラリから提供しているSystemExceptionResolverを使用する。詳細は、サーブレット単位でフレームワークがハンドリングする場合の基本フローを参照されたい。リクエスト内容が、不正であることを通知する場合(4)サーブレットコンテナのerror-page機能を使い、例外ハンドリングを行う。MVC管理外で発生する例外をハンドリングする場合に使用する。詳細は、Webアプリケーション単位でサーブレットコンテナがハンドリングする場合の基本フローを参照されたい。図-例外のハンドリング方法Note誰が例外ハンドリングを行うのか?(1)と(2)はアプリケーション開発者が設計・実装する。(3)と(4)はアプリケーションアーキテクトが設計・設定する。指定した際に、自動的に登録されるHandlerExceptionResolverの役割は、以下の通りである。優先順は、以下の並び順の通りとなる。項番クラス(優先順位)役割(1)ExceptionHandlerExceptionResolver(order=0)@ExceptionHandlerアノテーションが付与されているControllerクラスのメソッドを呼び出し、例外ハンドリングを行うためのクラス。No.2のハンドリング方法を実現するために必要なクラス。(2)ResponseStatusExceptionResolver(order=1)クラスアノテーションとして、@ResponseStatusが付与されている例外をハンドリングするためのクラス。msg)が呼び出される。MVC内で発生するフレームワーク例外を、ハンドリングするためのクラス。sc)が呼び出される。設定されるHTTPレスポンスコードの詳細は、DefaultHandlerExceptionResolverで設定されるHTTPレスポンスコードについてを参照されたい。ハンドリングされない例外をハンドリングするためのクラスである。そのため優先順は、DefaultHandlerExceptionResolverの後になるように設定する。追加された@ControllerAdviceアノテーションについて@ControllerAdviceの登場により、サーブレット単位で、@ExceptionHandlerを使った例外ハンドリングを行えるようになった。@ControllerAdviceアノテーションが付与されたクラスで、@ExceptionHandlerアノテーションを付与したメソッドを定義すると、サーブレット内のすべてのControllerに適用される。各Controllerでベースクラスを継承する必要があった。@ControllerAdviceが付与されたクラスで実装したメソッドを適用するControllerを柔軟に指定できるように改善されている。属性の詳細については、@ControllerAdviceの属性を参照されたい。Note@ControllerAdviceアノテーションの使いどころサーブレット単位で行う例外ハンドリングに対して、View名と、HTTPレスポンスコードの解決以外の処理が必要な場合。(AJAXや、REST用のControllerを作成する際の、エラーハンドリングとして使用する)。例外の種類¶アプリケーション実行時に発生する例外は、以下6種類に分類される。ビジネス例外¶ビジネスルールの違反を検知したことを通知する例外。本例外は、ドメイン層のロジック内で発生させる。アプリケーションとして想定される状態なので、システム運用者による対処は、不要である。(共通ライブラリから提供しているクラス)。細かくハンドリングする必要がある場合は、BusinessExceptionを継承した例外クラスを作成すること。共通ライブラリで用意しているビジネス例外クラスで、要件を満たせない場合は、プロジェクト毎にビジネス例外クラスを作成すること。システムが、正常稼働している時に発生する可能性のある例外。Frameworkや、その他のライブラリ内で発生する例外クラスを対象とする。アプリケーションとして想定される状態なので、システム運用者による対処は、不要である。(楽観排他でエラーが発生した場合に発生する例外)。(悲観排他でエラーが発生した場合に発生する例外)。(一意制約違反となった場合に発生する例外)。...TodoJPA(Hibernate)を使用すると、現状意図しないエラーとなることが発覚している。一意制約違反が発生した場合、DuplicateKeyExceptionではなく、org.springframework.dao.DataIntegrityViolationExceptionが発生する。システム例外¶システムが、正常稼働している時に、発生してはいけない状態を検知したことを通知する例外。本例外は、アプリケーション層、およびドメイン層のロジックで発生させる。システム運用者による対処が必要となる。事前に存在しているはずのマスタデータ、ディレクトリ、ファイルなどが存在しない場合。フレームワーク、ライブラリ内で発生する検査例外のうち、システム異常に分類される例外を捕捉した場合(ファイル操作時のIOExceptionなど)。(共通ライブラリから提供しているクラス)。遷移先のエラー画面や、HTTPレスポンスコードを細かく分ける場合は、SystemExceptionを継承した例外クラスを作成すること。共通ライブラリで用意しているシステム例外クラスだと要件を満たせない場合は、プロジェクト毎にシステム例外クラスを作成すること。予期しないシステム例外¶システムが、正常稼働している時には発生しない非検査例外。システム運用者による対処、またはシステム開発者による解析が必要となる。予期しないシステム例外は、アプリケーションコードでハンドリング(try-catch)すべきでない。アプリケーション、フレームワーク、ライブラリにバグが潜んでいる場合。DBサーバなどがダウンしている場合。...Note該当する例外クラスの例java.lang.NullPointerException(バグ起因で発生する例外)。org.springframework.dao.DataAccessResourceFailureException(DBサーバがダウンしている場合に発生する例外)。致命的なエラー¶システム(アプリケーション)全体に影響を及ぼす、致命的な問題が発生している事を通知するエラー。システム運用者、またはシステム開発者による対処・リカバリが必要となる。致命的なエラー(java.lang.Errorを継承しているエラーオブジェクト)は、アプリケーションコードでハンドリング(try-catch)してはいけない。Java仮想マシンで使用できるメモリが不足している場合。...Note該当するエラークラスの例java.lang.OutOfMemoryError(メモリ不足時に発生するエラー)。リクエスト不正時に発生するフレームワーク例外¶フレームワークが、リクエスト内容の不正を検知したことを通知する例外。MVC)内で発生する。原因は、クライアント側に存在するため、システム運用者による対処は、不要である。POSTメソッドのみ許容しているリクエストパスに対して、GETメソッドでアクセスした場合に発生する例外。@PathVariableアノテーションを使って、URIから値を抽出する際に、URIに型変換できない値が、指定された場合に発生する例外。...Note該当する例外クラスの例org.springframework.web.HttpRequestMethodNotSupportedException(サポート外のメソッドでアクセスされた場合に発生する例外)。org.springframework.beans.TypeMismatchException(URIに型変換できない値が指定された場合に発生する例外)。...DefaultHandlerExceptionResolverで設定されるHTTPレスポンスコードについての中の、HTTPステータスコードが「4XX」の例外が該当するクラス。例外ハンドリングは、目的に応じて、以下6種類のパターンに分類される。(1)-(2)はユースケース毎、(3)-(6)はシステム(アプリケーション)全体でハンドリングを行う。ユースケースの一部やり直し(途中からのやり直し)を促す場合¶ユースケースの一部やり直し(途中からのやり直し)を促す場合は、Controllerクラスのアプリケーションコードで捕捉(try-catch)し、リクエスト単位で例外処理を行う。Noteユースケースの一部やり直しを促す場合の例ショッピングサイトで注文処理を行った際に、在庫不足を通知するビジネス例外が発生する場合。このケースの場合、個数を減らせば注文処理が行えるため、個数が変更できる画面に遷移し、個数変更を促すメッセージを表示する。ユースケースのやり直し(先頭からのやり直し)を促す場合¶ユースケースのやり直し(先頭からのやり直し)を促す場合は、@ExceptionHandlerを使って捕捉し、ユースケース単位で例外処理を行う。Noteユースケースのやり直し(先頭からのやり直し)を促す場合の例ショッピングサイト(管理者向けサイト)で商品マスタの変更を行った際に、変更対象の商品マスタが他のオペレータによって変更されていた場合(楽観排他例外が発生した場合)。このケースの場合、他のユーザが行った変更内容を確認してから操作してもらう必要があるため、ユースケースの先頭画面(例えば商品マスタの検索画面)に遷移し、再操作を促すメッセージを表示する。システム、またはアプリケーションが、正常な状態でない事を通知する場合¶システム、またはアプリケーションが、正常な状態でないことを通知する例外を検知する場合は、SystemExceptionResolverで捕捉し、サーブレット単位で例外処理を行う。外部システムとの接続を行うユースケースにて、外部システムが、閉塞中であることを通知する例外が発生した場合。このケースの場合、外部システムが開局するまで実行できないため、エラー画面に遷移し、外部システムが開局するまでユースケースが実行できない旨を通知する。アプリケーションで指定した値を、条件にマスタ情報の検索を行った際に、該当するマスタ情報が存在しない場合。このケースの場合、マスタメンテナンス機能のバグ又はシステム運用者によるデータ投入ミス(リリースミス)の可能性があるため、システムエラー画面に遷移し、システム異常が発生した旨を通知する。ファイル操作時にAPIからIOExceptionが発生した場合。このケースの場合、ディスク異常などが考えられるため、システムエラー画面に遷移し、システム異常が発生した旨を通知する。リクエスト内容が、不正であることを通知する場合¶フレームワークによって、検知されたリクエスト不正を通知する場合は、DefaultHandlerExceptionResolverで捕捉し、サーブレット単位で例外処理を行う。Noteリクエスト内容が不正であることを通知する場合の例POSTメソッドのみ許可されているURIで、GETメソッドを使ってアクセスした場合。このケースの場合、ブラウザのお気に入り機能などを使って直接アクセスしている事が考えられるため、エラー画面に遷移し、リクエスト内容が不正であることを通知する。アノテーションを使ってURIから値を抽出する際に、URIから値を抽出できなかった場合。このケースの場合、ブラウザのアドレスバーの値を書き換えて、直接アクセスしている事が考えられるため、エラー画面に遷移し、リクエスト内容が不正であることを通知する。致命的なエラーが発生したことを検知する場合、サーブレットコンテナで捕捉し、Webアプリケーション単位で例外処理を行う。プレゼンテーション層(JSPなど)で、例外が発生したことを通知する場合¶プレゼンテーション層(JSPなど)で、例外が発生したことを通知する場合、サーブレットコンテナで捕捉し、Webアプリケーション単位で例外処理を行う。例外処理の基本フローを示す。共通ライブラリから提供しているクラスの概要については、共通ライブラリから提供している例外ハンドリング用のクラスについてを参照されたい。アプリケーションコードで行う処理(実装が必要な処理)についての説明は、太字で表現している。例外メッセージ、およびスタックトレースのログ出力は、共通ライブラリから提供しているクラス(FilterやInterceptorクラス)で行う。例外メッセージ、およびスタックトレース以外の情報を、ログ出力する必要がある場合は、各ロジックで個別にログを出力すること。例外ハンドリングのフロー説明であるため、Serviceクラスを呼び出すまでのフローに関する説明は、省略する。例外をリクエスト単位でハンドリングする場合、Controllerクラスのアプリケーションコードで捕捉(try-catch)し、例外処理を行う。基本フローは、以下の通りである。共通ライブラリから提供しているビジネス例外(org.terasoluna.gfw.common.exception.BusinessException)をハンドリングする場合の基本フローである。ログは、結果メッセージを保持している例外が発生したことを記録するインタセプタ(org.terasoluna.gfw.common.exception.ResultMessagesLoggingInterceptor)を使用して、出力する。BusinessExceptionを生成し、スローする。ExceptionLoggerを呼び出し、warnレベルのログ(監視ログとアプリケーションログ)を出力する。ResultMessagesLoggingInterceptorはResultMessagesNotificationExceptionのサブ例外(BusinessException/ResourceNotFoundException)が発生した場合のみ、ログを出力するクラスである。BusinessExceptionに設定されているメッセージ情報(ResultMessage)を画面表示用にModelに設定する(6’)。Controllerクラスは、遷移先のView名を返却する。DispatcherServletは、返却されたView名に対応するJSPを呼び出す。JSPは、MessagesPanelTagを使用して、メッセージ情報(ResultMessage)を取得し、メッセージ表示用のHTMLコードを生成する。JSPで生成されたレスポンスが表示される。例外をユースケース単位でハンドリングする場合、Controllerクラスの@ExceptionHandlerを使って捕捉し、例外処理を行う。基本フローは、以下の通りである。任意の例外(XxxException)をハンドリングする場合の、基本フローである。ログは、HandlerExceptionResolverによって、例外ハンドリングすることを記録するインタセプタ(org.terasoluna.gfw.web.exception.HandlerExceptionResolverLoggingInterceptor)を使用して、出力する。図-ユースケース単位で、Controllerクラスがハンドリングする場合の基本フローControllerクラスから呼び出されたServiceクラスにて、例外(XxxException)が発生する。DispatcherServletは、XxxExceptionを捕捉し、ExceptionHandlerExceptionResolverを呼び出す。ExceptionHandlerExceptionResolverは、Controllerクラスに用意されている例外ハンドリングメソッドを呼び出す。Controllerクラスは、メッセージ情報(ResultMessage)を生成し、画面表示用としてModelに設定する。Controllerクラスは、遷移先のView名を返却する。ExceptionHandlerExceptionResolverは、Controllerより返却されたView名を返却する。error)のログ(監視ログとアプリケーションログ)を出力する。HandlerExceptionResolverLoggingInterceptorは、ExceptionHandlerExceptionResolverより返却されたView名を返却する。DispatcherServletは、返却されたView名に対応するJSPを呼び出す。JSPは、MessagesPanelTagを使用して、メッセージ情報(ResultMessage)を取得し、メッセージ表示用のHTMLコードを生成する。JSPで生成されたレスポンスが表示される。例外をフレームワーク(サーブレット単位)でハンドリングする場合、SystemExceptionResolverで捕捉し例外処理を行う。基本フローは、以下の通りである。共通ライブラリから提供しているシステム例外(org.terasoluna.gfw.common.exception.SystemException)を、org.terasoluna.gfw.web.exception.SystemExceptionResolverを使ってハンドリングする場合の基本フローである。ログは、例外ハンドリングメソッドの引数に指定された例外を記録するインタセプタ(org.terasoluna.gfw.web.exception.HandlerExceptionResolverLoggingInterceptor)を使用して、出力する。図-サーブレット単位でフレームワークがハンドリングする場合の基本フローServiceクラスにて、システム例外に該当する状態を検知したため、SystemExceptionを発生させる。DispatcherServletは、SystemExceptionを捕捉し、SystemExceptionResolverを呼び出す。SystemExceptionResolverは、SystemExceptionから例外コードを取得し、画面表示用にHttpServletRequestに設定する(6’)。SystemExceptionResolverは、SystemException発生時の遷移先のView名を返却する。error)のログ(監視ログとアプリケーションログ)を出力する。HandlerExceptionResolverLoggingInterceptorは、SystemExceptionResolverより返却されたView名を返却する。DispatcherServletは、返却されたView名に対応するJSPを呼び出す。JSPは、HttpServletRequestより例外コードを取得し、メッセージ表示用のHTMLコードに埋め込む。JSPで生成されたレスポンスが表示される。例外をWebアプリケーション単位でハンドリングする場合、サーブレットコンテナで捕捉し、例外処理を行う。致命的なエラー、フレームワークでハンドリング対象となっていない例外(JSP内で発生した例外など)、Filterで発生した例外をハンドリングする。基本フローは以下の通りである。page”でハンドリングする場合のフローである。ログ出力は、ハンドリングされていない例外が発生したことを記録するサーブレットフィルタ(org.terasoluna.gfw.web.exception.ExceptionLoggingFilter)を使用して、出力する。図-Webアプリケーション単位でサーブレットコンテナがハンドリングする場合の基本フローDispatcherServletは、XxxErrorを捕捉し、ServletExceptionにラップしてスローする。ExceptionLoggingFilterは、ServletExceptionを捕捉し、ExceptionLoggerを呼び出す。error)のログ(監視ログとアプリケーションログ)を出力する。ExceptionLoggingFilterは、ServletExceptionを再スローする。ServletContainerは、ServletExceptionを捕捉し、サーバログにログを出力する。ログのレベルは、アプリケーションサーバによって異なる。定義されている遷移先(HTMLなど)を呼び出す。呼び出された遷移先で生成されたレスポンスが表示される。例外ハンドリング機能の使用方法について説明する。共通ライブラリから提供している例外ハンドリング用のクラスについては、共通ライブラリから提供している例外ハンドリング用のクラスについてを参照されたい。例外ハンドリングを使用する際に、必要なアプリケーション設定を、以下に示す。ブランクプロジェクトは、既に設定済みの状態になっているので、【プロジェクト毎にカスタマイズする箇所】の部分を変更すればよい。例外のログ出力を行うロガークラス(ExceptionLogger)を、bean定義に追加する。説明(1)ExceptionCodeResolverを、bean定義に追加する。(2)ハンドリング対象とする例外名と、適用する「例外コード(メッセージID)」のマッピングを指定する。“ResourceNotFoundException”が含まれている場合は、”w.xx.fw.5001”が「例外コード(メッセージID)」となる。後述の”BusinessException”を発生させる実装側で、「例外コード(メッセージID)」を指定することを推奨する。“BusinessException”に対する「例外コード(メッセージID)」の指定は、”BusinessException”発生時に指定されなかった場合の救済策である。【プロジェクト毎にカスタマイズする箇所】(3)デフォルトの「例外コード(メッセージID)」を指定する。上記の設定例では、例外クラス(または親クラス)のクラス名に”BusinessException”、または”ResourceNotFoundException”が含まれない場合、”e.xx.fw.9001”が例外コード(メッセージID)」となる。【プロジェクト毎にカスタマイズする箇所】Note例外コード(メッセージID)について例外コードは、ログに出力するのみ。(画面での取得もできる。プロパティに定義している形式でなくとも、運用上でわかるIDにすることが可能である。MA7001等(4)ExceptionLoggerを、bean定義に追加する。(5)ExceptionCodeResolverをDIする。logback.xml監視ログ用のログ定義を追加する。説明(1)監視ログを出力するための、appender定義を指定する。上記の設定例では、ファイルに出力するappenderとしているが、システム要件に一致するappenderを使うこと。【プロジェクト毎にカスタマイズする箇所】(2)監視ログ用の、ロガー定義を指定する。ExceptionLoggerを作成する際に、任意のロガー名を指定していない場合は、上記設定のままでよい。Warningadditivityの設定値についてfalseを指定すること。trueを指定すると、上位のロガー(例えば、root)によって、同じログが出力されてしまう。(3)出力レベルを指定する。errorの3種類のログを出力しているが、システム要件にあったレベルを指定すること。errorレベルを推奨する。【プロジェクト毎にカスタマイズする箇所】(4)出力先となるappenderを指定する。【プロジェクト毎にカスタマイズする箇所】アプリケーションログ用のログ定義を追加する。説明(1)アプリケーションログを出力するための、appender定義を指定する。上記の設定例では、ファイルに出力するappenderとしているが、システム要件に一致するappenderを使うこと。【プロジェクト毎にカスタマイズする箇所】(2)アプリケーションログ用の、ロガー定義を指定する。ExceptionLoggerを作成する際に、任意のロガー名を指定していない場合は、上記設定のままでよい。Noteアプリケーションログ出力用のappender定義についてアプリケーションログ用のappenderは、例外出力用に個別に定義するのではなく、フレームワークや、アプリケーションコードで出力するログ用のappenderと、同じものを使うことを推奨する。同じ出力先にすることで、例外の発生するまでの過程が追いやすくなる。(3)出力レベルを指定する。errorの3種類のログを出力しているが、システム要件にあったレベルを指定すること。本ガイドラインでは、infoレベルを推奨する。【プロジェクト毎にカスタマイズする箇所】(4)(2)で設定したロガーは、appenderを指定していないので、rootに流れる。そのため、出力先となるappenderを指定する。ここでは、”STDOUT”と”APPLICATION_LOG_FILE”に出力される。ドメイン層の設定¶ResultMessagesを保持する例外(BisinessException,ResourceNotFoundException)が発生した際に、ログを出力するためのインタセプタクラス(ResultMessagesLoggingInterceptor)と、AOPの設定を、bean定義に追加する。説明(1)ResultMessagesLoggingInterceptorを、bean定義に追加する。(2)例外のログ出力を行うロガーオブジェクトをDIする。(3)Serviceクラス(@Serviceアノテーションが付いているクラス)のメソッドに対して、ResultMessagesLoggingInterceptorを適用する。指定した際に、自動的に登録されるHandlerExceptionResolverによって、ハンドリングされない例外をハンドリングするためのクラス(SystemExceptionResolver)を、bean定義に追加する。説明(1)SystemExceptionResolverを、bean定義に追加する。(2)例外コード(メッセージID)を解決するオブジェクトをDIする。applicationContext.xmlに定義している、”exceptionCodeResolver”を指定する。(3)ハンドリングの優先順位を指定する。値は、基本的に「3」で良い。<mvc:annotation-driven>を指定した際に、自動的に、登録されるクラスの方が、優先順位が上となる。Pageで行う必要がある。Viewの解決をweb.xmlではなく、HandlerExceptionResolverで行いたい場合は、SystemExceptionResolverの優先順位を「1」にすると、DefaultHandlerExceptionResolverより前にハンドリング処理を実行することができる。DefaultHandlerExceptionResolverでハンドリングされた場合の、HTTPレスポンスコードのマッピングについては、DefaultHandlerExceptionResolverで設定されるHTTPレスポンスコードについてを参照されたい。(4)ハンドリング対象とする例外名と、遷移先となるView名のマッピングを指定する。上記の設定では、例外クラス(または親クラス)のクラス名に”.DataAccessException”が含まれている場合、”common/error/dataAccessError”が、遷移先のView名となる。例外クラスが”ResourceNotFoundException”の場合、”common/error/resourceNotFoundError”が、遷移先のView名となる。【プロジェクト毎にカスタマイズする箇所】(5)遷移先となるView名と、HTTPステータスコードのマッピングを指定する。Found)”がHTTPステータスコードとなる。【プロジェクト毎にカスタマイズする箇所】(6)遷移するデフォルトのView名を、指定する。上記の設定では、例外クラスに”ResourceNotFoundException”、”BusinessException”、”InvalidTransactionTokenException”や例外クラス(または親クラス)のクラス名に、”.DataAccessException”が含まれない場合、”common/error/systemError”が、遷移先のView名となる。【プロジェクト毎にカスタマイズする箇所】(7)レスポンスヘッダに設定するHTTPステータスコードのデフォルト値を指定する。設定することを推奨する。“200”(OK)扱いになるので、注意すること。(8)実際に遷移するViewは、ViewResolverの設定に依存する。上記の設定では、/WEB-INF/views/common/error/systemError.jsp/WEB-INF/views/common/error/resourceNotFoundError.jsp/WEB-INF/views/common/error/businessError.jsp/WEB-INF/views/common/error/transactionTokenError.jsp/WEB-INF/views/common/error/dataAccessError.jspが遷移先となる。4.1から追加されたXML要素である。<mvc:view-resolvers>要素を使用すると、ViewResolverをシンプルに定義することが出来る。従来通り<bean>要素を使用した場合の定義例を以下に示す。/></bean>HandlerExceptionResolverでハンドリングされた例外を、ログに出力するためのインタセプタクラス(HandlerExceptionResolverLoggingInterceptor)と、AOPの設定を、bean定義に追加する。説明(1)HandlerExceptionResolverLoggingInterceptorを、bean定義に追加する。(2)例外のログ出力を行うロガーオブジェクトを、DIする。applicationContext.xmlに定義している”exceptionLogger”を指定する。(3)HandlerExceptionResolverインタフェースのresolveExceptionメソッドに対して、HandlerExceptionResolverLoggingInterceptorを適用する。サブクラスの例外は、このクラスで行われるログ出力の対象外となっている。よってログ出力されるためである。MVC管理外で発生する例外を、ログに出力するためのFilterクラス(ExceptionLoggingFilter)を、bean定義とweb.xmlに追加する。説明(1)ExceptionLoggingFilterを、bean定義に追加する。(2)例外のログ出力を行うロガーオブジェクトを、DIする。applicationContext.xmlに定義している”exceptionLogger”を指定する。説明(1)フィルター名を指定する。applicationContext.xmlに定義したExceptionLoggingFilterのbean名と、一致させる。(2)フィルタークラスを指定する。org.springframework.web.filter.DelegatingFilterProxy固定。(3)マッピングするフィルターのフィルター名を指定する。(4)フィルターを適用するURLパターンを指定する。MVC管理外をログ出力するため、/*を推奨する。Page定義を追加する。MVCの、デフォルトの例外ハンドリング機能によって行われるエラー応答(HttpServletResponse#sendError)を、ハンドリングするための定義を追加する。MVCの、デフォルトの例外ハンドリング機能で応答されるHTTPレスポンスコードについては、DefaultHandlerExceptionResolverで設定されるHTTPレスポンスコードについてを参照されたい。(2)遷移するファイル名を指定する。Webアプリケーションルートからのパスで、指定する。上記の設定では、”${WebAppRoot}/WEB-INF/views/common/error/resourceNotFoundError.jsp”が、遷移先のファイルとなる。MVC管理外で発生する例外をハンドリングするための定義を追加する。説明(3)遷移するファイル名を指定する。Webアプリケーションルートからのパスで指定する。上記の設定では、”${WebAppRoot}/WEB-INF/views/common/error/unhandledSystemError.html”が、遷移先のファイルとなる。HTMLなどの静的コンテンツへのパスを指定することを推奨する。上記の設定が行われている状態で想定外のエラー応答(HttpServletResponse#sendError)が発生した場合、どのようなエラー応答が発生したのか特定できないケースがある。上記設定をコメントアウトして動かすことで、発生したエラー応答(HTTPレスポンスコード)を、画面で確認することできる。MVC管理外で発生する例外を、個別にハンドリングする必要がある場合は、例外毎の定義を追加する。(5)遷移するファイル名を指定する。Webアプリケーションルートからのパスで指定する。上記の設定では、”${WebAppRoot}/WEB-INF/views/common/error/systemError.jsp”が遷移先のファイルとなる。例外ハンドリングを行う際の、Serviceでのコーディングポイントを、以下に示す。ビジネス例外を発生させる¶ビジネス例外(BusinessException)の発生方法を、以下に示す。基本的には、ロジックでビジネスルールの違反を検知して、ビジネス例外を発生させる方法を推奨する。既存資材や、基盤機能(FWや共通機能)のAPI仕様として、ビジネスルールの違反が、例外によって通知される場合のみ、例外を捕捉してビジネス例外を発生させてもよい。例外を、処理フローを制御するために使用すると、処理全体の見通しが悪くなり、保守性を低下させる可能性がある。ロジックでビジネスルールの違反を検知して、ビジネス例外を発生させる。Warningデフォルトでは、ビジネス例外は、Serviceで発生させることを想定している。@Serviceアノテーションを付与したクラスで発生したビジネス例外のログを出力としている。Controllerなどでビジネス例外は、ログを出力しない。プロジェクトでの考えがある場合は変更すること。説明(1)ビジネスルールの違反がないか、チェックを行う。(2)違反している場合、ResultMessagesを生成する。上記の実装例では、errorレベルのResultMessagesを生成している。ResultMessagesの生成方法の詳細については、メッセージ管理を参照されたい。(3)ResultMessagesに、ResultMessageを追加する。第1引数(必須)にメッセージIDを、第2引数(任意)にメッセージ埋め込み値を指定する。メッセージ埋め込み値は、可変長パラメータなので、複数指定することができる。(4)ResultMessagesを指定して、BusinessExceptionを発生させる。説明用に(2)-(4)に分けて処理をしているが、1ステップで実装することができる。stockQuantity));xxx.properties参考としてプロパティの設定を記述する。number.下記のようなアプリケーションログが出力される。'exception'表示される画面Warningビジネス例外は、Controllerでハンドリングし、各業務画面でメッセージを表示させることを推奨する。上記例は、Controllerでハンドリングしなかった場合に、表示される画面となる。説明(1)ビジネスルールに違反した際に、発生する例外を捕捉する。(2)ResultMessagesと、原因例外(e)を指定して、BusinessExceptionを発生させる。システム例外を発生させる¶システム例外(SystemException)の発生方法を、以下に示す。ロジックで、システム異常を検知し、システム例外を発生させる。説明(1)システムが、正常な状態であることをチェックする。存在しない場合、システムで用意するべきリソースがないと判断して、システムエラーにしている。(2)システムが異常な状態の場合、第1引数に例外コード(メッセージID)を指定する。第2引数に例外メッセージを指定して、SystemExceptionを発生させる。上記の実装例では、メッセージ本文に、変数”itemId”の値を埋め込んでいる。下記のような、アプリケーションログが出力される。omitted表示される画面Noteシステムエラー画面は、個別に用意せず、共通的に決めることを推奨する。本ガイドラインの画面では、システムエラーのためのメッセージID(業務毎)を表示し、文言は固定にしている。その理由は、オペレータに対して、エラーの細かい内容を知らせる必要がなく、システムに異常があることだけを伝えればよいためである。そこで、開発側では、解析を簡易にするために、キーとなるメッセージIDを画面に表示して、システム異常の問い合わせに対するレスポンスを向上しようとしている。表示される画面については、各プロジェクトでUI規約に従い、用意すること。説明(1)システム異常に分類される検査例外を捕捉する。(2)例外コード(メッセージID)、メッセージ、原因例外(e)を指定して、SystemExceptionを発生させる。例外をキャッチして、処理を継続させる必要がある場合、発生した例外をログに出力してから、処理を継続するようにする。情報を取得する処理を、継続する場合の例である。この例では、顧客対応履歴の情報が取得できなくても、業務は継続できるため、処理を継続している。説明(1)共通ライブラリで提供しているorg.terasoluna.gfw.common.exception.ExceptionLoggerをログ出力するため、オブジェクトをDIする。(2)ハンドリング対象の例外を、キャッチする。(3)ハンドリングした例外を、ログに出力する。後に変更する可能性がない場合は、info、warn、errorメソッドを直接呼び出してもよい。(4)(3)でログを出力したのみで、処理を継続する。下記のような、アプリケーションログが出力される。omittedWarningexceptionLoggerで、log()を使用した場合には、errorレベルで出力されるため、デフォルトで監視ログにも出力される。exception次の例のように、処理を継続させて問題ない場合に、運用監視で監視ログを監視している場合は、出力レベルで監視されないレベルにするか、メッセージから監視されないよう定義が必要である。exceptionLogger.info(e);}デフォルトの設定では、errorレベル以外の監視ログは出力されない。アプリケーションログには、以下のように出力される。例外ハンドリングを行う際の、Controllerでのコーディングポイントを、以下に示す。例外をリクエスト単位でハンドリングし、引き継ぎ情報(メッセージ情報)を、Modelに設定する。その後、遷移する画面を表示するためのメソッドを呼び出すことで、遷移先で必要なモデルを生成し、View名を決定する。説明(1)エラー情報を、Viewと連携するためのオブジェクトとして、Modelを引数に定義する。(2)ハンドリング対象となる例外を、アプリケーションコードで捕捉する。(3)ResultMessagesオブジェクトを、Modelに追加する。(4)エラー時の遷移先を表示するためのメソッドを呼び出し、View表示に必要なモデルと、View名を取得した後に、表示するView名を返却する。例外を、ユースケース単位でハンドリングし、引き継ぎ情報(メッセージ情報など)が格納されたModelMap(ExtendedModelMap)を生成する。その後、遷移する画面を表示するためのメソッドを呼び出すことで、遷移先で必要なモデルを生成し、View名を決定する。説明(1)@ExceptionHandlerアノテーションのvalue属性に、ハンドリング対象とする例外クラスを指定する。ハンドリング対象とする例外は、複数指定することもできる。(2)@ResponseStatusアノテーションの、value属性に返却するHTTPステータスコードを指定する。例では、「409:Conflict」を指定している。(3)エラー情報と、モデル情報を、Viewと連携するためのオブジェクトとして、ExtendedModelMapを生成する。(4)ResultMessagesオブジェクトを、ExtendedModelMapに追加する。(5)エラー時の遷移先を表示するためのメソッドを呼び出し、View表示に必要なモデルと、View名を取得する。(6)(3)-(5)の処理で取得したView名と、Modelが格納されているModelAndViewを生成し、返却する。例外ハンドリングを行う際の、JSPでのコーディングポイントを、以下に示す。エラー画面として応答するHTMLのサイズが513バイト以上になるように実装する必要がある。Explorerが用意している簡易メッセージが表示される仕組みになっているためである。任意の場所に、ResultMessagesを出力する際の実装例を、以下に示す。説明(1)メッセージを出力したい場所に、<t:messagesPanel>タグを指定する。<t:messagesPanel>タグの使用方法の詳細については、メッセージ管理を参照されたい。任意の場所に、例外コード(メッセージID)と、固定メッセージを表示する際の実装例を、以下に示す。説明(1)例外コード(メッセージID)の存在チェックを行う。上記の実装例のように、記号などで例外コード(メッセージID)を囲む場合は、存在チェックを行うこと。(2)例外コード(メッセージID)を出力する。(3)メッセージ定義より取得した、固定メッセージを出力する。出力画面(exceptionCode有り)出力画面(exceptionCode無し)Noteシステム例外時に出力するメッセージについてシステム例外が発生した場合、エラー原因が特定できる、または推測できる詳細メッセージを出力せず、システム例外が発生したことだけを伝えるメッセージを表示することを推奨する。エラー原因が特定できる、または推測できる詳細メッセージを表示した場合、システムの脆弱性を公開してしまう可能性がある。Note例外コード(メッセージID)についてシステム例外が発生した場合、詳細メッセージの代わりに、例外コード(メッセージID)を出力することを推奨する。例外コード(メッセージID)を出力することで、システム利用者からの問い合わせに、素早く対応することができる。例外コード(メッセージID)からエラー原因を特定できるのは、システム管理者だけなので、システムの脆弱性を公開する危険性は少なくなる。(Ajax)¶Ajaxの例外ハンドリングについては、Ajaxを参照されたい。MVCが提供しているクラスとは別に、共通ライブラリより例外ハンドリングを行うためのクラスを提供している。クラスの役割は、以下の通りである。項番クラス役割(1)ExceptionCodeResolver例外クラスに対応する例外コード(メッセージID)を解決するためのインタフェース。例外コードとは、どのような例外が発生したのかを識別するためのコードで、システムエラー画面や、ログに出力することを想定している。ExceptionLogger、SystemExceptionResolverなどから参照される。(2)SimpleMappingExceptionCodeResolverExceptionCodeResolverの実装クラスで、例外クラスの名前と、例外コードのマッピングを保持することで、例外コードの解決を実現する。例外クラスの名前は、FQCNではなく、FQCNの一部や、親クラスの名前でもよい。WarningFQCNの一部を指定した場合、場合によっては想定していなかったクラスとマッチしてしまうことがあるので注意が必要である。親クラスの名前を指定した場合、全ての子クラスがマッチするので注意が必要である。(3)enums.ExceptionLevel例外クラスに対応する例外レベルを表現するenum。ERRORが定義されている。(4)ExceptionLevelResolver例外クラスに対応する例外レベル(ログレベル)を解決するためのインタフェース。例外レベルとは、どのようなレベルの例外が発生したのかを識別するためのコードで、ログの出力レベルを切り替えるために使われる。実装クラスで、例外コードの先頭1文字で、例外レベルを解決している。ExceptionLevel.ERRORレベルとして扱う。本クラスは、メッセージのガイドラインに記載されている、メッセージIDのルールに則った実装となっている。(6)ExceptionLogger例外をログ出力するためのクラス。監視ログ(メッセージのみ)と、アプリケーションログ(メッセージと、スタックトレースの両方)を出力することができる。本クラスは、フレームワークより提供しているFilterや、Interceptorクラスから使用されている。アプリケーションコードで、例外をハンドリングして処理を継続する場合、本クラスを用いて、ログを出力すること。)が発生した事をログに出力するためのInterceptorクラス。ログは全てWARNレベルで出力する。アノテーションが付与されているクラスのメソッドに対して適用することを想定している。ログは、ExceptionLoggerを使用して出力している。(8)BusinessExceptionビジネスルールの違反を検知したことを通知するための例外クラスで、ドメイン層のロジックで発生させる例外である。java.lang.RumtimeExceptionを継承しているため、デフォルトの動作として、トランザクションは、ロールバックされる。本例外クラスを指定する必要がある。(9)ResourceNotFoundException指定されたリソース(データ)が、システム内に存在しないことを通知するための例外クラスで、主に、ドメイン層のロジックで発生させる例外である。継承しているため、デフォルトの動作として、トランザクションは、ロールバックされる。(10)ResultMessagesNotificationException結果メッセージ(ResultMessages)を保持している例外であることを通知するための抽象例外クラスで、共通ライブラリでは、BusinessExceptionと、ResourceNotFoundExceptionが継承している。java.lang.RumtimeExceptionを継承しているため、デフォルトの動作としてはトランザクションはロールバックされる。本例外クラスを継承すると、ResultMessagesLoggingInterceptorによって、warnレベルのログが出力される。(11)SystemExceptionシステム又はアプリケーションの異常を検知した事を通知するための例外クラスで、アプリケーション層又はドメイン層のロジックで発生させる例外である。java.lang.RumtimeExceptionを継承しているため、デフォルトの動作として、トランザクションは、ロールバックされる。(12)ExceptionCodeProvider例外コードを保持する役割があることを示すインタフェースで、共通ライブラリでは、SystemExceptionが実装している。本インタフェースを実装した例外クラスを作成すると、共通ライブラリから提供している例外ハンドリング処理にて、例外で保持している例外コードで、そのまま使われる。項番クラス役割(13)SystemExceptionResolver<mvc:annotation-driven>を指定した際に、自動的に登録されるHandlerExceptionResolverによって、ハンドリングされない例外をハンドリングするためのクラス。MVCより提供されているSimpleMappingExceptionResolverを継承し、例外コードのResultMessagesを、Viewから参照できるように機能追加を行っている。(14)HandlerExceptionResolverLoggingInterceptorHandlerExceptionResolverでハンドリングされた例外を、ログに出力するためのInterceptorクラス。本Interceptorクラスでは、HandlerExceptionResolverで解決されたHTTPレスポンスコードの分類に応じて、ログの出力レベルを切り替えている。INFOレベルで出力する。WARNレベルで出力する。ERRORレベルで出力する。MVC管理下で発生する全ての例外を、ログに出力することができる。ログは、ExceptionLoggerを使用して出力している。MVC管理外で発生する例外を、ログに出力するためのFilterクラス。ログは、すべてERRORレベルで出力する。MVC管理外で発生するすべての例外を、ログに出力することができる。ログは、ExceptionLoggerを使用して出力している。本編で説明していない設定項目について、説明する。要件に応じて、設定を行うこと。説明デフォルト値(1)結果メッセージの属性名resultMessagesAttributeビジネス例外に設定されているメッセージ情報として、モデルに設定する際の属性名(String)を指定する。View(JSP)から結果メッセージにアクセスする際の、属性名となる。resultMessages(2)例外コード(メッセージID)の属性名exceptionCodeAttribute例外コード(メッセージID)として、HttpServletRequestに設定する際の属性名(String)を指定する。View(JSP)から例外コード(メッセージID)にアクセスする際の属性名となる。exceptionCode(3)例外コード(メッセージID)のヘッダー名exceptionCodeHeader例外コード(メッセージID)として、HttpServletResponseのレスポンスヘッダーに設定する際のヘッダー名(String)を指定する。X-Exception-Code(4)例外オブジェクトの属性名exceptionAttributeハンドリングした例外オブジェクトとして、モデルに設定する際の属性名(String)を指定する。View(JSP)から例外オブジェクトにアクセスする際の属性名となる。exception(5)本ExceptionResolverとして、使用するハンドラー(Controller)のオブジェクト一覧mappedHandlers本ExceptionResolverを使用するハンドラーの、オブジェクト一覧(Set)を指定する。指定したハンドラーオブジェクトで発生した例外のみ、ハンドリングが行われる。この設定項目は指定してはいけない。指定なし指定した場合の動作は、保証しない。(6)本ExceptionResolverを使用するハンドラー(Controller)のクラス一覧mappedHandlerClasses本ExceptionResolverを使用するハンドラーのクラス一覧(Class[])を指定する。指定したハンドラークラスで発生した例外のみハンドリングが行われる。この設定項目は指定してはいけない。指定なし指定した場合の動作は、保証しない。false:無)を指定する。true:有を指定すると、キャッシュを無効にするためのHTTPレスポンスヘッダーが追加される。false:無(1)-(3)は、org.terasoluna.gfw.web.exception.SystemExceptionResolverの設定項目。(4)は、org.springframework.web.servlet.handler.SimpleMappingExceptionResolverの設定項目。(5)-(7)は、org.springframework.web.servlet.handler.AbstractHandlerExceptionResolverの設定項目。結果メッセージの属性名¶SystemExceptionResolverでハンドリングして設定したメッセージと、アプリケーションコードでハンドリングして設定したメッセージを、View(JSP)で別のmessagesPanelとして出力したい場合は、SystemExceptionResolver専用の属性名を指定する。下記に示す例は、デフォルト値から「resultMessagesForExceptionResolver」に変更する場合の、設定&実装例である。説明(1)結果メッセージの属性名(resultMessagesAttribute)に、”resultMessagesForExceptionResolver”を指定する。(2)メッセージ属性名(messagesAttributeName)に、SystemExceptionResolverで設定した属性名を指定する。例外コード(メッセージID)の属性名¶デフォルトの属性名をアプリケーションコードで使用している場合は、重複を避けるために、別の値を設定すること。重複がない場合は、デフォルト値を変更する必要はない。下記は、デフォルト値から、「exceptionCodeForExceptionResolver」に変更する場合の、設定&実装例である。説明(1)例外コード(メッセージID)の属性名(exceptionCodeAttribute)に、”exceptionCodeForExceptionResolver”を指定する。(2)SystemExceptionResolverに設定した値(exceptionCodeForExceptionResolver)を、テスト対象(空チェック対応)の変数名として指定する。(3)SystemExceptionResolverに設定した値(exceptionCodeForExceptionResolver)を、出力対象の変数名として指定する。例外コード(メッセージID)のヘッダー名¶デフォルトのヘッダー名が使用されている場合、重複を避けるために、別の値を設定すること。重複がない場合は、デフォルト値を変更する必要はない。下記は、デフォルト値から「X-Exception-Code-ForExceptionResolver」に変更する場合の、設定&実装例である。説明(1)例外コード(メッセージID)のヘッダー名(exceptionCodeHeader)に、”X-Exception-Code-ForExceptionResolver”を指定する。例外オブジェクトの属性名¶デフォルトの属性名をアプリケーションコードで使用している場合は、重複を避けるために、別の値を設定すること。重複がない場合は、デフォルト値を変更する必要はない。下記は、デフォルト値から「exceptionForExceptionResolver」に変更する場合の、設定&実装例である。説明(1)例外オブジェクトの属性名(exceptionAttribute)に、”exceptionForExceptionResolver”を指定する。(2)SystemExceptionResolverに設定した値(exceptionForExceptionResolver)を、例外オブジェクトからメッセージを取得するための変数名として、指定する。HTTPレスポンスのキャッシュ制御有無¶HTTPレスポンスに、キャッシュ制御用のヘッダーを追加したい場合は、true:有を指定する。説明(1)HTTPレスポンスのキャッシュ制御有無(preventResponseCaching)に、true:有を指定する。Note有を指定した場合のHTTPレスポンスヘッダーHTTPレスポンスのキャッシュ制御有無を有にすると、以下のHTTPレスポンスヘッダーが出力される。本編で説明していない設定項目について、説明する。要件に応じて、設定を行うこと。よってハンドリングされた例外のうち、ログ出力しない例外クラスをリスト形式で指定する。本クラスでログの出力は行われない。本項目に指定する例外クラスは、別の場所(別の仕組み)でログ出力される例外のみ指定すること。ログ出力されるため、デフォルト設定として除外している。ログ出力対象から除外する例外クラスの一覧¶プロジェクトで用意した例外クラスをログ出力対象から除外したい場合は、以下のような設定となる。(2)プロジェクトで用意した例外クラスを除外対象に指定する。全ての例外クラスをログ出力対象とする場合は、以下のような設定となる。指定すると、全ての例外クラスがログ出力対象となる。DefaultHandlerExceptionResolverで設定されるHTTPレスポンスコードについて¶DefaultHandlerExceptionResolverでハンドリングされるフレームワーク例外と、HTTPステータスコードのマッピングを、以下に記載する。