KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > handler > SimpleMappingExceptionResolver


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.web.servlet.handler;
18
19 import java.util.Enumeration JavaDoc;
20 import java.util.Properties JavaDoc;
21 import java.util.Set JavaDoc;
22
23 import javax.servlet.http.HttpServletRequest JavaDoc;
24 import javax.servlet.http.HttpServletResponse JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import org.springframework.core.Ordered;
30 import org.springframework.web.servlet.HandlerExceptionResolver;
31 import org.springframework.web.servlet.ModelAndView;
32 import org.springframework.web.util.WebUtils;
33
34 /**
35  * {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation
36  * that allows for mapping exception class names to view names, either for a list
37  * of given handlers or for all handlers in the DispatcherServlet.
38  *
39  * <p>Error views are analogous to error page JSPs, but can be used with any
40  * kind of exception including any checked one, with fine-granular mappings for
41  * specific handlers.
42  *
43  * @author Juergen Hoeller
44  * @since 22.11.2003
45  * @see org.springframework.web.servlet.DispatcherServlet
46  */

47 public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, Ordered {
48
49     /**
50      * The default name of the exception attribute: "exception".
51      */

52     public static final String JavaDoc DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
53
54
55     /** Logger available to subclasses */
56     protected final Log logger = LogFactory.getLog(getClass());
57
58     private int order = Integer.MAX_VALUE; // default: same as non-Ordered
59

60     private Set JavaDoc mappedHandlers;
61
62     private Log warnLogger;
63
64     private Properties JavaDoc exceptionMappings;
65
66     private String JavaDoc defaultErrorView;
67
68     private Integer JavaDoc defaultStatusCode;
69
70     private String JavaDoc exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
71
72
73     public void setOrder(int order) {
74       this.order = order;
75     }
76
77     public int getOrder() {
78       return this.order;
79     }
80
81     /**
82      * Specify the set of handlers that this exception resolver should map.
83      * The exception mappings and the default error view will only apply
84      * to the specified handlers.
85      * <p>If no handlers set, both the exception mappings and the default error
86      * view will apply to all handlers. This means that a specified default
87      * error view will be used as fallback for all exceptions; any further
88      * HandlerExceptionResolvers in the chain will be ignored in this case.
89      */

90     public void setMappedHandlers(Set JavaDoc mappedHandlers) {
91         this.mappedHandlers = mappedHandlers;
92     }
93
94     /**
95      * Set the log category for warn logging. The name will be passed to the
96      * underlying logger implementation through Commons Logging, getting
97      * interpreted as log category according to the logger's configuration.
98      * <p>Default is no warn logging. Specify this setting to activate
99      * warn logging into a specific category. Alternatively, override
100      * the {@link #logException} method for custom logging.
101      * @see org.apache.commons.logging.LogFactory#getLog(String)
102      * @see org.apache.log4j.Logger#getLogger(String)
103      * @see java.util.logging.Logger#getLogger(String)
104      */

105     public void setWarnLogCategory(String JavaDoc loggerName) {
106         this.warnLogger = LogFactory.getLog(loggerName);
107     }
108
109     /**
110      * Set the mappings between exception class names and error view names.
111      * The exception class name can be a substring, with no wildcard support
112      * at present. A value of "ServletException" would match
113      * <code>javax.servlet.ServletException</code> and subclasses, for example.
114      * <p><b>NB:</b> Consider carefully how specific the pattern is, and whether
115      * to include package information (which isn't mandatory). For example,
116      * "Exception" will match nearly anything, and will probably hide other rules.
117      * "java.lang.Exception" would be correct if "Exception" was meant to define
118      * a rule for all checked exceptions. With more unusual exception names such
119      * as "BaseBusinessException" there's no need to use a FQN.
120      * <p>Follows the same matching algorithm as RuleBasedTransactionAttribute
121      * and RollbackRuleAttribute.
122      * @param mappings exception patterns (can also be fully qualified class names)
123      * as keys, and error view names as values
124      * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
125      * @see org.springframework.transaction.interceptor.RollbackRuleAttribute
126      */

127     public void setExceptionMappings(Properties JavaDoc mappings) {
128         this.exceptionMappings = mappings;
129     }
130
131     /**
132      * Set the name of the default error view.
133      * This view will be returned if no specific mapping was found.
134      * <p>Default is none.
135      */

136     public void setDefaultErrorView(String JavaDoc defaultErrorView) {
137         this.defaultErrorView = defaultErrorView;
138     }
139
140     /**
141      * Set the default HTTP status code that this exception resolver will apply
142      * if it resolves an error view.
143      * <p>Note that this error code will only get applied in case of a top-level
144      * request. It will not be set for an include request, since the HTTP status
145      * cannot be modified from within an include.
146      * <p>If not specified, no status code will be applied, either leaving this to
147      * the controller or view, or keeping the servlet engine's default of 200 (OK).
148      * @param defaultStatusCode HTTP status code value, for example
149      * 500 (SC_INTERNAL_SERVER_ERROR) or 404 (SC_NOT_FOUND)
150      * @see javax.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR
151      * @see javax.servlet.http.HttpServletResponse#SC_NOT_FOUND
152      */

153     public void setDefaultStatusCode(int defaultStatusCode) {
154         this.defaultStatusCode = new Integer JavaDoc(defaultStatusCode);
155     }
156
157     /**
158      * Set the name of the model attribute as which the exception should
159      * be exposed. Default is "exception".
160      * <p>This can be either set to a different attribute name or to
161      * <code>null</code> for not exposing an exception attribute at all.
162      * @see #DEFAULT_EXCEPTION_ATTRIBUTE
163      */

164     public void setExceptionAttribute(String JavaDoc exceptionAttribute) {
165         this.exceptionAttribute = exceptionAttribute;
166     }
167
168
169     public ModelAndView resolveException(
170         HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Object JavaDoc handler, Exception JavaDoc ex) {
171
172         // Check whether we're supposed to apply to the given handler.
173
if (this.mappedHandlers != null && !this.mappedHandlers.contains(handler)) {
174             return null;
175         }
176
177         // Log exception, both at debug log level and at warn level, if desired.
178
if (logger.isDebugEnabled()) {
179             logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
180         }
181         logException(ex, request);
182
183         // Expose ModelAndView for chosen error view.
184
String JavaDoc viewName = determineViewName(ex, request);
185         if (viewName != null) {
186             // Apply HTTP status code for error views, if specified.
187
// Only apply it if we're processing a top-level request.
188
Integer JavaDoc statusCode = determineStatusCode(request, viewName);
189             if (statusCode != null) {
190                 applyStatusCodeIfPossible(request, response, statusCode.intValue());
191             }
192             return getModelAndView(viewName, ex, request);
193         }
194         else {
195             return null;
196         }
197     }
198
199
200     /**
201      * Log the given exception at warn level, provided that warn logging has been
202      * activated through the {@link #setWarnLogCategory "warnLogCategory"} property.
203      * <p>Calls {@link #buildLogMessage} in order to determine the concrete message
204      * to log. Always passes the full exception to the logger.
205      * @param ex the exception that got thrown during handler execution
206      * @param request current HTTP request (useful for obtaining metadata)
207      * @see #setWarnLogCategory
208      * @see #buildLogMessage
209      * @see org.apache.commons.logging.Log#warn(Object, Throwable)
210      */

211     protected void logException(Exception JavaDoc ex, HttpServletRequest JavaDoc request) {
212         if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
213             this.warnLogger.warn(buildLogMessage(ex, request), ex);
214         }
215     }
216
217     /**
218      * Build a log message for the given exception, occured during processing
219      * the given request.
220      * @param ex the exception that got thrown during handler execution
221      * @param request current HTTP request (useful for obtaining metadata)
222      * @return the log message to use
223      */

