1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.struts.flow;
17
18 import java.io.BufferedReader;
19 import java.io.IOException;
20 import java.util.Iterator;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.Map;
24
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.apache.commons.chain.web.servlet.ServletWebContext;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.struts.action.Action;
33 import org.apache.struts.action.ActionErrors;
34 import org.apache.struts.action.ActionForm;
35 import org.apache.struts.action.ActionForward;
36 import org.apache.struts.action.ActionMapping;
37 import org.apache.struts.action.ActionMessages;
38 import org.apache.struts.flow.core.javascript.ConversionHelper;
39 import org.apache.struts.flow.core.Interpreter;
40 import org.apache.struts.flow.json.JSONArray;
41 import org.apache.struts.flow.json.JSONSerializer;
42 import org.mozilla.javascript.Scriptable;
43
44 /*** Executes scripts and continuations */
45 public class FlowAction extends Action {
46
47 private static final Log log = LogFactory.getLog(FlowAction.class);
48
49 /***
50 * Gets the interpreter when no script is specified
51 *
52 *@return The interpreter value
53 */
54 protected Interpreter getInterpreter(String prefix) {
55 return (Interpreter) servlet.getServletContext().getAttribute(FlowPlugIn.INTERPRETER_KEY+"/"+prefix);
56 }
57
58 /***
59 * Gets the interpreter for the requested script
60 *
61 *@param script The script to load the interpeter for
62 *@return The interpreter value
63 */
64 protected Interpreter getInterpreter(String prefix, String script) {
65 Map map = (Map)servlet.getServletContext().getAttribute(FlowPlugIn.INTERPRETERS_KEY+"/"+prefix);
66 return (Interpreter) map.get(script);
67 }
68
69
70 /***
71 * Handle by either starting a new Control Flow or continuing an existing
72 * one. The logic is:<br />
73 * - If request contains contid, then continue existing flow.<br />
74 * - Else if the action mapping parameter attribute has the contid, then
75 * continue the existing flow.<br />
76 * - Else start a new flow.<br />
77 * The name of the function to execute for a new flow should be specified
78 * in the <code>function</code> property of the custom action mapping
79 * class, <code>FlowMapping</code>
80 *
81 *@param request the request send by the client to the server
82 *@param response the response send by the server to the client
83 *@param mapping the action mapping
84 *@param form the action form
85 *@return the action forward
86 *@exception Exception If something goes wrong
87 */
88 public ActionForward execute(ActionMapping mapping,
89 ActionForm form,
90 HttpServletRequest request,
91 HttpServletResponse response)
92 throws Exception {
93
94 FlowMapping flowMapping = null;
95 if (mapping instanceof FlowMapping) {
96 flowMapping = (FlowMapping) mapping;
97 } else {
98 throw new ServletException("FlowMapping implementation of ActionMapping required");
99 }
100
101 ServletWebContext context = new ServletWebContext();
102 context.initialize(servlet.getServletContext(), request, response);
103 context.put(Constants.ACTION_SERVLET_KEY,
104 this.servlet);
105 context.put(Constants.ACTION_CONFIG_KEY,
106 mapping);
107 context.put(Constants.ACTION_FORM_KEY,
108 form);
109 context.put(Constants.MESSAGE_RESOURCES_KEY,
110 getResources(request));
111 context.put(Constants.ACTION_KEY, this);
112
113 String contid = request.getParameter("contid");
114
115 Interpreter interp = null;
116 if (flowMapping.getScript() == null) {
117 interp = getInterpreter(flowMapping.getModuleConfig().getPrefix());
118 } else {
119 interp = getInterpreter(flowMapping.getModuleConfig().getPrefix(), flowMapping.getScript());
120 }
121
122
123
124 boolean isFlowCall = (request.getParameter("FlowCall") != null);
125 String func = null;
126 if (isFlowCall) {
127 func = request.getParameter("FlowCall");
128 } else {
129 func = flowMapping.getFunction();
130 if (func == null || func.length() == 0) {
131 func = mapping.getParameter();
132 }
133 }
134
135
136 if (contid == null || contid.length() == 0) {
137
138
139
140 List args = new LinkedList();
141
142 if (func == null || func.length() == 0) {
143 throw new ServletException("You must specify a function name to call");
144 } else {
145
146
147 if (isFlowCall) {
148 StringBuffer sb = new StringBuffer();
149 char[] buffer = new char[1024];
150 int len = 0;
151 BufferedReader reader = request.getReader();
152 while ((len = reader.read(buffer)) > 0 ) {
153 sb.append(buffer, 0, len);
154 }
155 String json = sb.toString();
156 if (log.isDebugEnabled()) {
157 log.debug("processing json:"+json);
158 }
159 if (isValidJSON(json)) {
160 context.put("json", json);
161 }
162
163 }
164
165
166 Object ret = interp.callFunction(func, args, context);
167
168
169 String page = (String) ConversionHelper.jsobjectToObject(context.get(Constants.FORWARD_NAME_KEY));
170 contid = (String) context.get(Constants.CONTINUATION_ID_KEY);
171 Scriptable bizdata = (Scriptable) context.get(Constants.BIZ_DATA_KEY);
172 Map atts = null;
173 if (bizdata != null) {
174 atts = ConversionHelper.jsobjectToMap(bizdata);
175 } else if (ret != null && ret instanceof Scriptable) {
176 atts = ConversionHelper.jsobjectToMap((Scriptable) ret);
177 }
178
179
180 if (isFlowCall) {
181 String json = new JSONSerializer().serialize(atts);
182 if (log.isDebugEnabled()) {
183 log.debug("returning json: "+json);
184 }
185 response.getWriter().write(json);
186 response.getWriter().flush();
187 response.getWriter().close();
188 return null;
189 } else {
190 return dispatchToPage(request, response, mapping, page, contid, atts);
191 }
192 }
193 } else {
194
195
196
197 context.put("id", "5");
198
199 interp.handleContinuation(
200 request.getParameter("contid"), new LinkedList(), context);
201
202
203 String page = (String) context.get(Constants.FORWARD_NAME_KEY);
204 contid = (String) context.get(Constants.CONTINUATION_ID_KEY);
205 Scriptable bizdata = (Scriptable) context.get(Constants.BIZ_DATA_KEY);
206 Map atts = null;
207 if (bizdata != null) {
208 atts = ConversionHelper.jsobjectToMap(bizdata);
209 }
210 return dispatchToPage(request, response, mapping, page, contid, atts);
211 }
212 }
213
214
215 /***
216 * Add continuation ID and attributes to request scope, dispatch to page.
217 *
218 *@param request The request
219 *@param response The response
220 *@param page The action forward name
221 *@param contid Continuation ID to be set in request.
222 *@param atts Attributes to be set in request.
223 *@param mapping The action mapping
224 *@return
225 *@throws ServletException
226 *@throws IOException
227 */
228 private ActionForward dispatchToPage(
229 HttpServletRequest request, HttpServletResponse response, ActionMapping mapping,
230 String page, String contid, Map atts)
231 throws ServletException, IOException {
232
233
234
235 if (!response.isCommitted()) {
236 request.setAttribute("contid", contid);
237
238 if (atts != null) {
239 Iterator attkeys = atts.keySet().iterator();
240 while (attkeys.hasNext()) {
241 String attkey = (String) attkeys.next();
242 request.setAttribute(attkey, ConversionHelper.jsobjectToObject(atts.get(attkey)));
243 }
244 }
245
246 ActionForward af = mapping.findForward(page);
247 if (af == null) {
248 af = new ActionForward(page);
249 }
250 return af;
251 }
252 return null;
253 }
254
255 private boolean isValidJSON(String val) {
256 try {
257 if (val != null && val.length() > 1) {
258 JSONArray obj = new JSONArray(val);
259 if (log.isDebugEnabled()) {
260 log.debug("Valid JSON");
261 }
262 return true;
263 } else {
264 if (log.isDebugEnabled()) {
265 log.debug("No JSON detected");
266 }
267 }
268 } catch (Exception ex) {
269 log.warn("Invalid JSON object", ex);
270 }
271 return false;
272 }
273
274
275
276
277
278
279 /***
280 * Saves a token
281 *
282 *@param req The request object
283 */
284 public void saveToken(HttpServletRequest req) {
285 super.saveToken(req);
286 }
287
288
289 /***
290 * Checks to see if the request is cancelled
291 *
292 *@param req The request object
293 *@return True if cancelled
294 */
295 public boolean isCancelled(HttpServletRequest req) {
296 return super.isCancelled(req);
297 }
298
299
300 /***
301 * Checks to see if the token is valid
302 *
303 *@param req The request object
304 *@return True if valid
305 */
306 public boolean isTokenValid(HttpServletRequest req) {
307 return super.isTokenValid(req);
308 }
309
310
311 /***
312 * Resets the token
313 *
314 *@param req The request object
315 */
316 public void resetToken(HttpServletRequest req) {
317 super.resetToken(req);
318 }
319
320
321 /***
322 * Saves the messages to the request
323 *
324 *@param req The request object
325 *@param mes The action messages
326 */
327 public void saveMessages(HttpServletRequest req, ActionMessages mes) {
328 super.saveMessages(req, mes);
329 }
330
331
332 /***
333 * Saves the errors to the request
334 *
335 *@param req The request object
336 *@param errs The action errors
337 */
338 public void saveErrors(HttpServletRequest req, ActionErrors errs) {
339 super.saveErrors(req, errs);
340 }
341
342 }
343