/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.citrus.webx.util;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.ExceptionUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static com.alibaba.citrus.webx.util.ErrorHandlerHelper.LoggingDetail.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
/**
* 方便存取request中的错误信息的工具类。
*
* @author Michael Zhou
*/
public class ErrorHandlerHelper {
// Servlet规范中指定的用于表示error信息的key。
// SRV.9.9.1 Request Attributes
public final static String KEY_STATUS_CODE = "javax.servlet.error.status_code";
public final static String KEY_MESSAGE = "javax.servlet.error.message";
public final static String KEY_EXCEPTION = "javax.servlet.error.exception";
public final static String KEY_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
public final static String KEY_REQUEST_URI = "javax.servlet.error.request_uri";
public final static String KEY_SERVLET_NAME = "javax.servlet.error.servlet_name";
// 在request中保存errorHandlerHelper的key。
public final static String KEY_ERROR_HANDLER_HELPER = "_webx_errorHandlerHelper_";
/** HTTP标准statusCode所对应的message消息。 */
public final static Map<Integer, String> STATUS_CODE_MESSAGES = getCodeMessages();
// 对应的request
private final HttpServletRequest request;
// 错误信息
private int statusCode;
private String message;
private Throwable exception;
private Class<?> exceptionType;
private String requestURI;
private String servletName;
private ErrorHandlerHelper(HttpServletRequest request) {
this.request = request;
}
/** 从request中取得helper,如果不存在,则创建一个。 */
public static ErrorHandlerHelper getInstance(HttpServletRequest request) {
ErrorHandlerHelper helper = (ErrorHandlerHelper) assertNotNull(request, "request").getAttribute(
KEY_ERROR_HANDLER_HELPER);
if (helper == null) {
helper = new ErrorHandlerHelper(request);
request.setAttribute(KEY_ERROR_HANDLER_HELPER, helper);
}
return helper;
}
/** 初始化helper。 */
public void init(String servletName, Throwable exception, ExceptionCodeMapping mapping) {
setRequestURI(request.getRequestURI());
setServletName(servletName);
setException(assertNotNull(exception, "exception"));
int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
if (mapping != null) {
for (Throwable e : getCauses(exception, true)) {
int mappedCode = mapping.getExceptionCode(e);
if (mappedCode > 0) {
statusCode = mappedCode;
break;
}
}
}
setStatusCode(statusCode);
}
/** 将helper中的异常信息设置成标准的request attributes。 */
public void setServletErrorAttributes() {
setAttribute(KEY_STATUS_CODE, getStatusCode());
setAttribute(KEY_MESSAGE, getMessage());
setAttribute(KEY_EXCEPTION, getException());
setAttribute(KEY_EXCEPTION_TYPE, getExceptionType());
setAttribute(KEY_REQUEST_URI, getRequestURI());
setAttribute(KEY_SERVLET_NAME, getServletName());
}
/** 从request中设置或删除值。 */
private void setAttribute(String key, Object value) {
if (value == null) {
request.removeAttribute(key);
} else {
request.setAttribute(key, value);
}
}
/** 取得错误代码,如果没有,默认为500。 */
public int getStatusCode() {
if (statusCode <= 0) {
return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
} else {
return statusCode;
}
}
/** 取得异常的消息,如果没有,则返回<code>null</code>。 */
public String getMessage() {
if (message == null) {
return STATUS_CODE_MESSAGES.get(getStatusCode());
} else {
return message;
}
}
/** 设置错误代码。 */
public void setStatusCode(int sc) {
setStatusCode(sc, null);
}
/** 设置错误代码。 */
public void setStatusCode(int sc, String message) {
Integer code = sc <= 0 ? null : sc;
if (message == null) {
message = STATUS_CODE_MESSAGES.get(code);
}
this.statusCode = code;
this.message = message;
}
/** 取得异常,如果没有,则返回<code>null</code>。 */
public Throwable getException() {
return exception;
}
/** 取得异常类型,如果没有,则返回<code>null</code>。 */
public Class<?> getExceptionType() {
return exceptionType;
}
/** 设置异常。 */
public void setException(Throwable exception) {
this.exception = exception;
this.exceptionType = exception == null ? null : exception.getClass();
}
/** 取得发生错误的request URI,如果没有,则返回<code>null</code>。 */
public String getRequestURI() {
return requestURI;
}
/** 设置requestURI。 */
public void setRequestURI(String requestURI) {
this.requestURI = requestURI;
}
/** 取得发生错误的servlet名字,如果没有,则返回<code>null</code>。 */
public String getServletName() {
return servletName;
}
/** 设置servletName。 */
public void setServletName(String servletName) {
this.servletName = servletName;
}
/** 从<code>HttpServletResponse</code>中取得statusCode的描述。 */
private static Map<Integer, String> getCodeMessages() {
Map<Integer, String> messages = createTreeMap();
int constantMask = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC;
for (Field field : HttpServletResponse.class.getFields()) {
Class<?> type = field.getType();
String name = field.getName();
int modifiers = field.getModifiers() & constantMask;
if (modifiers == constantMask && name.startsWith("SC_") && int.class.equals(type)) {
try {
Integer sc = (Integer) field.get(null);
String message = name.substring("SC_".length());
messages.put(sc, message);
} catch (Exception e) {
unexpectedException(e);
}
}
}
return messages;
}
public void logError(Logger log) {
logError(log, null);
}
public void logError(Logger log, LoggingDetail detail) {
Throwable e = getException();
if (e == null) {
return;
}
if (detail == null) {
detail = detailed;
}
switch (detail) {
case detailed:
// 打印详细异常信息
log.error("Failed to process request " + getRequestURI() + ", the root cause was " + getRootCauseMessage(), getException());
break;
case brief:
// 打印root cause的message
log.error(getRootCauseMessage());
break;
default:
break;
}
}
private String getRootCauseMessage() {
Throwable rootCause = getRootCause(getException());
String message = rootCause.getClass().getSimpleName();
if (!isBlank(rootCause.getMessage())) {
message += ": " + rootCause.getMessage();
}
return message;
}
@Override
public String toString() {
return getStatusCode() + " " + getMessage();
}
/** 通过exception取得status code的接口。 */
public static interface ExceptionCodeMapping {
/** 取得status code。如果不确定,则返回<code>0</code>或<code>-1</code>。 */
int getExceptionCode(Throwable exception);
}
public static enum LoggingDetail {
detailed,
brief,
disabled
}
}