SPR-6464 Add @FlashAttributes annotation and FlashStatus method argument
This commit is contained in:
parent
2c504012ad
commit
f4adf227be
|
|
@ -50,8 +50,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||||
import org.springframework.web.bind.support.DefaultDataBinderFactory;
|
import org.springframework.web.bind.support.DefaultDataBinderFactory;
|
||||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||||
|
import org.springframework.web.bind.support.FlashStatus;
|
||||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||||
import org.springframework.web.bind.support.SessionStatus;
|
import org.springframework.web.bind.support.SessionStatus;
|
||||||
|
import org.springframework.web.bind.support.SimpleFlashStatus;
|
||||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
|
|
@ -59,6 +61,7 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||||
import org.springframework.web.context.request.WebRequest;
|
import org.springframework.web.context.request.WebRequest;
|
||||||
import org.springframework.web.method.HandlerMethod;
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.method.HandlerMethodSelector;
|
import org.springframework.web.method.HandlerMethodSelector;
|
||||||
|
import org.springframework.web.method.annotation.FlashAttributesHandler;
|
||||||
import org.springframework.web.method.annotation.ModelFactory;
|
import org.springframework.web.method.annotation.ModelFactory;
|
||||||
import org.springframework.web.method.annotation.SessionAttributesHandler;
|
import org.springframework.web.method.annotation.SessionAttributesHandler;
|
||||||
import org.springframework.web.method.annotation.support.ErrorsMethodArgumentResolver;
|
import org.springframework.web.method.annotation.support.ErrorsMethodArgumentResolver;
|
||||||
|
|
@ -146,6 +149,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||||
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache =
|
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache =
|
||||||
new ConcurrentHashMap<Class<?>, SessionAttributesHandler>();
|
new ConcurrentHashMap<Class<?>, SessionAttributesHandler>();
|
||||||
|
|
||||||
|
private final Map<Class<?>, FlashAttributesHandler> flashAttributesHandlerCache =
|
||||||
|
new ConcurrentHashMap<Class<?>, FlashAttributesHandler>();
|
||||||
|
|
||||||
private HandlerMethodArgumentResolverComposite argumentResolvers;
|
private HandlerMethodArgumentResolverComposite argumentResolvers;
|
||||||
|
|
||||||
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
|
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
|
||||||
|
|
@ -481,19 +487,27 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the given handler type defines any handler-specific session attributes via {@link SessionAttributes}.
|
* Whether the given handler type defines any handler-specific session attributes
|
||||||
* Also initializes the sessionAttributesHandlerCache for the given handler type.
|
* via {@link SessionAttributes}.
|
||||||
*/
|
*/
|
||||||
private boolean hasSessionAttributes(Class<?> handlerType) {
|
private boolean hasSessionAttributes(Class<?> handlerType) {
|
||||||
SessionAttributesHandler handler = null;
|
SessionAttributesHandler sessionAttrsHandler = null;
|
||||||
synchronized(this.sessionAttributesHandlerCache) {
|
synchronized(this.sessionAttributesHandlerCache) {
|
||||||
handler = this.sessionAttributesHandlerCache.get(handlerType);
|
sessionAttrsHandler = this.sessionAttributesHandlerCache.get(handlerType);
|
||||||
if (handler == null) {
|
if (sessionAttrsHandler == null) {
|
||||||
handler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
sessionAttrsHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
||||||
this.sessionAttributesHandlerCache.put(handlerType, handler);
|
this.sessionAttributesHandlerCache.put(handlerType, sessionAttrsHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return handler.hasSessionAttributes();
|
FlashAttributesHandler flashAttrsHandler = null;
|
||||||
|
synchronized(this.flashAttributesHandlerCache) {
|
||||||
|
flashAttrsHandler = this.flashAttributesHandlerCache.get(handlerType);
|
||||||
|
if (flashAttrsHandler == null) {
|
||||||
|
flashAttrsHandler = new FlashAttributesHandler(handlerType);
|
||||||
|
this.flashAttributesHandlerCache.put(handlerType, flashAttrsHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sessionAttrsHandler.hasSessionAttributes() || flashAttrsHandler.hasFlashAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -509,12 +523,13 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||||
|
|
||||||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||||
SessionStatus sessionStatus = new SimpleSessionStatus();
|
SessionStatus sessionStatus = new SimpleSessionStatus();
|
||||||
|
FlashStatus flashStatus = new SimpleFlashStatus();
|
||||||
|
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||||
modelFactory.initModel(webRequest, mavContainer, requestMethod);
|
modelFactory.initModel(webRequest, mavContainer, requestMethod);
|
||||||
|
|
||||||
requestMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus);
|
requestMethod.invokeAndHandle(webRequest, mavContainer, sessionStatus, flashStatus);
|
||||||
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
|
modelFactory.updateModel(webRequest, mavContainer, sessionStatus, flashStatus);
|
||||||
|
|
||||||
if (!mavContainer.isResolveView()) {
|
if (!mavContainer.isResolveView()) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -569,7 +584,10 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||||
modelAttrMethods.add(attrMethod);
|
modelAttrMethods.add(attrMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ModelFactory(modelAttrMethods, binderFactory, sessionAttributesHandlerCache.get(handlerType));
|
SessionAttributesHandler sessionAttrsHandler = sessionAttributesHandlerCache.get(handlerType);
|
||||||
|
FlashAttributesHandler flashAttrsHandler = flashAttributesHandlerCache.get(handlerType);
|
||||||
|
|
||||||
|
return new ModelFactory(modelAttrMethods, binderFactory, sessionAttrsHandler, flashAttrsHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod,
|
private ServletInvocableHandlerMethod createRequestMappingMethod(HandlerMethod handlerMethod,
|
||||||
|
|
|
||||||
|
|
@ -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.annotation;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
|
import org.springframework.mock.web.MockServletConfig;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||||
|
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for tests using on the DispatcherServlet and HandlerMethod infrastructure classes:
|
||||||
|
* <ul>
|
||||||
|
* <li>RequestMappingHandlerMapping
|
||||||
|
* <li>RequestMappingHandlerAdapter
|
||||||
|
* <li>ExceptionHandlerExceptionResolver
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class AbstractServletHandlerMethodTests {
|
||||||
|
|
||||||
|
private DispatcherServlet servlet;
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
this.servlet = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DispatcherServlet getServlet() {
|
||||||
|
assertNotNull("DispatcherServlet not initialized", servlet);
|
||||||
|
return servlet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a DispatcherServlet instance registering zero or more controller classes.
|
||||||
|
*/
|
||||||
|
protected WebApplicationContext initServletWithControllers(final Class<?>... controllerClasses)
|
||||||
|
throws ServletException {
|
||||||
|
return initServlet(null, controllerClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a DispatcherServlet instance registering zero or more controller classes
|
||||||
|
* and also providing additional bean definitions through a callback.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
protected WebApplicationContext initServlet(
|
||||||
|
final ApplicationContextInitializer<GenericWebApplicationContext> initializer,
|
||||||
|
final Class<?>... controllerClasses) throws ServletException {
|
||||||
|
|
||||||
|
final GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||||
|
|
||||||
|
servlet = new DispatcherServlet() {
|
||||||
|
@Override
|
||||||
|
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||||
|
for (Class<?> clazz : controllerClasses) {
|
||||||
|
wac.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> mappingType = RequestMappingHandlerMapping.class;
|
||||||
|
wac.registerBeanDefinition("handlerMapping", new RootBeanDefinition(mappingType));
|
||||||
|
|
||||||
|
Class<?> adapterType = RequestMappingHandlerAdapter.class;
|
||||||
|
wac.registerBeanDefinition("handlerAdapter", new RootBeanDefinition(adapterType));
|
||||||
|
|
||||||
|
Class<?> resolverType = ExceptionHandlerExceptionResolver.class;
|
||||||
|
wac.registerBeanDefinition("requestMappingResolver", new RootBeanDefinition(resolverType));
|
||||||
|
|
||||||
|
resolverType = ResponseStatusExceptionResolver.class;
|
||||||
|
wac.registerBeanDefinition("responseStatusResolver", new RootBeanDefinition(resolverType));
|
||||||
|
|
||||||
|
resolverType = DefaultHandlerExceptionResolver.class;
|
||||||
|
wac.registerBeanDefinition("defaultResolver", new RootBeanDefinition(resolverType));
|
||||||
|
|
||||||
|
if (initializer != null) {
|
||||||
|
initializer.initialize(wac);
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.refresh();
|
||||||
|
return wac;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
servlet.init(new MockServletConfig());
|
||||||
|
|
||||||
|
return wac;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.servlet.mvc.method.annotation;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.FlashAttributes;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.support.FlashStatus;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||||
|
import org.springframework.web.method.annotation.FlashAttributesHandler;
|
||||||
|
import org.springframework.web.servlet.View;
|
||||||
|
import org.springframework.web.servlet.ViewResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test controllers with @{@link FlashAttributes} through the DispatcherServlet.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class FlashAttributesServletTests extends AbstractServletHandlerMethodTests {
|
||||||
|
|
||||||
|
private static final String MESSAGE_KEY = "message";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void successMessage() throws Exception {
|
||||||
|
|
||||||
|
initServletWithModelExposingViewResolver(MessageController.class);
|
||||||
|
|
||||||
|
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/message");
|
||||||
|
MockHttpServletResponse res = new MockHttpServletResponse();
|
||||||
|
getServlet().service(req, res);
|
||||||
|
|
||||||
|
assertEquals(200, res.getStatus());
|
||||||
|
assertNull(getModelAttribute(req, MESSAGE_KEY));
|
||||||
|
assertNull(getFlashAttribute(req, MESSAGE_KEY));
|
||||||
|
|
||||||
|
req.setMethod("POST");
|
||||||
|
getServlet().service(req, res);
|
||||||
|
|
||||||
|
assertEquals(200, res.getStatus());
|
||||||
|
assertEquals("Yay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText());
|
||||||
|
assertEquals("Yay!", ((Message) getFlashAttribute(req, MESSAGE_KEY)).getText());
|
||||||
|
|
||||||
|
req.setMethod("GET");
|
||||||
|
getServlet().service(req, res);
|
||||||
|
|
||||||
|
assertEquals(200, res.getStatus());
|
||||||
|
assertEquals("Yay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText());
|
||||||
|
assertNull(getFlashAttribute(req, MESSAGE_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void successMessageAcrossControllers() throws Exception {
|
||||||
|
|
||||||
|
initServletWithModelExposingViewResolver(MessageController.class, SecondMessageController.class);
|
||||||
|
|
||||||
|
MockHttpServletRequest req = new MockHttpServletRequest("POST", "/message");
|
||||||
|
MockHttpServletResponse res = new MockHttpServletResponse();
|
||||||
|
getServlet().service(req, res);
|
||||||
|
|
||||||
|
req.setParameter("another", "true");
|
||||||
|
getServlet().service(req, res);
|
||||||
|
|
||||||
|
assertEquals(200, res.getStatus());
|
||||||
|
assertEquals("Nay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText());
|
||||||
|
assertEquals("Nay!", ((Message) getFlashAttribute(req, MESSAGE_KEY)).getText());
|
||||||
|
|
||||||
|
req.setMethod("GET");
|
||||||
|
req.setRequestURI("/second/message");
|
||||||
|
getServlet().service(req, res);
|
||||||
|
|
||||||
|
assertEquals(200, res.getStatus());
|
||||||
|
assertEquals("Nay!", ((Message) getModelAttribute(req, MESSAGE_KEY)).getText());
|
||||||
|
assertNull(getFlashAttribute(req, MESSAGE_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@FlashAttributes("message")
|
||||||
|
static class MessageController {
|
||||||
|
|
||||||
|
@RequestMapping(value="/message", method=RequestMethod.GET)
|
||||||
|
public void message(Model model) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value="/message", method=RequestMethod.POST)
|
||||||
|
public String sendMessage(Model model, FlashStatus status) {
|
||||||
|
status.setActive();
|
||||||
|
model.addAttribute(Message.success("Yay!"));
|
||||||
|
return "redirect:/message";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value="/message", method=RequestMethod.POST, params="another")
|
||||||
|
public String sendMessageToSecondController(Model model, FlashStatus status) {
|
||||||
|
status.setActive();
|
||||||
|
model.addAttribute(Message.error("Nay!"));
|
||||||
|
return "redirect:/second/message";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
static class SecondMessageController {
|
||||||
|
|
||||||
|
@RequestMapping(value="/second/message", method=RequestMethod.GET)
|
||||||
|
public void message(Model model) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Message {
|
||||||
|
|
||||||
|
private final MessageType type;
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
private Message(MessageType type, String text) {
|
||||||
|
this.type = type;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Message success(String text) {
|
||||||
|
return new Message(MessageType.success, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Message error(String text) {
|
||||||
|
return new Message(MessageType.error, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return type + ": " + text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static enum MessageType {
|
||||||
|
info, success, warning, error
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Object getModelAttribute(MockHttpServletRequest req, String key) {
|
||||||
|
Map<String, ?> model = (Map<String, ?>) req.getAttribute(ModelExposingViewResolver.REQUEST_ATTRIBITE_MODEL);
|
||||||
|
return model.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Object getFlashAttribute(MockHttpServletRequest req, String key) {
|
||||||
|
String flashAttributesKey = FlashAttributesHandler.FLASH_ATTRIBUTES_SESSION_KEY;
|
||||||
|
Map<String, Object> attrs = (Map<String, Object>) req.getSession().getAttribute(flashAttributesKey);
|
||||||
|
return (attrs != null) ? attrs.get(key) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebApplicationContext initServletWithModelExposingViewResolver(Class<?>... controllerClasses)
|
||||||
|
throws ServletException {
|
||||||
|
|
||||||
|
return initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
||||||
|
public void initialize(GenericWebApplicationContext wac) {
|
||||||
|
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(ModelExposingViewResolver.class));
|
||||||
|
}
|
||||||
|
}, controllerClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ModelExposingViewResolver implements ViewResolver {
|
||||||
|
|
||||||
|
static String REQUEST_ATTRIBITE_MODEL = "ModelExposingViewResolver.model";
|
||||||
|
|
||||||
|
public View resolveViewName(final String viewName, Locale locale) throws Exception {
|
||||||
|
return new View() {
|
||||||
|
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
request.setAttribute(REQUEST_ATTRIBITE_MODEL, model);
|
||||||
|
}
|
||||||
|
public String getContentType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -92,7 +92,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletWebA
|
||||||
* <p>If you wish to add high-level tests, consider the following other "integration"-style tests:
|
* <p>If you wish to add high-level tests, consider the following other "integration"-style tests:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link HandlerMethodAnnotationDetectionTests}
|
* <li>{@link HandlerMethodAnnotationDetectionTests}
|
||||||
* <li>{@link ServletHandlerMethodTests}
|
* <li>{@link ServletAnnotationControllerHandlerMethodTests}
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ import org.springframework.web.servlet.mvc.method.annotation.support.ServletRequ
|
||||||
*
|
*
|
||||||
* <p>For higher-level adapter tests see:
|
* <p>For higher-level adapter tests see:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link ServletHandlerMethodTests}
|
* <li>{@link ServletAnnotationControllerHandlerMethodTests}
|
||||||
* <li>{@link HandlerMethodAnnotationDetectionTests}
|
* <li>{@link HandlerMethodAnnotationDetectionTests}
|
||||||
* <li>{@link RequestMappingHandlerAdapterIntegrationTests}
|
* <li>{@link RequestMappingHandlerAdapterIntegrationTests}
|
||||||
* </ul>
|
* </ul>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -28,16 +28,15 @@ import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||||
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.MockServletConfig;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
|
|
@ -46,45 +45,51 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.servlet.View;
|
import org.springframework.web.servlet.View;
|
||||||
import org.springframework.web.servlet.ViewResolver;
|
import org.springframework.web.servlet.ViewResolver;
|
||||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
|
||||||
import org.springframework.web.servlet.mvc.annotation.UriTemplateServletAnnotationControllerTests;
|
import org.springframework.web.servlet.mvc.annotation.UriTemplateServletAnnotationControllerTests;
|
||||||
import org.springframework.web.servlet.mvc.annotation.UriTemplateServletAnnotationControllerTests.VariableNamesController;
|
|
||||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
|
||||||
import org.springframework.web.servlet.view.AbstractView;
|
import org.springframework.web.servlet.view.AbstractView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The origin of this test class is {@link UriTemplateServletAnnotationControllerTests} with the tests in this class
|
* The origin of this test class is {@link UriTemplateServletAnnotationControllerTests}.
|
||||||
* adapted to run against the HandlerMethod infrastructure rather than against the DefaultAnnotationHandlerMapping,
|
*
|
||||||
* the AnnotationMethodHandlerAdapter, and the AnnotationMethodHandlerExceptionResolver. Tests that are not supported
|
* Tests in this class run against the {@link HandlerMethod} infrastructure:
|
||||||
* are listed at the bottom.
|
* <ul>
|
||||||
*
|
* <li>RequestMappingHandlerMapping
|
||||||
|
* <li>RequestMappingHandlerAdapter
|
||||||
|
* <li>ExceptionHandlerExceptionResolver
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Rather than against the existing infrastructure:
|
||||||
|
* <ul>
|
||||||
|
* <li>DefaultAnnotationHandlerMapping
|
||||||
|
* <li>AnnotationMethodHandlerAdapter
|
||||||
|
* <li>AnnotationMethodHandlerExceptionResolver
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public class UriTemplateServletHandlerMethodTests {
|
public class UriTemplateServletAnnotationControllerHandlerMethodTests extends AbstractServletHandlerMethodTests {
|
||||||
|
|
||||||
private DispatcherServlet servlet;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simple() throws Exception {
|
public void simple() throws Exception {
|
||||||
initDispatcherServlet(SimpleUriTemplateController.class, null);
|
initServletWithControllers(SimpleUriTemplateController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42", response.getContentAsString());
|
assertEquals("test-42", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multiple() throws Exception {
|
public void multiple() throws Exception {
|
||||||
initDispatcherServlet(MultipleUriTemplateController.class, null);
|
initServletWithControllers(MultipleUriTemplateController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21-other");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21-other");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42-21-other", response.getContentAsString());
|
assertEquals("test-42-21-other", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,16 +101,16 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
pathVars.put("other", "other");
|
pathVars.put("other", "other");
|
||||||
|
|
||||||
WebApplicationContext wac =
|
WebApplicationContext wac =
|
||||||
initDispatcherServlet(ViewRenderingController.class, new BeanDefinitionRegistrar() {
|
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
||||||
public void register(GenericWebApplicationContext context) {
|
public void initialize(GenericWebApplicationContext context) {
|
||||||
RootBeanDefinition beanDef = new RootBeanDefinition(ModelValidatingViewResolver.class);
|
RootBeanDefinition beanDef = new RootBeanDefinition(ModelValidatingViewResolver.class);
|
||||||
beanDef.getConstructorArgumentValues().addGenericArgumentValue(pathVars);
|
beanDef.getConstructorArgumentValues().addGenericArgumentValue(pathVars);
|
||||||
context.registerBeanDefinition("viewResolver", beanDef);
|
context.registerBeanDefinition("viewResolver", beanDef);
|
||||||
}
|
}
|
||||||
});
|
}, ViewRenderingController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21-other");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21-other");
|
||||||
servlet.service(request, new MockHttpServletResponse());
|
getServlet().service(request, new MockHttpServletResponse());
|
||||||
|
|
||||||
ModelValidatingViewResolver resolver = wac.getBean(ModelValidatingViewResolver.class);
|
ModelValidatingViewResolver resolver = wac.getBean(ModelValidatingViewResolver.class);
|
||||||
assertEquals(3, resolver.validatedAttrCount);
|
assertEquals(3, resolver.validatedAttrCount);
|
||||||
|
|
@ -113,153 +118,153 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void binding() throws Exception {
|
public void binding() throws Exception {
|
||||||
initDispatcherServlet(BindingUriTemplateController.class, null);
|
initServletWithControllers(BindingUriTemplateController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-11-18");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-11-18");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-foo-bar");
|
request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-foo-bar");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals(400, response.getStatus());
|
assertEquals(400, response.getStatus());
|
||||||
|
|
||||||
initDispatcherServlet(NonBindingUriTemplateController.class, null);
|
initServletWithControllers(NonBindingUriTemplateController.class);
|
||||||
request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-foo-bar");
|
request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-foo-bar");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals(500, response.getStatus());
|
assertEquals(500, response.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ambiguous() throws Exception {
|
public void ambiguous() throws Exception {
|
||||||
initDispatcherServlet(AmbiguousUriTemplateController.class, null);
|
initServletWithControllers(AmbiguousUriTemplateController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/new");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/new");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("specific", response.getContentAsString());
|
assertEquals("specific", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void relative() throws Exception {
|
public void relative() throws Exception {
|
||||||
initDispatcherServlet(RelativePathUriTemplateController.class, null);
|
initServletWithControllers(RelativePathUriTemplateController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42-21", response.getContentAsString());
|
assertEquals("test-42-21", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21.html");
|
request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21.html");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42-21", response.getContentAsString());
|
assertEquals("test-42-21", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void extension() throws Exception {
|
public void extension() throws Exception {
|
||||||
initDispatcherServlet(SimpleUriTemplateController.class, null);
|
initServletWithControllers(SimpleUriTemplateController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42.xml");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42.xml");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42", response.getContentAsString());
|
assertEquals("test-42", response.getContentAsString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void typeConversionError() throws Exception {
|
public void typeConversionError() throws Exception {
|
||||||
initDispatcherServlet(SimpleUriTemplateController.class, null);
|
initServletWithControllers(SimpleUriTemplateController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo.xml");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo.xml");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
assertEquals("Invalid response status code", HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void explicitSubPath() throws Exception {
|
public void explicitSubPath() throws Exception {
|
||||||
initDispatcherServlet(ExplicitSubPathController.class, null);
|
initServletWithControllers(ExplicitSubPathController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42", response.getContentAsString());
|
assertEquals("test-42", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void implicitSubPath() throws Exception {
|
public void implicitSubPath() throws Exception {
|
||||||
initDispatcherServlet(ImplicitSubPathController.class, null);
|
initServletWithControllers(ImplicitSubPathController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42", response.getContentAsString());
|
assertEquals("test-42", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void crud() throws Exception {
|
public void crud() throws Exception {
|
||||||
initDispatcherServlet(CrudController.class, null);
|
initServletWithControllers(CrudController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("list", response.getContentAsString());
|
assertEquals("list", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/hotels/");
|
request = new MockHttpServletRequest("GET", "/hotels/");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("list", response.getContentAsString());
|
assertEquals("list", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("POST", "/hotels");
|
request = new MockHttpServletRequest("POST", "/hotels");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("create", response.getContentAsString());
|
assertEquals("create", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/hotels/42");
|
request = new MockHttpServletRequest("GET", "/hotels/42");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("show-42", response.getContentAsString());
|
assertEquals("show-42", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/hotels/42/");
|
request = new MockHttpServletRequest("GET", "/hotels/42/");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("show-42", response.getContentAsString());
|
assertEquals("show-42", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("PUT", "/hotels/42");
|
request = new MockHttpServletRequest("PUT", "/hotels/42");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("createOrUpdate-42", response.getContentAsString());
|
assertEquals("createOrUpdate-42", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("DELETE", "/hotels/42");
|
request = new MockHttpServletRequest("DELETE", "/hotels/42");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("remove-42", response.getContentAsString());
|
assertEquals("remove-42", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void methodNotSupported() throws Exception {
|
public void methodNotSupported() throws Exception {
|
||||||
initDispatcherServlet(MethodNotAllowedController.class, null);
|
initServletWithControllers(MethodNotAllowedController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/1");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/1");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("POST", "/hotels/1");
|
request = new MockHttpServletRequest("POST", "/hotels/1");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals(405, response.getStatus());
|
assertEquals(405, response.getStatus());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/hotels");
|
request = new MockHttpServletRequest("GET", "/hotels");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("POST", "/hotels");
|
request = new MockHttpServletRequest("POST", "/hotels");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals(405, response.getStatus());
|
assertEquals(405, response.getStatus());
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -267,26 +272,26 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multiPaths() throws Exception {
|
public void multiPaths() throws Exception {
|
||||||
initDispatcherServlet(MultiPathController.class, null);
|
initServletWithControllers(MultiPathController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/category/page/5");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/category/page/5");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("handle4-page-5", response.getContentAsString());
|
assertEquals("handle4-page-5", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/category/page/5.html");
|
request = new MockHttpServletRequest("GET", "/category/page/5.html");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("handle4-page-5", response.getContentAsString());
|
assertEquals("handle4-page-5", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customRegex() throws Exception {
|
public void customRegex() throws Exception {
|
||||||
initDispatcherServlet(CustomRegexController.class, null);
|
initServletWithControllers(CustomRegexController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("test-42", response.getContentAsString());
|
assertEquals("test-42", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,11 +300,11 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void menuTree() throws Exception {
|
public void menuTree() throws Exception {
|
||||||
initDispatcherServlet(MenuTreeController.class, null);
|
initServletWithControllers(MenuTreeController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/book/menu/type/M5");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/book/menu/type/M5");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("M5", response.getContentAsString());
|
assertEquals("M5", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,16 +313,16 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void variableNames() throws Exception {
|
public void variableNames() throws Exception {
|
||||||
initDispatcherServlet(VariableNamesController.class, null);
|
initServletWithControllers(VariableNamesController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("foo-foo", response.getContentAsString());
|
assertEquals("foo-foo", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("DELETE", "/test/bar");
|
request = new MockHttpServletRequest("DELETE", "/test/bar");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("bar-bar", response.getContentAsString());
|
assertEquals("bar-bar", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,11 +331,11 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void variableNamesWithUrlExtension() throws Exception {
|
public void variableNamesWithUrlExtension() throws Exception {
|
||||||
initDispatcherServlet(VariableNamesController.class, null);
|
initServletWithControllers(VariableNamesController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo.json");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo.json");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("foo-foo", response.getContentAsString());
|
assertEquals("foo-foo", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,26 +344,26 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void doIt() throws Exception {
|
public void doIt() throws Exception {
|
||||||
initDispatcherServlet(Spr6978Controller.class, null);
|
initServletWithControllers(Spr6978Controller.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/100");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/100");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("loadEntity:foo:100", response.getContentAsString());
|
assertEquals("loadEntity:foo:100", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("POST", "/foo/100");
|
request = new MockHttpServletRequest("POST", "/foo/100");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("publish:foo:100", response.getContentAsString());
|
assertEquals("publish:foo:100", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("GET", "/module/100");
|
request = new MockHttpServletRequest("GET", "/module/100");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("loadModule:100", response.getContentAsString());
|
assertEquals("loadModule:100", response.getContentAsString());
|
||||||
|
|
||||||
request = new MockHttpServletRequest("POST", "/module/100");
|
request = new MockHttpServletRequest("POST", "/module/100");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
servlet.service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("publish:module:100", response.getContentAsString());
|
assertEquals("publish:module:100", response.getContentAsString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -672,50 +677,6 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface BeanDefinitionRegistrar {
|
|
||||||
public void register(GenericWebApplicationContext context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
private WebApplicationContext initDispatcherServlet(final Class<?> controllerClass, final BeanDefinitionRegistrar registrar)
|
|
||||||
throws ServletException {
|
|
||||||
|
|
||||||
final GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
|
||||||
|
|
||||||
servlet = new DispatcherServlet() {
|
|
||||||
@Override
|
|
||||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
|
||||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass));
|
|
||||||
|
|
||||||
Class<?> mappingType = RequestMappingHandlerMapping.class;
|
|
||||||
wac.registerBeanDefinition("handlerMapping", new RootBeanDefinition(mappingType));
|
|
||||||
|
|
||||||
Class<?> adapterType = RequestMappingHandlerAdapter.class;
|
|
||||||
wac.registerBeanDefinition("handlerAdapter", new RootBeanDefinition(adapterType));
|
|
||||||
|
|
||||||
Class<?> resolverType = ExceptionHandlerExceptionResolver.class;
|
|
||||||
wac.registerBeanDefinition("requestMappingResolver", new RootBeanDefinition(resolverType));
|
|
||||||
|
|
||||||
resolverType = ResponseStatusExceptionResolver.class;
|
|
||||||
wac.registerBeanDefinition("responseStatusResolver", new RootBeanDefinition(resolverType));
|
|
||||||
|
|
||||||
resolverType = DefaultHandlerExceptionResolver.class;
|
|
||||||
wac.registerBeanDefinition("defaultResolver", new RootBeanDefinition(resolverType));
|
|
||||||
|
|
||||||
if (registrar != null) {
|
|
||||||
registrar.register(wac);
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.refresh();
|
|
||||||
return wac;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
servlet.init(new MockServletConfig());
|
|
||||||
|
|
||||||
return wac;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Ignore("ControllerClassNameHandlerMapping")
|
// @Ignore("ControllerClassNameHandlerMapping")
|
||||||
// public void controllerClassName() throws Exception {
|
// public void controllerClassName() throws Exception {
|
||||||
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2010 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.bind.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation that indicates what attributes should be stored in the session or in
|
||||||
|
* some conversational storage in order to survive a client-side redirect.
|
||||||
|
*
|
||||||
|
* TODO ...
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
* @since 3.1
|
||||||
|
*
|
||||||
|
* @see org.springframework.web.bind.support.FlashStatus
|
||||||
|
*/
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
@Documented
|
||||||
|
public @interface FlashAttributes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The names of flash attributes in the model to be stored.
|
||||||
|
*
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
String[] value() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Class[] types() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.bind.support;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.FlashAttributes;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple interface to pass into controller methods to allow them to activate
|
||||||
|
* a mode in which model attributes identified as "flash attributes" are
|
||||||
|
* temporarily stored in the session to make them available to the next
|
||||||
|
* request. The most common scenario is a client-side redirect.
|
||||||
|
*
|
||||||
|
* <p>In active mode, model attributes that match the attribute names or
|
||||||
|
* types declared via @{@link FlashAttributes} are saved in the session.
|
||||||
|
* On the next request, any flash attributes found in the session are
|
||||||
|
* automatically added to the model of the target controller method and
|
||||||
|
* are also cleared from the session.
|
||||||
|
*
|
||||||
|
* TODO ...
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
public interface FlashStatus {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
void setActive();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
boolean isActive();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.bind.support;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
public class SimpleFlashStatus implements FlashStatus {
|
||||||
|
|
||||||
|
private boolean active = false;
|
||||||
|
|
||||||
|
public void setActive() {
|
||||||
|
this.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return this.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.web.method.annotation;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.web.bind.annotation.FlashAttributes;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages flash attributes declared via @{@link FlashAttributes}.
|
||||||
|
*
|
||||||
|
* TODO ...
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
* @since 3.1
|
||||||
|
*/
|
||||||
|
public class FlashAttributesHandler {
|
||||||
|
|
||||||
|
public static final String FLASH_ATTRIBUTES_SESSION_KEY = FlashAttributesHandler.class.getName() + ".attributes";
|
||||||
|
|
||||||
|
private final Set<String> attributeNames = new HashSet<String>();
|
||||||
|
|
||||||
|
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
public FlashAttributesHandler(Class<?> handlerType) {
|
||||||
|
FlashAttributes annotation = AnnotationUtils.findAnnotation(handlerType, FlashAttributes.class);
|
||||||
|
if (annotation != null) {
|
||||||
|
this.attributeNames.addAll(Arrays.asList(annotation.value()));
|
||||||
|
this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the controller represented by this handler has declared flash
|
||||||
|
* attribute names or types via @{@link FlashAttributes}.
|
||||||
|
*/
|
||||||
|
public boolean hasFlashAttributes() {
|
||||||
|
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
public boolean isFlashAttribute(String attributeName, Class<?> attributeType) {
|
||||||
|
return (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
|
||||||
|
Map<String, Object> filtered = filterAttributes(attributes);
|
||||||
|
if (!filtered.isEmpty()) {
|
||||||
|
request.setAttribute(FLASH_ATTRIBUTES_SESSION_KEY, filtered, WebRequest.SCOPE_SESSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> filterAttributes(Map<String, ?> attributes) {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
||||||
|
for (String name : attributes.keySet()) {
|
||||||
|
Object value = attributes.get(name);
|
||||||
|
Class<?> type = (value != null) ? value.getClass() : null;
|
||||||
|
if (isFlashAttribute(name, type)) {
|
||||||
|
result.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Map<String, Object> retrieveAttributes(WebRequest request) {
|
||||||
|
return (Map<String, Object>) request.getAttribute(FLASH_ATTRIBUTES_SESSION_KEY, WebRequest.SCOPE_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO ...
|
||||||
|
*/
|
||||||
|
public void cleanupAttributes(WebRequest request) {
|
||||||
|
request.removeAttribute(FLASH_ATTRIBUTES_SESSION_KEY, WebRequest.SCOPE_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.core.Conventions;
|
import org.springframework.core.Conventions;
|
||||||
import org.springframework.core.GenericTypeResolver;
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
|
|
@ -34,6 +35,7 @@ import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||||
|
import org.springframework.web.bind.support.FlashStatus;
|
||||||
import org.springframework.web.bind.support.SessionStatus;
|
import org.springframework.web.bind.support.SessionStatus;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
|
@ -42,15 +44,16 @@ import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains methods for creating and updating a model. A {@link ModelFactory} is associated with a specific controller
|
* Provides methods to initialize the {@link Model} before a controller method
|
||||||
* through knowledge of its @{@link ModelAttribute} methods and @{@link SessionAttributes}.
|
* invocation and to update it after the controller method has been invoked.
|
||||||
*
|
*
|
||||||
* <p>{@link #initModel(NativeWebRequest, ModelAndViewContainer, HandlerMethod)} populates the model
|
* <p>On initialization the model may be populated with session attributes
|
||||||
* with handler session attributes and by invoking model attribute methods.
|
* stored during a previous request as a result of a {@link SessionAttributes}
|
||||||
|
* annotation. @{@link ModelAttribute} methods in the same controller may
|
||||||
|
* also be invoked to populate the model.
|
||||||
*
|
*
|
||||||
* <p>{@link #updateModel(NativeWebRequest, ModelAndViewContainer, SessionStatus)} updates
|
* <p>On update attributes may be removed from or stored in the session.
|
||||||
* the model (usually after the {@link RequestMapping} method has been called) promoting attributes
|
* {@link BindingResult} attributes may also be added as necessary.
|
||||||
* to the session and adding {@link BindingResult} attributes as necessary.
|
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -61,45 +64,52 @@ public final class ModelFactory {
|
||||||
|
|
||||||
private final WebDataBinderFactory binderFactory;
|
private final WebDataBinderFactory binderFactory;
|
||||||
|
|
||||||
private final SessionAttributesHandler sessionHandler;
|
private final SessionAttributesHandler sessionAttributesHandler;
|
||||||
|
|
||||||
|
private final FlashAttributesHandler flashAttributesHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a ModelFactory instance with the provided {@link ModelAttribute} methods.
|
* Create a ModelFactory instance with the provided {@link ModelAttribute} methods.
|
||||||
* @param attributeMethods {@link ModelAttribute}-annotated methods to invoke when populating a model
|
* @param attributeMethods {@link ModelAttribute} methods to initialize model instances with
|
||||||
* @param binderFactory the binder factory to use when adding {@link BindingResult}s to the model
|
* @param binderFactory used to add {@link BindingResult} attributes to the model
|
||||||
* @param sessionHandler a session attributes handler to synch attributes with the session
|
* @param sessionAttributesHandler used to access handler-specific session attributes
|
||||||
|
* @param flashAttributesHandler used to access flash attributes
|
||||||
*/
|
*/
|
||||||
public ModelFactory(List<InvocableHandlerMethod> attributeMethods,
|
public ModelFactory(List<InvocableHandlerMethod> attributeMethods,
|
||||||
WebDataBinderFactory binderFactory,
|
WebDataBinderFactory binderFactory,
|
||||||
SessionAttributesHandler sessionHandler) {
|
SessionAttributesHandler sessionAttributesHandler,
|
||||||
|
FlashAttributesHandler flashAttributesHandler) {
|
||||||
this.attributeMethods = (attributeMethods != null) ? attributeMethods : new ArrayList<InvocableHandlerMethod>();
|
this.attributeMethods = (attributeMethods != null) ? attributeMethods : new ArrayList<InvocableHandlerMethod>();
|
||||||
this.binderFactory = binderFactory;
|
this.binderFactory = binderFactory;
|
||||||
this.sessionHandler = sessionHandler;
|
this.sessionAttributesHandler = sessionAttributesHandler;
|
||||||
|
this.flashAttributesHandler = flashAttributesHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the model for a request with attributes obtained in the following order:
|
* Populate the model in the following order:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>Retrieve "known" (i.e. have been in the model in prior requests) handler session attributes from the session
|
* <li>Retrieve "remembered" (i.e. previously stored) controller-specific session attributes
|
||||||
* <li>Create attributes by invoking model attribute methods
|
* <li>Invoke @{@link ModelAttribute} methods
|
||||||
* <li>Check for not yet known handler session attributes in the session
|
* <li>Check the session for any controller-specific attributes not yet "remembered".
|
||||||
* </ol>
|
* </ol>
|
||||||
* <p>As a general rule model attributes are added only once following the above order.
|
|
||||||
*
|
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param mavContainer the {@link ModelAndViewContainer} to add model attributes to
|
* @param mavContainer contains the model to initialize
|
||||||
* @param requestMethod the request handling method for which the model is needed
|
* @param handlerMethod the @{@link RequestMapping} method for which the model is initialized
|
||||||
* @throws Exception if an exception occurs while invoking model attribute methods
|
* @throws Exception may arise from the invocation of @{@link ModelAttribute} methods
|
||||||
*/
|
*/
|
||||||
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod requestMethod)
|
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
Map<String, ?> sessionAttributes = this.sessionHandler.retrieveHandlerSessionAttributes(request);
|
Map<String, ?> sessionAttrs = this.sessionAttributesHandler.retrieveAttributes(request);
|
||||||
mavContainer.addAllAttributes(sessionAttributes);
|
mavContainer.addAllAttributes(sessionAttrs);
|
||||||
|
|
||||||
|
Map<String, ?> flashAttrs = this.flashAttributesHandler.retrieveAttributes(request);
|
||||||
|
mavContainer.addAllAttributes(flashAttrs);
|
||||||
|
this.flashAttributesHandler.cleanupAttributes(request);
|
||||||
|
|
||||||
invokeAttributeMethods(request, mavContainer);
|
invokeAttributeMethods(request, mavContainer);
|
||||||
|
|
||||||
checkMissingSessionAttributes(request, mavContainer, requestMethod);
|
checkHandlerSessionAttributes(request, mavContainer, handlerMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -125,26 +135,26 @@ public final class ModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if any {@link ModelAttribute}-annotated handler method arguments are eligible as handler session
|
* Checks if any @{@link ModelAttribute} handler method arguments declared as
|
||||||
* attributes, as defined by @{@link SessionAttributes}, and are not yet present in the model.
|
* session attributes via @{@link SessionAttributes} but are not already in the
|
||||||
* If so, attempts to retrieve them from the session and add them to the model.
|
* model. If found add them to the model, raise an exception otherwise.
|
||||||
*
|
*
|
||||||
* @throws HttpSessionRequiredException raised if a handler session attribute could is missing
|
* @throws HttpSessionRequiredException raised if a handler session attribute could is missing
|
||||||
*/
|
*/
|
||||||
private void checkMissingSessionAttributes(NativeWebRequest request,
|
private void checkHandlerSessionAttributes(NativeWebRequest request,
|
||||||
ModelAndViewContainer mavContainer,
|
ModelAndViewContainer mavContainer,
|
||||||
HandlerMethod requestMethod) throws HttpSessionRequiredException {
|
HandlerMethod handlerMethod) throws HttpSessionRequiredException {
|
||||||
for (MethodParameter parameter : requestMethod.getMethodParameters()) {
|
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
|
||||||
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
|
||||||
String name = getNameForParameter(parameter);
|
String attrName = getNameForParameter(parameter);
|
||||||
if (!mavContainer.containsAttribute(name)) {
|
if (!mavContainer.containsAttribute(attrName)) {
|
||||||
if (sessionHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
|
if (sessionAttributesHandler.isHandlerSessionAttribute(attrName, parameter.getParameterType())) {
|
||||||
Object attrValue = sessionHandler.retrieveAttribute(request, name);
|
Object attrValue = sessionAttributesHandler.retrieveAttribute(request, attrName);
|
||||||
if (attrValue == null){
|
if (attrValue == null){
|
||||||
throw new HttpSessionRequiredException(
|
throw new HttpSessionRequiredException(
|
||||||
"Session attribute '" + name + "' not found in session: " + requestMethod);
|
"Session attribute '" + attrName + "' not found in session: " + handlerMethod);
|
||||||
}
|
}
|
||||||
mavContainer.addAttribute(name, attrValue);
|
mavContainer.addAttribute(attrName, attrValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -196,15 +206,20 @@ public final class ModelFactory {
|
||||||
* @param sessionStatus whether session processing is complete
|
* @param sessionStatus whether session processing is complete
|
||||||
* @throws Exception if the process of creating {@link BindingResult} attributes causes an error
|
* @throws Exception if the process of creating {@link BindingResult} attributes causes an error
|
||||||
*/
|
*/
|
||||||
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer, SessionStatus sessionStatus)
|
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer,
|
||||||
throws Exception {
|
SessionStatus sessionStatus, FlashStatus flashStatus) throws Exception {
|
||||||
|
|
||||||
if (sessionStatus.isComplete()){
|
if (sessionStatus.isComplete()){
|
||||||
this.sessionHandler.cleanupHandlerSessionAttributes(request);
|
this.sessionAttributesHandler.cleanupAttributes(request);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sessionHandler.storeHandlerSessionAttributes(request, mavContainer.getModel());
|
if (flashStatus.isActive()) {
|
||||||
|
this.flashAttributesHandler.storeAttributes(request, mavContainer.getModel());
|
||||||
|
}
|
||||||
|
|
||||||
if (mavContainer.isResolveView()) {
|
if (mavContainer.isResolveView()) {
|
||||||
updateBindingResult(request, mavContainer.getModel());
|
updateBindingResult(request, mavContainer.getModel());
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +253,7 @@ public final class ModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> attrType = (value != null) ? value.getClass() : null;
|
Class<?> attrType = (value != null) ? value.getClass() : null;
|
||||||
if (this.sessionHandler.isHandlerSessionAttribute(attributeName, attrType)) {
|
if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,22 +32,15 @@ import org.springframework.web.bind.support.SessionStatus;
|
||||||
import org.springframework.web.context.request.WebRequest;
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides operations for managing handler-specific session attributes as defined by the
|
* Manages handler-specific session attributes declared via @{@link SessionAttributes}.
|
||||||
* {@link SessionAttributes} type-level annotation performing all operations through an
|
* Actual storage is performed through an instance of {@link SessionAttributeStore}.
|
||||||
* instance of a {@link SessionAttributeStore}.
|
|
||||||
*
|
*
|
||||||
* <p>A typical scenario involves a handler adding attributes to the {@link Model} during
|
* <p>A typical scenario begins with a controller adding attributes to the {@link Model}.
|
||||||
* a request. At the end of the request, model attributes that match to session attribute
|
* At the end of the request, model attributes are checked to see if any of them match
|
||||||
* names defined through an {@link SessionAttributes} annotation are automatically
|
* the names and types declared via @{@link SessionAttributes}. Matching model
|
||||||
* "promoted" to the session. Handler session attributes are then removed when
|
* attributes are "promoted" to the session and remain there until the controller
|
||||||
* {@link SessionStatus#setComplete()} is called by a handler.
|
* calls {@link SessionStatus#setComplete()} to indicate the session attributes are
|
||||||
*
|
* no longer needed and can be removed.
|
||||||
* <p>Therefore "session attributes" for this class means only attributes that have been
|
|
||||||
* previously confirmed by calls to {@link #isHandlerSessionAttribute(String, Class)}.
|
|
||||||
* Attribute names that have never been resolved that way will be filtered out from
|
|
||||||
* operations of this class. That means initially the actual set of resolved session
|
|
||||||
* attribute names is empty and it grows gradually as attributes are added to
|
|
||||||
* the {@link Model} and then considered for being added to the session.
|
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
|
|
@ -56,48 +49,48 @@ public class SessionAttributesHandler {
|
||||||
|
|
||||||
private final Set<String> attributeNames = new HashSet<String>();
|
private final Set<String> attributeNames = new HashSet<String>();
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
|
||||||
private final Set<Class> attributeTypes = new HashSet<Class>();
|
|
||||||
|
|
||||||
private final Set<String> resolvedAttributeNames = Collections.synchronizedSet(new HashSet<String>(4));
|
private final Set<String> resolvedAttributeNames = Collections.synchronizedSet(new HashSet<String>(4));
|
||||||
|
|
||||||
private final SessionAttributeStore attributeStore;
|
private final SessionAttributeStore sessionAttributeStore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link SessionAttributesHandler} instance for the specified handlerType.
|
* Creates a {@link SessionAttributesHandler} instance for the specified handler type
|
||||||
* <p>Inspects the given handler type for the presence of a {@link SessionAttributes} annotation and
|
* Inspects the given handler type for the presence of an @{@link SessionAttributes}
|
||||||
* stores that information for use in subsequent calls to {@link #isHandlerSessionAttribute(String, Class)}.
|
* and stores that information to identify model attribute that need to be stored,
|
||||||
* If the handler type does not contain such an annotation,
|
* retrieved, or removed from the session.
|
||||||
* {@link #isHandlerSessionAttribute(String, Class)} always returns {@code false} and all other operations
|
|
||||||
* on handler session attributes have no effect on the backend session.
|
|
||||||
* <p>Use {@link #hasSessionAttributes()} to check if the handler type has defined any session attribute names
|
|
||||||
* of interest through a {@link SessionAttributes} annotation.
|
|
||||||
* @param handlerType the handler type to inspect for a {@link SessionAttributes} annotation
|
* @param handlerType the handler type to inspect for a {@link SessionAttributes} annotation
|
||||||
* @param attributeStore the {@link SessionAttributeStore} to delegate to for the actual backend session access
|
* @param sessionAttributeStore used for session access
|
||||||
*/
|
*/
|
||||||
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore attributeStore) {
|
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
|
||||||
Assert.notNull(attributeStore, "SessionAttributeStore may not be null.");
|
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
|
||||||
this.attributeStore = attributeStore;
|
this.sessionAttributeStore = sessionAttributeStore;
|
||||||
|
|
||||||
SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
|
SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
this.attributeNames.addAll(Arrays.asList(annotation.value()));
|
this.attributeNames.addAll(Arrays.asList(annotation.value()));
|
||||||
this.attributeTypes.addAll(Arrays.asList(annotation.types()));
|
this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the handler type has specified any session attribute names of interest through a
|
* Whether the controller represented by this handler has declared session
|
||||||
* {@link SessionAttributes} annotation.
|
* attribute names or types of interest via @{@link SessionAttributes}.
|
||||||
*/
|
*/
|
||||||
public boolean hasSessionAttributes() {
|
public boolean hasSessionAttributes() {
|
||||||
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
|
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate whether or not an attribute is a handler session attribute of interest as defined
|
* Whether the controller represented by this instance has declared a specific
|
||||||
* in a {@link SessionAttributes} annotation. Attributes names successfully resolved through
|
* attribute as a session attribute via @{@link SessionAttributes}.
|
||||||
* this method are remembered and in other operations.
|
*
|
||||||
|
* <p>Attributes successfully resolved through this method are "remembered" and
|
||||||
|
* used by calls to {@link #retrieveAttributes(WebRequest)} and
|
||||||
|
* {@link #cleanupAttributes(WebRequest)}. In other words unless attributes
|
||||||
|
* have been resolved and stored before, retrieval and cleanup have no impact.
|
||||||
|
*
|
||||||
* @param attributeName the attribute name to check, must not be null
|
* @param attributeName the attribute name to check, must not be null
|
||||||
* @param attributeType the type for the attribute, not required but should be provided when
|
* @param attributeType the type for the attribute, not required but should be provided when
|
||||||
* available as session attributes of interest can be matched by type
|
* available as session attributes of interest can be matched by type
|
||||||
|
|
@ -114,29 +107,32 @@ public class SessionAttributesHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the specified attribute through the underlying {@link SessionAttributeStore}.
|
* Stores a subset of the given attributes in the session. Attributes not
|
||||||
* Although not required use of this method implies a prior call to
|
* declared as session attributes via @{@link SessionAttributes} are ignored.
|
||||||
* {@link #isHandlerSessionAttribute(String, Class)} has been made to see if the attribute
|
* @param request the current request
|
||||||
* name is a handler-specific session attribute of interest.
|
* @param attributes candidate attributes for session storage
|
||||||
* @param request the request for the session operation
|
|
||||||
* @param attributeName the name of the attribute
|
|
||||||
* @return the attribute value or {@code null} if none
|
|
||||||
*/
|
*/
|
||||||
public Object retrieveAttribute(WebRequest request, String attributeName) {
|
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
|
||||||
return this.attributeStore.retrieveAttribute(request, attributeName);
|
for (String name : attributes.keySet()) {
|
||||||
|
Object value = attributes.get(name);
|
||||||
|
Class<?> attrType = (value != null) ? value.getClass() : null;
|
||||||
|
|
||||||
|
if (isHandlerSessionAttribute(name, attrType)) {
|
||||||
|
this.sessionAttributeStore.storeAttribute(request, name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve attributes for the underlying handler type from the backend session.
|
* Retrieves "remembered" (i.e. previously stored) session attributes
|
||||||
* <p>Only attributes that have previously been successfully resolved via calls to
|
* for the controller represented by this handler.
|
||||||
* {@link #isHandlerSessionAttribute(String, Class)} are considered.
|
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @return a map with attributes or an empty map
|
* @return a map with handler session attributes; possibly empty.
|
||||||
*/
|
*/
|
||||||
public Map<String, ?> retrieveHandlerSessionAttributes(WebRequest request) {
|
public Map<String, Object> retrieveAttributes(WebRequest request) {
|
||||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||||
for (String name : this.resolvedAttributeNames) {
|
for (String name : this.resolvedAttributeNames) {
|
||||||
Object value = this.attributeStore.retrieveAttribute(request, name);
|
Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
attributes.put(name, value);
|
attributes.put(name, value);
|
||||||
}
|
}
|
||||||
|
|
@ -145,34 +141,24 @@ public class SessionAttributesHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up attributes for the underlying handler type from the backend session.
|
* Cleans "remembered" (i.e. previously stored) session attributes
|
||||||
* <p>Only attributes that have previously been successfully resolved via calls to
|
* for the controller represented by this handler.
|
||||||
* {@link #isHandlerSessionAttribute(String, Class)} are removed.
|
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
*/
|
*/
|
||||||
public void cleanupHandlerSessionAttributes(WebRequest request) {
|
public void cleanupAttributes(WebRequest request) {
|
||||||
for (String attributeName : this.resolvedAttributeNames) {
|
for (String attributeName : this.resolvedAttributeNames) {
|
||||||
this.attributeStore.cleanupAttribute(request, attributeName);
|
this.sessionAttributeStore.cleanupAttribute(request, attributeName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store attributes in the backend session.
|
* A pass-through call to the underlying {@link SessionAttributeStore}.
|
||||||
* <p>Only attributes that have previously been successfully resolved via calls to
|
|
||||||
* {@link #isHandlerSessionAttribute(String, Class)} are stored. All other attributes
|
|
||||||
* from the input map are ignored.
|
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param attributes the attribute pairs to consider for storing
|
* @param attributeName the name of the attribute of interest
|
||||||
|
* @return the attribute value or {@code null}
|
||||||
*/
|
*/
|
||||||
public void storeHandlerSessionAttributes(WebRequest request, Map<String, Object> attributes) {
|
Object retrieveAttribute(WebRequest request, String attributeName) {
|
||||||
for (String name : attributes.keySet()) {
|
return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
|
||||||
Object value = attributes.get(name);
|
|
||||||
Class<?> attrType = (value != null) ? value.getClass() : null;
|
|
||||||
|
|
||||||
if (isHandlerSessionAttribute(name, attrType)) {
|
|
||||||
this.attributeStore.storeAttribute(request, name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -41,6 +41,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||||
|
import org.springframework.web.bind.support.SimpleFlashStatus;
|
||||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
|
@ -63,8 +64,10 @@ public class ModelFactoryTests {
|
||||||
|
|
||||||
private InvocableHandlerMethod handleSessionAttrMethod;
|
private InvocableHandlerMethod handleSessionAttrMethod;
|
||||||
|
|
||||||
private SessionAttributesHandler handlerSessionAttributeStore;
|
private SessionAttributesHandler sessionAttrsHandler;
|
||||||
|
|
||||||
|
private FlashAttributesHandler flashAttrsHandler;
|
||||||
|
|
||||||
private SessionAttributeStore sessionAttributeStore;
|
private SessionAttributeStore sessionAttributeStore;
|
||||||
|
|
||||||
private ModelAndViewContainer mavContainer;
|
private ModelAndViewContainer mavContainer;
|
||||||
|
|
@ -78,13 +81,14 @@ public class ModelFactoryTests {
|
||||||
Method method = handlerType.getDeclaredMethod("handleSessionAttr", String.class);
|
Method method = handlerType.getDeclaredMethod("handleSessionAttr", String.class);
|
||||||
handleSessionAttrMethod = new InvocableHandlerMethod(handler, method);
|
handleSessionAttrMethod = new InvocableHandlerMethod(handler, method);
|
||||||
sessionAttributeStore = new DefaultSessionAttributeStore();
|
sessionAttributeStore = new DefaultSessionAttributeStore();
|
||||||
handlerSessionAttributeStore = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
sessionAttrsHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
|
||||||
|
flashAttrsHandler = new FlashAttributesHandler(handlerType);
|
||||||
mavContainer = new ModelAndViewContainer();
|
mavContainer = new ModelAndViewContainer();
|
||||||
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addAttributeToModel() throws Exception {
|
public void modelAttributeMethod() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
||||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||||
|
|
||||||
|
|
@ -92,7 +96,7 @@ public class ModelFactoryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void returnAttributeWithName() throws Exception {
|
public void modelAttributeMethodWithSpecifiedName() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttrWithName");
|
ModelFactory modelFactory = createModelFactory("modelAttrWithName");
|
||||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||||
|
|
||||||
|
|
@ -100,7 +104,7 @@ public class ModelFactoryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void returnAttributeWithNameByConvention() throws Exception {
|
public void modelAttributeMethodWithNameByConvention() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttrConvention");
|
ModelFactory modelFactory = createModelFactory("modelAttrConvention");
|
||||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||||
|
|
||||||
|
|
@ -108,7 +112,7 @@ public class ModelFactoryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void returnNullAttributeValue() throws Exception {
|
public void modelAttributeMethodWithNullReturnValue() throws Exception {
|
||||||
ModelFactory modelFactory = createModelFactory("nullModelAttr");
|
ModelFactory modelFactory = createModelFactory("nullModelAttr");
|
||||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||||
|
|
||||||
|
|
@ -117,11 +121,11 @@ public class ModelFactoryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void retrieveAttributeFromSession() throws Exception {
|
public void sessionAttribute() throws Exception {
|
||||||
sessionAttributeStore.storeAttribute(webRequest, "sessionAttr", "sessionAttrValue");
|
sessionAttributeStore.storeAttribute(webRequest, "sessionAttr", "sessionAttrValue");
|
||||||
|
|
||||||
// Resolve successfully handler session attribute once
|
// Resolve successfully handler session attribute once
|
||||||
assertTrue(handlerSessionAttributeStore.isHandlerSessionAttribute("sessionAttr", null));
|
assertTrue(sessionAttrsHandler.isHandlerSessionAttribute("sessionAttr", null));
|
||||||
|
|
||||||
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
|
||||||
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
modelFactory.initModel(webRequest, mavContainer, handleMethod);
|
||||||
|
|
@ -131,7 +135,7 @@ public class ModelFactoryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requiredSessionAttribute() throws Exception {
|
public void requiredSessionAttribute() throws Exception {
|
||||||
ModelFactory modelFactory = new ModelFactory(null, null, handlerSessionAttributeStore);
|
ModelFactory modelFactory = new ModelFactory(null, null, sessionAttrsHandler, flashAttrsHandler);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
modelFactory.initModel(webRequest, mavContainer, handleSessionAttrMethod);
|
modelFactory.initModel(webRequest, mavContainer, handleSessionAttrMethod);
|
||||||
|
|
@ -145,19 +149,18 @@ public class ModelFactoryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateBindingResult() throws Exception {
|
public void updateModelBindingResultKeys() throws Exception {
|
||||||
String attrName = "attr1";
|
String attrName = "attr1";
|
||||||
Object attrValue = new Object();
|
Object attrValue = new Object();
|
||||||
mavContainer.addAttribute(attrName, attrValue);
|
mavContainer.addAttribute(attrName, attrValue);
|
||||||
|
|
||||||
WebDataBinder dataBinder = new WebDataBinder(attrValue, attrName);
|
WebDataBinder dataBinder = new WebDataBinder(attrValue, attrName);
|
||||||
|
|
||||||
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
|
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
|
||||||
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
|
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
|
||||||
replay(binderFactory);
|
replay(binderFactory);
|
||||||
|
|
||||||
ModelFactory modelFactory = new ModelFactory(null, binderFactory, handlerSessionAttributeStore);
|
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler, flashAttrsHandler);
|
||||||
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus());
|
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus(), new SimpleFlashStatus());
|
||||||
|
|
||||||
assertEquals(attrValue, mavContainer.getModel().remove(attrName));
|
assertEquals(attrValue, mavContainer.getModel().remove(attrName));
|
||||||
assertSame(dataBinder.getBindingResult(), mavContainer.getModel().remove(bindingResultKey(attrName)));
|
assertSame(dataBinder.getBindingResult(), mavContainer.getModel().remove(bindingResultKey(attrName)));
|
||||||
|
|
@ -165,6 +168,36 @@ public class ModelFactoryTests {
|
||||||
|
|
||||||
verify(binderFactory);
|
verify(binderFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateModelSessionStatusComplete() throws Exception {
|
||||||
|
String attrName = "sessionAttr";
|
||||||
|
String attrValue = "sessionAttrValue";
|
||||||
|
|
||||||
|
mavContainer.addAttribute(attrName, attrValue);
|
||||||
|
sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
|
||||||
|
|
||||||
|
// Resolve successfully handler session attribute once
|
||||||
|
assertTrue(sessionAttrsHandler.isHandlerSessionAttribute(attrName, null));
|
||||||
|
|
||||||
|
WebDataBinder dataBinder = new WebDataBinder(attrValue, attrName);
|
||||||
|
WebDataBinderFactory binderFactory = createMock(WebDataBinderFactory.class);
|
||||||
|
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
|
||||||
|
replay(binderFactory);
|
||||||
|
|
||||||
|
SimpleSessionStatus status = new SimpleSessionStatus();
|
||||||
|
status.setComplete();
|
||||||
|
|
||||||
|
// TODO: test with active FlashStatus
|
||||||
|
|
||||||
|
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler, flashAttrsHandler);
|
||||||
|
modelFactory.updateModel(webRequest, mavContainer, status, new SimpleFlashStatus());
|
||||||
|
|
||||||
|
assertEquals(attrValue, mavContainer.getAttribute(attrName));
|
||||||
|
assertNull(sessionAttributeStore.retrieveAttribute(webRequest, attrName));
|
||||||
|
|
||||||
|
verify(binderFactory);
|
||||||
|
}
|
||||||
|
|
||||||
private String bindingResultKey(String key) {
|
private String bindingResultKey(String key) {
|
||||||
return BindingResult.MODEL_KEY_PREFIX + key;
|
return BindingResult.MODEL_KEY_PREFIX + key;
|
||||||
|
|
@ -181,7 +214,7 @@ public class ModelFactoryTests {
|
||||||
handlerMethod.setDataBinderFactory(null);
|
handlerMethod.setDataBinderFactory(null);
|
||||||
handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||||
|
|
||||||
return new ModelFactory(Arrays.asList(handlerMethod), null, handlerSessionAttributeStore);
|
return new ModelFactory(Arrays.asList(handlerMethod), null, sessionAttrsHandler, flashAttrsHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SessionAttributes("sessionAttr") @SuppressWarnings("unused")
|
@SessionAttributes("sessionAttr") @SuppressWarnings("unused")
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class HandlerSessionAttributeStoreTests {
|
public class SessionAttributesHandlerTests {
|
||||||
|
|
||||||
private Class<?> handlerType = SessionAttributeHandler.class;
|
private Class<?> handlerType = SessionAttributeHandler.class;
|
||||||
|
|
||||||
|
|
@ -77,7 +77,7 @@ public class HandlerSessionAttributeStoreTests {
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr1", null));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr1", null));
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class));
|
||||||
|
|
||||||
Map<String, ?> attributes = sessionAttributesHandler.retrieveHandlerSessionAttributes(request);
|
Map<String, ?> attributes = sessionAttributesHandler.retrieveAttributes(request);
|
||||||
|
|
||||||
assertEquals(new HashSet<String>(asList("attr1", "attr3")), attributes.keySet());
|
assertEquals(new HashSet<String>(asList("attr1", "attr3")), attributes.keySet());
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +92,7 @@ public class HandlerSessionAttributeStoreTests {
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr1", null));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr1", null));
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class));
|
||||||
|
|
||||||
sessionAttributesHandler.cleanupHandlerSessionAttributes(request);
|
sessionAttributesHandler.cleanupAttributes(request);
|
||||||
|
|
||||||
assertNull(sessionAttributeStore.retrieveAttribute(request, "attr1"));
|
assertNull(sessionAttributeStore.retrieveAttribute(request, "attr1"));
|
||||||
assertNotNull(sessionAttributeStore.retrieveAttribute(request, "attr2"));
|
assertNotNull(sessionAttributeStore.retrieveAttribute(request, "attr2"));
|
||||||
|
|
@ -111,7 +111,7 @@ public class HandlerSessionAttributeStoreTests {
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr2", null));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr2", null));
|
||||||
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class));
|
assertTrue(sessionAttributesHandler.isHandlerSessionAttribute("attr3", TestBean.class));
|
||||||
|
|
||||||
sessionAttributesHandler.storeHandlerSessionAttributes(request, model);
|
sessionAttributesHandler.storeAttributes(request, model);
|
||||||
|
|
||||||
assertEquals("value1", sessionAttributeStore.retrieveAttribute(request, "attr1"));
|
assertEquals("value1", sessionAttributeStore.retrieveAttribute(request, "attr1"));
|
||||||
assertEquals("value2", sessionAttributeStore.retrieveAttribute(request, "attr2"));
|
assertEquals("value2", sessionAttributeStore.retrieveAttribute(request, "attr2"));
|
||||||
Loading…
Reference in New Issue