/*
* Copyright 2005-2008 the original author or authors.
*
* 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 org.openuap.cms.core.hander;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import java.lang.reflect.InvocationTargetException;
/**
* <p>
* CMS URL匹配异常解析器
* </p>
*
* <p>
* $Id: CmsMappingExceptionResolver.java 3764 2010-04-19 11:18:56Z
* orangeforjava$
* </p>
*
* @author Joseph
* @version 1.0
*/
public class CmsMappingExceptionResolver implements HandlerExceptionResolver,
Ordered {
/**
* The default name of the exception attribute: "exception".
*/
public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
protected final Log logger = LogFactory.getLog(getClass());
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
private Properties exceptionMappings;
private String defaultErrorView;
private Set mappedHandlers;
private Integer defaultStatusCode;
private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return order;
}
/**
* Set the mappings between exception class names and error view names. The
* exception class name can be a substring, with no wildcard support at
* present. A value of "ServletException" would match
* <code>javax.servlet.ServletException</code> and subclasses, for example.
* <p>
* <b>NB:</b> Consider carefully how specific the pattern is, and whether to
* include package information (which isn't mandatory). For example,
* "Exception" will match nearly anything, and will probably hide other
* rules. "java.lang.Exception" would be correct if "Exception" was meant to
* define a rule for all checked exceptions. With more unusual exception
* names such as "BaseBusinessException" there's no need to use a FQN.
* <p>
* Follows the same matching algorithm as RuleBasedTransactionAttribute and
* RollbackRuleAttribute.
*
* @param mappings
* exception patterns (can also be fully qualified class names)
* as keys, and error view names as values
* @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute
*/
public void setExceptionMappings(Properties mappings) {
this.exceptionMappings = mappings;
}
/**
* Set the name of the default error view. This view will be returned if no
* specific mapping was found.
*
* <p>
* Default is none.
*
* @param defaultErrorView
* String
*/
public void setDefaultErrorView(String defaultErrorView) {
this.defaultErrorView = defaultErrorView;
if (logger.isInfoEnabled()) {
logger
.info("Default error view is '" + this.defaultErrorView
+ "'");
}
}
/**
* Specify the set of handlers that this exception resolver should map. The
* exception mappings and the default error view will only apply to the
* specified handlers.
*
* <p>
* If no handlers set, both the exception mappings and the default error
* view will apply to all handlers. This means that a specified default
* error view will be used as fallback for all exceptions; any further
* HandlerExceptionResolvers in the chain will be ignored in this case.
*
* @param mappedHandlers
* Set
*/
public void setMappedHandlers(Set mappedHandlers) {
this.mappedHandlers = mappedHandlers;
}
/**
* Set the default HTTP status code that this exception resolver will apply
* if it resolves an error view.
* <p>
* If not specified, no status code will be applied, either leaving this to
* the controller or view, or keeping the servlet engine's default of 200
* (OK).
*
* @param defaultStatusCode
* HTTP status code value, for example 500
* (SC_INTERNAL_SERVER_ERROR) or 404 (SC_NOT_FOUND)
* @see javax.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR
* @see javax.servlet.http.HttpServletResponse#SC_NOT_FOUND
*/
public void setDefaultStatusCode(int defaultStatusCode) {
this.defaultStatusCode = new Integer(defaultStatusCode);
}
/**
* Set the name of the model attribute as which the exception should be
* exposed. Default is "exception".
*
* <p>
* This can be either set to a different attribute name or to
* <code>null</code> for not exposing an exception attribute at all.
*
* @see #DEFAULT_EXCEPTION_ATTRIBUTE
* @param exceptionAttribute
* String
*/
public void setExceptionAttribute(String exceptionAttribute) {
this.exceptionAttribute = exceptionAttribute;
}
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
// Check whether we're supposed to apply to the given handler.
if (this.mappedHandlers != null
&& !this.mappedHandlers.contains(handler)) {
return null;
}
Exception shouldEx = ex;
if (ex instanceof InvocationTargetException) {
Throwable trowable = ((InvocationTargetException) ex)
.getTargetException();
if (trowable instanceof Exception) {
shouldEx = (Exception) trowable;
}
}
String viewName = null;
// Check for specific exception mappings.
if (this.exceptionMappings != null) {
int deepest = Integer.MAX_VALUE;
for (Enumeration names = this.exceptionMappings.propertyNames(); names
.hasMoreElements();) {
String exceptionMapping = (String) names.nextElement();
int depth = getDepth(exceptionMapping, shouldEx);
if (depth >= 0 && depth < deepest) {
deepest = depth;
viewName = this.exceptionMappings
.getProperty(exceptionMapping);
}
}
}
// Return default error view else, if defined.
if (viewName == null && this.defaultErrorView != null) {
viewName = this.defaultErrorView;
}
if (viewName != null) {
// Apply HTTP status code for error views, if specified.
if (this.defaultStatusCode != null) {
response.setStatus(this.defaultStatusCode.intValue());
}
return getModelAndView(viewName, ex);
} else {
return null;
}
}
/**
* 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.
*
* <p>
* Follows the same algorithm as RollbackRuleAttribute.
*
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute
* @param exceptionMapping
* String
* @param ex
* Exception
* @return int
*/
public int getDepth(String exceptionMapping, Exception ex) {
return getDepth(exceptionMapping, ex.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);
}
/**
* Return a ModelAndView for the given view name and exception. Default
* implementation adds the specified exception attribute. Can be overridden
* in subclasses.
*
* @param viewName
* the name of the error view
* @param ex
* the exception that got thrown during handler execution
* @return the ModelAndView instance
* @see #setExceptionAttribute
*/
protected ModelAndView getModelAndView(String viewName, Exception ex) {
ModelAndView mv = new ModelAndView(viewName);
if (this.exceptionAttribute != null) {
mv.addObject(this.exceptionAttribute, ex);
}
return mv;
}
}