KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > webflow > executor > FlowExecutorImpl


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.executor;
17
18 import org.springframework.binding.mapping.AttributeMapper;
19 import org.springframework.util.Assert;
20 import org.springframework.webflow.context.ExternalContext;
21 import org.springframework.webflow.context.ExternalContextHolder;
22 import org.springframework.webflow.core.FlowException;
23 import org.springframework.webflow.core.collection.LocalAttributeMap;
24 import org.springframework.webflow.core.collection.MutableAttributeMap;
25 import org.springframework.webflow.definition.FlowDefinition;
26 import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
27 import org.springframework.webflow.execution.FlowExecution;
28 import org.springframework.webflow.execution.FlowExecutionFactory;
29 import org.springframework.webflow.execution.ViewSelection;
30 import org.springframework.webflow.execution.repository.FlowExecutionKey;
31 import org.springframework.webflow.execution.repository.FlowExecutionLock;
32 import org.springframework.webflow.execution.repository.FlowExecutionRepository;
33
34 /**
35  * The default implementation of the central facade for <i>driving</i> the
36  * execution of flows within an application.
37  * <p>
38  * This object is responsible for creating and starting new flow executions as
39  * requested by clients, as well as signaling events for processing by existing,
40  * paused executions (that are waiting to be resumed in response to a user
41  * event).
42  * <p>
43  * This object is a facade or entry point into the Spring Web Flow execution
44  * system and makes the overall system easier to use. The name <i>executor</i>
45  * was chosen as <i>executors drive executions</i>.
46  * <p>
47  * <b>Commonly used configurable properties</b><br>
48  * <table border="1">
49  * <tr>
50  * <td><b>name</b></td>
51  * <td><b>description</b></td>
52  * <td><b>default</b></td>
53  * </tr>
54  * <tr>
55  * <td>definitionLocator</td>
56  * <td>The service locator responsible for loading flow definitions to execute.</td>
57  * <td>None</td>
58  * </tr>
59  * <tr>
60  * <td>executionFactory</td>
61  * <td>The factory responsible for creating new flow executions.</td>
62  * <td>None</td>
63  * </tr>
64  * <tr>
65  * <td>executionRepository</td>
66  * <td>The repository responsible for managing flow execution persistence.</td>
67  * <td>None</td>
68  * </tr>
69  * <tr>
70  * <td>inputMapper</td>
71  * <td>The service responsible for mapping attributes of
72  * {@link ExternalContext external contexts} that request to launch new
73  * {@link FlowExecution flow executions}.
74  * After mapping, the target map is then passed to the FlowExecution, exposing
75  * external context attributes as input to the flow during startup.</td>
76  * <td>A
77  * {@link org.springframework.webflow.executor.RequestParameterInputMapper request parameter mapper},
78  * which exposes all request parameters in to the flow execution for input
79  * mapping.</td>
80  * </tr>
81  * </table>
82  * </p>
83  *
84  * @see FlowDefinitionLocator
85  * @see FlowExecutionFactory
86  * @see FlowExecutionRepository
87  * @see AttributeMapper
88  *
89  * @author Erwin Vervaet
90  * @author Keith Donald
91  * @author Colin Sampaleanu
92  */

