From 9b64bac5a8b546a10916094846af387ab8214a4f Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Wed, 24 Jun 2009 22:12:49 +0000 Subject: [PATCH] SWF-1114 - Consider making ViewActionStateHolder and ViewRootHolder put in flash scope Serializable --- .../support/ActionExecutingViewFactory.java | 4 +- .../webflow/execution/View.java | 6 +- .../webflow/mvc/view/AbstractMvcView.java | 3 +- .../mvc/view/ViewActionStateHolder.java | 6 +- .../webflow/test/MockViewFactoryCreator.java | 3 +- .../webflow/engine/StubViewFactory.java | 7 +- .../webflow/mvc/view/MvcViewTests.java | 79 +++++++++++++++++++ 7 files changed, 99 insertions(+), 9 deletions(-) diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java index 90868705..7274c3a3 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java @@ -15,6 +15,8 @@ */ package org.springframework.webflow.engine.support; +import java.io.Serializable; + import org.springframework.web.util.WebUtils; import org.springframework.webflow.execution.Action; import org.springframework.webflow.execution.ActionExecutor; @@ -79,7 +81,7 @@ public class ActionExecutingViewFactory implements ViewFactory { userEventProcessed = true; } - public Object getUserEventState() { + public Serializable getUserEventState() { return null; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java index 8176358b..55c6159a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java @@ -16,6 +16,7 @@ package org.springframework.webflow.execution; import java.io.IOException; +import java.io.Serializable; import org.springframework.webflow.engine.ViewState; @@ -24,6 +25,7 @@ import org.springframework.webflow.engine.ViewState; * and handle the resulting event once the client responds. * * @author Keith Donald + * @author Jeremy Grelle * @see ViewFactory */ public interface View { @@ -74,10 +76,10 @@ public interface View { /** * A memento holding the results of processing a user event. Used to allow transient view state such as binding and * validation errors to survive a flow execution redirect. - * @return the user event state object, or null if no event state needs managing + * @return the serializable user event state object, or null if no event state needs managing * @see #processUserEvent() */ - public Object getUserEventState(); + public Serializable getUserEventState(); /** * Saves any state associated with this view out to view scope. Called when exiting a {@link ViewState} to allow for diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java index 6278718d..5da73882 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java @@ -16,6 +16,7 @@ package org.springframework.webflow.mvc.view; import java.io.IOException; +import java.io.Serializable; import java.lang.reflect.Array; import java.util.HashMap; import java.util.Iterator; @@ -224,7 +225,7 @@ public abstract class AbstractMvcView implements View { userEventProcessed = true; } - public Object getUserEventState() { + public Serializable getUserEventState() { return new ViewActionStateHolder(eventId, userEventProcessed, mappingResults); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewActionStateHolder.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewActionStateHolder.java index cf8475a5..5d14e0c9 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewActionStateHolder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewActionStateHolder.java @@ -15,6 +15,8 @@ */ package org.springframework.webflow.mvc.view; +import java.io.Serializable; + import org.springframework.binding.mapping.MappingResults; import org.springframework.core.style.ToStringCreator; @@ -23,7 +25,7 @@ import org.springframework.core.style.ToStringCreator; * * @author Scott Andrews */ -public class ViewActionStateHolder { +public class ViewActionStateHolder implements Serializable { public static final String KEY = "webflowViewActionStateHolder"; @@ -31,7 +33,7 @@ public class ViewActionStateHolder { private boolean userEventProcessed; - private MappingResults mappingResults; + private transient MappingResults mappingResults; public ViewActionStateHolder(String eventId, boolean userEventProcessed, MappingResults mappingResults) { this.eventId = eventId; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java index d7245974..ee81c6d6 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java @@ -16,6 +16,7 @@ package org.springframework.webflow.test; import java.io.IOException; +import java.io.Serializable; import org.springframework.binding.convert.ConversionService; import org.springframework.binding.expression.Expression; @@ -107,7 +108,7 @@ class MockViewFactoryCreator implements ViewFactoryCreator { // TODO - implement me as appropriate for a test environment } - public Object getUserEventState() { + public Serializable getUserEventState() { return null; } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/StubViewFactory.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/StubViewFactory.java index 4c6d7fa4..4f2f63be 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/StubViewFactory.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/StubViewFactory.java @@ -15,6 +15,8 @@ */ package org.springframework.webflow.engine; +import java.io.Serializable; + import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.View; @@ -22,7 +24,8 @@ import org.springframework.webflow.execution.ViewFactory; public class StubViewFactory implements ViewFactory { - public static Object USER_EVENT_STATE = new Object(); + public static Serializable USER_EVENT_STATE = new Serializable() { + }; public View getView(RequestContext context) { return new NullView(context); @@ -47,7 +50,7 @@ public class StubViewFactory implements ViewFactory { } - public Object getUserEventState() { + public Serializable getUserEventState() { return USER_EVENT_STATE; } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java index ad2fa931..c601c876 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java @@ -1,6 +1,12 @@ package org.springframework.webflow.mvc.view; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.security.Principal; +import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -289,6 +295,79 @@ public class MvcViewTests extends TestCase { assertEquals("bogus 2", bm.getFieldValue("dateProperty")); } + public void testResumeEventBindingErrorsRedirectToReplicatedSessionAfterPost() throws Exception { + MockRequestControlContext context = new MockRequestControlContext(); + context.putRequestParameter("_eventId", "submit"); + context.putRequestParameter("integerProperty", "bogus 1"); + context.putRequestParameter("dateProperty", "bogus 2"); + BindBean bindBean = new BindBean(); + StaticExpression modelObject = new StaticExpression(bindBean); + modelObject.setExpressionString("bindBean"); + context.getCurrentState().getAttributes().put("model", modelObject); + context.getFlowScope().put("bindBean", bindBean); + context.getMockExternalContext().setNativeContext(new MockServletContext()); + context.getMockExternalContext().setNativeRequest(new MockHttpServletRequest()); + context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); + context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); + org.springframework.web.servlet.View mvcView = new MockView(); + AbstractMvcView view = new MockMvcView(mvcView, context); + view.setExpressionParser(DefaultExpressionParserFactory.getExpressionParser()); + view.setMessageCodesResolver(new WebFlowMessageCodesResolver()); + context.setAlwaysRedirectOnPause(true); + assertTrue(view.userEventQueued()); + view.processUserEvent(); + assertFalse(view.userEventQueued()); + assertFalse(view.hasFlowEvent()); + Object viewActionState = view.getUserEventState(); + assertNotNull(viewActionState); + + viewActionState = saveAndRestoreViewActionState(viewActionState); + + MockRequestControlContext context2 = new MockRequestControlContext(); + context2.getFlashScope().put(org.springframework.webflow.execution.View.USER_EVENT_STATE_ATTRIBUTE, + viewActionState); + BindBean bindBean2 = new BindBean(); + StaticExpression modelObject2 = new StaticExpression(bindBean2); + modelObject2.setExpressionString("bindBean"); + context2.getCurrentState().getAttributes().put("model", modelObject); + context2.getFlowScope().put("bindBean", bindBean); + context2.getMockExternalContext().setNativeContext(new MockServletContext()); + context2.getMockExternalContext().setNativeRequest(new MockHttpServletRequest()); + context2.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); + context2.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); + AbstractMvcView view2 = new MockMvcView(mvcView, context2); + view2.setExpressionParser(DefaultExpressionParserFactory.getExpressionParser()); + view2.setMessageCodesResolver(new WebFlowMessageCodesResolver()); + view2.restoreState((ViewActionStateHolder) viewActionState); + assertFalse(view2.userEventQueued()); + view2.render(); + assertEquals(context2.getFlowScope().get("bindBean"), model.get("bindBean")); + BindingModel bm = (BindingModel) model.get(BindingResult.MODEL_KEY_PREFIX + "bindBean"); + assertNotNull(bm); + assertEquals(new Integer(3), bm.getFieldValue("integerProperty")); + assertEquals(new SimpleDateFormat("MM-dd-yyyy").parse("01-01-2008"), bm.getFieldValue("dateProperty")); + } + + private Object saveAndRestoreViewActionState(Object viewActionState) throws Exception { + File tempFile = new File("serializable.tmp"); + + FileOutputStream fos = new FileOutputStream(tempFile); + ObjectOutputStream objOut = new ObjectOutputStream(fos); + objOut.writeObject(viewActionState); + objOut.close(); + + FileInputStream fis = new FileInputStream(tempFile); + ObjectInputStream objIn = new ObjectInputStream(fis); + Object restoredState = objIn.readObject(); + objIn.close(); + + tempFile.delete(); + + assertNotSame(viewActionState, restoredState); + + return restoredState; + } + public void testResumeEventModelBindingAllowedFields() throws Exception { MockRequestContext context = new MockRequestContext(); context.putRequestParameter("_eventId", "submit");