KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > webflow > executor > struts > FlowAction


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.struts;
17
18 import java.util.HashMap JavaDoc;
19 import java.util.Map JavaDoc;
20
21 import javax.servlet.http.HttpServletRequest JavaDoc;
22 import javax.servlet.http.HttpServletResponse JavaDoc;
23
24 import org.apache.struts.action.ActionForm;
25 import org.apache.struts.action.ActionForward;
26 import org.apache.struts.action.ActionMapping;
27 import org.springframework.validation.Errors;
28 import org.springframework.web.context.WebApplicationContext;
29 import org.springframework.web.struts.ActionSupport;
30 import org.springframework.web.struts.DelegatingActionProxy;
31 import org.springframework.web.struts.SpringBindingActionForm;
32 import org.springframework.web.util.WebUtils;
33 import org.springframework.webflow.action.FormObjectAccessor;
34 import org.springframework.webflow.context.ExternalContext;
35 import org.springframework.webflow.execution.support.ApplicationView;
36 import org.springframework.webflow.execution.support.ExternalRedirect;
37 import org.springframework.webflow.execution.support.FlowDefinitionRedirect;
38 import org.springframework.webflow.executor.FlowExecutor;
39 import org.springframework.webflow.executor.ResponseInstruction;
40 import org.springframework.webflow.executor.support.FlowExecutorArgumentHandler;
41 import org.springframework.webflow.executor.support.FlowRequestHandler;
42 import org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler;
43
44 /**
45  * Point of integration between Struts and Spring Web Flow: a Struts Action that
46  * acts a front controller entry point into the web flow system. A single
47  * FlowAction may launch any new FlowExecution. In addition, a single Flow
48  * Action may signal events in any existing/restored FlowExecutions.
49  * <p>
50  * Requests are managed by and delegated to a {@link FlowExecutor}, which this
51  * class delegates to using a {@link FlowRequestHandler} (allowing reuse of
52  * common front flow controller logic in other environments). Consult the
53  * JavaDoc of those classes for more information on how requests are processed.
54  * <p>
55  * <li>By default, to have this controller launch a new flow execution
56  * (conversation), have the client send a
57  * {@link FlowExecutorArgumentHandler#getFlowIdArgumentName()} request
58  * parameter indicating the flow definition to launch.
59  * <li>To have this controller participate in an existing flow execution
60  * (conversation), have the client send a
61  * {@link FlowExecutorArgumentHandler#getFlowExecutionKeyArgumentName()}
62  * request parameter identifying the conversation to participate in.
63  * <p>
64  * On each request received by this action, a {@link StrutsExternalContext}
65  * object is created as input to the web flow system. This external source event
66  * provides access to the action form, action mapping, and other Struts-specific
67  * constructs.
68  * <p>
69  * This class also is aware of the {@link SpringBindingActionForm} adapter,
70  * which adapts Spring's data binding infrastructure (based on POJO binding, a
71  * standard Errors interface, and property editor type conversion) to the Struts
72  * action form model. This option gives backend web-tier developers full support
73  * for POJO-based binding with minimal hassel, while still providing consistency
74  * to view developers who already have a lot of experience with Struts for
75  * markup and request dispatching.
76  * <p>
77  * Below is an example <code>struts-config.xml</code> configuration for a
78  * FlowAction:
79  *
80  * <pre>
81  * &lt;action path=&quot;/userRegistration&quot;
82  * type=&quot;org.springframework.webflow.executor.struts.FlowAction&quot;
83  * name=&quot;springBindingActionForm&quot; scope=&quot;request&quot;&gt;
84  * &lt;/action&gt;
85  * </pre>
86  *
87  * This example maps the logical request URL <code>/userRegistration.do</code>
88  * as a Flow controller (<code>FlowAction</code>). It is expected that flows
89  * to launch be provided in a dynamic fashion by the views (allowing this single
90  * <code>FlowAction</code> to manage any number of flow executions). A Spring
91  * binding action form instance is set in request scope, acting as an adapter
92  * enabling POJO-based binding and validation with Spring.
93  * <p>
94  * Other notes regarding Struts/Spring Web Flow integration:
95  * <ul>
96  * <li>Logical view names returned when <code>ViewStates</code> and
97  * <code>EndStates</code> are entered are mapped to physical view templates
98  * using standard Struts action forwards (typically global forwards).</li>
99  * <li>Use of the <code>SpringBindingActionForm</code> requires no special
100  * setup in <code>struts-config.xml</code>: simply declare a form bean in
101  * request scope of the class
102  * <code>org.springframework.web.struts.SpringBindingActionForm</code> and use
103  * it with your FlowAction.</li>
104  * <li>This class depends on a {@link FlowExecutor} instance to be configured.
105  * If relying on Spring's {@link DelegatingActionProxy} (which is recommended),
106  * a FlowExecutor reference can simply be injected using standard Spring
107  * dependency injection techniques. If you are not using the proxy-based
108  * approach, this class will attempt a root context lookup on initialization,
109  * first querying for a bean of instance {@link FlowExecutor} named
110  * {@link #FLOW_EXECUTOR_BEAN_NAME}.</li>
111  * <li>The
112  * {@link org.springframework.webflow.executor.support.FlowExecutorArgumentHandler}
113  * used by the FlowAction can be configured in the root context using a bean of
114  * name {@link #FLOW_EXECUTOR_ARGUMENT_HANDLER_BEAN_NAME}. If not explicitly
115  * specified it will default to a normal
116  * {@link org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler}
117  * with standard configuration.</li>
118  * </ul>
119  * <p>
120  * The benefits here are considerable: developers now have a powerful web flow
121  * capability integrated with Struts, with a consistent-approach to POJO-based
122  * binding and validation that addresses the proliferation of
123  * <code>ActionForm</code> classes found in traditional Struts-based apps.
124  *
125  * @see org.springframework.webflow.executor.FlowExecutor
126  * @see org.springframework.webflow.executor.support.FlowRequestHandler
127  * @see org.springframework.web.struts.SpringBindingActionForm
128  * @see org.springframework.web.struts.DelegatingActionProxy
129  *
130  * @author Keith Donald
131  * @author Erwin Vervaet
132  */

