KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > webflow > executor > mvc > PortletFlowController


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.mvc;
17
18 import java.util.HashMap JavaDoc;
19 import java.util.Map JavaDoc;
20
21 import javax.portlet.ActionRequest;
22 import javax.portlet.ActionResponse;
23 import javax.portlet.PortletRequest;
24 import javax.portlet.PortletSession;
25 import javax.portlet.RenderRequest;
26 import javax.portlet.RenderResponse;
27
28 import org.springframework.beans.factory.InitializingBean;
29 import org.springframework.util.Assert;
30 import org.springframework.web.portlet.ModelAndView;
31 import org.springframework.web.portlet.mvc.AbstractController;
32 import org.springframework.web.portlet.mvc.Controller;
33 import org.springframework.webflow.context.portlet.PortletExternalContext;
34 import org.springframework.webflow.execution.support.ApplicationView;
35 import org.springframework.webflow.execution.support.ExternalRedirect;
36 import org.springframework.webflow.execution.support.FlowDefinitionRedirect;
37 import org.springframework.webflow.executor.FlowExecutor;
38 import org.springframework.webflow.executor.ResponseInstruction;
39 import org.springframework.webflow.executor.support.FlowExecutorArgumentHandler;
40 import org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler;
41
42 /**
43  * Point of integration between Spring Portlet MVC and Spring Web Flow: a
44  * {@link Controller} that routes incoming portlet requests to one or more
45  * managed flow executions.
46  * <p>
47  * Requests into the web flow system are handled by a {@link FlowExecutor},
48  * which this class delegates to. Consult the JavaDoc of that class for more
49  * information on how requests are processed.
50  * <p>
51  * Note: a single <code>PortletFlowController</code> may execute all flows
52  * within your application. See the <code>phonebook-portlet</code> sample
53  * application for examples of the various strategies for launching and resuming
54  * flow executions in a Portlet environment.
55  * <p>
56  * It is also possible to customize the {@link FlowExecutorArgumentHandler}
57  * strategy to allow for different types of controller parameterization, for
58  * example perhaps in conjunction with a REST-style request mapper.
59  *
60  * @see org.springframework.webflow.executor.FlowExecutor
61  * @see org.springframework.webflow.executor.support.FlowExecutorArgumentHandler
62  *
63  * @author Keith Donald
64  * @author Erwin Vervaet
65  * @author J.Enrique Ruiz
66  * @author César Ordiñana
67  */