224     protected String JavaDoc buildLogMessage(Exception JavaDoc ex, HttpServletRequest JavaDoc request) {
225         return "Handler execution resulted in exception";
226     }
227
228
229     /**
230      * Determine the view name for the given exception, searching the
231      * {@link #setExceptionMappings "exceptionMappings"}, using the
232      * {@link #setDefaultErrorView "defaultErrorView"} as fallback.
233      * @param ex the exception that got thrown during handler execution
234      * @param request current HTTP request (useful for obtaining metadata)
235      * @return the resolved view name, or <code>null</code> if none found
236      */

237     protected String JavaDoc determineViewName(Exception JavaDoc ex, HttpServletRequest JavaDoc request) {
238         String JavaDoc viewName = null;
239         // Check for specific exception mappings.
240
if (this.exceptionMappings != null) {
241             viewName = findMatchingViewName(this.exceptionMappings, ex);
242         }
243         // Return default error view else, if defined.
244
if (viewName == null && this.defaultErrorView != null) {
245             if (logger.isDebugEnabled()) {
246                 logger.debug("Resolving to default view '" + this.defaultErrorView +
247                         "' for exception of type [" + ex.getClass().getName() + "]");
248             }
249             viewName = this.defaultErrorView;
250         }
251         return viewName;
252     }
253
254     /**
255      * Find a matching view name in the given exception mappings.
256      * @param exceptionMappings mappings between exception class names and error view names
257      * @param ex the exception that got thrown during handler execution
258      * @return the view name, or <code>null</code> if none found
259      * @see #setExceptionMappings
260      */

