view scope

This commit is contained in:
Keith Donald
2008-03-29 06:10:01 +00:00
parent 794b7345a9
commit bc2ed3e868
20 changed files with 166 additions and 31 deletions

View File

@@ -97,4 +97,8 @@ public class SerializableListDataModel extends DataModel implements Serializable
setRowIndex(newRowIndex);
}
public String toString() {
return data.toString();
}
}

View File

@@ -74,7 +74,7 @@ public class FlowViewStateManager extends StateManager {
}
RequestContext requestContext = RequestContextHolder.getRequestContext();
SerializedView view = (SerializedView) requestContext.getFlowScope().get(SERIALIZED_VIEW_STATE);
SerializedView view = (SerializedView) requestContext.getViewScope().get(SERIALIZED_VIEW_STATE);
viewRoot.processRestoreState(context, view.componentState);
logger.debug("UIViewRoot component state restored");
}
@@ -85,7 +85,7 @@ public class FlowViewStateManager extends StateManager {
}
RequestContext requestContext = RequestContextHolder.getRequestContext();
SerializedView view = (SerializedView) requestContext.getFlowScope().get(SERIALIZED_VIEW_STATE);
SerializedView view = (SerializedView) requestContext.getViewScope().get(SERIALIZED_VIEW_STATE);
if (view == null || !view.viewId.equals(viewId)) {
logger.debug("No matching view in flow scope;");
return null;
@@ -145,7 +145,7 @@ public class FlowViewStateManager extends StateManager {
}
SerializedView view = new SerializedView(context.getViewRoot().getViewId(), getTreeStructureToSave(context),
getComponentStateToSave(context));
requestContext.getFlowScope().put(SERIALIZED_VIEW_STATE, view);
requestContext.getViewScope().put(SERIALIZED_VIEW_STATE, view);
return view;
}

View File

