SPR-6464 Drop @FlashAttributes, add ResponseContext, ViewResponse, and RedirectResponse types for annotated controllers to use to prepare a redirect response with flash attributes; Add FlashMap and FlashMapManager and update DispatcherServlet to discover and invoke the FlashMapManager.

This commit is contained in:
Rossen Stoyanchev
2011-08-08 14:00:07 +00:00
parent 11597c906d
commit 1df0cd9f20
28 changed files with 1171 additions and 724 deletions

View File

@@ -1,56 +0,0 @@
/*
* 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 {};
}

View File

@@ -1,51 +0,0 @@
/*
* 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();
}

View File

@@ -1,37 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,108 +0,0 @@
/*
* 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);
}
}

View File

@@ -35,7 +35,6 @@ import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.FlashStatus;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
@@ -66,23 +65,18 @@ public final class ModelFactory {
private final SessionAttributesHandler sessionAttributesHandler;
private final FlashAttributesHandler flashAttributesHandler;
/**
* Create a ModelFactory instance with the provided {@link ModelAttribute} methods.
* @param attributeMethods {@link ModelAttribute} methods to initialize model instances with
* @param binderFactory used to add {@link BindingResult} attributes to the model
* @param sessionAttributesHandler used to access handler-specific session attributes
* @param flashAttributesHandler used to access flash attributes
*/
public ModelFactory(List<InvocableHandlerMethod> attributeMethods,
WebDataBinderFactory binderFactory,
SessionAttributesHandler sessionAttributesHandler,
FlashAttributesHandler flashAttributesHandler) {
SessionAttributesHandler sessionAttributesHandler) {
this.attributeMethods = (attributeMethods != null) ? attributeMethods : new ArrayList<InvocableHandlerMethod>();
this.binderFactory = binderFactory;
this.sessionAttributesHandler = sessionAttributesHandler;
this.flashAttributesHandler = flashAttributesHandler;
}
/**
@@ -93,21 +87,17 @@ public final class ModelFactory {
* <li>Check the session for any controller-specific attributes not yet "remembered".
* </ol>
* @param request the current request
* @param mavContainer contains the model to initialize
* @param mavContainer contains the model to initialize
* @param handlerMethod the @{@link RequestMapping} method for which the model is initialized
* @throws Exception may arise from the invocation of @{@link ModelAttribute} methods
*/
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
throws Exception {
Map<String, ?> sessionAttrs = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.addAllAttributes(sessionAttrs);
mavContainer.mergeAttributes(sessionAttrs);
Map<String, ?> flashAttrs = this.flashAttributesHandler.retrieveAttributes(request);
mavContainer.addAllAttributes(flashAttrs);
this.flashAttributesHandler.cleanupAttributes(request);
invokeAttributeMethods(request, mavContainer);
invokeModelAttributeMethods(request, mavContainer);
checkHandlerSessionAttributes(request, mavContainer, handlerMethod);
}
@@ -116,7 +106,7 @@ public final class ModelFactory {
* Invoke model attribute methods to populate the model.
* If two methods return the same attribute, the attribute from the first method is added.
*/
private void invokeAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
throws Exception {
for (InvocableHandlerMethod attrMethod : this.attributeMethods) {
@@ -128,18 +118,23 @@ public final class ModelFactory {
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
if (!attrMethod.isVoid()){
String valueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
mavContainer.mergeAttribute(valueName, returnValue);
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
if (!mavContainer.containsAttribute(returnValueName)) {
mavContainer.addAttribute(returnValueName, returnValue);
}
}
}
}
/**
* Checks if any @{@link ModelAttribute} handler method arguments declared as
* session attributes via @{@link SessionAttributes} but are not already in the
* model. If found add them to the model, raise an exception otherwise.
* Checks for @{@link ModelAttribute} arguments in the signature of the
* {@link RequestMapping} method that are declared as session attributes
* via @{@link SessionAttributes} but are not already in the model.
* Those attributes may have been outside of this controller.
* Try to locate the attributes in the session or raise an exception.
*
* @throws HttpSessionRequiredException raised if a handler session attribute could is missing
* @throws HttpSessionRequiredException raised if an attribute declared
* as session attribute is missing.
*/
private void checkHandlerSessionAttributes(NativeWebRequest request,
ModelAndViewContainer mavContainer,
@@ -203,11 +198,11 @@ public final class ModelFactory {
* promotes model attributes to the session, and adds {@link BindingResult} attributes where missing.
* @param request the current request
* @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param sessionStatus whether session processing is complete
* @param sessionStatus the session status for the current request
* @throws Exception if the process of creating {@link BindingResult} attributes causes an error
*/
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer,
SessionStatus sessionStatus, FlashStatus flashStatus) throws Exception {
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer, SessionStatus sessionStatus)
throws Exception {
if (sessionStatus.isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
@@ -215,10 +210,6 @@ public final class ModelFactory {
else {
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
}
if (flashStatus.isActive()) {
this.flashAttributesHandler.storeAttributes(request, mavContainer.getModel());
}
if (mavContainer.isResolveView()) {
updateBindingResult(request, mavContainer.getModel());

View File

@@ -95,7 +95,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
WebDataBinderFactory binderFactory) throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object target = (mavContainer.containsAttribute(name)) ?
mavContainer.getAttribute(name) : createAttribute(name, parameter, binderFactory, request);
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
WebDataBinder binder = binderFactory.createBinder(request, target, name);

View File

@@ -56,9 +56,7 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
boolean hasModelAttr = returnType.getMethodAnnotation(ModelAttribute.class) != null;
return (Model.class.isAssignableFrom(paramType)
|| (Map.class.isAssignableFrom(paramType) && !hasModelAttr));
return (Model.class.isAssignableFrom(paramType) || (Map.class.isAssignableFrom(paramType) && !hasModelAttr));
}
@SuppressWarnings({ "unchecked", "rawtypes" })

View File

@@ -18,142 +18,141 @@ package org.springframework.web.method.support;
import java.util.Map;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.support.BindingAwareModelMap;
/**
* Provides access to the model and a place to record model and view related decisions made by
* {@link HandlerMethodArgumentResolver}s or a {@link HandlerMethodReturnValueHandler}.
*
* <p>In addition to storing model attributes and a view, the {@link ModelAndViewContainer} also provides
* a {@link #setResolveView(boolean)} flag, which can be used to request or bypass a view resolution phase.
* This is most commonly used from {@link HandlerMethodReturnValueHandler}s but in some cases may also be
* used from {@link HandlerMethodArgumentResolver}s such as when a handler method accepts an argument
* providing access to the response. When that is the case, if the handler method returns {@code null},
* view resolution is skipped.
* Record model and view related decisions made by {@link HandlerMethodArgumentResolver}s
* and {@link HandlerMethodReturnValueHandler}s during the course of invocation of a
* request-handling method.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public class ModelAndViewContainer {
private String viewName;
private Object view;
private final ModelMap model;
private boolean resolveView = true;
private final ModelMap model = new BindingAwareModelMap();
/**
* Create a {@link ModelAndViewContainer} instance with a {@link BindingAwareModelMap}.
* Create a new instance.
*/
public ModelAndViewContainer() {
this.model = new BindingAwareModelMap();
}
/**
* Create a {@link ModelAndViewContainer} instance with the given {@link ModelMap} instance.
* @param model the model to use
*/
public ModelAndViewContainer(ModelMap model) {
Assert.notNull(model);
this.model = model;
}
/**
* @return the model for the current request
*/
public ModelMap getModel() {
return model;
}
/**
* @return the view name to use for view resolution, or {@code null}
*/
public String getViewName() {
return this.viewName;
}
/**
* @param viewName the name of the view to use for view resolution
* Set a view name to be resolved by the DispatcherServlet via a ViewResolver.
* Will override any pre-existing view name or View.
*/
public void setViewName(String viewName) {
this.viewName = viewName;
this.view = viewName;
}
/**
* @return the view instance to use for view resolution
* Return the view name to be resolved by the DispatcherServlet via a
* ViewResolver, or {@code null} if a View object is set.
*/
public Object getView() {
return this.view;
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
/**
* @param view the view instance to use for view resolution
* Set a View object to be used by the DispatcherServlet.
* Will override any pre-existing view name or View.
*/
public void setView(Object view) {
this.view = view;
}
/**
* @return whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
* Return the View object, or {@code null} if we using a view name
* to be resolved by the DispatcherServlet via a ViewResolver.
*/
public boolean isResolveView() {
return resolveView;
public Object getView() {
return this.view;
}
/**
* @param resolveView whether the view resolution is requested ({@code true}), or should be bypassed ({@code false})
* Whether the view is a view reference specified via a name to be
* resolved by the DispatcherServlet via a ViewResolver.
*/
public boolean isViewReference() {
return (this.view instanceof String);
}
/**
* Whether view resolution is required or not. The default value is "true".
* <p>When set to "false" by a {@link HandlerMethodReturnValueHandler}, the response
* is considered complete and view resolution is not be performed.
* <p>When set to "false" by {@link HandlerMethodArgumentResolver}, the response is
* considered complete only in combination with the request mapping method
* returning {@code null} or void.
*/
public void setResolveView(boolean resolveView) {
this.resolveView = resolveView;
}
/**
* Whether view resolution is required or not.
*/
public boolean isResolveView() {
return this.resolveView;
}
/**
* Whether model contains an attribute of the given name.
* @param name the name of the model attribute
* @return {@code true} if the model contains an attribute by that name and the name is not an empty string
* Return the underlying {@code ModelMap} instance, never {@code null}.
*/
public ModelMap getModel() {
return this.model;
}
/**
* Add the supplied attribute to the underlying model.
* @see ModelMap#addAttribute(String, Object)
*/
public ModelAndViewContainer addAttribute(String name, Object value) {
this.model.addAttribute(name, value);
return this;
}
/**
* Add the supplied attribute to the underlying model.
* @see Model#addAttribute(Object)
*/
public ModelAndViewContainer addAttribute(Object value) {
this.model.addAttribute(value);
return this;
}
/**
* Copy all attributes to the underlying model.
* @see ModelMap#addAllAttributes(Map)
*/
public ModelAndViewContainer addAllAttributes(Map<String, ?> attributes) {
this.model.addAllAttributes(attributes);
return this;
}
/**
* Copy attributes in the supplied <code>Map</code> with existing objects of
* the same name taking precedence (i.e. not getting replaced).
* @see ModelMap#mergeAttributes(Map)
*/
public ModelAndViewContainer mergeAttributes(Map<String, ?> attributes) {
this.model.mergeAttributes(attributes);
return this;
}
/**
* Whether the underlying model contains the given attribute name.
* @see ModelMap#containsAttribute(String)
*/
public boolean containsAttribute(String name) {
return (StringUtils.hasText(name) && model.containsAttribute(name));
}
/**
* @param name the attribute to get from the model
* @return the attribute or {@code null}
*/
public Object getAttribute(String name) {
return model.get(name);
}
/**
* Add the supplied attribute under the given name.
* @param name the name of the model attribute (never null)
* @param value the model attribute value (can be null)
*/
public void addAttribute(String name, Object value) {
model.addAttribute(name, value);
}
/**
* Copy all attributes in the supplied Map into the model
*/
public void addAllAttributes(Map<String, ?> attributes) {
model.addAllAttributes(attributes);
return this.model.containsAttribute(name);
}
/**
* Add the given attribute if the model does not already contain such an attribute.
* @param name the name of the attribute to check and add
* @param value the value of the attribute
*/
public void mergeAttribute(String name, Object value) {
if (!containsAttribute(name)) {
model.addAttribute(name, value);
}
}
}
}

View File

@@ -41,7 +41,7 @@ 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.SimpleFlashStatus;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.bind.support.SimpleSessionStatus;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
@@ -66,12 +66,8 @@ public class ModelFactoryTests {
private SessionAttributesHandler sessionAttrsHandler;
private FlashAttributesHandler flashAttrsHandler;
private SessionAttributeStore sessionAttributeStore;
private ModelAndViewContainer mavContainer;
private NativeWebRequest webRequest;
@Before
@@ -82,42 +78,44 @@ public class ModelFactoryTests {
handleSessionAttrMethod = new InvocableHandlerMethod(handler, method);
sessionAttributeStore = new DefaultSessionAttributeStore();
sessionAttrsHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
flashAttrsHandler = new FlashAttributesHandler(handlerType);
mavContainer = new ModelAndViewContainer();
webRequest = new ServletWebRequest(new MockHttpServletRequest());
}
@Test
public void modelAttributeMethod() throws Exception {
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, handleMethod);
assertEquals(Boolean.TRUE, mavContainer.getAttribute("modelAttr"));
assertEquals(Boolean.TRUE, mavContainer.getModel().get("modelAttr"));
}
@Test
public void modelAttributeMethodWithSpecifiedName() throws Exception {
ModelFactory modelFactory = createModelFactory("modelAttrWithName");
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, handleMethod);
assertEquals(Boolean.TRUE, mavContainer.getAttribute("name"));
assertEquals(Boolean.TRUE, mavContainer.getModel().get("name"));
}
@Test
public void modelAttributeMethodWithNameByConvention() throws Exception {
ModelFactory modelFactory = createModelFactory("modelAttrConvention");
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, handleMethod);
assertEquals(Boolean.TRUE, mavContainer.getAttribute("boolean"));
assertEquals(Boolean.TRUE, mavContainer.getModel().get("boolean"));
}
@Test
public void modelAttributeMethodWithNullReturnValue() throws Exception {
ModelFactory modelFactory = createModelFactory("nullModelAttr");
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, handleMethod);
assertTrue(mavContainer.containsAttribute("name"));
assertNull(mavContainer.getAttribute("name"));
assertNull(mavContainer.getModel().get("name"));
}
@Test
@@ -128,30 +126,33 @@ public class ModelFactoryTests {
assertTrue(sessionAttrsHandler.isHandlerSessionAttribute("sessionAttr", null));
ModelFactory modelFactory = createModelFactory("modelAttr", Model.class);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, handleMethod);
assertEquals("sessionAttrValue", mavContainer.getAttribute("sessionAttr"));
assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr"));
}
@Test
public void requiredSessionAttribute() throws Exception {
ModelFactory modelFactory = new ModelFactory(null, null, sessionAttrsHandler, flashAttrsHandler);
ModelFactory modelFactory = new ModelFactory(null, null, sessionAttrsHandler);
try {
modelFactory.initModel(webRequest, mavContainer, handleSessionAttrMethod);
modelFactory.initModel(webRequest, new ModelAndViewContainer(), handleSessionAttrMethod);
fail("Expected HttpSessionRequiredException");
} catch (HttpSessionRequiredException e) { }
sessionAttributeStore.storeAttribute(webRequest, "sessionAttr", "sessionAttrValue");
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
modelFactory.initModel(webRequest, mavContainer, handleSessionAttrMethod);
assertEquals("sessionAttrValue", mavContainer.getAttribute("sessionAttr"));
assertEquals("sessionAttrValue", mavContainer.getModel().get("sessionAttr"));
}
@Test
public void updateModelBindingResultKeys() throws Exception {
String attrName = "attr1";
Object attrValue = new Object();
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAttribute(attrName, attrValue);
WebDataBinder dataBinder = new WebDataBinder(attrValue, attrName);
@@ -159,8 +160,8 @@ public class ModelFactoryTests {
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
replay(binderFactory);
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler, flashAttrsHandler);
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus(), new SimpleFlashStatus());
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler);
modelFactory.updateModel(webRequest, mavContainer, new SimpleSessionStatus());
assertEquals(attrValue, mavContainer.getModel().remove(attrName));
assertSame(dataBinder.getBindingResult(), mavContainer.getModel().remove(bindingResultKey(attrName)));
@@ -174,6 +175,7 @@ public class ModelFactoryTests {
String attrName = "sessionAttr";
String attrValue = "sessionAttrValue";
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAttribute(attrName, attrValue);
sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
@@ -185,15 +187,13 @@ public class ModelFactoryTests {
expect(binderFactory.createBinder(webRequest, attrValue, attrName)).andReturn(dataBinder);
replay(binderFactory);
SimpleSessionStatus status = new SimpleSessionStatus();
status.setComplete();
SessionStatus sessionStatus = new SimpleSessionStatus();
sessionStatus.setComplete();
// TODO: test with active FlashStatus
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler);
modelFactory.updateModel(webRequest, mavContainer, sessionStatus);
ModelFactory modelFactory = new ModelFactory(null, binderFactory, sessionAttrsHandler, flashAttrsHandler);
modelFactory.updateModel(webRequest, mavContainer, status, new SimpleFlashStatus());
assertEquals(attrValue, mavContainer.getAttribute(attrName));
assertEquals(attrValue, mavContainer.getModel().get(attrName));
assertNull(sessionAttributeStore.retrieveAttribute(webRequest, attrName));
verify(binderFactory);
@@ -214,7 +214,7 @@ public class ModelFactoryTests {
handlerMethod.setDataBinderFactory(null);
handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
return new ModelFactory(Arrays.asList(handlerMethod), null, sessionAttrsHandler, flashAttrsHandler);
return new ModelFactory(Arrays.asList(handlerMethod), null, sessionAttrsHandler);
}
@SessionAttributes("sessionAttr") @SuppressWarnings("unused")