All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nablarch.fw.web.handler.HttpErrorHandler Maven / Gradle / Ivy

package nablarch.fw.web.handler;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import nablarch.common.web.WebConfig;
import nablarch.common.web.WebConfigFinder;
import nablarch.core.log.Logger;
import nablarch.core.log.LoggerManager;
import nablarch.core.log.app.FailureLogUtil;
import nablarch.core.message.ApplicationException;
import nablarch.core.util.Builder;
import nablarch.fw.ExecutionContext;
import nablarch.fw.NoMoreHandlerException;
import nablarch.fw.Result;
import nablarch.fw.results.ServiceError;
import nablarch.fw.web.message.ErrorMessages;
import nablarch.fw.web.HttpErrorResponse;
import nablarch.fw.web.HttpRequest;
import nablarch.fw.web.HttpRequestHandler;
import nablarch.fw.web.HttpResponse;
import nablarch.fw.web.ResourceLocator;

/**
 * 共通エラーハンドラー。
 * 
 * HttpResponse/HttpErrorResponse のHTTPエラーコードに対応した
 * エラー画面に遷移させる。
 * また、実行時例外を捕捉し、システムエラー画面に遷移させる。
 * 
* @author Iwauo Tajima */ public class HttpErrorHandler implements HttpRequestHandler { /** ロガー */ private static final Logger LOGGER = LoggerManager.get(HttpErrorHandler.class); /** {@link Result.Error}の中で障害通知ログを出力する対象を表すステータスコード */ protected Pattern writeFailureLogPattern = Pattern.compile("5([1-9][0-9]|0[012456789])"); // SUPPRESS CHECKSTYLE サブクラスで使用するフィールドのため。 /** {@inheritDoc} */ public HttpResponse handle(HttpRequest req, ExecutionContext ctx) { HttpResponse res = null; try { res = ctx.handleNext(req); if (LOGGER.isTraceEnabled()) { LOGGER.logTrace("HTTP Response: ", res, res.getContentPath()); } } catch (NoMoreHandlerException e) { // ハンドラキューが空になり、後続ハンドラに処理を // 委譲できなかった場合は、404エラーをレスポンスする。 LOGGER.logInfo(Builder.concat("There were no Handlers in handlerQueue.", " uri = [", req.getRequestUri(), "]")); res = HttpResponse.Status.NOT_FOUND.handle(req, ctx); } catch (HttpErrorResponse e) { // HTTPエラーコードに対応したアプリケーションエラー。 // レスポンスオブジェクトに従った画面遷移を行う。 ctx.setException(e.getCause()); res = e.getResponse(); // 原因例外がApplicationExceptionの場合はテンプレートエンジンで扱えるよう、 // メッセージを保持するオブジェクトに変換しリクエストスコープに格納する。 if (e.getCause() instanceof ApplicationException) { final WebConfig webConfig = WebConfigFinder.getWebConfig(); ctx.setRequestScopedVar(webConfig.getErrorMessageRequestAttributeName(), new ErrorMessages((ApplicationException) e.getCause())); } } catch (Result.Error e) { // 共通ハンドラ等から送出される汎用例外。 // 対応するHTTPステータスコードのエラー画面をレスポンスする。 if (writeFailureLogPattern.matcher(String.valueOf(e.getStatusCode())).matches()) { if (e instanceof nablarch.fw.results.InternalError) { ((ServiceError) e).writeLog(ctx); } else { FailureLogUtil.logFatal(e, ctx.getDataProcessedWhenThrown(e), null); } } ctx.setException(e.getCause()); res = new HttpResponse(e.getStatusCode()); } catch (RuntimeException e) { // 未捕捉の実行時例外が発生した場合はエラーログを出力し、 // HTTPステータス500のレスポンスオブジェクトを返却する。 // Uncaught runtime exception: FailureLogUtil.logFatal(e, ctx.getDataProcessedWhenThrown(e), null); ctx.setException(e); res = HttpResponse.Status.INTERNAL_SERVER_ERROR.handle(req, ctx); } catch (StackOverflowError e) { // 無限ループのバグの可能性が高いので、通常のエラー扱い。 // Uncaught Error: FailureLogUtil.logFatal(e, ctx.getDataProcessedWhenThrown(e), null); ctx.setException(e); res = HttpResponse.Status.INTERNAL_SERVER_ERROR.handle(req, ctx); } catch (ThreadDeath e) { throw e; } catch (VirtualMachineError e) { throw e; // アプリケーション側で対処すべき状況ではないので、 // このハンドラでは特段の処理を行わず上位にリスローする。 } catch (Error e) { // 上記以外のエラーについてはログ出力後、 // ステータスコード500のレスポンスを返す。 FailureLogUtil.logFatal(e, ctx.getDataProcessedWhenThrown(e), null); ctx.setException(e); res = HttpResponse.Status.INTERNAL_SERVER_ERROR.handle(req, ctx); } finally { final WebConfig webConfig = WebConfigFinder.getWebConfig(); final String varName = webConfig.getErrorMessageRequestAttributeName(); if (ctx.getRequestScopedVar(varName) == null) { ctx.setRequestScopedVar(varName, ErrorMessages.empty()); } } if (res.isBodyEmpty()) { res.setContentPath(getDefaultPageFor(res.getStatusCode())); } return res; } /** * レスポンスステータスコードごとのデフォルトページを設定する。 * * HttpResponseオブジェクトのボディ内容(contentPath/contentBody)が設定されていない場合、 * ここでステータスコード毎に設定したデフォルトページがボディとしてレスポンスされる。 * 設定は後から設定した内容ほど優先される。 * ステータスコードには1桁分のワイルドカードとして "." を使用することができる。 * * 設定例:: *
     *   // デフォルトページ定義
     *   setDefaultPage("303", "file:///www/docroot/redirecting.html");
     *   setDefaultPage("4..", "servlet://jsp/errors/userError.jsp");
     *   setDefaultPage("5..", "servlet://jsp/errors/systemError.jsp");
     * 
* * デフォルトページの設定を行わない場合、 * web.xmlに定義されているエラーページに遷移する。 * * @param statusCode ステータスコードのパターン * @param contentPath デフォルトページのコンテンツパス * @return このオブジェクト自体 */ public HttpErrorHandler setDefaultPage(String statusCode, String contentPath) { if (!statusCode.matches("[0-9.]{3}")) { throw new RuntimeException("invalid status code format.: " + contentPath); } defaultPages.put(statusCode, ResourceLocator.valueOf(contentPath)); return this; } /** * レスポンスステータスコードごとのデフォルトページを設定する。 * @param defaultPages デフォルトページ設定 * @return このオブジェクト自体 */ public HttpErrorHandler setDefaultPages(Map defaultPages) { this.defaultPages.clear(); if (defaultPages != null) { for (Entry defaultPage : defaultPages.entrySet()) { setDefaultPage(defaultPage.getKey(), defaultPage.getValue()); } } return this; } /** * 指定されたステータスコードに対するデフォルトページのコンテンツパスを返す。 * @param statusCode ステータスコード * @return デフォルト画面のコンテンツパス */ public ResourceLocator getDefaultPageFor(int statusCode) { String statusCodeStr = Integer.valueOf(statusCode).toString(); int hiScore = 0; ResourceLocator result = null; if (defaultPages != null) { for (Map.Entry entry : defaultPages.entrySet()) { if (entry.getKey().indexOf('.') == -1) { if (statusCodeStr.equals(entry.getKey())) { return entry.getValue(); } } else { int score = matchingScore(statusCodeStr, entry.getKey()); if (hiScore < score) { result = entry.getValue(); hiScore = score; } } } } return result; } /** * 一致文字数を計数する。 * @param aStr 基準文字列 * @param bStr 比較対象文字列 * @return 一致する文字数 */ private int matchingScore(String aStr, String bStr) { int score = 0; for (int i = 0; i < aStr.length(); i++) { if (i >= bStr.length()) { break; } if (aStr.charAt(i) == bStr.charAt(i)) { score++; } } return score; } /** デフォルトページのドキュメントルート */ private static final String DOCROOT = "servlet:///jsp/"; /** * 既定のデフォルトページ */ private final Map defaultPages = new HashMap(); /** * {@link #handle(nablarch.fw.web.HttpRequest, nablarch.fw.ExecutionContext)}で、{@link Result.Error}を補足した際に、 * 障害通知ログを出力する必要のあるステータスコードを正規表現で設定する。 * * ここで設定した正規表現が、{@link nablarch.fw.Result.Error#getStatusCode()}にマッチした場合のみ、 * 障害通知ログが出力され障害として検知される。 * なお、本設定を省略した場合のデフォルト動作では、{@code 5([1-9][0-9]|0[012456789])}に一致するステータスコードが障害通知ログの出力対象となる。 * @param writeFailureLogPattern 障害通知対象のステータスコードを表す正規表現 */ public void setWriteFailureLogPattern(String writeFailureLogPattern) { this.writeFailureLogPattern = Pattern.compile(writeFailureLogPattern); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy