A flow execution represents an executing flow at a point in time. At runtime there can be any number of flow executions active in parallel. A single user can even have multiple executions active at the same time (for example, when a user is operating multiple windows or tabs within their browser).
Many of these flow executions span multiple requests into the server and therefore must be saved so they can be resumed on subsequent requests. This presents technical challenges, as there must exist a stable mechanism for a new request to be associated with an existing execution in the view state that matches what the user expects. This problem is more difficult when you consider that many applications require use of browser navigational buttons, and use of these buttons involves updating local history without notifying the server.
The problem of flow execution persistence is addressed by Spring Web Flow's flow execution repository subsystem. In this chapter, you will learn how to use the system to manage the storage of active web conversations in a stable manner.
Recall the following bullet points noting what happens when a flow execution enters a ViewState:
When a flow execution reaches a ViewState it is said to have paused, where it waits in that state for user input to be provided so it can continue. After pausing, the ViewSelection returned is used to issue a response to the user that provides a vehicle for collecting the required user input.
User input is provided by signaling an event that resumes the flow execution in the paused view state. The input event communicates what user action was taken.
Each time an active flow execution is paused, it is saved out to a repository. When the next request comes in for that flow execution, it is restored from the repository, resumed, and continued. This process continues until the flow execution reaches an end state, at which time it is removed from the repository.
This process is demonstrated over the next two graphics:
When a new flow execution is created, it marks the start of a new conversation between a browser and the server. A new flow execution that is still active after startup processing indicates the start of a conversation that will span more than one request and needs to be persisted. When this is the case, that flow execution is assigned an persistent identifer by the repository. By default, the structure of this identifier consists of a two-part composite key. This key is used by clients to restore the flow execution on subsequent requests.
The first part of a flow execution's persistent identity is a unique conversation identifier. This serves as an index into the logical conversation between the browser and the server that has just started.
The second part of a flow execution's persistent identity is a continuation identifier. This identifier serves as an index into a flow execution representing the state of the conversation at this point in time.
Together the conversation id plus the continuation id make up the unique two-part flow execution key that identifies a state of a conversation at a point in time. By submitting this key in a subsequent request, a browser can restore the conversation at that point and continue from there.
So on a subsequent request, the conversation is resumed by restoring a flow execution from the repository using the two-part key. After event processing, if the flow execution is still active it is saved back out to the repository. At this time a new flow execution key is generated. By default, that key retains the same conversation identifier, as the same logical conversation is in progress; however the continuation identifier changes to provide an index into the state of the flow execution at this new point in time.
By submitting this new key in a subsequent request, a browser can restore the conversation at that point and continue from there. This process continues until a flow execution reaches an end state during event processing signaling the end of the conversation.
When a flow execution reaches an end state it terminates. If the flow execution was associated with a logical conversation that spanned more than on request, it is removed from the repository. More specifically, the entire conversation is ended, resulting in any flow execution continuations associated with the conversation being purged.
Once a conversation has ended, the conversation identifier is no longer valid and can never be used again.
The next section looks at the repository implementations that are available for use with Spring Web Flow out-of-the-box.
The simplest possible repository (SimpleFlowExecutionRepository). This repository stores exactly one flow execution instance per conversation in the user's session, invalidating it when its end state is reached. This repository implementation has been designed with minimal storage overhead in mind.
It is important to understand that use of this repository consistently prevents duplicate submission when using the back button. If you attempt to go back and resubmit, the continuation id stored in your browser history will not match the current continuation id needed to access the flow execution and access will be disallowed.
This repository implementation should generally be used when you do not have to support browser navigational button use; for example, when you lock down the browser and require that all navigation events to be routed through Spring Web Flow.
This repository (ContinuationFlowExecutionRepository) stores one to many flow execution instances per conversation in the user's session, where each flow execution represents a paused and restorable state of the conversation at a point in time. This repository implementation is considerably more flexible than the simple one, but incurs more storage overhead.
It is important to understand that use of this repository allows resubmission when using the back button. If you attempt to go back and resubmit while the conversation is active, the continuation id stored in your browser history will match the continuation id of a previous flow execution in the repository. Access to that flow execution representing the state of the conversation at that point in time will be granted.
Like the simple implementation, this repository implementation provides support for conversation invalidation after completion where once a logical conversation completes (by one of its FlowExecutions reaching an end state), the entire conversation is invalidated. This prevents the possibility of resubmission after completion.
This repository is more elaborate than the default repository, offering more power (by enabling multiple continuations to exist per conversation), but incurring more storage overhead. This repository implementation should be considered when you do have to support browser navigational button use. This implementation is the default.
This repository is entirely stateless and its use entails no server-side state (ClientContinuationFlowExecutionRepository).
This is achieved by encoding a serialized flow execution directly into the flow execution continuation key that is sent in the response.
When asked to load a flow execution by its key on a subsequent request, this repository decodes and deserializes the flow execution, restoring it to the state it was in when it was serialized.
This repository implementation does not currently support conversation invalidation after completion, as this capability requires tracking active conversations using some form of centralized storage, like a database table.
Storing state (a flow execution continuation) on the client entails a certain security risk that should be evaluated. Furthermore, it puts practical constraints on the size of the flow execution.