diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.properties b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.properties index 68c701b5536..35e056826cf 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.properties +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.properties @@ -7,4 +7,6 @@ org.springframework.web.portlet.HandlerMapping=org.springframework.web.portlet.m org.springframework.web.portlet.HandlerAdapter=org.springframework.web.portlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter +org.springframework.web.portlet.HandlerExceptionResolver=org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver + org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/AbstractHandlerExceptionResolver.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/AbstractHandlerExceptionResolver.java new file mode 100644 index 00000000000..37bae30edc9 --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/AbstractHandlerExceptionResolver.java @@ -0,0 +1,228 @@ +/* + * Copyright 2002-2009 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.springframework.web.portlet.handler; + +import java.util.Set; +import javax.portlet.PortletRequest; +import javax.portlet.WindowState; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; +import javax.portlet.MimeResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.web.portlet.HandlerExceptionResolver; +import org.springframework.web.portlet.ModelAndView; +import org.springframework.core.Ordered; + +/** + * Abstract base class for {@link HandlerExceptionResolver} implementations.
Provides a set of mapped handlers that + * the resolver should map to, and the {@link Ordered} implementation. + * + * @author Arjen Poutsma + * @since 3.0 + */ +public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered { + + /** Logger available to subclasses */ + protected final Log logger = LogFactory.getLog(getClass()); + + private int order = Ordered.LOWEST_PRECEDENCE; + + private Set mappedHandlers; + + private Class[] mappedHandlerClasses; + + private Log warnLogger; + + private boolean renderWhenMinimized = false; + + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return this.order; + } + + /** + * 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. + *
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. + */ + public void setMappedHandlers(Set mappedHandlers) { + this.mappedHandlers = mappedHandlers; + } + + /** + * Specify the set of classes that this exception resolver should apply to. + * The exception mappings and the default error view will only apply + * to handlers of the specified type; the specified types may be interfaces + * and superclasses of handlers as well. + *
If no handlers and handler classes are set, 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. + */ + public void setMappedHandlerClasses(Class[] mappedHandlerClasses) { + this.mappedHandlerClasses = mappedHandlerClasses; + } + + /** + * Set the log category for warn logging. The name will be passed to the + * underlying logger implementation through Commons Logging, getting + * interpreted as log category according to the logger's configuration. + *
Default is no warn logging. Specify this setting to activate + * warn logging into a specific category. Alternatively, override + * the {@link #logException} method for custom logging. + * @see org.apache.commons.logging.LogFactory#getLog(String) + * @see org.apache.log4j.Logger#getLogger(String) + * @see java.util.logging.Logger#getLogger(String) + */ + public void setWarnLogCategory(String loggerName) { + this.warnLogger = LogFactory.getLog(loggerName); + } + + /** + * Set if the resolver should render a view when the portlet is in + * a minimized window. The default is "false". + * @see javax.portlet.RenderRequest#getWindowState() + * @see javax.portlet.WindowState#MINIMIZED + */ + public void setRenderWhenMinimized(boolean renderWhenMinimized) { + this.renderWhenMinimized = renderWhenMinimized; + } + + /** + * Checks whether this resolver is supposed to apply (i.e. the handler + * matches in case of "mappedHandlers" having been specified), then + * delegates to the {@link #doResolveException} template method. + */ + public ModelAndView resolveException( + RenderRequest request, RenderResponse response, Object handler, Exception ex) { + + if (shouldApplyTo(request, handler)) { + return doResolveException(request, response, handler, ex); + } + else { + return null; + } + } + + public ModelAndView resolveException( + ResourceRequest request, ResourceResponse response, Object handler, Exception ex) { + + if (shouldApplyTo(request, handler)) { + return doResolveException(request, response, handler, ex); + } + else { + return null; + } + } + + /** + * Check whether this resolver is supposed to apply to the given handler. + *
The default implementation checks against the specified mapped handlers
+ * and handler classes, if any, and alspo checks the window state (according
+ * to the "renderWhenMinimize" property).
+ * @param request current portlet request
+ * @param handler the executed handler, or null if none chosen at the
+ * time of the exception (for example, if multipart resolution failed)
+ * @return whether this resolved should proceed with resolving the exception
+ * for the given request and handler
+ * @see #setMappedHandlers
+ * @see #setMappedHandlerClasses
+ */
+ protected boolean shouldApplyTo(PortletRequest request, Object handler) {
+ // If the portlet is minimized and we don't want to render then return null.
+ if (WindowState.MINIMIZED.equals(request.getWindowState()) && !this.renderWhenMinimized) {
+ return false;
+ }
+ // Check mapped handlers...
+ if (handler != null) {
+ if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
+ return true;
+ }
+ if (this.mappedHandlerClasses != null) {
+ for (Class mappedClass : this.mappedHandlerClasses) {
+ if (mappedClass.isInstance(handler)) {
+ return true;
+ }
+ }
+ }
+ }
+ // Else only apply if there are no explicit handler mappings.
+ return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
+ }
+
+ /**
+ * Log the given exception at warn level, provided that warn logging has been
+ * activated through the {@link #setWarnLogCategory "warnLogCategory"} property.
+ *
Calls {@link #buildLogMessage} in order to determine the concrete message + * to log. Always passes the full exception to the logger. + * @param ex the exception that got thrown during handler execution + * @param request current portlet request (useful for obtaining metadata) + * @see #setWarnLogCategory + * @see #buildLogMessage + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + protected void logException(Exception ex, PortletRequest request) { + if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) { + this.warnLogger.warn(buildLogMessage(ex, request), ex); + } + } + + /** + * Build a log message for the given exception, occured during processing + * the given request. + * @param ex the exception that got thrown during handler execution + * @param request current portlet request (useful for obtaining metadata) + * @return the log message to use + */ + protected String buildLogMessage(Exception ex, PortletRequest request) { + return "Handler execution resulted in exception"; + } + + /** + * Actually resolve the given exception that got thrown during on handler execution, + * returning a ModelAndView that represents a specific error page if appropriate. + * + *
Must be overridden in subclasses, in order to apply specific exception checks. Note that this template method + * will be invoked after checking whether this resolved applies ("mappedHandlers" etc), so an implementation + * may simply proceed with its actual exception handling. + + * @param request current portlet request + * @param response current portlet response + * @param handler the executed handler, or null if none chosen at the time of + * the exception (for example, if multipart resolution failed) + * @param ex the exception that got thrown during handler execution + * @return a corresponding ModelAndView to forward to, or null for default processing + */ + protected abstract ModelAndView doResolveException(PortletRequest request, + MimeResponse response, + Object handler, + Exception ex); + +} diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java index 9bb2f51b678..accdd3adfcc 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/handler/SimpleMappingExceptionResolver.java @@ -18,20 +18,9 @@ package org.springframework.web.portlet.handler; import java.util.Enumeration; import java.util.Properties; -import java.util.Set; import javax.portlet.MimeResponse; import javax.portlet.PortletRequest; -import javax.portlet.RenderRequest; -import javax.portlet.RenderResponse; -import javax.portlet.ResourceRequest; -import javax.portlet.ResourceResponse; -import javax.portlet.WindowState; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.core.Ordered; -import org.springframework.web.portlet.HandlerExceptionResolver; import org.springframework.web.portlet.ModelAndView; /** @@ -45,97 +34,22 @@ import org.springframework.web.portlet.ModelAndView; * * @author Juergen Hoeller * @author John A. Lewis + * @author Arjen Poutsma * @since 2.0 */ -public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, Ordered { +public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver { /** * The default name of the exception attribute: "exception". */ public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception"; - - /** Logger available to subclasses */ - protected final Log logger = LogFactory.getLog(getClass()); - - private int order = Integer.MAX_VALUE; // default: same as non-Ordered - - private Set mappedHandlers; - - private Class[] mappedHandlerClasses; - - private boolean renderWhenMinimized = false; - - private Log warnLogger; - private Properties exceptionMappings; private String defaultErrorView; private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE; - - public void setOrder(int order) { - this.order = order; - } - - public int getOrder() { - return this.order; - } - - /** - * 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. - *
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. - */ - public void setMappedHandlers(Set mappedHandlers) { - this.mappedHandlers = mappedHandlers; - } - - /** - * Specify the set of classes that this exception resolver should apply to. - * The exception mappings and the default error view will only apply - * to handlers of the specified type; the specified types may be interfaces - * and superclasses of handlers as well. - *
If no handlers and handler classes are set, 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. - */ - public void setMappedHandlerClasses(Class[] mappedHandlerClasses) { - this.mappedHandlerClasses = mappedHandlerClasses; - } - - /** - * Set if the resolver should render a view when the portlet is in - * a minimized window. The default is "false". - * @see javax.portlet.RenderRequest#getWindowState() - * @see javax.portlet.WindowState#MINIMIZED - */ - public void setRenderWhenMinimized(boolean renderWhenMinimized) { - this.renderWhenMinimized = renderWhenMinimized; - } - - /** - * Set the log category for warn logging. The name will be passed to the - * underlying logger implementation through Commons Logging, getting - * interpreted as log category according to the logger's configuration. - *
Default is no warn logging. Specify this setting to activate - * warn logging into a specific category. Alternatively, override - * the {@link #logException} method for custom logging. - * @see org.apache.commons.logging.LogFactory#getLog(String) - * @see org.apache.log4j.Logger#getLogger(String) - * @see java.util.logging.Logger#getLogger(String) - */ - public void setWarnLogCategory(String loggerName) { - this.warnLogger = LogFactory.getLog(loggerName); - } - /** * Set the mappings between exception class names and error view names. * The exception class name can be a substring, with no wildcard support @@ -176,70 +90,6 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, this.exceptionAttribute = exceptionAttribute; } - - /** - * Checks whether this resolver is supposed to apply (i.e. the handler - * matches in case of "mappedHandlers" having been specified), then - * delegates to the {@link #doResolveException} template method. - */ - public ModelAndView resolveException( - RenderRequest request, RenderResponse response, Object handler, Exception ex) { - - if (shouldApplyTo(request, handler)) { - return doResolveException(request, response, handler, ex); - } - else { - return null; - } - } - - public ModelAndView resolveException( - ResourceRequest request, ResourceResponse response, Object handler, Exception ex) { - - if (shouldApplyTo(request, handler)) { - return doResolveException(request, response, handler, ex); - } - else { - return null; - } - } - - - /** - * Check whether this resolver is supposed to apply to the given handler. - *
The default implementation checks against the specified mapped handlers
- * and handler classes, if any, and alspo checks the window state (according
- * to the "renderWhenMinimize" property).
- * @param request current portlet request
- * @param handler the executed handler, or null if none chosen at the
- * time of the exception (for example, if multipart resolution failed)
- * @return whether this resolved should proceed with resolving the exception
- * for the given request and handler
- * @see #setMappedHandlers
- * @see #setMappedHandlerClasses
- */
- protected boolean shouldApplyTo(PortletRequest request, Object handler) {
- // If the portlet is minimized and we don't want to render then return null.
- if (WindowState.MINIMIZED.equals(request.getWindowState()) && !this.renderWhenMinimized) {
- return false;
- }
- // Check mapped handlers...
- if (handler != null) {
- if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
- return true;
- }
- if (this.mappedHandlerClasses != null) {
- for (Class mappedClass : this.mappedHandlerClasses) {
- if (mappedClass.isInstance(handler)) {
- return true;
- }
- }
- }
- }
- // Else only apply if there are no explicit handler mappings.
- return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
- }
-
/**
* Actually resolve the given exception that got thrown during on handler execution,
* returning a ModelAndView that represents a specific error page if appropriate.
@@ -250,6 +100,7 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
* @param ex the exception that got thrown during handler execution
* @return a corresponding ModelAndView to forward to, or null for default processing
*/
+ @Override
protected ModelAndView doResolveException(
PortletRequest request, MimeResponse response, Object handler, Exception ex) {
@@ -269,36 +120,6 @@ public class SimpleMappingExceptionResolver implements HandlerExceptionResolver,
}
}
-
- /**
- * Log the given exception at warn level, provided that warn logging has been
- * activated through the {@link #setWarnLogCategory "warnLogCategory"} property.
- *
Calls {@link #buildLogMessage} in order to determine the concrete message - * to log. Always passes the full exception to the logger. - * @param ex the exception that got thrown during handler execution - * @param request current portlet request (useful for obtaining metadata) - * @see #setWarnLogCategory - * @see #buildLogMessage - * @see org.apache.commons.logging.Log#warn(Object, Throwable) - */ - protected void logException(Exception ex, PortletRequest request) { - if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) { - this.warnLogger.warn(buildLogMessage(ex, request), ex); - } - } - - /** - * Build a log message for the given exception, occured during processing - * the given request. - * @param ex the exception that got thrown during handler execution - * @param request current portlet request (useful for obtaining metadata) - * @return the log message to use - */ - protected String buildLogMessage(Exception ex, PortletRequest request) { - return "Handler execution resulted in exception"; - } - - /** * Determine the view name for the given exception, searching the * {@link #setExceptionMappings "exceptionMappings"}, using the diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.java new file mode 100644 index 00000000000..bcc5bedb0df --- /dev/null +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.java @@ -0,0 +1,416 @@ +/* + * Copyright 2002-2009 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.springframework.web.portlet.mvc.annotation; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import javax.portlet.ClientDataRequest; +import javax.portlet.Event; +import javax.portlet.EventRequest; +import javax.portlet.MimeResponse; +import javax.portlet.PortalContext; +import javax.portlet.PortletMode; +import javax.portlet.PortletPreferences; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; +import javax.portlet.PortletSession; +import javax.portlet.WindowState; + +import org.springframework.core.GenericTypeResolver; +import org.springframework.core.MethodParameter; +import org.springframework.ui.Model; +import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.support.WebArgumentResolver; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.portlet.ModelAndView; +import org.springframework.web.portlet.context.PortletWebRequest; +import org.springframework.web.portlet.handler.AbstractHandlerExceptionResolver; +import org.springframework.web.servlet.View; + +/** + * Implementation of the {@link org.springframework.web.portlet.HandlerExceptionResolver} interface that handles + * exceptions through the {@link ExceptionHandler} annotation. + *
This exception resolver is enabled by default in the {@link org.springframework.web.portlet.DispatcherPortlet}.
+ *
+ * @author Arjen Poutsma
+ * @since 3.0
+ */
+public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
+
+ private WebArgumentResolver[] customArgumentResolvers;
+
+ /**
+ * Set a custom ArgumentResolvers to use for special method parameter types. Such a custom ArgumentResolver will kick
+ * in first, having a chance to resolve an argument value before the standard argument handling kicks in.
+ */
+ public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
+ this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver};
+ }
+
+ /**
+ * Set one or more custom ArgumentResolvers to use for special method parameter types. Any such custom ArgumentResolver
+ * will kick in first, having a chance to resolve an argument value before the standard argument handling kicks in.
+ */
+ public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
+ this.customArgumentResolvers = argumentResolvers;
+ }
+
+ @Override
+ protected ModelAndView doResolveException(PortletRequest request,
+ MimeResponse response,
+ Object handler,
+ Exception ex) {
+ if (handler != null) {
+ Method handlerMethod = findBestExceptionHandlerMethod(handler, ex);
+ if (handlerMethod != null) {
+ NativeWebRequest webRequest = new PortletWebRequest(request, response);
+ try {
+ Object[] args = resolveHandlerArguments(handlerMethod, handler, webRequest, ex);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Invoking request handler method: " + handlerMethod);
+ }
+ Object retVal = doInvokeMethod(handlerMethod, handler, args);
+ return getModelAndView(retVal);
+ }
+ catch (Exception invocationEx) {
+ logger.error("Invoking request method resulted in exception : " + handlerMethod, invocationEx);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds the handler method that matches the thrown exception best.
+ *
+ * @param handler the handler object
+ * @param thrownException the exception to be handled
+ * @return the best matching method; or Default implementation looks for exceptions in the {@linkplain ExceptionHandler#value() annotation}, or -
+ * if that annotation element is empty - any exceptions listed in the method parameters if the method is annotated
+ * with {@code @ExceptionHandler}.
+ *
+ * @param method the method
+ * @return the handled exceptions
+ */
+ @SuppressWarnings("unchecked")
+ protected Listnull if none is found
+ */
+ private Method findBestExceptionHandlerMethod(Object handler, final Exception thrownException) {
+ final Class> handlerType = handler.getClass();
+ final Class extends Throwable> thrownExceptionType = thrownException.getClass();
+
+ final Map