5 changed files with 754 additions and 182 deletions
@ -0,0 +1,228 @@
@@ -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. <p>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. |
||||
* <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. |
||||
*/ |
||||
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. |
||||
* <p>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. |
||||
* <p>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. |
||||
* <p>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 <code>null</code> 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. |
||||
* <p>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. |
||||
* |
||||
* <p>Must be overridden in subclasses, in order to apply specific exception checks. Note that this template method |
||||
* will be invoked <i>after</i> 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); |
||||
|
||||
} |
||||
@ -0,0 +1,416 @@
@@ -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. |
||||
* <p>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 <code>null</code> 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<Class<? extends Throwable>, Method> resolverMethods = |
||||
new LinkedHashMap<Class<? extends Throwable>, Method>(); |
||||
|
||||
ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() { |
||||
public void doWith(Method method) { |
||||
method = ClassUtils.getMostSpecificMethod(method, handlerType); |
||||
List<Class<? extends Throwable>> handledExceptions = getHandledExceptions(method); |
||||
for (Class<? extends Throwable> handledException : handledExceptions) { |
||||
if (handledException.isAssignableFrom(thrownExceptionType)) { |
||||
if (!resolverMethods.containsKey(handledException)) { |
||||
resolverMethods.put(handledException, method); |
||||
} |
||||
else { |
||||
Method oldMappedMethod = resolverMethods.get(handledException); |
||||
throw new IllegalStateException( |
||||
"Ambiguous exception handler mapped for " + handledException + "]: {" + |
||||
oldMappedMethod + ", " + method + "}."); |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
return getBestMatchingMethod(thrownException, resolverMethods); |
||||
} |
||||
|
||||
/** |
||||
* Returns all the exception classes handled by the given method. |
||||
* |
||||
* <p>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 List<Class<? extends Throwable>> getHandledExceptions(Method method) { |
||||
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>(); |
||||
ExceptionHandler exceptionHandler = method.getAnnotation(ExceptionHandler.class); |
||||
if (exceptionHandler != null) { |
||||
if (!ObjectUtils.isEmpty(exceptionHandler.value())) { |
||||
result.addAll(Arrays.asList(exceptionHandler.value())); |
||||
} |
||||
else { |
||||
for (Class<?> param : method.getParameterTypes()) { |
||||
if (Throwable.class.isAssignableFrom(param)) { |
||||
result.add((Class<? extends Throwable>) param); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the best matching method. Uses the {@link DepthComparator}. |
||||
*/ |
||||
private Method getBestMatchingMethod(Exception thrownException, |
||||
Map<Class<? extends Throwable>, Method> resolverMethods) { |
||||
if (!resolverMethods.isEmpty()) { |
||||
List<Class<? extends Throwable>> handledExceptions = |
||||
new ArrayList<Class<? extends Throwable>>(resolverMethods.keySet()); |
||||
Collections.sort(handledExceptions, new DepthComparator(thrownException)); |
||||
Class<? extends Throwable> bestMatchMethod = handledExceptions.get(0); |
||||
return resolverMethods.get(bestMatchMethod); |
||||
} |
||||
else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Resolves the arguments for the given method. Delegates to {@link #resolveCommonArgument}. |
||||
*/ |
||||
private Object[] resolveHandlerArguments(Method handlerMethod, |
||||
Object handler, |
||||
NativeWebRequest webRequest, |
||||
Exception thrownException) throws Exception { |
||||
|
||||
Class[] paramTypes = handlerMethod.getParameterTypes(); |
||||
Object[] args = new Object[paramTypes.length]; |
||||
|
||||
Class<?> handlerType = handler.getClass(); |
||||
|
||||
for (int i = 0; i < args.length; i++) { |
||||
MethodParameter methodParam = new MethodParameter(handlerMethod, i); |
||||
GenericTypeResolver.resolveParameterType(methodParam, handlerType); |
||||
|
||||
Class paramType = methodParam.getParameterType(); |
||||
|
||||
Object argValue = resolveCommonArgument(methodParam, webRequest, thrownException); |
||||
if (argValue != WebArgumentResolver.UNRESOLVED) { |
||||
args[i] = argValue; |
||||
} |
||||
else { |
||||
throw new IllegalStateException( |
||||
"Unsupported argument [" + paramType.getName() + "] for @ExceptionHandler method: " + |
||||
handlerMethod); |
||||
} |
||||
} |
||||
return args; |
||||
} |
||||
|
||||
/** |
||||
* Resolves common method arguments. Delegates to registered {@link #setCustomArgumentResolver(org.springframework.web.bind.support.WebArgumentResolver) argumentResolvers} first, |
||||
* then checking {@link #resolveStandardArgument}. |
||||
* |
||||
* @param methodParameter the method parameter |
||||
* @param webRequest the request |
||||
* @param thrownException the exception thrown |
||||
* @return the argument value, or {@link org.springframework.web.bind.support.WebArgumentResolver#UNRESOLVED} |
||||
*/ |
||||
protected Object resolveCommonArgument(MethodParameter methodParameter, |
||||
NativeWebRequest webRequest, |
||||
Exception thrownException) throws Exception { |
||||
// Invoke custom argument resolvers if present...
|
||||
if (this.customArgumentResolvers != null) { |
||||
for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) { |
||||
Object value = argumentResolver.resolveArgument(methodParameter, webRequest); |
||||
if (value != WebArgumentResolver.UNRESOLVED) { |
||||
return value; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Resolution of standard parameter types...
|
||||
Class paramType = methodParameter.getParameterType(); |
||||
Object value = resolveStandardArgument(paramType, webRequest, thrownException); |
||||
if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) { |
||||
throw new IllegalStateException( |
||||
"Standard argument type [" + paramType.getName() + "] resolved to incompatible value of type [" + |
||||
(value != null ? value.getClass() : null) + |
||||
"]. Consider declaring the argument type in a less specific fashion."); |
||||
} |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* Resolves standard method arguments. Default implementation handles {@link org.springframework.web.context.request.NativeWebRequest}, |
||||
* {@link javax.servlet.ServletRequest}, {@link javax.servlet.ServletResponse}, {@link javax.servlet.http.HttpSession}, {@link java.security.Principal}, {@link java.util.Locale}, |
||||
* request {@link java.io.InputStream}, request {@link java.io.Reader}, response {@link java.io.OutputStream}, response {@link java.io.Writer}, |
||||
* and the given {@code thrownException}. |
||||
* |
||||
* @param parameterType the method parameter type |
||||
* @param webRequest the request |
||||
* @param thrownException the exception thrown |
||||
* @return the argument value, or {@link org.springframework.web.bind.support.WebArgumentResolver#UNRESOLVED} |
||||
*/ |
||||
protected Object resolveStandardArgument(Class parameterType, |
||||
NativeWebRequest webRequest, |
||||
Exception thrownException) throws Exception { |
||||
if (WebRequest.class.isAssignableFrom(parameterType)) { |
||||
return webRequest; |
||||
} |
||||
|
||||
PortletRequest request = (PortletRequest) webRequest.getNativeRequest(); |
||||
PortletResponse response = (PortletResponse) webRequest.getNativeResponse(); |
||||
|
||||
if (PortletRequest.class.isAssignableFrom(parameterType)) { |
||||
return request; |
||||
} |
||||
else if (PortletResponse.class.isAssignableFrom(parameterType)) { |
||||
return response; |
||||
} |
||||
else if (PortletSession.class.isAssignableFrom(parameterType)) { |
||||
return request.getPortletSession(); |
||||
} |
||||
else if (PortletPreferences.class.isAssignableFrom(parameterType)) { |
||||
return request.getPreferences(); |
||||
} |
||||
else if (PortletMode.class.isAssignableFrom(parameterType)) { |
||||
return request.getPortletMode(); |
||||
} |
||||
else if (WindowState.class.isAssignableFrom(parameterType)) { |
||||
return request.getWindowState(); |
||||
} |
||||
else if (PortalContext.class.isAssignableFrom(parameterType)) { |
||||
return request.getPortalContext(); |
||||
} |
||||
else if (Principal.class.isAssignableFrom(parameterType)) { |
||||
return request.getUserPrincipal(); |
||||
} |
||||
else if (Locale.class.equals(parameterType)) { |
||||
return request.getLocale(); |
||||
} |
||||
else if (InputStream.class.isAssignableFrom(parameterType)) { |
||||
if (!(request instanceof ClientDataRequest)) { |
||||
throw new IllegalStateException("InputStream can only get obtained for Action/ResourceRequest"); |
||||
} |
||||
return ((ClientDataRequest) request).getPortletInputStream(); |
||||
} |
||||
else if (Reader.class.isAssignableFrom(parameterType)) { |
||||
if (!(request instanceof ClientDataRequest)) { |
||||
throw new IllegalStateException("Reader can only get obtained for Action/ResourceRequest"); |
||||
} |
||||
return ((ClientDataRequest) request).getReader(); |
||||
} |
||||
else if (OutputStream.class.isAssignableFrom(parameterType)) { |
||||
if (!(response instanceof MimeResponse)) { |
||||
throw new IllegalStateException("OutputStream can only get obtained for Render/ResourceResponse"); |
||||
} |
||||
return ((MimeResponse) response).getPortletOutputStream(); |
||||
} |
||||
else if (Writer.class.isAssignableFrom(parameterType)) { |
||||
if (!(response instanceof MimeResponse)) { |
||||
throw new IllegalStateException("Writer can only get obtained for Render/ResourceResponse"); |
||||
} |
||||
return ((MimeResponse) response).getWriter(); |
||||
} |
||||
else if (Event.class.equals(parameterType)) { |
||||
if (!(request instanceof EventRequest)) { |
||||
throw new IllegalStateException("Event can only get obtained from EventRequest"); |
||||
} |
||||
return ((EventRequest) request).getEvent(); |
||||
} |
||||
else if (parameterType.isInstance(thrownException)) { |
||||
return thrownException; |
||||
} |
||||
else { |
||||
return WebArgumentResolver.UNRESOLVED; |
||||
} |
||||
} |
||||
|
||||
private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception { |
||||
ReflectionUtils.makeAccessible(method); |
||||
try { |
||||
return method.invoke(target, args); |
||||
} |
||||
catch (InvocationTargetException ex) { |
||||
ReflectionUtils.rethrowException(ex.getTargetException()); |
||||
} |
||||
throw new IllegalStateException("Should never get here"); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private ModelAndView getModelAndView(Object returnValue) { |
||||
if (returnValue instanceof ModelAndView) { |
||||
return (ModelAndView) returnValue; |
||||
} |
||||
else if (returnValue instanceof Model) { |
||||
return new ModelAndView().addAllObjects(((Model) returnValue).asMap()); |
||||
} |
||||
else if (returnValue instanceof Map) { |
||||
return new ModelAndView().addAllObjects((Map) returnValue); |
||||
} |
||||
else if (returnValue instanceof View) { |
||||
return new ModelAndView(returnValue); |
||||
} |
||||
else if (returnValue instanceof String) { |
||||
return new ModelAndView((String) returnValue); |
||||
} |
||||
else if (returnValue == null) { |
||||
return null; |
||||
} |
||||
else { |
||||
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Comparator capable of sorting exceptions based on their depth from the thrown exception type. |
||||
*/ |
||||
private static class DepthComparator implements Comparator<Class<? extends Throwable>> { |
||||
|
||||
private final Class<? extends Throwable> handlerExceptionType; |
||||
|
||||
private DepthComparator(Exception handlerException) { |
||||
this.handlerExceptionType = handlerException.getClass(); |
||||
} |
||||
|
||||
public int compare(Class<? extends Throwable> o1, Class<? extends Throwable> o2) { |
||||
int depth1 = getDepth(o1, 0); |
||||
int depth2 = getDepth(o2, 0); |
||||
|
||||
return depth2 - depth1; |
||||
} |
||||
|
||||
private int getDepth(Class exceptionType, int depth) { |
||||
if (exceptionType.equals(handlerExceptionType)) { |
||||
// Found it!
|
||||
return depth; |
||||
} |
||||
// If we've gone as far as we can go and haven't found it...
|
||||
if (Throwable.class.equals(exceptionType)) { |
||||
return -1; |
||||
} |
||||
return getDepth(exceptionType.getSuperclass(), depth + 1); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
/* |
||||
* 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.IOException; |
||||
import java.net.BindException; |
||||
|
||||
import javax.portlet.PortletRequest; |
||||
import javax.portlet.PortletResponse; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.mock.web.portlet.MockPortletRequest; |
||||
import org.springframework.mock.web.portlet.MockPortletResponse; |
||||
import org.springframework.mock.web.portlet.MockRenderRequest; |
||||
import org.springframework.mock.web.portlet.MockRenderResponse; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
import org.springframework.web.portlet.ModelAndView; |
||||
|
||||
/** @author Arjen Poutsma */ |
||||
public class AnnotationMethodHandlerExceptionResolverTests { |
||||
|
||||
private AnnotationMethodHandlerExceptionResolver exceptionResolver; |
||||
|
||||
private MockRenderRequest request; |
||||
|
||||
private MockRenderResponse response; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
exceptionResolver = new AnnotationMethodHandlerExceptionResolver(); |
||||
request = new MockRenderRequest(); |
||||
response = new MockRenderResponse(); |
||||
} |
||||
|
||||
@Test |
||||
public void simple() { |
||||
BindException ex = new BindException(); |
||||
SimpleController controller = new SimpleController(); |
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, controller, ex); |
||||
assertNotNull("No ModelAndView returned", mav); |
||||
assertEquals("Invalid view name returned", "BindException", mav.getViewName()); |
||||
} |
||||
|
||||
@Test(expected = IllegalStateException.class) |
||||
public void ambiguous() { |
||||
IllegalArgumentException ex = new IllegalArgumentException(); |
||||
AmbiguousController controller = new AmbiguousController(); |
||||
exceptionResolver.resolveException(request, response, controller, ex); |
||||
} |
||||
|
||||
@Controller |
||||
private static class SimpleController { |
||||
|
||||
@ExceptionHandler(IOException.class) |
||||
public String handleIOException(IOException ex, PortletRequest request) { |
||||
return ClassUtils.getShortName(ex.getClass()); |
||||
} |
||||
|
||||
@ExceptionHandler(BindException.class) |
||||
public String handleBindException(Exception ex, PortletResponse response) { |
||||
return ClassUtils.getShortName(ex.getClass()); |
||||
} |
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class) |
||||
public String handleIllegalArgumentException(Exception ex) { |
||||
return ClassUtils.getShortName(ex.getClass()); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Controller |
||||
private static class AmbiguousController { |
||||
|
||||
@ExceptionHandler({BindException.class, IllegalArgumentException.class}) |
||||
public String handle1(Exception ex, PortletRequest request, PortletResponse response) |
||||
throws IOException { |
||||
return ClassUtils.getShortName(ex.getClass()); |
||||
} |
||||
|
||||
@ExceptionHandler |
||||
public String handle2(IllegalArgumentException ex) { |
||||
return ClassUtils.getShortName(ex.getClass()); |
||||
} |
||||
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue