/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.method.annotation;
import java.lang.reflect.Method;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
import org.springframework.web.bind.support.SessionAttributeStore;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.mock;
/**
* Text fixture for {@link ModelFactory} tests.
*
* @author Rossen Stoyanchev
*/
public class ModelFactoryTests {
private NativeWebRequest webRequest;
private SessionAttributesHandler attributeHandler;
private SessionAttributeStore attributeStore;
private TestController controller = new TestController();
private ModelAndViewContainer mavContainer;
@Before
public void setUp() throws Exception {
this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
this.attributeStore = new DefaultSessionAttributeStore();
this.attributeHandler = new SessionAttributesHandler(TestController.class, this.attributeStore);
this.controller = new TestController();
this.mavContainer = new ModelAndViewContainer();
}
@Test
public void modelAttributeMethod() throws Exception {
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
HandlerMethod handlerMethod = createHandlerMethod("handle");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("modelAttr"));
}
@Test
public void modelAttributeMethodWithExplicitName() throws Exception {
ModelFactory modelFactory = createModelFactory("modelAttrWithName");
HandlerMethod handlerMethod = createHandlerMethod("handle");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("name"));
}
@Test
public void modelAttributeMethodWithNameByConvention() throws Exception {
ModelFactory modelFactory = createModelFactory("modelAttrConvention");
HandlerMethod handlerMethod = createHandlerMethod("handle");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertEquals(Boolean.TRUE, this.mavContainer.getModel().get("boolean"));
}
@Test
public void modelAttributeMethodWithNullReturnValue() throws Exception {
ModelFactory modelFactory = createModelFactory("nullModelAttr");
HandlerMethod handlerMethod = createHandlerMethod("handle");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertTrue(this.mavContainer.containsAttribute("name"));
assertNull(this.mavContainer.getModel().get("name"));
}
@Test
public void modelAttributeWithBindingDisabled() throws Exception {
ModelFactory modelFactory = createModelFactory("modelAttrWithBindingDisabled");
HandlerMethod handlerMethod = createHandlerMethod("handle");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertTrue(this.mavContainer.containsAttribute("foo"));
assertTrue(this.mavContainer.isBindingDisabled("foo"));
}
@Test
public void modelAttributeFromSessionWithBindingDisabled() throws Exception {
Foo foo = new Foo();
this.attributeStore.storeAttribute(this.webRequest, "foo", foo);
ModelFactory modelFactory = createModelFactory("modelAttrWithBindingDisabled");
HandlerMethod handlerMethod = createHandlerMethod("handle");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertTrue(this.mavContainer.containsAttribute("foo"));
assertSame(foo, this.mavContainer.getModel().get("foo"));
assertTrue(this.mavContainer.isBindingDisabled("foo"));
}
@Test
public void sessionAttribute() throws Exception {
this.attributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue");
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
HandlerMethod handlerMethod = createHandlerMethod("handle");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertEquals("sessionAttrValue", this.mavContainer.getModel().get("sessionAttr"));
}
@Test
public void sessionAttributeNotPresent() throws Exception {
ModelFactory modelFactory = new ModelFactory(null, null, this.attributeHandler);
HandlerMethod handlerMethod = createHandlerMethod("handleSessionAttr", String.class);
try {
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
fail("Expected HttpSessionRequiredException");
}
catch (HttpSessionRequiredException e) {
// expected
}
// Now add attribute and try again
this.attributeStore.storeAttribute(this.webRequest, "sessionAttr", "sessionAttrValue");
modelFactory.initModel(this.webRequest, this.mavContainer, handlerMethod);
assertEquals("sessionAttrValue", this.mavContainer.getModel().get("sessionAttr"));
}
@Test
public void updateModelBindingResult() throws Exception {
String commandName = "attr1";
Object command = new Object();
ModelAndViewContainer container = new ModelAndViewContainer();
container.addAttribute(commandName, command);
WebDataBinder dataBinder = new WebDataBinder(command, commandName);
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
given(binderFactory.createBinder(this.webRequest, command, commandName)).willReturn(dataBinder);
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
modelFactory.updateModel(this.webRequest, container);
assertEquals(command, container.getModel().get(commandName));
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + commandName;
assertSame(dataBinder.getBindingResult(), container.getModel().get(bindingResultKey));
assertEquals(2, container.getModel().size());
}
@Test
public void updateModelSessionAttributesSaved() throws Exception {
String attributeName = "sessionAttr";
String attribute = "value";
ModelAndViewContainer container = new ModelAndViewContainer();
container.addAttribute(attributeName, attribute);
WebDataBinder dataBinder = new WebDataBinder(attribute, attributeName);
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder);
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
modelFactory.updateModel(this.webRequest, container);
assertEquals(attribute, container.getModel().get(attributeName));
assertEquals(attribute, this.attributeStore.retrieveAttribute(this.webRequest, attributeName));
}
@Test
public void updateModelSessionAttributesRemoved() throws Exception {
String attributeName = "sessionAttr";
String attribute = "value";
ModelAndViewContainer container = new ModelAndViewContainer();
container.addAttribute(attributeName, attribute);
this.attributeStore.storeAttribute(this.webRequest, attributeName, attribute);
WebDataBinder dataBinder = new WebDataBinder(attribute, attributeName);
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder);
container.getSessionStatus().setComplete();
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
modelFactory.updateModel(this.webRequest, container);
assertEquals(attribute, container.getModel().get(attributeName));
assertNull(this.attributeStore.retrieveAttribute(this.webRequest, attributeName));
}
// SPR-12542
@Test
public void updateModelWhenRedirecting() throws Exception {
String attributeName = "sessionAttr";
String attribute = "value";
ModelAndViewContainer container = new ModelAndViewContainer();
container.addAttribute(attributeName, attribute);
String queryParam = "123";
String queryParamName = "q";
container.setRedirectModel(new ModelMap(queryParamName, queryParam));
container.setRedirectModelScenario(true);
WebDataBinder dataBinder = new WebDataBinder(attribute, attributeName);
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
given(binderFactory.createBinder(this.webRequest, attribute, attributeName)).willReturn(dataBinder);
ModelFactory modelFactory = new ModelFactory(null, binderFactory, this.attributeHandler);
modelFactory.updateModel(this.webRequest, container);
assertEquals(queryParam, container.getModel().get(queryParamName));
assertEquals(1, container.getModel().size());
assertEquals(attribute, this.attributeStore.retrieveAttribute(this.webRequest, attributeName));
}
private ModelFactory createModelFactory(String methodName, Class<?>... parameterTypes) throws Exception {
HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
resolvers.addResolver(new ModelMethodProcessor());
InvocableHandlerMethod modelMethod = createHandlerMethod(methodName, parameterTypes);
modelMethod.setHandlerMethodArgumentResolvers(resolvers);
modelMethod.setDataBinderFactory(null);
modelMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
return new ModelFactory(Collections.singletonList(modelMethod), null, this.attributeHandler);
}
private InvocableHandlerMethod createHandlerMethod(String methodName, Class<?>... paramTypes) throws Exception {
Method method = this.controller.getClass().getMethod(methodName, paramTypes);
return new InvocableHandlerMethod(this.controller, method);
}
@SessionAttributes({"sessionAttr", "foo"}) @SuppressWarnings("unused")
private static class TestController {
@ModelAttribute
public void modelAttr(Model model) {
model.addAttribute("modelAttr", Boolean.TRUE);
}
@ModelAttribute("name")
public Boolean modelAttrWithName() {
return Boolean.TRUE;
}
@ModelAttribute
public Boolean modelAttrConvention() {
return Boolean.TRUE;
}
@ModelAttribute("name")
public Boolean nullModelAttr() {
return null;
}
@ModelAttribute(name="foo", binding=false)
public Foo modelAttrWithBindingDisabled() {
return new Foo();
}
public void handle() {
}
public void handleSessionAttr(@ModelAttribute("sessionAttr") String sessionAttr) {
}
}
private static class Foo {
}
}