133 public class FlowAction extends ActionSupport {
134
135     /**
136      * The flow executor will be retreived from the application context using
137      * this bean name if no executor is explicitly set. ("flowExecutor")
138      */

139     protected static final String JavaDoc FLOW_EXECUTOR_BEAN_NAME = "flowExecutor";
140
141     /**
142      * The flow executor argument handler will be retreived from the
143      * application context using this bean name if no argument handler is
144      * explicitly set. ("argumentHandler")
145      */

146     protected static final String JavaDoc FLOW_EXECUTOR_ARGUMENT_HANDLER_BEAN_NAME = "argumentHandler";
147
148     /**
149      * The service responsible for launching and signaling Struts-originating
150      * events in flow executions.
151      */

152     private FlowExecutor flowExecutor;
153
154     /**
155      * Delegate to handle flow executor arguments.
156      */

157     private FlowExecutorArgumentHandler argumentHandler;
158
159     /**
160      * Returns the flow executor used by this controller.
161      * @return the flow executor
162      */

163     public FlowExecutor getFlowExecutor() {
164         return flowExecutor;
165     }
166
167     /**
168      * Configures the flow executor implementation to use. Required.
169      * @param flowExecutor the fully configured flow executor
170      */

171     public void setFlowExecutor(FlowExecutor flowExecutor) {
172         this.flowExecutor = flowExecutor;
173     }
174
175     /**
176      * Returns the flow executor argument handler used by this controller.
177      * @return the argument handler
178      */

179     public FlowExecutorArgumentHandler getArgumentHandler() {
180         return argumentHandler;
181     }
182
183     /**
184      * Sets the flow executor argument handler to use.
185      * @param argumentHandler the fully configured argument handler
186      */

187     public void setArgumentHandler(FlowExecutorArgumentHandler argumentHandler) {
188         this.argumentHandler = argumentHandler;
189     }
190
191     protected void onInit() {
192         WebApplicationContext context = getWebApplicationContext();
193         if (getFlowExecutor() == null) {
194             if (context.containsBean(FLOW_EXECUTOR_BEAN_NAME)) {
195                 setFlowExecutor((FlowExecutor)context.getBean(FLOW_EXECUTOR_BEAN_NAME, FlowExecutor.class));
196             }
197             else {
198                 throw new IllegalStateException JavaDoc("No '" + FLOW_EXECUTOR_BEAN_NAME
199                         + "' bean definition could be found; to use Spring Web Flow with Struts you must "
200                         + "configure this FlowAction with a FlowExecutor");
201             }
202         }
203         if (getArgumentHandler() == null) {
204             if (context.containsBean(FLOW_EXECUTOR_ARGUMENT_HANDLER_BEAN_NAME)) {
205                 setArgumentHandler((FlowExecutorArgumentHandler)context.getBean(
206                         FLOW_EXECUTOR_ARGUMENT_HANDLER_BEAN_NAME, FlowExecutorArgumentHandler.class));
207             }
208             else {
209                 // default
210
argumentHandler = new RequestParameterFlowExecutorArgumentHandler();
211             }
212         }
213     }
214
215     public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest JavaDoc request,
216             HttpServletResponse JavaDoc response) throws Exception JavaDoc {
217         ExternalContext context = new StrutsExternalContext(mapping, form, getServletContext(), request, response);
218         ResponseInstruction responseInstruction = createRequestHandler().handleFlowRequest(context);
219         return toActionForward(responseInstruction, mapping, form, request, response, context);
220     }
221
222     /**
223      * Factory method that creates a new helper for processing a request into
224      * this flow controller.
225      * @return the controller helper
226      */

