1
2
3
4
5
6 package net.kwfgrid.gwui.servlets;
7
8 import javax.servlet.http.HttpServlet;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 import javax.servlet.http.HttpSession;
12 import javax.servlet.ServletException;
13 import javax.servlet.ServletContext;
14 import javax.servlet.RequestDispatcher;
15 import java.io.*;
16 import java.util.Properties;
17 import java.util.List;
18 import java.util.LinkedList;
19 import java.rmi.RemoteException;
20
21 import org.apache.log4j.Logger;
22
23 import net.kwfgrid.gworkflowdl.protocol.IllegalModificationException;
24 import net.kwfgrid.gworkflowdl.protocol.IncompatibleVersionsException;
25 import net.kwfgrid.gworkflowdl.protocol.client.ClientWorkflow;
26 import net.kwfgrid.gworkflowdl.structure.*;
27
28 /***
29 <b>NOTE:</b> All servlets needed to fire one transition (including default and generic servlet) need to be deployed in one and the same webapp
30 so that they can share session information.
31 The file <code>dataservletbase.properties</code> must be present in the root directory of the classpath and must contain the following properties:
32 <ul>
33 <li>servlet-base-url = <The base URL of the servlets></li>
34 </ul>
35 */
36 public abstract class DataServletBase extends HttpServlet implements ServletConstants {
37 private static Logger logger = Logger.getLogger(DataServletBase.class);
38
39 /*** The placeholder for the servlet URL. */
40 public static final String PLACEHOLDER_SERVLET_URL = "//$SERVLET_URL";
41
42 private static Properties PROPERTIES = new Properties();
43
44 static {
45 try {
46 PROPERTIES.load(DataServletBase.class.getResourceAsStream("/dataservletbase.properties"));
47 PROPERTIES.list(System.out);
48
49 if (PROPERTIES.getProperty(KEY_SERVLET_BASE_URL) == null)
50 throw new IOException("No servlet-base-url property defined.");
51 } catch (Exception x) {
52 logger.fatal("Could not load properties.", x);
53 }
54 }
55
56 protected abstract Logger getLogger();
57
58 protected final String getSelfURL() {
59 return getProperty(KEY_SERVLET_BASE_URL)+getServletName();
60 }
61
62 protected String getProperty(String key) {
63 return PROPERTIES.getProperty(key);
64 }
65
66 protected void doPost(HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
67 doAll( request, response );
68 }
69
70 protected void doGet(HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
71 doAll( request, response );
72 }
73
74 private void doAll(HttpServletRequest request, HttpServletResponse response )throws ServletException, IOException {
75 HttpSession session = request.getSession(true);
76 boolean invalidate = true;
77
78 getLogger().info("Handling session: "+session.getId());
79
80 try {
81 if (!session.isNew() && request.getParameter("newsession") != null) {
82 getLogger().info("Got outdated session information. Recreating session.");
83
84 session.invalidate();
85 session = request.getSession(true);
86 }
87
88 if (session.isNew() || FORM_ACTION.equals((String)session.getAttribute(KEY_ACTION))) {
89 try {
90 if (session.isNew()) {
91 getLogger().info("Session is new. Creating session information...");
92 createSessionInformation(request, session);
93 }
94
95 String formurl = ServletUtilities.decode(request.getParameter(KEY_FORMURL));
96 if (formurl == null)
97 throw new IllegalArgumentException("No formURL parameter specified.");
98 session.setAttribute(KEY_FORMURL, formurl);
99
100 getLogger().info("Displaying form...");
101 showForm(request, session, response);
102 session.setAttribute(KEY_ACTION, TOKEN_ACTION);
103 invalidate = false;
104 } catch (UnsupportedEncodingException x) {
105 getLogger().error("Unsupported encoding when creating session information.", x);
106 showErrorDialog(session, response, "Unsupported encoding when creating session information.", x);
107 } catch (IllegalArgumentException x) {
108 getLogger().error("Insufficient request.", x);
109 showErrorDialog(session, response, "Insufficient request.", x);
110 } catch (IncompatibleVersionsException x) {
111 getLogger().error("Incompatible versions of workflow.", x);
112 showErrorDialog(session, response, "Incompatible versions of workflow.", x);
113 } catch (RemoteException x) {
114 getLogger().error("Workflow server unreachable.", x);
115 showErrorDialog(session, response, "Workflow server unreachable.", x);
116 }
117 } else {
118 try {
119 getLogger().info("Session is known. Handling user input.");
120 invalidate = !handleUserRequest(request, session, response);
121 } catch (IncompatibleVersionsException x) {
122 getLogger().error("Incompatible versions of workflow.", x);
123 showErrorDialog(session, response, "Incompatible versions of workflow.", x);
124 } catch (IllegalArgumentException x) {
125 getLogger().error("Invalid session information.", x);
126 showErrorDialog(session, response, "Invalid session information.", x);
127 } catch (IllegalModificationException x) {
128 getLogger().error("Error manipulating workflow.", x);
129 showErrorDialog(session, response, "Error manipulating workflow.", x);
130 } catch (RemoteException x) {
131 getLogger().error("Workflow server unreachable.", x);
132 showErrorDialog(session, response, "Workflow server unreachable.", x);
133 } catch (WorkflowFormatException x) {
134 getLogger().error("Could not create token from request.", x);
135 showErrorDialog(session, response, "Could not create token from request.", x);
136 } catch (CapacityException x) {
137 getLogger().error("Could not put token on place.", x);
138 showErrorDialog(session, response, "Could not put token on place.", x);
139 } catch (UnsupportedEncodingException x) {
140 getLogger().error("Unsupported encoding.", x);
141 showErrorDialog(session, response, "Unsupported encoding.", x);
142 }
143 }
144 } finally {
145 if (invalidate) {
146 session.invalidate();
147 getLogger().info("Session invalidated.");
148 }
149 }
150 }
151
152
153
154
155
156 /***
157 Create the session information for a request.
158 This will write certain input parameters of the request to the session.
159 */
160 private void createSessionInformation(HttpServletRequest request, HttpSession session)
161 throws UnsupportedEncodingException, IllegalArgumentException, RemoteException, IncompatibleVersionsException {
162 String command = ServletUtilities.decode(request.getParameter(KEY_COMMAND));
163 String workflowid = ServletUtilities.decode(request.getParameter(KEY_WORKFLOWID));
164 String gwesaddress = ServletUtilities.decode(request.getParameter(KEY_GWESADDRESS));
165 String versionnumberspec = ServletUtilities.decode(request.getParameter(KEY_VERSIONNUMBER));
166 int versionnumber = -1;
167 ClientWorkflow workflow = null;
168
169 if (command == null)
170 throw new IllegalArgumentException("No command specified.");
171 if (workflowid == null)
172 throw new IllegalArgumentException("No Workflow ID specified.");
173 if (gwesaddress == null)
174 throw new IllegalArgumentException("No reference to GWES specified.");
175 if (versionnumberspec == null)
176 throw new IllegalArgumentException("No version number specified.");
177
178 session.setAttribute(KEY_COMMAND, command);
179 session.setAttribute(KEY_WORKFLOWID, workflowid);
180 session.setAttribute(KEY_GWESADDRESS, gwesaddress);
181 try {
182 versionnumber = Integer.parseInt(versionnumberspec);
183 session.setAttribute(KEY_VERSIONNUMBER, new Integer(versionnumber));
184 } catch (NumberFormatException x) {
185 IllegalArgumentException iox = new IllegalArgumentException("Could not parse version number.");
186 iox.initCause(x);
187 throw iox;
188 }
189
190 if (EDIT_COMMAND.equals(command)) {
191 addEditSessionInformation(request, session);
192 } else if (CREATE_COMMAND.equals(command)) {
193 addCreateSessionInformation(request, session);
194 } else if (FIRE_COMMAND.equals(command)) {
195 addFireSessionInformation(request, session);
196 } else {
197 throw new IllegalArgumentException("Unknown command for GWUI servlet: "+command);
198 }
199
200 workflow = (ClientWorkflow)Factory.newWorkflow(workflowid);
201 workflow.update();
202 if (workflow.getVersionNumber() != versionnumber)
203 throw new IncompatibleVersionsException("Workflow has changed. Please retry.");
204 if (EDIT_COMMAND.equals(command)) {
205 checkWorkflowInstanceForEdit(session, workflow);
206 } else if (CREATE_COMMAND.equals(command)) {
207 checkWorkflowInstanceForCreate(session, workflow);
208 } else if (FIRE_COMMAND.equals(command)) {
209 checkWorkflowInstanceForFire(session, workflow);
210 }
211
212 session.setAttribute(KEY_WORKFLOWINSTANCE, workflow);
213 getLogger().info("Added workflow instance "+workflow+" to session.");
214 }
215
216 private void addEditSessionInformation(HttpServletRequest request, HttpSession session)
217 throws UnsupportedEncodingException, IllegalArgumentException {
218 String placeid = ServletUtilities.decode(request.getParameter(KEY_PLACEID));
219 String tokenindexspec = ServletUtilities.decode(request.getParameter(KEY_TOKENINDEX));
220
221 if (placeid == null)
222 throw new IllegalArgumentException("Place ID needs to be specified to edit token.");
223 if (tokenindexspec == null)
224 throw new IllegalArgumentException("Token index needs to be specified to edit token.");
225
226 session.setAttribute(KEY_PLACEID, placeid);
227 try {
228 session.setAttribute(KEY_TOKENINDEX, new Integer(Integer.parseInt(tokenindexspec)));
229 } catch (NumberFormatException x) {
230 IllegalArgumentException iox = new IllegalArgumentException("Could not parse token index.");
231 iox.initCause(x);
232 throw iox;
233 }
234 }
235
236 private void addCreateSessionInformation(HttpServletRequest request, HttpSession session)
237 throws UnsupportedEncodingException, IllegalArgumentException {
238 String placeid = ServletUtilities.decode(request.getParameter(KEY_PLACEID));
239
240 if (placeid == null)
241 throw new IllegalArgumentException("Place ID needs to be specified to create token.");
242
243 session.setAttribute(KEY_PLACEID, placeid);
244 }
245
246 private void addFireSessionInformation(HttpServletRequest request, HttpSession session)
247 throws UnsupportedEncodingException, IllegalArgumentException {
248 String transitionid = ServletUtilities.decode(request.getParameter(KEY_TRANSITIONID));
249
250 if (transitionid == null)
251 throw new IllegalArgumentException("Transition ID needs to be specified to fire transition.");
252
253 session.setAttribute(KEY_TRANSITIONID, transitionid);
254 session.setAttribute(KEY_OUTEDGEINDEX, new Integer(0));
255 session.setAttribute(KEY_TOKENLIST, new LinkedList());
256 }
257
258 private void checkWorkflowInstanceForEdit(HttpSession session, ClientWorkflow workflow) throws IllegalArgumentException {
259 String placeid = (String)session.getAttribute(KEY_PLACEID);
260 int tokenindex = ((Integer)session.getAttribute(KEY_TOKENINDEX)).intValue();
261 Place place = workflow.getPlace(placeid);
262
263 if (place == null)
264 throw new IllegalArgumentException("Workflow does not contain a place with ID "+placeid+".");
265 if (tokenindex >= place.getTokens().length)
266 throw new IllegalArgumentException("Place with ID "+placeid+" does not contain "+(tokenindex+1)+" token(s).");
267 }
268
269 private void checkWorkflowInstanceForCreate(HttpSession session, ClientWorkflow workflow) throws IllegalArgumentException {
270 String placeid = (String)session.getAttribute(KEY_PLACEID);
271 Place place = workflow.getPlace(placeid);
272
273 if (place == null)
274 throw new IllegalArgumentException("Workflow does not contain a place with ID "+placeid+".");
275 }
276
277 private void checkWorkflowInstanceForFire(HttpSession session, ClientWorkflow workflow) throws IllegalArgumentException {
278 String transitionid = (String)session.getAttribute(KEY_TRANSITIONID);
279 Transition transition = workflow.getTransition(transitionid);
280
281 if (transition == null)
282 throw new IllegalArgumentException("Workflow does not contain a transition with ID "+transitionid+".");
283 if (!transition.isEnabled())
284 throw new IllegalArgumentException("Transition with ID "+transitionid+" is not enabled.");
285
286 if (transition.getOutEdges().length < 1)
287 throw new IllegalArgumentException("Transitions with ID "+transitionid+" has no output places.");
288 }
289
290 private void checkWorkflowVersion(HttpSession session, ClientWorkflow workflow) throws IncompatibleVersionsException {
291 int versionnumber = ((Integer)session.getAttribute(KEY_VERSIONNUMBER)).intValue();
292 if (workflow.getVersionNumber() != versionnumber)
293 throw new IncompatibleVersionsException("Workflow has changed. Please retry.");
294 }
295
296
297
298
299
300 /***
301 Show the a form.
302 */
303 private void showForm(HttpServletRequest request, HttpSession session, HttpServletResponse response)
304 throws IOException, IncompatibleVersionsException, RemoteException, IllegalArgumentException {
305 String command = (String)session.getAttribute(KEY_COMMAND);
306 if (EDIT_COMMAND.equals(command)) {
307 showEditForm(request, session, response);
308 } else if (CREATE_COMMAND.equals(command)) {
309 showCreateForm(request, session, response);
310 } else if (FIRE_COMMAND.equals(command)) {
311 showFireForm(request, session, response);
312 } else {
313 throw new IllegalArgumentException("Invalid session information. Command attribute invalid or not set.");
314 }
315 }
316
317 private void showEditForm(HttpServletRequest request, HttpSession session, HttpServletResponse response)
318 throws IOException, IncompatibleVersionsException, RemoteException, IllegalArgumentException {
319 ClientWorkflow workflow = (ClientWorkflow)session.getAttribute(KEY_WORKFLOWINSTANCE);
320 workflow.update();
321 checkWorkflowVersion(session, workflow);
322 String placeid = (String)session.getAttribute(KEY_PLACEID);
323 Place place = workflow.getPlace(placeid);
324 int tokenindex = ((Integer)session.getAttribute(KEY_TOKENINDEX)).intValue();
325 Token token = place.getTokens()[tokenindex];
326 String formurl = (String)session.getAttribute(KEY_FORMURL);
327
328 getLogger().info("Displaying form for edit: "+formurl);
329
330 if (formurl == null)
331 throw new IllegalArgumentException("No form specified.");
332
333 String form = ServletUtilities.readFile(formurl);
334
335 getLogger().info("Form template before replace:\n"+form);
336
337 form = ServletUtilities.replace(form, PLACEHOLDER_SERVLET_URL, response.encodeURL(getSelfURL()));
338
339 getLogger().info("Form template after replace servlet URL:\n"+form);
340
341 form = replaceValues(form, token);
342
343 getLogger().info("Form template after replace servlet values:\n"+form);
344
345 PrintWriter out = response.getWriter();
346 out.println(form);
347 }
348
349 private void showCreateForm(HttpServletRequest request, HttpSession session, HttpServletResponse response)
350 throws IOException, IncompatibleVersionsException, RemoteException, IllegalArgumentException {
351 ClientWorkflow workflow = (ClientWorkflow)session.getAttribute(KEY_WORKFLOWINSTANCE);
352 workflow.update();
353 checkWorkflowVersion(session, workflow);
354 String placeid = (String)session.getAttribute(KEY_PLACEID);
355 Place place = workflow.getPlace(placeid);
356 String formurl = (String)session.getAttribute(KEY_FORMURL);
357
358 getLogger().info("Displaying form for create: "+formurl);
359
360 if (formurl == null)
361 throw new IllegalArgumentException("No form specified in parameters.");
362
363 String form = ServletUtilities.readFile(formurl);
364 form = ServletUtilities.replace(form, PLACEHOLDER_SERVLET_URL, response.encodeURL(getSelfURL()));
365 form = clearValues(form);
366
367 PrintWriter out = response.getWriter();
368 out.println(form);
369 }
370
371 private void showFireForm(HttpServletRequest request, HttpSession session, HttpServletResponse response)
372 throws IOException, IncompatibleVersionsException, RemoteException, IllegalArgumentException {
373 ClientWorkflow workflow = (ClientWorkflow)session.getAttribute(KEY_WORKFLOWINSTANCE);
374 workflow.update();
375 checkWorkflowVersion(session, workflow);
376 String transitionid = (String)session.getAttribute(KEY_TRANSITIONID);
377 Transition transition = workflow.getTransition(transitionid);
378 int outedgeindex = ((Integer)session.getAttribute(KEY_OUTEDGEINDEX)).intValue();
379 Edge[] outedges = transition.getOutEdges();
380 Place currentplace = outedges[outedgeindex].getPlace();
381 String formurl = (String)session.getAttribute(KEY_FORMURL);
382
383 getLogger().info("Displaying form for fire: "+formurl);
384
385 if (formurl == null)
386 throw new IllegalArgumentException("No form specified in parameters.");
387
388 String form = ServletUtilities.readFile(formurl);
389 form = ServletUtilities.replace(form, PLACEHOLDER_SERVLET_URL, response.encodeURL(getSelfURL()));
390 form = replaceValues(form, transition);
391
392 PrintWriter out = response.getWriter();
393 out.println(form);
394 }
395
396 /***
397 Replace all placeholders for values in the form with the actual values from the token.
398 */
399 protected abstract String replaceValues(String form, Token token);
400
401 /***
402 Replace all placeholders for values in the form with the empty string.
403 */
404 protected abstract String clearValues(String form);
405
406 /***
407 Replace all placeholders for values in the form with the correct values from the first tokens on the input
408 places of the specified transition.
409 */
410 protected abstract String replaceValues(String form, Transition transition);
411
412
413
414
415
416 private boolean handleUserRequest(HttpServletRequest request, HttpSession session, HttpServletResponse response)
417 throws IOException,
418 IncompatibleVersionsException,
419 RemoteException,
420 WorkflowFormatException,
421 CapacityException,
422 UnsupportedEncodingException,
423 IllegalModificationException,
424 IllegalArgumentException,
425 ServletException {
426 String command = (String)session.getAttribute(KEY_COMMAND);
427 if (EDIT_COMMAND.equals(command)) {
428 return handleEditRequest(request, session, response);
429 } else if (CREATE_COMMAND.equals(command)) {
430 return handleCreateRequest(request, session, response);
431 } else if (FIRE_COMMAND.equals(command)) {
432 return handleFireRequest(request, session, response);
433 } else {
434 throw new IllegalArgumentException("Invalid session information. Command attribute invalid or not set.");
435 }
436 }
437
438 private boolean handleEditRequest(HttpServletRequest request, HttpSession session, HttpServletResponse response)
439 throws IOException,
440 IncompatibleVersionsException,
441 RemoteException,
442 IllegalArgumentException,
443 IllegalModificationException,
444 WorkflowFormatException,
445 CapacityException,
446 UnsupportedEncodingException {
447 ClientWorkflow workflow = (ClientWorkflow)session.getAttribute(KEY_WORKFLOWINSTANCE);
448 workflow.update();
449 checkWorkflowVersion(session, workflow);
450 String placeid = (String)session.getAttribute(KEY_PLACEID);
451 Place place = workflow.getPlace(placeid);
452 int tokenindex = ((Integer)session.getAttribute(KEY_TOKENINDEX)).intValue();
453
454 Token newtoken = createToken(request);
455
456 place.removeToken(tokenindex);
457 place.addToken(newtoken);
458
459 showConfirmationDialog(session, response);
460 return false;
461 }
462
463 private boolean handleCreateRequest(HttpServletRequest request, HttpSession session, HttpServletResponse response)
464 throws IOException,
465 IncompatibleVersionsException,
466 RemoteException,
467 IllegalArgumentException,
468 IllegalModificationException,
469 WorkflowFormatException,
470 CapacityException,
471 UnsupportedEncodingException {
472 ClientWorkflow workflow = (ClientWorkflow)session.getAttribute(KEY_WORKFLOWINSTANCE);
473 workflow.update();
474 checkWorkflowVersion(session, workflow);
475 String placeid = (String)session.getAttribute(KEY_PLACEID);
476 Place place = workflow.getPlace(placeid);
477
478 Token newtoken = createToken(request);
479
480 place.addToken(newtoken);
481
482 showConfirmationDialog(session, response);
483 return false;
484 }
485
486 private boolean handleFireRequest(HttpServletRequest request, HttpSession session, HttpServletResponse response)
487 throws IOException,
488 IncompatibleVersionsException,
489 RemoteException,
490 IllegalArgumentException,
491 IllegalModificationException,
492 WorkflowFormatException,
493 CapacityException,
494 UnsupportedEncodingException,
495 ServletException {
496 ClientWorkflow workflow = (ClientWorkflow)session.getAttribute(KEY_WORKFLOWINSTANCE);
497 workflow.update();
498 checkWorkflowVersion(session, workflow);
499 String transitionid = (String)session.getAttribute(KEY_TRANSITIONID);
500 Transition transition = workflow.getTransition(transitionid);
501 int outedgeindex = ((Integer)session.getAttribute(KEY_OUTEDGEINDEX)).intValue();
502 Edge[] outedges = transition.getOutEdges();
503 List tokens = (List)session.getAttribute(KEY_TOKENLIST);
504 Place currentplace = outedges[outedgeindex].getPlace();
505
506 Token token = createToken(request);
507 tokens.add(token);
508
509 if (outedgeindex == outedges.length-1) {
510 getLogger().debug("Removing one token from each input place of transition "+transitionid);
511
512
513 Edge[] inedges = transition.getInEdges();
514 for (int i=0; i<inedges.length; i++) {
515 inedges[i].getPlace().removeToken(0);
516 }
517
518 getLogger().debug("Adding tokens to output places of transition "+transitionid);
519
520 for (int i=0; i<outedges.length; i++) {
521 token = (Token)tokens.get(i);
522 outedges[i].getPlace().addToken(token);
523 }
524
525 showConfirmationDialog(session, response);
526
527 return false;
528 } else {
529
530 outedgeindex++;
531 session.setAttribute(KEY_OUTEDGEINDEX, new Integer(outedgeindex));
532 currentplace = outedges[outedgeindex].getPlace();
533
534 String servlet = "/FormSelectionServlet";
535 ServletContext ctx = getServletContext();
536 RequestDispatcher rq = ctx.getRequestDispatcher(servlet);
537
538 getLogger().info("Forwarding request to "+servlet+" for place "+currentplace.getID());
539
540 session.setAttribute(KEY_ACTION, FORM_ACTION);
541
542 rq.forward(request, response);
543
544 return true;
545 }
546 }
547
548 /***
549 Create a token from the specified request.
550 */
551 protected abstract Token createToken(HttpServletRequest request)
552 throws WorkflowFormatException, UnsupportedEncodingException, IllegalArgumentException;
553
554
555
556
557
558 /***
559 Show a confirmation dialog after successful workflow manipulation.
560 */
561 protected void showConfirmationDialog(HttpSession session, HttpServletResponse response) throws IOException {
562 PrintWriter out = response.getWriter();
563 out.println("<html>\n");
564 ServletUtilities.printHeadWithTitle("OK", out);
565 out.println("<body>\n"+
566 "<h2>Data has been inserted into the workflow.</h2><br>\n");
567 out.println("</body></html>\n");
568 }
569
570 /***
571 Show an error dialog.
572 */
573 protected void showErrorDialog(HttpSession session, HttpServletResponse response, String message, Exception exception) throws IOException {
574 PrintWriter out = response.getWriter();
575 out.println("<html>\n");
576 ServletUtilities.printHeadWithTitle("Error", out);
577 out.println("<body>\n"+
578 "<h2>Your request could not be handled!</h2><br>\n"+
579 "<b>"+message+"</b><br><br>\n");
580 out.println("<pre>");
581 exception.printStackTrace(out);
582 out.println("</pre>");
583 out.println("</body></html>\n");
584 }
585 }