diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java index 18ed81c7..e0cd8195 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java @@ -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; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/AbstractFlowExecutionContinuationRepository.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/AbstractSnapshottingFlowExecutionRepository.java similarity index 51% rename from spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/AbstractFlowExecutionContinuationRepository.java rename to spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/AbstractSnapshottingFlowExecutionRepository.java index 8c8ba635..032a753d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/AbstractFlowExecutionContinuationRepository.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/AbstractSnapshottingFlowExecutionRepository.java @@ -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(); } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationNotFoundException.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationNotFoundException.java deleted file mode 100644 index 2a5072e0..00000000 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationNotFoundException.java +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionContinuation.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionSnapshot.java similarity index 71% rename from spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionContinuation.java rename to spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionSnapshot.java index f7b85991..ad6aa7aa 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionContinuation.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionSnapshot.java @@ -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(); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionContinuationFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionSnapshotFactory.java similarity index 52% rename from spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionContinuationFactory.java rename to spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionSnapshotFactory.java index 91383bea..d8fe1630 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionContinuationFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/FlowExecutionSnapshotFactory.java @@ -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; } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuation.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionSnapshot.java similarity index 81% rename from spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuation.java rename to spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionSnapshot.java index 34dbf2f2..79d9fedf 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuation.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionSnapshot.java @@ -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. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuationFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionSnapshotFactory.java similarity index 57% rename from spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuationFactory.java rename to spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionSnapshotFactory.java index d817e9cf..3ea9378f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuationFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionSnapshotFactory.java @@ -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); } } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationCreationException.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotCreationException.java similarity index 82% rename from spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationCreationException.java rename to spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotCreationException.java index 1c94f059..d546cd2a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationCreationException.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotCreationException.java @@ -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; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotNotFoundException.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotNotFoundException.java new file mode 100644 index 00000000..85297fc6 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotNotFoundException.java @@ -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; + } +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationUnmarshalException.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotUnmarshalException.java similarity index 83% rename from spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationUnmarshalException.java rename to spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotUnmarshalException.java index 72ed845f..bf96d70c 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/ContinuationUnmarshalException.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/continuation/SnapshotUnmarshalException.java @@ -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); } } \ No newline at end of file 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 6af51ee2..95c39971 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 @@ -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 one to many 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 one to {@link #getMaxSnapshots() max} flow + * execution snapshots, where each snapshot represents a copy of a {@link FlowExecution} taken at a point in time. *

- * 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. *

* This repository is responsible for: *

*

- * This repository implementation also provides support for conversation invalidation after completion, 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. - *

- * 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 execution invalidation after completion, 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; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionContinuationGroup.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionContinuationGroup.java deleted file mode 100644 index b66010e1..00000000 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionContinuationGroup.java +++ /dev/null @@ -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 id, or null 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()); - } - -} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionSnapshotGroup.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionSnapshotGroup.java new file mode 100644 index 00000000..497ce29b --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionSnapshotGroup.java @@ -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 id, or null 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(); + +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/SimpleFlowExecutionSnapshotGroup.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/SimpleFlowExecutionSnapshotGroup.java new file mode 100644 index 00000000..d51bdded --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/SimpleFlowExecutionSnapshotGroup.java @@ -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()); + } + +} \ No newline at end of file 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 37c6f767..564b3416 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 @@ -46,7 +46,7 @@ import org.springframework.webflow.execution.repository.NoSuchFlowExecutionExcep * rehydrate a flow execution after it has been obtained from storage from resume. *

* 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) { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKey.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKey.java index c3b4231c..7314e543 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKey.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKey.java @@ -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: *

    - *
  1. A conversationId, identifying an active conversation managed by a {@link ConversationManager}. - *
  2. A continuationId, identifying a restorable {@link FlowExecutionContinuation} within a continuation group - * governed by that conversation. + *
  3. A executionId, identifying a logical {@link FlowExecution} that is running. + *
  4. A snapshotId, identifying a physical flow execution snapshot that can be restored. *
- *

- * 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 + "" + CONTINUATION_ID_PREFIX - + ""; + private static final String FORMAT = EXECUTION_ID_PREFIX + "" + SNAPSHOT_ID_PREFIX + ""; - /** - * 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 }; } } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuationFactoryTests.java b/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuationFactoryTests.java index 5ae83ae5..3d9d5c14 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuationFactoryTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/continuation/SerializedFlowExecutionContinuationFactoryTests.java @@ -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); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepositoryTests.java b/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepositoryTests.java index ad2d26b0..ea449ba3 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepositoryTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepositoryTests.java @@ -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; diff --git a/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKeyTests.java b/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKeyTests.java index 6b8fb906..eeea938d 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKeyTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/execution/repository/support/CompositeFlowExecutionKeyTests.java @@ -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() {