flash scope and messages now stored in conversation scope

This commit is contained in:
Keith Donald
2008-04-22 18:52:56 +00:00
parent bd995ff9aa
commit 3468308226
7 changed files with 65 additions and 62 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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 + "']";
}

View File

@@ -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() {

View File

@@ -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) {

View File

@@ -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");
}
}