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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user