SPR-8215 Move HandlerMethod code into trunk
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.handler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
|
||||
* implementations that support {@link HandlerMethod HandlerMethod}s.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
/**
|
||||
* Checks if the handler is a {@link HandlerMethod} instance and performs the check against the bean
|
||||
* instance it contains. If the provided handler is not an instance of {@link HandlerMethod},
|
||||
* {@code false} is returned instead.
|
||||
*/
|
||||
@Override
|
||||
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
|
||||
if (handler == null) {
|
||||
return super.shouldApplyTo(request, handler);
|
||||
}
|
||||
else if (handler instanceof HandlerMethod) {
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
handler = handlerMethod.getBean();
|
||||
return super.shouldApplyTo(request, handler);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ModelAndView doResolveException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex) {
|
||||
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually resolve the given exception that got thrown during on handler execution,
|
||||
* returning a ModelAndView that represents a specific error page if appropriate.
|
||||
* <p>May 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 HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handlerMethod the executed handler method, or <code>null</code> 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 <code>null</code> for default processing
|
||||
*/
|
||||
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod,
|
||||
Exception ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link org.springframework.web.servlet.HandlerMapping HandlerMapping} implementations that
|
||||
* support {@link HandlerMethod}s.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping {
|
||||
|
||||
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
|
||||
|
||||
/**
|
||||
* Calls the initialization of the superclass and detects handlers.
|
||||
*/
|
||||
@Override
|
||||
public void initApplicationContext() throws ApplicationContextException {
|
||||
super.initApplicationContext();
|
||||
initHandlerMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register handler methods found in beans of the current ApplicationContext.
|
||||
* <p>The actual key determination for a handler is up to the concrete
|
||||
* {@link #getKeyForMethod(Method)} implementation. A method in a bean for which no key
|
||||
* could be determined is simply not considered a handler method.
|
||||
* @see #getKeyForMethod(Method)
|
||||
*/
|
||||
protected void initHandlerMethods() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
|
||||
}
|
||||
for (String beanName : getApplicationContext().getBeanNamesForType(Object.class)) {
|
||||
if (isHandler(beanName)){
|
||||
detectHandlerMethods(beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given bean is a handler that should be introspected for handler methods.
|
||||
* @param beanName the name of the bean to check
|
||||
* @return true if the bean is a handler and may contain handler methods, false otherwise.
|
||||
*/
|
||||
protected abstract boolean isHandler(String beanName);
|
||||
|
||||
private void detectHandlerMethods(final String handlerName) {
|
||||
Class<?> handlerType = getApplicationContext().getType(handlerName);
|
||||
|
||||
Set<Method> methods = HandlerMethodSelector.selectMethods(handlerType, new MethodFilter() {
|
||||
public boolean matches(Method method) {
|
||||
return getKeyForMethod(method) != null;
|
||||
}
|
||||
});
|
||||
for (Method method : methods) {
|
||||
T key = getKeyForMethod(method);
|
||||
HandlerMethod handlerMethod = new HandlerMethod(handlerName, getApplicationContext(), method);
|
||||
registerHandlerMethod(key, handlerMethod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a lookup key for the given method. A method for which no key can be determined is
|
||||
* not considered a handler method.
|
||||
*
|
||||
* @param method the method to create a key for
|
||||
* @return the lookup key, or {@code null} if the method has none
|
||||
*/
|
||||
protected abstract T getKeyForMethod(Method method);
|
||||
|
||||
/**
|
||||
* Registers a {@link HandlerMethod} under the given key.
|
||||
*
|
||||
* @param key the key to register the method under
|
||||
* @param handlerMethod the handler method to register
|
||||
* @throws IllegalStateException if another method was already register under the key
|
||||
*/
|
||||
protected void registerHandlerMethod(T key, HandlerMethod handlerMethod) {
|
||||
Assert.notNull(key, "'key' must not be null");
|
||||
Assert.notNull(handlerMethod, "'handlerMethod' must not be null");
|
||||
HandlerMethod mappedHandlerMethod = handlerMethods.get(key);
|
||||
if (mappedHandlerMethod != null && !mappedHandlerMethod.equals(handlerMethod)) {
|
||||
throw new IllegalStateException("Cannot map " + handlerMethod + " to \"" + key + "\": There is already "
|
||||
+ mappedHandlerMethod + " mapped.");
|
||||
}
|
||||
handlerMethods.put(key, handlerMethod);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapped \"" + key + "\" onto " + handlerMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
T key = getKeyForRequest(request);
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking up handler method for [" + key + "]");
|
||||
}
|
||||
|
||||
HandlerMethod handlerMethod = lookupHandlerMethod(key, request);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (handlerMethod != null) {
|
||||
logger.debug("Returning [" + handlerMethod + "] as best match for [" + key + "]");
|
||||
}
|
||||
else {
|
||||
logger.debug("Did not find handler method for [" + key + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method that returns the lookup key for the given HTTP servlet request.
|
||||
*
|
||||
* @param request the request to look up the key for
|
||||
* @return the key, or {@code null} if the request does not have one
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
protected abstract T getKeyForRequest(HttpServletRequest request) throws Exception;
|
||||
|
||||
/**
|
||||
* Looks up the best-matching {@link HandlerMethod} for the given request.
|
||||
*
|
||||
* <p>This implementation iterators through all handler methods, calls {@link #getMatchingKey(Object,
|
||||
* HttpServletRequest)} for each of them, {@linkplain #getKeyComparator(HttpServletRequest) sorts} all matches, and
|
||||
* returns the 1st entry, if any. If no matches are found, {@link #handleNoMatch(Set, HttpServletRequest)} is
|
||||
* invoked.
|
||||
*
|
||||
* @param lookupKey current lookup key
|
||||
* @param request the current HTTP servlet request
|
||||
* @return the best-matching handler method, or {@code null} if there is no match
|
||||
*/
|
||||
protected HandlerMethod lookupHandlerMethod(T lookupKey, HttpServletRequest request) throws Exception {
|
||||
if (handlerMethods.containsKey(lookupKey)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Found direct match for [" + lookupKey + "]");
|
||||
}
|
||||
|
||||
handleMatch(lookupKey, request);
|
||||
return handlerMethods.get(lookupKey);
|
||||
}
|
||||
else {
|
||||
List<Match> matches = new ArrayList<Match>();
|
||||
|
||||
for (Map.Entry<T, HandlerMethod> entry : handlerMethods.entrySet()) {
|
||||
T match = getMatchingKey(entry.getKey(), request);
|
||||
if (match != null) {
|
||||
matches.add(new Match(match, entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!matches.isEmpty()) {
|
||||
Comparator<Match> comparator = getMatchComparator(request);
|
||||
Collections.sort(matches, comparator);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Found " + matches.size() + " matching key(s) for [" + lookupKey + "] : " + matches);
|
||||
}
|
||||
|
||||
Match bestMatch = matches.get(0);
|
||||
if (matches.size() > 1) {
|
||||
Match secondBestMatch = matches.get(1);
|
||||
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
|
||||
Method m1 = bestMatch.handlerMethod.getMethod();
|
||||
Method m2 = secondBestMatch.handlerMethod.getMethod();
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
|
||||
m1 + ", " + m2 + "}");
|
||||
}
|
||||
}
|
||||
|
||||
handleMatch(bestMatch.key, request);
|
||||
return bestMatch.handlerMethod;
|
||||
}
|
||||
else {
|
||||
return handleNoMatch(handlerMethods.keySet(), request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a key matching to a request has been identified.
|
||||
*
|
||||
* @param key the key selected for the request returned by {@link #getMatchingKey(Object, HttpServletRequest)}.
|
||||
* @param request the current request
|
||||
*/
|
||||
protected void handleMatch(T key, HttpServletRequest request) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching variant of the given key, given the current HTTP servlet request.
|
||||
*
|
||||
* @param key the key to get the matches for
|
||||
* @param request the current HTTP servlet request
|
||||
* @return the matching key, or {@code null} if the given key does not match against the servlet request
|
||||
*/
|
||||
protected abstract T getMatchingKey(T key, HttpServletRequest request);
|
||||
|
||||
private Comparator<Match> getMatchComparator(HttpServletRequest request) {
|
||||
final Comparator<T> keyComparator = getKeyComparator(request);
|
||||
return new Comparator<Match>() {
|
||||
public int compare(Match m1, Match m2) {
|
||||
return keyComparator.compare(m1.key, m2.key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comparator to sort the keys with. The returned comparator should sort 'better' matches higher.
|
||||
*
|
||||
* @param request the current HTTP servlet request
|
||||
* @return the comparator
|
||||
*/
|
||||
protected abstract Comparator<T> getKeyComparator(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Invoked when no match was found. Default implementation returns {@code null}.
|
||||
*
|
||||
* @param requestKeys the registered request keys
|
||||
* @param request the current HTTP request
|
||||
* @throws ServletException in case of errors
|
||||
*/
|
||||
protected HandlerMethod handleNoMatch(Set<T> requestKeys, HttpServletRequest request) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class Match {
|
||||
|
||||
private final T key;
|
||||
|
||||
private final HandlerMethod handlerMethod;
|
||||
|
||||
private Match(T key, HandlerMethod handlerMethod) {
|
||||
this.key = key;
|
||||
this.handlerMethod = handlerMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -204,9 +204,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
}
|
||||
}
|
||||
if (handler != null && this.mappedInterceptors != null) {
|
||||
Set<HandlerInterceptor> mappedInterceptors =
|
||||
HandlerInterceptor[] mappedInterceptors =
|
||||
this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher);
|
||||
if (!mappedInterceptors.isEmpty()) {
|
||||
if (mappedInterceptors.length != 0) {
|
||||
HandlerExecutionChain chain;
|
||||
if (handler instanceof HandlerExecutionChain) {
|
||||
chain = (HandlerExecutionChain) handler;
|
||||
@@ -214,7 +214,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
else {
|
||||
chain = new HandlerExecutionChain(handler);
|
||||
}
|
||||
chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()]));
|
||||
chain.addInterceptors(mappedInterceptors);
|
||||
}
|
||||
}
|
||||
if (handler != null && logger.isDebugEnabled()) {
|
||||
|
||||
@@ -1,29 +1,60 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.handler;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
class MappedInterceptors {
|
||||
|
||||
public final class MappedInterceptors {
|
||||
|
||||
private MappedInterceptor[] mappedInterceptors;
|
||||
|
||||
public MappedInterceptors(MappedInterceptor[] mappedInterceptors) {
|
||||
this.mappedInterceptors = mappedInterceptors;
|
||||
}
|
||||
|
||||
public Set<HandlerInterceptor> getInterceptors(String lookupPath, PathMatcher pathMatcher) {
|
||||
|
||||
public static MappedInterceptors createFromDeclaredBeans(ListableBeanFactory beanFactory) {
|
||||
Map<String, MappedInterceptor> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory,
|
||||
MappedInterceptor.class, true, false);
|
||||
|
||||
if (!beans.isEmpty()) {
|
||||
return new MappedInterceptors(beans.values().toArray(new MappedInterceptor[beans.size()]));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public HandlerInterceptor[] getInterceptors(String lookupPath, PathMatcher pathMatcher) {
|
||||
Set<HandlerInterceptor> interceptors = new LinkedHashSet<HandlerInterceptor>();
|
||||
for (MappedInterceptor interceptor : this.mappedInterceptors) {
|
||||
if (matches(interceptor, lookupPath, pathMatcher)) {
|
||||
interceptors.add(interceptor.getInterceptor());
|
||||
interceptors.add(interceptor.getInterceptor());
|
||||
}
|
||||
}
|
||||
return interceptors;
|
||||
return interceptors.toArray(new HandlerInterceptor[interceptors.size()]);
|
||||
}
|
||||
|
||||
|
||||
private boolean matches(MappedInterceptor interceptor, String lookupPath, PathMatcher pathMatcher) {
|
||||
String[] pathPatterns = interceptor.getPathPatterns();
|
||||
if (pathPatterns != null) {
|
||||
@@ -33,9 +64,10 @@ class MappedInterceptors {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.support.WebContentGenerator;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link HandlerAdapter} implementations that support {@link HandlerMethod}s.
|
||||
* Contains template methods for handling these handler method.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
public AbstractHandlerMethodAdapter() {
|
||||
// no restriction of HTTP methods by default
|
||||
super(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order value for this HandlerAdapter bean.
|
||||
* <p>Default value is <code>Integer.MAX_VALUE</code>, meaning that it's non-ordered.
|
||||
* @see org.springframework.core.Ordered#getOrder()
|
||||
*/
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} <p>This implementation expects the handler to be an {@link HandlerMethod}.
|
||||
*
|
||||
* @param handler the handler instance to check
|
||||
* @return whether or not this adapter can adapt the given handler
|
||||
*/
|
||||
public final boolean supports(Object handler) {
|
||||
return handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a handler method, return whether or not this adapter can support it.
|
||||
*
|
||||
* @param handlerMethod the handler method to check
|
||||
* @return whether or not this adapter can adapt the given method
|
||||
*/
|
||||
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
|
||||
|
||||
/**
|
||||
* {@inheritDoc} <p>This implementation expects the handler to be an {@link HandlerMethod}.
|
||||
*/
|
||||
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
return handleInternal(request, response, (HandlerMethod) handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given handler method to handle the request.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handlerMethod handler method to use. This object must have previously been passed to the
|
||||
* {@link #supportsInternal(HandlerMethod)} this interface, which must have returned {@code true}.
|
||||
* @return ModelAndView object with the name of the view and the required model data, or {@code null} if
|
||||
* the request has been handled directly
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
protected abstract ModelAndView handleInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod) throws Exception;
|
||||
|
||||
/**
|
||||
* {@inheritDoc} <p>This implementation expects the handler to be an {@link HandlerMethod}.
|
||||
*/
|
||||
public final long getLastModified(HttpServletRequest request, Object handler) {
|
||||
return getLastModifiedInternal(request, (HandlerMethod) handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same contract as for {@link javax.servlet.http.HttpServlet#getLastModified(HttpServletRequest)}.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param handlerMethod handler method to use
|
||||
* @return the lastModified value for the given handler
|
||||
*/
|
||||
protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A condition that can be matched to a ServletRequest.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
interface RequestCondition {
|
||||
|
||||
boolean match(HttpServletRequest request);
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Factory for request condition objects.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public abstract class RequestConditionFactory {
|
||||
|
||||
/**
|
||||
* Parses the given parameters, and returns them as a set of request conditions.
|
||||
*
|
||||
* @param params the parameters
|
||||
* @return the request conditions
|
||||
* @see org.springframework.web.bind.annotation.RequestMapping#params()
|
||||
*/
|
||||
public static Set<RequestCondition> parseParams(String... params) {
|
||||
if (params == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<RequestCondition> result = new LinkedHashSet<RequestCondition>(params.length);
|
||||
for (String expression : params) {
|
||||
result.add(new ParamNameValueCondition(expression));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given headers, and returns them as a set of request conditions.
|
||||
*
|
||||
* @param headers the headers
|
||||
* @return the request conditions
|
||||
* @see org.springframework.web.bind.annotation.RequestMapping#headers()
|
||||
*/
|
||||
public static Set<RequestCondition> parseHeaders(String... headers) {
|
||||
if (headers == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<RequestCondition> result = new LinkedHashSet<RequestCondition>(headers.length);
|
||||
for (String expression : headers) {
|
||||
HeaderNameValueCondition header = new HeaderNameValueCondition(expression);
|
||||
if (isMediaTypeHeader(header.name)) {
|
||||
result.add(new MediaTypeHeaderNameValueCondition(expression));
|
||||
}
|
||||
else {
|
||||
result.add(header);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isMediaTypeHeader(String name) {
|
||||
return "Accept".equalsIgnoreCase(name) || "Content-Type".equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A condition that supports simple "name=value" style expressions as documented in
|
||||
* <code>@RequestMapping.params()</code> and <code>@RequestMapping.headers()</code>.
|
||||
*/
|
||||
private static abstract class AbstractNameValueCondition<T> implements RequestCondition {
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final T value;
|
||||
|
||||
protected final boolean isNegated;
|
||||
|
||||
protected AbstractNameValueCondition(String expression) {
|
||||
int separator = expression.indexOf('=');
|
||||
if (separator == -1) {
|
||||
this.isNegated = expression.startsWith("!");
|
||||
this.name = isNegated ? expression.substring(1) : expression;
|
||||
this.value = null;
|
||||
}
|
||||
else {
|
||||
this.isNegated = (separator > 0) && (expression.charAt(separator - 1) == '!');
|
||||
this.name = isNegated ? expression.substring(0, separator - 1) : expression.substring(0, separator);
|
||||
this.value = parseValue(expression.substring(separator + 1));
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract T parseValue(String valueExpression);
|
||||
|
||||
public final boolean match(HttpServletRequest request) {
|
||||
boolean isMatch;
|
||||
if (this.value != null) {
|
||||
isMatch = matchValue(request);
|
||||
}
|
||||
else {
|
||||
isMatch = matchName(request);
|
||||
}
|
||||
return isNegated ? !isMatch : isMatch;
|
||||
}
|
||||
|
||||
protected abstract boolean matchName(HttpServletRequest request);
|
||||
|
||||
protected abstract boolean matchValue(HttpServletRequest request);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (value != null) {
|
||||
builder.append(name);
|
||||
if (isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append('=');
|
||||
builder.append(value);
|
||||
}
|
||||
else {
|
||||
if (isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append(name);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name.hashCode();
|
||||
result = 31 * result + (value != null ? value.hashCode() : 0);
|
||||
result = 31 * result + (isNegated ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameter name-value condition.
|
||||
*/
|
||||
private static class ParamNameValueCondition extends AbstractNameValueCondition<String> {
|
||||
|
||||
private ParamNameValueCondition(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseValue(String valueExpression) {
|
||||
return valueExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(HttpServletRequest request) {
|
||||
return WebUtils.hasSubmitParameter(request, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(HttpServletRequest request) {
|
||||
return value.equals(request.getParameter(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof ParamNameValueCondition) {
|
||||
ParamNameValueCondition other = (ParamNameValueCondition) obj;
|
||||
return ((this.name.equals(other.name)) &&
|
||||
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
|
||||
this.isNegated == other.isNegated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request header name-value condition.
|
||||
*/
|
||||
static class HeaderNameValueCondition extends AbstractNameValueCondition<String> {
|
||||
|
||||
public HeaderNameValueCondition(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseValue(String valueExpression) {
|
||||
return valueExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(HttpServletRequest request) {
|
||||
return request.getHeader(name) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
final protected boolean matchValue(HttpServletRequest request) {
|
||||
return value.equals(request.getHeader(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof HeaderNameValueCondition) {
|
||||
HeaderNameValueCondition other = (HeaderNameValueCondition) obj;
|
||||
return ((this.name.equalsIgnoreCase(other.name)) &&
|
||||
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
|
||||
this.isNegated == other.isNegated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A RequestCondition that for headers that contain {@link org.springframework.http.MediaType MediaTypes}.
|
||||
*/
|
||||
private static class MediaTypeHeaderNameValueCondition extends AbstractNameValueCondition<List<MediaType>> {
|
||||
|
||||
public MediaTypeHeaderNameValueCondition(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<MediaType> parseValue(String valueExpression) {
|
||||
return Collections.unmodifiableList(MediaType.parseMediaTypes(valueExpression));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(HttpServletRequest request) {
|
||||
return request.getHeader(name) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(HttpServletRequest request) {
|
||||
List<MediaType> requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(name));
|
||||
|
||||
for (MediaType mediaType : this.value) {
|
||||
for (MediaType requestMediaType : requestMediaTypes) {
|
||||
if (mediaType.includes(requestMediaType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof MediaTypeHeaderNameValueCondition) {
|
||||
MediaTypeHeaderNameValueCondition other = (MediaTypeHeaderNameValueCondition) obj;
|
||||
return ((this.name.equalsIgnoreCase(other.name)) &&
|
||||
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
|
||||
this.isNegated == other.isNegated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public final class RequestKey {
|
||||
|
||||
private final Set<String> patterns;
|
||||
|
||||
private final Set<RequestMethod> methods;
|
||||
|
||||
private final Set<RequestCondition> paramConditions;
|
||||
|
||||
private final Set<RequestCondition> headerConditions;
|
||||
|
||||
private int hash;
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestKey} instance with the given parameters.
|
||||
*
|
||||
* <p/>Package protected for testing purposes.
|
||||
*/
|
||||
RequestKey(Collection<String> patterns,
|
||||
Collection<RequestMethod> methods,
|
||||
Collection<RequestCondition> paramConditions,
|
||||
Collection<RequestCondition> headerConditions) {
|
||||
this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns));
|
||||
this.methods = asUnmodifiableSet(methods);
|
||||
this.paramConditions = asUnmodifiableSet(paramConditions);
|
||||
this.headerConditions = asUnmodifiableSet(headerConditions);
|
||||
}
|
||||
|
||||
private static Set<String> prependLeadingSlash(Collection<String> patterns) {
|
||||
if (patterns == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> result = new LinkedHashSet<String>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
if (!pattern.startsWith("/")) {
|
||||
pattern = "/" + pattern;
|
||||
}
|
||||
result.add(pattern);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <T> Set<T> asUnmodifiableSet(Collection<T> collection) {
|
||||
if (collection == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<T> result = new LinkedHashSet<T>(collection);
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestKey} from a {@link RequestMapping @RequestMapping} annotation.
|
||||
*
|
||||
* @param annotation the annotation
|
||||
* @return the request key created from the annotation
|
||||
*/
|
||||
public static RequestKey createFromRequestMapping(RequestMapping annotation) {
|
||||
return new RequestKey(Arrays.asList(annotation.value()), Arrays.asList(annotation.method()),
|
||||
RequestConditionFactory.parseParams(annotation.params()),
|
||||
RequestConditionFactory.parseHeaders(annotation.headers()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestKey} from a {@link HttpServletRequest}.
|
||||
*
|
||||
* @param request the servlet request
|
||||
* @param urlPathHelper to create the {@linkplain UrlPathHelper#getLookupPathForRequest(HttpServletRequest) lookup
|
||||
* path}
|
||||
* @return the request key created from the servlet request
|
||||
*/
|
||||
public static RequestKey createFromServletRequest(HttpServletRequest request, UrlPathHelper urlPathHelper) {
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
RequestMethod method = RequestMethod.valueOf(request.getMethod());
|
||||
return new RequestKey(Arrays.asList(lookupPath), Arrays.asList(method), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the patterns of this request key.
|
||||
*/
|
||||
public Set<String> getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request methods of this request key.
|
||||
*/
|
||||
public Set<RequestMethod> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request parameters of this request key.
|
||||
*/
|
||||
public Set<RequestCondition> getParams() {
|
||||
return paramConditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request headers of this request key.
|
||||
*/
|
||||
public Set<RequestCondition> getHeaders() {
|
||||
return headerConditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code RequestKey} by combining it with another. The typical use case for this is combining type
|
||||
* and method-level {@link RequestMapping @RequestMapping} annotations.
|
||||
*
|
||||
* @param methodKey the method-level RequestKey
|
||||
* @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns
|
||||
* @return the combined request key
|
||||
*/
|
||||
public RequestKey combine(RequestKey methodKey, PathMatcher pathMatcher) {
|
||||
Set<String> patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher);
|
||||
Set<RequestMethod> methods = union(this.methods, methodKey.methods);
|
||||
Set<RequestCondition> params = union(this.paramConditions, methodKey.paramConditions);
|
||||
Set<RequestCondition> headers = union(this.headerConditions, methodKey.headerConditions);
|
||||
|
||||
return new RequestKey(patterns, methods, params, headers);
|
||||
}
|
||||
|
||||
private static Set<String> combinePatterns(Collection<String> typePatterns,
|
||||
Collection<String> methodPatterns,
|
||||
PathMatcher pathMatcher) {
|
||||
Set<String> result = new LinkedHashSet<String>();
|
||||
if (!typePatterns.isEmpty() && !methodPatterns.isEmpty()) {
|
||||
for (String pattern1 : typePatterns) {
|
||||
for (String p2 : methodPatterns) {
|
||||
result.add(pathMatcher.combine(pattern1, p2));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!typePatterns.isEmpty()) {
|
||||
result.addAll(typePatterns);
|
||||
}
|
||||
else if (!methodPatterns.isEmpty()) {
|
||||
result.addAll(methodPatterns);
|
||||
}
|
||||
else {
|
||||
result.add("");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <T> Set<T> union(Collection<T> s1, Collection<T> s2) {
|
||||
Set<T> union = new LinkedHashSet<T>(s1);
|
||||
union.addAll(s2);
|
||||
return union;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@code RequestKey} that contains all matching attributes of this key, given the {@link
|
||||
* HttpServletRequest}. Matching patterns in the returned RequestKey are sorted according to {@link
|
||||
* PathMatcher#getPatternComparator(String)} with the best matching pattern at the top.
|
||||
*
|
||||
* @param request the servlet request
|
||||
* @param pathMatcher to {@linkplain PathMatcher#match(String, String) match} patterns
|
||||
* @param urlPathHelper to create the {@linkplain UrlPathHelper#getLookupPathForRequest(HttpServletRequest) lookup
|
||||
* path}
|
||||
* @return a new request key that contains all matching attributes
|
||||
*/
|
||||
public RequestKey getMatchingKey(HttpServletRequest request, PathMatcher pathMatcher, UrlPathHelper urlPathHelper) {
|
||||
if (!checkMethod(request) || !checkParams(request) || !checkHeaders(request)) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
List<String> matchingPatterns = getMatchingPatterns(request, pathMatcher, urlPathHelper);
|
||||
if (!matchingPatterns.isEmpty()) {
|
||||
Set<RequestMethod> matchingMethods = getMatchingMethods(request);
|
||||
return new RequestKey(matchingPatterns, matchingMethods, this.paramConditions, this.headerConditions);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getMatchingPatterns(HttpServletRequest request,
|
||||
PathMatcher pathMatcher,
|
||||
UrlPathHelper urlPathHelper) {
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
|
||||
List<String> matchingPatterns = new ArrayList<String>();
|
||||
for (String pattern : this.patterns) {
|
||||
String matchingPattern = getMatchingPattern(pattern, lookupPath, pathMatcher);
|
||||
if (matchingPattern != null) {
|
||||
matchingPatterns.add(matchingPattern);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(matchingPatterns, pathMatcher.getPatternComparator(lookupPath));
|
||||
|
||||
return matchingPatterns;
|
||||
}
|
||||
|
||||
private Set<RequestMethod> getMatchingMethods(HttpServletRequest request) {
|
||||
if (this.methods.isEmpty()) {
|
||||
return this.methods;
|
||||
}
|
||||
else {
|
||||
return Collections.singleton(RequestMethod.valueOf(request.getMethod()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkMethod(HttpServletRequest request) {
|
||||
return methods.isEmpty() || methods.contains(RequestMethod.valueOf(request.getMethod()));
|
||||
}
|
||||
|
||||
private boolean checkParams(HttpServletRequest request) {
|
||||
return checkConditions(paramConditions, request);
|
||||
}
|
||||
|
||||
private boolean checkHeaders(HttpServletRequest request) {
|
||||
return checkConditions(headerConditions, request);
|
||||
}
|
||||
|
||||
private String getMatchingPattern(String pattern, String lookupPath, PathMatcher pathMatcher) {
|
||||
if (pattern.equals(lookupPath) || pathMatcher.match(pattern, lookupPath)) {
|
||||
return pattern;
|
||||
}
|
||||
boolean hasSuffix = pattern.indexOf('.') != -1;
|
||||
if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
|
||||
return pattern + ".*";
|
||||
}
|
||||
boolean endsWithSlash = pattern.endsWith("/");
|
||||
if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) {
|
||||
return pattern +"/";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean checkConditions(Set<RequestCondition> conditions, HttpServletRequest request) {
|
||||
for (RequestCondition condition : conditions) {
|
||||
if (!condition.match(request)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof RequestKey) {
|
||||
RequestKey other = (RequestKey) obj;
|
||||
return (this.patterns.equals(other.patterns) && this.methods.equals(other.methods) &&
|
||||
this.paramConditions.equals(other.paramConditions) &&
|
||||
this.headerConditions.equals(other.headerConditions));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = hash;
|
||||
if (result == 0) {
|
||||
result = patterns.hashCode();
|
||||
result = 31 * result + methods.hashCode();
|
||||
result = 31 * result + paramConditions.hashCode();
|
||||
result = 31 * result + headerConditions.hashCode();
|
||||
hash = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("{");
|
||||
builder.append(patterns);
|
||||
if (!methods.isEmpty()) {
|
||||
builder.append(',');
|
||||
builder.append(methods);
|
||||
}
|
||||
if (!headerConditions.isEmpty()) {
|
||||
builder.append(',');
|
||||
builder.append(headerConditions);
|
||||
}
|
||||
if (!paramConditions.isEmpty()) {
|
||||
builder.append(',');
|
||||
builder.append(paramConditions);
|
||||
}
|
||||
builder.append('}');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.support.DefaultDataBinderFactory;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.method.annotation.ModelFactory;
|
||||
import org.springframework.web.method.annotation.SessionAttributesHandler;
|
||||
import org.springframework.web.method.annotation.support.ErrorsMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.support.ExpressionValueMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
||||
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
||||
import org.springframework.web.method.annotation.support.RequestHeaderMapMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.support.RequestHeaderMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.support.RequestParamMapMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.support.RequestParamMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.support.WebArgumentResolverAdapter;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverContainer;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerContainer;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
||||
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.PathVariableMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletCookieValueMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletModelAttributeMethodProcessor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* An extension of {@link AbstractHandlerMethodAdapter} with support for {@link RequestMapping} handler methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,
|
||||
InitializingBean {
|
||||
|
||||
private WebArgumentResolver[] customArgumentResolvers;
|
||||
|
||||
private ModelAndViewResolver[] customModelAndViewResolvers;
|
||||
|
||||
private HttpMessageConverter<?>[] messageConverters;
|
||||
|
||||
private WebBindingInitializer webBindingInitializer;
|
||||
|
||||
private int cacheSecondsForSessionAttributeHandlers = 0;
|
||||
|
||||
private boolean synchronizeOnSession = false;
|
||||
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
|
||||
|
||||
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache =
|
||||
new ConcurrentHashMap<Class<?>, SessionAttributesHandler>();
|
||||
|
||||
private final Map<Class<?>, Set<Method>> modelAttributeMethodCache = new ConcurrentHashMap<Class<?>, Set<Method>>();
|
||||
|
||||
private final Map<Class<?>, Set<Method>> initBinderMethodCache = new ConcurrentHashMap<Class<?>, Set<Method>>();
|
||||
|
||||
private final HandlerMethodReturnValueHandlerContainer returnValueHandlers = new HandlerMethodReturnValueHandlerContainer();
|
||||
|
||||
private final HandlerMethodArgumentResolverContainer requestMethodArgResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
|
||||
private final HandlerMethodArgumentResolverContainer binderMethodArgResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
|
||||
/**
|
||||
* Create a {@link RequestMappingHandlerAdapter} instance.
|
||||
*/
|
||||
public RequestMappingHandlerAdapter() {
|
||||
|
||||
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
|
||||
stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316
|
||||
|
||||
this.messageConverters = new HttpMessageConverter[] { new ByteArrayHttpMessageConverter(),
|
||||
stringHttpMessageConverter, new SourceHttpMessageConverter<Source>(),
|
||||
new XmlAwareFormHttpMessageConverter() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom WebArgumentResolvers to use for special method parameter types.
|
||||
* <p>Such a custom WebArgumentResolver 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 WebArgumentResolvers to use for special method parameter types.
|
||||
* <p>Any such custom WebArgumentResolver 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom ModelAndViewResolvers to use for special method return types.
|
||||
* <p>Such a custom ModelAndViewResolver will kick in first, having a chance to resolve
|
||||
* a return value before the standard ModelAndView handling kicks in.
|
||||
*/
|
||||
public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
|
||||
this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more custom ModelAndViewResolvers to use for special method return types.
|
||||
* <p>Any such custom ModelAndViewResolver will kick in first, having a chance to resolve
|
||||
* a return value before the standard ModelAndView handling kicks in.
|
||||
*/
|
||||
public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
|
||||
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message body converters to use.
|
||||
* <p>These converters are used to convert from and to HTTP requests and responses.
|
||||
*/
|
||||
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a WebBindingInitializer which will apply pre-configured
|
||||
* configuration to every DataBinder that this controller uses.
|
||||
*/
|
||||
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||
this.webBindingInitializer = webBindingInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the strategy to store session attributes with.
|
||||
* <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||
* storing session attributes in the HttpSession, using the same attribute name as in the model.
|
||||
*/
|
||||
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
|
||||
this.sessionAttributeStore = sessionAttributeStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache content produced by <code>@SessionAttributes</code> annotated handlers
|
||||
* for the given number of seconds. Default is 0, preventing caching completely.
|
||||
* <p>In contrast to the "cacheSeconds" property which will apply to all general handlers
|
||||
* (but not to <code>@SessionAttributes</code> annotated handlers), this setting will
|
||||
* apply to <code>@SessionAttributes</code> annotated handlers only.
|
||||
* @see #setCacheSeconds
|
||||
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||
*/
|
||||
public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) {
|
||||
this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if controller execution should be synchronized on the session,
|
||||
* to serialize parallel invocations from the same client.
|
||||
* <p>More specifically, the execution of the <code>handleRequestInternal</code>
|
||||
* method will get synchronized if this flag is "true". The best available
|
||||
* session mutex will be used for the synchronization; ideally, this will
|
||||
* be a mutex exposed by HttpSessionMutexListener.
|
||||
* <p>The session mutex is guaranteed to be the same object during
|
||||
* the entire lifetime of the session, available under the key defined
|
||||
* by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
|
||||
* safe reference to synchronize on for locking on the current session.
|
||||
* <p>In many cases, the HttpSession reference itself is a safe mutex
|
||||
* as well, since it will always be the same object reference for the
|
||||
* same active logical session. However, this is not guaranteed across
|
||||
* different servlet containers; the only 100% safe way is a session mutex.
|
||||
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||
* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
|
||||
*/
|
||||
public void setSynchronizeOnSession(boolean synchronizeOnSession) {
|
||||
this.synchronizeOnSession = synchronizeOnSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed
|
||||
* (e.g. for default attribute names).
|
||||
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
initRequestMethodArgResolvers();
|
||||
initBinderMethodArgResolvers();
|
||||
initReturnValueHandlers();
|
||||
}
|
||||
|
||||
private void initRequestMethodArgResolvers() {
|
||||
requestMethodArgResolvers.registerArgumentResolver(new RequestParamMethodArgumentResolver(beanFactory, false));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new RequestParamMapMethodArgumentResolver());
|
||||
requestMethodArgResolvers.registerArgumentResolver(new PathVariableMethodArgumentResolver(beanFactory));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ServletModelAttributeMethodProcessor(false));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new RequestResponseBodyMethodProcessor(messageConverters));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new RequestHeaderMethodArgumentResolver(beanFactory));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new RequestHeaderMapMethodArgumentResolver());
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ServletCookieValueMethodArgumentResolver(beanFactory));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ExpressionValueMethodArgumentResolver(beanFactory));
|
||||
|
||||
if (customArgumentResolvers != null) {
|
||||
for (WebArgumentResolver customResolver : customArgumentResolvers) {
|
||||
requestMethodArgResolvers.registerArgumentResolver(new WebArgumentResolverAdapter(customResolver));
|
||||
}
|
||||
}
|
||||
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ServletRequestMethodArgumentResolver());
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ServletResponseMethodArgumentResolver());
|
||||
requestMethodArgResolvers.registerArgumentResolver(new HttpEntityMethodProcessor(messageConverters));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ModelMethodProcessor());
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ErrorsMethodArgumentResolver());
|
||||
requestMethodArgResolvers.registerArgumentResolver(new RequestParamMethodArgumentResolver(beanFactory, true));
|
||||
requestMethodArgResolvers.registerArgumentResolver(new ServletModelAttributeMethodProcessor(true));
|
||||
}
|
||||
|
||||
private void initBinderMethodArgResolvers() {
|
||||
binderMethodArgResolvers.registerArgumentResolver(new RequestParamMethodArgumentResolver(beanFactory, false));
|
||||
binderMethodArgResolvers.registerArgumentResolver(new RequestParamMapMethodArgumentResolver());
|
||||
binderMethodArgResolvers.registerArgumentResolver(new PathVariableMethodArgumentResolver(beanFactory));
|
||||
binderMethodArgResolvers.registerArgumentResolver(new ExpressionValueMethodArgumentResolver(beanFactory));
|
||||
|
||||
if (customArgumentResolvers != null) {
|
||||
for (WebArgumentResolver customResolver : customArgumentResolvers) {
|
||||
binderMethodArgResolvers.registerArgumentResolver(new WebArgumentResolverAdapter(customResolver));
|
||||
}
|
||||
}
|
||||
|
||||
binderMethodArgResolvers.registerArgumentResolver(new ServletRequestMethodArgumentResolver());
|
||||
binderMethodArgResolvers.registerArgumentResolver(new ServletResponseMethodArgumentResolver());
|
||||
binderMethodArgResolvers.registerArgumentResolver(new RequestParamMethodArgumentResolver(beanFactory, true));
|
||||
}
|
||||
|
||||
private void initReturnValueHandlers() {
|
||||
returnValueHandlers.registerReturnValueHandler(new RequestResponseBodyMethodProcessor(messageConverters));
|
||||
returnValueHandlers.registerReturnValueHandler(new ModelAttributeMethodProcessor(false));
|
||||
returnValueHandlers.registerReturnValueHandler(new ModelAndViewMethodReturnValueHandler());
|
||||
returnValueHandlers.registerReturnValueHandler(new ModelMethodProcessor());
|
||||
returnValueHandlers.registerReturnValueHandler(new ViewMethodReturnValueHandler());
|
||||
returnValueHandlers.registerReturnValueHandler(new HttpEntityMethodProcessor(messageConverters));
|
||||
returnValueHandlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(customModelAndViewResolvers));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsInternal(HandlerMethod handlerMethod) {
|
||||
return supportsMethodParameters(handlerMethod.getMethodParameters()) &&
|
||||
supportsReturnType(handlerMethod.getReturnType());
|
||||
}
|
||||
|
||||
private boolean supportsMethodParameters(MethodParameter[] methodParameters) {
|
||||
for (MethodParameter methodParameter : methodParameters) {
|
||||
if (! this.requestMethodArgResolvers.supportsParameter(methodParameter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean supportsReturnType(MethodParameter methodReturnType) {
|
||||
return (this.returnValueHandlers.supportsReturnType(methodReturnType) ||
|
||||
Void.TYPE.equals(methodReturnType.getParameterType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ModelAndView handleInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod) throws Exception {
|
||||
|
||||
if (hasSessionAttributes(handlerMethod.getBeanType())) {
|
||||
// Always prevent caching in case of session attribute management.
|
||||
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
|
||||
}
|
||||
else {
|
||||
// Uses configured default cacheSeconds setting.
|
||||
checkAndPrepare(request, response, true);
|
||||
}
|
||||
|
||||
// Execute invokeHandlerMethod in synchronized block if required.
|
||||
if (this.synchronizeOnSession) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
Object mutex = WebUtils.getSessionMutex(session);
|
||||
synchronized (mutex) {
|
||||
return invokeHandlerMethod(request, response, handlerMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return invokeHandlerMethod(request, response, handlerMethod);
|
||||
}
|
||||
|
||||
private boolean hasSessionAttributes(Class<?> handlerType) {
|
||||
SessionAttributesHandler handler = null;
|
||||
synchronized(this.sessionAttributesHandlerCache) {
|
||||
handler = this.sessionAttributesHandlerCache.get(handlerType);
|
||||
if (handler == null) {
|
||||
handler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
||||
this.sessionAttributesHandlerCache.put(handlerType, handler);
|
||||
}
|
||||
}
|
||||
return handler.hasSessionAttributes();
|
||||
}
|
||||
|
||||
private ModelAndView invokeHandlerMethod(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod) throws Exception {
|
||||
|
||||
WebDataBinderFactory binderFactory = createDataBinderFactory(handlerMethod);
|
||||
ModelFactory modelFactory = createModelFactory(handlerMethod, binderFactory);
|
||||
ServletInvocableHandlerMethod requestMethod = createRequestMappingMethod(handlerMethod, binderFactory);
|
||||
|
||||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||
SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
|
||||
ModelMap implicitModel = modelFactory.createModel(webRequest, requestMethod);
|
||||
ModelAndView mav = requestMethod.invokeAndHandle(webRequest, implicitModel, sessionStatus);
|
||||
|
||||
ModelMap actualModel = (mav != null) ? mav.getModelMap() : null;
|
||||
modelFactory.updateAttributes(webRequest, sessionStatus, actualModel, implicitModel);
|
||||
|
||||
return mav;
|
||||
}
|
||||
|
||||
private WebDataBinderFactory createDataBinderFactory(HandlerMethod handlerMethod) {
|
||||
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
Set<Method> binderMethods = initBinderMethodCache.get(handlerType);
|
||||
if (binderMethods == null) {
|
||||
binderMethods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
|
||||
initBinderMethodCache.put(handlerType, binderMethods);
|
||||
}
|
||||
|
||||
for (Method method : binderMethods) {
|
||||
Object bean = handlerMethod.getBean();
|
||||
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
|
||||
binderMethod.setArgumentResolverContainer(this.binderMethodArgResolvers);
|
||||
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
|
||||
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
|
||||
initBinderMethods.add(binderMethod);
|
||||
}
|
||||
|
||||
return new ServletInitBinderMethodDataBinderFactory(initBinderMethods, this.webBindingInitializer);
|
||||
}
|
||||
|
||||
private ModelFactory createModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
|
||||
List<InvocableHandlerMethod> modelAttrMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
Set<Method> attributeMethods = modelAttributeMethodCache.get(handlerType);
|
||||
if (attributeMethods == null) {
|
||||
attributeMethods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
|
||||
modelAttributeMethodCache.put(handlerType, attributeMethods);
|
||||
}
|
||||
|
||||
for (Method method : attributeMethods) {
|
||||
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
attrMethod.setArgumentResolverContainer(this.requestMethodArgResolvers);
|
||||
attrMethod.setDataBinderFactory(binderFactory);
|
||||
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
modelAttrMethods.add(attrMethod);
|
||||
}
|
||||
|
||||
return new ModelFactory(modelAttrMethods, binderFactory, sessionAttributesHandlerCache.get(handlerType));
|
||||
}
|
||||
|
||||
private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod,
|
||||
WebDataBinderFactory binderFactory) {
|
||||
Method method = handlerMethod.getMethod();
|
||||
ServletInvocableHandlerMethod requestMethod = new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
|
||||
requestMethod.setArgumentResolverContainer(this.requestMethodArgResolvers);
|
||||
requestMethod.setReturnValueHandlers(this.returnValueHandlers);
|
||||
requestMethod.setDataBinderFactory(binderFactory);
|
||||
requestMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||||
return requestMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
|
||||
*/
|
||||
public static MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
|
||||
|
||||
public boolean matches(Method method) {
|
||||
return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
|
||||
*/
|
||||
public static MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
|
||||
|
||||
public boolean matches(Method method) {
|
||||
return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
|
||||
(AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.method.annotation.ExceptionMethodMapping;
|
||||
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
||||
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
||||
import org.springframework.web.method.annotation.support.WebArgumentResolverAdapter;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverContainer;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerContainer;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ModelAndViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||
|
||||
/**
|
||||
* An extension of {@link AbstractHandlerMethodExceptionResolver} that matches thrown exceptions to
|
||||
* {@link ExceptionHandler @ExceptionHandler} methods in the handler. If a match is found the
|
||||
* exception-handling method is invoked to process the request.
|
||||
*
|
||||
* <p>See {@link ExceptionHandler} for information on supported method arguments and return values
|
||||
* for exception-handling methods. You can customize method argument resolution and return value
|
||||
* processing through the various bean properties in this class.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
* @see #setCustomArgumentResolvers(WebArgumentResolver[])
|
||||
* @see #setCustomModelAndViewResolvers(ModelAndViewResolver[])
|
||||
* @see #setMessageConverters(HttpMessageConverter[])
|
||||
*/
|
||||
public class RequestMappingHandlerMethodExceptionResolver extends AbstractHandlerMethodExceptionResolver implements
|
||||
InitializingBean {
|
||||
|
||||
private WebArgumentResolver[] customArgumentResolvers;
|
||||
|
||||
private HttpMessageConverter<?>[] messageConverters;
|
||||
|
||||
private ModelAndViewResolver[] customModelAndViewResolvers;
|
||||
|
||||
private final Map<Class<?>, ExceptionMethodMapping> exceptionMethodMappingCache =
|
||||
new ConcurrentHashMap<Class<?>, ExceptionMethodMapping>();
|
||||
|
||||
private final HandlerMethodArgumentResolverContainer argumentResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
|
||||
private final HandlerMethodReturnValueHandlerContainer returnValueHandlers = new HandlerMethodReturnValueHandlerContainer();
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link RequestMappingHandlerMethodExceptionResolver}.
|
||||
*/
|
||||
public RequestMappingHandlerMethodExceptionResolver() {
|
||||
|
||||
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
|
||||
stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316
|
||||
|
||||
this.messageConverters = new HttpMessageConverter[] { new ByteArrayHttpMessageConverter(),
|
||||
stringHttpMessageConverter, new SourceHttpMessageConverter<Source>(),
|
||||
new XmlAwareFormHttpMessageConverter() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom ArgumentResolvers to use for special method parameter types.
|
||||
* <p>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.
|
||||
* <p>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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message body converters to use.
|
||||
* <p>These converters are used to convert from and to HTTP requests and responses.
|
||||
*/
|
||||
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom ModelAndViewResolvers to use for special method return types.
|
||||
* <p>Such a custom ModelAndViewResolver will kick in first, having a chance to resolve
|
||||
* a return value before the standard ModelAndView handling kicks in.
|
||||
*/
|
||||
public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
|
||||
this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more custom ModelAndViewResolvers to use for special method return types.
|
||||
* <p>Any such custom ModelAndViewResolver will kick in first, having a chance to resolve
|
||||
* a return value before the standard ModelAndView handling kicks in.
|
||||
*/
|
||||
public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
|
||||
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (customArgumentResolvers != null) {
|
||||
for (WebArgumentResolver customResolver : customArgumentResolvers) {
|
||||
argumentResolvers.registerArgumentResolver(new WebArgumentResolverAdapter(customResolver));
|
||||
}
|
||||
}
|
||||
|
||||
argumentResolvers.registerArgumentResolver(new ServletRequestMethodArgumentResolver());
|
||||
argumentResolvers.registerArgumentResolver(new ServletResponseMethodArgumentResolver());
|
||||
|
||||
returnValueHandlers.registerReturnValueHandler(new RequestResponseBodyMethodProcessor(messageConverters));
|
||||
returnValueHandlers.registerReturnValueHandler(new ModelAttributeMethodProcessor(false));
|
||||
returnValueHandlers.registerReturnValueHandler(new ModelAndViewMethodReturnValueHandler());
|
||||
returnValueHandlers.registerReturnValueHandler(new ModelMethodProcessor());
|
||||
returnValueHandlers.registerReturnValueHandler(new ViewMethodReturnValueHandler());
|
||||
returnValueHandlers.registerReturnValueHandler(new HttpEntityMethodProcessor(messageConverters));
|
||||
returnValueHandlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(customModelAndViewResolvers));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
HandlerMethod handlerMethod,
|
||||
Exception ex) {
|
||||
if (handlerMethod != null) {
|
||||
ExceptionMethodMapping mapping = getExceptionMethodMapping(handlerMethod);
|
||||
Method method = mapping.getMethod(ex);
|
||||
|
||||
if (method != null) {
|
||||
Object handler = handlerMethod.getBean();
|
||||
ServletInvocableHandlerMethod exceptionHandler = new ServletInvocableHandlerMethod(handler, method);
|
||||
exceptionHandler.setArgumentResolverContainer(argumentResolvers);
|
||||
exceptionHandler.setReturnValueHandlers(returnValueHandlers);
|
||||
|
||||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||
ModelMap model = new ExtendedModelMap();
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking exception-handling method: " + exceptionHandler);
|
||||
}
|
||||
ModelAndView mav = exceptionHandler.invokeAndHandle(webRequest , model , ex);
|
||||
return (mav != null) ? mav : new ModelAndView();
|
||||
}
|
||||
catch (Exception invocationEx) {
|
||||
logger.error("Invoking exception-handling method resulted in exception : " +
|
||||
exceptionHandler, invocationEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ExceptionMethodMapping getExceptionMethodMapping(HandlerMethod handlerMethod) {
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
ExceptionMethodMapping mapping = exceptionMethodMappingCache.get(handlerType);
|
||||
if (mapping == null) {
|
||||
Set<Method> methods = HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS);
|
||||
mapping = new ExceptionMethodMapping(methods);
|
||||
exceptionMethodMappingCache.put(handlerType, mapping);
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-built MethodFilter that matches {@link ExceptionHandler @ExceptionHandler} methods.
|
||||
*/
|
||||
public static MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
|
||||
|
||||
public boolean matches(Method method) {
|
||||
return AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptors;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMapping<RequestKey> {
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private MappedInterceptors mappedInterceptors;
|
||||
|
||||
/**
|
||||
* Set if URL lookup should always use the full path within the current servlet context. Else, the path within the
|
||||
* current servlet mapping is used if applicable (that is, in the case of a ".../*" servlet mapping in web.xml).
|
||||
* <p>Default is "false".
|
||||
*
|
||||
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
|
||||
*/
|
||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if context path and request URI should be URL-decoded. Both are returned <i>undecoded</i> by the Servlet API, in
|
||||
* contrast to the servlet path. <p>Uses either the request encoding or the default encoding according to the Servlet
|
||||
* spec (ISO-8859-1).
|
||||
*
|
||||
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
this.urlPathHelper.setUrlDecode(urlDecode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the UrlPathHelper to use for resolution of lookup paths. <p>Use this to override the default UrlPathHelper
|
||||
* with a custom subclass, or to share common UrlPathHelper settings across multiple HandlerMappings and
|
||||
* MethodNameResolvers.
|
||||
*
|
||||
*/
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||
this.urlPathHelper = urlPathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is
|
||||
* AntPathMatcher.
|
||||
*
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||
this.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link MappedInterceptor} instances to use to intercept handler method invocations.
|
||||
*/
|
||||
public void setMappedInterceptors(MappedInterceptor[] mappedInterceptors) {
|
||||
this.mappedInterceptors = new MappedInterceptors(mappedInterceptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initInterceptors() {
|
||||
super.initInterceptors();
|
||||
if (this.mappedInterceptors == null) {
|
||||
this.mappedInterceptors = MappedInterceptors.createFromDeclaredBeans(getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* The handler determination is made based on the presence of a type-level {@link Controller} or
|
||||
* a type-level {@link RequestMapping} annotation.
|
||||
*/
|
||||
@Override
|
||||
protected boolean isHandler(String beanName) {
|
||||
return ((getApplicationContext().findAnnotationOnBean(beanName, RequestMapping.class) != null) ||
|
||||
(getApplicationContext().findAnnotationOnBean(beanName, Controller.class) != null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link RequestKey} instances that represents the given HTTP servlet request.
|
||||
*
|
||||
* @param request the request to look up the key for
|
||||
* @return the key, never null
|
||||
*/
|
||||
@Override
|
||||
protected RequestKey getKeyForRequest(HttpServletRequest request) {
|
||||
return RequestKey.createFromServletRequest(request, urlPathHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link RequestKey} for the given method.
|
||||
* <p>Only {@link RequestMapping @RequestMapping}-annotated methods are considered.
|
||||
* Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their
|
||||
* attributes combined with method-level {@link RequestMapping @RequestMapping} attributes.
|
||||
*
|
||||
* @param method the method to create a key for
|
||||
* @return the key, or {@code null}
|
||||
* @see RequestKey#combine(RequestKey, PathMatcher)
|
||||
*/
|
||||
@Override
|
||||
protected RequestKey getKeyForMethod(Method method) {
|
||||
RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
|
||||
if (annotation != null) {
|
||||
RequestKey methodKey = RequestKey.createFromRequestMapping(annotation);
|
||||
RequestMapping typeAnnot = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequestMapping.class);
|
||||
if (typeAnnot != null) {
|
||||
RequestKey typeKey = RequestKey.createFromRequestMapping(typeAnnot);
|
||||
return typeKey.combine(methodKey, pathMatcher);
|
||||
}
|
||||
else {
|
||||
return methodKey;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comparator<RequestKey> getKeyComparator(HttpServletRequest request) {
|
||||
return new RequestKeyComparator(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMatch(RequestKey key, HttpServletRequest request) {
|
||||
String pattern = key.getPatterns().iterator().next();
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
Map<String, String> uriTemplateVariables = pathMatcher.extractUriTemplateVariables(pattern, lookupPath);
|
||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequestKey getMatchingKey(RequestKey key, HttpServletRequest request) {
|
||||
return key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
|
||||
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
|
||||
if (this.mappedInterceptors != null) {
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
HandlerInterceptor[] handlerInterceptors = mappedInterceptors.getInterceptors(lookupPath, pathMatcher);
|
||||
if (handlerInterceptors.length > 0) {
|
||||
chain.addInterceptors(handlerInterceptors);
|
||||
}
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerMethod handleNoMatch(Set<RequestKey> requestKeys, HttpServletRequest request)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
Set<String> allowedMethods = new HashSet<String>(6);
|
||||
for (RequestKey requestKey : requestKeys) {
|
||||
for (String pattern : requestKey.getPatterns()) {
|
||||
if (pathMatcher.match(pattern, lookupPath)) {
|
||||
for (RequestMethod method : requestKey.getMethods()) {
|
||||
allowedMethods.add(method.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allowedMethods.isEmpty()) {
|
||||
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
|
||||
allowedMethods.toArray(new String[allowedMethods.size()]));
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparator for RequestKey types. Effective comparison can only be done in the context of a specific request. For
|
||||
* example not all configured patterns may apply to the current request. Therefore an HttpServletRequest is required as
|
||||
* input.
|
||||
*
|
||||
* Furthermore, the following assumptions are made about the input RequestKeys: <ul> <li>Each RequestKey has been fully
|
||||
* matched to the request <li>The RequestKey contains matched patterns only <li>Patterns are ordered with the best
|
||||
* matching pattern at the top </ul>
|
||||
*
|
||||
* @see RequestMappingHandlerMethodMapping#getMatchingKey(RequestKey, HttpServletRequest)
|
||||
*/
|
||||
private class RequestKeyComparator implements Comparator<RequestKey> {
|
||||
|
||||
private Comparator<String> patternComparator;
|
||||
|
||||
private List<MediaType> requestAcceptHeader;
|
||||
|
||||
public RequestKeyComparator(HttpServletRequest request) {
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
this.patternComparator = pathMatcher.getPatternComparator(lookupPath);
|
||||
String acceptHeader = request.getHeader("Accept");
|
||||
this.requestAcceptHeader = MediaType.parseMediaTypes(acceptHeader);
|
||||
MediaType.sortByQualityValue(this.requestAcceptHeader);
|
||||
}
|
||||
|
||||
public int compare(RequestKey key, RequestKey otherKey) {
|
||||
int result = comparePatterns(key.getPatterns(), otherKey.getPatterns());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = otherKey.getParams().size() - key.getParams().size();
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = otherKey.getHeaders().size() - key.getHeaders().size();
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
/*
|
||||
TODO: fix
|
||||
result = compareAcceptHeaders(key.getAcceptHeaderMediaTypes(), otherKey.getAcceptHeaderMediaTypes());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
result = otherKey.getMethods().size() - key.getMethods().size();
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int comparePatterns(Set<String> patterns, Set<String> otherPatterns) {
|
||||
Iterator<String> iterator = patterns.iterator();
|
||||
Iterator<String> iteratorOther = otherPatterns.iterator();
|
||||
while (iterator.hasNext() && iteratorOther.hasNext()) {
|
||||
int result = patternComparator.compare(iterator.next(), iteratorOther.next());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (iterator.hasNext()) {
|
||||
return -1;
|
||||
}
|
||||
else if (iteratorOther.hasNext()) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int compareAcceptHeaders(List<MediaType> accept, List<MediaType> otherAccept) {
|
||||
for (MediaType requestAccept : this.requestAcceptHeader) {
|
||||
int pos1 = indexOfIncluded(requestAccept, accept);
|
||||
int pos2 = indexOfIncluded(requestAccept, otherAccept);
|
||||
if (pos1 != pos2) {
|
||||
return pos2 - pos1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int indexOfIncluded(MediaType requestAccept, List<MediaType> accept) {
|
||||
for (int i = 0; i < accept.size(); i++) {
|
||||
if (requestAccept.includes(accept.get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.method.annotation.InitBinderMethodDataBinderFactory;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
|
||||
/**
|
||||
* An {@link InitBinderMethodDataBinderFactory} for Servlet environments.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ServletInitBinderMethodDataBinderFactory extends InitBinderMethodDataBinderFactory {
|
||||
|
||||
/**
|
||||
* Create an {@link ServletInitBinderMethodDataBinderFactory} instance.
|
||||
* @param initBinderMethods init binder methods to use to initialize new data binders.
|
||||
* @param bindingInitializer a WebBindingInitializer to use to initialize created data binder instances.
|
||||
*/
|
||||
public ServletInitBinderMethodDataBinderFactory(List<InvocableHandlerMethod> initBinderMethods,
|
||||
WebBindingInitializer bindingInitializer) {
|
||||
super(initBinderMethods, bindingInitializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} creates a Servlet data binder.
|
||||
*/
|
||||
@Override
|
||||
protected WebDataBinder createBinderInstance(Object target, String objectName) {
|
||||
return new ServletRequestDataBinder(target, objectName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodProcessor;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerContainer;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.View;
|
||||
|
||||
/**
|
||||
* Extends {@link InvocableHandlerMethod} with the ability to handle the return value of the invocation
|
||||
* resulting in a {@link ModelAndView} according to the {@link HandlerAdapter} contract.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
|
||||
|
||||
private HttpStatus responseStatus;
|
||||
|
||||
private String responseReason;
|
||||
|
||||
private HandlerMethodReturnValueHandlerContainer returnValueHandlers;
|
||||
|
||||
public void setReturnValueHandlers(HandlerMethodReturnValueHandlerContainer returnValueHandlers) {
|
||||
this.returnValueHandlers = returnValueHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ServletInvocableHandlerMethod} instance with the given bean and method.
|
||||
* @param handler the object handler
|
||||
* @param method the method
|
||||
*/
|
||||
public ServletInvocableHandlerMethod(Object handler, Method method) {
|
||||
super(handler, method);
|
||||
|
||||
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
|
||||
if (annotation != null) {
|
||||
this.responseStatus = annotation.value();
|
||||
this.responseReason = annotation.reason();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the method via {@link #invokeForRequest(NativeWebRequest, ModelMap, Object...)} and also handles the
|
||||
* return value by invoking one of the {@link HandlerMethodReturnValueHandler} instances registered via
|
||||
* {@link #setReturnValueHandlers(HandlerMethodReturnValueHandlerContainer)}.
|
||||
* If the method is annotated with {@link SessionStatus} the response status will be set.
|
||||
* @param request the current request
|
||||
* @param model the model used throughout the current request
|
||||
* @param providedArgs argument values to use as-is if they match to a method parameter's type
|
||||
* @return ModelAndView object with the name of the view and the required model data, or <code>null</code>
|
||||
* if the response was handled
|
||||
*/
|
||||
public final ModelAndView invokeAndHandle(NativeWebRequest request,
|
||||
ModelMap model,
|
||||
Object... providedArgs) throws Exception {
|
||||
|
||||
if (!returnValueHandlers.supportsReturnType(getReturnType())) {
|
||||
throw new IllegalStateException("No suitable HandlerMethodReturnValueHandler for method " + toString());
|
||||
}
|
||||
|
||||
Object returnValue = invokeForRequest(request, model, providedArgs);
|
||||
|
||||
setResponseStatus((ServletWebRequest) request);
|
||||
|
||||
ModelAndViewContainer<View> mavContainer = new ModelAndViewContainer<View>(model);
|
||||
returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);
|
||||
|
||||
return getModelAndView(request, mavContainer, returnValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response status according to the {@link ResponseStatus} annotation.
|
||||
*/
|
||||
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
|
||||
if (this.responseStatus != null) {
|
||||
if (StringUtils.hasText(this.responseReason)) {
|
||||
webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
|
||||
}
|
||||
else {
|
||||
webRequest.getResponse().sendError(this.responseStatus.value());
|
||||
}
|
||||
|
||||
// to be picked up by the RedirectView
|
||||
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ModelAndView} from a {@link ModelAndViewContainer}.
|
||||
*/
|
||||
private ModelAndView getModelAndView(NativeWebRequest request,
|
||||
ModelAndViewContainer<View> mavContainer,
|
||||
Object returnValue) {
|
||||
if (returnValue == null && isResponseHandled(request)) {
|
||||
return null;
|
||||
}
|
||||
else if (returnValueHandlerUsesResponseArgument()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
|
||||
mav.setViewName(mavContainer.getViewName());
|
||||
if (mavContainer.getView() != null) {
|
||||
mav.setView(mavContainer.getView());
|
||||
}
|
||||
return mav;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResponseHandled(NativeWebRequest request) {
|
||||
ServletWebRequest servletRequest = (ServletWebRequest) request;
|
||||
return (servletRequest.isNotModified() || (responseStatus != null) || usesResponseArgument());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any of the underlying {@link HandlerMethodArgumentResolver}s or
|
||||
* {@link HandlerMethodReturnValueHandler}s use the response argument.
|
||||
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
|
||||
*/
|
||||
protected boolean usesResponseArgument() {
|
||||
return (super.usesResponseArgument() || returnValueHandlerUsesResponseArgument());
|
||||
}
|
||||
|
||||
private boolean returnValueHandlerUsesResponseArgument() {
|
||||
return returnValueHandlers.usesResponseArgument(getReturnType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractMessageConverterMethodProcessor
|
||||
implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final HttpMessageConverter<?>[] messageConverters;
|
||||
|
||||
protected AbstractMessageConverterMethodProcessor(HttpMessageConverter<?>... messageConverters) {
|
||||
Assert.notNull(messageConverters, "'messageConverters' must not be null");
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
|
||||
MethodParameter methodParam,
|
||||
Class<T> paramType)
|
||||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
HttpInputMessage inputMessage = createInputMessage(webRequest);
|
||||
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
|
||||
String paramName = methodParam.getParameterName();
|
||||
if (paramName != null) {
|
||||
builder.append(' ');
|
||||
builder.append(paramName);
|
||||
}
|
||||
throw new HttpMediaTypeNotSupportedException("Cannot read parameter (" + builder.toString() +
|
||||
") using HttpMessageConverters: no Content-Type found in HTTP request");
|
||||
}
|
||||
|
||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||
if (this.messageConverters != null) {
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||
if (messageConverter.canRead(paramType, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
}
|
||||
return ((HttpMessageConverter<T>) messageConverter).read(paramType, inputMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
|
||||
}
|
||||
|
||||
protected abstract HttpInputMessage createInputMessage(NativeWebRequest webRequest);
|
||||
|
||||
protected void writeWithMessageConverters(NativeWebRequest webRequest, Object returnValue)
|
||||
throws IOException, HttpMediaTypeNotAcceptableException {
|
||||
writeWithMessageConverters(returnValue, createInputMessage(webRequest), createOutputMessage(webRequest));
|
||||
}
|
||||
|
||||
protected abstract HttpOutputMessage createOutputMessage(NativeWebRequest webRequest);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> void writeWithMessageConverters(T returnValue,
|
||||
HttpInputMessage inputMessage,
|
||||
HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMediaTypeNotAcceptableException {
|
||||
|
||||
List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(inputMessage);
|
||||
|
||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||
if (this.messageConverters != null) {
|
||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (!messageConverter.canWrite(returnValue.getClass(), acceptedMediaType)) {
|
||||
continue;
|
||||
}
|
||||
((HttpMessageConverter<T>) messageConverter).write(returnValue, acceptedMediaType, outputMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
contentType = acceptedMediaType;
|
||||
}
|
||||
logger.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||
}
|
||||
}
|
||||
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
|
||||
}
|
||||
|
||||
private List<MediaType> getAcceptedMediaTypes(HttpInputMessage inputMessage) {
|
||||
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
||||
if (acceptedMediaTypes.isEmpty()) {
|
||||
acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
|
||||
}
|
||||
|
||||
MediaType.sortByQualityValue(acceptedMediaTypes);
|
||||
return acceptedMediaTypes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.annotation.ModelFactory;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
||||
|
||||
/**
|
||||
* A catch-all {@link HandlerMethodReturnValueHandler} to handle return values not handled by any other return
|
||||
* value handler.
|
||||
*
|
||||
* <p>This handler should always be last in the order as {@link #supportsReturnType(MethodParameter)} always returns
|
||||
* {@code true}. An attempt is made to handle the return value through a custom {@link ModelAndViewResolver}s or
|
||||
* otherwise by treating it as a single model attribute.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DefaultMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
private final ModelAndViewResolver[] customModelAndViewResolvers;
|
||||
|
||||
public DefaultMethodReturnValueHandler(ModelAndViewResolver[] customResolvers) {
|
||||
this.customModelAndViewResolvers = (customResolvers != null) ? customResolvers : new ModelAndViewResolver[] {};
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
for (ModelAndViewResolver resolver : this.customModelAndViewResolvers) {
|
||||
Class<?> handlerType = returnType.getDeclaringClass();
|
||||
Method method = returnType.getMethod();
|
||||
ExtendedModelMap extModel = (ExtendedModelMap) mavContainer.getModel();
|
||||
ModelAndView mav = resolver.resolveModelAndView(method, handlerType, returnValue, extModel, webRequest);
|
||||
if (mav != ModelAndViewResolver.UNRESOLVED) {
|
||||
mavContainer.setView((V) mav.getView());
|
||||
mavContainer.setViewName(mav.getViewName());
|
||||
mavContainer.addModelAttributes(mav.getModel());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (returnValue == null) {
|
||||
return;
|
||||
}
|
||||
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
|
||||
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
|
||||
mavContainer.addModelAttribute(name, returnValue);
|
||||
}
|
||||
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} and {@link HandlerMethodReturnValueHandler}
|
||||
* that supports {@link HttpEntity} and {@link ResponseEntity}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
|
||||
|
||||
public HttpEntityMethodProcessor(HttpMessageConverter<?>... messageConverters) {
|
||||
super(messageConverters);
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
return HttpEntity.class.equals(parameterType);
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
Class<?> parameterType = returnType.getParameterType();
|
||||
return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType);
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameterOrReturnType) {
|
||||
// only when HttpEntity or ResponseEntity is used as a return type
|
||||
return parameterOrReturnType.getParameterIndex() == -1;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory)
|
||||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
Class<?> paramType = getHttpEntityType(parameter);
|
||||
Object body = readWithMessageConverters(webRequest, parameter, paramType);
|
||||
HttpInputMessage inputMessage = createInputMessage(webRequest);
|
||||
return new HttpEntity<Object>(body, inputMessage.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
return new ServletServerHttpRequest(servletRequest);
|
||||
}
|
||||
|
||||
private Class<?> getHttpEntityType(MethodParameter methodParam) {
|
||||
Assert.isAssignable(HttpEntity.class, methodParam.getParameterType());
|
||||
ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType();
|
||||
if (type.getActualTypeArguments().length == 1) {
|
||||
Type typeArgument = type.getActualTypeArguments()[0];
|
||||
if (typeArgument instanceof Class) {
|
||||
return (Class<?>) typeArgument;
|
||||
}
|
||||
else if (typeArgument instanceof GenericArrayType) {
|
||||
Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
|
||||
if (componentType instanceof Class) {
|
||||
// Surely, there should be a nicer way to do this
|
||||
Object array = Array.newInstance((Class<?>) componentType, 0);
|
||||
return array.getClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");
|
||||
|
||||
}
|
||||
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
if (returnValue == null) {
|
||||
return;
|
||||
}
|
||||
Assert.isInstanceOf(HttpEntity.class, returnValue);
|
||||
HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
|
||||
HttpOutputMessage outputMessage = createOutputMessage(webRequest);
|
||||
if (responseEntity instanceof ResponseEntity) {
|
||||
((ServerHttpResponse) outputMessage).setStatusCode(((ResponseEntity<?>) responseEntity).getStatusCode());
|
||||
}
|
||||
HttpHeaders entityHeaders = responseEntity.getHeaders();
|
||||
if (!entityHeaders.isEmpty()) {
|
||||
outputMessage.getHeaders().putAll(entityHeaders);
|
||||
}
|
||||
Object body = responseEntity.getBody();
|
||||
if (body != null) {
|
||||
writeWithMessageConverters(body, createInputMessage(webRequest), outputMessage);
|
||||
}
|
||||
else {
|
||||
// flush headers to the HttpServletResponse
|
||||
outputMessage.getBody();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpOutputMessage createOutputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
|
||||
return new ServletServerHttpResponse(servletResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Handles {@link ModelAndView} return values.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
ModelAndView mav = (ModelAndView) returnValue;
|
||||
mavContainer.setView((V) mav.getView());
|
||||
mavContainer.setViewName(mav.getViewName());
|
||||
mavContainer.addModelAttributes(mav.getModel());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.method.annotation.support.AbstractNamedValueMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with
|
||||
* {@link PathVariable @PathVariable}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
public PathVariableMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(PathVariable.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class);
|
||||
return new PathVariableNamedValueInfo(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, MethodParameter parameter, String name)
|
||||
throws Exception {
|
||||
Map<String, String> uriTemplateVariables = (Map<String, String>) webRequest.getAttribute(
|
||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||
return (uriTemplateVariables != null) ? uriTemplateVariables.get(name) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
|
||||
throw new IllegalStateException("Could not find @PathVariable [" + name + "] in @RequestMapping");
|
||||
}
|
||||
|
||||
private static class PathVariableNamedValueInfo extends NamedValueInfo {
|
||||
|
||||
private PathVariableNamedValueInfo(PathVariable annotation) {
|
||||
super(annotation.value(), true, ValueConstants.DEFAULT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} and {@link HandlerMethodReturnValueHandler} that supports
|
||||
* parameters annotated with {@link RequestBody} and return values annotated with {@link ResponseBody}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
|
||||
|
||||
public RequestResponseBodyMethodProcessor(HttpMessageConverter<?>... messageConverters) {
|
||||
super(messageConverters);
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestBody.class);
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return returnType.getMethodAnnotation(ResponseBody.class) != null;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameterOrReturnType) {
|
||||
return parameterOrReturnType.getParameterIndex() == -1 &&
|
||||
parameterOrReturnType.getMethodAnnotation(ResponseBody.class) != null;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory)
|
||||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
return readWithMessageConverters(webRequest, parameter, parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
return new ServletServerHttpRequest(servletRequest);
|
||||
}
|
||||
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest)
|
||||
throws IOException, HttpMediaTypeNotAcceptableException {
|
||||
if (returnValue != null) {
|
||||
writeWithMessageConverters(webRequest, returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpOutputMessage createOutputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
|
||||
return new ServletServerHttpResponse(servletResponse);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.annotation.support.CookieValueMethodArgumentResolver;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* A {@link CookieValueMethodArgumentResolver} for Servlet environments.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ServletCookieValueMethodArgumentResolver extends CookieValueMethodArgumentResolver {
|
||||
|
||||
public ServletCookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String cookieName) throws Exception {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
|
||||
if (Cookie.class.isAssignableFrom(parameter.getParameterType())) {
|
||||
return cookieValue;
|
||||
}
|
||||
else if (cookieValue != null) {
|
||||
return getUrlPathHelper().decodeRequestString(servletRequest, cookieValue.getValue());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
||||
|
||||
/**
|
||||
* A {@link ModelAttributeMethodProcessor} for Servlet environments.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
|
||||
|
||||
/**
|
||||
* Creates a {@link ServletModelAttributeMethodProcessor} instance.
|
||||
* @param resolveWithoutAnnotations enable default resolution mode in which parameters without
|
||||
* annotations that aren't simple types (see {@link BeanUtils#isSimpleProperty(Class)})
|
||||
* are also treated as model attributes with a default name based on the model attribute type.
|
||||
*/
|
||||
public ServletModelAttributeMethodProcessor(boolean resolveWithoutAnnotations) {
|
||||
super(resolveWithoutAnnotations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects the data binder to be an instance of {@link ServletRequestDataBinder}.
|
||||
*/
|
||||
@Override
|
||||
protected void doBind(WebDataBinder binder, NativeWebRequest request) {
|
||||
ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
|
||||
((ServletRequestDataBinder) binder).bind(servletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.security.Principal;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link ServletRequest} and related arguments.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
return ServletRequest.class.isAssignableFrom(parameterType) ||
|
||||
MultipartRequest.class.isAssignableFrom(parameterType) ||
|
||||
HttpSession.class.isAssignableFrom(parameterType) || Principal.class.isAssignableFrom(parameterType) ||
|
||||
Locale.class.equals(parameterType) || InputStream.class.isAssignableFrom(parameterType) ||
|
||||
Reader.class.isAssignableFrom(parameterType);
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws IOException {
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
|
||||
if (ServletRequest.class.isAssignableFrom(parameterType) ||
|
||||
MultipartRequest.class.isAssignableFrom(parameterType)) {
|
||||
Object nativeRequest = webRequest.getNativeRequest(parameterType);
|
||||
if (nativeRequest == null) {
|
||||
throw new IllegalStateException(
|
||||
"Current request is not of type [" + parameterType.getName() + "]: " + request);
|
||||
}
|
||||
return nativeRequest;
|
||||
}
|
||||
else if (HttpSession.class.isAssignableFrom(parameterType)) {
|
||||
return request.getSession();
|
||||
}
|
||||
else if (Principal.class.isAssignableFrom(parameterType)) {
|
||||
return request.getUserPrincipal();
|
||||
}
|
||||
else if (Locale.class.equals(parameterType)) {
|
||||
return RequestContextUtils.getLocale(request);
|
||||
}
|
||||
else if (InputStream.class.isAssignableFrom(parameterType)) {
|
||||
return request.getInputStream();
|
||||
}
|
||||
else if (Reader.class.isAssignableFrom(parameterType)) {
|
||||
return request.getReader();
|
||||
}
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link ServletResponse} and related arguments.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
return ServletResponse.class.isAssignableFrom(parameterType) ||
|
||||
OutputStream.class.isAssignableFrom(parameterType) || Writer.class.isAssignableFrom(parameterType);
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws IOException {
|
||||
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
|
||||
if (ServletResponse.class.isAssignableFrom(parameterType)) {
|
||||
Object nativeResponse = webRequest.getNativeResponse(parameterType);
|
||||
if (nativeResponse == null) {
|
||||
throw new IllegalStateException(
|
||||
"Current response is not of type [" + parameterType.getName() + "]: " + response);
|
||||
}
|
||||
return nativeResponse;
|
||||
}
|
||||
else if (OutputStream.class.isAssignableFrom(parameterType)) {
|
||||
return response.getOutputStream();
|
||||
}
|
||||
else if (Writer.class.isAssignableFrom(parameterType)) {
|
||||
return response.getWriter();
|
||||
}
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.View;
|
||||
|
||||
/**
|
||||
* Handles {@link View} and view name return values.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
Class<?> paramType = returnType.getParameterType();
|
||||
return (View.class.isAssignableFrom(paramType) || (String.class.equals(paramType)));
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
if (returnValue instanceof String) {
|
||||
mavContainer.setViewName((String) returnValue);
|
||||
}
|
||||
else {
|
||||
V view = (V) returnValue;
|
||||
mavContainer.setView(view);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Comparator;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test for {@link AbstractHandlerMethodMapping}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class HandlerMethodMappingTests {
|
||||
|
||||
private AbstractHandlerMethodMapping<String> mapping;
|
||||
|
||||
private HandlerMethod handlerMethod1;
|
||||
|
||||
private HandlerMethod handlerMethod2;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mapping = new MyHandlerMethodMapping();
|
||||
MyHandler handler = new MyHandler();
|
||||
handlerMethod1 = new HandlerMethod(handler, "handlerMethod1");
|
||||
handlerMethod2 = new HandlerMethod(handler, "handlerMethod2");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void registerDuplicates() {
|
||||
mapping.registerHandlerMethod("foo", handlerMethod1);
|
||||
mapping.registerHandlerMethod("foo", handlerMethod2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void directMatch() throws Exception {
|
||||
String key = "foo";
|
||||
mapping.registerHandlerMethod(key, handlerMethod1);
|
||||
|
||||
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", key));
|
||||
assertEquals(handlerMethod1, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternMatch() throws Exception {
|
||||
mapping.registerHandlerMethod("/fo*", handlerMethod1);
|
||||
mapping.registerHandlerMethod("/f*", handlerMethod1);
|
||||
|
||||
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
|
||||
assertEquals(handlerMethod1, result);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void ambiguousMatch() throws Exception {
|
||||
mapping.registerHandlerMethod("/f?o", handlerMethod1);
|
||||
mapping.registerHandlerMethod("/fo?", handlerMethod2);
|
||||
|
||||
mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
|
||||
}
|
||||
|
||||
private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<String> {
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
@Override
|
||||
protected String getKeyForRequest(HttpServletRequest request) throws Exception {
|
||||
return urlPathHelper.getLookupPathForRequest(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMatchingKey(String pattern, HttpServletRequest request) {
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
|
||||
return pathMatcher.match(pattern, lookupPath) ? pattern : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKeyForMethod(Method method) {
|
||||
String methodName = method.getName();
|
||||
return methodName.startsWith("handler") ? methodName : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comparator<String> getKeyComparator(HttpServletRequest request) {
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
|
||||
return pathMatcher.getPatternComparator(lookupPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isHandler(String beanName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handlerMethod1() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handlerMethod2() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.support.DefaultDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.method.annotation.support.ModelAttributeMethodProcessor;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverContainer;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerContainer;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
||||
|
||||
/**
|
||||
* Test various scenarios for detecting method-level and method parameter annotations depending
|
||||
* on where they are located -- on interfaces, parent classes, in parameterized methods, or in
|
||||
* combination with proxies.
|
||||
*
|
||||
* Note the following:
|
||||
* <ul>
|
||||
* <li>Parameterized methods cannot be used in combination with JDK dynamic proxies since the
|
||||
* proxy interface does not contain the bridged methods that need to be invoked.
|
||||
* <li>When using JDK dynamic proxies, the proxied interface must contain all required method
|
||||
* and method parameter annotations.
|
||||
* <li>Method-level annotations can be placed on super types (interface or parent class) while
|
||||
* method parameter annotations must be present on the method being invoked.
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class ControllerMethodAnnotationDetectionTests {
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> handlerTypes() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ new MappingIfcController(), false },
|
||||
{ new MappingAbstractClassController(), false },
|
||||
{ new ParameterizedIfcController(), false },
|
||||
{ new MappingParameterizedIfcController(), false },
|
||||
{ new MappingIfcProxyController(), true },
|
||||
{ new PlainController(), true },
|
||||
{ new MappingAbstractClassController(), true }
|
||||
});
|
||||
}
|
||||
|
||||
private Object handler;
|
||||
|
||||
private boolean useAutoProxy;
|
||||
|
||||
public ControllerMethodAnnotationDetectionTests(Object handler, boolean useAutoProxy) {
|
||||
this.handler = handler;
|
||||
this.useAutoProxy = useAutoProxy;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeModelAttributeMethod() throws Exception {
|
||||
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handler, useAutoProxy);
|
||||
|
||||
ModelAttribute annot = requestMappingMethod.getMethodAnnotation(ModelAttribute.class);
|
||||
assertEquals("Failed to detect method annotation", "attrName", annot.value());
|
||||
|
||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
|
||||
NativeWebRequest webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
|
||||
servletRequest.setParameter("name", "Chad");
|
||||
|
||||
ModelMap model = new ExtendedModelMap();
|
||||
ModelAndView mav = requestMappingMethod.invokeAndHandle(webRequest, model);
|
||||
|
||||
Object modelAttr = mav.getModelMap().get("attrName");
|
||||
|
||||
assertEquals(TestBean.class, modelAttr.getClass());
|
||||
assertEquals("Chad", ((TestBean) modelAttr).getName());
|
||||
}
|
||||
|
||||
private ServletInvocableHandlerMethod createRequestMappingMethod(Object handler, boolean useAutoProxy) {
|
||||
if (useAutoProxy) {
|
||||
handler = getProxyBean(handler);
|
||||
}
|
||||
HandlerMethodArgumentResolverContainer argResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
argResolvers.registerArgumentResolver(new ModelAttributeMethodProcessor(false));
|
||||
|
||||
HandlerMethodReturnValueHandlerContainer handlers = new HandlerMethodReturnValueHandlerContainer();
|
||||
handlers.registerReturnValueHandler(new ModelAttributeMethodProcessor(false));
|
||||
handlers.registerReturnValueHandler(new DefaultMethodReturnValueHandler(null));
|
||||
|
||||
Class<?> handlerType = ClassUtils.getUserClass(handler.getClass());
|
||||
Set<Method> methods = HandlerMethodSelector.selectMethods(handlerType, REQUEST_MAPPING_METHODS);
|
||||
Method method = methods.iterator().next();
|
||||
|
||||
ServletInvocableHandlerMethod attrMethod = new ServletInvocableHandlerMethod(handler, method);
|
||||
attrMethod.setArgumentResolverContainer(argResolvers);
|
||||
attrMethod.setReturnValueHandlers(handlers);
|
||||
attrMethod.setDataBinderFactory(new DefaultDataBinderFactory(null));
|
||||
|
||||
return attrMethod;
|
||||
}
|
||||
|
||||
private Object getProxyBean(Object handler) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(handler.getClass()));
|
||||
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
autoProxyCreator.setBeanFactory(wac.getBeanFactory());
|
||||
wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
|
||||
wac.getBeanFactory().registerSingleton("advsr", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
|
||||
wac.refresh();
|
||||
|
||||
return wac.getBean("controller");
|
||||
}
|
||||
|
||||
public static MethodFilter REQUEST_MAPPING_METHODS = new MethodFilter() {
|
||||
|
||||
public boolean matches(Method method) {
|
||||
return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null;
|
||||
}
|
||||
};
|
||||
|
||||
private interface MappingIfc {
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
TestBean model(TestBean input);
|
||||
}
|
||||
|
||||
private static class MappingIfcController implements MappingIfc {
|
||||
public TestBean model(@ModelAttribute TestBean input) {
|
||||
return new TestBean(input.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private interface MappingProxyIfc {
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
TestBean model(@ModelAttribute TestBean input);
|
||||
}
|
||||
|
||||
private static class MappingIfcProxyController implements MappingProxyIfc {
|
||||
@ModelAttribute("attrName")
|
||||
public TestBean model(@ModelAttribute TestBean input) {
|
||||
return new TestBean(input.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class MappingAbstractClass {
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
TestBean model(TestBean input) {
|
||||
return new TestBean(input.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static class MappingAbstractClassController extends MappingAbstractClass {
|
||||
public TestBean model(@ModelAttribute TestBean input) {
|
||||
TestBean testBean = super.model(input);
|
||||
testBean.setAge(14);
|
||||
return testBean;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ParameterizedIfc<TB, S> {
|
||||
TB model(S input);
|
||||
}
|
||||
|
||||
public static class ParameterizedIfcController implements ParameterizedIfc<TestBean, TestBean> {
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
public TestBean model(@ModelAttribute TestBean input) {
|
||||
return new TestBean(input.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public interface MappingParameterizedIfc<TB, S> {
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
TB model(S input);
|
||||
}
|
||||
|
||||
public static class MappingParameterizedIfcController implements MappingParameterizedIfc<TestBean, TestBean> {
|
||||
public TestBean model(@ModelAttribute TestBean input) {
|
||||
return new TestBean(input.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public interface MappingParameterizedProxyIfc<TB, S> {
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
TB model(@ModelAttribute("inputName") S input);
|
||||
}
|
||||
|
||||
public static class MappingParameterizedProxyIfcController implements MappingParameterizedProxyIfc<TestBean, TestBean> {
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
public TestBean model(@ModelAttribute TestBean input) {
|
||||
return new TestBean(input.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlainController {
|
||||
public PlainController() {
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
@ModelAttribute("attrName")
|
||||
public TestBean model(@ModelAttribute TestBean input) {
|
||||
return new TestBean(input.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestCondition;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestConditionFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestConditionFactoryTests {
|
||||
|
||||
@Test
|
||||
public void paramEquals() {
|
||||
assertEquals(getSingleParamCondition("foo"), getSingleParamCondition("foo"));
|
||||
assertFalse(getSingleParamCondition("foo").equals(getSingleParamCondition("bar")));
|
||||
assertFalse(getSingleParamCondition("foo").equals(getSingleParamCondition("FOO")));
|
||||
assertEquals(getSingleParamCondition("foo=bar"), getSingleParamCondition("foo=bar"));
|
||||
assertFalse(getSingleParamCondition("foo=bar").equals(getSingleParamCondition("FOO=bar")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerEquals() {
|
||||
assertEquals(getSingleHeaderCondition("foo"), getSingleHeaderCondition("foo"));
|
||||
assertEquals(getSingleHeaderCondition("foo"), getSingleHeaderCondition("FOO"));
|
||||
assertFalse(getSingleHeaderCondition("foo").equals(getSingleHeaderCondition("bar")));
|
||||
assertEquals(getSingleHeaderCondition("foo=bar"), getSingleHeaderCondition("foo=bar"));
|
||||
assertEquals(getSingleHeaderCondition("foo=bar"), getSingleHeaderCondition("FOO=bar"));
|
||||
assertEquals(getSingleHeaderCondition("content-type=text/xml"),
|
||||
getSingleHeaderCondition("Content-Type=TEXT/XML"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerPresent() {
|
||||
RequestCondition condition = getSingleHeaderCondition("accept");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("Accept", "");
|
||||
|
||||
assertTrue(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerPresentNoMatch() {
|
||||
RequestCondition condition = getSingleHeaderCondition("foo");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("bar", "");
|
||||
|
||||
assertFalse(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerNotPresent() {
|
||||
RequestCondition condition = getSingleHeaderCondition("!accept");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
|
||||
assertTrue(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerValueMatch() {
|
||||
RequestCondition condition = getSingleHeaderCondition("foo=bar");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("foo", "bar");
|
||||
|
||||
assertTrue(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerValueNoMatch() {
|
||||
RequestCondition condition = getSingleHeaderCondition("foo=bar");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("foo", "bazz");
|
||||
|
||||
assertFalse(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerCaseSensitiveValueMatch() {
|
||||
RequestCondition condition = getSingleHeaderCondition("foo=Bar");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("foo", "bar");
|
||||
|
||||
assertFalse(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerValueMatchNegated() {
|
||||
RequestCondition condition = getSingleHeaderCondition("foo!=bar");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("foo", "baz");
|
||||
|
||||
assertTrue(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaTypeHeaderValueMatch() {
|
||||
RequestCondition condition = getSingleHeaderCondition("accept=text/html");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("Accept", "text/html");
|
||||
|
||||
assertTrue(condition.match(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaTypeHeaderValueMatchNegated() {
|
||||
RequestCondition condition = getSingleHeaderCondition("accept!=text/html");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("Accept", "application/html");
|
||||
|
||||
assertTrue(condition.match(request));
|
||||
}
|
||||
|
||||
private RequestCondition getSingleHeaderCondition(String expression) {
|
||||
Set<RequestCondition> conditions = RequestConditionFactory.parseHeaders(expression);
|
||||
assertEquals(1, conditions.size());
|
||||
return conditions.iterator().next();
|
||||
}
|
||||
|
||||
private RequestCondition getSingleParamCondition(String expression) {
|
||||
Set<RequestCondition> conditions = RequestConditionFactory.parseParams(expression);
|
||||
assertEquals(1, conditions.size());
|
||||
return conditions.iterator().next();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestConditionFactory;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestKey;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestKeyComparatorTests {
|
||||
|
||||
private RequestMappingHandlerMethodMapping handlerMapping;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.handlerMapping = new RequestMappingHandlerMethodMapping();
|
||||
this.request = new MockHttpServletRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moreSpecificPatternWins() {
|
||||
request.setRequestURI("/foo");
|
||||
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);
|
||||
RequestKey key1 = new RequestKey(asList("/fo*"), null, null, null);
|
||||
RequestKey key2 = new RequestKey(asList("/foo"), null, null, null);
|
||||
|
||||
assertEquals(1, comparator.compare(key1, key2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalPatterns() {
|
||||
request.setRequestURI("/foo");
|
||||
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);
|
||||
RequestKey key1 = new RequestKey(asList("/foo*"), null, null, null);
|
||||
RequestKey key2 = new RequestKey(asList("/foo*"), null, null, null);
|
||||
|
||||
assertEquals(0, comparator.compare(key1, key2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void greaterNumberOfMatchingPatternsWins() throws Exception {
|
||||
request.setRequestURI("/foo.html");
|
||||
RequestKey key1 = new RequestKey(asList("/foo", "*.jpeg"), null, null, null);
|
||||
RequestKey key2 = new RequestKey(asList("/foo", "*.html"), null, null, null);
|
||||
RequestKey match1 = handlerMapping.getMatchingKey(key1, request);
|
||||
RequestKey match2 = handlerMapping.getMatchingKey(key2, request);
|
||||
List<RequestKey> matches = asList(match1, match2);
|
||||
Collections.sort(matches, handlerMapping.getKeyComparator(request));
|
||||
|
||||
assertSame(match2.getPatterns(), matches.get(0).getPatterns());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneMethodWinsOverNone() {
|
||||
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);
|
||||
RequestKey key1 = new RequestKey(null, null, null, null);
|
||||
RequestKey key2 = new RequestKey(null, asList(RequestMethod.GET), null, null);
|
||||
|
||||
assertEquals(1, comparator.compare(key1, key2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodsAndParams() {
|
||||
RequestKey empty = new RequestKey(null, null, null, null);
|
||||
RequestKey oneMethod = new RequestKey(null, asList(RequestMethod.GET), null, null);
|
||||
RequestKey oneMethodOneParam =
|
||||
new RequestKey(null, asList(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null);
|
||||
List<RequestKey> list = asList(empty, oneMethod, oneMethodOneParam);
|
||||
Collections.shuffle(list);
|
||||
Collections.sort(list, handlerMapping.getKeyComparator(request));
|
||||
|
||||
assertEquals(oneMethodOneParam, list.get(0));
|
||||
assertEquals(oneMethod, list.get(1));
|
||||
assertEquals(empty, list.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO : remove ignore
|
||||
public void acceptHeaders() {
|
||||
RequestKey html = new RequestKey(null, null, null, RequestConditionFactory.parseHeaders("accept=text/html"));
|
||||
RequestKey xml = new RequestKey(null, null, null, RequestConditionFactory.parseHeaders("accept=application/xml"));
|
||||
RequestKey none = new RequestKey(null, null, null, null);
|
||||
|
||||
request.addHeader("Accept", "application/xml, text/html");
|
||||
Comparator<RequestKey> comparator = handlerMapping.getKeyComparator(request);
|
||||
|
||||
assertTrue(comparator.compare(html, xml) > 0);
|
||||
assertTrue(comparator.compare(xml, html) < 0);
|
||||
assertTrue(comparator.compare(xml, none) < 0);
|
||||
assertTrue(comparator.compare(none, xml) > 0);
|
||||
assertTrue(comparator.compare(html, none) < 0);
|
||||
assertTrue(comparator.compare(none, html) > 0);
|
||||
|
||||
request = new MockHttpServletRequest();
|
||||
request.addHeader("Accept", "application/xml, text/*");
|
||||
comparator = handlerMapping.getKeyComparator(request);
|
||||
|
||||
assertTrue(comparator.compare(html, xml) > 0);
|
||||
assertTrue(comparator.compare(xml, html) < 0);
|
||||
|
||||
request = new MockHttpServletRequest();
|
||||
request.addHeader("Accept", "application/pdf");
|
||||
comparator = handlerMapping.getKeyComparator(request);
|
||||
|
||||
assertTrue(comparator.compare(html, xml) == 0);
|
||||
assertTrue(comparator.compare(xml, html) == 0);
|
||||
|
||||
// See SPR-7000
|
||||
request = new MockHttpServletRequest();
|
||||
request.addHeader("Accept", "text/html;q=0.9,application/xml");
|
||||
comparator = handlerMapping.getKeyComparator(request);
|
||||
|
||||
assertTrue(comparator.compare(html, xml) > 0);
|
||||
assertTrue(comparator.compare(xml, html) < 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestConditionFactory;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestKey;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestKeyTests {
|
||||
|
||||
@Test
|
||||
public void equals() {
|
||||
RequestKey key1 = new RequestKey(asList("/foo"), asList(GET), null, null);
|
||||
RequestKey key2 = new RequestKey(asList("/foo"), asList(GET), null, null);
|
||||
|
||||
assertEquals(key1, key2);
|
||||
assertEquals(key1.hashCode(), key2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsPrependSlash() {
|
||||
RequestKey key1 = new RequestKey(asList("/foo"), asList(GET), null, null);
|
||||
RequestKey key2 = new RequestKey(asList("foo"), asList(GET), null, null);
|
||||
|
||||
assertEquals(key1, key2);
|
||||
assertEquals(key1.hashCode(), key2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combinePatterns() {
|
||||
AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
RequestKey key1 = createKeyFromPatterns("/t1", "/t2");
|
||||
RequestKey key2 = createKeyFromPatterns("/m1", "/m2");
|
||||
RequestKey key3 = createKeyFromPatterns("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2");
|
||||
assertEquals(key3, key1.combine(key2, pathMatcher));
|
||||
|
||||
key1 = createKeyFromPatterns("/t1");
|
||||
key2 = createKeyFromPatterns(new String[] {});
|
||||
key3 = createKeyFromPatterns("/t1");
|
||||
assertEquals(key3, key1.combine(key2, pathMatcher));
|
||||
|
||||
key1 = createKeyFromPatterns(new String[] {});
|
||||
key2 = createKeyFromPatterns("/m1");
|
||||
key3 = createKeyFromPatterns("/m1");
|
||||
assertEquals(key3, key1.combine(key2, pathMatcher));
|
||||
|
||||
key1 = createKeyFromPatterns(new String[] {});
|
||||
key2 = createKeyFromPatterns(new String[] {});
|
||||
key3 = createKeyFromPatterns("/");
|
||||
assertEquals(key3, key1.combine(key2, pathMatcher));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchPatternsToRequest() {
|
||||
UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
RequestKey key = new RequestKey(asList("/foo"), null, null, null);
|
||||
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
request = new MockHttpServletRequest("GET", "/foo/bar");
|
||||
key = new RequestKey(asList("/foo/*"), null, null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull("Pattern match", match);
|
||||
|
||||
request = new MockHttpServletRequest("GET", "/foo.html");
|
||||
key = new RequestKey(asList("/foo"), null, null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull("Implicit match by extension", match);
|
||||
assertEquals("Contains matched pattern", "/foo.*", match.getPatterns().iterator().next());
|
||||
|
||||
request = new MockHttpServletRequest("GET", "/foo/");
|
||||
key = new RequestKey(asList("/foo"), null, null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull("Implicit match by trailing slash", match);
|
||||
assertEquals("Contains matched pattern", "/foo/", match.getPatterns().iterator().next());
|
||||
|
||||
request = new MockHttpServletRequest("GET", "/foo.html");
|
||||
key = new RequestKey(asList("/foo.jpg"), null, null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNull("Implicit match ignored if pattern has extension", match);
|
||||
|
||||
request = new MockHttpServletRequest("GET", "/foo.html");
|
||||
key = new RequestKey(asList("/foo.jpg"), null, null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNull("Implicit match ignored on pattern with trailing slash", match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchRequestMethods() {
|
||||
UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
|
||||
RequestKey key = new RequestKey(asList("/foo"), null, null, null);
|
||||
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull("No method matches any method", match);
|
||||
|
||||
key = new RequestKey(asList("/foo"), asList(GET), null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull("Exact match", match);
|
||||
|
||||
key = new RequestKey(asList("/foo"), asList(POST), null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNull("No match", match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchingKeyContent() {
|
||||
UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
|
||||
RequestKey key = new RequestKey(asList("/foo*", "/bar"), asList(GET, POST), null, null);
|
||||
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
RequestKey expected = new RequestKey(asList("/foo*"), asList(GET), null, null);
|
||||
|
||||
assertEquals("Matching RequestKey contains matched patterns and methods only", expected, match);
|
||||
|
||||
key = new RequestKey(asList("/**", "/foo*", "/foo"), null, null, null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
expected = new RequestKey(asList("/foo", "/foo*", "/**"), null, null, null);
|
||||
|
||||
assertEquals("Matched patterns are sorted with best match at the top", expected, match);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParamConditions() {
|
||||
UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setParameter("foo", "bar");
|
||||
|
||||
RequestKey key = new RequestKey(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null);
|
||||
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
key = new RequestKey(asList("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null);
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderConditions() {
|
||||
UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.addHeader("foo", "bar");
|
||||
|
||||
RequestKey key = new RequestKey(asList("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"));
|
||||
RequestKey match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNotNull(match);
|
||||
|
||||
key = new RequestKey(asList("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"));
|
||||
match = key.getMatchingKey(request, pathMatcher, urlPathHelper);
|
||||
|
||||
assertNull(match);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromServletRequest() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
RequestKey key = RequestKey.createFromServletRequest(request, new UrlPathHelper());
|
||||
assertEquals(new RequestKey(asList("/foo"), asList(RequestMethod.GET), null, null), key);
|
||||
}
|
||||
|
||||
private RequestKey createKeyFromPatterns(String... patterns) {
|
||||
return new RequestKey(asList(patterns), null, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Principal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
/**
|
||||
* A test fixture for higher-level {@link RequestMappingHandlerAdapter} tests.
|
||||
*
|
||||
* <p>The aim here is not to test {@link RequestMappingHandlerAdapter} itself nor to exercise
|
||||
* every {@link Controller @Controller} method feature but to have a place to try any feature
|
||||
* related to {@link Controller @Controller} invocations. Preferably actual tests should be
|
||||
* added to the components that provide that respective functionality.
|
||||
*
|
||||
* <p>The following integration tests for detecting annotations on super types, parameterized
|
||||
* methods, and proxies may also be of interest:
|
||||
* <ul>
|
||||
* <li>{@link RequestMappingHandlerMethodDetectionTests}
|
||||
* <li>{@link ControllerMethodAnnotationDetectionTests}
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestMappingHandlerAdapterIntegrationTests {
|
||||
|
||||
private RequestMappingHandlerAdapter handlerAdapter;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer();
|
||||
bindingInitializer.setValidator(new StubValidator());
|
||||
|
||||
this.handlerAdapter = new RequestMappingHandlerAdapter();
|
||||
this.handlerAdapter.setWebBindingInitializer(bindingInitializer);
|
||||
this.handlerAdapter.setCustomArgumentResolvers(new WebArgumentResolver[] { new ColorArgumentResolver() });
|
||||
|
||||
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||
context.refresh();
|
||||
|
||||
this.handlerAdapter.setApplicationContext(context);
|
||||
this.handlerAdapter.setBeanFactory(context.getBeanFactory());
|
||||
this.handlerAdapter.afterPropertiesSet();
|
||||
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
|
||||
// Expose request to the current thread (for SpEL expressions)
|
||||
RequestContextHolder.setRequestAttributes(new ServletWebRequest(request));
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMvc() throws Exception {
|
||||
Class<?>[] paramTypes = new Class<?>[] { int.class, String.class, String.class, String.class, Map.class,
|
||||
Date.class, Map.class, String.class, String.class, TestBean.class, Errors.class, TestBean.class,
|
||||
Color.class, HttpServletRequest.class, HttpServletResponse.class, User.class, OtherUser.class,
|
||||
Model.class };
|
||||
|
||||
/* URI template vars (see RequestMappingHandlerMethodMapping) */
|
||||
Map<String, String> uriTemplateVars = new HashMap<String, String>();
|
||||
uriTemplateVars.put("pathvar", "pathvarValue");
|
||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars);
|
||||
|
||||
Date date = new GregorianCalendar(2011, Calendar.MARCH, 16).getTime();
|
||||
String formattedDate = "2011.03.16";
|
||||
|
||||
System.setProperty("systemHeader", "systemHeaderValue");
|
||||
|
||||
request.setCookies(new Cookie("cookie", "99"));
|
||||
request.addHeader("header", "headerValue");
|
||||
request.addHeader("anotherHeader", "anotherHeaderValue");
|
||||
request.addParameter("datePattern", "yyyy.MM.dd");
|
||||
request.addParameter("dateParam", formattedDate);
|
||||
request.addParameter("paramByConvention", "paramByConventionValue");
|
||||
request.addParameter("age", "25");
|
||||
request.setContextPath("/contextPath");
|
||||
|
||||
request.addHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
request.setContent("Hello World".getBytes("UTF-8"));
|
||||
|
||||
request.setUserPrincipal(new User());
|
||||
|
||||
HandlerMethod handlerMethod = handlerMethod(new RequestMappingHandler(), "handleMvc", paramTypes);
|
||||
ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod);
|
||||
ModelMap model = mav.getModelMap();
|
||||
|
||||
assertEquals("viewName", mav.getViewName());
|
||||
assertEquals(99, model.get("cookie"));
|
||||
assertEquals("pathvarValue", model.get("pathvar"));
|
||||
assertEquals("headerValue", model.get("header"));
|
||||
assertEquals(date, model.get("dateParam"));
|
||||
|
||||
Map<?,?> map = (Map<?,?>) model.get("headerMap");
|
||||
assertEquals("headerValue", map.get("header"));
|
||||
assertEquals("anotherHeaderValue", map.get("anotherHeader"));
|
||||
assertEquals("systemHeaderValue", model.get("systemHeader"));
|
||||
|
||||
map = (Map<?,?>) model.get("paramMap");
|
||||
assertEquals(formattedDate, map.get("dateParam"));
|
||||
assertEquals("paramByConventionValue", map.get("paramByConvention"));
|
||||
|
||||
assertEquals("/contextPath", model.get("value"));
|
||||
|
||||
TestBean modelAttr = (TestBean) model.get("modelAttr");
|
||||
assertEquals(25, modelAttr.getAge());
|
||||
assertEquals("Set by model method [modelAttr]", modelAttr.getName());
|
||||
assertSame(modelAttr, request.getSession().getAttribute("modelAttr"));
|
||||
|
||||
BindingResult bindingResult = (BindingResult) model.get(BindingResult.MODEL_KEY_PREFIX + "modelAttr");
|
||||
assertSame(modelAttr, bindingResult.getTarget());
|
||||
assertEquals(1, bindingResult.getErrorCount());
|
||||
|
||||
String conventionAttrName = "testBean";
|
||||
TestBean modelAttrByConvention = (TestBean) model.get(conventionAttrName);
|
||||
assertEquals(25, modelAttrByConvention.getAge());
|
||||
assertEquals("Set by model method [modelAttrByConvention]", modelAttrByConvention.getName());
|
||||
assertSame(modelAttrByConvention, request.getSession().getAttribute(conventionAttrName));
|
||||
|
||||
bindingResult = (BindingResult) model.get(BindingResult.MODEL_KEY_PREFIX + conventionAttrName);
|
||||
assertSame(modelAttrByConvention, bindingResult.getTarget());
|
||||
|
||||
assertTrue(model.get("customArg") instanceof Color);
|
||||
assertEquals(User.class, model.get("user").getClass());
|
||||
assertEquals(OtherUser.class, model.get("otherUser").getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleRequestBody() throws Exception {
|
||||
Class<?>[] paramTypes = new Class<?>[] { byte[].class };
|
||||
|
||||
request.addHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
request.setContent("Hello Server".getBytes("UTF-8"));
|
||||
|
||||
HandlerMethod handlerMethod = handlerMethod(new RequestMappingHandler(), "handleRequestBody", paramTypes);
|
||||
|
||||
ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod);
|
||||
|
||||
assertNull(mav);
|
||||
assertEquals("Handled requestBody=[Hello Server]", new String(response.getContentAsByteArray(), "UTF-8"));
|
||||
assertEquals(HttpStatus.ACCEPTED.value(), response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleHttpEntity() throws Exception {
|
||||
Class<?>[] paramTypes = new Class<?>[] { HttpEntity.class };
|
||||
|
||||
request.addHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
request.setContent("Hello Server".getBytes("UTF-8"));
|
||||
|
||||
HandlerMethod handlerMethod = handlerMethod(new RequestMappingHandler(), "handleHttpEntity", paramTypes);
|
||||
|
||||
ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod);
|
||||
|
||||
assertNull(mav);
|
||||
assertEquals(HttpStatus.ACCEPTED.value(), response.getStatus());
|
||||
assertEquals("Handled requestBody=[Hello Server]", new String(response.getContentAsByteArray(), "UTF-8"));
|
||||
assertEquals("headerValue", response.getHeader("header"));
|
||||
}
|
||||
|
||||
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
|
||||
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
|
||||
return new InvocableHandlerMethod(handler, method);
|
||||
}
|
||||
|
||||
@SessionAttributes(types=TestBean.class)
|
||||
private static class RequestMappingHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@InitBinder("dateParam")
|
||||
public void initBinder(WebDataBinder dataBinder, @RequestParam("datePattern") String datePattern) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
|
||||
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ModelAttribute
|
||||
public void model(Model model) {
|
||||
TestBean modelAttr = new TestBean();
|
||||
modelAttr.setName("Set by model method [modelAttr]");
|
||||
model.addAttribute("modelAttr", modelAttr);
|
||||
|
||||
modelAttr = new TestBean();
|
||||
modelAttr.setName("Set by model method [modelAttrByConvention]");
|
||||
model.addAttribute(modelAttr);
|
||||
|
||||
model.addAttribute(new OtherUser());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String handleMvc(
|
||||
@CookieValue("cookie") int cookie,
|
||||
@PathVariable("pathvar") String pathvar,
|
||||
@RequestHeader("header") String header,
|
||||
@RequestHeader(defaultValue="#{systemProperties.systemHeader}") String systemHeader,
|
||||
@RequestHeader Map<String, Object> headerMap,
|
||||
@RequestParam("dateParam") Date dateParam,
|
||||
@RequestParam Map<String, Object> paramMap,
|
||||
String paramByConvention,
|
||||
@Value("#{request.contextPath}") String value,
|
||||
@ModelAttribute("modelAttr") @Valid TestBean modelAttr,
|
||||
Errors errors,
|
||||
TestBean modelAttrByConvention,
|
||||
Color customArg,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
User user,
|
||||
@ModelAttribute OtherUser otherUser,
|
||||
Model model) throws Exception {
|
||||
|
||||
model.addAttribute("cookie", cookie).addAttribute("pathvar", pathvar).addAttribute("header", header)
|
||||
.addAttribute("systemHeader", systemHeader).addAttribute("headerMap", headerMap)
|
||||
.addAttribute("dateParam", dateParam).addAttribute("paramMap", paramMap)
|
||||
.addAttribute("paramByConvention", paramByConvention).addAttribute("value", value)
|
||||
.addAttribute("customArg", customArg).addAttribute(user);
|
||||
|
||||
assertNotNull(request);
|
||||
assertNotNull(response);
|
||||
|
||||
return "viewName";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ResponseStatus(value=HttpStatus.ACCEPTED)
|
||||
@ResponseBody
|
||||
public String handleRequestBody(@RequestBody byte[] bytes) throws Exception {
|
||||
String requestBody = new String(bytes, "UTF-8");
|
||||
return "Handled requestBody=[" + requestBody + "]";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public ResponseEntity<String> handleHttpEntity(HttpEntity<byte[]> httpEntity) throws Exception {
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.set("header", "headerValue");
|
||||
String responseBody = "Handled requestBody=[" + new String(httpEntity.getBody(), "UTF-8") + "]";
|
||||
return new ResponseEntity<String>(responseBody, responseHeaders, HttpStatus.ACCEPTED);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StubValidator implements Validator {
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void validate(Object target, Errors errors) {
|
||||
errors.reject("error");
|
||||
}
|
||||
}
|
||||
|
||||
private static class ColorArgumentResolver implements WebArgumentResolver {
|
||||
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
|
||||
return new Color(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static class User implements Principal {
|
||||
public String getName() {
|
||||
return "user";
|
||||
}
|
||||
}
|
||||
|
||||
private static class OtherUser implements Principal {
|
||||
public String getName() {
|
||||
return "other user";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link RequestMappingHandlerAdapter} unit tests.
|
||||
*
|
||||
* The tests in this class focus on {@link RequestMappingHandlerAdapter} functionality exclusively.
|
||||
* Also see {@link RequestMappingHandlerAdapterIntegrationTests} for higher-level tests invoking
|
||||
* {@link Controller @Controller} methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestMappingHandlerAdapterTests {
|
||||
|
||||
private RequestMappingHandlerAdapter handlerAdapter;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
this.handlerAdapter = new RequestMappingHandlerAdapter();
|
||||
this.handlerAdapter.setApplicationContext(new GenericWebApplicationContext());
|
||||
this.handlerAdapter.afterPropertiesSet();
|
||||
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheControlWithoutSessionAttributes() throws Exception {
|
||||
handlerAdapter.setCacheSeconds(100);
|
||||
handlerAdapter.handle(request, response, handlerMethod(new SimpleHandler(), "handle"));
|
||||
|
||||
assertTrue(response.getHeader("Cache-Control").toString().contains("max-age"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheControlWithSessionAttributes() throws Exception {
|
||||
handlerAdapter.setCacheSeconds(100);
|
||||
handlerAdapter.handle(request, response, handlerMethod(new SessionAttributeHandler(), "handle"));
|
||||
|
||||
assertEquals("no-cache", response.getHeader("Cache-Control"));
|
||||
}
|
||||
|
||||
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
|
||||
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
|
||||
return new InvocableHandlerMethod(handler, method);
|
||||
}
|
||||
|
||||
private static class SimpleHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle() {
|
||||
}
|
||||
}
|
||||
|
||||
@SessionAttributes("attr1")
|
||||
private static class SessionAttributeHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping;
|
||||
|
||||
/**
|
||||
* Test various scenarios for detecting handler methods depending on where @RequestMapping annotations
|
||||
* are located -- super types, parameterized methods, or in combination with proxies.
|
||||
*
|
||||
* Note the following:
|
||||
* <ul>
|
||||
* <li>Parameterized methods cannot be used in combination with JDK dynamic proxies since the
|
||||
* proxy interface does not contain the bridged methods that need to be invoked.
|
||||
* <li>When using JDK dynamic proxies, the proxied interface must contain all required annotations.
|
||||
* <li>Method-level annotations can be placed on parent classes or interfaces.
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class RequestMappingHandlerMethodDetectionTests {
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> handlerTypes() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ new MappingInterfaceController(), false},
|
||||
{ new MappingAbstractClassController(), false},
|
||||
{ new ParameterizedInterfaceController(), false },
|
||||
{ new MappingParameterizedInterfaceController(), false },
|
||||
{ new MappingAbstractClassController(), true},
|
||||
{ new PlainController(), true}
|
||||
});
|
||||
}
|
||||
|
||||
private Object handler;
|
||||
|
||||
private boolean useAutoProxy;
|
||||
|
||||
public RequestMappingHandlerMethodDetectionTests(Object handler, boolean useAutoProxy) {
|
||||
this.handler = handler;
|
||||
this.useAutoProxy = useAutoProxy;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectAndMapHandlerMethod() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/handle");
|
||||
|
||||
TestRequestMappingHandlerMethodMapping mapping = createHandlerMapping(handler.getClass(), useAutoProxy);
|
||||
HandlerMethod handlerMethod = mapping.getHandlerInternal(request);
|
||||
|
||||
assertNotNull("Failed to detect and map @RequestMapping handler method", handlerMethod);
|
||||
}
|
||||
|
||||
private TestRequestMappingHandlerMethodMapping createHandlerMapping(Class<?> controllerType, boolean useAutoProxy) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerType));
|
||||
if (useAutoProxy) {
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
autoProxyCreator.setBeanFactory(wac.getBeanFactory());
|
||||
wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
|
||||
wac.getBeanFactory().registerSingleton("advsr", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
}
|
||||
|
||||
TestRequestMappingHandlerMethodMapping mapping = new TestRequestMappingHandlerMethodMapping();
|
||||
mapping.setApplicationContext(wac);
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public static class TestRequestMappingHandlerMethodMapping extends RequestMappingHandlerMethodMapping {
|
||||
public HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
return super.getHandlerInternal(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public interface MappingInterface {
|
||||
@RequestMapping(value="/handle", method = RequestMethod.GET)
|
||||
void handle();
|
||||
}
|
||||
|
||||
public static class MappingInterfaceController implements MappingInterface {
|
||||
public void handle() {
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public static abstract class MappingAbstractClass {
|
||||
@RequestMapping(value = "/handle", method = RequestMethod.GET)
|
||||
public abstract void handle();
|
||||
}
|
||||
|
||||
public static class MappingAbstractClassController extends MappingAbstractClass {
|
||||
public void handle() {
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public interface ParameterizedInterface<T> {
|
||||
void handle(T object);
|
||||
}
|
||||
|
||||
public static class ParameterizedInterfaceController implements ParameterizedInterface<TestBean> {
|
||||
@RequestMapping(value = "/handle", method = RequestMethod.GET)
|
||||
public void handle(TestBean object) {
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public interface MappingParameterizedInterface<T> {
|
||||
@RequestMapping(value = "/handle", method = RequestMethod.GET)
|
||||
void handle(T object);
|
||||
}
|
||||
|
||||
public static class MappingParameterizedInterfaceController implements MappingParameterizedInterface<TestBean> {
|
||||
public void handle(TestBean object) {
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public static class PlainController {
|
||||
public PlainController() {
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/handle", method = RequestMethod.GET)
|
||||
public void handle() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.net.BindException;
|
||||
import java.net.SocketException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodExceptionResolver;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link RequestMappingHandlerMethodExceptionResolver} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RequestMappingHandlerMethodExceptionResolverTests {
|
||||
|
||||
private RequestMappingHandlerMethodExceptionResolver exceptionResolver;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
exceptionResolver = new RequestMappingHandlerMethodExceptionResolver();
|
||||
exceptionResolver.afterPropertiesSet();
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
request.setMethod("GET");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleWithIOException() throws NoSuchMethodException {
|
||||
IOException ex = new IOException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new SimpleController(), "handle");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertEquals("Invalid view name returned", "X:IOException", mav.getViewName());
|
||||
assertEquals("Invalid status code returned", 500, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleWithSocketException() throws NoSuchMethodException {
|
||||
SocketException ex = new SocketException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new SimpleController(), "handle");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertEquals("Invalid view name returned", "Y:SocketException", mav.getViewName());
|
||||
assertEquals("Invalid status code returned", 406, response.getStatus());
|
||||
assertEquals("Invalid status reason returned", "This is simply unacceptable!", response.getErrorMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleWithFileNotFoundException() throws NoSuchMethodException {
|
||||
FileNotFoundException ex = new FileNotFoundException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new SimpleController(), "handle");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertEquals("Invalid view name returned", "X:FileNotFoundException", mav.getViewName());
|
||||
assertEquals("Invalid status code returned", 500, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleWithBindException() throws NoSuchMethodException {
|
||||
BindException ex = new BindException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new SimpleController(), "handle");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertEquals("Invalid view name returned", "Y:BindException", mav.getViewName());
|
||||
assertEquals("Invalid status code returned", 406, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inherited() throws NoSuchMethodException {
|
||||
IOException ex = new IOException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new InheritedController(), "handle");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertEquals("Invalid view name returned", "GenericError", mav.getViewName());
|
||||
assertEquals("Invalid status code returned", 500, response.getStatus());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void ambiguous() throws NoSuchMethodException {
|
||||
IllegalArgumentException ex = new IllegalArgumentException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new AmbiguousController(), "handle");
|
||||
exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noModelAndView() throws UnsupportedEncodingException, NoSuchMethodException {
|
||||
IllegalArgumentException ex = new IllegalArgumentException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new NoMAVReturningController(), "handle");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertTrue("ModelAndView not empty", mav.isEmpty());
|
||||
assertEquals("Invalid response written", "IllegalArgumentException", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseBody() throws UnsupportedEncodingException, NoSuchMethodException {
|
||||
IllegalArgumentException ex = new IllegalArgumentException();
|
||||
HandlerMethod handlerMethod = new InvocableHandlerMethod(new ResponseBodyController(), "handle");
|
||||
request.addHeader("Accept", "text/plain");
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, handlerMethod, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertTrue("ModelAndView not empty", mav.isEmpty());
|
||||
assertEquals("Invalid response written", "IllegalArgumentException", response.getContentAsString());
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class SimpleController {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle() {}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ExceptionHandler(IOException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public String handleIOException(IOException ex, HttpServletRequest request) {
|
||||
return "X:" + ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ExceptionHandler(SocketException.class)
|
||||
@ResponseStatus(value = HttpStatus.NOT_ACCEPTABLE, reason = "This is simply unacceptable!")
|
||||
public String handleSocketException(Exception ex, HttpServletResponse response) {
|
||||
return "Y:" + ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public String handleIllegalArgumentException(Exception ex) {
|
||||
return ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class InheritedController extends SimpleController {
|
||||
|
||||
@Override
|
||||
public String handleIOException(IOException ex, HttpServletRequest request) {
|
||||
return "GenericError";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class AmbiguousController {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle() {}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ExceptionHandler({BindException.class, IllegalArgumentException.class})
|
||||
public String handle1(Exception ex, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
return ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ExceptionHandler
|
||||
public String handle2(IllegalArgumentException ex) {
|
||||
return ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class NoMAVReturningController {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle() {}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ExceptionHandler(Exception.class)
|
||||
public void handle(Exception ex, Writer writer) throws IOException {
|
||||
writer.write(ClassUtils.getShortName(ex.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
private static class ResponseBodyController {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle() {}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseBody
|
||||
public String handle(Exception ex) {
|
||||
return ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestKey;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodMapping;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestMappingHandlerMethodMappingTests {
|
||||
|
||||
private MyRequestMappingHandlerMethodMapping mapping;
|
||||
|
||||
private MyHandler handler;
|
||||
|
||||
private HandlerMethod fooMethod;
|
||||
|
||||
private HandlerMethod barMethod;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
handler = new MyHandler();
|
||||
fooMethod = new HandlerMethod(handler, "foo");
|
||||
barMethod = new HandlerMethod(handler, "bar");
|
||||
|
||||
StaticApplicationContext context = new StaticApplicationContext();
|
||||
context.registerSingleton("handler", handler.getClass());
|
||||
|
||||
mapping = new MyRequestMappingHandlerMethodMapping();
|
||||
mapping.setApplicationContext(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void directMatch() throws Exception {
|
||||
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
|
||||
assertEquals(fooMethod.getMethod(), result.getMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void globMatch() throws Exception {
|
||||
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/bar"));
|
||||
assertEquals(barMethod.getMethod(), result.getMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodNotAllowed() throws Exception {
|
||||
try {
|
||||
mapping.getHandlerInternal(new MockHttpServletRequest("POST", "/bar"));
|
||||
fail("HttpRequestMethodNotSupportedException expected");
|
||||
}
|
||||
catch (HttpRequestMethodNotSupportedException ex) {
|
||||
assertArrayEquals("Invalid supported methods", new String[]{"GET", "HEAD"}, ex.getSupportedMethods());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriTemplateVariables() {
|
||||
RequestKey key = new RequestKey(Arrays.asList("/{path1}/{path2}"), null, null, null);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2");
|
||||
|
||||
mapping.handleMatch(key, request);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> actual = (Map<String, String>) request.getAttribute(
|
||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
|
||||
assertNotNull(actual);
|
||||
assertEquals("1", actual.get("path1"));
|
||||
assertEquals("2", actual.get("path2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappedInterceptors() {
|
||||
String path = "/handle";
|
||||
HandlerInterceptor interceptor = new HandlerInterceptorAdapter() {};
|
||||
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] {path}, interceptor);
|
||||
|
||||
MyRequestMappingHandlerMethodMapping mapping = new MyRequestMappingHandlerMethodMapping();
|
||||
mapping.setMappedInterceptors(new MappedInterceptor[] { mappedInterceptor });
|
||||
|
||||
HandlerExecutionChain chain = mapping.getHandlerExecutionChain(handler, new MockHttpServletRequest("GET", path));
|
||||
assertNotNull(chain.getInterceptors());
|
||||
assertSame(interceptor, chain.getInterceptors()[0]);
|
||||
|
||||
chain = mapping.getHandlerExecutionChain(handler, new MockHttpServletRequest("GET", "/invalid"));
|
||||
assertNull(chain.getInterceptors());
|
||||
}
|
||||
|
||||
private static class MyRequestMappingHandlerMethodMapping extends RequestMappingHandlerMethodMapping {
|
||||
|
||||
@Override
|
||||
public HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
return super.getHandlerInternal(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
private static class MyHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RequestMapping(value = "/foo", method = RequestMethod.GET)
|
||||
public void foo() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RequestMapping(value = "/ba*", method = { RequestMethod.GET, RequestMethod.HEAD })
|
||||
public void bar() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerContainer;
|
||||
import org.springframework.web.method.support.StubReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ServletInvocableHandlerMethod} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ServletInvocableHandlerMethodTests {
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
response = new MockHttpServletResponse();
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest(), response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setResponseStatus() throws Exception {
|
||||
HandlerMethodReturnValueHandlerContainer handlers = new HandlerMethodReturnValueHandlerContainer();
|
||||
handlers.registerReturnValueHandler(new StubReturnValueHandler(void.class, false));
|
||||
|
||||
Method method = Handler.class.getDeclaredMethod("responseStatus");
|
||||
ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Handler(), method);
|
||||
handlerMethod.setReturnValueHandlers(handlers);
|
||||
|
||||
handlerMethod.invokeAndHandle(webRequest, null);
|
||||
|
||||
assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
|
||||
assertEquals("400 Bad Request", response.getErrorMessage());
|
||||
}
|
||||
|
||||
private static class Handler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "400 Bad Request")
|
||||
public void responseStatus() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link DefaultMethodReturnValueHandler} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class DefaultMethodReturnValueHandlerTests {
|
||||
|
||||
private DefaultMethodReturnValueHandler handler;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
private ModelAndViewContainer<View> mavContainer;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.handler = new DefaultMethodReturnValueHandler(null);
|
||||
this.mavContainer = new ModelAndViewContainer<View>(new ExtendedModelMap());
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test(expected=UnsupportedOperationException.class)
|
||||
public void returnSimpleType() throws Exception {
|
||||
handler.handleReturnValue(55, createMethodParam("simpleType"), mavContainer, webRequest);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnVoid() throws Exception {
|
||||
handler.handleReturnValue(null, null, mavContainer, webRequest);
|
||||
assertNull(mavContainer.getView());
|
||||
assertNull(mavContainer.getViewName());
|
||||
assertTrue(mavContainer.getModel().isEmpty());
|
||||
}
|
||||
|
||||
private MethodParameter createMethodParam(String methodName) throws Exception {
|
||||
Method method = getClass().getDeclaredMethod(methodName);
|
||||
return new MethodParameter(method, -1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private int simpleType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void voidReturnValue() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.eq;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.HttpEntityMethodProcessor;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class HttpEntityMethodProcessorTests {
|
||||
|
||||
private HttpEntityMethodProcessor processor;
|
||||
|
||||
private HttpMessageConverter<String> messageConverter;
|
||||
|
||||
private MethodParameter httpEntityParam;
|
||||
|
||||
private MethodParameter responseEntityReturnValue;
|
||||
|
||||
private MethodParameter responseEntityParameter;
|
||||
|
||||
private MethodParameter intReturnValue;
|
||||
|
||||
private ServletWebRequest request;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
private MethodParameter httpEntityReturnValue;
|
||||
|
||||
private MethodParameter intParameter;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messageConverter = createMock(HttpMessageConverter.class);
|
||||
processor = new HttpEntityMethodProcessor(messageConverter);
|
||||
|
||||
Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE);
|
||||
httpEntityParam = new MethodParameter(handle1, 0);
|
||||
responseEntityParameter = new MethodParameter(handle1, 1);
|
||||
intParameter = new MethodParameter(handle1, 2);
|
||||
responseEntityReturnValue = new MethodParameter(handle1, -1);
|
||||
|
||||
Method handle2 = getClass().getMethod("handle2", HttpEntity.class);
|
||||
httpEntityReturnValue = new MethodParameter(handle2, -1);
|
||||
|
||||
Method other = getClass().getMethod("otherMethod");
|
||||
intReturnValue = new MethodParameter(other, -1);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
request = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("HttpEntity parameter not supported", processor.supportsParameter(httpEntityParam));
|
||||
assertFalse("ResponseEntity parameter supported", processor.supportsParameter(responseEntityParameter));
|
||||
assertFalse("non-entity parameter supported", processor.supportsParameter(intParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue("ResponseEntity return type not supported", processor.supportsReturnType(responseEntityReturnValue));
|
||||
assertTrue("HttpEntity return type not supported", processor.supportsReturnType(httpEntityReturnValue));
|
||||
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(intReturnValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() {
|
||||
assertFalse("HttpEntity parameter uses response argument", processor.usesResponseArgument(httpEntityParam));
|
||||
assertTrue("ResponseBody return type does not use response argument",
|
||||
processor.usesResponseArgument(responseEntityReturnValue));
|
||||
assertTrue("HttpEntity return type does not use response argument",
|
||||
processor.usesResponseArgument(httpEntityReturnValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveArgument() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
String expected = "Foo";
|
||||
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(expected);
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
HttpEntity<?> result = (HttpEntity<String>) processor.resolveArgument(httpEntityParam, null, request, null);
|
||||
assertEquals("Invalid argument", expected, result.getBody());
|
||||
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNotReadable() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(false);
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
processor.resolveArgument(httpEntityParam, null, request, null);
|
||||
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNoContentType() throws Exception {
|
||||
processor.resolveArgument(httpEntityParam, null, request, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
String s = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(s, HttpStatus.OK);
|
||||
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(true);
|
||||
messageConverter.write(eq(s), eq(accepted), isA(HttpOutputMessage.class));
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
|
||||
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptable() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
String s = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(s, HttpStatus.OK);
|
||||
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
|
||||
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseHeaderNoBody() throws Exception {
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.set("header", "headerValue");
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(responseHeaders, HttpStatus.ACCEPTED);
|
||||
|
||||
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(new StringHttpMessageConverter());
|
||||
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
|
||||
|
||||
assertEquals("headerValue", servletResponse.getHeader("header"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseHeaderAndBody() throws Exception {
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.set("header", "headerValue");
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>("body", responseHeaders, HttpStatus.ACCEPTED);
|
||||
|
||||
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(new StringHttpMessageConverter());
|
||||
processor.handleReturnValue(returnValue, responseEntityReturnValue, null, request);
|
||||
|
||||
assertEquals("headerValue", servletResponse.getHeader("header"));
|
||||
}
|
||||
|
||||
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> responseEntity, int i) {
|
||||
return responseEntity;
|
||||
}
|
||||
|
||||
public HttpEntity<?> handle2(HttpEntity<?> entity) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public HttpEntity<?> handle3() {
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.set("header", "headerValue");
|
||||
return new ResponseEntity<String>(responseHeaders, HttpStatus.OK);
|
||||
}
|
||||
|
||||
public int otherMethod() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.PathVariableMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class PathVariableMethodArgumentResolverTests {
|
||||
|
||||
private PathVariableMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter pathVarParam;
|
||||
|
||||
private MethodParameter stringParam;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new PathVariableMethodArgumentResolver(null);
|
||||
Method method = getClass().getMethod("handle", String.class, String.class);
|
||||
pathVarParam = new MethodParameter(method, 0);
|
||||
stringParam = new MethodParameter(method, 1);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() {
|
||||
assertFalse(resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("Parameter with @PathVariable annotation", resolver.supportsParameter(pathVarParam));
|
||||
assertFalse("Parameter without @PathVariable annotation", resolver.supportsParameter(stringParam));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveStringArgument() throws Exception {
|
||||
String expected = "foo";
|
||||
|
||||
Map<String, String> uriTemplateVars = new HashMap<String, String>();
|
||||
uriTemplateVars.put("name", expected);
|
||||
servletRequest.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars);
|
||||
|
||||
String result = (String) resolver.resolveArgument(pathVarParam, null, webRequest, null);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void handleMissingValue() throws Exception {
|
||||
resolver.resolveArgument(pathVarParam, null, webRequest, null);
|
||||
fail("Unresolved path variable should lead to exception.");
|
||||
}
|
||||
|
||||
public void handle(@PathVariable(value = "name") String param1, String param2) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.eq;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestResponseBodyMethodProcessorTests {
|
||||
|
||||
private RequestResponseBodyMethodProcessor processor;
|
||||
|
||||
private HttpMessageConverter<String> messageConverter;
|
||||
|
||||
private MethodParameter stringParameter;
|
||||
|
||||
private MethodParameter stringReturnValue;
|
||||
|
||||
private MethodParameter intParameter;
|
||||
|
||||
private MethodParameter intReturnValue;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messageConverter = createMock(HttpMessageConverter.class);
|
||||
processor = new RequestResponseBodyMethodProcessor(messageConverter);
|
||||
Method handle = getClass().getMethod("handle", String.class, Integer.TYPE);
|
||||
stringParameter = new MethodParameter(handle, 0);
|
||||
intParameter = new MethodParameter(handle, 1);
|
||||
stringReturnValue = new MethodParameter(handle, -1);
|
||||
Method other = getClass().getMethod("otherMethod");
|
||||
intReturnValue = new MethodParameter(other, -1);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("RequestBody parameter not supported", processor.supportsParameter(stringParameter));
|
||||
assertFalse("non-RequestBody parameter supported", processor.supportsParameter(intParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue("ResponseBody return type not supported", processor.supportsReturnType(stringReturnValue));
|
||||
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(intReturnValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() {
|
||||
assertFalse("RequestBody parameter uses response argument", processor.usesResponseArgument(stringParameter));
|
||||
assertTrue("ResponseBody return type does not use response argument",
|
||||
processor.usesResponseArgument(stringReturnValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
String expected = "Foo";
|
||||
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(expected);
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
Object result = processor.resolveArgument(stringParameter, null, webRequest, null);
|
||||
assertEquals("Invalid argument", expected, result);
|
||||
|
||||
verify(messageConverter);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNotReadable() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(false);
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
processor.resolveArgument(stringParameter, null, webRequest, null);
|
||||
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNoContentType() throws Exception {
|
||||
processor.resolveArgument(stringParameter, null, webRequest, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
String returnValue = "Foo";
|
||||
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(true);
|
||||
messageConverter.write(eq(returnValue), eq(accepted), isA(HttpOutputMessage.class));
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, stringReturnValue, null, webRequest);
|
||||
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptable() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
String returnValue = "Foo";
|
||||
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
|
||||
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, stringReturnValue, null, webRequest);
|
||||
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
public String handle(@RequestBody String s, int i) {
|
||||
return s;
|
||||
}
|
||||
|
||||
public int otherMethod() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.CookieValueMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletCookieValueMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ServletCookieValueMethodArgumentResolverTests {
|
||||
|
||||
private CookieValueMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter cookieParameter;
|
||||
|
||||
private MethodParameter cookieStringParameter;
|
||||
|
||||
private MethodParameter otherParameter;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new ServletCookieValueMethodArgumentResolver(null);
|
||||
Method method = getClass().getMethod("params", Cookie.class, String.class, String.class);
|
||||
cookieParameter = new MethodParameter(method, 0);
|
||||
cookieStringParameter = new MethodParameter(method, 1);
|
||||
otherParameter = new MethodParameter(method, 2);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() throws NoSuchMethodException {
|
||||
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("Cookie parameter not supported", resolver.supportsParameter(cookieParameter));
|
||||
assertTrue("Cookie string parameter not supported", resolver.supportsParameter(cookieStringParameter));
|
||||
assertFalse("non-@CookieValue parameter supported", resolver.supportsParameter(otherParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveCookieArgument() throws Exception {
|
||||
Cookie expected = new Cookie("name", "foo");
|
||||
servletRequest.setCookies(expected);
|
||||
|
||||
Cookie result = (Cookie) resolver.resolveArgument(cookieParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveCookieStringArgument() throws Exception {
|
||||
Cookie cookie = new Cookie("name", "foo");
|
||||
servletRequest.setCookies(cookie);
|
||||
|
||||
String result = (String) resolver.resolveArgument(cookieStringParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", cookie.getValue(), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveCookieDefaultValue() throws Exception {
|
||||
String result = (String) resolver.resolveArgument(cookieStringParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", "bar", result);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void notFound() throws Exception {
|
||||
String result = (String) resolver.resolveArgument(cookieParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", "bar", result);
|
||||
}
|
||||
|
||||
public void params(@CookieValue("name") Cookie cookie,
|
||||
@CookieValue(value = "name", defaultValue = "bar") String cookieString,
|
||||
String unsupported) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Principal;
|
||||
import java.util.Locale;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequestMethodArgumentResolver;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ServletRequestMethodArgumentResolverTests {
|
||||
|
||||
private ServletRequestMethodArgumentResolver resolver;
|
||||
|
||||
private Method supportedParams;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new ServletRequestMethodArgumentResolver();
|
||||
supportedParams = getClass()
|
||||
.getMethod("supportedParams", ServletRequest.class, MultipartRequest.class, HttpSession.class,
|
||||
Principal.class, Locale.class, InputStream.class, Reader.class);
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() {
|
||||
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void servletRequest() throws Exception {
|
||||
MethodParameter servletRequestParameter = new MethodParameter(supportedParams, 0);
|
||||
|
||||
assertTrue("ServletRequest not supported", resolver.supportsParameter(servletRequestParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(servletRequestParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", servletRequest, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void session() throws Exception {
|
||||
MockHttpSession session = new MockHttpSession();
|
||||
servletRequest.setSession(session);
|
||||
MethodParameter sessionParameter = new MethodParameter(supportedParams, 2);
|
||||
|
||||
assertTrue("Session not supported", resolver.supportsParameter(sessionParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(sessionParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", session, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void principal() throws Exception {
|
||||
Principal principal = new Principal() {
|
||||
public String getName() {
|
||||
return "Foo";
|
||||
}
|
||||
};
|
||||
servletRequest.setUserPrincipal(principal);
|
||||
MethodParameter principalParameter = new MethodParameter(supportedParams, 3);
|
||||
|
||||
assertTrue("Principal not supported", resolver.supportsParameter(principalParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(principalParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", principal, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void locale() throws Exception {
|
||||
Locale locale = Locale.ENGLISH;
|
||||
servletRequest.addPreferredLocale(locale);
|
||||
MethodParameter localeParameter = new MethodParameter(supportedParams, 4);
|
||||
|
||||
assertTrue("Locale not supported", resolver.supportsParameter(localeParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(localeParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", locale, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputStream() throws Exception {
|
||||
MethodParameter inputStreamParameter = new MethodParameter(supportedParams, 5);
|
||||
|
||||
assertTrue("InputStream not supported", resolver.supportsParameter(inputStreamParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(inputStreamParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", webRequest.getRequest().getInputStream(), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reader() throws Exception {
|
||||
MethodParameter readerParameter = new MethodParameter(supportedParams, 6);
|
||||
|
||||
assertTrue("Reader not supported", resolver.supportsParameter(readerParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(readerParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", webRequest.getRequest().getReader(), result);
|
||||
}
|
||||
|
||||
public void supportedParams(ServletRequest p0,
|
||||
MultipartRequest p1,
|
||||
HttpSession p2,
|
||||
Principal p3,
|
||||
Locale p4,
|
||||
InputStream p5,
|
||||
Reader p9) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ServletResponseMethodArgumentResolver;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ServletResponseMethodArgumentResolverTests {
|
||||
|
||||
private ServletResponseMethodArgumentResolver resolver;
|
||||
|
||||
private Method supportedParams;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new ServletResponseMethodArgumentResolver();
|
||||
supportedParams =
|
||||
getClass().getMethod("supportedParams", ServletResponse.class, OutputStream.class, Writer.class);
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(new MockHttpServletRequest(), servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() {
|
||||
assertTrue("resolver uses response argument", resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void servletResponse() throws Exception {
|
||||
MethodParameter servletResponseParameter = new MethodParameter(supportedParams, 0);
|
||||
|
||||
assertTrue("ServletResponse not supported", resolver.supportsParameter(servletResponseParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(servletResponseParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", servletResponse, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputStream() throws Exception {
|
||||
MethodParameter outputStreamParameter = new MethodParameter(supportedParams, 1);
|
||||
|
||||
assertTrue("OutputStream not supported", resolver.supportsParameter(outputStreamParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(outputStreamParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", servletResponse.getOutputStream(), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writer() throws Exception {
|
||||
MethodParameter writerParameter = new MethodParameter(supportedParams, 2);
|
||||
|
||||
assertTrue("Writer not supported", resolver.supportsParameter(writerParameter));
|
||||
|
||||
Object result = resolver.resolveArgument(writerParameter, null, webRequest, null);
|
||||
assertSame("Invalid result", servletResponse.getWriter(), result);
|
||||
}
|
||||
|
||||
public void supportedParams(ServletResponse p0, OutputStream p1, Writer p2) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.servlet.mvc.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.support.ViewMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.view.InternalResourceView;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link DefaultMethodReturnValueHandler} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ViewMethodReturnValueHandlerTests {
|
||||
|
||||
private ViewMethodReturnValueHandler handler;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
private ModelAndViewContainer<View> mavContainer;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.handler = new ViewMethodReturnValueHandler();
|
||||
this.mavContainer = new ModelAndViewContainer<View>(new ExtendedModelMap());
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() throws Exception {
|
||||
assertTrue(handler.supportsReturnType(createMethodParam("view")));
|
||||
assertTrue(handler.supportsReturnType(createMethodParam("viewName")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnView() throws Exception {
|
||||
InternalResourceView view = new InternalResourceView("testView");
|
||||
handler.handleReturnValue(view, createMethodParam("view"), mavContainer, webRequest);
|
||||
assertSame(view, mavContainer.getView());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnViewName() throws Exception {
|
||||
handler.handleReturnValue("testView", createMethodParam("viewName"), mavContainer, webRequest);
|
||||
assertEquals("testView", mavContainer.getViewName());
|
||||
}
|
||||
|
||||
private MethodParameter createMethodParam(String methodName) throws Exception {
|
||||
Method method = getClass().getDeclaredMethod(methodName);
|
||||
return new MethodParameter(method, -1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private View view() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String viewName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.bind.support;
|
||||
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* A {@link WebDataBinderFactory} that creates {@link WebDataBinder} and initializes them
|
||||
* with a {@link WebBindingInitializer}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DefaultDataBinderFactory implements WebDataBinderFactory {
|
||||
|
||||
private final WebBindingInitializer bindingInitializer;
|
||||
|
||||
/**
|
||||
* Create {@link DefaultDataBinderFactory} instance.
|
||||
* @param bindingInitializer a {@link WebBindingInitializer} to initialize new data binder instances with
|
||||
*/
|
||||
public DefaultDataBinderFactory(WebBindingInitializer bindingInitializer) {
|
||||
this.bindingInitializer = bindingInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link WebDataBinder} for the given target object and initialize it through
|
||||
* a {@link WebBindingInitializer}.
|
||||
*/
|
||||
public WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
|
||||
WebDataBinder dataBinder = createBinderInstance(target, objectName);
|
||||
|
||||
if (bindingInitializer != null) {
|
||||
this.bindingInitializer.initBinder(dataBinder, webRequest);
|
||||
}
|
||||
|
||||
return dataBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link WebDataBinder} instance.
|
||||
* @param target the object to create a data binder for, or {@code null} if creating a binder for a simple type
|
||||
* @param objectName the name of the target object
|
||||
*/
|
||||
protected WebDataBinder createBinderInstance(Object target, String objectName) {
|
||||
return new WebRequestDataBinder(target, objectName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.bind.support;
|
||||
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* A factory for creating a {@link WebDataBinder} instance for a named target object.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface WebDataBinderFactory {
|
||||
|
||||
/**
|
||||
* Create a {@link WebDataBinder} for the given object.
|
||||
* @param webRequest the current request
|
||||
* @param target the object to create a data binder for, or {@code null} if creating a binder for a simple type
|
||||
* @param objectName the name of the target object
|
||||
* @return the created {@link WebDataBinder} instance, never null
|
||||
* @throws Exception raised if the creation and initialization of the data binder fails
|
||||
*/
|
||||
WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Encapsulates information about a bean method consisting of a {@linkplain #getMethod() method} and a
|
||||
* {@linkplain #getBean() bean}. Provides convenient access to method parameters, the method return value,
|
||||
* method annotations.
|
||||
*
|
||||
* <p>The class may be created with a bean instance or with a bean name (e.g. lazy bean, prototype bean).
|
||||
* Use {@link #createWithResolvedBean()} to obtain an {@link HandlerMethod} instance with a bean instance
|
||||
* initialized through the bean factory.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HandlerMethod {
|
||||
|
||||
/** Logger that is available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final Object bean;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private MethodParameter[] parameters;
|
||||
|
||||
private final Method bridgedMethod;
|
||||
|
||||
/**
|
||||
* Constructs a new handler method with the given bean instance and method.
|
||||
* @param bean the object bean
|
||||
* @param method the method
|
||||
*/
|
||||
public HandlerMethod(Object bean, Method method) {
|
||||
Assert.notNull(bean, "bean must not be null");
|
||||
Assert.notNull(method, "method must not be null");
|
||||
this.bean = bean;
|
||||
this.beanFactory = null;
|
||||
this.method = method;
|
||||
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new handler method with the given bean instance, method name and parameters.
|
||||
* @param bean the object bean
|
||||
* @param methodName the method name
|
||||
* @param parameterTypes the method parameter types
|
||||
* @throws NoSuchMethodException when the method cannot be found
|
||||
*/
|
||||
public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
|
||||
Assert.notNull(bean, "bean must not be null");
|
||||
Assert.notNull(methodName, "method must not be null");
|
||||
this.bean = bean;
|
||||
this.beanFactory = null;
|
||||
this.method = bean.getClass().getMethod(methodName, parameterTypes);
|
||||
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new handler method with the given bean name and method. The bean name will be lazily
|
||||
* initialized when {@link #createWithResolvedBean()} is called.
|
||||
* @param beanName the bean name
|
||||
* @param beanFactory the bean factory to use for bean initialization
|
||||
* @param method the method for the bean
|
||||
*/
|
||||
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
|
||||
Assert.hasText(beanName, "'beanName' must not be null");
|
||||
Assert.notNull(beanFactory, "'beanFactory' must not be null");
|
||||
Assert.notNull(method, "'method' must not be null");
|
||||
Assert.isTrue(beanFactory.containsBean(beanName),
|
||||
"Bean factory [" + beanFactory + "] does not contain bean " + "with name [" + beanName + "]");
|
||||
this.bean = beanName;
|
||||
this.beanFactory = beanFactory;
|
||||
this.method = method;
|
||||
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bean for this handler method.
|
||||
*/
|
||||
public Object getBean() {
|
||||
return this.bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the method for this handler method.
|
||||
*/
|
||||
public Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the handler for this handler method.
|
||||
* Note that if the bean type is a CGLIB-generated class, the original, user-defined class is returned.
|
||||
*/
|
||||
public Class<?> getBeanType() {
|
||||
if (bean instanceof String) {
|
||||
String beanName = (String) bean;
|
||||
return beanFactory.getType(beanName);
|
||||
}
|
||||
else {
|
||||
return ClassUtils.getUserClass(bean.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the bean method is a bridge method, this method returns the bridged (user-defined) method.
|
||||
* Otherwise it returns the same method as {@link #getMethod()}.
|
||||
*/
|
||||
protected Method getBridgedMethod() {
|
||||
return bridgedMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the method parameters for this handler method.
|
||||
*/
|
||||
public MethodParameter[] getMethodParameters() {
|
||||
if (this.parameters == null) {
|
||||
int parameterCount = this.bridgedMethod.getParameterTypes().length;
|
||||
MethodParameter[] p = new MethodParameter[parameterCount];
|
||||
for (int i = 0; i < parameterCount; i++) {
|
||||
p[i] = new HandlerMethodParameter(this.bridgedMethod, i);
|
||||
}
|
||||
this.parameters = p;
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the method return type, as {@code MethodParameter}.
|
||||
*/
|
||||
public MethodParameter getReturnType() {
|
||||
return new HandlerMethodParameter(this.bridgedMethod, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the method return type is void, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isVoid() {
|
||||
return Void.TYPE.equals(getReturnType().getParameterType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single annotation on the underlying method traversing its super methods if no
|
||||
* annotation can be found on the given method itself.
|
||||
* @param annotationType the type of annotation to introspect the method for.
|
||||
* @return the annotation, or {@code null} if none found
|
||||
*/
|
||||
public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
|
||||
return AnnotationUtils.findAnnotation(this.method, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the provided instance contains a bean name rather than an object instance, the bean name is resolved
|
||||
* before a {@link HandlerMethod} is created and returned.
|
||||
*/
|
||||
public HandlerMethod createWithResolvedBean() {
|
||||
Object handler = this.bean;
|
||||
if (this.bean instanceof String) {
|
||||
String beanName = (String) this.bean;
|
||||
handler = this.beanFactory.getBean(beanName);
|
||||
}
|
||||
return new HandlerMethod(handler, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o != null && o instanceof HandlerMethod) {
|
||||
HandlerMethod other = (HandlerMethod) o;
|
||||
return this.bean.equals(other.bean) && this.method.equals(other.method);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * this.bean.hashCode() + this.method.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return method.toGenericString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link MethodParameter} that resolves method annotations even when the actual annotations
|
||||
* are on a bridge method rather than on the current method. Annotations on super types are
|
||||
* also returned via {@link AnnotationUtils#findAnnotation(Method, Class)}.
|
||||
*/
|
||||
private class HandlerMethodParameter extends MethodParameter {
|
||||
|
||||
public HandlerMethodParameter(Method method, int parameterIndex) {
|
||||
super(method, parameterIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link HandlerMethod#getBeanType()} rather than the method's class, which could be
|
||||
* important for the proper discovery of generic types.
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
return HandlerMethod.this.getBeanType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the method annotation via {@link HandlerMethod#getMethodAnnotation(Class)}, which will find
|
||||
* the annotation by traversing super-types and handling annotations on bridge methods correctly.
|
||||
*/
|
||||
@Override
|
||||
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
|
||||
return HandlerMethod.this.getMethodAnnotation(annotationType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
|
||||
/**
|
||||
* Defines the algorithm for searching handler methods exhaustively including interfaces and parent
|
||||
* classes while also dealing with parameterized methods and interface and class-based proxies.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class HandlerMethodSelector {
|
||||
|
||||
/**
|
||||
* Selects handler methods for the given handler type. Callers of this method define handler methods
|
||||
* of interest through the {@link MethodFilter} parameter.
|
||||
*
|
||||
* @param handlerType the handler type to search handler methods on
|
||||
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
|
||||
*/
|
||||
public static Set<Method> selectMethods(final Class<?> handlerType, final MethodFilter handlerMethodFilter) {
|
||||
final Set<Method> handlerMethods = new LinkedHashSet<Method>();
|
||||
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
|
||||
Class<?> specificHandlerType = null;
|
||||
if (!Proxy.isProxyClass(handlerType)) {
|
||||
handlerTypes.add(handlerType);
|
||||
specificHandlerType = handlerType;
|
||||
}
|
||||
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
|
||||
for (Class<?> currentHandlerType : handlerTypes) {
|
||||
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
|
||||
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) {
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
if (handlerMethodFilter.matches(specificMethod) &&
|
||||
(bridgedMethod == specificMethod || !handlerMethodFilter.matches(bridgedMethod))) {
|
||||
handlerMethods.add(specificMethod);
|
||||
}
|
||||
}
|
||||
}, ReflectionUtils.USER_DECLARED_METHODS);
|
||||
}
|
||||
return handlerMethods;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.core.ExceptionDepthComparator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
/**
|
||||
* Extracts and stores method-to-exception type mappings from a set of {@link ExceptionHandler}-annotated methods.
|
||||
* Subsequently {@link #getMethod(Exception)} can be used to matches an {@link Exception} to a method.
|
||||
*
|
||||
* <p>Method-to-exception type mappings are usually derived from a method's {@link ExceptionHandler} annotation value.
|
||||
* The method argument list may also be checked for {@link Throwable} types if that's empty. Exception types can be
|
||||
* mapped to one method only.
|
||||
*
|
||||
* <p>When multiple exception types match a given exception, the best matching exception type is selected by sorting
|
||||
* the list of matches with {@link ExceptionDepthComparator}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ExceptionMethodMapping {
|
||||
|
||||
protected static final Method NO_METHOD_FOUND = ClassUtils.getMethodIfAvailable(System.class, "currentTimeMillis");
|
||||
|
||||
private final Map<Class<? extends Throwable>, Method> mappedExceptionTypes =
|
||||
new HashMap<Class<? extends Throwable>, Method>();
|
||||
|
||||
private final Map<Class<? extends Throwable>, Method> resolvedExceptionTypes =
|
||||
new ConcurrentHashMap<Class<? extends Throwable>, Method>();
|
||||
|
||||
/**
|
||||
* Creates an {@link ExceptionMethodMapping} instance from a set of {@link ExceptionHandler} methods.
|
||||
* <p>While any {@link ExceptionHandler} methods can be provided, it is expected that the exception types
|
||||
* handled by any one method do not overlap with the exception types handled by any other method.
|
||||
* If two methods map to the same exception type, an exception is raised.
|
||||
* @param methods the {@link ExceptionHandler}-annotated methods to add to the mappings
|
||||
*/
|
||||
public ExceptionMethodMapping(Set<Method> methods) {
|
||||
initExceptionMap(methods);
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the provided methods and populates mapped exception types.
|
||||
*/
|
||||
private void initExceptionMap(Set<Method> methods) {
|
||||
for (Method method : methods) {
|
||||
for (Class<? extends Throwable> exceptionType : getMappedExceptionTypes(method)) {
|
||||
Method prevMethod = mappedExceptionTypes.put(exceptionType, method);
|
||||
|
||||
if (prevMethod != null && !prevMethod.equals(method)) {
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous exception handler mapped for [" + exceptionType + "]: {" +
|
||||
prevMethod + ", " + method + "}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the list of exception types mapped to the given method in one of the following ways:
|
||||
* <ol>
|
||||
* <li>The {@link ExceptionHandler} annotation value
|
||||
* <li>{@link Throwable} types that appear in the method parameter list
|
||||
* </ol>
|
||||
* @param method the method to derive mapped exception types for
|
||||
* @return the list of exception types the method is mapped to, or an empty list
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected List<Class<? extends Throwable>> getMappedExceptionTypes(Method method) {
|
||||
ExceptionHandler annotation = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
|
||||
if (annotation.value().length != 0) {
|
||||
return Arrays.asList(annotation.value());
|
||||
}
|
||||
else {
|
||||
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
|
||||
for (Class<?> paramType : method.getParameterTypes()) {
|
||||
if (Throwable.class.isAssignableFrom(paramType)) {
|
||||
result.add((Class<? extends Throwable>) paramType);
|
||||
}
|
||||
}
|
||||
Assert.notEmpty(result, "No exception types mapped to {" + method + "}");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ExceptionHandler} method that matches the type of the provided {@link Exception}.
|
||||
* In case of multiple matches, the best match is selected with {@link ExceptionDepthComparator}.
|
||||
* @param exception the exception to find a matching {@link ExceptionHandler} method for
|
||||
* @return the mapped method, or {@code null} if none
|
||||
*/
|
||||
public Method getMethod(Exception exception) {
|
||||
Class<? extends Exception> exceptionType = exception.getClass();
|
||||
Method method = resolvedExceptionTypes.get(exceptionType);
|
||||
if (method == null) {
|
||||
method = resolveExceptionType(exceptionType);
|
||||
resolvedExceptionTypes.put(exceptionType, method);
|
||||
}
|
||||
return (method != NO_METHOD_FOUND) ? method : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given exception type by iterating mapped exception types.
|
||||
* Uses {@link #getBestMatchingExceptionType(List, Class)} to select the best match.
|
||||
* @param exceptionType the exception type to resolve
|
||||
* @return the best matching method, or {@link ExceptionMethodMapping#NO_METHOD_FOUND}
|
||||
*/
|
||||
protected final Method resolveExceptionType(Class<? extends Exception> exceptionType) {
|
||||
List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
|
||||
for(Class<? extends Throwable> mappedExceptionType : mappedExceptionTypes.keySet()) {
|
||||
if (mappedExceptionType.isAssignableFrom(exceptionType)) {
|
||||
matches.add(mappedExceptionType);
|
||||
}
|
||||
}
|
||||
if (matches.isEmpty()) {
|
||||
return NO_METHOD_FOUND;
|
||||
}
|
||||
else {
|
||||
return mappedExceptionTypes.get(getBestMatchingExceptionType(matches, exceptionType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the best match from the given list of exception types.
|
||||
*/
|
||||
protected Class<? extends Throwable> getBestMatchingExceptionType(List<Class<? extends Throwable>> exceptionTypes,
|
||||
Class<? extends Exception> exceptionType) {
|
||||
Assert.isTrue(exceptionTypes.size() > 0, "No exception types to select from!");
|
||||
if (exceptionTypes.size() > 1) {
|
||||
Collections.sort(exceptionTypes, new ExceptionDepthComparator(exceptionType));
|
||||
}
|
||||
return exceptionTypes.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.support.DefaultDataBinderFactory;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
|
||||
/**
|
||||
* A specialization of {@link DefaultDataBinderFactory} that further initializes {@link WebDataBinder} instances
|
||||
* through the invocation one or more {@link InitBinder} methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class InitBinderMethodDataBinderFactory extends DefaultDataBinderFactory {
|
||||
|
||||
private final List<InvocableHandlerMethod> initBinderMethods;
|
||||
|
||||
/**
|
||||
* Create an {@code InitBinderMethodDataBinderFactory} instance with the given {@link InitBinder} methods.
|
||||
* @param initBinderMethods {@link InitBinder} methods to use when initializing new data binder instances
|
||||
* @param bindingInitializer a {@link WebBindingInitializer} to initialize new data binder instances with
|
||||
*/
|
||||
public InitBinderMethodDataBinderFactory(List<InvocableHandlerMethod> initBinderMethods,
|
||||
WebBindingInitializer bindingInitializer) {
|
||||
super(bindingInitializer);
|
||||
this.initBinderMethods = initBinderMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link WebDataBinder} for the given target object and initialize it through the invocation
|
||||
* of {@link InitBinder} methods. Only {@link InitBinder} annotations that don't specify attributes names
|
||||
* and {@link InitBinder} annotations that contain the target object name are invoked.
|
||||
* @see InitBinder#value()
|
||||
*/
|
||||
@Override
|
||||
public WebDataBinder createBinder(NativeWebRequest request, Object target, String objectName) throws Exception {
|
||||
WebDataBinder dataBinder = super.createBinder(request, target, objectName);
|
||||
|
||||
for (InvocableHandlerMethod binderMethod : this.initBinderMethods) {
|
||||
InitBinder annot = binderMethod.getMethodAnnotation(InitBinder.class);
|
||||
Set<String> attributeNames = new HashSet<String>(Arrays.asList(annot.value()));
|
||||
|
||||
if (attributeNames.size() == 0 || attributeNames.contains(objectName)) {
|
||||
Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
|
||||
|
||||
if (returnValue != null) {
|
||||
throw new IllegalStateException("InitBinder methods must not have a return value: " + binderMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dataBinder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
import org.springframework.web.HttpSessionRequiredException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
|
||||
/**
|
||||
* Provides methods to create and update the "implicit" model for a given request.
|
||||
*
|
||||
* <p>{@link #createModel(NativeWebRequest, HandlerMethod)} prepares a model for use with
|
||||
* a {@link RequestMapping} method. The model is populated with handler session attributes as well
|
||||
* as request attributes obtained by invoking model attribute methods.
|
||||
*
|
||||
* <p>{@link #updateAttributes(NativeWebRequest, SessionStatus, ModelMap, ModelMap)} updates
|
||||
* the model used for the invocation of a {@link RequestMapping} method, adding handler session
|
||||
* attributes and {@link BindingResult} structures as necessary.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class ModelFactory {
|
||||
|
||||
private final List<InvocableHandlerMethod> attributeMethods;
|
||||
|
||||
private final WebDataBinderFactory binderFactory;
|
||||
|
||||
private final SessionAttributesHandler sessionHandler;
|
||||
|
||||
/**
|
||||
* Create a ModelFactory instance with the provided {@link ModelAttribute} methods.
|
||||
* @param attributeMethods {@link ModelAttribute}-annotated methods to invoke when populating a model
|
||||
* @param binderFactory the binder factory to use to add {@link BindingResult} instances to the model
|
||||
* @param sessionHandler a session attributes handler to synch attributes in the model with the session
|
||||
*/
|
||||
public ModelFactory(List<InvocableHandlerMethod> attributeMethods,
|
||||
WebDataBinderFactory binderFactory,
|
||||
SessionAttributesHandler sessionHandler) {
|
||||
this.attributeMethods = attributeMethods;
|
||||
this.binderFactory = binderFactory;
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a model for the current request obtaining attributes in the following order:
|
||||
* <ol>
|
||||
* <li>Retrieve previously accessed handler session attributes from the session
|
||||
* <li>Invoke model attribute methods
|
||||
* <li>Find request-handling method {@link ModelAttribute}-annotated arguments that are handler session attributes
|
||||
* </ol>
|
||||
* <p>As a general rule a model attribute is added only once following the above order.
|
||||
* @param request the current request
|
||||
* @param requestMethod the request handling method for which the model is needed
|
||||
* @return the created model
|
||||
* @throws Exception if an exception occurs while invoking model attribute methods
|
||||
*/
|
||||
public ModelMap createModel(NativeWebRequest request, HandlerMethod requestMethod) throws Exception {
|
||||
ExtendedModelMap model = new BindingAwareModelMap();
|
||||
|
||||
Map<String, ?> sessionAttributes = this.sessionHandler.retrieveHandlerSessionAttributes(request);
|
||||
model.addAllAttributes(sessionAttributes);
|
||||
|
||||
invokeAttributeMethods(request, model);
|
||||
|
||||
addSessionAttributesByName(request, requestMethod, model);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the model by invoking model attribute methods. If two methods provide the same attribute,
|
||||
* the attribute produced by the first method is used.
|
||||
*/
|
||||
private void invokeAttributeMethods(NativeWebRequest request, ExtendedModelMap model) throws Exception {
|
||||
for (InvocableHandlerMethod attrMethod : this.attributeMethods) {
|
||||
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
|
||||
if (StringUtils.hasText(modelName) && model.containsAttribute(modelName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object returnValue = attrMethod.invokeForRequest(request, model);
|
||||
|
||||
if (!attrMethod.isVoid()){
|
||||
String valueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
|
||||
if (!model.containsAttribute(valueName)) {
|
||||
model.addAttribute(valueName, returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the model name for the given method return value using one of the following:
|
||||
* <ol>
|
||||
* <li>The method {@link ModelAttribute} annotation value
|
||||
* <li>The name of the return type
|
||||
* <li>The name of the return value type if the method return type is {@code Object}
|
||||
* </ol>
|
||||
* @param returnValue the value returned from a method invocation
|
||||
* @param returnType the return type of the method
|
||||
* @return the model name, never {@code null} nor empty
|
||||
*/
|
||||
public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) {
|
||||
ModelAttribute annot = returnType.getMethodAnnotation(ModelAttribute.class);
|
||||
if (annot != null && StringUtils.hasText(annot.value())) {
|
||||
return annot.value();
|
||||
}
|
||||
else {
|
||||
Method method = returnType.getMethod();
|
||||
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getDeclaringClass());
|
||||
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find request-handling method, {@link ModelAttribute}-annotated arguments that are handler session attributes
|
||||
* and add them to the model if not present.
|
||||
*/
|
||||
private void addSessionAttributesByName(NativeWebRequest request, HandlerMethod requestMethod, ModelMap model) {
|
||||
for (MethodParameter parameter : requestMethod.getMethodParameters()) {
|
||||
if (!parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||
continue;
|
||||
}
|
||||
String attrName = getNameForParameter(parameter);
|
||||
if (!model.containsKey(attrName)) {
|
||||
if (sessionHandler.isHandlerSessionAttribute(attrName, parameter.getParameterType())) {
|
||||
Object attrValue = sessionHandler.retrieveAttribute(request, attrName);
|
||||
if (attrValue == null){
|
||||
new HttpSessionRequiredException("Session attribute '" + attrName + "' not found in session");
|
||||
}
|
||||
model.addAttribute(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives the model name for the given method parameter using one of the following:
|
||||
* <ol>
|
||||
* <li>The parameter {@link ModelAttribute} annotation value
|
||||
* <li>The name of the parameter type
|
||||
* </ol>
|
||||
* @return the method parameter model name, never {@code null} or an empty string
|
||||
*/
|
||||
public static String getNameForParameter(MethodParameter parameter) {
|
||||
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||
String attrName = (annot != null) ? annot.value() : null;
|
||||
return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up handler session attributes when {@link SessionStatus#isComplete()} is {@code true}.
|
||||
* Promote model attributes to the session. Add {@link BindingResult} attributes where missing.
|
||||
* @param request the current request
|
||||
* @param sessionStatus indicates whether handler session attributes are to be cleaned
|
||||
* @param actualModel the model returned from the request method, or {@code null} when the response was handled
|
||||
* @param implicitModel the model for the request
|
||||
* @throws Exception if the process of creating {@link BindingResult} attributes causes a problem
|
||||
*/
|
||||
public void updateAttributes(NativeWebRequest request,
|
||||
SessionStatus sessionStatus,
|
||||
ModelMap actualModel,
|
||||
ModelMap implicitModel) throws Exception {
|
||||
if (sessionStatus.isComplete()){
|
||||
this.sessionHandler.cleanupHandlerSessionAttributes(request);
|
||||
}
|
||||
|
||||
if (actualModel != null) {
|
||||
this.sessionHandler.storeHandlerSessionAttributes(request, actualModel);
|
||||
updateBindingResult(request, actualModel);
|
||||
}
|
||||
else {
|
||||
this.sessionHandler.storeHandlerSessionAttributes(request, implicitModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link BindingResult} structures to the model for attributes that require it.
|
||||
*/
|
||||
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
|
||||
List<String> keyNames = new ArrayList<String>(model.keySet());
|
||||
for (String name : keyNames) {
|
||||
Object value = model.get(name);
|
||||
|
||||
if (isBindingCandidate(name, value)) {
|
||||
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
|
||||
|
||||
if (!model.containsAttribute(bindingResultKey)) {
|
||||
WebDataBinder dataBinder = binderFactory.createBinder(request, value, name);
|
||||
model.put(bindingResultKey, dataBinder.getBindingResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given attribute requires a {@link BindingResult} added to the model.
|
||||
*/
|
||||
private boolean isBindingCandidate(String attributeName, Object value) {
|
||||
if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<?> attrType = (value != null) ? value.getClass() : null;
|
||||
if (this.sessionHandler.isHandlerSessionAttribute(attributeName, attrType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
|
||||
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Provides operations for managing handler-specific session attributes as defined by the
|
||||
* {@link SessionAttributes} type-level annotation performing all operations through an
|
||||
* instance of a {@link SessionAttributeStore}.
|
||||
*
|
||||
* <p>A typical scenario involves a handler adding attributes to the {@link Model} during
|
||||
* a request. At the end of the request, model attributes that match to session attribute
|
||||
* names defined through an {@link SessionAttributes} annotation are automatically
|
||||
* "promoted" to the session. Handler session attributes are then removed when
|
||||
* {@link SessionStatus#setComplete()} is called by a handler.
|
||||
*
|
||||
* <p>Therefore "session attributes" for this class means only attributes that have been
|
||||
* previously confirmed by calls to {@link #isHandlerSessionAttribute(String, Class)}.
|
||||
* Attribute names that have never been resolved that way will be filtered out from
|
||||
* operations of this class. That means initially the actual set of resolved session
|
||||
* attribute names is empty and it grows gradually as attributes are added to
|
||||
* the {@link Model} and then considered for being added to the session.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SessionAttributesHandler {
|
||||
|
||||
private final Set<String> attributeNames = new HashSet<String>();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final Set<Class> attributeTypes = new HashSet<Class>();
|
||||
|
||||
private final Set<String> resolvedAttributeNames = Collections.synchronizedSet(new HashSet<String>(4));
|
||||
|
||||
private final SessionAttributeStore attributeStore;
|
||||
|
||||
/**
|
||||
* Creates a {@link SessionAttributesHandler} instance for the specified handlerType.
|
||||
* <p>Inspects the given handler type for the presence of a {@link SessionAttributes} annotation and
|
||||
* stores that information for use in subsequent calls to {@link #isHandlerSessionAttribute(String, Class)}.
|
||||
* If the handler type does not contain such an annotation,
|
||||
* {@link #isHandlerSessionAttribute(String, Class)} always returns {@code false} and all other operations
|
||||
* on handler session attributes have no effect on the backend session.
|
||||
* <p>Use {@link #hasSessionAttributes()} to check if the handler type has defined any session attribute names
|
||||
* of interest through a {@link SessionAttributes} annotation.
|
||||
* @param handlerType the handler type to inspect for a {@link SessionAttributes} annotation
|
||||
* @param attributeStore the {@link SessionAttributeStore} to delegate to for the actual backend session access
|
||||
*/
|
||||
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore attributeStore) {
|
||||
Assert.notNull(attributeStore, "SessionAttributeStore may not be null.");
|
||||
this.attributeStore = attributeStore;
|
||||
|
||||
SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
|
||||
if (annotation != null) {
|
||||
this.attributeNames.addAll(Arrays.asList(annotation.value()));
|
||||
this.attributeTypes.addAll(Arrays.asList(annotation.types()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the handler type has specified any session attribute names of interest through a
|
||||
* {@link SessionAttributes} annotation.
|
||||
*/
|
||||
public boolean hasSessionAttributes() {
|
||||
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether or not an attribute is a handler session attribute of interest as defined
|
||||
* in a {@link SessionAttributes} annotation. Attributes names successfully resolved through
|
||||
* this method are remembered and in other operations.
|
||||
* @param attributeName the attribute name to check, must not be null
|
||||
* @param attributeType the type for the attribute, not required but should be provided when
|
||||
* available as session attributes of interest can be matched by type
|
||||
*/
|
||||
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
|
||||
Assert.notNull(attributeName, "Attribute name must not be null");
|
||||
if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
|
||||
this.resolvedAttributeNames.add(attributeName);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the specified attribute through the underlying {@link SessionAttributeStore}.
|
||||
* Although not required use of this method implies a prior call to
|
||||
* {@link #isHandlerSessionAttribute(String, Class)} has been made to see if the attribute
|
||||
* name is a handler-specific session attribute of interest.
|
||||
* @param request the request for the session operation
|
||||
* @param attributeName the name of the attribute
|
||||
* @return the attribute value or {@code null} if none
|
||||
*/
|
||||
public Object retrieveAttribute(WebRequest request, String attributeName) {
|
||||
return this.attributeStore.retrieveAttribute(request, attributeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve attributes for the underlying handler type from the backend session.
|
||||
* <p>Only attributes that have previously been successfully resolved via calls to
|
||||
* {@link #isHandlerSessionAttribute(String, Class)} are considered.
|
||||
* @param request the current request
|
||||
* @return a map with attributes or an empty map
|
||||
*/
|
||||
public Map<String, ?> retrieveHandlerSessionAttributes(WebRequest request) {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
for (String name : this.resolvedAttributeNames) {
|
||||
Object value = this.attributeStore.retrieveAttribute(request, name);
|
||||
if (value != null) {
|
||||
attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up attributes for the underlying handler type from the backend session.
|
||||
* <p>Only attributes that have previously been successfully resolved via calls to
|
||||
* {@link #isHandlerSessionAttribute(String, Class)} are removed.
|
||||
* @param request the current request
|
||||
*/
|
||||
public void cleanupHandlerSessionAttributes(WebRequest request) {
|
||||
for (String attributeName : this.resolvedAttributeNames) {
|
||||
this.attributeStore.cleanupAttribute(request, attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store attributes in the backend session.
|
||||
* <p>Only attributes that have previously been successfully resolved via calls to
|
||||
* {@link #isHandlerSessionAttribute(String, Class)} are stored. All other attributes
|
||||
* from the input map are ignored.
|
||||
* @param request the current request
|
||||
* @param attributes the attribute pairs to consider for storing
|
||||
*/
|
||||
public void storeHandlerSessionAttributes(WebRequest request, Map<String, Object> attributes) {
|
||||
for (String name : attributes.keySet()) {
|
||||
Object value = attributes.get(name);
|
||||
Class<?> attrType = (value != null) ? value.getClass() : null;
|
||||
|
||||
if (isHandlerSessionAttribute(name, attrType)) {
|
||||
this.attributeStore.storeAttribute(request, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestScope;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Abstract base class for argument resolvers that resolve named values.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private final BeanExpressionContext expressionContext;
|
||||
|
||||
private Map<MethodParameter, NamedValueInfo> namedValueInfoCache =
|
||||
new ConcurrentHashMap<MethodParameter, NamedValueInfo>();
|
||||
|
||||
public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
||||
|
||||
Object arg = resolveNamedValueArgument(webRequest, parameter, namedValueInfo.name);
|
||||
|
||||
if (arg == null) {
|
||||
if (namedValueInfo.defaultValue != null) {
|
||||
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
||||
}
|
||||
else if (namedValueInfo.required) {
|
||||
handleMissingValue(namedValueInfo.name, parameter);
|
||||
}
|
||||
arg = checkForNull(namedValueInfo.name, arg, paramType);
|
||||
}
|
||||
|
||||
if (binderFactory != null) {
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
|
||||
return binder.convertIfNecessary(arg, paramType, parameter);
|
||||
}
|
||||
else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
|
||||
NamedValueInfo result = namedValueInfoCache.get(parameter);
|
||||
if (result == null) {
|
||||
NamedValueInfo info = createNamedValueInfo(parameter);
|
||||
String name = info.name;
|
||||
if (name.length() == 0) {
|
||||
name = parameter.getParameterName();
|
||||
if (name == null) {
|
||||
throw new IllegalStateException("No parameter name specified for argument of type [" +
|
||||
parameter.getParameterType().getName() +
|
||||
"], and no parameter name information found in class file either.");
|
||||
}
|
||||
}
|
||||
boolean required = info.required;
|
||||
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
|
||||
|
||||
result = new NamedValueInfo(name, required, defaultValue);
|
||||
namedValueInfoCache.put(parameter, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NamedValueInfo} object for the given method parameter.
|
||||
*
|
||||
* <p>Implementations typically retrieve the method annotation by means of {@link
|
||||
* MethodParameter#getParameterAnnotation(Class)}.
|
||||
*
|
||||
* @param parameter the method parameter
|
||||
* @return the named value information
|
||||
*/
|
||||
protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Resolves the given parameter into a method argument.
|
||||
*
|
||||
* @param webRequest the current web request, allowing access to the native request as well
|
||||
* @param parameter the parameter to resolve to an argument. This parameter must have previously been passed to the
|
||||
* {@link #supportsParameter(org.springframework.core.MethodParameter)} method of this interface, which must have
|
||||
* returned {@code true}.
|
||||
* @param name the name
|
||||
* @return the resolved argument. May be {@code null}.
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
protected abstract Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String name) throws Exception;
|
||||
|
||||
private Object resolveDefaultValue(String value) {
|
||||
if (beanFactory == null) {
|
||||
return value;
|
||||
}
|
||||
String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
|
||||
BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
|
||||
if (exprResolver == null) {
|
||||
return value;
|
||||
}
|
||||
return exprResolver.evaluate(placeholdersResolved, expressionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a named value is required, but
|
||||
* {@link #resolveNamedValueArgument(NativeWebRequest, MethodParameter, String)} returned {@code null}
|
||||
* and there is no default value set.
|
||||
*
|
||||
* <p>Concrete subclasses typically throw an exception in this scenario.
|
||||
*
|
||||
* @param name the name
|
||||
* @param parameter the method parameter
|
||||
*/
|
||||
protected abstract void handleMissingValue(String name, MethodParameter parameter) throws ServletException;
|
||||
|
||||
private Object checkForNull(String name, Object value, Class<?> paramType) {
|
||||
if (value == null) {
|
||||
if (Boolean.TYPE.equals(paramType)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
else if (paramType.isPrimitive()) {
|
||||
throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
|
||||
"' is present but cannot be translated into a null value due to being declared as a " +
|
||||
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the information about a named value, including name, whether it's required and a default value.
|
||||
*/
|
||||
protected static class NamedValueInfo {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final boolean required;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
protected NamedValueInfo(String name, boolean required, String defaultValue) {
|
||||
this.name = name;
|
||||
this.required = required;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with
|
||||
* {@link CookieValue @CookieValue}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public CookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
public UrlPathHelper getUrlPathHelper() {
|
||||
return urlPathHelper;
|
||||
}
|
||||
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
this.urlPathHelper = urlPathHelper;
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(CookieValue.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
CookieValue annotation = parameter.getParameterAnnotation(CookieValue.class);
|
||||
return new CookieValueNamedValueInfo(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String cookieName) throws Exception {
|
||||
|
||||
throw new UnsupportedOperationException("@CookieValue not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String cookieName, MethodParameter parameter) {
|
||||
throw new IllegalStateException(
|
||||
"Missing cookie value '" + cookieName + "' of type [" + parameter.getParameterType().getName() + "]");
|
||||
}
|
||||
|
||||
private static class CookieValueNamedValueInfo extends NamedValueInfo {
|
||||
|
||||
private CookieValueNamedValueInfo(CookieValue annotation) {
|
||||
super(annotation.value(), annotation.required(), annotation.defaultValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* An implementation of {@link HandlerMethodArgumentResolver} that resolves {@link Errors} method parameters.
|
||||
* Such parameters must be preceded by {@link ModelAttribute} parameters as described in {@link RequestMapping}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
return Errors.class.isAssignableFrom(paramType);
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
if (model.size() > 0) {
|
||||
List<String> keys = new ArrayList<String>(model.keySet());
|
||||
String lastKey = keys.get(model.size()-1);
|
||||
if (isBindingResultKey(lastKey)) {
|
||||
return model.get(lastKey);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Errors/BindingResult argument declared "
|
||||
+ "without preceding model attribute. Check your handler method signature!");
|
||||
}
|
||||
|
||||
private boolean isBindingResultKey(String key) {
|
||||
return key.startsWith(BindingResult.MODEL_KEY_PREFIX);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated
|
||||
* with {@link Value @Value}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(Value.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
Value annotation = parameter.getParameterAnnotation(Value.class);
|
||||
return new ExpressionValueNamedValueInfo(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest, MethodParameter parameter, String name)
|
||||
throws Exception {
|
||||
// Only interested in default value resolution
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
|
||||
// Should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static class ExpressionValueNamedValueInfo extends NamedValueInfo {
|
||||
|
||||
private ExpressionValueNamedValueInfo(Value annotation) {
|
||||
super("@Value", false, annotation.value());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.bind.support.WebRequestDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.annotation.ModelFactory;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves model attribute method parameters.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ModelAttributeMethodProcessor
|
||||
implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
private final boolean resolveArgumentsWithoutAnnotations;
|
||||
|
||||
/**
|
||||
* Creates a {@link ModelAttributeMethodProcessor} instance.
|
||||
* @param resolveArgumentsWithoutAnnotations enable default resolution mode in which arguments without
|
||||
* annotations that aren't simple types (see {@link BeanUtils#isSimpleProperty(Class)})
|
||||
* are also treated as model attributes with a default name based on the model attribute type.
|
||||
*/
|
||||
public ModelAttributeMethodProcessor(boolean resolveArgumentsWithoutAnnotations) {
|
||||
this.resolveArgumentsWithoutAnnotations = resolveArgumentsWithoutAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the parameter is annotated with {@link ModelAttribute} or if it is a
|
||||
* simple type without any annotations.
|
||||
*/
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||
return true;
|
||||
}
|
||||
else if (this.resolveArgumentsWithoutAnnotations && !parameter.hasParameterAnnotations()) {
|
||||
return !BeanUtils.isSimpleProperty(parameter.getParameterType());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link WebDataBinder} for the target model attribute and applies data binding to it.
|
||||
* The model attribute may be obtained from the "implicit" model, from the session via, or by
|
||||
* direct instantiation.
|
||||
*
|
||||
* @throws Exception if invoking an data binder initialization fails or if data binding and/or
|
||||
* validation results in errors and the next method parameter is not of type {@link Errors}.
|
||||
*/
|
||||
public final Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
WebDataBinder binder = createDataBinder(parameter, model, webRequest, binderFactory);
|
||||
|
||||
if (binder.getTarget() != null) {
|
||||
doBind(binder, webRequest);
|
||||
|
||||
if (shouldValidate(parameter)) {
|
||||
binder.validate();
|
||||
}
|
||||
|
||||
if (failOnError(parameter) && binder.getBindingResult().hasErrors()) {
|
||||
throw new BindException(binder.getBindingResult());
|
||||
}
|
||||
}
|
||||
|
||||
model.putAll(binder.getBindingResult().getModel());
|
||||
|
||||
return binder.getTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link WebDataBinder} for a target object which may be obtained from the "implicit" model,
|
||||
* the session via {@link SessionAttributeStore}, or by direct instantiation.
|
||||
*/
|
||||
private WebDataBinder createDataBinder(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
String attrName = ModelFactory.getNameForParameter(parameter);
|
||||
|
||||
Object target;
|
||||
if (model.containsKey(attrName)) {
|
||||
target = model.get(attrName);
|
||||
}
|
||||
else {
|
||||
target = BeanUtils.instantiateClass(parameter.getParameterType());
|
||||
}
|
||||
|
||||
return binderFactory.createBinder(webRequest, target, attrName);
|
||||
}
|
||||
|
||||
protected void doBind(WebDataBinder binder, NativeWebRequest request) {
|
||||
((WebRequestDataBinder) binder).bind(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if {@link DataBinder#validate()} should be invoked, false otherwise.
|
||||
*/
|
||||
protected boolean shouldValidate(MethodParameter parameter) {
|
||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||
for (Annotation annot : annotations) {
|
||||
if ("Valid".equals(annot.annotationType().getSimpleName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the binding or validation errors should result in a {@link BindException}, false otherwise.
|
||||
*/
|
||||
protected boolean failOnError(MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
|
||||
return !hasBindingResult;
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return returnType.getMethodAnnotation(ModelAttribute.class) != null;
|
||||
}
|
||||
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
|
||||
mavContainer.addModelAttribute(name, returnValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves {@link Model} and {@link Map} method parameters.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
return Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType);
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
if (Model.class.isAssignableFrom(paramType)) {
|
||||
return model;
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(paramType)) {
|
||||
return model;
|
||||
}
|
||||
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
Class<?> paramType = returnType.getParameterType();
|
||||
boolean hasModelAttr = returnType.getMethodAnnotation(ModelAttribute.class) != null;
|
||||
|
||||
return (Model.class.isAssignableFrom(paramType)
|
||||
|| (Map.class.isAssignableFrom(paramType) && !hasModelAttr));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
if (returnValue instanceof Model) {
|
||||
mavContainer.addModelAttributes((Model) returnValue);
|
||||
}
|
||||
else {
|
||||
mavContainer.addModelAttributes((Map) returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
|
||||
* {@link RequestHeader @RequestHeader}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestHeader.class)
|
||||
&& Map.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
if (MultiValueMap.class.isAssignableFrom(paramType)) {
|
||||
MultiValueMap<String, String> result;
|
||||
if (HttpHeaders.class.isAssignableFrom(paramType)) {
|
||||
result = new HttpHeaders();
|
||||
}
|
||||
else {
|
||||
result = new LinkedMultiValueMap<String, String>();
|
||||
}
|
||||
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
|
||||
String headerName = iterator.next();
|
||||
for (String headerValue : webRequest.getHeaderValues(headerName)) {
|
||||
result.add(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Map<String, String> result = new LinkedHashMap<String, String>();
|
||||
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
|
||||
String headerName = iterator.next();
|
||||
String headerValue = webRequest.getHeader(headerName);
|
||||
result.put(headerName, headerValue);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with
|
||||
* {@link RequestHeader @RequestHeader}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestHeader.class)
|
||||
&& !Map.class.isAssignableFrom(parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
RequestHeader annotation = parameter.getParameterAnnotation(RequestHeader.class);
|
||||
return new RequestHeaderNamedValueInfo(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String headerName) throws Exception {
|
||||
String[] headerValues = webRequest.getHeaderValues(headerName);
|
||||
if (headerValues != null) {
|
||||
return (headerValues.length == 1 ? headerValues[0] : headerValues);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String headerName, MethodParameter parameter) {
|
||||
throw new IllegalStateException(
|
||||
"Missing header '" + headerName + "' of type [" + parameter.getParameterType().getName() + "]");
|
||||
}
|
||||
|
||||
private static class RequestHeaderNamedValueInfo extends NamedValueInfo {
|
||||
|
||||
private RequestHeaderNamedValueInfo(RequestHeader annotation) {
|
||||
super(annotation.value(), annotation.required(), annotation.defaultValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports {@link Map} arguments annotated with
|
||||
* {@link RequestParam @RequestParam}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
RequestParam requestParamAnnot = parameter.getParameterAnnotation(RequestParam.class);
|
||||
if (requestParamAnnot != null) {
|
||||
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
|
||||
return !StringUtils.hasText(requestParamAnnot.value());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
Map<String, String[]> parameterMap = webRequest.getParameterMap();
|
||||
if (MultiValueMap.class.isAssignableFrom(paramType)) {
|
||||
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(parameterMap.size());
|
||||
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
result.add(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Map<String, String> result = new LinkedHashMap<String, String>(parameterMap.size());
|
||||
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
|
||||
if (entry.getValue().length > 0) {
|
||||
result.put(entry.getKey(), entry.getValue()[0]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that supports arguments annotated with
|
||||
* {@link RequestParam @RequestParam}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
private final boolean resolveParamsWithoutAnnotations;
|
||||
|
||||
/**
|
||||
* Creates a {@link RequestParamMethodArgumentResolver} instance.
|
||||
*
|
||||
* @param beanFactory the bean factory to use for resolving default value expressions
|
||||
* @param resolveParamsWithoutAnnotations enable default resolution mode in which parameters without
|
||||
* annotations that are simple types (see {@link BeanUtils#isSimpleProperty(Class)})
|
||||
* are also treated as model attributes with a default name based on the method argument name.
|
||||
*/
|
||||
public RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
boolean resolveParamsWithoutAnnotations) {
|
||||
super(beanFactory);
|
||||
this.resolveParamsWithoutAnnotations = resolveParamsWithoutAnnotations;
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
RequestParam requestParamAnnot = parameter.getParameterAnnotation(RequestParam.class);
|
||||
if (requestParamAnnot != null) {
|
||||
if (Map.class.isAssignableFrom(paramType)) {
|
||||
return StringUtils.hasText(requestParamAnnot.value());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (this.resolveParamsWithoutAnnotations && !parameter.hasParameterAnnotations()) {
|
||||
return BeanUtils.isSimpleProperty(paramType);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
RequestParam annotation = parameter.getParameterAnnotation(RequestParam.class);
|
||||
return (annotation != null) ?
|
||||
new RequestParamNamedValueInfo(annotation) :
|
||||
new RequestParamNamedValueInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNamedValueArgument(NativeWebRequest webRequest,
|
||||
MethodParameter parameter,
|
||||
String paramName) throws Exception {
|
||||
MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
|
||||
if (multipartRequest != null) {
|
||||
List<MultipartFile> files = multipartRequest.getFiles(paramName);
|
||||
if (!files.isEmpty()) {
|
||||
return (files.size() == 1 ? files.get(0) : files);
|
||||
}
|
||||
}
|
||||
|
||||
String[] paramValues = webRequest.getParameterValues(paramName);
|
||||
if (paramValues != null) {
|
||||
return paramValues.length == 1 ? paramValues[0] : paramValues;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String paramName, MethodParameter parameter) throws ServletException {
|
||||
throw new MissingServletRequestParameterException(paramName, parameter.getParameterType().getSimpleName());
|
||||
}
|
||||
|
||||
private class RequestParamNamedValueInfo extends NamedValueInfo {
|
||||
|
||||
private RequestParamNamedValueInfo() {
|
||||
super("", true, ValueConstants.DEFAULT_NONE);
|
||||
}
|
||||
|
||||
private RequestParamNamedValueInfo(RequestParam annotation) {
|
||||
super(annotation.value(), annotation.required(), annotation.defaultValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Adapts a {@link WebArgumentResolver} into the {@link HandlerMethodArgumentResolver} contract.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class WebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final WebArgumentResolver adaptee;
|
||||
|
||||
public WebArgumentResolverAdapter(WebArgumentResolver adaptee) {
|
||||
Assert.notNull(adaptee, "'adaptee' must not be null");
|
||||
this.adaptee = adaptee;
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
try {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
Object result ;
|
||||
if (requestAttributes instanceof NativeWebRequest) {
|
||||
result = adaptee.resolveArgument(parameter, (NativeWebRequest) requestAttributes);
|
||||
}
|
||||
else {
|
||||
result = adaptee.resolveArgument(parameter, null);
|
||||
}
|
||||
if (result == WebArgumentResolver.UNRESOLVED) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return ClassUtils.isAssignableValue(parameter.getParameterType(), result);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// ignore
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
Object result = adaptee.resolveArgument(parameter, webRequest);
|
||||
if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) {
|
||||
throw new IllegalStateException(
|
||||
"Standard argument type [" + paramType.getName() + "] resolved to incompatible value of type [" +
|
||||
(result != null ? result.getClass() : null) +
|
||||
"]. Consider declaring the argument type in a less specific fashion.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface for resolving method parameters into argument values from a given request.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface HandlerMethodArgumentResolver extends HandlerMethodProcessor {
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method parameter} is supported by this resolver.
|
||||
*
|
||||
* @param parameter the method parameter to check
|
||||
* @return {@code true} if this resolver supports the supplied parameter; {@code false} otherwise
|
||||
*/
|
||||
boolean supportsParameter(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Resolves a method parameter into an argument value from a given request and a {@link ModelMap} providing
|
||||
* the ability to both access and add new model attributes. A {@link WebDataBinderFactory} is also provided
|
||||
* for creating a {@link WebDataBinder} instance to use for data binding and type conversion.
|
||||
*
|
||||
* @param parameter the parameter to resolve to an argument. This parameter must have previously been passed to
|
||||
* {@link #supportsParameter(org.springframework.core.MethodParameter)} and it must have returned {@code true}
|
||||
* @param model the model for the current request
|
||||
* @param webRequest the current request.
|
||||
* @param binderFactory a factory in case the resolver needs to create a {@link WebDataBinder} instance
|
||||
* @return the resolved argument value, or {@code null}.
|
||||
* @throws Exception in case of errors with the preparation of argument values
|
||||
*/
|
||||
Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodArgumentResolver} that resolves handler method arguments by delegating
|
||||
* to a list of registered {@link HandlerMethodArgumentResolver}s.
|
||||
*
|
||||
* <p>Previously resolved method argument types are cached internally for faster lookups.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HandlerMethodArgumentResolverContainer implements HandlerMethodArgumentResolver {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(HandlerMethodArgumentResolverContainer.class);
|
||||
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers =
|
||||
new ArrayList<HandlerMethodArgumentResolver>();
|
||||
|
||||
private Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
|
||||
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>();
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method parameter} is supported by any of the
|
||||
* registered {@link HandlerMethodArgumentResolver}s.
|
||||
*/
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return getArgumentResolver(parameter) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a method parameter into an argument value for the given request by iterating over registered
|
||||
* {@link HandlerMethodArgumentResolver}s to find one that supports the given method parameter.
|
||||
*/
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelMap model,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
|
||||
if (resolver != null) {
|
||||
return resolver.resolveArgument(parameter, model, webRequest, binderFactory);
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"No suitable HandlerMethodArgumentResolver found. " +
|
||||
"supportsParameter(MethodParameter) should have been called previously.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
|
||||
* @return a {@link HandlerMethodArgumentResolver} instance, or {@code null} if none
|
||||
*/
|
||||
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
|
||||
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
|
||||
if (result == null) {
|
||||
for (HandlerMethodArgumentResolver methodArgumentResolver : argumentResolvers) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
|
||||
parameter.getGenericParameterType() + "]");
|
||||
}
|
||||
if (methodArgumentResolver.supportsParameter(parameter)) {
|
||||
result = methodArgumentResolver;
|
||||
this.argumentResolverCache.put(parameter, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the argument resolver that supports the given method parameter uses the response argument.
|
||||
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
|
||||
*/
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
|
||||
return (resolver != null && resolver.usesResponseArgument(parameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given {@link HandlerMethodArgumentResolver}.
|
||||
*/
|
||||
public void registerArgumentResolver(HandlerMethodArgumentResolver argumentResolver) {
|
||||
this.argumentResolvers.add(argumentResolver);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
/**
|
||||
* A base interface for {@link HandlerMethodArgumentResolver}s and {@link HandlerMethodReturnValueHandler}s.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface HandlerMethodProcessor {
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain org.springframework.core.MethodParameter method parameter},
|
||||
* uses the response argument with the implication that the response will be handled directly by invoking
|
||||
* the method and will not require view name resolution followed by rendering.
|
||||
*
|
||||
* @param parameter the method parameter, either a method argument or a return type
|
||||
* @return {@code true} if the supplied parameter uses the response argument; {@code false} otherwise
|
||||
*/
|
||||
boolean usesResponseArgument(MethodParameter parameter);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface to process the value returned from a handler method invocation.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface HandlerMethodReturnValueHandler extends HandlerMethodProcessor {
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method return type} is supported by this handler.
|
||||
*
|
||||
* @param returnType the method return type to check
|
||||
* @return {@code true} if this handler supports the supplied return type; {@code false} otherwise
|
||||
*/
|
||||
boolean supportsReturnType(MethodParameter returnType);
|
||||
|
||||
/**
|
||||
* Handles the given value returned by a handler method invocation by writing directly to the response
|
||||
* or by using the {@code mavContainer} argument to add model attributes and/or set the view.
|
||||
*
|
||||
* @param returnValue the return value to handle
|
||||
* @param returnType the return type to handle. This type must have previously been passed to
|
||||
* {@link #supportsReturnType(org.springframework.core.MethodParameter)} and it must have returned {@code true}
|
||||
* @param mavContainer records model and view choices
|
||||
* @param webRequest the current request
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
<V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HandlerMethodReturnValueHandler} that handles method return values by
|
||||
* delegating to a list of registered {@link HandlerMethodReturnValueHandler}s.
|
||||
*
|
||||
* <p>Previously resolved return types are cached internally for faster lookups.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HandlerMethodReturnValueHandlerContainer implements HandlerMethodReturnValueHandler {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(HandlerMethodArgumentResolverContainer.class);
|
||||
|
||||
private List<HandlerMethodReturnValueHandler> returnValueHandlers =
|
||||
new ArrayList<HandlerMethodReturnValueHandler>();
|
||||
|
||||
private Map<MethodParameter, HandlerMethodReturnValueHandler> returnValueHandlerCache =
|
||||
new ConcurrentHashMap<MethodParameter, HandlerMethodReturnValueHandler>();
|
||||
|
||||
/**
|
||||
* Indicates whether the given {@linkplain MethodParameter method return type} is supported by any of the
|
||||
* registered {@link HandlerMethodReturnValueHandler}s.
|
||||
*/
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return getReturnValueHandler(returnType) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the given method return value by iterating over registered {@link HandlerMethodReturnValueHandler}s
|
||||
* to find one that supports it.
|
||||
*/
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
|
||||
if (handler != null) {
|
||||
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"No suitable HandlerMethodReturnValueHandler found. " +
|
||||
"supportsReturnType(MethodParameter) should have been called previously");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given method return type.
|
||||
*/
|
||||
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
|
||||
if (this.returnValueHandlers == null) {
|
||||
return null;
|
||||
}
|
||||
HandlerMethodReturnValueHandler result = this.returnValueHandlerCache.get(returnType);
|
||||
if (result == null) {
|
||||
for (HandlerMethodReturnValueHandler methodReturnValueHandler : returnValueHandlers) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Testing if return value handler [" + methodReturnValueHandler + "] supports [" +
|
||||
returnType.getGenericParameterType() + "]");
|
||||
}
|
||||
if (methodReturnValueHandler.supportsReturnType(returnType)) {
|
||||
result = methodReturnValueHandler;
|
||||
this.returnValueHandlerCache.put(returnType, methodReturnValueHandler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the return value handler that supports the given method parameter uses the response.
|
||||
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
|
||||
*/
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
HandlerMethodReturnValueHandler handler = getReturnValueHandler(parameter);
|
||||
return (handler != null && handler.usesResponseArgument(parameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given {@link HandlerMethodReturnValueHandler}.
|
||||
*/
|
||||
public void registerReturnValueHandler(HandlerMethodReturnValueHandler returnValuehandler) {
|
||||
returnValueHandlers.add(returnValuehandler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
/**
|
||||
* Provides a way to invoke a handler method after resolving its method argument values through
|
||||
* {@link HandlerMethodArgumentResolver}s in the context of the current request.
|
||||
*
|
||||
* <p>Resolving argument values often requires a {@link WebDataBinder} for data binding and type conversion.
|
||||
* Use {@link #setDataBinderFactory(WebDataBinderFactory)} to provide a factory for that. The list of argument
|
||||
* resolvers can be set via {@link #setArgumentResolverContainer(HandlerMethodArgumentResolverContainer)}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class InvocableHandlerMethod extends HandlerMethod {
|
||||
|
||||
private HandlerMethodArgumentResolverContainer argumentResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
|
||||
private WebDataBinderFactory dataBinderFactory;
|
||||
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
/**
|
||||
* Constructs a new invocable handler method with the given bean instance and method.
|
||||
* @param bean the bean instance
|
||||
* @param method the method
|
||||
*/
|
||||
public InvocableHandlerMethod(Object bean, Method method) {
|
||||
super(bean, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new invocable handler method with the given bean instance, method name and parameters.
|
||||
* @param bean the object bean
|
||||
* @param methodName the method name
|
||||
* @param parameterTypes the method parameter types
|
||||
* @throws NoSuchMethodException when the method cannot be found
|
||||
*/
|
||||
public InvocableHandlerMethod(
|
||||
Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
|
||||
super(bean, methodName, parameterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link WebDataBinderFactory} to be passed to argument resolvers that require a
|
||||
* {@link WebDataBinder} to do type conversion or data binding on the method argument value.
|
||||
*
|
||||
* @param dataBinderFactory the data binder factory.
|
||||
*/
|
||||
public void setDataBinderFactory(WebDataBinderFactory dataBinderFactory) {
|
||||
this.dataBinderFactory = dataBinderFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link HandlerMethodArgumentResolver}s to use to use for resolving method argument values.
|
||||
*/
|
||||
public void setArgumentResolverContainer(HandlerMethodArgumentResolverContainer argumentResolvers) {
|
||||
this.argumentResolvers = argumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed
|
||||
* (e.g. for default attribute names).
|
||||
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method after resolving its argument values based on the given request.
|
||||
* <p>Most argument values are resolved with the help of {@link HandlerMethodArgumentResolver}s
|
||||
* configured via {@link #setArgumentResolverContainer(HandlerMethodArgumentResolverContainer)}.
|
||||
* However, the {@code provideArgs} parameter can be used to supply argument values for use
|
||||
* directly rather than relying on argument resolution - e.g. {@link WebDataBinder},
|
||||
* {@link SessionStatus}, or the thrown exception in a HandlerExceptionResolver.
|
||||
* @param request the current request
|
||||
* @param model the model used throughout the current request
|
||||
* @param providedArgs argument values to try to use, thus bypassing argument resolution
|
||||
* @return the raw value returned by the invoked method
|
||||
*/
|
||||
public final Object invokeForRequest(NativeWebRequest request, ModelMap model, Object... providedArgs)
|
||||
throws Exception {
|
||||
|
||||
Object[] args = getMethodArguments(request, model, providedArgs);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
StringBuilder builder = new StringBuilder("Invoking [");
|
||||
builder.append(this.getMethod().getName()).append("] method with arguments ");
|
||||
builder.append(Arrays.asList(args));
|
||||
logger.trace(builder.toString());
|
||||
}
|
||||
|
||||
Object returnValue = invoke(args);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]");
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private Object[] getMethodArguments(NativeWebRequest request, ModelMap model, Object... providedArgs)
|
||||
throws Exception {
|
||||
MethodParameter[] parameters = getMethodParameters();
|
||||
Object[] args = new Object[parameters.length];
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
MethodParameter parameter = parameters[i];
|
||||
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
|
||||
|
||||
args[i] = resolveProvidedArgument(parameter, providedArgs);
|
||||
|
||||
if (args[i] != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.argumentResolvers.supportsParameter(parameter)) {
|
||||
args[i] = this.argumentResolvers.resolveArgument(parameter, model, request, dataBinderFactory);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Cannot resolve argument index=" + parameter.getParameterIndex() + ""
|
||||
+ ", name=" + parameter.getParameterName() + ", type=" + parameter.getParameterType()
|
||||
+ " in method " + toString());
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
|
||||
if (providedArgs == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object providedArg : providedArgs) {
|
||||
if (parameter.getParameterType().isInstance(providedArg)) {
|
||||
return providedArg;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes this handler method with the given argument values.
|
||||
* @param args the argument values
|
||||
* @return the result of the invocation
|
||||
* @throws Exception when the method invocation results in an exception
|
||||
*/
|
||||
private Object invoke(Object... args) throws Exception {
|
||||
ReflectionUtils.makeAccessible(this.getBridgedMethod());
|
||||
try {
|
||||
return getBridgedMethod().invoke(getBean(), args);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
handleIllegalArgumentException(ex, args);
|
||||
throw ex;
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
handleInvocationTargetException(ex);
|
||||
throw new IllegalStateException(
|
||||
"Unexpected exception thrown by method - " + ex.getTargetException().getClass().getName() + ": " +
|
||||
ex.getTargetException().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleIllegalArgumentException(IllegalArgumentException ex, Object... args) {
|
||||
StringBuilder builder = new StringBuilder(ex.getMessage());
|
||||
builder.append(" :: method=").append(getBridgedMethod().toGenericString());
|
||||
builder.append(" :: invoked with handler type=").append(getBeanType().getName());
|
||||
|
||||
if (args != null && args.length > 0) {
|
||||
builder.append(" and argument types ");
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
builder.append(" : arg[").append(i).append("] ").append(args[i].getClass());
|
||||
}
|
||||
}
|
||||
else {
|
||||
builder.append(" and 0 arguments");
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(builder.toString(), ex);
|
||||
}
|
||||
|
||||
private void handleInvocationTargetException(InvocationTargetException ex) throws Exception {
|
||||
Throwable targetException = ex.getTargetException();
|
||||
if (targetException instanceof RuntimeException) {
|
||||
throw (RuntimeException) targetException;
|
||||
}
|
||||
if (targetException instanceof Error) {
|
||||
throw (Error) targetException;
|
||||
}
|
||||
if (targetException instanceof Exception) {
|
||||
throw (Exception) targetException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any of the registered {@link HandlerMethodArgumentResolver}s use the response argument.
|
||||
* @see HandlerMethodProcessor#usesResponseArgument(MethodParameter)
|
||||
*/
|
||||
protected boolean usesResponseArgument() {
|
||||
MethodParameter[] methodParameters = getMethodParameters();
|
||||
for (MethodParameter methodParameter : methodParameters) {
|
||||
if (this.argumentResolvers.usesResponseArgument(methodParameter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
|
||||
/**
|
||||
* Contains model and view choices made by {@link HandlerMethodReturnValueHandler}s.
|
||||
*
|
||||
* <p>Allows return value handlers to set only the bits that are relevant to them - i.e. model, view,
|
||||
* or none, while also taking care of merging attributes added by the {@link HandlerMethodReturnValueHandler}
|
||||
* with attributes from the implicit model.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @param <V> Servlet or Portlet specific View type.
|
||||
*/
|
||||
public class ModelAndViewContainer<V> {
|
||||
|
||||
private String viewName;
|
||||
|
||||
private V view;
|
||||
|
||||
private final ModelMap actualModel = new ExtendedModelMap();
|
||||
|
||||
private final ModelMap implicitModel;
|
||||
|
||||
public ModelAndViewContainer(ModelMap implicitModel) {
|
||||
this.implicitModel = (implicitModel != null) ? implicitModel : new ExtendedModelMap();
|
||||
}
|
||||
|
||||
public ModelMap getModel() {
|
||||
return new ExtendedModelMap().addAllAttributes(actualModel).mergeAttributes(implicitModel);
|
||||
}
|
||||
|
||||
public String getViewName() {
|
||||
return this.viewName;
|
||||
}
|
||||
|
||||
public void setViewName(String viewName) {
|
||||
this.viewName = viewName;
|
||||
}
|
||||
|
||||
public V getView() {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
public void setView(V view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public void addModelAttributes(Model attributes) {
|
||||
actualModel.addAllAttributes(attributes.asMap());
|
||||
}
|
||||
|
||||
public void addModelAttributes(Map<String, Object> attributes) {
|
||||
actualModel.addAllAttributes(attributes);
|
||||
}
|
||||
|
||||
public void addModelAttribute(String name, Object value) {
|
||||
actualModel.addAttribute(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.SessionAttributesHandler;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link SessionAttributesHandler} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HandlerSessionAttributeStoreTests {
|
||||
|
||||
private DefaultSessionAttributeStore sessionAttributes;
|
||||
|
||||
private SessionAttributesHandler handlerSessionAttributes;
|
||||
|
||||
private Class<?> handlerType = SessionAttributeHandler.class;
|
||||
|
||||
private NativeWebRequest request;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.sessionAttributes = new DefaultSessionAttributeStore();
|
||||
this.handlerSessionAttributes = new SessionAttributesHandler(handlerType, sessionAttributes);
|
||||
|
||||
this.request = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSessionAttribute() throws Exception {
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr1", null));
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr2", null));
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("simple", TestBean.class));
|
||||
|
||||
assertFalse("Attribute name not known", handlerSessionAttributes.isHandlerSessionAttribute("simple", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveAttributes() throws Exception {
|
||||
sessionAttributes.storeAttribute(request, "attr1", "value1");
|
||||
sessionAttributes.storeAttribute(request, "attr2", "value2");
|
||||
sessionAttributes.storeAttribute(request, "attr3", new TestBean());
|
||||
|
||||
// Query attributes to associate them with the handler type
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr1", null));
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr3", TestBean.class));
|
||||
|
||||
Map<String, ?> attributes = handlerSessionAttributes.retrieveHandlerSessionAttributes(request);
|
||||
|
||||
assertEquals(new HashSet<String>(asList("attr1", "attr3")), attributes.keySet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupAttribute() throws Exception {
|
||||
sessionAttributes.storeAttribute(request, "attr1", "value1");
|
||||
sessionAttributes.storeAttribute(request, "attr2", "value2");
|
||||
sessionAttributes.storeAttribute(request, "attr3", new TestBean());
|
||||
|
||||
// Query attribute to associate it with the handler type
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr1", null));
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr3", TestBean.class));
|
||||
|
||||
handlerSessionAttributes.cleanupHandlerSessionAttributes(request);
|
||||
|
||||
assertNull(sessionAttributes.retrieveAttribute(request, "attr1"));
|
||||
assertNotNull(sessionAttributes.retrieveAttribute(request, "attr2"));
|
||||
assertNull(sessionAttributes.retrieveAttribute(request, "attr3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeAttributes() throws Exception {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
attributes.put("attr1", "value1");
|
||||
attributes.put("attr2", "value2");
|
||||
attributes.put("attr3", new TestBean());
|
||||
|
||||
// Query attribute to associate it with the handler type
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr1", null));
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr2", null));
|
||||
assertTrue(handlerSessionAttributes.isHandlerSessionAttribute("attr3", TestBean.class));
|
||||
|
||||
handlerSessionAttributes.storeHandlerSessionAttributes(request, attributes);
|
||||
|
||||
assertEquals("value1", sessionAttributes.retrieveAttribute(request, "attr1"));
|
||||
assertEquals("value2", sessionAttributes.retrieveAttribute(request, "attr2"));
|
||||
assertTrue(sessionAttributes.retrieveAttribute(request, "attr3") instanceof TestBean);
|
||||
}
|
||||
|
||||
@SessionAttributes(value = { "attr1", "attr2" }, types = { TestBean.class })
|
||||
private static class SessionAttributeHandler {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.bind.support.DefaultDataBinderFactory;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.RequestParamMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverContainer;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link InitBinderMethodDataBinderFactory} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class InitBinderMethodDataBinderFactoryTests {
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private ConfigurableWebBindingInitializer bindingInitializer;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.webRequest = new ServletWebRequest(request);
|
||||
this.bindingInitializer = new ConfigurableWebBindingInitializer();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinder() throws Exception {
|
||||
InitBinderMethodDataBinderFactory factory = createFactory("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, null);
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("id", dataBinder.getDisallowedFields()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithGlobalInitialization() throws Exception {
|
||||
ConversionService conversionService = new DefaultFormattingConversionService();
|
||||
bindingInitializer.setConversionService(conversionService );
|
||||
|
||||
InitBinderMethodDataBinderFactory factory = createFactory("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, null);
|
||||
|
||||
assertSame(conversionService, dataBinder.getConversionService());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithAttrName() throws Exception {
|
||||
InitBinderMethodDataBinderFactory factory = createFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, "foo");
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("id", dataBinder.getDisallowedFields()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithAttrNameNoMatch() throws Exception {
|
||||
WebDataBinderFactory factory = createFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, "invalidName");
|
||||
|
||||
assertNull(dataBinder.getDisallowedFields());
|
||||
}
|
||||
|
||||
@Test(expected=IllegalStateException.class)
|
||||
public void returnValueNotExpected() throws Exception {
|
||||
WebDataBinderFactory factory = createFactory("initBinderReturnValue", WebDataBinder.class);
|
||||
factory.createBinder(webRequest, null, "invalidName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderTypeConversion() throws Exception {
|
||||
request.setParameter("requestParam", "22");
|
||||
|
||||
HandlerMethodArgumentResolverContainer argResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
argResolvers.registerArgumentResolver(new RequestParamMethodArgumentResolver(null, false));
|
||||
|
||||
String methodName = "initBinderTypeConversion";
|
||||
WebDataBinderFactory factory = createFactory(argResolvers, methodName, WebDataBinder.class, int.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, "foo");
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("requestParam-22", dataBinder.getDisallowedFields()[0]);
|
||||
}
|
||||
|
||||
private InitBinderMethodDataBinderFactory createFactory(String methodName, Class<?>... parameterTypes)
|
||||
throws Exception {
|
||||
return createFactory(new HandlerMethodArgumentResolverContainer(), methodName, parameterTypes);
|
||||
}
|
||||
|
||||
private InitBinderMethodDataBinderFactory createFactory(HandlerMethodArgumentResolverContainer argResolvers,
|
||||
String methodName, Class<?>... parameterTypes) throws Exception {
|
||||
Object handler = new InitBinderHandler();
|
||||
Method method = InitBinderHandler.class.getMethod(methodName, parameterTypes);
|
||||
|
||||
InvocableHandlerMethod controllerMethod = new InvocableHandlerMethod(handler, method);
|
||||
controllerMethod.setArgumentResolverContainer(argResolvers);
|
||||
controllerMethod.setDataBinderFactory(new DefaultDataBinderFactory(null));
|
||||
controllerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||
|
||||
return new InitBinderMethodDataBinderFactory(Arrays.asList(controllerMethod), bindingInitializer);
|
||||
}
|
||||
|
||||
private static class InitBinderHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder dataBinder) {
|
||||
dataBinder.setDisallowedFields("id");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@InitBinder(value="foo")
|
||||
public void initBinderWithAttributeName(WebDataBinder dataBinder) {
|
||||
dataBinder.setDisallowedFields("id");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@InitBinder
|
||||
public String initBinderReturnValue(WebDataBinder dataBinder) {
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@InitBinder
|
||||
public void initBinderTypeConversion(WebDataBinder dataBinder, @RequestParam int requestParam) {
|
||||
dataBinder.setDisallowedFields("requestParam-" + requestParam);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.ModelMethodProcessor;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverContainer;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
|
||||
/**
|
||||
* Text fixture for {@link ModelFactory} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ModelFactoryTests {
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private SessionAttributeStore sessionAttributeStore;
|
||||
|
||||
private SessionAttributesHandler handlerSessionAttributeStore;
|
||||
|
||||
private InvocableHandlerMethod requestMethod;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Object handler = new ModelHandler();
|
||||
Method method = handler.getClass().getDeclaredMethod("handle");
|
||||
this.requestMethod = new InvocableHandlerMethod(handler, method);
|
||||
|
||||
this.sessionAttributeStore = new DefaultSessionAttributeStore();
|
||||
this.handlerSessionAttributeStore = new SessionAttributesHandler(handler.getClass(), sessionAttributeStore);
|
||||
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createModel() throws Exception {
|
||||
ModelMap model = createFactory(new ModelHandler(), "model", Model.class).createModel(webRequest, requestMethod);
|
||||
assertEquals(Boolean.TRUE, model.get("model"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createModelWithName() throws Exception {
|
||||
ModelMap model = createFactory(new ModelHandler(), "modelWithName").createModel(webRequest, requestMethod);
|
||||
assertEquals(Boolean.TRUE, model.get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createModelWithDefaultName() throws Exception {
|
||||
ModelMap model = createFactory(new ModelHandler(), "modelWithDefaultName").createModel(webRequest, requestMethod);
|
||||
assertEquals(Boolean.TRUE, model.get("boolean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createModelWithExistingName() throws Exception {
|
||||
ModelMap model = createFactory(new ModelHandler(), "modelWithName").createModel(webRequest, requestMethod);
|
||||
assertEquals(Boolean.TRUE, model.get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createModelWithNullAttribute() throws Exception {
|
||||
ModelMap model = createFactory(new ModelHandler(), "modelWithNullAttribute").createModel(webRequest, requestMethod);
|
||||
assertTrue(model.containsKey("name"));
|
||||
assertNull(model.get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createModelExistingSessionAttributes() throws Exception {
|
||||
sessionAttributeStore.storeAttribute(webRequest, "sessionAttr", "sessionAttrValue");
|
||||
|
||||
// Query attribute to associate it with the handler type
|
||||
assertTrue(handlerSessionAttributeStore.isHandlerSessionAttribute("sessionAttr", null));
|
||||
|
||||
ModelMap model = createFactory(new ModelHandler(), "model", Model.class).createModel(webRequest, requestMethod);
|
||||
assertEquals("sessionAttrValue", model.get("sessionAttr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBindingResult() throws Exception {
|
||||
Object handler = new ModelHandler();
|
||||
|
||||
SessionAttributeStore store = new DefaultSessionAttributeStore();
|
||||
SessionAttributesHandler sessionAttributeStore = new SessionAttributesHandler(handler.getClass(), store);
|
||||
|
||||
String attrName = "attr1";
|
||||
Object attrValue = new Object();
|
||||
|
||||
ModelMap actualModel = new ExtendedModelMap();
|
||||
actualModel.addAttribute(attrName, attrValue);
|
||||
|
||||
WebDataBinder dataBinder = new WebDataBinder(attrValue, attrName);
|
||||
|
||||
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
|
||||
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
|
||||
replay(binderFactory);
|
||||
|
||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttributeStore);
|
||||
modelFactory.updateAttributes(webRequest, new SimpleSessionStatus(), actualModel, null);
|
||||
|
||||
assertEquals(attrValue, actualModel.remove(attrName));
|
||||
assertSame(dataBinder.getBindingResult(), actualModel.remove(bindingResultKey(attrName)));
|
||||
assertEquals(0, actualModel.size());
|
||||
|
||||
verify(binderFactory);
|
||||
}
|
||||
|
||||
private String bindingResultKey(String key) {
|
||||
return BindingResult.MODEL_KEY_PREFIX + key;
|
||||
}
|
||||
|
||||
private ModelFactory createFactory(Object handler, String methodName, Class<?>... parameterTypes) throws Exception{
|
||||
Method method = ModelHandler.class.getMethod(methodName, parameterTypes);
|
||||
|
||||
HandlerMethodArgumentResolverContainer argResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
argResolvers.registerArgumentResolver(new ModelMethodProcessor());
|
||||
|
||||
InvocableHandlerMethod controllerMethod = new InvocableHandlerMethod(handler, method);
|
||||
controllerMethod.setArgumentResolverContainer(argResolvers);
|
||||
controllerMethod.setDataBinderFactory(null);
|
||||
controllerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||
|
||||
return new ModelFactory(Arrays.asList(controllerMethod), null, handlerSessionAttributeStore);
|
||||
}
|
||||
|
||||
@SessionAttributes("sessionAttr")
|
||||
private static class ModelHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ModelAttribute
|
||||
public void model(Model model) {
|
||||
model.addAttribute("model", Boolean.TRUE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ModelAttribute("name")
|
||||
public Boolean modelWithName() {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ModelAttribute
|
||||
public Boolean modelWithDefaultName() {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ModelAttribute("name")
|
||||
public Boolean modelWithNullAttribute() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.ErrorsMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ErrorsMethodArgumentResolver} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ErrorsMethodHandlerArgumentResolverTests {
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private ErrorsMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter errorsParam;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
|
||||
this.resolver = new ErrorsMethodArgumentResolver();
|
||||
|
||||
Method method = this.getClass().getDeclaredMethod("handle", Errors.class);
|
||||
this.errorsParam = new MethodParameter(method, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supports() throws Exception {
|
||||
resolver.supportsParameter(errorsParam);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingResult() throws Exception {
|
||||
WebDataBinder dataBinder = new WebDataBinder(new Object(), "attr");
|
||||
BindingResult bindingResult = dataBinder.getBindingResult();
|
||||
|
||||
ModelMap model = new ExtendedModelMap();
|
||||
model.addAttribute("ignore1", "value1");
|
||||
model.addAttribute("ignore2", "value2");
|
||||
model.addAttribute("ignore3", "value3");
|
||||
model.addAttribute("ignore4", "value4");
|
||||
model.addAttribute("ignore5", "value5");
|
||||
model.addAllAttributes(bindingResult.getModel()); // Predictable iteration order of model keys important!
|
||||
|
||||
Object actual = resolver.resolveArgument(errorsParam, model, webRequest, null);
|
||||
|
||||
assertSame(actual, bindingResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingResultExistingModelAttribute() throws Exception {
|
||||
Object target1 = new Object();
|
||||
WebDataBinder binder1 = new WebDataBinder(target1, "attr1");
|
||||
BindingResult bindingResult1 = binder1.getBindingResult();
|
||||
|
||||
Object target2 = new Object();
|
||||
WebDataBinder binder2 = new WebDataBinder(target1, "attr2");
|
||||
BindingResult bindingResult2 = binder2.getBindingResult();
|
||||
|
||||
ModelMap model = new ExtendedModelMap();
|
||||
model.addAttribute("attr1", target1);
|
||||
model.addAttribute("attr2", target2);
|
||||
model.addAttribute("filler", "fillerValue");
|
||||
model.addAllAttributes(bindingResult1.getModel());
|
||||
model.addAllAttributes(bindingResult2.getModel());
|
||||
|
||||
Object actual = resolver.resolveArgument(errorsParam, model, webRequest, null);
|
||||
|
||||
assertSame("Should resolve to the latest BindingResult added", actual, bindingResult2);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalStateException.class)
|
||||
public void bindingResultNotFound() throws Exception {
|
||||
WebDataBinder dataBinder = new WebDataBinder(new Object(), "attr");
|
||||
BindingResult bindingResult = dataBinder.getBindingResult();
|
||||
|
||||
ModelMap model = new ExtendedModelMap();
|
||||
model.addAllAttributes(bindingResult.getModel());
|
||||
model.addAttribute("ignore1", "value1");
|
||||
|
||||
resolver.resolveArgument(errorsParam, model, webRequest, null);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalStateException.class)
|
||||
public void noBindingResult() throws Exception {
|
||||
resolver.resolveArgument(errorsParam, new ExtendedModelMap(), webRequest, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handle(Errors errors) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.method.annotation.support.ExpressionValueMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ExpressionValueMethodArgumentResolver} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ExpressionValueMethodArgumentResolverTests {
|
||||
|
||||
private ExpressionValueMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter systemParameter;
|
||||
|
||||
private MethodParameter requestParameter;
|
||||
|
||||
private MethodParameter unsupported;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||
context.refresh();
|
||||
|
||||
resolver = new ExpressionValueMethodArgumentResolver(context.getBeanFactory());
|
||||
|
||||
Method method = getClass().getMethod("params", int.class, String.class, String.class);
|
||||
systemParameter = new MethodParameter(method, 0);
|
||||
requestParameter = new MethodParameter(method, 1);
|
||||
unsupported = new MethodParameter(method, 2);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
|
||||
|
||||
// Expose request to the current thread (for SpEL expressions)
|
||||
RequestContextHolder.setRequestAttributes(webRequest);
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() throws Exception {
|
||||
assertTrue(resolver.supportsParameter(systemParameter));
|
||||
assertTrue(resolver.supportsParameter(requestParameter));
|
||||
|
||||
assertFalse(resolver.supportsParameter(unsupported));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveSystemProperty() throws Exception {
|
||||
System.setProperty("systemIntValue", "22");
|
||||
Object value = resolver.resolveArgument(systemParameter, null, webRequest, null);
|
||||
|
||||
assertEquals("22", value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveRequestProperty() throws Exception {
|
||||
servletRequest.setContextPath("/contextPath");
|
||||
Object value = resolver.resolveArgument(requestParameter, null, webRequest, null);
|
||||
|
||||
assertEquals("/contextPath", value);
|
||||
}
|
||||
|
||||
public void params(@Value("#{systemProperties.systemIntValue}") int param1,
|
||||
@Value("#{request.contextPath}") String param2,
|
||||
String unsupported) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static java.lang.annotation.ElementType.CONSTRUCTOR;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.easymock.EasyMock.anyObject;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.eq;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.notNull;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.bind.support.WebRequestDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ModelAttributeMethodProcessor} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ModelAttributeMethodProcessorTests {
|
||||
|
||||
private ModelAttributeMethodProcessor processor;
|
||||
|
||||
private MethodParameter annotatedParam;
|
||||
|
||||
private MethodParameter integerParam;
|
||||
|
||||
private MethodParameter defaultNameParam;
|
||||
|
||||
private MethodParameter notAnnotatedParam;
|
||||
|
||||
private MethodParameter annotatedReturnParam;
|
||||
|
||||
private MethodParameter notAnnotatedReturnParam;
|
||||
|
||||
private ModelMap model;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.processor = new ModelAttributeMethodProcessor(true);
|
||||
|
||||
Class<?> handlerType = ModelAttributeHandler.class;
|
||||
Method method = handlerType.getDeclaredMethod("modelAttribute", TestBean.class, Errors.class, int.class,
|
||||
TestBean.class, TestBean.class);
|
||||
|
||||
this.annotatedParam = new MethodParameter(method, 0);
|
||||
this.integerParam = new MethodParameter(method, 2);
|
||||
this.defaultNameParam = new MethodParameter(method, 3);
|
||||
this.notAnnotatedParam = new MethodParameter(method, 4);
|
||||
|
||||
this.annotatedReturnParam = new MethodParameter(getClass().getDeclaredMethod("annotatedReturnValue"), -1);
|
||||
this.notAnnotatedReturnParam = new MethodParameter(getClass().getDeclaredMethod("notAnnotatedReturnValue"), -1);
|
||||
|
||||
model = new ExtendedModelMap();
|
||||
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportParameter() throws Exception {
|
||||
assertTrue(processor.supportsParameter(annotatedParam));
|
||||
assertFalse(processor.supportsParameter(integerParam));
|
||||
assertTrue(processor.supportsParameter(notAnnotatedParam));
|
||||
|
||||
this.processor = new ModelAttributeMethodProcessor(false);
|
||||
assertFalse(processor.supportsParameter(notAnnotatedParam));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() throws Exception {
|
||||
assertTrue(processor.supportsReturnType(annotatedReturnParam));
|
||||
assertFalse(processor.supportsReturnType(notAnnotatedReturnParam));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldValidate() throws Exception {
|
||||
assertTrue(processor.shouldValidate(annotatedParam));
|
||||
assertFalse(processor.shouldValidate(notAnnotatedParam));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failOnError() throws Exception {
|
||||
assertFalse(processor.failOnError(annotatedParam));
|
||||
assertTrue(processor.failOnError(notAnnotatedParam));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderFromModelAttribute() throws Exception {
|
||||
createBinderFromModelAttr("attrName", annotatedParam);
|
||||
createBinderFromModelAttr("testBean", defaultNameParam);
|
||||
createBinderFromModelAttr("testBean", notAnnotatedParam);
|
||||
}
|
||||
|
||||
private void createBinderFromModelAttr(String expectedAttrName, MethodParameter param) throws Exception {
|
||||
Object target = new TestBean();
|
||||
model.addAttribute(expectedAttrName, target);
|
||||
|
||||
WebDataBinder dataBinder = new WebRequestDataBinder(null);
|
||||
|
||||
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
|
||||
expect(binderFactory.createBinder(webRequest, target, expectedAttrName)).andReturn(dataBinder);
|
||||
replay(binderFactory);
|
||||
|
||||
processor.resolveArgument(param, model, webRequest, binderFactory);
|
||||
|
||||
verify(binderFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithAttributeConstructor() throws Exception {
|
||||
WebDataBinder dataBinder = new WebRequestDataBinder(null);
|
||||
|
||||
WebDataBinderFactory factory = createMock(WebDataBinderFactory.class);
|
||||
expect(factory.createBinder((NativeWebRequest) anyObject(), notNull(), eq("attrName"))).andReturn(dataBinder);
|
||||
replay(factory);
|
||||
|
||||
processor.resolveArgument(annotatedParam, model, webRequest, factory);
|
||||
|
||||
verify(factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindAndValidate() throws Exception {
|
||||
Object target = new TestBean();
|
||||
model.addAttribute("attrName", target);
|
||||
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target);
|
||||
|
||||
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
|
||||
expect(binderFactory.createBinder(webRequest, target, "attrName")).andReturn(dataBinder);
|
||||
replay(binderFactory);
|
||||
|
||||
processor.resolveArgument(annotatedParam, model, webRequest, binderFactory);
|
||||
|
||||
assertTrue(dataBinder.isBindInvoked());
|
||||
assertTrue(dataBinder.isValidateInvoked());
|
||||
}
|
||||
|
||||
@Test(expected=BindException.class)
|
||||
public void bindAndFail() throws Exception {
|
||||
Object target = new TestBean();
|
||||
model.addAttribute(target);
|
||||
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target);
|
||||
dataBinder.getBindingResult().reject("error");
|
||||
|
||||
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
|
||||
expect(binderFactory.createBinder(webRequest, target, "testBean")).andReturn(dataBinder);
|
||||
replay(binderFactory);
|
||||
|
||||
processor.resolveArgument(notAnnotatedParam, model, webRequest, binderFactory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void handleAnnotatedReturnValue() throws Exception {
|
||||
ModelAndViewContainer<?> mavContainer = new ModelAndViewContainer(model);
|
||||
processor.handleReturnValue("expected", annotatedReturnParam, mavContainer, webRequest);
|
||||
|
||||
assertEquals("expected", mavContainer.getModel().get("modelAttrName"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void handleNotAnnotatedReturnValue() throws Exception {
|
||||
TestBean testBean = new TestBean("expected");
|
||||
ModelAndViewContainer<?> mavContainer = new ModelAndViewContainer(model);
|
||||
processor.handleReturnValue(testBean, notAnnotatedReturnParam, mavContainer, webRequest);
|
||||
|
||||
assertSame(testBean, mavContainer.getModel().get("testBean"));
|
||||
}
|
||||
|
||||
|
||||
@SessionAttributes(types=TestBean.class)
|
||||
private static class ModelAttributeHandler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void modelAttribute(@ModelAttribute("attrName") @Valid TestBean annotatedAttr,
|
||||
Errors errors,
|
||||
int intArg,
|
||||
@ModelAttribute TestBean defaultNameAttr,
|
||||
TestBean notAnnotatedAttr) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ModelAttribute("modelAttrName")
|
||||
private String annotatedReturnValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private TestBean notAnnotatedReturnValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class StubRequestDataBinder extends WebRequestDataBinder {
|
||||
|
||||
private boolean bindInvoked;
|
||||
|
||||
private boolean validateInvoked;
|
||||
|
||||
public StubRequestDataBinder(Object target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
public boolean isBindInvoked() {
|
||||
return bindInvoked;
|
||||
}
|
||||
|
||||
public boolean isValidateInvoked() {
|
||||
return validateInvoked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(WebRequest request) {
|
||||
this.bindInvoked = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
this.validateInvoked = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER })
|
||||
@Retention(RUNTIME)
|
||||
public @interface Valid {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ModelMethodProcessor} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ModelMethodProcessorTests {
|
||||
|
||||
private ModelMethodProcessor resolver;
|
||||
|
||||
private MethodParameter modelParameter;
|
||||
|
||||
private MethodParameter modelReturnType;
|
||||
|
||||
private MethodParameter mapParameter;
|
||||
|
||||
private MethodParameter mapReturnType;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.resolver = new ModelMethodProcessor();
|
||||
|
||||
Method modelMethod = getClass().getDeclaredMethod("model", Model.class);
|
||||
this.modelParameter = new MethodParameter(modelMethod, 0);
|
||||
this.modelReturnType = new MethodParameter(modelMethod, -1);
|
||||
|
||||
Method mapMethod = getClass().getDeclaredMethod("map", Map.class);
|
||||
this.mapParameter = new MethodParameter(mapMethod, 0);
|
||||
this.mapReturnType = new MethodParameter(mapMethod, 0);
|
||||
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() {
|
||||
assertFalse(resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(resolver.supportsParameter(modelParameter));
|
||||
assertTrue(resolver.supportsParameter(mapParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue(resolver.supportsReturnType(modelReturnType));
|
||||
assertTrue(resolver.supportsReturnType(mapReturnType));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentValue() throws Exception {
|
||||
ExtendedModelMap model = new ExtendedModelMap();
|
||||
|
||||
Object result = resolver.resolveArgument(modelParameter, model, webRequest, null);
|
||||
assertSame(model, result);
|
||||
|
||||
result = resolver.resolveArgument(mapParameter, model, webRequest, null);
|
||||
assertSame(model, result);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
ExtendedModelMap implicitModel = new ExtendedModelMap();
|
||||
implicitModel.put("attr1", "value1");
|
||||
|
||||
ExtendedModelMap returnValue = new ExtendedModelMap();
|
||||
returnValue.put("attr2", "value2");
|
||||
|
||||
ModelAndViewContainer<?> mavContainer = new ModelAndViewContainer(implicitModel);
|
||||
resolver.handleReturnValue(returnValue , modelReturnType, mavContainer, webRequest);
|
||||
ModelMap actualModel = mavContainer.getModel();
|
||||
assertEquals("value1", actualModel.get("attr1"));
|
||||
assertEquals("value2", actualModel.get("attr2"));
|
||||
|
||||
mavContainer = new ModelAndViewContainer(implicitModel);
|
||||
resolver.handleReturnValue(returnValue , mapReturnType, mavContainer, webRequest);
|
||||
actualModel = mavContainer.getModel();
|
||||
assertEquals("value1", actualModel.get("attr1"));
|
||||
assertEquals("value2", actualModel.get("attr2"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Model model(Model model) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Map<String, Object> map(Map<String, Object> map) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.RequestHeaderMapMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestHeaderMapMethodArgumentResolverTests {
|
||||
|
||||
private RequestHeaderMapMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter mapParameter;
|
||||
|
||||
private MethodParameter multiValueMapParameter;
|
||||
|
||||
private MethodParameter httpHeadersParameter;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private MethodParameter unsupportedParameter;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new RequestHeaderMapMethodArgumentResolver();
|
||||
Method method = getClass()
|
||||
.getMethod("params", Map.class, MultiValueMap.class, HttpHeaders.class, Map.class);
|
||||
mapParameter = new MethodParameter(method, 0);
|
||||
multiValueMapParameter = new MethodParameter(method, 1);
|
||||
httpHeadersParameter = new MethodParameter(method, 2);
|
||||
unsupportedParameter = new MethodParameter(method, 3);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() throws NoSuchMethodException {
|
||||
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("Map parameter not supported", resolver.supportsParameter(mapParameter));
|
||||
assertTrue("MultiValueMap parameter not supported", resolver.supportsParameter(multiValueMapParameter));
|
||||
assertTrue("HttpHeaders parameter not supported", resolver.supportsParameter(httpHeadersParameter));
|
||||
assertFalse("non-@RequestParam map supported", resolver.supportsParameter(unsupportedParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMapArgument() throws Exception {
|
||||
String headerName = "foo";
|
||||
String headerValue = "bar";
|
||||
Map<String, String> expected = Collections.singletonMap(headerName, headerValue);
|
||||
servletRequest.addHeader(headerName, headerValue);
|
||||
|
||||
Map<String, String> result = (Map<String, String>) resolver.resolveArgument(mapParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMultiValueMapArgument() throws Exception {
|
||||
String headerName = "foo";
|
||||
String headerValue1 = "bar";
|
||||
String headerValue2 = "baz";
|
||||
MultiValueMap<String, String> expected = new LinkedMultiValueMap<String, String>(1);
|
||||
expected.add(headerName, headerValue1);
|
||||
expected.add(headerName, headerValue2);
|
||||
servletRequest.addHeader(headerName, headerValue1);
|
||||
servletRequest.addHeader(headerName, headerValue2);
|
||||
|
||||
MultiValueMap<String, String> result =
|
||||
(MultiValueMap<String, String>) resolver.resolveArgument(multiValueMapParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveHttpHeadersArgument() throws Exception {
|
||||
String headerName = "foo";
|
||||
String headerValue1 = "bar";
|
||||
String headerValue2 = "baz";
|
||||
HttpHeaders expected = new HttpHeaders();
|
||||
expected.add(headerName, headerValue1);
|
||||
expected.add(headerName, headerValue2);
|
||||
servletRequest.addHeader(headerName, headerValue1);
|
||||
servletRequest.addHeader(headerName, headerValue2);
|
||||
|
||||
HttpHeaders result = (HttpHeaders) resolver.resolveArgument(httpHeadersParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
public void params(@RequestHeader Map<?, ?> param1,
|
||||
@RequestHeader MultiValueMap<?, ?> param2,
|
||||
@RequestHeader HttpHeaders param3,
|
||||
Map<?,?> unsupported) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.method.annotation.support.RequestHeaderMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestHeaderMethodArgumentResolverTests {
|
||||
|
||||
private RequestHeaderMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter stringParameter;
|
||||
|
||||
private MethodParameter stringArrayParameter;
|
||||
|
||||
private MethodParameter systemPropertyParameter;
|
||||
|
||||
private MethodParameter contextPathParameter;
|
||||
|
||||
private MethodParameter otherParameter;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||
context.refresh();
|
||||
|
||||
resolver = new RequestHeaderMethodArgumentResolver(context.getBeanFactory());
|
||||
|
||||
Method method = getClass().getMethod("params", String.class, String[].class, String.class, String.class, Map.class);
|
||||
stringParameter = new MethodParameter(method, 0);
|
||||
stringArrayParameter = new MethodParameter(method, 1);
|
||||
systemPropertyParameter = new MethodParameter(method, 2);
|
||||
contextPathParameter = new MethodParameter(method, 3);
|
||||
otherParameter = new MethodParameter(method, 4);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
|
||||
|
||||
// Expose request to the current thread (for SpEL expressions)
|
||||
RequestContextHolder.setRequestAttributes(webRequest);
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() throws NoSuchMethodException {
|
||||
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("String parameter not supported", resolver.supportsParameter(stringParameter));
|
||||
assertTrue("String array parameter not supported", resolver.supportsParameter(stringArrayParameter));
|
||||
assertFalse("non-@RequestParam parameter supported", resolver.supportsParameter(otherParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveStringArgument() throws Exception {
|
||||
String expected = "foo";
|
||||
servletRequest.addHeader("name", expected);
|
||||
|
||||
String result = (String) resolver.resolveArgument(stringParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveStringArrayArgument() throws Exception {
|
||||
String[] expected = new String[]{"foo", "bar"};
|
||||
servletRequest.addHeader("name", expected);
|
||||
|
||||
String[] result = (String[]) resolver.resolveArgument(stringArrayParameter, null, webRequest, null);
|
||||
assertArrayEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDefaultValue() throws Exception {
|
||||
String result = (String) resolver.resolveArgument(stringParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", "bar", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDefaultValueFromSystemProperty() throws Exception {
|
||||
System.setProperty("header", "bar");
|
||||
String result = (String) resolver.resolveArgument(systemPropertyParameter, null, webRequest, null);
|
||||
assertEquals("bar", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDefaultValueFromRequest() throws Exception {
|
||||
servletRequest.setContextPath("/bar");
|
||||
String result = (String) resolver.resolveArgument(contextPathParameter, null, webRequest, null);
|
||||
assertEquals("/bar", result);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void notFound() throws Exception {
|
||||
String result = (String) resolver.resolveArgument(stringArrayParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", "bar", result);
|
||||
}
|
||||
|
||||
public void params(@RequestHeader(value = "name", defaultValue = "bar") String param1,
|
||||
@RequestHeader("name") String[] param2,
|
||||
@RequestHeader(value = "name", defaultValue="#{systemProperties.header}") String param3,
|
||||
@RequestHeader(value = "name", defaultValue="#{request.contextPath}") String param4,
|
||||
@RequestHeader("name") Map<?, ?> unsupported) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.RequestParamMapMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestParamMapMethodArgumentResolverTests {
|
||||
|
||||
private RequestParamMapMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter mapParameter;
|
||||
|
||||
private MethodParameter multiValueMapParameter;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private MethodParameter unsupportedParameter;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new RequestParamMapMethodArgumentResolver();
|
||||
Method method = getClass()
|
||||
.getMethod("params", Map.class, MultiValueMap.class, Map.class);
|
||||
mapParameter = new MethodParameter(method, 0);
|
||||
multiValueMapParameter = new MethodParameter(method, 1);
|
||||
unsupportedParameter = new MethodParameter(method, 2);
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() throws NoSuchMethodException {
|
||||
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("Map parameter not supported", resolver.supportsParameter(mapParameter));
|
||||
assertTrue("MultiValueMap parameter not supported", resolver.supportsParameter(multiValueMapParameter));
|
||||
assertFalse("non-@RequestParam map supported", resolver.supportsParameter(unsupportedParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMapArgument() throws Exception {
|
||||
String headerName = "foo";
|
||||
String headerValue = "bar";
|
||||
Map<String, String> expected = Collections.singletonMap(headerName, headerValue);
|
||||
servletRequest.addParameter(headerName, headerValue);
|
||||
|
||||
Map<String, String> result = (Map<String, String>) resolver.resolveArgument(mapParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMultiValueMapArgument() throws Exception {
|
||||
String headerName = "foo";
|
||||
String headerValue1 = "bar";
|
||||
String headerValue2 = "baz";
|
||||
MultiValueMap<String, String> expected = new LinkedMultiValueMap<String, String>(1);
|
||||
expected.add(headerName, headerValue1);
|
||||
expected.add(headerName, headerValue2);
|
||||
servletRequest.addParameter(headerName, new String[]{headerValue1, headerValue2});
|
||||
|
||||
MultiValueMap<String, String> result =
|
||||
(MultiValueMap<String, String>) resolver.resolveArgument(multiValueMapParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
public void params(@RequestParam Map<?, ?> param1,
|
||||
@RequestParam MultiValueMap<?, ?> param2,
|
||||
Map<?, ?> unsupported) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.mock.web.MockMultipartHttpServletRequest;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.RequestParamMethodArgumentResolver;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestParamMethodArgumentResolverTests {
|
||||
|
||||
private RequestParamMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter stringParameter;
|
||||
|
||||
private MethodParameter stringArrayParameter;
|
||||
|
||||
private MethodParameter mapParameter;
|
||||
|
||||
private MethodParameter fileParameter;
|
||||
|
||||
private MethodParameter otherParameter;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private MethodParameter plainParameter;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new RequestParamMethodArgumentResolver(null, true);
|
||||
Method method = getClass()
|
||||
.getMethod("params", String.class, String[].class, Map.class, MultipartFile.class, Map.class, String.class);
|
||||
stringParameter = new MethodParameter(method, 0);
|
||||
stringArrayParameter = new MethodParameter(method, 1);
|
||||
mapParameter = new MethodParameter(method, 2);
|
||||
fileParameter = new MethodParameter(method, 3);
|
||||
otherParameter = new MethodParameter(method, 4);
|
||||
plainParameter = new MethodParameter(method, 5);
|
||||
|
||||
plainParameter.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgument() throws NoSuchMethodException {
|
||||
assertFalse("resolver uses response argument", resolver.usesResponseArgument(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("String parameter not supported", resolver.supportsParameter(stringParameter));
|
||||
assertTrue("String array parameter not supported", resolver.supportsParameter(stringArrayParameter));
|
||||
assertTrue("Named map not parameter supported", resolver.supportsParameter(mapParameter));
|
||||
assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(fileParameter));
|
||||
assertFalse("non-@RequestParam parameter supported", resolver.supportsParameter(otherParameter));
|
||||
assertTrue("Simple type params supported w/o annotations", resolver.supportsParameter(plainParameter));
|
||||
|
||||
resolver = new RequestParamMethodArgumentResolver(null, false);
|
||||
assertFalse(resolver.supportsParameter(plainParameter));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveStringArgument() throws Exception {
|
||||
String expected = "foo";
|
||||
servletRequest.addParameter("name", expected);
|
||||
|
||||
String result = (String) resolver.resolveArgument(stringParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveStringArrayArgument() throws Exception {
|
||||
String[] expected = new String[]{"foo", "bar"};
|
||||
servletRequest.addParameter("name", expected);
|
||||
|
||||
String[] result = (String[]) resolver.resolveArgument(stringArrayParameter, null, webRequest, null);
|
||||
assertArrayEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMultipartFileArgument() throws Exception {
|
||||
MockMultipartHttpServletRequest servletRequest = new MockMultipartHttpServletRequest();
|
||||
MultipartFile expected = new MockMultipartFile("file", "Hello World".getBytes());
|
||||
servletRequest.addFile(expected);
|
||||
webRequest = new ServletWebRequest(servletRequest);
|
||||
|
||||
MultipartFile result = (MultipartFile) resolver.resolveArgument(fileParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDefaultValue() throws Exception {
|
||||
String result = (String) resolver.resolveArgument(stringParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", "bar", result);
|
||||
}
|
||||
|
||||
@Test(expected = MissingServletRequestParameterException.class)
|
||||
public void notFound() throws Exception {
|
||||
String result = (String) resolver.resolveArgument(stringArrayParameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", "bar", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveSimpleTypeParam() throws Exception {
|
||||
servletRequest.setParameter("plainParam", "plainValue");
|
||||
String result = (String) resolver.resolveArgument(plainParameter, null, webRequest, null);
|
||||
assertEquals("plainValue", result);
|
||||
}
|
||||
|
||||
public void params(@RequestParam(value = "name", defaultValue = "bar") String param1,
|
||||
@RequestParam("name") String[] param2,
|
||||
@RequestParam("name") Map<?, ?> param3,
|
||||
@RequestParam(value = "file") MultipartFile file,
|
||||
@RequestParam Map<?, ?> unsupported,
|
||||
String plainParam) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.annotation.support;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.annotation.support.WebArgumentResolverAdapter;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class WebArgumentResolverAdapterTests {
|
||||
|
||||
private WebArgumentResolver adaptee;
|
||||
|
||||
private WebArgumentResolverAdapter adapter;
|
||||
|
||||
private MethodParameter parameter;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
adaptee = createMock(WebArgumentResolver.class);
|
||||
adapter = new WebArgumentResolverAdapter(adaptee);
|
||||
|
||||
parameter = new MethodParameter(getClass().getMethod("handle", Integer.TYPE), 0);
|
||||
|
||||
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
RequestContextHolder.setRequestAttributes(webRequest);
|
||||
}
|
||||
|
||||
@After
|
||||
public void resetRequestContextHolder() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() throws Exception {
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andReturn(42);
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
boolean result = adapter.supportsParameter(parameter);
|
||||
assertTrue("Parameter not supported", result);
|
||||
|
||||
verify(adaptee);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameterUnresolved() throws Exception {
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andReturn(WebArgumentResolver.UNRESOLVED);
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
boolean result = adapter.supportsParameter(parameter);
|
||||
assertFalse("Parameter supported", result);
|
||||
|
||||
verify(adaptee);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameterWrongType() throws Exception {
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andReturn("Foo");
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
boolean result = adapter.supportsParameter(parameter);
|
||||
assertFalse("Parameter supported", result);
|
||||
|
||||
verify(adaptee);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameterThrowsException() throws Exception {
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andThrow(new Exception());
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
boolean result = adapter.supportsParameter(parameter);
|
||||
assertFalse("Parameter supported", result);
|
||||
|
||||
verify(adaptee);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
int expected = 42;
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andReturn(expected);
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
Object result = adapter.resolveArgument(parameter, null, webRequest, null);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
|
||||
verify(adaptee);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveArgumentUnresolved() throws Exception {
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andReturn(WebArgumentResolver.UNRESOLVED);
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
adapter.resolveArgument(parameter, null, webRequest, null);
|
||||
|
||||
verify(adaptee);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveArgumentWrongType() throws Exception {
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andReturn("Foo");
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
adapter.resolveArgument(parameter, null, webRequest, null);
|
||||
|
||||
verify(adaptee);
|
||||
}
|
||||
|
||||
@Test(expected = Exception.class)
|
||||
public void resolveArgumentThrowsException() throws Exception {
|
||||
expect(adaptee.resolveArgument(parameter, webRequest)).andThrow(new Exception());
|
||||
|
||||
replay(adaptee);
|
||||
|
||||
adapter.resolveArgument(parameter, null, webRequest, null);
|
||||
|
||||
verify(adaptee);
|
||||
}
|
||||
|
||||
public void handle(int param) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link HandlerMethodArgumentResolverContainer} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HandlerMethodArgumentResolverContainerTests {
|
||||
|
||||
private HandlerMethodArgumentResolverContainer container;
|
||||
|
||||
private MethodParameter paramInteger;
|
||||
|
||||
private MethodParameter paramString;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.container = new HandlerMethodArgumentResolverContainer();
|
||||
|
||||
Method method = getClass().getDeclaredMethod("handle", Integer.class, String.class);
|
||||
this.paramInteger = new MethodParameter(method, 0);
|
||||
this.paramString = new MethodParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() throws Exception {
|
||||
registerResolver(Integer.class, null, false);
|
||||
|
||||
assertTrue(this.container.supportsParameter(paramInteger));
|
||||
assertFalse(this.container.supportsParameter(paramString));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
registerResolver(Integer.class, Integer.valueOf(55), false);
|
||||
Object resolvedValue = this.container.resolveArgument(paramInteger, null, null, null);
|
||||
|
||||
assertEquals(Integer.valueOf(55), resolvedValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentMultipleResolvers() throws Exception {
|
||||
registerResolver(Integer.class, Integer.valueOf(1), false);
|
||||
registerResolver(Integer.class, Integer.valueOf(2), false);
|
||||
Object resolvedValue = this.container.resolveArgument(paramInteger, null, null, null);
|
||||
|
||||
assertEquals("Didn't use the first registered resolver", Integer.valueOf(1), resolvedValue);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalStateException.class)
|
||||
public void noSuitableArgumentResolver() throws Exception {
|
||||
this.container.resolveArgument(paramString, null, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void argResolverUsesResponse() throws Exception {
|
||||
registerResolver(Integer.class, null, true);
|
||||
assertTrue(this.container.usesResponseArgument(paramInteger));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void argResolverDoesntUseResponse() throws Exception {
|
||||
registerResolver(Integer.class, null, false);
|
||||
assertFalse(this.container.usesResponseArgument(paramInteger));
|
||||
}
|
||||
|
||||
protected StubArgumentResolver registerResolver(Class<?> supportedType, Object stubValue, boolean usesResponse) {
|
||||
StubArgumentResolver resolver = new StubArgumentResolver(supportedType, stubValue, usesResponse);
|
||||
this.container.registerArgumentResolver(resolver);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handle(Integer arg1, String arg2) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link HandlerMethodReturnValueHandlerContainer} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HandlerMethodReturnValueHandlerContainerTests {
|
||||
|
||||
private HandlerMethodReturnValueHandlerContainer container;
|
||||
|
||||
ModelAndViewContainer<?> mavContainer;
|
||||
|
||||
private MethodParameter paramInteger;
|
||||
|
||||
private MethodParameter paramString;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.container = new HandlerMethodReturnValueHandlerContainer();
|
||||
|
||||
this.paramInteger = new MethodParameter(getClass().getDeclaredMethod("handleInteger"), -1);
|
||||
this.paramString = new MethodParameter(getClass().getDeclaredMethod("handleString"), -1);
|
||||
|
||||
mavContainer = new ModelAndViewContainer<Object>(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() throws Exception {
|
||||
registerReturnValueHandler(Integer.class, false);
|
||||
|
||||
assertTrue(this.container.supportsReturnType(paramInteger));
|
||||
assertFalse(this.container.supportsReturnType(paramString));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
StubReturnValueHandler handler = registerReturnValueHandler(Integer.class, false);
|
||||
this.container.handleReturnValue(Integer.valueOf(55), paramInteger, mavContainer, null);
|
||||
|
||||
assertEquals(Integer.valueOf(55), handler.getUnhandledReturnValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueMultipleHandlers() throws Exception {
|
||||
StubReturnValueHandler handler1 = registerReturnValueHandler(Integer.class, false);
|
||||
StubReturnValueHandler handler2 = registerReturnValueHandler(Integer.class, false);
|
||||
this.container.handleReturnValue(Integer.valueOf(55), paramInteger, mavContainer, null);
|
||||
|
||||
assertEquals("Didn't use the 1st registered handler", Integer.valueOf(55), handler1.getUnhandledReturnValue());
|
||||
assertNull("Shouldn't have use the 2nd registered handler", handler2.getUnhandledReturnValue());
|
||||
}
|
||||
|
||||
@Test(expected=IllegalStateException.class)
|
||||
public void noSuitableReturnValueHandler() throws Exception {
|
||||
registerReturnValueHandler(Integer.class, false);
|
||||
this.container.handleReturnValue("value", paramString, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnValueHandlerUsesResponse() throws Exception {
|
||||
registerReturnValueHandler(Integer.class, true);
|
||||
assertTrue(this.container.usesResponseArgument(paramInteger));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnValueHandlerDosntUseResponse() throws Exception {
|
||||
registerReturnValueHandler(Integer.class, false);
|
||||
assertFalse(this.container.usesResponseArgument(paramInteger));
|
||||
}
|
||||
|
||||
protected StubReturnValueHandler registerReturnValueHandler(Class<?> returnType, boolean usesResponse) {
|
||||
StubReturnValueHandler handler = new StubReturnValueHandler(returnType, usesResponse);
|
||||
this.container.registerReturnValueHandler(handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Integer handleInteger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String handleString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link InvocableHandlerMethod} unit tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class InvocableHandlerMethodTests {
|
||||
|
||||
private HandlerMethodArgumentResolverContainer argResolvers;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
argResolvers = new HandlerMethodArgumentResolverContainer();
|
||||
|
||||
response = new MockHttpServletResponse();
|
||||
this.webRequest = new ServletWebRequest(new MockHttpServletRequest(), response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void argResolutionAndReturnValueHandling() throws Exception {
|
||||
StubArgumentResolver resolver0 = registerResolver(Integer.class, 99, false);
|
||||
StubArgumentResolver resolver1 = registerResolver(String.class, "value", false);
|
||||
|
||||
InvocableHandlerMethod method = handlerMethod(new Handler(), "handle", Integer.class, String.class);
|
||||
Object returnValue = method.invokeForRequest(webRequest, null);
|
||||
|
||||
assertEquals("Integer resolver not invoked", 1, resolver0.getResolvedParameterNames().size());
|
||||
assertEquals("String resolver not invoked", 1, resolver1.getResolvedParameterNames().size());
|
||||
assertEquals("Invalid return value", "99-value", returnValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void providedArgResolution() throws Exception {
|
||||
InvocableHandlerMethod method = handlerMethod(new Handler(), "handle", Integer.class, String.class);
|
||||
Object returnValue = method.invokeForRequest(webRequest, null, 99, "value");
|
||||
|
||||
assertEquals("Expected raw return value with no handlers registered", String.class, returnValue.getClass());
|
||||
assertEquals("Provided argument values were not resolved", "99-value", returnValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parameterNameDiscovery() throws Exception {
|
||||
StubArgumentResolver resolver = registerResolver(Integer.class, 99, false);
|
||||
|
||||
InvocableHandlerMethod method = handlerMethod(new Handler(), "parameterNameDiscovery", Integer.class);
|
||||
method.invokeForRequest(webRequest, null);
|
||||
|
||||
assertEquals("intArg", resolver.getResolvedParameterNames().get(0).getParameterName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesResponseArgResolver() throws Exception {
|
||||
InvocableHandlerMethod requestMethod = handlerMethod(new Handler(), "usesResponse", int.class);
|
||||
registerResolver(int.class, 99, true);
|
||||
|
||||
assertTrue(requestMethod.usesResponseArgument());
|
||||
}
|
||||
|
||||
private InvocableHandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes)
|
||||
throws Exception {
|
||||
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
|
||||
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(handler, method);
|
||||
handlerMethod.setArgumentResolverContainer(argResolvers);
|
||||
return handlerMethod;
|
||||
}
|
||||
|
||||
private StubArgumentResolver registerResolver(Class<?> supportedType, Object stubValue, boolean usesResponse) {
|
||||
StubArgumentResolver resolver = new StubArgumentResolver(supportedType, stubValue, usesResponse);
|
||||
argResolvers.registerArgumentResolver(resolver);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
private static class Handler {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String handle(Integer intArg, String stringArg) {
|
||||
return intArg + "-" + stringArg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RequestMapping
|
||||
public void parameterNameDiscovery(Integer intArg) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "400 Bad Request")
|
||||
public void responseStatus() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String usesResponse(int arg) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
/**
|
||||
* Resolves a method argument from a stub value. Records all resolved parameters.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class StubArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final Class<?> supportedType;
|
||||
|
||||
private final Object stubValue;
|
||||
|
||||
private final boolean usesResponse;
|
||||
|
||||
private List<MethodParameter> resolvedParameters = new ArrayList<MethodParameter>();
|
||||
|
||||
public StubArgumentResolver(Class<?> supportedType, Object stubValue, boolean usesResponse) {
|
||||
this.supportedType = supportedType;
|
||||
this.stubValue = stubValue;
|
||||
this.usesResponse = usesResponse;
|
||||
}
|
||||
|
||||
public List<MethodParameter> getResolvedParameterNames() {
|
||||
return resolvedParameters;
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return this.usesResponse;
|
||||
}
|
||||
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.getParameterType().equals(this.supportedType);
|
||||
}
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter, ModelMap model, NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
this.resolvedParameters.add(parameter);
|
||||
return this.stubValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.method.support;
|
||||
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Handles a return value by adding it as a model attribute with a default name.
|
||||
* Records the raw return value (before handling).
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class StubReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
private final Class<?> supportedReturnType;
|
||||
|
||||
private final boolean usesResponse;
|
||||
|
||||
private Object unhandledReturnValue;
|
||||
|
||||
public StubReturnValueHandler(Class<?> supportedReturnType, boolean usesResponse) {
|
||||
this.supportedReturnType = supportedReturnType;
|
||||
this.usesResponse = usesResponse;
|
||||
}
|
||||
|
||||
public Object getUnhandledReturnValue() {
|
||||
return this.unhandledReturnValue;
|
||||
}
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return returnType.getParameterType().equals(this.supportedReturnType);
|
||||
}
|
||||
|
||||
public <V> void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer<V> mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
this.unhandledReturnValue = returnValue;
|
||||
if (returnValue != null) {
|
||||
mavContainer.addModelAttribute(Conventions.getVariableName(returnValue), returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean usesResponseArgument(MethodParameter parameter) {
|
||||
return this.usesResponse;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user