/* * 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; } }