227     protected FlowRequestHandler createRequestHandler() {
228         return new FlowRequestHandler(getFlowExecutor(), getArgumentHandler());
229     }
230
231     /**
232      * Return a Struts ActionForward given a ResponseInstruction. Adds all
233      * attributes from the ResponseInstruction as request attributes.
234      */

235     protected ActionForward toActionForward(ResponseInstruction response, ActionMapping mapping, ActionForm form,
236             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc httpResponse, ExternalContext context) throws Exception JavaDoc {
237         if (response.isApplicationView()) {
238             // forward to a view as part of an active conversation
239
ApplicationView forward = (ApplicationView)response.getViewSelection();
240             Map JavaDoc model = new HashMap JavaDoc(forward.getModel());
241             argumentHandler.exposeFlowExecutionContext(
242                     response.getFlowExecutionKey(), response.getFlowExecutionContext(), model);
243             WebUtils.exposeRequestAttributes(request, model);
244             if (form instanceof SpringBindingActionForm) {
245                 SpringBindingActionForm bindingForm = (SpringBindingActionForm)form;
246                 // expose the form object and associated errors as the
247
// "current form object" in the request
248
Errors currentErrors = (Errors)model.get(FormObjectAccessor.getCurrentFormErrorsName());
249                 bindingForm.expose(currentErrors, request);
250             }
251             return findForward(forward, mapping);
252
253         }
254         else if (response.isFlowExecutionRedirect()) {
255             // redirect to active flow execution URL
256
String JavaDoc flowExecutionUrl = argumentHandler.createFlowExecutionUrl(
257                     response.getFlowExecutionKey(), response.getFlowExecutionContext(), context);
258             return createRedirectForward(flowExecutionUrl, httpResponse);
259         }
260         else if (response.isFlowDefinitionRedirect()) {
261             // restart the flow by redirecting to flow launch URL
262
String JavaDoc flowUrl = argumentHandler.createFlowDefinitionUrl((FlowDefinitionRedirect)response.getViewSelection(), context);
263             return createRedirectForward(flowUrl, httpResponse);
264         }
265         else if (response.isExternalRedirect()) {
266             // redirect to external URL
267
String JavaDoc externalUrl = argumentHandler.createExternalUrl((ExternalRedirect)response.getViewSelection(),
268                     response.getFlowExecutionKey(), context);
269             return createRedirectForward(externalUrl, httpResponse);
270         }
271         else if (response.isNull()) {
272             // no response to issue
273
return null;
274         }
275         else {
276             throw new IllegalArgumentException JavaDoc("Don't know how to handle response instruction " + response);
277         }
278     }
279
280     /**
281      * Handles a redirect. This implementation simply calls sendRedirect on the
282      * response object.
283      * @param url the url to redirect to
284      * @param response the http response
285      * @return the redirect forward, this implementation returns null
286      * @throws Exception an excpetion occured processing the redirect
287      * @see HttpServletResponse#sendRedirect(java.lang.String)
288      */

289     protected ActionForward createRedirectForward(String JavaDoc url, HttpServletResponse JavaDoc response) throws Exception JavaDoc {
290         response.sendRedirect(url);
291         return null;
292     }
293
294     /**
295      * Find an action forward for given application view. If no suitable forward
296      * is found in the action mapping using the view name as a key, this method
297      * will create a new action forward using the view name.
298      * @param forward the application view to find a forward for
299      * @param mapping the action mapping to use
300      * @return the action forward, never null
301      */

302     protected ActionForward findForward(ApplicationView forward, ActionMapping mapping) {
303         // note that this method is always creating a new ActionForward to make
304
// sure that the redirect flag is false -- redirect is controlled by SWF
305
// itself, not Struts
306
ActionForward actionForward = mapping.findForward(forward.getViewName());
307         if (actionForward != null) {
308             // the 1.2.1 copy constructor would ideally be better to
309
// use, but it is not Struts 1.1 compatible
310
actionForward = new ActionForward(actionForward.getName(), actionForward.getPath(), false);
311         }
312         else {
313             actionForward = new ActionForward(forward.getViewName(), false);
314         }
315         return actionForward;
316     }
317 }
Popular Tags