conversation/continuation terms renamed to execution/snapshot terms for simplicity / improved lexicon

This commit is contained in:
Keith Donald
2008-04-27 15:31:37 +00:00
parent aa2070e1f0
commit 25ba857774
19 changed files with 450 additions and 454 deletions

View File

@@ -175,7 +175,7 @@ class FlowExecutorFactoryBean implements FactoryBean, ApplicationContextAware, I
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
if (maxFlowExecutionSnapshots != null) {
repository.setMaxContinuations(maxFlowExecutionSnapshots.intValue());
repository.setMaxSnapshots(maxFlowExecutionSnapshots.intValue());
}
return repository;
}

View File

@@ -15,60 +15,72 @@
*/
package org.springframework.webflow.execution.repository.continuation;
import java.io.Serializable;
import org.springframework.util.Assert;
import org.springframework.webflow.conversation.ConversationManager;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionKey;
import org.springframework.webflow.execution.repository.support.AbstractFlowExecutionRepository;
import org.springframework.webflow.execution.repository.support.CompositeFlowExecutionKey;
import org.springframework.webflow.execution.repository.support.FlowExecutionStateRestorer;
/**
* Base class for repositories that create flow execution snapshots called "continuations".
* Base class for repositories that take flow execution snapshots using a {@link FlowExecutionSnapshotFactory}.
*
* @author Keith Donald
*/
public abstract class AbstractFlowExecutionContinuationRepository extends AbstractFlowExecutionRepository {
public abstract class AbstractSnapshottingFlowExecutionRepository extends AbstractFlowExecutionRepository {
/**
* The continuation factory that will be used to create new continuations to be added to active conversations.
* The factory to use to take flow execution snapshots.
*/
private FlowExecutionContinuationFactory continuationFactory;
private FlowExecutionSnapshotFactory executionSnapshotFactory;
/**
* Creates a new continuation repository.
* Creates a new snapshotting flow execution repository.
* @param conversationManager the conversation manager
* @param executionStateRestorer the execution state restorer
* @param continuationFactory the continuation factory
* @param executionSnapshotFactory the execution snapshot factory
*/
public AbstractFlowExecutionContinuationRepository(ConversationManager conversationManager,
FlowExecutionStateRestorer executionStateRestorer, FlowExecutionContinuationFactory continuationFactory) {
public AbstractSnapshottingFlowExecutionRepository(ConversationManager conversationManager,
FlowExecutionStateRestorer executionStateRestorer, FlowExecutionSnapshotFactory executionSnapshotFactory) {
super(conversationManager, executionStateRestorer);
Assert.notNull(continuationFactory, "The flow execution continuation factory is required");
this.continuationFactory = continuationFactory;
Assert.notNull(executionSnapshotFactory, "The flow execution snapshot factory is required");
this.executionSnapshotFactory = executionSnapshotFactory;
}
/**
* Returns the configured flow execution snapshot factory.
* @return the snapshot factory
*/
public FlowExecutionContinuationFactory getContinuationFactory() {
return continuationFactory;
public FlowExecutionSnapshotFactory getExecutionSnapshotFactory() {
return executionSnapshotFactory;
}
/**
* Take a new continuation snapshot.
* @param flowExecution the execution to snapshot
* @return the continuation snapshot
* Returns the snapshotId portion of the flow execution key.
* @param key the execution key
*/
protected FlowExecutionContinuation snapshot(FlowExecution flowExecution) {
return continuationFactory.createContinuation(flowExecution);
protected Serializable getSnapshotId(FlowExecutionKey key) {
return ((CompositeFlowExecutionKey) key).getSnapshotId();
}
/**
* Take a new flow execution snapshot.
* @param flowExecution the execution to snapshot
* @return the snapshot
*/
protected FlowExecutionSnapshot snapshot(FlowExecution flowExecution) {
return executionSnapshotFactory.createSnapshot(flowExecution);
}
/**
* Deserialize a serialized flow execution.
* @param continuationBytes the flow execution snapshot byte array
* @param snapshotBytes the flow execution snapshot byte array
* @return the deserialized flow execution
*/
protected FlowExecution deserializeExecution(byte[] continuationBytes) {
return continuationFactory.restoreContinuation(continuationBytes).unmarshal();
protected FlowExecution deserializeExecution(byte[] snapshotBytes) {
return executionSnapshotFactory.restoreSnapshot(snapshotBytes).unmarshal();
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.execution.repository.continuation;
import java.io.Serializable;
import org.springframework.webflow.execution.repository.FlowExecutionRepositoryException;
/**
* Thrown when no flow execution continuation exists within a continuation group with a particular id. This might occur
* if the continuation has expired or was explictly invalidated but a client's browser page cache still references it.
*
* @author Keith Donald
* @author Erwin Vervaet
*/
public class ContinuationNotFoundException extends FlowExecutionRepositoryException {
/**
* The unique continuation identifier that was not found.
*/
private Serializable continuationId;
/**
* Creates a continuation not found exception.
* @param continuationId the continuation id that could not be found
*/
public ContinuationNotFoundException(Serializable continuationId) {
super("No flow execution continuation could be found in this group with id '" + continuationId
+ "' -- perhaps the continuation has expired or has been invalidated? ");
this.continuationId = continuationId;
}
/**
* Returns the continuation id that could not be found.
*/
public Serializable getContinuationId() {
return continuationId;
}
}

View File

@@ -22,22 +22,22 @@ import org.springframework.webflow.execution.FlowExecution;
/**
* A snapshot of a flow execution that can be restored from and serialized to a byte array.
*
* @see FlowExecutionContinuationFactory
* @see FlowExecutionSnapshotFactory
*
* @author Erwin Vervaet
* @author Keith Donald
*/
public abstract class FlowExecutionContinuation implements Serializable {
public abstract class FlowExecutionSnapshot implements Serializable {
/**
* Restores the flow execution wrapped in this continuation.
* Returns underlying flow execution object.
* @return the unmarshalled flow execution
* @throws ContinuationUnmarshalException when there is a problem unmarshalling this continuation
* @throws SnapshotUnmarshalException when there is a problem unmarshalling the execution
*/
public abstract FlowExecution unmarshal() throws ContinuationUnmarshalException;
public abstract FlowExecution unmarshal() throws SnapshotUnmarshalException;
/**
* Converts this continuation to a byte array for convenient serialization.
* Converts this snapshot to a byte array for convenient serialization.
* @return this as a byte array
*/
public abstract byte[] toByteArray();

View File

@@ -18,27 +18,26 @@ package org.springframework.webflow.execution.repository.continuation;
import org.springframework.webflow.execution.FlowExecution;
/**
* A factory for creating different {@link FlowExecutionContinuation} implementations.
* A factory for creating different {@link FlowExecutionSnapshot} implementations.
*
* @author Keith Donald
* @author Erwin Vervaet
*/
public interface FlowExecutionContinuationFactory {
public interface FlowExecutionSnapshotFactory {
/**
* Creates a new flow execution continuation for given flow execution.
* Takes a snapshot of the flow execution.
* @param flowExecution the flow execution
* @return the continuation
* @throws ContinuationCreationException when the continuation cannot be created
* @return the new snapshot
* @throws SnapshotCreationException if the snapshot could not be created
*/
public FlowExecutionContinuation createContinuation(FlowExecution flowExecution)
throws ContinuationCreationException;
public FlowExecutionSnapshot createSnapshot(FlowExecution flowExecution) throws SnapshotCreationException;
/**
* Restore a flow execution continuation object from the provided byte array.
* @param bytes the flow execution byte array
* @return the continuation
* @throws ContinuationUnmarshalException when the continuation cannot be restored
* Restore a flow execution snapshot from a byte array.
* @param bytes the byte array
* @return the snapshot
* @throws SnapshotUnmarshalException if the snapshot could not be restored
*/
public FlowExecutionContinuation restoreContinuation(byte[] bytes) throws ContinuationUnmarshalException;
public FlowExecutionSnapshot restoreSnapshot(byte[] bytes) throws SnapshotUnmarshalException;
}

View File

@@ -32,73 +32,66 @@ import org.springframework.util.FileCopyUtils;
import org.springframework.webflow.execution.FlowExecution;
/**
* A continuation implementation that is based on standard Java serialization, created by a
* {@link SerializedFlowExecutionContinuationFactory}.
* A snapshot implementation that is based on standard Java serialization, created by a
* {@link SerializedFlowExecutionSnapshotFactory}.
*
* @see SerializedFlowExecutionContinuationFactory
* @see SerializedFlowExecutionSnapshotFactory
*
* @author Keith Donald
* @author Erwin Vervaet
*/
class SerializedFlowExecutionContinuation extends FlowExecutionContinuation implements Externalizable {
class SerializedFlowExecutionSnapshot extends FlowExecutionSnapshot implements Externalizable {
/**
* The serialized flow execution.
*/
private byte[] flowExecutionData;
/**
* Whether or not the flow execution byte array is compressed.
*/
private boolean compressed;
/**
* Default constructor necessary for {@link Externalizable} custom serialization semantics. Should not be called by
* application code.
*/
public SerializedFlowExecutionContinuation() {
public SerializedFlowExecutionSnapshot() {
}
/**
* Creates a new serialized flow execution continuation. This will marshall given flow execution into a serialized
* continuation form.
* Creates a new serialized flow execution snapshot.
* @param flowExecution the flow execution
* @param compress whether or not the flow execution should be compressed
* @param compress whether or not to apply compression during snapshotting
*/
public SerializedFlowExecutionContinuation(FlowExecution flowExecution, boolean compress)
throws ContinuationCreationException {
public SerializedFlowExecutionSnapshot(FlowExecution flowExecution, boolean compress)
throws SnapshotCreationException {
try {
flowExecutionData = serialize(flowExecution);
if (compress) {
flowExecutionData = compress(flowExecutionData);
}
} catch (NotSerializableException e) {
throw new ContinuationCreationException(flowExecution, "Could not serialize flow execution; "
throw new SnapshotCreationException(flowExecution, "Could not serialize flow execution; "
+ "make sure all objects stored in flow or flash scope are serializable", e);
} catch (IOException e) {
throw new ContinuationCreationException(flowExecution,
throw new SnapshotCreationException(flowExecution,
"IOException thrown serializing flow execution -- this should not happen!", e);
}
this.compressed = compress;
}
/**
* Returns whether or not the flow execution data in this continuation is compressed.
* Returns whether or not the flow execution data in this snapshot is compressed.
*/
public boolean isCompressed() {
return compressed;
}
public FlowExecution unmarshal() throws ContinuationUnmarshalException {
public FlowExecution unmarshal() throws SnapshotUnmarshalException {
try {
return deserialize(getFlowExecutionData());
} catch (IOException e) {
throw new ContinuationUnmarshalException(
"IOException thrown deserializing the flow execution stored in this continuation -- this should not happen!",
throw new SnapshotUnmarshalException(
"IOException thrown deserializing the flow execution stored in this snapshot -- this should not happen!",
e);
} catch (ClassNotFoundException e) {
throw new ContinuationUnmarshalException(
"ClassNotFoundException thrown deserializing the flow execution stored in this continuation -- "
throw new SnapshotUnmarshalException(
"ClassNotFoundException thrown deserializing the flow execution stored in this snapshot -- "
+ "This should not happen! Make sure there are no classloader issues. "
+ "For example, perhaps the Web Flow system is being loaded by a classloader "
+ "that is a parent of the classloader loading application classes?", e);
@@ -122,10 +115,10 @@ class SerializedFlowExecutionContinuation extends FlowExecutionContinuation impl
}
public boolean equals(Object o) {
if (!(o instanceof SerializedFlowExecutionContinuation)) {
if (!(o instanceof SerializedFlowExecutionSnapshot)) {
return false;
}
SerializedFlowExecutionContinuation c = (SerializedFlowExecutionContinuation) o;
SerializedFlowExecutionSnapshot c = (SerializedFlowExecutionSnapshot) o;
return Arrays.equals(flowExecutionData, c.flowExecutionData);
}
@@ -156,7 +149,7 @@ class SerializedFlowExecutionContinuation extends FlowExecutionContinuation impl
compressed = in.readBoolean();
}
// internal helpers
// subclassing hooks
/**
* Return the flow execution data in its raw byte[] form. Will decompress if necessary.

View File

@@ -22,49 +22,48 @@ import java.io.ObjectInputStream;
import org.springframework.webflow.execution.FlowExecution;
/**
* A factory that creates new instances of flow execution continuations based on standard Java serialization.
* A factory that creates new instances of flow execution snapshots based on standard Java serialization.
*
* @author Keith Donald
* @author Erwin Vervaet
*/
public class SerializedFlowExecutionContinuationFactory implements FlowExecutionContinuationFactory {
public class SerializedFlowExecutionSnapshotFactory implements FlowExecutionSnapshotFactory {
/**
* Flag to toggle continuation compression; compression is on by default.
* Flag to toggle snapshot compression; compression is on by default.
*/
private boolean compress = true;
/**
* Returns whether or not the continuations should be compressed.
* Returns whether or not the snapshots should be compressed.
*/
public boolean getCompress() {
return compress;
}
/**
* Set whether or not the continuations should be compressed.
* Set whether or not the snapshots should be compressed.
*/
public void setCompress(boolean compress) {
this.compress = compress;
}
public FlowExecutionContinuation createContinuation(FlowExecution flowExecution)
throws ContinuationCreationException {
return new SerializedFlowExecutionContinuation(flowExecution, compress);
public FlowExecutionSnapshot createSnapshot(FlowExecution flowExecution) throws SnapshotCreationException {
return new SerializedFlowExecutionSnapshot(flowExecution, compress);
}
public FlowExecutionContinuation restoreContinuation(byte[] bytes) throws ContinuationUnmarshalException {
public FlowExecutionSnapshot restoreSnapshot(byte[] bytes) throws SnapshotUnmarshalException {
try {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
try {
return (FlowExecutionContinuation) ois.readObject();
return (FlowExecutionSnapshot) ois.readObject();
} finally {
ois.close();
}
} catch (IOException e) {
throw new ContinuationUnmarshalException("IO problem while creating a flow execution continuation", e);
throw new SnapshotUnmarshalException("IO problem while creating the flow execution snapshot", e);
} catch (ClassNotFoundException e) {
throw new ContinuationUnmarshalException("Class not found while creating a flow execution continuation", e);
throw new SnapshotUnmarshalException("Class not found while creating the flow execution snapshot", e);
}
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.webflow.execution.repository.FlowExecutionRepositoryE
*
* @author Keith Donald
*/
public class ContinuationCreationException extends FlowExecutionRepositoryException {
public class SnapshotCreationException extends FlowExecutionRepositoryException {
/**
* The flow execution that could not be snapshotted.
@@ -31,12 +31,12 @@ public class ContinuationCreationException extends FlowExecutionRepositoryExcept
private FlowExecution flowExecution;
/**
* Creates a new continuation creation exception.
* Creates a new snapshot creation exception.
* @param flowExecution the flow execution
* @param message a descriptive message
* @param cause the cause
*/
public ContinuationCreationException(FlowExecution flowExecution, String message, Throwable cause) {
public SnapshotCreationException(FlowExecution flowExecution, String message, Throwable cause) {
super(message, cause);
this.flowExecution = flowExecution;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.execution.repository.continuation;
import java.io.Serializable;
import org.springframework.webflow.execution.repository.FlowExecutionRepositoryException;
/**
* Thrown when a flow execution snapshot cannot be found This usually occurs when the client references a snapshot that
* has since been removed.
*
* @author Keith Donald
* @author Erwin Vervaet
*/
public class SnapshotNotFoundException extends FlowExecutionRepositoryException {
private Serializable snapshotId;
/**
* Creates a snapshot not found exception.
* @param snapshotId the snapshot id that could not be found
*/
public SnapshotNotFoundException(Serializable snapshotId) {
super("No flow execution snapshot could be found with id '" + snapshotId
+ "'; perhaps the snapshot has been removed? ");
this.snapshotId = snapshotId;
}
/**
* The id of the snapshot that was not found.
*/
public Serializable getSnapshotId() {
return snapshotId;
}
}

View File

@@ -21,19 +21,19 @@ import org.springframework.webflow.execution.repository.FlowExecutionRepositoryE
/**
* Thrown when a FlowExecutionContinuation could not be deserialized into a FlowExecution.
*
* @see FlowExecutionContinuation
* @see FlowExecutionSnapshot
* @see FlowExecution
*
* @author Keith Donald
*/
public class ContinuationUnmarshalException extends FlowExecutionRepositoryException {
public class SnapshotUnmarshalException extends FlowExecutionRepositoryException {
/**
* Creates a new flow execution unmarshalling exception.
* @param message the exception message
* @param cause the cause
*/
public ContinuationUnmarshalException(String message, Throwable cause) {
public SnapshotUnmarshalException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -20,94 +20,87 @@ import org.springframework.webflow.conversation.ConversationManager;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionKey;
import org.springframework.webflow.execution.repository.FlowExecutionRestorationFailureException;
import org.springframework.webflow.execution.repository.continuation.AbstractFlowExecutionContinuationRepository;
import org.springframework.webflow.execution.repository.continuation.ContinuationNotFoundException;
import org.springframework.webflow.execution.repository.continuation.ContinuationUnmarshalException;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionContinuation;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionContinuationFactory;
import org.springframework.webflow.execution.repository.continuation.SerializedFlowExecutionContinuationFactory;
import org.springframework.webflow.execution.repository.continuation.AbstractSnapshottingFlowExecutionRepository;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionSnapshot;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionSnapshotFactory;
import org.springframework.webflow.execution.repository.continuation.SerializedFlowExecutionSnapshotFactory;
import org.springframework.webflow.execution.repository.continuation.SnapshotNotFoundException;
import org.springframework.webflow.execution.repository.continuation.SnapshotUnmarshalException;
import org.springframework.webflow.execution.repository.support.FlowExecutionStateRestorer;
/**
* Stores <i>one to many</i> flow execution continuations (snapshots) per conversation, where each continuation
* represents a paused, restorable view-state of a flow execution snapshotted at a point in time.
* The default flow execution repository implementation. Takes <i>one to {@link #getMaxSnapshots() max}</i> flow
* execution snapshots, where each snapshot represents a copy of a {@link FlowExecution} taken at a point in time.
* <p>
* The set of active user conversations are managed by a {@link ConversationManager} implementation, which this
* repository delegates to.
* The set of active flow executions are managed by a {@link ConversationManager} implementation, which this repository
* delegates to.
* <p>
* This repository is responsible for:
* <ul>
* <li>Beginning a new conversation when a new flow execution is made persistent. Each conversation is assigned a
* unique conversation id which forms one part of the flow execution key.
* <li>Associating a flow execution with that conversation by adding a {@link FlowExecutionContinuation} to a
* continuation group.<br>
* When a flow execution is placed in this repository a new continuation snapshot is created, assigned an id, and added
* to the group. Each continuation logically represents a state of the conversation at a point in time <i>that can be
* restored and continued</i>. These continuations can be restored to support users going back in their browser to
* continue a conversation from a previous point.
* <li>Ending existing conversations when persistent flow executions end, as part of a repository removal operation.
* <li>Beginning a new {@link Conversation} when a {@link FlowExecution} is assigned a persistent key. Each
* conversation is assigned a unique conversation id which forms one part of the flow execution key.
* <li>Taking {@link FlowExecutionSnapshot execution snapshots} to persist flow execution state. A snapshot is a copy
* of the execution created at a point in time <i>that can be restored and continued</i>. Snapshotting supports users
* going back in their browser to continue their flow execution from a previoius point.
* <li>Ending conversations when flow executions end.
* </ul>
* <p>
* This repository implementation also provides support for <i>conversation invalidation after completion</i>, where
* once a logical conversation completes (by one of its FlowExecution's reaching an end state), the entire conversation
* (including all continuations) is invalidated. This prevents the possibility of duplicate submission after completion.
* <p>
* This repository implementation should be considered when you do have to support browser navigational button use, e.g.
* you cannot lock down the browser and require that all navigational events to be routed explicitly through Spring Web
* Flow.
* This repository implementation also provides support for <i>execution invalidation after completion</i>, where once
* a logical flow execution completes, it and all of its snapshots are removed. This cleans up memory and prevents the
* possibility of duplicate submission after completion.
*
* @author Keith Donald
*/
public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinuationRepository {
public class DefaultFlowExecutionRepository extends AbstractSnapshottingFlowExecutionRepository {
/**
* The conversation attribute that stores the "continuation group".
* The conversation attribute that stores the group of flow execution snapshots.
*/
private static final String CONTINUATION_GROUP_ATTRIBUTE = "continuationGroup";
private static final String SNAPSHOT_GROUP_ATTRIBUTE = "flowExecutionSnapshotGroup";
/**
* The maximum number of continuations that can be active per conversation. The default is 30, which is high enough
* not to interfere with the user experience of normal users using the back button, but low enough to avoid
* excessive resource usage or easy denial of service attacks.
* The maximum number of snapshots that can be taken per execution. The default is 30, which is generally high
* enough not to interfere with the user experience of normal users using the back button, but low enough to avoid
* excessive resource usage or denial of service attacks.
*/
private int maxContinuations = 30;
private int maxSnapshots = 30;
/**
* Create a new continuation based flow execution repository using the given state restorer and conversation
* manager. Defaults to a {@link SerializedFlowExecutionContinuationFactory}.
* Create a new default flow execution repository using the given state restorer and conversation manager. Defaults
* to a {@link SerializedFlowExecutionSnapshotFactory}.
* @param conversationManager the conversation manager to use
* @param executionStateRestorer the state restoration strategy to use
*/
public DefaultFlowExecutionRepository(ConversationManager conversationManager,
FlowExecutionStateRestorer executionStateRestorer) {
super(conversationManager, executionStateRestorer, new SerializedFlowExecutionContinuationFactory());
super(conversationManager, executionStateRestorer, new SerializedFlowExecutionSnapshotFactory());
}
/**
* Create a new continuation based flow execution repository using the given state restorer, conversation manager,
* and continuation factory.
* Create a new default flow execution repository using the given state restorer, conversation manager, and snapshot
* factory.
* @param conversationManager the conversation manager to use
* @param executionStateRestorer the state restoration strategy to use
* @param continuationFactory the continuation factory to use
* @param executionSnapshotFactory the flow execution snapshot factory to use
*/
public DefaultFlowExecutionRepository(ConversationManager conversationManager,
FlowExecutionStateRestorer executionStateRestorer, FlowExecutionContinuationFactory continuationFactory) {
super(conversationManager, executionStateRestorer, continuationFactory);
FlowExecutionStateRestorer executionStateRestorer, FlowExecutionSnapshotFactory executionSnapshotFactory) {
super(conversationManager, executionStateRestorer, executionSnapshotFactory);
}
/**
* Returns the max number of continuations allowed per conversation by this repository.
* Returns the max number of snapshots allowed per flow execution by this repository.
*/
public int getMaxContinuations() {
return maxContinuations;
public int getMaxSnapshots() {
return maxSnapshots;
}
/**
* Sets the maximum number of continuations allowed per conversation by this repository. Use -1 for unlimited. The
* Sets the maximum number of snapshots allowed per flow execution by this repository. Use -1 for unlimited. The
* default is 30.
*/
public void setMaxContinuations(int maxContinuations) {
this.maxContinuations = maxContinuations;
public void setMaxSnapshots(int maxSnapshots) {
this.maxSnapshots = maxSnapshots;
}
// implementing flow execution repository
@@ -117,16 +110,16 @@ public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinu
logger.debug("Getting flow execution with key '" + key + "'");
}
Conversation conversation = getConversation(key);
FlowExecutionContinuation snapshot;
FlowExecutionSnapshot snapshot;
try {
snapshot = getContinuationGroup(conversation).get(getContinuationId(key));
} catch (ContinuationNotFoundException e) {
snapshot = getSnapshotGroup(conversation).getSnapshot(getSnapshotId(key));
} catch (SnapshotNotFoundException e) {
throw new FlowExecutionRestorationFailureException(key, e);
}
try {
FlowExecution execution = snapshot.unmarshal();
return restoreTransientState(execution, key, conversation);
} catch (ContinuationUnmarshalException e) {
} catch (SnapshotUnmarshalException e) {
throw new FlowExecutionRestorationFailureException(key, e);
}
}
@@ -138,12 +131,12 @@ public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinu
}
FlowExecutionKey key = flowExecution.getKey();
Conversation conversation = getConversation(key);
FlowExecutionContinuationGroup continuationGroup = getContinuationGroup(conversation);
FlowExecutionContinuation snapshot = snapshot(flowExecution);
FlowExecutionSnapshotGroup snapshotGroup = getSnapshotGroup(conversation);
FlowExecutionSnapshot snapshot = snapshot(flowExecution);
if (logger.isDebugEnabled()) {
logger.debug("Adding new snapshot to group with id " + getContinuationId(key));
logger.debug("Adding new snapshot to group with id " + getSnapshotId(key));
}
continuationGroup.add(getContinuationId(key), snapshot);
snapshotGroup.addSnapshot(getSnapshotId(key), snapshot);
putConversationScope(flowExecution, conversation);
}
@@ -151,38 +144,40 @@ public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinu
public void removeAllFlowExecutionSnapshots(FlowExecution execution) {
Conversation conversation = getConversation(execution.getKey());
getContinuationGroup(conversation).removeAllContinuations();
getSnapshotGroup(conversation).removeAllSnapshots();
}
public void removeFlowExecutionSnapshot(FlowExecution execution) {
FlowExecutionKey key = execution.getKey();
Conversation conversation = getConversation(key);
getContinuationGroup(conversation).removeContinuation(getContinuationId(key));
getSnapshotGroup(conversation).removeSnapshot(getSnapshotId(key));
}
public void updateFlowExecutionSnapshot(FlowExecution execution) {
FlowExecutionKey key = execution.getKey();
Conversation conversation = getConversation(key);
getContinuationGroup(conversation).updateContinuation(getContinuationId(key), snapshot(execution));
getSnapshotGroup(conversation).updateSnapshot(getSnapshotId(key), snapshot(execution));
}
// hooks for subclassing
protected FlowExecutionContinuationGroup createFlowExecutionContinuationGroup() {
return new FlowExecutionContinuationGroup(maxContinuations);
protected FlowExecutionSnapshotGroup createFlowExecutionSnapshotGroup() {
SimpleFlowExecutionSnapshotGroup group = new SimpleFlowExecutionSnapshotGroup();
group.setMaxSnapshots(maxSnapshots);
return group;
}
/**
* Returns the continuation group associated with the governing conversation.
* @param conversation the conversation where the continuation group is stored
* @return the continuation group
* Returns the snapshot group associated with the governing conversation.
* @param conversation the conversation where the snapshot group is stored
* @return the snapshot group
*/
protected FlowExecutionContinuationGroup getContinuationGroup(Conversation conversation) {
FlowExecutionContinuationGroup group = (FlowExecutionContinuationGroup) conversation
.getAttribute(CONTINUATION_GROUP_ATTRIBUTE);
protected FlowExecutionSnapshotGroup getSnapshotGroup(Conversation conversation) {
FlowExecutionSnapshotGroup group = (FlowExecutionSnapshotGroup) conversation
.getAttribute(SNAPSHOT_GROUP_ATTRIBUTE);
if (group == null) {
group = createFlowExecutionContinuationGroup();
conversation.putAttribute(CONTINUATION_GROUP_ATTRIBUTE, group);
group = createFlowExecutionSnapshotGroup();
conversation.putAttribute(SNAPSHOT_GROUP_ATTRIBUTE, group);
}
return group;
}

View File

@@ -1,144 +0,0 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.execution.repository.impl;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import org.springframework.webflow.execution.repository.continuation.ContinuationNotFoundException;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionContinuation;
/**
* A group of flow execution continuations. Simple typed data structure backed by a map and linked list. Supports
* expelling the oldest continuation once a maximum group size is met.
*
* @author Keith Donald
*/
class FlowExecutionContinuationGroup implements Serializable {
/**
* A map of continuations; the key is the continuation id, and the value is the {@link FlowExecutionContinuation}
* object.
*/
private Map continuations = new HashMap();
/**
* An ordered list of continuation ids. Each continuation id represents an pointer to a continuation in the map. The
* first element is the oldest continuation and the last is the youngest.
*/
private LinkedList continuationIds = new LinkedList();
/**
* The maximum number of continuations allowed in this group.
*/
private int maxContinuations = -1;
/**
* Creates a new flow execution continuation group.
* @param maxContinuations the maximum number of continuations that can be stored in this group, -1 for unlimited
*/
public FlowExecutionContinuationGroup(int maxContinuations) {
this.maxContinuations = maxContinuations;
}
/**
* Returns the count of continuations in this group.
*/
public int getContinuationCount() {
return continuationIds.size();
}
/**
* Returns the continuation with the provided <code>id</code>, or <code>null</code> if no such continuation
* exists with that id.
* @param continuationId the continuation id
* @return the continuation
* @throws ContinuationNotFoundException if the id does not match a continuation in this group
*/
public FlowExecutionContinuation get(Serializable continuationId) throws ContinuationNotFoundException {
FlowExecutionContinuation continuation = (FlowExecutionContinuation) continuations.get(continuationId);
if (continuation == null) {
throw new ContinuationNotFoundException(continuationId);
}
return continuation;
}
/**
* Add a flow execution continuation with given id to this group.
* @param continuationId the continuation id
* @param continuation the continuation
*/
public void add(Serializable continuationId, FlowExecutionContinuation continuation) {
continuations.put(continuationId, continuation);
if (continuationIds.contains(continuationId)) {
continuationIds.remove(continuationId);
}
continuationIds.add(continuationId);
// remove the oldest continuation if the maximium number of
// continuations has been exceeded
if (maxExceeded()) {
removeOldestContinuation();
}
}
/**
* Update the continuation with the given id.
* @param continuationId the continuation id
* @param continuation thew new continuation
* @throws ContinuationNotFoundException if there was no previous continuation to update
*/
public void updateContinuation(Serializable continuationId, FlowExecutionContinuation continuation)
throws ContinuationNotFoundException {
if (!continuations.containsKey(continuationId)) {
throw new ContinuationNotFoundException(continuationId);
}
continuations.put(continuationId, continuation);
}
/**
* Remove the continuation with the given id.
* @param continuationId the continuation id
*/
public void removeContinuation(Serializable continuationId) {
continuations.remove(continuationId);
continuationIds.remove(continuationId);
}
/**
* Remove all continuations in this group.
*/
public void removeAllContinuations() {
continuations.clear();
continuationIds.clear();
}
/**
* Has the maximum number of allowed continuations in this group been exceeded?
*/
private boolean maxExceeded() {
return maxContinuations > 0 && continuationIds.size() > maxContinuations;
}
/**
* Remove the olders continuation from this group.
*/
private void removeOldestContinuation() {
continuations.remove(continuationIds.removeFirst());
}
}

View File

@@ -0,0 +1,56 @@
package org.springframework.webflow.execution.repository.impl;
import java.io.Serializable;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionSnapshot;
import org.springframework.webflow.execution.repository.continuation.SnapshotNotFoundException;
/**
* A group of flow execution snapshots.
*
* @author Keith Donald
*/
public interface FlowExecutionSnapshotGroup {
/**
* Returns the snapshot with the provided <code>id</code>, or <code>null</code> if no such snapshot exists with
* that id.
* @param snapshotId the snapshot id
* @return the continuation
* @throws SnapshotNotFoundException if the id does not match a continuation in this group
*/
public FlowExecutionSnapshot getSnapshot(Serializable snapshotId) throws SnapshotNotFoundException;
/**
* Add a flow execution snapshot with given id to this group.
* @param snapshotId the snapshot id
* @param snapshot the snapshot
*/
public void addSnapshot(Serializable snapshotId, FlowExecutionSnapshot snapshot);
/**
* Update the snapshot with the given id.
* @param snapshotId the snapshot id
* @param snapshot the new snapshot
* @throws SnapshotNotFoundException if there was no previous snapshot to update
*/
public void updateSnapshot(Serializable snapshotId, FlowExecutionSnapshot snapshot)
throws SnapshotNotFoundException;
/**
* Remove the snapshot with the given id.
* @param snapshotId the continuation id
*/
public void removeSnapshot(Serializable snapshotId);
/**
* Remove all snapshots in this group.
*/
public void removeAllSnapshots();
/**
* Returns the count of snapshots in this group.
*/
public int getSnapshotCount();
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.execution.repository.impl;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionSnapshot;
import org.springframework.webflow.execution.repository.continuation.SnapshotNotFoundException;
/**
* A group of flow execution snapshots. Simple typed data structure backed by a map and linked list. Supports expelling
* the oldest snapshot if the maximum size is met.
*
* @author Keith Donald
*/
class SimpleFlowExecutionSnapshotGroup implements FlowExecutionSnapshotGroup, Serializable {
/**
* The snapshot map; the key is a snapshot id, and the value is a {@link FlowExecutionSnapshot} object.
*/
private Map snapshots = new HashMap();
/**
* An ordered list of snapshot ids. Each snapshot id represents an pointer to a {@link FlowExecutionSnapshot} in the
* map. The first element is the oldest snapshot and the last is the youngest.
*/
private LinkedList snapshotIds = new LinkedList();
/**
* The maximum number of snapshots allowed in this group.
*/
private int maxSnapshots = -1;
/**
* Returns the maximum number of snapshots allowed in this group.
*/
public int getMaxSnapshots() {
return maxSnapshots;
}
/**
* Sets the maximum number of snapshots allowed in this group.
* @param maxSnapshots them max number of snapshots
*/
public void setMaxSnapshots(int maxSnapshots) {
this.maxSnapshots = maxSnapshots;
}
public FlowExecutionSnapshot getSnapshot(Serializable snapshotId) throws SnapshotNotFoundException {
FlowExecutionSnapshot snapshot = (FlowExecutionSnapshot) snapshots.get(snapshotId);
if (snapshot == null) {
throw new SnapshotNotFoundException(snapshotId);
}
return snapshot;
}
public void addSnapshot(Serializable snapshotId, FlowExecutionSnapshot snapshot) {
snapshots.put(snapshotId, snapshot);
if (snapshotIds.contains(snapshotId)) {
snapshotIds.remove(snapshotId);
}
snapshotIds.add(snapshotId);
if (maxExceeded()) {
removeOldestSnapshot();
}
}
public void updateSnapshot(Serializable snapshotId, FlowExecutionSnapshot snapshot)
throws SnapshotNotFoundException {
if (!snapshots.containsKey(snapshotId)) {
throw new SnapshotNotFoundException(snapshotId);
}
snapshots.put(snapshotId, snapshot);
}
public void removeSnapshot(Serializable snapshotId) {
snapshots.remove(snapshotId);
snapshotIds.remove(snapshotId);
}
public void removeAllSnapshots() {
snapshots.clear();
snapshotIds.clear();
}
public int getSnapshotCount() {
return snapshotIds.size();
}
/**
* Has the maximum number of snapshots in this group been exceeded?
*/
private boolean maxExceeded() {
return maxSnapshots > 0 && snapshotIds.size() > maxSnapshots;
}
/**
* Remove the olders snapshot from this group.
*/
private void removeOldestSnapshot() {
snapshots.remove(snapshotIds.removeFirst());
}
}

View File

@@ -46,7 +46,7 @@ import org.springframework.webflow.execution.repository.NoSuchFlowExecutionExcep
* rehydrate a flow execution after it has been obtained from storage from resume.
* <p>
* 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, ...).
* implementation and its {@link FlowExecutionFactory}.
*
* @author Keith Donald
* @author Erwin Vervaet
@@ -122,14 +122,9 @@ public abstract class AbstractFlowExecutionRepository implements FlowExecutionRe
"The string-encoded flow execution key is required");
}
String[] keyParts = CompositeFlowExecutionKey.keyParts(encodedKey);
ConversationId conversationId;
try {
conversationId = conversationManager.parseConversationId(keyParts[0]);
} catch (ConversationException e) {
throw new BadlyFormattedFlowExecutionKeyException(encodedKey, CompositeFlowExecutionKey.getFormat(), e);
}
Serializable continuationId = parseContinuationId(keyParts[1], encodedKey);
return new CompositeFlowExecutionKey(conversationId, continuationId);
Serializable executionId = parseExecutionId(keyParts[0], encodedKey);
Serializable snapshotId = parseSnapshotId(keyParts[1], encodedKey);
return new CompositeFlowExecutionKey(executionId, snapshotId);
}
public FlowExecutionLock getLock(FlowExecutionKey key) throws FlowExecutionRepositoryException {
@@ -170,10 +165,9 @@ public abstract class AbstractFlowExecutionRepository implements FlowExecutionRe
*/
protected FlowExecutionKey getNextKey(FlowExecution execution) {
if (alwaysGenerateNewNextKey) {
CompositeFlowExecutionKey key = (CompositeFlowExecutionKey) execution.getKey();
Integer continuationId = (Integer) key.getContinuationId();
Integer nextId = nextContinuationId(continuationId);
return new CompositeFlowExecutionKey(key.getConversationId(), nextId);
CompositeFlowExecutionKey currentKey = (CompositeFlowExecutionKey) execution.getKey();
Integer currentSnapshotId = (Integer) currentKey.getSnapshotId();
return new CompositeFlowExecutionKey(currentKey.getExecutionId(), nextSnapshotId(currentSnapshotId));
} else {
return execution.getKey();
}
@@ -187,28 +181,13 @@ public abstract class AbstractFlowExecutionRepository implements FlowExecutionRe
*/
protected Conversation getConversation(FlowExecutionKey key) throws NoSuchFlowExecutionException {
try {
return conversationManager.getConversation(getConversationId(key));
ConversationId conversationId = (ConversationId) ((CompositeFlowExecutionKey) key).getExecutionId();
return conversationManager.getConversation(conversationId);
} catch (NoSuchConversationException e) {
throw new NoSuchFlowExecutionException(key, e);
}
}
/**
* Returns the conversationId portion of the flow execution key.
* @param key the execution key
*/
protected ConversationId getConversationId(FlowExecutionKey key) {
return ((CompositeFlowExecutionKey) key).getConversationId();
}
/**
* Returns the continuationId portion of the flow execution key.
* @param key the execution key
*/
protected Serializable getContinuationId(FlowExecutionKey key) {
return ((CompositeFlowExecutionKey) key).getContinuationId();
}
/**
* Returns the transient state of the flow execution after potential deserialization.
* @param execution the flow execution
@@ -251,15 +230,25 @@ public abstract class AbstractFlowExecutionRepository implements FlowExecutionRe
return conversation;
}
private Integer nextContinuationId(Integer continuationId) {
private Integer nextSnapshotId(Integer currentSnapshotId) {
if (JdkVersion.isAtLeastJava15()) {
return Integer.valueOf(continuationId.intValue() + 1);
return Integer.valueOf(currentSnapshotId.intValue() + 1);
} else {
return new Integer(continuationId.intValue() + 1);
return new Integer(currentSnapshotId.intValue() + 1);
}
}
private Serializable parseContinuationId(String encodedId, String encodedKey) {
private ConversationId parseExecutionId(String encodedId, String encodedKey)
throws BadlyFormattedFlowExecutionKeyException {
try {
return conversationManager.parseConversationId(encodedId);
} catch (ConversationException e) {
throw new BadlyFormattedFlowExecutionKeyException(encodedKey, CompositeFlowExecutionKey.getFormat(), e);
}
}
private Serializable parseSnapshotId(String encodedId, String encodedKey)
throws BadlyFormattedFlowExecutionKeyException {
try {
return Integer.valueOf(encodedId);
} catch (NumberFormatException e) {

View File

@@ -18,79 +18,54 @@ package org.springframework.webflow.execution.repository.support;
import java.io.Serializable;
import org.springframework.util.Assert;
import org.springframework.webflow.conversation.ConversationId;
import org.springframework.webflow.conversation.ConversationManager;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionKey;
import org.springframework.webflow.execution.repository.BadlyFormattedFlowExecutionKeyException;
import org.springframework.webflow.execution.repository.continuation.FlowExecutionContinuation;
/**
* A flow execution key consisting of two parts:
* A flow execution key that consists of two parts:
* <ol>
* <li>A <i>conversationId</i>, identifying an active conversation managed by a {@link ConversationManager}.
* <li>A <i>continuationId</i>, identifying a restorable {@link FlowExecutionContinuation} within a continuation group
* governed by that conversation.
* <li>A <i>executionId</i>, identifying a logical {@link FlowExecution} that is running.
* <li>A <i>snapshotId</i>, identifying a physical flow execution snapshot that can be restored.
* </ol>
* <p>
* This key is used to restore a FlowExecution from a conversation-service backed store.
*
* @see ConversationManager
* @see FlowExecutionContinuation
*
* @author Keith Donald
*/
public class CompositeFlowExecutionKey extends FlowExecutionKey {
/**
* The default conversation id prefix delimiter.
*/
private static final String CONVERSATION_ID_PREFIX = "c";
private static final String EXECUTION_ID_PREFIX = "e";
/**
* The default continuation id prefix delimiter.
*/
private static final String CONTINUATION_ID_PREFIX = "v";
private static final String SNAPSHOT_ID_PREFIX = "s";
/**
* The format of the default string-encoded form, as returned by toString().
*/
private static final String FORMAT = CONVERSATION_ID_PREFIX + "<conversationId>" + CONTINUATION_ID_PREFIX
+ "<continuationId>";
private static final String FORMAT = EXECUTION_ID_PREFIX + "<executionId>" + SNAPSHOT_ID_PREFIX + "<snapshotId>";
/**
* The conversation id.
*/
private ConversationId conversationId;
private Serializable executionId;
/**
* The continuation id.
*/
private Serializable continuationId;
private Serializable snapshotId;
/**
* Create a new composite flow execution key given the composing parts.
* @param conversationId the conversation id
* @param continuationId the continuation id
* @param executionId the execution id
* @param snapshotId the snapshot id
*/
public CompositeFlowExecutionKey(ConversationId conversationId, Serializable continuationId) {
Assert.notNull(conversationId, "The conversation id is required");
Assert.notNull(continuationId, "The continuation id is required");
this.conversationId = conversationId;
this.continuationId = continuationId;
public CompositeFlowExecutionKey(Serializable executionId, Serializable snapshotId) {
Assert.notNull(executionId, "The execution id is required");
Assert.notNull(snapshotId, "The snapshot id is required");
this.executionId = executionId;
this.snapshotId = snapshotId;
}
/**
* Returns the conversation id.
* Returns the execution id part of this key.
*/
public ConversationId getConversationId() {
return conversationId;
public Serializable getExecutionId() {
return executionId;
}
/**
* Returns the continuation id.
* Returns the snapshot id part of this key.
*/
public Serializable getContinuationId() {
return continuationId;
public Serializable getSnapshotId() {
return snapshotId;
}
public boolean equals(Object obj) {
@@ -98,16 +73,16 @@ public class CompositeFlowExecutionKey extends FlowExecutionKey {
return false;
}
CompositeFlowExecutionKey other = (CompositeFlowExecutionKey) obj;
return conversationId.equals(other.conversationId) && continuationId.equals(other.continuationId);
return executionId.equals(other.executionId) && snapshotId.equals(other.snapshotId);
}
public int hashCode() {
return conversationId.hashCode() + continuationId.hashCode();
return executionId.hashCode() + snapshotId.hashCode();
}
public String toString() {
return new StringBuffer().append(CONVERSATION_ID_PREFIX).append(getConversationId()).append(
CONTINUATION_ID_PREFIX).append(getContinuationId()).toString();
return new StringBuffer().append(EXECUTION_ID_PREFIX).append(executionId).append(SNAPSHOT_ID_PREFIX).append(
snapshotId).toString();
}
// static helpers
@@ -123,18 +98,18 @@ public class CompositeFlowExecutionKey extends FlowExecutionKey {
* Helper that splits the string-form of an instance of this class into its "parts" so the parts can be easily
* parsed.
* @param encodedKey the string-encoded composite flow execution key
* @return the composite key parts as a String array (conversationId = 0, continuationId = 1)
* @return the composite key parts as a String array (executionId = 0, snapshotId = 1)
*/
public static String[] keyParts(String encodedKey) throws BadlyFormattedFlowExecutionKeyException {
if (!encodedKey.startsWith(CONVERSATION_ID_PREFIX)) {
if (!encodedKey.startsWith(EXECUTION_ID_PREFIX)) {
throw new BadlyFormattedFlowExecutionKeyException(encodedKey, FORMAT);
}
int continuationStart = encodedKey.indexOf(CONTINUATION_ID_PREFIX, CONVERSATION_ID_PREFIX.length());
if (continuationStart == -1) {
int snapshotStart = encodedKey.indexOf(SNAPSHOT_ID_PREFIX, EXECUTION_ID_PREFIX.length());
if (snapshotStart == -1) {
throw new BadlyFormattedFlowExecutionKeyException(encodedKey, FORMAT);
}
String conversationId = encodedKey.substring(CONVERSATION_ID_PREFIX.length(), continuationStart);
String continuationId = encodedKey.substring(continuationStart + CONTINUATION_ID_PREFIX.length());
return new String[] { conversationId, continuationId };
String executionId = encodedKey.substring(EXECUTION_ID_PREFIX.length(), snapshotStart);
String snapshotId = encodedKey.substring(snapshotStart + SNAPSHOT_ID_PREFIX.length());
return new String[] { executionId, snapshotId };
}
}

View File

@@ -21,7 +21,7 @@ import org.springframework.webflow.test.MockFlowExecutionKeyFactory;
public class SerializedFlowExecutionContinuationFactoryTests extends TestCase {
private Flow flow;
private SerializedFlowExecutionContinuationFactory factory;
private SerializedFlowExecutionSnapshotFactory factory;
private FlowExecutionStateRestorer stateRestorer;
private FlowExecutionKeyFactory executionKeyFactory;
@@ -31,7 +31,7 @@ public class SerializedFlowExecutionContinuationFactoryTests extends TestCase {
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
}
};
factory = new SerializedFlowExecutionContinuationFactory();
factory = new SerializedFlowExecutionSnapshotFactory();
stateRestorer = new FlowExecutionImplStateRestorer(new FlowDefinitionLocator() {
public FlowDefinition getFlowDefinition(String flowId) throws NoSuchFlowDefinitionException,
FlowDefinitionConstructionException {
@@ -45,7 +45,7 @@ public class SerializedFlowExecutionContinuationFactoryTests extends TestCase {
FlowExecution flowExecution = new FlowExecutionImplFactory().createFlowExecution(flow);
flowExecution.start(null, new MockExternalContext());
flowExecution.getActiveSession().getScope().put("foo", "bar");
FlowExecutionContinuation continuation = factory.createContinuation(flowExecution);
FlowExecutionSnapshot continuation = factory.createSnapshot(flowExecution);
FlowExecutionImpl flowExecution2 = (FlowExecutionImpl) continuation.unmarshal();
assertNotSame(flowExecution, flowExecution2);
stateRestorer.restoreState(flowExecution2, null, flowExecution.getConversationScope(), executionKeyFactory);
@@ -60,9 +60,9 @@ public class SerializedFlowExecutionContinuationFactoryTests extends TestCase {
FlowExecution flowExecution = new FlowExecutionImplFactory().createFlowExecution(flow);
flowExecution.start(null, new MockExternalContext());
flowExecution.getActiveSession().getScope().put("foo", "bar");
FlowExecutionContinuation continuation = factory.createContinuation(flowExecution);
FlowExecutionSnapshot continuation = factory.createSnapshot(flowExecution);
byte[] bytes = continuation.toByteArray();
FlowExecutionContinuation continuation2 = factory.restoreContinuation(bytes);
FlowExecutionSnapshot continuation2 = factory.restoreSnapshot(bytes);
assertEquals(continuation, continuation2);
FlowExecutionImpl flowExecution2 = (FlowExecutionImpl) continuation2.unmarshal();
assertNotSame(flowExecution, flowExecution2);

View File

@@ -11,6 +11,7 @@ import org.springframework.webflow.conversation.ConversationId;
import org.springframework.webflow.conversation.ConversationManager;
import org.springframework.webflow.conversation.ConversationParameters;
import org.springframework.webflow.conversation.NoSuchConversationException;
import org.springframework.webflow.conversation.impl.BadlyFormattedConversationIdException;
import org.springframework.webflow.conversation.impl.SimpleConversationId;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.registry.FlowDefinitionConstructionException;
@@ -53,18 +54,18 @@ public class DefaultFlowExecutionRepositoryTests extends TestCase {
}
public void testParseFlowExecutionKey() {
String key = "c12345v54321";
String key = "e12345s54321";
FlowExecutionKey k = repository.parseFlowExecutionKey(key);
assertEquals(key, k.toString());
}
public void testParseBadlyFormattedFlowExecutionKey() {
String key = "c12345";
String key = "e12345";
try {
repository.parseFlowExecutionKey(key);
fail("Should have failed");
} catch (BadlyFormattedFlowExecutionKeyException e) {
assertEquals("c12345", e.getInvalidKey());
assertEquals("e12345", e.getInvalidKey());
assertNotNull(e.getFormat());
}
}
@@ -81,19 +82,19 @@ public class DefaultFlowExecutionRepositoryTests extends TestCase {
}
public void testGetLock() {
FlowExecutionKey key = repository.parseFlowExecutionKey("c12345v54321");
FlowExecutionKey key = repository.parseFlowExecutionKey("e12345s54321");
FlowExecutionLock lock = repository.getLock(key);
assertNotNull(lock);
lock.unlock();
}
public void testGetLockNoSuchFlowExecution() {
FlowExecutionKey key = repository.parseFlowExecutionKey("cbogusv54321");
FlowExecutionKey key = repository.parseFlowExecutionKey("e99999s54321");
try {
repository.getLock(key);
fail("should have failed");
} catch (NoSuchFlowExecutionException e) {
e.printStackTrace();
}
}
@@ -184,12 +185,16 @@ public class DefaultFlowExecutionRepositoryTests extends TestCase {
}
public ConversationId parseConversationId(String encodedId) throws ConversationException {
return new SimpleConversationId(encodedId);
try {
return new SimpleConversationId(new Integer(Integer.parseInt(encodedId)));
} catch (NumberFormatException e) {
throw new BadlyFormattedConversationIdException(encodedId, e);
}
}
private static class StubConversation implements Conversation {
private final ConversationId ID = new SimpleConversationId("12345");
private final ConversationId ID = new SimpleConversationId(new Integer(12345));
private boolean locked;

View File

@@ -23,7 +23,7 @@ public class CompositeFlowExecutionKeyTests extends TestCase {
public void testToString() {
CompositeFlowExecutionKey key = new CompositeFlowExecutionKey(new SimpleConversationId("1"), "1");
assertEquals("c1v1", key.toString());
assertEquals("e1s1", key.toString());
}
public void testEquals() {