flash scope and messages now stored in conversation scope
This commit is contained in:
@@ -249,6 +249,17 @@ public class LocalAttributeMap implements MutableAttributeMap, Serializable {
|
||||
return getMapInternal().remove(attributeName);
|
||||
}
|
||||
|
||||
public Object extract(String attributeName) {
|
||||
Map map = getMapInternal();
|
||||
if (map.containsKey(attributeName)) {
|
||||
Object value = map.get(attributeName);
|
||||
map.remove(attributeName);
|
||||
return value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public MutableAttributeMap clear() throws UnsupportedOperationException {
|
||||
getMapInternal().clear();
|
||||
return this;
|
||||
|
||||
@@ -34,7 +34,7 @@ public interface MutableAttributeMap extends AttributeMap {
|
||||
* <b>Note</b>: not all <code>MutableAttributeMap</code> implementations support this.
|
||||
* @param attributeName the attribute name
|
||||
* @param attributeValue the attribute value
|
||||
* @return the previous value of the attribute, or <tt>null</tt> of there was no previous value
|
||||
* @return the previous value of the attribute, or <code>null</code> of there was no previous value
|
||||
*/
|
||||
public Object put(String attributeName, Object attributeValue);
|
||||
|
||||
@@ -60,6 +60,13 @@ public interface MutableAttributeMap extends AttributeMap {
|
||||
*/
|
||||
public Object remove(String attributeName);
|
||||
|
||||
/**
|
||||
* Extract an attribute from this map, getting it and removing it in a single operation.
|
||||
* @param attributeName the attribute name
|
||||
* @return the value of the attribute, or <code>null</code> of there was no value
|
||||
*/
|
||||
public Object extract(String attributeName);
|
||||
|
||||
/**
|
||||
* Remove all attributes in this map.
|
||||
* @return this, to support call chaining
|
||||
|
||||
@@ -270,7 +270,6 @@ public class ViewState extends TransitionableState {
|
||||
} catch (IOException e) {
|
||||
throw new ViewRenderingException(getOwner().getId(), getId(), view, e);
|
||||
}
|
||||
context.getMessageContext().clearMessages();
|
||||
context.getFlashScope().clear();
|
||||
context.viewRendered(view);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FlowExecutionImpl.class);
|
||||
|
||||
private static final String FLASH_SCOPE_ATTRIBUTE = "flashScope";
|
||||
|
||||
/**
|
||||
* The execution's root flow; the top level flow that acts as the starting point for this flow execution.
|
||||
* <p>
|
||||
@@ -112,11 +114,6 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
*/
|
||||
private transient FlowExecutionKey key;
|
||||
|
||||
/**
|
||||
* The flash map ("flash scope").
|
||||
*/
|
||||
private MutableAttributeMap flashScope = new LocalAttributeMap();
|
||||
|
||||
/**
|
||||
* A data structure for attributes shared by all flow sessions.
|
||||
* <p>
|
||||
@@ -136,11 +133,6 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
*/
|
||||
private String flowId;
|
||||
|
||||
/**
|
||||
* Serializable snapshot of this flow execution's messages.
|
||||
*/
|
||||
private Serializable messagesMemento;
|
||||
|
||||
/**
|
||||
* The flow execution outcome event.
|
||||
*/
|
||||
@@ -164,6 +156,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
this.attributes = CollectionUtils.EMPTY_ATTRIBUTE_MAP;
|
||||
this.flowSessions = new LinkedList();
|
||||
this.conversationScope = new LocalAttributeMap();
|
||||
this.conversationScope.put(FLASH_SCOPE_ATTRIBUTE, new LocalAttributeMap());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +211,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
}
|
||||
|
||||
public MutableAttributeMap getFlashScope() {
|
||||
return flashScope;
|
||||
return (MutableAttributeMap) conversationScope.get(FLASH_SCOPE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
public MutableAttributeMap getConversationScope() {
|
||||
@@ -318,6 +311,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
|
||||
private MessageContext createMessageContext() {
|
||||
StateManageableMessageContext messageContext = messageContextFactory.createMessageContext();
|
||||
Serializable messagesMemento = (Serializable) getFlashScope().get("messagesMemento");
|
||||
if (messagesMemento != null) {
|
||||
messageContext.restoreMessages(messagesMemento);
|
||||
}
|
||||
@@ -325,7 +319,9 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
}
|
||||
|
||||
private void saveMessages(RequestContext context) {
|
||||
messagesMemento = ((StateManageableMessageContext) context.getMessageContext()).createMessagesMemento();
|
||||
Serializable messagesMemento = ((StateManageableMessageContext) context.getMessageContext())
|
||||
.createMessagesMemento();
|
||||
getFlashScope().put("messagesMemento", messagesMemento);
|
||||
}
|
||||
|
||||
// subclassing hooks
|
||||
@@ -512,16 +508,12 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
started = in.readBoolean();
|
||||
flowId = (String) in.readObject();
|
||||
flowSessions = (LinkedList) in.readObject();
|
||||
flashScope = (MutableAttributeMap) in.readObject();
|
||||
messagesMemento = (Serializable) in.readObject();
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeBoolean(started);
|
||||
out.writeObject(flow.getId());
|
||||
out.writeObject(flowSessions);
|
||||
out.writeObject(flashScope);
|
||||
out.writeObject(messagesMemento);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@@ -534,7 +526,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
} else {
|
||||
if (flow != null) {
|
||||
return new ToStringCreator(this).append("flow", flow.getId()).append("flowSessions", flowSessions)
|
||||
.append("flashScope", flashScope).toString();
|
||||
.toString();
|
||||
} else {
|
||||
return "[Unhydrated execution of '" + flowId + "']";
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.springframework.webflow.execution.FlowSession;
|
||||
*/
|
||||
class FlowSessionImpl implements FlowSession, Externalizable {
|
||||
|
||||
private static final String FLOW_VIEW_MAP_ATTRIBUTE = "flowViewMap";
|
||||
private static final String VIEW_SCOPE_ATTRIBUTE = "viewScope";
|
||||
|
||||
/**
|
||||
* The flow definition (a singleton).
|
||||
@@ -116,7 +116,7 @@ class FlowSessionImpl implements FlowSession, Externalizable {
|
||||
throw new IllegalStateException("The current state '" + state.getId() + "' of this flow '" + flow.getId()
|
||||
+ "' is not a view state - view scope not accessible");
|
||||
}
|
||||
return (MutableAttributeMap) scope.get(FLOW_VIEW_MAP_ATTRIBUTE);
|
||||
return (MutableAttributeMap) scope.get(VIEW_SCOPE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
public FlowSession getParent() {
|
||||
@@ -206,14 +206,14 @@ class FlowSessionImpl implements FlowSession, Externalizable {
|
||||
* Initialize the view scope data structure.
|
||||
*/
|
||||
private void initViewScope() {
|
||||
scope.put(FLOW_VIEW_MAP_ATTRIBUTE, new LocalAttributeMap());
|
||||
scope.put(VIEW_SCOPE_ATTRIBUTE, new LocalAttributeMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the view scope data structure.
|
||||
*/
|
||||
private void destroyViewScope() {
|
||||
scope.remove(FLOW_VIEW_MAP_ATTRIBUTE);
|
||||
scope.remove(VIEW_SCOPE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -116,10 +116,16 @@ public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinu
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Getting flow execution with key '" + key + "'");
|
||||
}
|
||||
FlowExecutionContinuation continuation = getContinuation(key);
|
||||
Conversation conversation = getConversation(key);
|
||||
FlowExecutionContinuation snapshot;
|
||||
try {
|
||||
FlowExecution execution = continuation.unmarshal();
|
||||
return restoreTransientState(execution, key);
|
||||
snapshot = getContinuationGroup(conversation).get(getContinuationId(key));
|
||||
} catch (ContinuationNotFoundException e) {
|
||||
throw new FlowExecutionRestorationFailureException(key, e);
|
||||
}
|
||||
try {
|
||||
FlowExecution execution = snapshot.unmarshal();
|
||||
return restoreTransientState(execution, key, conversation);
|
||||
} catch (ContinuationUnmarshalException e) {
|
||||
throw new FlowExecutionRestorationFailureException(key, e);
|
||||
}
|
||||
@@ -131,29 +137,33 @@ public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinu
|
||||
logger.debug("Putting flow execution '" + flowExecution + "' into repository");
|
||||
}
|
||||
FlowExecutionKey key = flowExecution.getKey();
|
||||
FlowExecutionContinuationGroup continuationGroup = getContinuationGroup(key);
|
||||
FlowExecutionContinuation continuation = snapshot(flowExecution);
|
||||
Conversation conversation = getConversation(key);
|
||||
FlowExecutionContinuationGroup continuationGroup = getContinuationGroup(conversation);
|
||||
FlowExecutionContinuation snapshot = snapshot(flowExecution);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding new continuation to group with id " + getContinuationId(key));
|
||||
logger.debug("Adding new snapshot to group with id " + getContinuationId(key));
|
||||
}
|
||||
continuationGroup.add(getContinuationId(key), continuation);
|
||||
putConversationScope(flowExecution);
|
||||
continuationGroup.add(getContinuationId(key), snapshot);
|
||||
putConversationScope(flowExecution, conversation);
|
||||
}
|
||||
|
||||
// implementing flow execution key factory
|
||||
|
||||
public void removeAllFlowExecutionSnapshots(FlowExecution execution) {
|
||||
getContinuationGroup(execution.getKey()).removeAllContinuations();
|
||||
Conversation conversation = getConversation(execution.getKey());
|
||||
getContinuationGroup(conversation).removeAllContinuations();
|
||||
}
|
||||
|
||||
public void removeFlowExecutionSnapshot(FlowExecution execution) {
|
||||
FlowExecutionKey key = execution.getKey();
|
||||
getContinuationGroup(key).removeContinuation(getContinuationId(key));
|
||||
Conversation conversation = getConversation(key);
|
||||
getContinuationGroup(conversation).removeContinuation(getContinuationId(key));
|
||||
}
|
||||
|
||||
public void updateFlowExecutionSnapshot(FlowExecution execution) {
|
||||
FlowExecutionKey key = execution.getKey();
|
||||
getContinuationGroup(key).updateContinuation(getContinuationId(key), snapshot(execution));
|
||||
Conversation conversation = getConversation(key);
|
||||
getContinuationGroup(conversation).updateContinuation(getContinuationId(key), snapshot(execution));
|
||||
}
|
||||
|
||||
// hooks for subclassing
|
||||
@@ -162,29 +172,12 @@ public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinu
|
||||
return new FlowExecutionContinuationGroup(maxContinuations);
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
/**
|
||||
* Returns the continuation in the group with the specified key.
|
||||
* @param key the flow execution key
|
||||
* @return the continuation.
|
||||
*/
|
||||
private FlowExecutionContinuation getContinuation(FlowExecutionKey key)
|
||||
throws FlowExecutionRestorationFailureException {
|
||||
try {
|
||||
return getContinuationGroup(key).get(getContinuationId(key));
|
||||
} catch (ContinuationNotFoundException e) {
|
||||
throw new FlowExecutionRestorationFailureException(key, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the continuation group associated with the governing conversation.
|
||||
* @param key the flow execution key
|
||||
* @param conversation the conversation where the continuation group is stored
|
||||
* @return the continuation group
|
||||
*/
|
||||
private FlowExecutionContinuationGroup getContinuationGroup(FlowExecutionKey key) {
|
||||
Conversation conversation = getConversation(key);
|
||||
protected FlowExecutionContinuationGroup getContinuationGroup(Conversation conversation) {
|
||||
FlowExecutionContinuationGroup group = (FlowExecutionContinuationGroup) conversation
|
||||
.getAttribute(CONTINUATION_GROUP_ATTRIBUTE);
|
||||
if (group == null) {
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.webflow.execution.repository.NoSuchFlowExecutionExcep
|
||||
* The configured {@link FlowExecutionStateRestorer} should be compatible with the chosen {@link FlowExecution}
|
||||
* implementation and is configuration as done by a {@link FlowExecutionFactory} (listeners, execution attributes, ...).
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public abstract class AbstractFlowExecutionRepository implements FlowExecutionRepository, FlowExecutionKeyFactory {
|
||||
@@ -146,7 +147,7 @@ public abstract class AbstractFlowExecutionRepository implements FlowExecutionRe
|
||||
*/
|
||||
protected ConversationParameters createConversationParameters(FlowExecution flowExecution) {
|
||||
FlowDefinition flow = flowExecution.getDefinition();
|
||||
return new ConversationParameters(flow.getId().toString(), flow.getCaption(), flow.getDescription());
|
||||
return new ConversationParameters(flow.getId(), flow.getCaption(), flow.getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,17 +200,21 @@ public abstract class AbstractFlowExecutionRepository implements FlowExecutionRe
|
||||
* Returns the transient state of the flow execution after potential deserialization.
|
||||
* @param execution the flow execution
|
||||
* @param key the flow execution key
|
||||
* @param conversation the governing conversation where the execution is stored
|
||||
*/
|
||||
protected FlowExecution restoreTransientState(FlowExecution execution, FlowExecutionKey key) {
|
||||
return executionStateRestorer.restoreState(execution, key, getConversationScope(key), this);
|
||||
protected FlowExecution restoreTransientState(FlowExecution execution, FlowExecutionKey key,
|
||||
Conversation conversation) {
|
||||
MutableAttributeMap conversationScope = (MutableAttributeMap) conversation.getAttribute("scope");
|
||||
return executionStateRestorer.restoreState(execution, key, conversationScope, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the value of conversation scope in the conversation object.
|
||||
* @param flowExecution the flow execution holding a reference to conversation scope
|
||||
* @param conversation the conversation where conversation scope is stored
|
||||
*/
|
||||
protected void putConversationScope(FlowExecution flowExecution) {
|
||||
getConversation(flowExecution.getKey()).putAttribute("scope", flowExecution.getConversationScope());
|
||||
protected void putConversationScope(FlowExecution flowExecution, Conversation conversation) {
|
||||
conversation.putAttribute("scope", flowExecution.getConversationScope());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,8 +260,4 @@ public abstract class AbstractFlowExecutionRepository implements FlowExecutionRe
|
||||
return conversation;
|
||||
}
|
||||
|
||||
private MutableAttributeMap getConversationScope(FlowExecutionKey key) {
|
||||
return (MutableAttributeMap) getConversation(key).getAttribute("scope");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user