KickJava   Java API By Example, From Geeks To Geeks.

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


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.Conversation;
22 import org.springframework.webflow.conversation.ConversationException;
23 import org.springframework.webflow.conversation.ConversationId;
24 import org.springframework.webflow.conversation.ConversationManager;
25 import org.springframework.webflow.conversation.ConversationParameters;
26 import org.springframework.webflow.conversation.NoSuchConversationException;
27 import org.springframework.webflow.core.collection.MutableAttributeMap;
28 import org.springframework.webflow.definition.FlowDefinition;
29 import org.springframework.webflow.execution.FlowExecution;
30 import org.springframework.webflow.execution.repository.BadlyFormattedFlowExecutionKeyException;
31 import org.springframework.webflow.execution.repository.FlowExecutionKey;
32 import org.springframework.webflow.execution.repository.FlowExecutionLock;
33 import org.springframework.webflow.execution.repository.FlowExecutionRepositoryException;
34 import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException;
35
36 /**
37  * A convenient base class for flow execution repository implementations that delegate
38  * to a conversation service for managing conversations that govern the
39  * persistent state of paused flow executions.
40  *
41  * @see ConversationManager
42  *
43  * @author Keith Donald
44  */

45 public abstract class AbstractConversationFlowExecutionRepository extends AbstractFlowExecutionRepository {
46
47     /**
48      * The conversation attribute holding conversation scope ("scope").
49      */

50     private static final String JavaDoc SCOPE_ATTRIBUTE = "scope";
51
52     /**
53      * The conversation service to delegate to for managing conversations
54      * initiated by this repository.
55      */

56     private ConversationManager conversationManager;
57
58     /**
59      * Constructor for use in subclasses.
60      * @param executionStateRestorer the transient flow execution state restorer
61      * @param conversationManager the conversation manager to use
62      */

63     protected AbstractConversationFlowExecutionRepository(FlowExecutionStateRestorer executionStateRestorer,
64             ConversationManager conversationManager) {
65         super(executionStateRestorer);
66         setConversationManager(conversationManager);
67     }
68
69     /**
70      * Returns the configured conversation manager.
71      */

72     protected ConversationManager getConversationManager() {
73         return conversationManager;
74     }
75
76     /**
77      * Sets the conversation manager to use.
78      * @param conversationManager the conversation service, may not be null
79      */

80     private void setConversationManager(ConversationManager conversationManager) {
81         Assert.notNull(conversationManager, "The conversation manager is required");
82         this.conversationManager = conversationManager;
83     }
84         
85     public FlowExecutionKey generateKey(FlowExecution flowExecution) {
86         // we need to generate a key for a new flow execution, so a new conversation has
87
// started
88
ConversationParameters parameters = createConversationParameters(flowExecution);
89         Conversation conversation = conversationManager.beginConversation(parameters);
90         onBegin(conversation);
91         return new CompositeFlowExecutionKey(conversation.getId(), generateContinuationId(flowExecution));
92     }
93
94     public FlowExecutionKey getNextKey(FlowExecution flowExecution, FlowExecutionKey previousKey) {
95         CompositeFlowExecutionKey key = (CompositeFlowExecutionKey)previousKey;
96         // the conversation id remains the same for the life of the flow execution
97
// but the continuation id changes
98
return new CompositeFlowExecutionKey(key.getConversationId(), generateContinuationId(flowExecution));
99     }
100
101     public FlowExecutionLock getLock(FlowExecutionKey key) throws FlowExecutionRepositoryException {
102         // lock the entire conversation
103
return new ConversationBackedFlowExecutionLock(getConversation(key));
104     }
105
106     public abstract FlowExecution getFlowExecution(FlowExecutionKey key) throws FlowExecutionRepositoryException;
107
108     public abstract void putFlowExecution(FlowExecutionKey key, FlowExecution flowExecution)
109             throws FlowExecutionRepositoryException;
110
111     public void removeFlowExecution(FlowExecutionKey key) throws FlowExecutionRepositoryException {
112         // end the governing conversation
113
Conversation conversation = getConversation(key);
114         conversation.end();
115         onEnd(conversation);
116     }
117
118     public FlowExecutionKey parseFlowExecutionKey(String JavaDoc encodedKey) throws FlowExecutionRepositoryException {
119         Assert.hasText(encodedKey, "The string encoded flow execution key is required");
120         String JavaDoc[] keyParts = CompositeFlowExecutionKey.keyParts(encodedKey);
121         
122         // parse out the conversation id
123
ConversationId conversationId;
124         try {
125             conversationId = conversationManager.parseConversationId(keyParts[0]);
126         }
127         catch (ConversationException e) {
128             throw new BadlyFormattedFlowExecutionKeyException(encodedKey,
129                     "The conversation id '" + keyParts[0] + "' contained in the composite flow execution key '"
130                     + encodedKey + "' is invalid", e);
131         }
132         
133         // parse out the continuation id
134
Serializable JavaDoc continuationId;
135         try {
136             continuationId = parseContinuationId(keyParts[1]);
137         }
138         catch (FlowExecutionRepositoryException e) {
139             throw new BadlyFormattedFlowExecutionKeyException(encodedKey,
140                     "The continuation id '" + keyParts[1] + "' contained in the composite flow execution key '"
141                     + encodedKey + "' is invalid", e);
142         }
143         
144         return new CompositeFlowExecutionKey(conversationId, continuationId);
145     }
146
147     // overridable hooks for use in subclasses
148

149     /**
150      * Factory method that maps a new flow execution to a descriptive
151      * {@link ConversationParameters conversation parameters} object.
152      * @param flowExecution the new flow execution
153      * @return the conversation parameters object to pass to the conversation
154      * manager when the conversation is started
155      */

156     protected ConversationParameters createConversationParameters(FlowExecution flowExecution) {
157         FlowDefinition flow = flowExecution.getDefinition();
158         return new ConversationParameters(flow.getId(), flow.getCaption(), flow.getDescription());
159     }
160
161     /**
162      * An "on begin conversation" callback, allowing for insertion of custom
163      * logic after a new conversation has begun.
164      * This implementation is emtpy.
165      * @param conversation the conversation that has begun
166      */

167     protected void onBegin(Conversation conversation) {
168     }
169     
170     /**
171      * An "on conversation end" callback, allowing for insertion of custom logic
172      * after a conversation has ended (it's {@link Conversation#end()} method has been
173      * called).
174      * This implementation is empty.
175      * @param conversation the conversation that has ended
176      */

177     protected void onEnd(Conversation conversation) {
178     }
179
180     /**
181      * Returns the conversation id part of given composite flow execution key.
182      * @param key the composite key
183      * @return the conversationId key part
184      */

185     protected ConversationId getConversationId(FlowExecutionKey key) {
186         return ((CompositeFlowExecutionKey)key).getConversationId();
187     }
188
189     /**
190      * Returns the continuation id part of given composite flow execution key.
191      * @param key the composite key
192      * @return the continuation id key part
193      */

194     protected Serializable JavaDoc getContinuationId(FlowExecutionKey key) {
195         return ((CompositeFlowExecutionKey)key).getContinuationId();
196     }
197
198     /**
199      * Returns the conversation governing the execution of the
200      * {@link FlowExecution} with the provided key.
201      * @param key the flow execution key
202      * @return the governing conversation
203      * @throws NoSuchFlowExecutionException when the conversation for identified
204      * flow execution cannot be found
205      */

206     protected Conversation getConversation(FlowExecutionKey key) throws NoSuchFlowExecutionException {
207         try {
208             return getConversationManager().getConversation(getConversationId(key));
209         }
210         catch (NoSuchConversationException e) {
211             throw new NoSuchFlowExecutionException(key, e);
212         }
213     }
214
215     /**
216      * Returns the "conversation scope" for the flow execution with the
217      * key provided. This is mainly useful for reinitialisation of a flow execution
218      * ofter restoration from the repository.
219      * @param key the flow execution key
220      * @return the execution's conversation scope
221      */

222     protected MutableAttributeMap getConversationScope(FlowExecutionKey key) {
223         return (MutableAttributeMap)getConversation(key).getAttribute(SCOPE_ATTRIBUTE);
224     }
225
226     /**
227      * Sets the conversation scope attribute for the flow execution with the key
228      * provided.
229      * @param key the flow execution key
230      * @param scope the execution's conversation scope
231      */

232     protected void putConversationScope(FlowExecutionKey key, MutableAttributeMap scope) {
233         Assert.notNull(scope, "The conversation scope attribute map is required");
234         getConversation(key).putAttribute(SCOPE_ATTRIBUTE, scope);
235     }
236
237     // abstract template methods
238

239     /**
240      * Template method used to generate a new continuation id for given flow
241      * execution. Subclasses must override.
242      * @param flowExecution the flow execution
243      * @return the continuation id
244      */

245     protected abstract Serializable JavaDoc generateContinuationId(FlowExecution flowExecution);
246
247     /**
248      * Template method to parse the continuation id from the encoded string.
249      * @param encodedId the string identifier
250      * @return the parsed continuation id
251      */

252     protected abstract Serializable JavaDoc parseContinuationId(String JavaDoc encodedId) throws FlowExecutionRepositoryException;
253
254 }
Popular Tags