com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xwork Show documentation
Show all versions of xwork Show documentation
XWork is an command-pattern framework that is used to power WebWork
as well as other applications. XWork provides an Inversion of Control
container, a powerful expression language, data type conversion,
validation, and pluggable configuration.
/*
* Copyright (c) 2002-2006 by OpenSymphony
* All rights reserved.
*/
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Iterator;
import java.util.List;
/**
*
*
* This interceptor forms the core functionality of the exception handling feature. Exception handling allows you to map
* an exception to a result code, just as if the action returned a result code instead of throwing an unexpected
* exception. When an exception is encountered, it is wrapped with an {@link ExceptionHolder} and pushed on the stack,
* providing easy access to the exception from within your result.
*
* Note: While you can configure exception mapping in your configuration file at any point, the configuration
* will not have any effect if this interceptor is not in the interceptor stack for your actions. It is recommended that
* you make this interceptor the first interceptor on the stack, ensuring that it has full access to catch any
* exception, even those caused by other interceptors.
*
*
*
* Interceptor parameters:
*
*
*
*
*
* - logEnabled (optional) - Should exceptions also be logged? (boolean true|false)
*
* - logLevel (optional) - what log level should we use (
trace, debug, info, warn, error, fatal
)? - defaut is debug
*
* - logCategory (optional) - If provided we would use this category (eg.
com.mycompany.app
).
* Default is to use com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor
.
*
*
*
* The parameters above enables us to log all thrown exceptions with stacktace in our own logfile,
* and present a friendly webpage (with no stacktrace) to the end user.
*
*
*
* Extending the interceptor:
*
*
*
*
*
* If you want to add custom handling for publishing the Exception, you may override
* {@link #publishException(com.opensymphony.xwork2.ActionInvocation, ExceptionHolder)}. The default implementation
* pushes the given ExceptionHolder on value stack. A custom implementation could add additional logging etc.
*
*
*
* Example code:
*
*
*
* <xwork>
* <package name="default" extends="xwork-default">
* <global-results>
* <result name="success" type="freemarker">error.ftl</result>
* </global-results>
*
* <global-exception-mappings>
* <exception-mapping exception="java.lang.Exception" result="error"/>
* </global-exception-mappings>
*
* <action name="test">
* <interceptor-ref name="exception"/>
* <interceptor-ref name="basicStack"/>
* <exception-mapping exception="com.acme.CustomException" result="custom_error"/>
* <result name="custom_error">custom_error.ftl</result>
* <result name="success" type="freemarker">test.ftl</result>
* </action>
* </package>
* </xwork>
*
*
*
*
* This second example will also log the exceptions using our own category
* com.mycompany.app.unhandled at WARN level.
*
*
*
* <xwork>
* <package name="something" extends="xwork-default">
* <interceptors>
* <interceptor-stack name="exceptionmapping-stack">
* <interceptor-ref name="exception">
* <param name="logEnabled">true</param>
* <param name="logCategory">com.mycompany.app.unhandled</param>
* <param name="logLevel">WARN</param>
* </interceptor-ref>
* <interceptor-ref name="i18n"/>
* <interceptor-ref name="static-params"/>
* <interceptor-ref name="params"/>
* <interceptor-ref name="validation">
* <param name="excludeMethods">input,back,cancel,browse</param>
* </interceptor-ref>
* </interceptor-stack>
* </interceptors>
*
* <default-interceptor-ref name="exceptionmapping-stack"/>
*
* <global-results>
* <result name="unhandledException">/unhandled-exception.jsp</result>
* </global-results>
*
* <global-exception-mappings>
* <exception-mapping exception="java.lang.Exception" result="unhandledException"/>
* </global-exception-mappings>
*
* <action name="exceptionDemo" class="org.apache.struts2.showcase.exceptionmapping.ExceptionMappingAction">
* <exception-mapping exception="org.apache.struts2.showcase.exceptionmapping.ExceptionMappingException"
* result="damm"/>
* <result name="input">index.jsp</result>
* <result name="success">success.jsp</result>
* <result name="damm">damm.jsp</result>
* </action>
*
* </package>
* </xwork>
*
*
*
* @author Matthew E. Porter (matthew dot porter at metissian dot com)
* @author Claus Ibsen
*/
public class ExceptionMappingInterceptor extends AbstractInterceptor {
protected static final Log log = LogFactory.getLog(ExceptionMappingInterceptor.class);
protected Log categoryLogger;
protected boolean logEnabled = false;
protected String logCategory;
protected String logLevel;
public boolean isLogEnabled() {
return logEnabled;
}
public void setLogEnabled(boolean logEnabled) {
this.logEnabled = logEnabled;
}
public String getLogCategory() {
return logCategory;
}
public void setLogCategory(String logCatgory) {
this.logCategory = logCatgory;
}
public String getLogLevel() {
return logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
public String intercept(ActionInvocation invocation) throws Exception {
String result;
try {
result = invocation.invoke();
} catch (Exception e) {
if (logEnabled) {
handleLogging(e);
}
List exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
String mappedResult = this.findResultFromExceptions(exceptionMappings, e);
if (mappedResult != null) {
result = mappedResult;
publishException(invocation, new ExceptionHolder(e));
} else {
throw e;
}
}
return result;
}
/**
* Handles the logging of the exception.
*
* @param e the exception to log.
*/
protected void handleLogging(Exception e) {
if (logCategory != null) {
if (categoryLogger == null) {
// init category logger
categoryLogger = LogFactory.getLog(logCategory);
}
doLog(categoryLogger, e);
} else {
doLog(log, e);
}
}
/**
* Performs the actual logging.
*
* @param logger the provided logger to use.
* @param e the exception to log.
*/
protected void doLog(Log logger, Exception e) {
if (logLevel == null) {
logger.debug(e.getMessage(), e);
return;
}
if ("trace".equalsIgnoreCase(logLevel)) {
logger.trace(e.getMessage(), e);
} else if ("debug".equalsIgnoreCase(logLevel)) {
logger.debug(e.getMessage(), e);
} else if ("info".equalsIgnoreCase(logLevel)) {
logger.info(e.getMessage(), e);
} else if ("warn".equalsIgnoreCase(logLevel)) {
logger.warn(e.getMessage(), e);
} else if ("error".equalsIgnoreCase(logLevel)) {
logger.error(e.getMessage(), e);
} else if ("fatal".equalsIgnoreCase(logLevel)) {
logger.fatal(e.getMessage(), e);
} else {
throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported");
}
}
private String findResultFromExceptions(List exceptionMappings, Throwable t) {
String result = null;
// Check for specific exception mappings.
if (exceptionMappings != null) {
int deepest = Integer.MAX_VALUE;
for (Iterator iter = exceptionMappings.iterator(); iter.hasNext();) {
ExceptionMappingConfig exceptionMappingConfig = (ExceptionMappingConfig) iter.next();
int depth = getDepth(exceptionMappingConfig.getExceptionClassName(), t);
if (depth >= 0 && depth < deepest) {
deepest = depth;
result = exceptionMappingConfig.getResult();
}
}
}
return result;
}
/**
* Return the depth to the superclass matching. 0 means ex matches exactly. Returns -1 if there's no match.
* Otherwise, returns depth. Lowest depth wins.
*
* @param exceptionMapping the mapping classname
* @param t the cause
* @return the depth, if not found -1 is returned.
*/
public int getDepth(String exceptionMapping, Throwable t) {
return getDepth(exceptionMapping, t.getClass(), 0);
}
private int getDepth(String exceptionMapping, Class exceptionClass, int depth) {
if (exceptionClass.getName().indexOf(exceptionMapping) != -1) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass.equals(Throwable.class)) {
return -1;
}
return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
}
/**
* Default implementation to handle ExceptionHolder publishing. Pushes given ExceptionHolder on the stack.
* Subclasses may override this to customize publishing.
*
* @param invocation The invocation to publish Exception for.
* @param exceptionHolder The exceptionHolder wrapping the Exception to publish.
*/
protected void publishException(ActionInvocation invocation, ExceptionHolder exceptionHolder) {
invocation.getStack().push(exceptionHolder);
}
}