93 public class FlowExecutorImpl implements FlowExecutor {
94
95     /**
96      * A locator to access flow definitions registered in a central registry.
97      */

98     private FlowDefinitionLocator definitionLocator;
99
100     /**
101      * An abstract factory for creating a new execution of a flow definition.
102      */

103     private FlowExecutionFactory executionFactory;
104
105     /**
106      * An repository used to save, update, and load existing flow executions
107      * to/from a persistent store.
108      */

109     private FlowExecutionRepository executionRepository;
110
111     /**
112      * The service responsible for mapping attributes of an
113      * {@link ExternalContext} to a new {@link FlowExecution} during the
114      * {@link #launch(String, ExternalContext) launch flow} operation.
115      * <p>
116      * This allows developers to control what attributes are made available in
117      * the <code>inputMap</code> to new top-level flow executions. The
118      * starting execution may then choose to map that available input into its
119      * own local scope.
120      * <p>
121      * The default implementation simply exposes all request parameters as flow
122      * execution input attributes. May be null.
123      */

124     private AttributeMapper inputMapper = new RequestParameterInputMapper();
125
126     /**
127      * Create a new flow executor.
128      * @param definitionLocator the locator for accessing flow definitions to
129      * execute
130      * @param executionFactory the factory for creating executions of flow
131      * definitions
132      * @param executionRepository the repository for persisting paused flow
133      * executions
134      */

135     public FlowExecutorImpl(FlowDefinitionLocator definitionLocator, FlowExecutionFactory executionFactory,
136             FlowExecutionRepository executionRepository) {
137         Assert.notNull(definitionLocator, "The locator for accessing flow definitions is required");
138         Assert.notNull(executionFactory, "The execution factory for creating new flow executions is required");
139         Assert.notNull(executionRepository, "The repository for persisting flow executions is required");
140         this.definitionLocator = definitionLocator;
141         this.executionFactory = executionFactory;
142         this.executionRepository = executionRepository;
143     }
144
145     /**
146      * Exposes the configured input mapper to subclasses and privileged
147      * accessors.
148      * @return the input mapper
149      */

150     public AttributeMapper getInputMapper() {
151         return inputMapper;
152     }
153
154     /**
155      * Set the service responsible for mapping attributes of an
156      * {@link ExternalContext} to a new {@link FlowExecution} during the
157      * {@link #launch(String, ExternalContext) launch flow} operation.
158      * <p>
159      * The default implementation simply exposes all request parameters as flow
160      * execution input attributes. May be null.
161      * @see RequestParameterInputMapper
162      */

163     public void setInputMapper(AttributeMapper inputMapper) {
164         this.inputMapper = inputMapper;
165     }
166
167     /**
168      * Exposes the configured flow definition locator to subclasses and
169      * privileged accessors.
170      * @return the flow definition locator
171      */

172     public FlowDefinitionLocator getDefinitionLocator() {
173         return definitionLocator;
174     }
175
176     /**
177      * Exposes the configured execution factory to subclasses and privileged
178      * accessors.
179      * @return the execution factory
180      */

181     public FlowExecutionFactory getExecutionFactory() {
182         return executionFactory;
183     }
184
185     /**
186      * Exposes the execution repository to subclasses and privileged accessors.
187      * @return the execution repository
188      */

189     public FlowExecutionRepository getExecutionRepository() {
190         return executionRepository;
191     }
192
193     public ResponseInstruction launch(String JavaDoc flowDefinitionId, ExternalContext context) throws FlowException {
194         // expose external context as a thread-bound service
195
ExternalContextHolder.setExternalContext(context);
196         try {
197             FlowDefinition flowDefinition = definitionLocator.getFlowDefinition(flowDefinitionId);
198             FlowExecution flowExecution = executionFactory.createFlowExecution(flowDefinition);
199             ViewSelection selectedView = flowExecution.start(createInput(context), context);
200             if (flowExecution.isActive()) {
201                 // execution still active => store it in the repository
202
FlowExecutionKey key = executionRepository.generateKey(flowExecution);
203                 executionRepository.putFlowExecution(key, flowExecution);
204                 return new ResponseInstruction(key.toString(), flowExecution, selectedView);
205             }
206             else {
207                 // execution already ended => just render the selected view
208
return new ResponseInstruction(flowExecution, selectedView);
209             }
210         }
211         finally {
212             ExternalContextHolder.setExternalContext(null);
213         }
214     }
215
216     public ResponseInstruction resume(String JavaDoc flowExecutionKey, String JavaDoc eventId, ExternalContext context)
217             throws FlowException {
218         // expose external context as a thread-bound service
219
ExternalContextHolder.setExternalContext(context);
220         try {
221             FlowExecutionKey key = executionRepository.parseFlowExecutionKey(flowExecutionKey);
222             FlowExecutionLock lock = executionRepository.getLock(key);
223             // make sure we're the only one manipulating the flow execution
224
lock.lock();
225             try {
226                 FlowExecution flowExecution = executionRepository.getFlowExecution(key);
227                 ViewSelection selectedView = flowExecution.signalEvent(eventId, context);
228                 if (flowExecution.isActive()) {
229                     // execution still active => store it in the repository
230
key = executionRepository.getNextKey(flowExecution, key);
231                     executionRepository.putFlowExecution(key, flowExecution);
232                     return new ResponseInstruction(key.toString(), flowExecution, selectedView);
233                 }
234                 else {
235                     // execution ended => remove it from the repository
236
executionRepository.removeFlowExecution(key);
237                     return new ResponseInstruction(flowExecution, selectedView);
238                 }
239             }
240             finally {
241                 lock.unlock();
242             }
243         }
244         finally {
245             ExternalContextHolder.setExternalContext(null);
246         }
247     }
248
249     public ResponseInstruction refresh(String JavaDoc flowExecutionKey, ExternalContext context) throws FlowException {
250         // expose external context as a thread-bound service
251
ExternalContextHolder.setExternalContext(context);
252         try {
253             FlowExecutionKey key = executionRepository.parseFlowExecutionKey(flowExecutionKey);
254             FlowExecutionLock lock = executionRepository.getLock(key);
255             // make sure we're the only one manipulating the flow execution
256
lock.lock();
257             try {
258                 FlowExecution flowExecution = executionRepository.getFlowExecution(key);
259                 ViewSelection selectedView = flowExecution.refresh(context);
260                 // don't generate a new key for a refresh, just update
261
// the flow execution with it's existing key
262
executionRepository.putFlowExecution(key, flowExecution);
263                 return new ResponseInstruction(key.toString(), flowExecution, selectedView);
264             }
265             finally {
266                 lock.unlock();
267             }
268         }
269         finally {
270             ExternalContextHolder.setExternalContext(null);
271         }
272     }
273
274     // helper methods
275

276     /**
277      * Factory method that creates the input attribute map for a newly created
278      * {@link FlowExecution}. This implementation uses the registered input mapper,
279      * if any.
280      * @param context the external context
281      * @return the input map, or null if no input
282      */

283     protected MutableAttributeMap createInput(ExternalContext context) {
284         if (inputMapper != null) {
285             MutableAttributeMap inputMap = new LocalAttributeMap();
286             inputMapper.map(context, inputMap, null);
287             return inputMap;
288         }
289         else {
290             return null;
291         }
292     }
293 }
Popular Tags