/*
* Danet GmbH
* Beratung und Software-Entwicklung
* Gesch�ftstelle AN
*
* $Id: TransMgr.java 2368 2007-05-03 21:58:25Z mlipp $
*
* $Log$
* Revision 1.2 2007/03/27 21:59:42 mlipp
* Fixed lots of checkstyle warnings.
*
* Revision 1.1.1.3 2004/08/18 15:18:46 drmlipp
* Update to 1.2
*
* Revision 1.43 2004/05/05 09:44:07 lipp
* Finished SAX based process creation (no cleanup of old code, yet).
*
* Revision 1.42 2004/02/21 14:42:43 lipp
* Moved TransitionManager to domain package. Having it in its own
* package caused too many circular dependencies (and there are good
* points for having it in the domain package anyway).
*
* Revision 1.41 2003/09/19 13:12:29 lipp
* Adapted to closed.completed having a substate.
*
* Revision 1.40 2003/05/31 20:05:25 lipp
* Added support for different condition types.
*
* Revision 1.39 2003/04/26 16:12:35 lipp
* Moved some classes to reduce package dependencies.
*
* Revision 1.38 2003/04/24 20:51:21 lipp
* Fixed some warnings.
*
* Revision 1.37 2003/02/03 10:09:27 lipp
* Adapted to latest changes in src.
*
* Revision 1.36 2002/11/25 21:16:23 lipp
* Adapted to transition interface changes.
*
* Revision 1.35 2002/11/19 15:14:52 lipp
* New transition manager.
*
* Revision 1.34 2002/11/15 15:13:54 lipp
* Clarified usage of transitions as attribute. Included in caching.
*
* Revision 1.33 2002/11/11 12:20:45 lipp
* Some fixes.
*
* Revision 1.32 2002/11/04 08:40:25 barzik
* adapted to several modifications in the workflow component
*
* Revision 1.31 2002/10/21 19:08:05 lipp
* Continuing implementation of new state handling.
*
* Revision 1.30 2002/10/09 15:17:51 lipp
* Improved start mode handling.
*
* Revision 1.29 2002/10/09 14:27:33 lipp
* Intermediate, compilable state.
*
* Revision 1.28 2002/09/11 14:17:22 lipp
* Execptions using msgs now.
*
* Revision 1.27 2002/09/08 18:46:39 lipp
* Clean up.
*
* Revision 1.26 2002/08/30 13:37:05 lipp
* Using Workflow engine facade now.
*
* Revision 1.25 2002/08/30 09:06:40 lipp
* Renamed internal state to typed state and use polymorphism for type
* names where possible.
*
* Revision 1.24 2002/08/28 12:58:03 lipp
* Fixed unittests.
*
* Revision 1.23 2002/08/28 12:47:20 lipp
* Starting new process generation.
*
* Revision 1.22 2002/08/22 15:19:34 lipp
* Redesign of EJB persistence.
*
* Revision 1.21 2002/08/21 22:06:48 lipp
* Finished transition to ProcessMgrStub.
*
* Revision 1.20 2002/08/02 09:28:53 huaiyang
* Use JDOM.
*
* Revision 1.19 2002/06/27 10:47:35 lipp
* Adapted to change in return type.
*
* Revision 1.18 2002/05/27 14:55:29 lipp
* Adapted to API changes.
*
* Revision 1.17 2002/02/04 22:43:12 lipp
* Adapted tests to changed.
*
* Revision 1.16 2002/02/04 15:18:55 lipp
* Made ActivityFinderAndKey visible in EJB Remote interface.
*
* Revision 1.15 2002/02/03 21:41:41 lipp
* Cleaned up unittests.
*
* Revision 1.14 2002/01/23 15:42:04 lipp
* Adapted to interface changes.
*
* Revision 1.13 2001/12/20 09:39:03 lipp
* Adapted to modified method signature.
*
* Revision 1.12 2001/12/18 22:16:53 lipp
* Restructured DOM generation, implemented "assignments" method from ras.
*
* Revision 1.11 2001/12/15 12:47:07 lipp
* Getting an ActivityFinder implemented.
*
* Revision 1.10 2001/12/13 21:00:05 lipp
* Simplified temporary implementation of a requester.
*
* Revision 1.9 2001/12/13 09:41:04 lipp
* equals and hashCode methods added.
*
* Revision 1.8 2001/10/25 17:15:51 lipp
* Renamed getTransitionsRefs() to getNextActivities() (more appropriate
* name) and added TransitionRefs to DOM representatiosn.
*
* Revision 1.7 2001/10/23 14:07:15 lipp
* Adapted to interface changes.
*
* Revision 1.6 2001/10/09 11:55:07 montag
* TransitionManager now without ejbs.
* transitionref list now contains Activity elements.
*
* Revision 1.5 2001/10/09 07:43:51 montag
* BaseElement reactivated
*
* Revision 1.4 2001/10/08 10:58:24 lipp
* Adapted to interface/implementation separation in domain.
*
* Revision 1.3 2001/10/04 08:01:54 montag
* unittests adopt for new transitionmanager
*
* Revision 1.2 2001/09/27 07:26:08 montag
* Default behavior for activities is now
* AND-Join and AND-Split.
* TO DO: XOR-Join and XOR-Split
*
* Revision 1.1 2001/09/26 08:15:39 montag
* allToActivities() implemented.
* test case for transition manager.
*
*
*/
package domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.rmi.RemoteException;
import de.danet.an.workflow.localapi.ActivityLocal;
import de.danet.an.workflow.localcoreapi.WfActivityLocal;
import de.danet.an.workflow.omgcore.AlreadyRunningException;
import de.danet.an.workflow.omgcore.CannotStartException;
import de.danet.an.workflow.omgcore.InvalidDataException;
import de.danet.an.workflow.omgcore.InvalidStateException;
import de.danet.an.workflow.omgcore.ProcessData;
import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
import de.danet.an.workflow.omgcore.WfActivity;
import de.danet.an.workflow.omgcore.WfExecutionObject.ClosedState;
import de.danet.an.workflow.omgcore.WfExecutionObject.OpenState;
import de.danet.an.workflow.omgcore.WfExecutionObject.State;
import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.Transition;
import de.danet.an.workflow.api.Activity.JoinAndSplitMode;
import de.danet.an.workflow.api.Activity.StartFinishMode;
import de.danet.an.workflow.domain.TransitionDefinition;
import de.danet.an.workflow.domain.TransitionDefinitionLocal;
import de.danet.an.workflow.domain.TransitionManager;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Zusammenstellung aller TransitionMgrTests.
* @author <a href="mailto:lipp@danet.de"></a>
* @version 1.0
*/
public class TransMgr extends TestCase {
/**
* Konstruktor zum Erzeugen eines TestCase
* @param name a <code>String</code> value
*/
public TransMgr(String name) {
super (name);
}
/**
* Stellt diese TestSuite zusammen.
* @return a <code>Test</code> value
*/
public static Test suite() {
TestSuite suite = new TestSuite();
// Because the transition manager currently
// works only on EJB, the tests are deactivated
// until there are new interfaces for process and
// activity to e.g. retrieve the transition ref list
// from a process.
suite.addTest(new TransMgr("transTest1"));
suite.addTest(new TransMgr("joinAndSplit1"));
return suite;
}
/**
* Test for Process with activities and transitions
* @exception Exception if an error occurs
*/
public void transTest1() throws Exception {
TestProcess p = getProcess();
assertTrue (p != null);
ActivityLocal a1 = getActivity(p,1);
assertTrue (a1 != null);
ActivityLocal a2 = getActivity(p,2);
assertTrue (a2 != null);
Collection c = new Vector();
c.add(a1);
c.add(a2);
p.setActivities(c);
String group = "t";
int order = 0;
p.createTransition("t",group, order, a1,a2);
p.testTransitionManager();
}
/**
* Test for Process with activities and transitions
* @exception Exception if an error occurs
*/
public void joinAndSplit1() throws Exception {
TestProcess p = getProcess();
assertTrue (p != null);
TestActivity[] a = new TestActivity[9];
Collection c = new Vector();
for (int i = 0; i <9; i++) {
a[i] = (TestActivity)getActivity(p,i);
a[i].setJoinMode(JoinAndSplitMode.AND);
a[i].setSplitMode(JoinAndSplitMode.AND);
c.add(a[i]);
}
p.setActivities(c);
List tr = new ArrayList();
TransitionDefinitionLocal[] t = new TransitionDefinitionLocal[12];
t[0]= new TransitionDefinitionLocal
(p.key(),"0","0",0,a[0],a[1],Transition.COND_TYPE_CONDITION,null);
t[1]= new TransitionDefinitionLocal
(p.key(),"1","1",0,a[0],a[2],Transition.COND_TYPE_CONDITION,null);
t[2]= new TransitionDefinitionLocal
(p.key(),"2","2",0,a[1],a[3],Transition.COND_TYPE_CONDITION,null);
t[3]= new TransitionDefinitionLocal
(p.key(),"3","3",0,a[1],a[4],Transition.COND_TYPE_CONDITION,null);
t[4]= new TransitionDefinitionLocal
(p.key(),"4","4",0,a[2],a[4],Transition.COND_TYPE_CONDITION,null);
t[5]= new TransitionDefinitionLocal
(p.key(),"5","5",0,a[2],a[5],Transition.COND_TYPE_CONDITION,null);
t[6]= new TransitionDefinitionLocal
(p.key(),"6","6",0,a[3],a[6],Transition.COND_TYPE_CONDITION,null);
t[7]= new TransitionDefinitionLocal
(p.key(),"7","7",0,a[4],a[6],Transition.COND_TYPE_CONDITION,null);
t[8]= new TransitionDefinitionLocal
(p.key(),"8","8",0,a[4],a[7],Transition.COND_TYPE_CONDITION,null);
t[9]= new TransitionDefinitionLocal
(p.key(),"9","9",0,a[5],a[7],Transition.COND_TYPE_CONDITION,null);
t[10]= new TransitionDefinitionLocal
(p.key(),"10","10",0,a[6],a[8],Transition.COND_TYPE_CONDITION,null);
t[11]= new TransitionDefinitionLocal
(p.key(),"11","11",0,a[7],a[8],Transition.COND_TYPE_CONDITION,null);
for (int i = 0; i <12; i++) {
p.createTransition (t[i].id(), t[i].group(), t[i].order(),
t[i].from(), t[i].to());
}
p.start();
for (int i = 0; i < 9; i++) {
assertTrue(a[i].state().startsWith("closed.completed"));
}
}
//////////////////////////////////////////////////////////
private TestProcess getProcess() {
return new TestProcess("p1");
}
/**
* Describe class <code>TestProcess</code> here.
*
*/
public class TestProcess extends VolatileProcess {
/**
* Creates a new <code>TestProcess</code> instance.
*
* @param key a <code>String</code> value
*/
public TestProcess (String key) {
super (key);
}
/**
* Test the TransitionManager.<p>
* Preconditions:
* <ul>
* <li>The process has not been started.</li>
* <li>There a two activities an one transition
* between these two activities.</li>
* </ul>
* @exception Exception if an error occurs
*/
public void testTransitionManager() throws Exception {
assertTrue(transitionsLocal().size() > 0);
TransitionManager tm = transitionManager();
assertTrue(tm != null);
Collection a = steps();
// Test allOpenableActivities()
Collection c = tm.startableActivities();
// only the first activity should be openable
assertTrue(c.size() == 1);
}
/**
* Describe <code>start</code> method here.
*
* @exception CannotStartException if an error occurs
* @exception AlreadyRunningException if an error occurs
*/
public void start ()
throws CannotStartException, AlreadyRunningException {
// set process state
super.start();
// start all activities
TransitionManager tm = transitionManager();
Collection activitiesToStart = tm.startableActivities();
Iterator it = activitiesToStart.iterator();
while (it.hasNext()) {
WfActivityLocal a = (WfActivityLocal)it.next();
try {
// Due to concurrency, an activity
// could be started interim.
if (!a.workflowState()
.equals(State.CLOSED)
&& a.state()
.equals("open.not_running.not_runnable")) {
a.setProcessContext(null);
}
} catch (InvalidDataException ide) {
throw new CannotStartException("");
} catch (UpdateNotAllowedException ue) {
throw new CannotStartException("");
}
}
}
/**
* This method notifies the process that one of its
* activities has changed its state. For the 1. iteration
* an activity signals that it has been completed.
* The next activity (if exists) are searched and started.
* @param i a <code>Long</code> value
* @param s the actual state of the activity
*/
public void complete (Long i, State s) {
try {
// checkAllActivities
if (transitionManager().isAtEnd()) {
// TODO: remove condition check
if (!State.fromString(state())
.isSameOrSubState(ClosedState.ABORTED)) {
// last activity executed, finish process
updateState(ClosedState.COMPLETED);
}
} else {
// start all runnableActivities
Collection activitiesToStart = transitionManager()
.startableActivities();
Iterator it = activitiesToStart.iterator();
while (it.hasNext()) {
WfActivity a = (WfActivity)it.next();
try {
// Due to concurrency, an activity
// could be started interim.
if (!a.workflowState()
.equals(State.CLOSED)
&& a.state()
.equals("open.not_running.not_runnable")) {
a.setProcessContext(null);
}
} catch (UpdateNotAllowedException ue) {
ue.printStackTrace();
}
}
}
} catch (RemoteException re) {
re.printStackTrace();
} catch (InvalidStateException re) {
re.printStackTrace();
} catch (InvalidDataException re) {
re.printStackTrace();
}
}
}
private ActivityLocal getActivity(TestProcess p, int i) {
return new TestActivity(p,i);
}
/**
* Describe class <code>TestActivity</code> here.
*
*/
public class TestActivity extends VolatileActivity {
private int id;
/**
* Describe <code>thisRemote</code> method here.
*
* @return an <code>Activity</code> value
*/
protected Activity thisRemote () {
return new ActivityRemoteWrapper(this);
}
/**
* Creates a new <code>TestActivity</code> instance.
*
* @param p a <code>TestProcess</code> value
* @param i an <code>int</code> value
*/
public TestActivity(TestProcess p, int i) {
super (p, "ta" + i);
id = i;
setPaName ("ta" + i);
setPaDescription ("ta" + i);
}
/**
* Describe <code>setProcessContext</code> method here.
*
* @param newValue a <code>ProcessData</code> value
* @exception InvalidDataException if an error occurs
* @exception UpdateNotAllowedException if an error occurs
*/
public void setProcessContext (ProcessData newValue)
throws InvalidDataException, UpdateNotAllowedException {
try {
updateState (OpenState.NOT_RUNNING);
if (startMode() == StartFinishMode.MANUAL) {
// hold state
} else {
if (finishMode() == StartFinishMode.MANUAL) {
changeState("open.running");
} else {
changeState("open.running");
updateState (ClosedState.COMPLETED);
}
}
} catch (InvalidStateException ie) {
throw new UpdateNotAllowedException(ie.getMessage());
} catch (TransitionNotAllowedException te) {
throw new UpdateNotAllowedException(te.getMessage());
}
}
/**
* Describe <code>pubSetState</code> method here.
*
* @param s a <code>State</code> value
* @exception InvalidStateException if an error occurs
*/
public void pubSetState (State s)
throws InvalidStateException {
updateState (s);
}
}
}