@@ -228,6 +228,10 @@ public class JsfViewFactoryTests extends TestCase {
private class NormalViewState implements StateDefinition {
public boolean isViewState() {
return true;
}
public String getId() {
throw new UnsupportedOperationException("Auto-generated method stub");
}

View File

@@ -265,6 +265,10 @@ public class JsfViewTests extends TestCase {
MutableAttributeMap attrs = new LocalAttributeMap();
public boolean isViewState() {
return true;
}
public ModalViewState() {
attrs.asMap().put("modal", Boolean.TRUE);
}

View File

@@ -7,7 +7,7 @@
<persistence-context/>
<input name="hotelId" value="flowScope.hotelId"/>
<input name="hotelId" value="flowScope.hotelId" required="true"/>
<on-start>
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" />

View File

@@ -7,7 +7,7 @@
<view-state id="enterSearchCriteria">
<on-render>
<evaluate expression="bookingService.findBookings(currentUser.name)" result="flowScope.bookings" result-type="dataModel" />
<evaluate expression="bookingService.findBookings(currentUser.name)" result="viewScope.bookings" result-type="dataModel" />
</on-render>
<transition on="search" to="reviewHotels" />
<transition on="cancelBooking">
@@ -18,7 +18,7 @@
<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels" result-type="dataModel" />
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" result-type="dataModel" />
</on-render>
<transition on="previous">
<evaluate expression="searchCriteria.previousPage()" />
@@ -28,20 +28,19 @@
<evaluate expression="searchCriteria.nextPage()" />
<render fragments="hotels:searchResultsFragment" />
</transition>
<transition on="select" to="reviewHotel" />
<transition on="select" to="reviewHotel">
<set name="flowScope.hotel" value="hotels.selectedRow" />
</transition>
<transition on="changeSearch" to="changeSearchCriteria" />
</view-state>
<view-state id="reviewHotel">
<on-render>
<set name="requestScope.hotel" value="hotels.selectedRow" />
</on-render>
<transition on="book" to="bookHotel" />
<transition on="cancel" to="enterSearchCriteria" />
</view-state>
<subflow-state id="bookHotel" subflow="booking">
<input name="hotelId" value="hotels.selectedRow.id" />
<input name="hotelId" value="hotel.id" />
<transition on="bookingConfirmed" to="finish" />
<transition on="bookingCancelled" to="enterSearchCriteria" />
</subflow-state>
@@ -56,5 +55,5 @@
</view-state>
<end-state id="finish" />
</flow>

View File

@@ -17,22 +17,22 @@
<ui:fragment id="searchResultsFragment">
<div id="searchResults">
<h:outputText id="noHotelsText" value="No Hotels Found" rendered="#{hotels.rowCount == 0}"/>
<h:dataTable id="hotels" styleClass="summary" value="#{hotels}" var="hotel" rendered="#{hotels.rowCount > 0}">
<h:dataTable id="hotels" styleClass="summary" value="#{hotels}" var="h" rendered="#{hotels.rowCount > 0}">
<h:column>
<f:facet name="header">Name</f:facet>
#{hotel.name}
#{h.name}
</h:column>
<h:column>
<f:facet name="header">Address</f:facet>
#{hotel.address}
#{h.address}
</h:column>
<h:column>
<f:facet name="header">City, State</f:facet>
#{hotel.city}, #{hotel.state}, #{hotel.country}
#{h.city}, #{h.state}, #{h.country}
</h:column>
<h:column>
<f:facet name="header">Zip</f:facet>
#{hotel.zip}
#{h.zip}
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>

View File

@@ -37,4 +37,10 @@ public interface StateDefinition extends Annotated {
* @return the state identifier
*/
public String getId();
/**
* Returns true if this state is a view state.
* @return true if a view state, false otherwise
*/
public boolean isViewState();
}

View File

@@ -117,4 +117,14 @@ public interface RequestControlContext extends RequestContext {
*/
public boolean getAlwaysRedirectOnPause();
/**
* Initialize view scope. Called by view states when they enter.
*/
public void initViewScope();
/**
* Destroy view-scope. Called by view-states when they exit.
*/
public void destroyViewScope();
}

View File

@@ -98,6 +98,10 @@ public abstract class State extends AnnotatedObject implements StateDefinition {
return id;
}
public boolean isViewState() {
return false;
}
// implementation specific
/**

View File

@@ -77,6 +77,12 @@ public class ViewState extends TransitionableState {
this.viewFactory = viewFactory;
}
// implementing StateDefinition
public boolean isViewState() {
return true;
}
/**
* Adds a view variable.
* @param variable the variable
@@ -156,6 +162,7 @@ public class ViewState extends TransitionableState {
}
protected void doPreEntryActions(RequestControlContext context) throws FlowExecutionException {
context.initViewScope();
createVariables(context);
}
@@ -196,6 +203,7 @@ public class ViewState extends TransitionableState {
public void exit(RequestControlContext context) {
destroyVariables(context);
context.destroyViewScope();
super.exit(context);
}

View File

@@ -58,7 +58,7 @@ public class ViewVariable extends AnnotatedObject {
*/
public void create(RequestContext context) {
Object value = valueFactory.createInitialValue(context);
context.getFlowScope().put(name, value);
context.getViewScope().put(name, value);
}
/**
@@ -67,7 +67,7 @@ public class ViewVariable extends AnnotatedObject {
* @param context the executing flow
*/
public void restore(RequestContext context) {
Object value = context.getFlowScope().get(name);
Object value = context.getViewScope().get(name);
valueFactory.restoreReferences(value, context);
}
@@ -76,6 +76,6 @@ public class ViewVariable extends AnnotatedObject {
* @param context the executing flow
*/
public Object destroy(RequestContext context) {
return context.getFlowScope().remove(name);
return context.getViewScope().remove(name);
}
}

View File

@@ -48,6 +48,8 @@ import org.springframework.webflow.execution.FlowSession;
*/
class RequestControlContextImpl implements RequestControlContext {
private static final String FLOW_VIEW_MAP_ATTRIBUTE = "flowViewMap";
/**
* The owning flow execution carrying out this request.
*/
@@ -116,6 +118,21 @@ class RequestControlContextImpl implements RequestControlContext {
return flowExecution.getFlashScope();
}
public boolean inViewState() {
return flowExecution.isActive() && getCurrentState() != null && getCurrentState().isViewState();
}
public MutableAttributeMap getViewScope() throws IllegalStateException {
if (!flowExecution.isActive()) {
throw new IllegalStateException("This flow is not active");
}
if (!getCurrentState().isViewState()) {
throw new IllegalStateException("The current state '" + getCurrentState().getId() + "' of this flow '"
+ getActiveFlow().getId() + "' is not a view state - view scope not accessible");
}
return (MutableAttributeMap) getFlowScope().get(FLOW_VIEW_MAP_ATTRIBUTE);
}
public MutableAttributeMap getFlowScope() {
return flowExecution.getActiveSession().getScope();
}
@@ -196,6 +213,14 @@ class RequestControlContextImpl implements RequestControlContext {
flowExecution.start(flow, input, this);
}
public void initViewScope() {
getFlowScope().put(FLOW_VIEW_MAP_ATTRIBUTE, new LocalAttributeMap());
}
public void destroyViewScope() {
getFlowScope().remove(FLOW_VIEW_MAP_ATTRIBUTE);
}
public boolean handleEvent(Event event) throws FlowExecutionException {
this.lastEvent = event;
return flowExecution.handleEvent(event, this);

View File

@@ -89,6 +89,22 @@ public interface RequestContext {
*/
public MutableAttributeMap getFlashScope();
/**
* Returns true if the flow is currently active and in a view state. When in a view state {@link #getViewScope()},
* can be safely called.
* @see #getViewScope()
* @return true if in a view state, false if not
*/
public boolean inViewState();
/**
* Returns a mutable map for accessing and/or setting attributes in view scope. <b>View scoped attributes exist for
* the life of the current view state.</b>
* @return the view scope
* @throws IllegalStateException this flow is not in a view-state
*/
public MutableAttributeMap getViewScope() throws IllegalStateException;
/**
* Returns a mutable map for accessing and/or setting attributes in flow scope. <b>Flow scoped attributes exist for
* the life of the active flow session.</b>
@@ -177,4 +193,5 @@ public interface RequestContext {
* @return the flow execution URL
*/
public String getFlowExecutionUrl() throws IllegalStateException;
}

View File

@@ -151,6 +151,7 @@ public class ImplicitFlowVariableELResolver extends ELResolver {
vars.put("requestParameters", requestContextResolver);
vars.put("requestScope", requestContextResolver);
vars.put("flashScope", requestContextResolver);
vars.put("viewScope", requestContextResolver);
vars.put("flowScope", requestContextResolver);
vars.put("conversationScope", requestContextResolver);
vars.put("messageContext", requestContextResolver);

View File

@@ -67,6 +67,9 @@ public class ScopeSearchingELResolver extends ELResolver {
} else if (requestContext.getFlashScope().contains(attributeName)) {
elContext.setPropertyResolved(true);
return requestContext.getFlashScope().get(attributeName).getClass();
} else if (requestContext.inViewState() && requestContext.getViewScope().contains(attributeName)) {
elContext.setPropertyResolved(true);
return requestContext.getViewScope().get(attributeName).getClass();
} else if (requestContext.getFlowScope().contains(attributeName)) {
elContext.setPropertyResolved(true);
return requestContext.getFlowScope().get(attributeName).getClass();
@@ -96,6 +99,12 @@ public class ScopeSearchingELResolver extends ELResolver {
}
elContext.setPropertyResolved(true);
return requestContext.getFlashScope().get(attributeName);
} else if (requestContext.inViewState() && requestContext.getViewScope().contains(attributeName)) {
if (logger.isDebugEnabled()) {
logger.debug("Successfully resolved view scoped variable '" + property + "'");
}
elContext.setPropertyResolved(true);
return requestContext.getViewScope().get(attributeName);
} else if (requestContext.getFlowScope().contains(attributeName)) {
if (logger.isDebugEnabled()) {
logger.debug("Successfully resolved flow scoped variable '" + property + "'");
@@ -125,6 +134,9 @@ public class ScopeSearchingELResolver extends ELResolver {
} else if (requestContext.getFlashScope().contains(attributeName)) {
elContext.setPropertyResolved(true);
return false;
} else if (requestContext.inViewState() && requestContext.getViewScope().contains(attributeName)) {
elContext.setPropertyResolved(true);
return false;
} else if (requestContext.getFlowScope().contains(attributeName)) {
elContext.setPropertyResolved(true);
return false;
@@ -154,6 +166,12 @@ public class ScopeSearchingELResolver extends ELResolver {
}
elContext.setPropertyResolved(true);
requestContext.getFlashScope().put(attributeName, value);
} else if (requestContext.inViewState() && requestContext.getViewScope().contains(attributeName)) {
if (logger.isDebugEnabled()) {
logger.debug("Successfully resolved view scoped variable '" + property + "'");
}
elContext.setPropertyResolved(true);
requestContext.getViewScope().put(attributeName, value);
} else if (requestContext.getFlowScope().contains(attributeName)) {
if (logger.isDebugEnabled()) {
logger.debug("Successfully resolved flow scoped variable '" + property + "'");

View File

@@ -45,6 +45,8 @@ import org.springframework.webflow.execution.RequestContext;
*/
public class MockRequestContext implements RequestContext {
protected static final String FLOW_VIEW_MAP_ATTRIBUTE = "flowViewMap";
private FlowExecutionContext flowExecutionContext;
private ExternalContext externalContext;
@@ -126,6 +128,21 @@ public class MockRequestContext implements RequestContext {
return getMockFlowExecutionContext().getFlashScope();
}
public boolean inViewState() {
return getFlowExecutionContext().isActive() && getCurrentState() != null && getCurrentState().isViewState();
}
public MutableAttributeMap getViewScope() throws IllegalStateException {
if (!getFlowExecutionContext().isActive()) {
throw new IllegalStateException("This flow is not active");
}
if (!getCurrentState().isViewState()) {
throw new IllegalStateException("The current state '" + getCurrentState().getId() + "' of this flow '"
+ getActiveFlow().getId() + "' is not a view state - view scope not accessible");
}
return (MutableAttributeMap) getFlowScope().get(FLOW_VIEW_MAP_ATTRIBUTE);
}
public MutableAttributeMap getFlowScope() {
return getFlowExecutionContext().getActiveSession().getScope();
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.webflow.test;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.RequestControlContext;
@@ -73,6 +74,14 @@ public class MockRequestControlContext extends MockRequestContext implements Req
flow.start(this, input);
}
public void initViewScope() {
getFlowScope().put(FLOW_VIEW_MAP_ATTRIBUTE, new LocalAttributeMap());
}
public void destroyViewScope() {
getFlowScope().remove(FLOW_VIEW_MAP_ATTRIBUTE);
}
public boolean handleEvent(Event event) {
setLastEvent(event);
return ((Flow) getActiveFlow()).handleEvent(this);

View File

@@ -55,7 +55,7 @@ public class ViewStateTests extends TestCase {
}));
MockRequestControlContext context = new MockRequestControlContext(flow);
state.enter(context);
assertEquals("bar", context.getFlowScope().getString("foo"));
assertEquals("bar", context.getViewScope().getString("foo"));
assertTrue("Render not called", context.getFlowScope().contains("renderCalled"));
assertFalse(context.getFlowExecutionRedirectSent());
}
@@ -151,7 +151,7 @@ public class ViewStateTests extends TestCase {
state.resume(context);
assertTrue("Render not called", context.getFlowScope().contains("renderCalled"));
assertFalse(context.getFlowExecutionRedirectSent());
assertEquals("Restored", ((TestBean) context.getFlowScope().get("foo")).datum1);
assertEquals("Restored", ((TestBean) context.getViewScope().get("foo")).datum1);
}
public void testResumeViewStateForEvent() {
@@ -202,7 +202,7 @@ public class ViewStateTests extends TestCase {
new ViewState(flow, "next", viewFactory);
MockRequestControlContext context = new MockRequestControlContext(flow);
state.enter(context);
assertTrue(context.getFlowScope().contains("foo"));
assertTrue(context.getViewScope().contains("foo"));
context = new MockRequestControlContext(context.getFlowExecutionContext());
context.putRequestParameter("_eventId", "submit");
state.resume(context);

View File

@@ -3,7 +3,7 @@ package org.springframework.webflow.engine;
import junit.framework.TestCase;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.test.MockRequestContext;
import org.springframework.webflow.test.MockRequestControlContext;
public class ViewVariableTests extends TestCase {
@@ -18,9 +18,12 @@ public class ViewVariableTests extends TestCase {
public void restoreReferences(Object value, RequestContext context) {
}
});
MockRequestContext context = new MockRequestContext();
Flow flow = new Flow("flow");
ViewState view = new ViewState(flow, "view", new StubViewFactory());
MockRequestControlContext context = new MockRequestControlContext(flow);
view.enter(context);
var.create(context);
assertEquals("bar", context.getFlowScope().get("foo"));
assertEquals("bar", context.getViewScope().get("foo"));
}
public void testDestroyVariable() {
@@ -32,11 +35,14 @@ public class ViewVariableTests extends TestCase {
public void restoreReferences(Object value, RequestContext context) {
}
});
MockRequestContext context = new MockRequestContext();
Flow flow = new Flow("flow");
ViewState view = new ViewState(flow, "view", new StubViewFactory());
MockRequestControlContext context = new MockRequestControlContext(flow);
view.enter(context);
var.create(context);
assertEquals("bar", context.getFlowScope().get("foo"));
assertEquals("bar", context.getViewScope().get("foo"));
var.destroy(context);
assertFalse(context.getFlowScope().contains("foo"));
assertFalse(context.getViewScope().contains("foo"));
}
public void testRestoreVariable() {
@@ -50,10 +56,13 @@ public class ViewVariableTests extends TestCase {
assertEquals("bar", value);
}
});
MockRequestContext context = new MockRequestContext();
Flow flow = new Flow("flow");
ViewState view = new ViewState(flow, "view", new StubViewFactory());
MockRequestControlContext context = new MockRequestControlContext(flow);
view.enter(context);
var.create(context);
var.restore(context);
assertEquals("bar", context.getFlowScope().get("foo"));
assertEquals("bar", context.getViewScope().get("foo"));
assertTrue(restoreCalled);
}