261     protected String JavaDoc findMatchingViewName(Properties JavaDoc exceptionMappings, Exception JavaDoc ex) {
262         String JavaDoc viewName = null;
263         String JavaDoc dominantMapping = null;
264         int deepest = Integer.MAX_VALUE;
265         for (Enumeration JavaDoc names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
266             String JavaDoc exceptionMapping = (String JavaDoc) names.nextElement();
267             int depth = getDepth(exceptionMapping, ex);
268             if (depth >= 0 && depth < deepest) {
269                 deepest = depth;
270                 dominantMapping = exceptionMapping;
271                 viewName = exceptionMappings.getProperty(exceptionMapping);
272             }
273         }
274         if (viewName != null && logger.isDebugEnabled()) {
275             logger.debug("Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() +
276                     "], based on exception mapping [" + dominantMapping + "]");
277         }
278         return viewName;
279     }
280
281     /**
282      * Return the depth to the superclass matching.
283      * <p>0 means ex matches exactly. Returns -1 if there's no match.
284      * Otherwise, returns depth. Lowest depth wins.
285      * <p>Follows the same algorithm as
286      * {@link org.springframework.transaction.interceptor.RollbackRuleAttribute}.
287      */

288     protected int getDepth(String JavaDoc exceptionMapping, Exception JavaDoc ex) {
289         return getDepth(exceptionMapping, ex.getClass(), 0);
290     }
291
292     private int getDepth(String JavaDoc exceptionMapping, Class JavaDoc exceptionClass, int depth) {
293         if (exceptionClass.getName().indexOf(exceptionMapping) != -1) {
294             // Found it!
295
return depth;
296         }
297         // If we've gone as far as we can go and haven't found it...
298
if (exceptionClass.equals(Throwable JavaDoc.class)) {
299             return -1;
300         }
301         return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
302     }
303
304
305     /**
306      * Determine the HTTP status code to apply for the given error view.
307      * <p>The default implementation always returns the specified
308      * {@link #setDefaultStatusCode "defaultStatusCode"}, as a common
309      * status code for all error views. Override this in a custom subclass
310      * to determine a specific status code for the given view.
311      * @param request current HTTP request
312      * @param viewName the name of the error view
313      * @return the HTTP status code to use, or <code>null</code> for the
314      * servlet container's default (200 in case of a standard error view)
315      * @see #setDefaultStatusCode
316      * @see #applyStatusCodeIfPossible
317      */

318     protected Integer JavaDoc determineStatusCode(HttpServletRequest JavaDoc request, String JavaDoc viewName) {
319         return this.defaultStatusCode;
320     }
321
322     /**
323      * Apply the specified HTTP status code to the given response, if possible
324      * (that is, if not executing within an include request).
325      * @param request current HTTP request
326      * @param response current HTTP response
327      * @param statusCode the status code to apply
328      * @see #determineStatusCode
329      * @see #setDefaultStatusCode
330      * @see javax.servlet.http.HttpServletResponse#setStatus
331      */

332     protected void applyStatusCodeIfPossible(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, int statusCode) {
333         if (!WebUtils.isIncludeRequest(request)) {
334             if (logger.isDebugEnabled()) {
335                 logger.debug("Applying HTTP status code " + statusCode);
336             }
337             response.setStatus(statusCode);
338         }
339     }
340
341     /**
342      * Return a ModelAndView for the given request, view name and exception.
343      * <p>The default implementation delegates to {@link #getModelAndView(String, Exception)}.
344      * @param viewName the name of the error view
345      * @param ex the exception that got thrown during handler execution
346      * @param request current HTTP request (useful for obtaining metadata)
347      * @return the ModelAndView instance
348      */

349     protected ModelAndView getModelAndView(String JavaDoc viewName, Exception JavaDoc ex, HttpServletRequest JavaDoc request) {
350         return getModelAndView(viewName, ex);
351     }
352
353     /**
354      * Return a ModelAndView for the given view name and exception.
355      * <p>The default implementation adds the specified exception attribute.
356      * Can be overridden in subclasses.
357      * @param viewName the name of the error view
358      * @param ex the exception that got thrown during handler execution
359      * @return the ModelAndView instance
360      * @see #setExceptionAttribute
361      */

362     protected ModelAndView getModelAndView(String JavaDoc viewName, Exception JavaDoc ex) {
363         ModelAndView mv = new ModelAndView(viewName);
364         if (this.exceptionAttribute != null) {
365             if (logger.isDebugEnabled()) {
366                 logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'");
367             }
368             mv.addObject(this.exceptionAttribute, ex);
369         }
370         return mv;
371     }
372
373 }
374
Popular Tags