KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > webflow > execution > repository > support > SimpleFlowExecutionRepository


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.webflow.execution.repository.support;
17
18 import java.io.Serializable JavaDoc;
19
20 import org.springframework.util.Assert;
21 import org.springframework.webflow.conversation.ConversationManager;
22 import org.springframework.webflow.execution.FlowExecution;
23 import org.springframework.webflow.execution.repository.FlowExecutionKey;
24 import org.springframework.webflow.execution.repository.PermissionDeniedFlowExecutionAccessException;
25 import org.springframework.webflow.util.RandomGuidUidGenerator;
26 import org.springframework.webflow.util.UidGenerator;
27
28 /**
29  * Conversation manager based flow execution repository that stores
30  * <i>exactly one</i> flow execution per conversation.
31  * <p>
32  * It is important to note that by default use of this repository <b>does not</b>
33  * allow for duplicate submission in conjunction with browser navigational buttons
34  * (such as the back button). Specifically, if you attempt to "go back" and resubmit,
35  * the continuation id stored on the page in your browser history will <b>not</b>
36  * match the continuation id of the flow execution entry and access to the
37  * conversation will be disallowed. This is because the
38  * <code>continuationId</code> changes on each request to consistently prevent
39  * the possibility of duplicate submission ({@link #setAlwaysGenerateNewNextKey(boolean)}).
40  * <p>
41  * This repository is specifically designed to be 'simple': incurring minimal
42  * resources and overhead, as only one {@link FlowExecution} is stored <i>per
43  * conversation</i>. This repository implementation should only be used
44  * when you do not have to support browser navigational button use, e.g. you
45  * lock down the browser and require that all navigational events to be routed
46  * explicitly through Spring Web Flow.
47  *
48  * @author Erwin Vervaet
49  * @author Keith Donald
50  */

