diff --git a/spring-webflow/src/main/java/org/springframework/webflow/core/collection/LocalAttributeMap.java b/spring-webflow/src/main/java/org/springframework/webflow/core/collection/LocalAttributeMap.java
index 72ed3a09..1259d681 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/core/collection/LocalAttributeMap.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/core/collection/LocalAttributeMap.java
@@ -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;
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/core/collection/MutableAttributeMap.java b/spring-webflow/src/main/java/org/springframework/webflow/core/collection/MutableAttributeMap.java
index a2658020..f0bcf52e 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/core/collection/MutableAttributeMap.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/core/collection/MutableAttributeMap.java
@@ -34,7 +34,7 @@ public interface MutableAttributeMap extends AttributeMap {
* Note: not all MutableAttributeMap implementations support this.
* @param attributeName the attribute name
* @param attributeValue the attribute value
- * @return the previous value of the attribute, or null of there was no previous value
+ * @return the previous value of the attribute, or null 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 null of there was no value
+ */
+ public Object extract(String attributeName);
+
/**
* Remove all attributes in this map.
* @return this, to support call chaining
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
index 18444e3e..d6d2af3e 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
@@ -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);
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
index efd98f8d..2da84da3 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
@@ -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.
*
@@ -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. *
@@ -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 + "']"; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java index a96a59e1..8ffc38d5 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java @@ -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() { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java index c96ba142..6af51ee2 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java @@ -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) { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/AbstractFlowExecutionRepository.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/AbstractFlowExecutionRepository.java index 58ac0749..edb46c41 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/AbstractFlowExecutionRepository.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/AbstractFlowExecutionRepository.java @@ -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"); - } - } \ No newline at end of file