68 public class PortletFlowController extends AbstractController implements InitializingBean {
69
70     /**
71      * Name of the attribute under which the response instruction will be stored
72      * in the session.
73      */

74     private static final String JavaDoc RESPONSE_INSTRUCTION_SESSION_ATTRIBUTE = "actionRequest.responseInstruction";
75
76     /**
77      * Delegate for executing flow executions (launching new executions, and
78      * resuming existing executions).
79      */

80     private FlowExecutor flowExecutor;
81
82     /**
83      * Delegate for handler flow executor arguments.
84      */

85     private FlowExecutorArgumentHandler argumentHandler = new RequestParameterFlowExecutorArgumentHandler();
86
87     /**
88      * Create a new portlet flow controller. Allows for bean style usage.
89      * @see #setFlowExecutor(FlowExecutor)
90      * @see #setArgumentHandler(FlowExecutorArgumentHandler)
91      */

92     public PortletFlowController() {
93         // set the cache seconds property to 0 so no pages are cached by default
94
// for flows
95
setCacheSeconds(0);
96         // this controller stores ResponseInstruction objects in the session, so
97
// we need to ensure we do this in an orderly manner
98
// see exposeToRenderPhase() and extractActionResponseInstruction()
99
setSynchronizeOnSession(true);
100     }
101
102     /**
103      * Returns the flow executor used by this controller.
104      * @return the flow executor
105      */

106     public FlowExecutor getFlowExecutor() {
107         return flowExecutor;
108     }
109
110     /**
111      * Configures the flow executor implementation to use. Required.
112      * @param flowExecutor the fully configured flow executor
113      */

114     public void setFlowExecutor(FlowExecutor flowExecutor) {
115         this.flowExecutor = flowExecutor;
116     }
117
118     /**
119      * Returns the flow executor argument handler used by this controller.
120      * @return the argument handler
121      */

122     public FlowExecutorArgumentHandler getArgumentHandler() {
123         return argumentHandler;
124     }
125
126     /**
127      * Sets the flow executor argument handler to use.
128      * @param argumentHandler the fully configured argument handler
129      */

130     public void setArgumentHandler(FlowExecutorArgumentHandler argumentHandler) {
131         this.argumentHandler = argumentHandler;
132     }
133
134     /**
135      * Sets the identifier of the default flow to launch if no flowId argument
136      * can be extracted by the configured {@link FlowExecutorArgumentHandler}
137      * during render request processing.
138      * <p>
139      * This is a convenience method that sets the default flow id of the
140      * controller's argument handler. Don't use this when using
141      * {@link #setArgumentHandler(FlowExecutorArgumentHandler)}.
142      */

143     public void setDefaultFlowId(String JavaDoc defaultFlowId) {
144         argumentHandler.setDefaultFlowId(defaultFlowId);
145     }
146
147     public void afterPropertiesSet() {
148         Assert.notNull(flowExecutor, "The flow executor property is required");
149         Assert.notNull(argumentHandler, "The argument handler property is required");
150     }
151
152     protected ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) throws Exception JavaDoc {
153         PortletExternalContext context = new PortletExternalContext(getPortletContext(), request, response);
154         if (argumentHandler.isFlowExecutionKeyPresent(context)) {
155             // flowExecutionKey render param present: this is a request to
156
// render an active flow execution -- extract its key
157
String JavaDoc flowExecutionKey = argumentHandler.extractFlowExecutionKey(context);
158             // look for a cached response instruction in the session put there
159
// by the action request phase as part of an "active view" forward
160
ResponseInstruction responseInstruction = extractActionResponseInstruction(request);
161             if (responseInstruction == null) {
162                 // no response instruction found, simply refresh the current
163
// view state of the flow execution
164
return toModelAndView(flowExecutor.refresh(flowExecutionKey, context));
165             }
166             else {
167                 // found: convert it to model and view for rendering
168
return toModelAndView(responseInstruction);
169             }
170         }
171         else {
172             // this is either a "launch" flow request or a "confirmation view"
173
// render request -- look for the cached "confirmation view"
174
// response instruction
175
ResponseInstruction responseInstruction = extractActionResponseInstruction(request);
176             if (responseInstruction == null) {
177                 // no response instruction found in session - launch a new flow
178
// execution
179
String JavaDoc flowId = argumentHandler.extractFlowId(context);
180                 return toModelAndView(flowExecutor.launch(flowId, context));
181             }
182             else {
183                 // found: convert it to model and view for rendering
184
return toModelAndView(responseInstruction);
185             }
186         }
187     }
188
189     protected void handleActionRequestInternal(ActionRequest request, ActionResponse response) throws Exception JavaDoc {
190         PortletExternalContext context = new PortletExternalContext(getPortletContext(), request, response);
191         String JavaDoc flowExecutionKey = argumentHandler.extractFlowExecutionKey(context);
192         String JavaDoc eventId = argumentHandler.extractEventId(context);
193         // signal the event against the flow execution, returning the next
194
// response instruction
195
ResponseInstruction responseInstruction = flowExecutor.resume(flowExecutionKey, eventId, context);
196         if (responseInstruction.isApplicationView()) {
197             // response instruction is a forward to an "application view"
198
if (responseInstruction.isActiveView()) {
199                 // is an "active" forward from a view-state (not end-state) --
200
// set the flow execution key render parameter to support
201
// browser refresh
202
response.setRenderParameter(
203                         argumentHandler.getFlowExecutionKeyArgumentName(),
204                         responseInstruction.getFlowExecutionKey());
205             }
206             // cache response instruction for access during render phase of this
207
// portlet
208
exposeToRenderPhase(responseInstruction, request);
209         }
210         else if (responseInstruction.isFlowExecutionRedirect()) {
211             // is a flow execution redirect: simply expose key parameter to
212
// support refresh during render phase
213
response.setRenderParameter(
214                     argumentHandler.getFlowExecutionKeyArgumentName(),
215                     responseInstruction.getFlowExecutionKey());
216         }
217         else if (responseInstruction.isFlowDefinitionRedirect()) {
218             // set flow id render parameter to request that a new flow be
219
// launched within this portlet
220
FlowDefinitionRedirect redirect = (FlowDefinitionRedirect)responseInstruction.getViewSelection();
221             response.setRenderParameters(redirect.getExecutionInput());
222             response.setRenderParameter(argumentHandler.getFlowIdArgumentName(), redirect.getFlowDefinitionId());
223         }
224         else if (responseInstruction.isExternalRedirect()) {
225             // issue the redirect to the external URL
226
ExternalRedirect redirect = (ExternalRedirect)responseInstruction.getViewSelection();
227             String JavaDoc url = argumentHandler.createExternalUrl(redirect, flowExecutionKey, context);
228             response.sendRedirect(url);
229         }
230         else {
231             throw new IllegalArgumentException JavaDoc("Don't know how to handle response instruction " + responseInstruction);
232         }
233     }
234
235     // helpers
236

237     /**
238      * Expose given response instruction to the render phase by putting it in
239      * the session.
240      */

241     private void exposeToRenderPhase(ResponseInstruction responseInstruction, ActionRequest request) {
242         PortletSession session = request.getPortletSession(false);
243         Assert.notNull(session, "A PortletSession is required");
244         session.setAttribute(RESPONSE_INSTRUCTION_SESSION_ATTRIBUTE, responseInstruction);
245     }
246
247     /**
248      * Extract a response instruction stored in the session during the action
249      * phase by {@link #exposeToRenderPhase(ResponseInstruction, ActionRequest)}.
250      * If a response instruction is found, it will be removed from the session.
251      * @param request the portlet request
252      * @return the response instructions found in the session or null if not
253      * found
254      */

255     private ResponseInstruction extractActionResponseInstruction(PortletRequest request) {
256         PortletSession session = request.getPortletSession(false);
257         ResponseInstruction response = null;
258         if (session != null) {
259             response = (ResponseInstruction)session.getAttribute(RESPONSE_INSTRUCTION_SESSION_ATTRIBUTE);
260             if (response != null) {
261                 // remove it
262
session.removeAttribute(RESPONSE_INSTRUCTION_SESSION_ATTRIBUTE);
263             }
264         }
265         return response;
266     }
267
268     /**
269      * Convert given response instruction into a Spring Portlet MVC model and
270      * view.
271      */

272     protected ModelAndView toModelAndView(ResponseInstruction response) {
273         if (response.isApplicationView()) {
274             // forward to a view as part of an active conversation
275
ApplicationView forward = (ApplicationView)response.getViewSelection();
276             Map JavaDoc model = new HashMap JavaDoc(forward.getModel());
277             argumentHandler.exposeFlowExecutionContext(
278                     response.getFlowExecutionKey(), response.getFlowExecutionContext(), model);
279             return new ModelAndView(forward.getViewName(), model);
280         }
281         else if (response.isNull()) {
282             // no response to issue
283
return null;
284         }
285         else {
286             throw new IllegalArgumentException JavaDoc("Don't know how to handle response instruction " + response);
287         }
288     }
289 }
Popular Tags