Source code: domain/TransMgr.java
1 /*
2 * Danet GmbH
3 * Beratung und Software-Entwicklung
4 * Geschäftstelle AN
5 *
6 * $Id: TransMgr.java,v 1.41 2003/09/19 13:12:29 lipp Exp $
7 *
8 * $Log: TransMgr.java,v $
9 * Revision 1.41 2003/09/19 13:12:29 lipp
10 * Adapted to closed.completed having a substate.
11 *
12 * Revision 1.40 2003/05/31 20:05:25 lipp
13 * Added support for different condition types.
14 *
15 * Revision 1.39 2003/04/26 16:12:35 lipp
16 * Moved some classes to reduce package dependencies.
17 *
18 * Revision 1.38 2003/04/24 20:51:21 lipp
19 * Fixed some warnings.
20 *
21 * Revision 1.37 2003/02/03 10:09:27 lipp
22 * Adapted to latest changes in src.
23 *
24 * Revision 1.36 2002/11/25 21:16:23 lipp
25 * Adapted to transition interface changes.
26 *
27 * Revision 1.35 2002/11/19 15:14:52 lipp
28 * New transition manager.
29 *
30 * Revision 1.34 2002/11/15 15:13:54 lipp
31 * Clarified usage of transitions as attribute. Included in caching.
32 *
33 * Revision 1.33 2002/11/11 12:20:45 lipp
34 * Some fixes.
35 *
36 * Revision 1.32 2002/11/04 08:40:25 barzik
37 * adapted to several modifications in the workflow component
38 *
39 * Revision 1.31 2002/10/21 19:08:05 lipp
40 * Continuing implementation of new state handling.
41 *
42 * Revision 1.30 2002/10/09 15:17:51 lipp
43 * Improved start mode handling.
44 *
45 * Revision 1.29 2002/10/09 14:27:33 lipp
46 * Intermediate, compilable state.
47 *
48 * Revision 1.28 2002/09/11 14:17:22 lipp
49 * Execptions using msgs now.
50 *
51 * Revision 1.27 2002/09/08 18:46:39 lipp
52 * Clean up.
53 *
54 * Revision 1.26 2002/08/30 13:37:05 lipp
55 * Using Workflow engine facade now.
56 *
57 * Revision 1.25 2002/08/30 09:06:40 lipp
58 * Renamed internal state to typed state and use polymorphism for type
59 * names where possible.
60 *
61 * Revision 1.24 2002/08/28 12:58:03 lipp
62 * Fixed unittests.
63 *
64 * Revision 1.23 2002/08/28 12:47:20 lipp
65 * Starting new process generation.
66 *
67 * Revision 1.22 2002/08/22 15:19:34 lipp
68 * Redesign of EJB persistence.
69 *
70 * Revision 1.21 2002/08/21 22:06:48 lipp
71 * Finished transition to ProcessMgrStub.
72 *
73 * Revision 1.20 2002/08/02 09:28:53 huaiyang
74 * Use JDOM.
75 *
76 * Revision 1.19 2002/06/27 10:47:35 lipp
77 * Adapted to change in return type.
78 *
79 * Revision 1.18 2002/05/27 14:55:29 lipp
80 * Adapted to API changes.
81 *
82 * Revision 1.17 2002/02/04 22:43:12 lipp
83 * Adapted tests to changed.
84 *
85 * Revision 1.16 2002/02/04 15:18:55 lipp
86 * Made ActivityFinderAndKey visible in EJB Remote interface.
87 *
88 * Revision 1.15 2002/02/03 21:41:41 lipp
89 * Cleaned up unittests.
90 *
91 * Revision 1.14 2002/01/23 15:42:04 lipp
92 * Adapted to interface changes.
93 *
94 * Revision 1.13 2001/12/20 09:39:03 lipp
95 * Adapted to modified method signature.
96 *
97 * Revision 1.12 2001/12/18 22:16:53 lipp
98 * Restructured DOM generation, implemented "assignments" method from ras.
99 *
100 * Revision 1.11 2001/12/15 12:47:07 lipp
101 * Getting an ActivityFinder implemented.
102 *
103 * Revision 1.10 2001/12/13 21:00:05 lipp
104 * Simplified temporary implementation of a requester.
105 *
106 * Revision 1.9 2001/12/13 09:41:04 lipp
107 * equals and hashCode methods added.
108 *
109 * Revision 1.8 2001/10/25 17:15:51 lipp
110 * Renamed getTransitionsRefs() to getNextActivities() (more appropriate
111 * name) and added TransitionRefs to DOM representatiosn.
112 *
113 * Revision 1.7 2001/10/23 14:07:15 lipp
114 * Adapted to interface changes.
115 *
116 * Revision 1.6 2001/10/09 11:55:07 montag
117 * TransitionManager now without ejbs.
118 * transitionref list now contains Activity elements.
119 *
120 * Revision 1.5 2001/10/09 07:43:51 montag
121 * BaseElement reactivated
122 *
123 * Revision 1.4 2001/10/08 10:58:24 lipp
124 * Adapted to interface/implementation separation in domain.
125 *
126 * Revision 1.3 2001/10/04 08:01:54 montag
127 * unittests adopt for new transitionmanager
128 *
129 * Revision 1.2 2001/09/27 07:26:08 montag
130 * Default behavior for activities is now
131 * AND-Join and AND-Split.
132 * TO DO: XOR-Join and XOR-Split
133 *
134 * Revision 1.1 2001/09/26 08:15:39 montag
135 * allToActivities() implemented.
136 * test case for transition manager.
137 *
138 *
139 */
140 package domain;
141
142 import java.util.ArrayList;
143 import java.util.Collection;
144 import java.util.Iterator;
145 import java.util.List;
146 import java.util.Vector;
147
148 import java.rmi.RemoteException;
149
150 import de.danet.an.workflow.omgcore.AlreadyRunningException;
151 import de.danet.an.workflow.omgcore.CannotStartException;
152 import de.danet.an.workflow.omgcore.InvalidDataException;
153 import de.danet.an.workflow.omgcore.InvalidStateException;
154 import de.danet.an.workflow.omgcore.ProcessData;
155 import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
156 import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
157 import de.danet.an.workflow.omgcore.WfActivity;
158
159 import de.danet.an.workflow.api.Activity;
160 import de.danet.an.workflow.api.Transition;
161
162 import de.danet.an.workflow.domain.TransitionDefinition;
163
164 import de.danet.an.workflow.engine.TransitionManager;
165
166 import junit.framework.Test;
167 import junit.framework.TestCase;
168 import junit.framework.TestSuite;
169
170 /**
171 * Zusammenstellung aller TransitionMgrTests.
172 * @author <a href="mailto:lipp@danet.de"></a>
173 * @version 1.0
174 */
175 public class TransMgr extends TestCase {
176
177 /**
178 * Konstruktor zum Erzeugen eines TestCase
179 * @param name a <code>String</code> value
180 */
181 public TransMgr(String name) {
182 super (name);
183 }
184
185 /**
186 * Stellt diese TestSuite zusammen.
187 * @return a <code>Test</code> value
188 */
189 public static Test suite() {
190 TestSuite suite = new TestSuite();
191 // Because the transition manager currently
192 // works only on EJB, the tests are deactivated
193 // until there are new interfaces for process and
194 // activity to e.g. retrieve the transition ref list
195 // from a process.
196 suite.addTest(new TransMgr("transTest1"));
197 suite.addTest(new TransMgr("joinAndSplit1"));
198 return suite;
199 }
200
201 /**
202 * Test for Process with activities and transitions
203 * @exception Exception if an error occurs
204 */
205 public void transTest1() throws Exception {
206 TestProcess p = getProcess();
207 assertTrue (p != null);
208 Activity a1 = getActivity(p,1);
209 assertTrue (a1 != null);
210 Activity a2 = getActivity(p,2);
211 assertTrue (a2 != null);
212 Collection c = new Vector();
213 c.add(a1);
214 c.add(a2);
215 p.setActivities(c);
216 String group = "t";
217 int order = 0;
218 p.createTransition("t",group, order, a1,a2);
219 p.testTransitionManager();
220 }
221
222 /**
223 * Test for Process with activities and transitions
224 * @exception Exception if an error occurs
225 */
226 public void joinAndSplit1() throws Exception {
227 TestProcess p = getProcess();
228 assertTrue (p != null);
229 TestActivity a[] = new TestActivity[9];
230 Collection c = new Vector();
231 for (int i = 0; i <9; i++) {
232 a[i] = (TestActivity)getActivity(p,i);
233 a[i].setJoinAndSplitMode("AND", "AND");
234 c.add(a[i]);
235 }
236 p.setActivities(c);
237 List tr = new ArrayList();
238 TransitionDefinition t[] = new TransitionDefinition[12];
239 t[0]= new TransitionDefinition
240 (p.key(),"0","0",0,a[0],a[1],Transition.COND_TYPE_CONDITION,null);
241 t[1]= new TransitionDefinition
242 (p.key(),"1","1",0,a[0],a[2],Transition.COND_TYPE_CONDITION,null);
243 t[2]= new TransitionDefinition
244 (p.key(),"2","2",0,a[1],a[3],Transition.COND_TYPE_CONDITION,null);
245 t[3]= new TransitionDefinition
246 (p.key(),"3","3",0,a[1],a[4],Transition.COND_TYPE_CONDITION,null);
247 t[4]= new TransitionDefinition
248 (p.key(),"4","4",0,a[2],a[4],Transition.COND_TYPE_CONDITION,null);
249 t[5]= new TransitionDefinition
250 (p.key(),"5","5",0,a[2],a[5],Transition.COND_TYPE_CONDITION,null);
251 t[6]= new TransitionDefinition
252 (p.key(),"6","6",0,a[3],a[6],Transition.COND_TYPE_CONDITION,null);
253 t[7]= new TransitionDefinition
254 (p.key(),"7","7",0,a[4],a[6],Transition.COND_TYPE_CONDITION,null);
255 t[8]= new TransitionDefinition
256 (p.key(),"8","8",0,a[4],a[7],Transition.COND_TYPE_CONDITION,null);
257 t[9]= new TransitionDefinition
258 (p.key(),"9","9",0,a[5],a[7],Transition.COND_TYPE_CONDITION,null);
259 t[10]= new TransitionDefinition
260 (p.key(),"10","10",0,a[6],a[8],Transition.COND_TYPE_CONDITION,null);
261 t[11]= new TransitionDefinition
262 (p.key(),"11","11",0,a[7],a[8],Transition.COND_TYPE_CONDITION,null);
263 for (int i = 0; i <12; i++) {
264 p.createTransition (t[i].id(), t[i].group(), t[i].order(),
265 t[i].from(), t[i].to());
266 }
267 p.start();
268
269 for (int i = 0; i < 9; i++) {
270 assertTrue(a[i].state().startsWith("closed.completed"));
271 }
272
273 }
274
275 //////////////////////////////////////////////////////////
276
277 private TestProcess getProcess() {
278 return new TestProcess("p1");
279 }
280
281 /**
282 * Describe class <code>TestProcess</code> here.
283 *
284 */
285 public class TestProcess extends VolatileProcess {
286
287 /**
288 * Creates a new <code>TestProcess</code> instance.
289 *
290 * @param key a <code>String</code> value
291 */
292 public TestProcess (String key) {
293 super (key);
294 }
295
296 /**
297 * Test the TransitionManager.<p>
298 * Preconditions:
299 * <ul>
300 * <li>The process has not been started.</li>
301 * <li>There a two activities an one transition
302 * between these two activities.</li>
303 * </ul>
304 * @exception Exception if an error occurs
305 */
306 public void testTransitionManager() throws Exception {
307 assertTrue(transitions().size() > 0);
308 TransitionManager tm = transitionManager();
309 assertTrue(tm != null);
310 Collection a = steps();
311 // Test allOpenableActivities()
312 Collection c = tm.startableActivities();
313 // only the first activity should be openable
314 assertTrue(c.size() == 1);
315 }
316
317 /**
318 * Describe <code>start</code> method here.
319 *
320 * @exception RemoteException if an error occurs
321 * @exception CannotStartException if an error occurs
322 * @exception AlreadyRunningException if an error occurs
323 */
324 public void start () throws RemoteException,
325 CannotStartException, AlreadyRunningException {
326 // set process state
327 super.start();
328
329 // start all activities
330 TransitionManager tm = transitionManager();
331 Collection activitiesToStart = tm.startableActivities();
332 Iterator it = activitiesToStart.iterator();
333 while (it.hasNext()) {
334 WfActivity a = (WfActivity)it.next();
335 try {
336 // Due to concurrency, an activity
337 // could be started interim.
338 if (!a.workflowState()
339 .equals(State.CLOSED)
340 && a.state()
341 .equals("open.not_running.not_runnable")) {
342 a.setProcessContext(null);
343 }
344 } catch (InvalidDataException ide) {
345 throw new CannotStartException("");
346 } catch (UpdateNotAllowedException ue) {
347 throw new CannotStartException("");
348 }
349 }
350 }
351
352 /**
353 * This method notifies the process that one of its
354 * activities has changed its state. For the 1. iteration
355 * an activity signals that it has been completed.
356 * The next activity (if exists) are searched and started.
357 * @param i a <code>Long</code> value
358 * @param s the actual state of the activity
359 */
360 public void complete (Long i, State s) {
361 try {
362 // checkAllActivities
363 if (transitionManager().isAtEnd()) {
364 // TODO: remove condition check
365 if (!State.fromString(state())
366 .isSameOrSubState(ClosedState.ABORTED)) {
367 // last activity executed, finish process
368 updateState(ClosedState.COMPLETED);
369 }
370 } else {
371 // start all runnableActivities
372 Collection activitiesToStart = transitionManager()
373 .startableActivities();
374 Iterator it = activitiesToStart.iterator();
375 while (it.hasNext()) {
376 WfActivity a = (WfActivity)it.next();
377 try {
378 // Due to concurrency, an activity
379 // could be started interim.
380 if (!a.workflowState()
381 .equals(State.CLOSED)
382 && a.state()
383 .equals("open.not_running.not_runnable")) {
384 a.setProcessContext(null);
385 }
386 } catch (UpdateNotAllowedException ue) {
387 ue.printStackTrace();
388 }
389 }
390 }
391 } catch (RemoteException re) {
392 re.printStackTrace();
393 } catch (InvalidStateException re) {
394 re.printStackTrace();
395 } catch (InvalidDataException re) {
396 re.printStackTrace();
397 }
398 }
399 }
400
401 private Activity getActivity(TestProcess p, int i) {
402 return new TestActivity(p,i);
403 }
404
405 /**
406 * Describe class <code>TestActivity</code> here.
407 *
408 */
409 public class TestActivity extends VolatileActivity {
410
411 private int id;
412
413 /**
414 * Describe <code>thisRemote</code> method here.
415 *
416 * @return an <code>Activity</code> value
417 */
418 protected Activity thisRemote () {
419 return this;
420 }
421 /**
422 * Describe <code>setJoinAndSplitMode</code> method here.
423 *
424 * @param join a <code>String</code> value
425 * @param split a <code>String</code> value
426 */
427 public void setJoinAndSplitMode(String join, String split) {
428 setInternalJoinMode(join);
429 setInternalSplitMode(split);
430 }
431 /**
432 * Creates a new <code>TestActivity</code> instance.
433 *
434 * @param p a <code>TestProcess</code> value
435 * @param i an <code>int</code> value
436 */
437 public TestActivity(TestProcess p, int i) {
438 super (p, "ta" + i);
439 id = i;
440 setPaName ("ta" + i);
441 setPaDescription ("ta" + i);
442 }
443 /**
444 * Describe <code>setProcessContext</code> method here.
445 *
446 * @param newValue a <code>ProcessData</code> value
447 * @exception InvalidDataException if an error occurs
448 * @exception UpdateNotAllowedException if an error occurs
449 * @exception RemoteException if an error occurs
450 */
451 public void setProcessContext (ProcessData newValue)
452 throws InvalidDataException, UpdateNotAllowedException,
453 RemoteException {
454
455 try {
456 updateState (OpenState.NOT_RUNNING);
457
458 if (startMode() == StartFinishMode.MANUAL) {
459 // hold state
460 } else {
461 if (finishMode() == StartFinishMode.MANUAL) {
462 changeState("open.running");
463 } else {
464 changeState("open.running");
465 updateState (ClosedState.COMPLETED);
466 }
467 }
468 } catch (InvalidStateException ie) {
469 throw new UpdateNotAllowedException(ie.getMessage());
470 } catch (TransitionNotAllowedException te) {
471 throw new UpdateNotAllowedException(te.getMessage());
472 }
473 }
474
475 /**
476 * Describe <code>pubSetState</code> method here.
477 *
478 * @param s a <code>State</code> value
479 * @exception InvalidStateException if an error occurs
480 */
481 public void pubSetState (State s)
482 throws InvalidStateException {
483 updateState (s);
484 }
485
486 }
487 }
488