51 public class SimpleFlowExecutionRepository extends AbstractConversationFlowExecutionRepository {
52
53     /**
54      * The conversation attribute holding the flow execution entry.
55      */

56     private static final String JavaDoc FLOW_EXECUTION_ENTRY_ATTRIBUTE = "flowExecutionEntry";
57
58     /**
59      * Flag to indicate whether or not a new flow execution key should always be
60      * generated before each put call. Default is true.
61      */

62     private boolean alwaysGenerateNewNextKey = true;
63     
64     /**
65      * The uid generation strategy to use.
66      */

67     private UidGenerator continuationIdGenerator = new RandomGuidUidGenerator();
68
69     /**
70      * Create a new simple repository using given state restorer and conversation manager.
71      * @param executionStateRestorer the flow execution state restoration strategy to use
72      * @param conversationManager the conversation manager to use
73      */

74     public SimpleFlowExecutionRepository(FlowExecutionStateRestorer executionStateRestorer,
75             ConversationManager conversationManager) {
76         super(executionStateRestorer, conversationManager);
77     }
78
79     /**
80      * Returns whether or not a new flow execution key should always be
81      * generated before each put call. Default is true.
82      */

83     public boolean isAlwaysGenerateNewNextKey() {
84         return alwaysGenerateNewNextKey;
85     }
86
87     /**
88      * Sets a flag indicating if a new {@link FlowExecutionKey} should always be
89      * generated before each put call. By setting this to false a FlowExecution
90      * can remain identified by the same key throughout its life.
91      */

92     public void setAlwaysGenerateNewNextKey(boolean alwaysGenerateNewNextKey) {
93         this.alwaysGenerateNewNextKey = alwaysGenerateNewNextKey;
94     }
95
96     /**
97      * Returns the uid generation strategy used to generate continuation
98      * identifiers. Defaults to {@link RandomGuidUidGenerator}.
99      */

100     public UidGenerator getContinuationIdGenerator() {
101         return continuationIdGenerator;
102     }
103
104     /**
105      * Sets the uid generation strategy used to generate unique continuation
106      * identifiers for {@link FlowExecutionKey flow execution keys}.
107      */

108     public void setContinuationIdGenerator(UidGenerator continuationIdGenerator) {
109         Assert.notNull(continuationIdGenerator, "The continuation id generator is required");
110         this.continuationIdGenerator = continuationIdGenerator;
111     }
112
113     public FlowExecutionKey getNextKey(FlowExecution flowExecution, FlowExecutionKey previousKey) {
114         if (isAlwaysGenerateNewNextKey()) {
115             return super.getNextKey(flowExecution, previousKey);
116         }
117         else {
118             return previousKey;
119         }
120     }
121
122     public FlowExecution getFlowExecution(FlowExecutionKey key) {
123         try {
124             FlowExecution execution = getEntry(key).access(getContinuationId(key));
125             // it could be that the entry was serialized out and read back in, so
126
// we need to restore transient flow execution state
127
return getExecutionStateRestorer().restoreState(execution, getConversationScope(key));
128         }
129         catch (InvalidContinuationIdException e) {
130             throw new PermissionDeniedFlowExecutionAccessException(key, e);
131         }
132     }
133
134     public void putFlowExecution(FlowExecutionKey key, FlowExecution flowExecution) {
135         FlowExecutionEntry entry = new FlowExecutionEntry(getContinuationId(key), flowExecution);
136         putEntry(key, entry);
137         putConversationScope(key, flowExecution.getConversationScope());
138     }
139
140     protected Serializable JavaDoc generateContinuationId(FlowExecution flowExecution) {
141         return continuationIdGenerator.generateUid();
142     }
143
144     protected Serializable JavaDoc parseContinuationId(String JavaDoc encodedId) {
145         return continuationIdGenerator.parseUid(encodedId);
146     }
147     
148     // internal helpers
149

150     /**
151      * Lookup the entry for keyed flow execution in the governing conversation.
152      */

153     private FlowExecutionEntry getEntry(FlowExecutionKey key) {
154         FlowExecutionEntry entry =
155             (FlowExecutionEntry)getConversation(key).getAttribute(FLOW_EXECUTION_ENTRY_ATTRIBUTE);
156         if (entry == null) {
157             throw new IllegalStateException JavaDoc("No '" + FLOW_EXECUTION_ENTRY_ATTRIBUTE
158                     + "' attribute present in the governing conversation: "
159                     + "possible programmer error -- do not call get before calling put");
160         }
161         return entry;
162     }
163
164     /**
165      * Store given flow execution entry in the governing conversation using given key.
166      * @param key the key to use
167      * @param entry the entry to store
168      */

169     private void putEntry(FlowExecutionKey key, FlowExecutionEntry entry) {
170         getConversation(key).putAttribute(FLOW_EXECUTION_ENTRY_ATTRIBUTE, entry);
171     }
172
173     /**
174      * Simple holder for a flow execution. In order to access the held flow
175      * execution you must present a valid continuationId.
176      *
177      * @author Keith Donald
178      */

179     private static class FlowExecutionEntry implements Serializable JavaDoc {
180
181         /**
182          * The id required to access the execution.
183          */

184         private Serializable JavaDoc continuationId;
185
186         /**
187          * The flow execution.
188          */

189         private FlowExecution flowExecution;
190
191         /**
192          * Creates a new flow execution entry.
193          * @param continuationId the continuation id
194          * @param flowExecution the flow execution
195          */

196         public FlowExecutionEntry(Serializable JavaDoc continuationId, FlowExecution flowExecution) {
197             this.continuationId = continuationId;
198             this.flowExecution = flowExecution;
199         }
200
201         /**
202          * Access the wrapped flow execution, using given continuation id as a <i>password</i>.
203          * @param continuationId the continuation id to match
204          * @return the flow execution
205          * @throws InvalidContinuationIdException given continuation id does not match the
206          * continuation id stored in this entry
207          */

208         public FlowExecution access(Serializable JavaDoc continuationId) throws InvalidContinuationIdException {
209             if (!this.continuationId.equals(continuationId)) {
210                 throw new InvalidContinuationIdException(continuationId);
211             }
212             return flowExecution;
213         }
214     }
215 }
Popular Tags