Fix "embedded" mode to be flow session local.
The "embedded" mode flag, which is used to suppress redirects so that a flow can be embedded on a surrounding page, was previously stored as a flow execution attribute. The idea was that a flow can be launched in embedded mode simply by passing a flow input rather than having that decision fixed through a static flow definition. However flow execution attributes are not a suitable place to store the embedded mode flag since they are globally shared. Instead this information is now stored a flow session attribute, which means that each individual session (including subflows) can have be in embedded mode or not without affecting others
This commit is contained in:
@@ -7,6 +7,7 @@ Changes in version 2.3.1.RELEASE
|
||||
Upgrade JSF Mojarra version to 2.1.7
|
||||
Modify Jsf2FlowFacesContext.isValidationFailed() to check Web Flow's MessageContext for errors
|
||||
Recognize class-level bean validation messages in BindingResult.getGlobalErrors()
|
||||
Fix "embedded" mode to be flow session local, i.e. specific to a specific flow or subflow.
|
||||
|
||||
Changes in version 2.3.0.RELEASE (Feb 28, 2011)
|
||||
-----------------------------------------------
|
||||
|
||||
@@ -214,9 +214,6 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Starting in " + externalContext + " with input " + input);
|
||||
}
|
||||
if (hasEmbeddedModeAttribute(input)) {
|
||||
attributes.asMap().put("embeddedMode", Boolean.TRUE);
|
||||
}
|
||||
MessageContext messageContext = createMessageContext(null);
|
||||
RequestControlContext requestContext = createRequestContext(externalContext, messageContext);
|
||||
RequestContextHolder.setRequestContext(requestContext);
|
||||
@@ -353,13 +350,16 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
|
||||
void start(Flow flow, MutableAttributeMap input, RequestControlContext context) {
|
||||
listeners.fireSessionCreating(context, flow);
|
||||
FlowSession session = activateSession(flow);
|
||||
FlowSessionImpl session = activateSession(flow);
|
||||
if (session.isRoot()) {
|
||||
status = FlowExecutionStatus.ACTIVE;
|
||||
}
|
||||
if (input == null) {
|
||||
input = new LocalAttributeMap();
|
||||
}
|
||||
if (hasEmbeddedModeAttribute(input)) {
|
||||
session.setEmbeddedMode();
|
||||
}
|
||||
StateManageableMessageContext messageContext = (StateManageableMessageContext) context.getMessageContext();
|
||||
messageContext.setMessageSource(flow.getApplicationContext());
|
||||
listeners.fireSessionStarting(context, session, input);
|
||||
|
||||
@@ -78,7 +78,7 @@ public class FlowExecutionImplFactory implements FlowExecutionFactory {
|
||||
logger.debug("Creating new execution of '" + flowDefinition.getId() + "'");
|
||||
}
|
||||
FlowExecutionImpl execution = new FlowExecutionImpl((Flow) flowDefinition);
|
||||
execution.setAttributes(new LocalAttributeMap(executionAttributes.asMap()));
|
||||
execution.setAttributes(executionAttributes);
|
||||
execution.setListeners(executionListenerLoader.getListeners(execution.getDefinition()));
|
||||
execution.setKeyFactory(executionKeyFactory);
|
||||
return execution;
|
||||
|
||||
@@ -32,8 +32,8 @@ import org.springframework.webflow.execution.FlowSession;
|
||||
|
||||
/**
|
||||
* Implementation of the FlowSession interfaced used internally by the <code>FlowExecutionImpl</code>. This class is
|
||||
* closely coupled with <code>FlowExecutionImpl</code> and <code>RequestControlContextImpl</code>. The three
|
||||
* classes work together to form a complete flow execution implementation.
|
||||
* closely coupled with <code>FlowExecutionImpl</code> and <code>RequestControlContextImpl</code>. The three classes
|
||||
* work together to form a complete flow execution implementation.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
@@ -42,6 +42,8 @@ class FlowSessionImpl implements FlowSession, Externalizable {
|
||||
|
||||
private static final String VIEW_SCOPE_ATTRIBUTE = "viewScope";
|
||||
|
||||
private static final String EMBEDDED_MODE_ATTRIBUTE = "embeddedMode";
|
||||
|
||||
/**
|
||||
* The flow definition (a singleton).
|
||||
* <p>
|
||||
@@ -119,6 +121,10 @@ class FlowSessionImpl implements FlowSession, Externalizable {
|
||||
return (MutableAttributeMap) scope.get(VIEW_SCOPE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
public boolean isEmbeddedMode() {
|
||||
return (Boolean) scope.get(EMBEDDED_MODE_ATTRIBUTE, Boolean.FALSE);
|
||||
}
|
||||
|
||||
public FlowSession getParent() {
|
||||
return parent;
|
||||
}
|
||||
@@ -224,6 +230,14 @@ class FlowSessionImpl implements FlowSession, Externalizable {
|
||||
this.stateId = stateId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flow session attribute to indicate the current session should execute in embedded mode.
|
||||
* @see FlowSession#isEmbeddedMode()
|
||||
*/
|
||||
void setEmbeddedMode() {
|
||||
this.scope.put(EMBEDDED_MODE_ATTRIBUTE, true);
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
/**
|
||||
@@ -242,8 +256,8 @@ class FlowSessionImpl implements FlowSession, Externalizable {
|
||||
|
||||
public String toString() {
|
||||
if (flow != null) {
|
||||
return new ToStringCreator(this).append("flow", getFlowId()).append("state", getStateId()).append("scope",
|
||||
scope).toString();
|
||||
return new ToStringCreator(this).append("flow", getFlowId()).append("state", getStateId())
|
||||
.append("scope", scope).toString();
|
||||
} else {
|
||||
return "[Unhydrated session '" + flowId + "' in state '" + stateId + "']";
|
||||
}
|
||||
|
||||
@@ -255,8 +255,7 @@ class RequestControlContextImpl implements RequestControlContext {
|
||||
}
|
||||
|
||||
public boolean getEmbeddedMode() {
|
||||
Boolean embedded = flowExecution.getAttributes().getBoolean("embeddedMode");
|
||||
return (embedded != null) ? embedded.booleanValue() : false;
|
||||
return flowExecution.getActiveSession().isEmbeddedMode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -23,9 +23,9 @@ import org.springframework.webflow.definition.StateDefinition;
|
||||
* A single, local instantiation of a {@link FlowDefinition flow definition} launched within an overall flow execution.
|
||||
* <p>
|
||||
* This object maintains all instance state including session status within exactly one governing FlowExecution, as well
|
||||
* as the current flow state. This object also acts as the local "flow scope" data model. Data in
|
||||
* {@link #getScope() flow scope} lives for the life of this object and is cleaned up automatically when this object is
|
||||
* destroyed. Destruction happens when this session enters an end state.
|
||||
* as the current flow state. This object also acts as the local "flow scope" data model. Data in {@link #getScope()
|
||||
* flow scope} lives for the life of this object and is cleaned up automatically when this object is destroyed.
|
||||
* Destruction happens when this session enters an end state.
|
||||
* <p>
|
||||
* Note that a flow <i>session</i> is in no way linked to an HTTP session. It just uses the familiar "session" naming
|
||||
* convention to denote a stateful object.
|
||||
@@ -62,6 +62,12 @@ public interface FlowSession {
|
||||
*/
|
||||
public MutableAttributeMap getViewScope() throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Returns true if the flow session was started in embedded page mode. An embedded flow can make different
|
||||
* assumptions with regards to whether redirect after post is necessary.
|
||||
*/
|
||||
public boolean isEmbeddedMode();
|
||||
|
||||
/**
|
||||
* Returns the parent flow session in the current flow execution, or <code>null</code> if there is no parent flow
|
||||
* session.
|
||||
|
||||
@@ -36,7 +36,9 @@ import org.springframework.webflow.execution.FlowSession;
|
||||
*/
|
||||
public class MockFlowSession implements FlowSession {
|
||||
|
||||
private static final String FLOW_VIEW_MAP_ATTRIBUTE = "flowViewMap";
|
||||
private static final String VIEW_MAP_ATTRIBUTE = "flowViewMap";
|
||||
|
||||
private static final String EMBEDDED_MODE_ATTRIBUTE = "embeddedMode";
|
||||
|
||||
private Flow definition;
|
||||
|
||||
@@ -99,7 +101,11 @@ public class MockFlowSession implements FlowSession {
|
||||
throw new IllegalStateException("The current state '" + state.getId() + "' of this flow '"
|
||||
+ definition.getId() + "' is not a view state - view scope not accessible");
|
||||
}
|
||||
return (MutableAttributeMap) scope.get(FLOW_VIEW_MAP_ATTRIBUTE);
|
||||
return (MutableAttributeMap) scope.get(VIEW_MAP_ATTRIBUTE);
|
||||
}
|
||||
|
||||
public boolean isEmbeddedMode() {
|
||||
return (Boolean) scope.get(EMBEDDED_MODE_ATTRIBUTE, Boolean.FALSE);
|
||||
}
|
||||
|
||||
public FlowSession getParent() {
|
||||
@@ -163,13 +169,21 @@ public class MockFlowSession implements FlowSession {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flow session attribute to indicate the current session should execute in embedded mode.
|
||||
* @see FlowSession#isEmbeddedMode()
|
||||
*/
|
||||
void setEmbeddedMode() {
|
||||
this.scope.put(EMBEDDED_MODE_ATTRIBUTE, true);
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private void initViewScope() {
|
||||
scope.put(FLOW_VIEW_MAP_ATTRIBUTE, new LocalAttributeMap());
|
||||
scope.put(VIEW_MAP_ATTRIBUTE, new LocalAttributeMap());
|
||||
}
|
||||
|
||||
private void destroyViewScope() {
|
||||
scope.remove(FLOW_VIEW_MAP_ATTRIBUTE);
|
||||
scope.remove(VIEW_MAP_ATTRIBUTE);
|
||||
}
|
||||
}
|
||||
@@ -138,11 +138,7 @@ public class MockRequestControlContext extends MockRequestContext implements Req
|
||||
}
|
||||
|
||||
public boolean getEmbeddedMode() {
|
||||
Boolean embedded = getMockFlowExecutionContext().getAttributes().getBoolean("embeddedMode");
|
||||
if (embedded != null) {
|
||||
return embedded;
|
||||
}
|
||||
return false;
|
||||
return getMockFlowExecutionContext().getMockActiveSession().isEmbeddedMode();
|
||||
}
|
||||
|
||||
// implementation specific accessors for testing
|
||||
@@ -157,8 +153,7 @@ public class MockRequestControlContext extends MockRequestContext implements Req
|
||||
.put("redirectInSameState", Boolean.valueOf(redirectInSameState));
|
||||
}
|
||||
|
||||
public void setEmbeddedMode(boolean embedded) {
|
||||
getMockFlowExecutionContext().getAttributeMap().put("embeddedMode", Boolean.valueOf(embedded));
|
||||
public void setEmbeddedMode() {
|
||||
getMockFlowExecutionContext().getMockActiveSession().setEmbeddedMode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -451,7 +451,7 @@ public class ViewStateTests extends TestCase {
|
||||
state.getTransitionSet().add(t);
|
||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||
context.getMockExternalContext().setAjaxRequest(true);
|
||||
context.setEmbeddedMode(true);
|
||||
context.setEmbeddedMode();
|
||||
context.setAlwaysRedirectOnPause(true);
|
||||
context.setRedirectInSameState(true);
|
||||
state.enter(context);
|
||||
@@ -468,7 +468,7 @@ public class ViewStateTests extends TestCase {
|
||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||
state.enter(context);
|
||||
context.getMockExternalContext().setAjaxRequest(true);
|
||||
context.setEmbeddedMode(true);
|
||||
context.setEmbeddedMode();
|
||||
context.setAlwaysRedirectOnPause(true);
|
||||
context.setRedirectInSameState(true);
|
||||
assertFalse(context.getMockExternalContext().getFlowExecutionRedirectRequested());
|
||||
|
||||
@@ -85,7 +85,7 @@ public class FlowExecutionImplFactoryTests extends TestCase {
|
||||
factory.setExecutionAttributes(attributes);
|
||||
FlowExecution execution = factory.createFlowExecution(flowDefinition);
|
||||
assertEquals(attributes, execution.getAttributes());
|
||||
assertNotSame(attributes, execution.getAttributes());
|
||||
assertSame("Flow execution attributes are global", attributes.asMap(), execution.getAttributes().asMap());
|
||||
}
|
||||
|
||||
public void testCreateWithExecutionListener() {
|
||||
@@ -142,7 +142,8 @@ public class FlowExecutionImplFactoryTests extends TestCase {
|
||||
flowExecution.getFlowSessions().add(session1);
|
||||
flowExecution.getFlowSessions().add(session2);
|
||||
factory.restoreFlowExecution(flowExecution, flowDefinition, flowExecutionKey, conversationScope, locator);
|
||||
assertSame(executionAttributes, flowExecution.getAttributes());
|
||||
assertSame("Flow execution attributes are global", flowExecution.getAttributes().asMap(),
|
||||
executionAttributes.asMap());
|
||||
assertEquals(1, flowExecution.getListeners().length);
|
||||
assertSame(listener, flowExecution.getListeners()[0]);
|
||||
assertSame(flowExecutionKey, flowExecution.getKey());
|
||||
|
||||
Reference in New Issue
Block a user