package com.c2kernel.lifecycle.instance; import java.util.ArrayList; import java.util.Map; import java.util.Vector; import com.c2kernel.common.AccessRightsException; import com.c2kernel.common.GTimeStamp; import com.c2kernel.common.InvalidDataException; import com.c2kernel.common.InvalidTransitionException; import com.c2kernel.common.ObjectAlreadyExistsException; import com.c2kernel.common.ObjectNotFoundException; import com.c2kernel.common.PersistencyException; import com.c2kernel.entity.agent.Job; import com.c2kernel.events.Event; import com.c2kernel.events.History; import com.c2kernel.graph.model.Vertex; import com.c2kernel.lifecycle.WfCastorHashMap; import com.c2kernel.lifecycle.instance.stateMachine.State; import com.c2kernel.lifecycle.instance.stateMachine.StateMachine; import com.c2kernel.lifecycle.instance.stateMachine.Transition; import com.c2kernel.lookup.AgentPath; import com.c2kernel.lookup.LDAPRoleManager; import com.c2kernel.lookup.RolePath; import com.c2kernel.persistency.ClusterStorageException; import com.c2kernel.persistency.outcome.Outcome; import com.c2kernel.persistency.outcome.Schema; import com.c2kernel.persistency.outcome.Viewpoint; import com.c2kernel.process.Gateway; import com.c2kernel.utils.DateUtility; import com.c2kernel.utils.LocalObjectLoader; import com.c2kernel.utils.Logger; /** * @version $Revision: 1.222 $ $Date: 2005/10/05 07:39:37 $ * @author $Author: abranson $ */ public class Activity extends WfVertex { /** * vector of errors (Strings) that is constructed each time verify() is launched */ protected Vector mErrors; /** @associates a State machine engine */ private StateMachine machine; protected int state = -1; /** true is available to be executed */ public boolean active = false; /** used in verify() */ private boolean loopTested; private GTimeStamp mStateDate; private String mType; public Activity() { super(); setProperties(new WfCastorHashMap()); getProperties().put("StateMachineName", getDefaultSMName()); mErrors = new Vector(0, 1); mStateDate = new GTimeStamp(); DateUtility.setToNow(mStateDate); } protected String getDefaultSMName() { return "Default"; } /** add the activity which id is idNext as next of the current one */ void addNext(String idNext) { new Next(this, (WfVertex) getParent().search(idNext)); } /** * adds a New link between the current Activity and the WfVertex passed in param */ @Override public Next addNext(WfVertex vertex) { return new Next(this, vertex); } public StateMachine getStateMachine() throws InvalidDataException { if (machine == null) { String name = (String)getProperties().get("StateMachineName"); int version = getVersionNumberProperty("StateMachineVersion"); try { machine = LocalObjectLoader.getStateMachine(name, version); } catch (ObjectNotFoundException ex) { if (name.equals(getDefaultSMName()) && version == 0) { // default state machine not imported yet. Fake it. try { String marshalledSM = Gateway.getResource().getTextResource(null, "boot/SM/"+getDefaultSMName()+".xml"); StateMachine bootstrap = (StateMachine)Gateway.getMarshaller().unmarshall(marshalledSM); bootstrap.validate(); machine = bootstrap; return bootstrap; } catch (Exception ex2) { Logger.error(ex2); throw new InvalidDataException("Could not bootstrap default state machine from resources.", ""); } } Logger.error(ex); throw new InvalidDataException("Error loading state machine '"+name+"' v"+version, ""); } } return machine; } /** return the current State of the State machine (Used in Serialisation) */ public int getState() throws InvalidDataException { if (state == -1) state = getStateMachine().getInitialStateCode(); return state; } public String getStateName() throws InvalidDataException { return getStateMachine().getState(getState()).getName(); } /** Sets a new State */ public void setState(int state) { this.state = state; } public boolean isFinished() throws InvalidDataException { return getStateMachine().getState(getState()).isFinished(); } /** cf Item request * @throws ObjectNotFoundException * @throws PersistencyException */ public void request(AgentPath agent, int itemSysKey, int transitionID, String requestData) throws AccessRightsException, InvalidTransitionException, InvalidDataException, ObjectNotFoundException, PersistencyException { // Find requested transition Transition transition = getStateMachine().getTransition(transitionID); // Check if the transition is possible String usedRole = transition.getPerformingRole(this, agent); // Verify outcome Schema schema = null; String viewName = null; boolean storeOutcome = false; if (transition.hasOutcome(getProperties())) { schema = transition.getSchema(getProperties()); viewName = (String)getProperties().get("Viewpoint"); if (requestData != null && requestData.length()>0) storeOutcome = true; else if (transition.getOutcome().isRequired()) throw new InvalidDataException("Transition requires outcome data, but none was given", null); } // Get new state State newState = getStateMachine().traverse(this, transition, agent); // Run extra logic in predefined steps here runActivityLogic(agent, itemSysKey, transitionID, requestData); // set new state and reservation setState(newState.getId()); getProperties().put("Agent Name", transition.getReservation(this, agent)); // store new event Event newEvent = null; try { History hist = getWf().getHistory(); if (storeOutcome) newEvent = hist.addEvent(agent.getAgentName(), usedRole, getName(), getPath(), getType(), schema.docType, schema.docVersion, getStateMachine().getName(), getStateMachine().getVersion(), transition, viewName); else newEvent = hist.addEvent(agent.getAgentName(), usedRole, getName(), getPath(), getType(), getStateMachine().getName(), getStateMachine().getVersion(), transition); Logger.msg(7, "Activity::auditEvent() - Event:" + newEvent.getName() + " was added to the AuditTrail"); if (storeOutcome) { Outcome newOutcome = new Outcome(newEvent.getID(), requestData, schema.docType, schema.docVersion); Gateway.getStorage().put(itemSysKey, newOutcome, getWf()); // update specific view if defined if (viewName != null && !viewName.equals("")) { Viewpoint currentView = new Viewpoint(itemSysKey, schema.docType, viewName, schema.docVersion, newEvent.getID()); Gateway.getStorage().put(itemSysKey, currentView, getWf()); } // update last view Viewpoint currentView = new Viewpoint(itemSysKey, schema.docType, "last", schema.docVersion, newEvent.getID()); Gateway.getStorage().put(itemSysKey, currentView, getWf()); } Gateway.getStorage().commit(getWf()); } catch (ClusterStorageException ex) { Logger.error(ex); Gateway.getStorage().abort(getWf()); throw new PersistencyException("Exception storing event data"); } if (newState.isFinished()) { if (!getProperties().get("Breakpoint").equals(Boolean.TRUE)) runNext(agent, itemSysKey); } DateUtility.setToNow(mStateDate); //refresh all the job lists pushJobsToAgents(itemSysKey); } protected String runActivityLogic(AgentPath agent, int itemSysKey, int transitionID, String requestData) throws InvalidDataException { // Overriden in predefined steps return requestData; } @Override public boolean verify() { mErrors.removeAllElements(); int nbInEdgres = getInEdges().length; int nbOutEdges = getOutEdges().length; if (nbInEdgres == 0 && this.getID() != getParent().getChildrenGraphModel().getStartVertexId()) { mErrors.add("Unreachable"); return false; } else if (nbInEdgres > 1) { mErrors.add("Bad nb of previous"); return false; } else if (nbOutEdges > 1) { mErrors.add("too many next"); return false; } else if (nbOutEdges == 0) { if (!((CompositeActivity) getParent()).hasGoodNumberOfActivity()) { mErrors.add("too many endpoints"); return false; } } // else // { // Vertex[] outV = getOutGraphables(); // Vertex[] anteVertices = GraphTraversal.getTraversal(getParent().getChildrenGraphModel(), this, GraphTraversal.kUp, false); // boolean errInLoop = false; // for (int i = 0; i < outV.length; i++) // { // for (int j = 0; j < anteVertices.length; j++) // if (!loop() && outV[i].getID() == anteVertices[j].getID()) // errInLoop = true; // } // if (errInLoop) // { // mErrors.add("Error In Loop"); // return false; // } // } return true; } /** Used in verify() */ @Override public boolean loop() { boolean loop2 = false; if (!loopTested) { loopTested = true; if (getOutGraphables().length != 0) loop2 = ((WfVertex) getOutGraphables()[0]).loop(); } loopTested = false; return loop2; } /** sets the next activity available if possible * @throws ObjectNotFoundException * @throws AccessRightsException * @throws InvalidTransitionException * @throws PersistencyException * @throws ObjectAlreadyExistsException */ @Override public void runNext(AgentPath agent, int itemSysKey) throws InvalidDataException, InvalidTransitionException, AccessRightsException, ObjectNotFoundException, PersistencyException { setActive(false); try { Vertex[] outVertices = getOutGraphables(); Vertex[] outVertices2 = getOutGraphables(); boolean hasNoNext = false; boolean out = false; while (!out) if (outVertices2.length > 0) { if (outVertices2[0] instanceof Join) outVertices2 = ((WfVertex) outVertices2[0]).getOutGraphables(); else out = true; } else { hasNoNext = true; out = true; } Logger.debug(8, outVertices + " " + outVertices2); if (!hasNoNext) ((WfVertex) outVertices[0]).run(agent, itemSysKey); else { if (getParent() != null && getParent().getName().equals("domain")) // workflow // finished setActive(true); else { CompositeActivity parent = (CompositeActivity) getParent(); if (parent != null) parent.runNext(agent, itemSysKey); } } } catch (InvalidDataException s) { setActive(true); throw s; } } /** @return the only Next of the Activity */ public Next getNext() { if (getOutEdges().length > 0) return (Next) getOutEdges()[0]; else return null; } /** reinitialises the Activity and propagate (for Loop) * @throws InvalidDataException * @throws ObjectNotFoundException */ @Override public void reinit(int idLoop) throws InvalidDataException { Vertex[] outVertices = getOutGraphables(); setState(getStateMachine().getInitialState().getId()); if (outVertices.length > 0) { WfVertex nextAct = (WfVertex) outVertices[0]; nextAct.reinit(idLoop); } } /** return the String that identifies the errors found in th activity */ @Override public String getErrors() { if (mErrors.size() == 0) return "No error"; return mErrors.elementAt(0); } /** * called by precedent Activity runNext() for setting the activity able to be executed * @throws InvalidDataException * @throws ObjectAlreadyExistsException * @throws AccessRightsException * @throws InvalidTransitionException * @throws ObjectNotFoundException * @throws PersistencyException */ @Override public void run(AgentPath agent, int itemSysKey) throws InvalidDataException, InvalidTransitionException, AccessRightsException, ObjectNotFoundException, PersistencyException { Logger.debug(8, getPath() + " run " + getState()); if (!getActive()) setActive(true); boolean finished = getStateMachine().getState(getState()).isFinished(); if (finished) { runNext(agent, itemSysKey); } else { DateUtility.setToNow(mStateDate); pushJobsToAgents(itemSysKey); } } /** * sets the activity available to be executed on start of Workflow or composite activity (when it is the first one of the (sub)process * @throws InvalidDataException * @throws ObjectAlreadyExistsException * @throws ObjectNotFoundException * @throws AccessRightsException * @throws InvalidTransitionException * @throws PersistencyException */ @Override public void runFirst(AgentPath agent, int itemSysKey) throws InvalidDataException, InvalidTransitionException, AccessRightsException, ObjectNotFoundException, PersistencyException { Logger.debug(8, getPath() + " runfirst"); run(agent, itemSysKey); } /** @return the current ability to be executed */ public boolean getActive() { return active; } /** sets the ability to be executed */ public void setActive(boolean acti) { active = acti; } /** @return the Description field of properties */ public String getDescription() { if (getProperties().containsKey("Description")) return (String) (getProperties().get("Description")); return "No description"; } public String getCurrentAgentName() { return (String) getProperties().get("Agent Name"); } public String getCurrentAgentRole() { return (String) getProperties().get("Agent Role"); } /** * returns the lists of jobs for the activity and children (cf com.c2kernel.entity.Job) */ public ArrayList calculateJobs(AgentPath agent, int itemSysKey, boolean recurse) throws ObjectNotFoundException, InvalidDataException { return calculateJobsBase(agent, itemSysKey, false); } // public ArrayList calculateAllJobs(AgentPath agent, int itemSysKey, boolean recurse) throws ObjectNotFoundException, InvalidDataException { return calculateJobsBase(agent, itemSysKey, true); } private ArrayList calculateJobsBase(AgentPath agent, int itemSysKey, boolean includeInactive) throws ObjectNotFoundException, InvalidDataException { Logger.msg(7, "calculateJobs - " + getPath()); ArrayList jobs = new ArrayList(); Map transitions; if ((includeInactive || getActive()) && !getName().equals("domain")) { transitions = getStateMachine().getPossibleTransitions(this, agent); Logger.msg(7, "Activity.calculateJobs() - Got " + transitions.size() + " transitions."); for (Transition transition : transitions.keySet()) { Logger.msg(7, "Creating Job object for transition " + transition); jobs.add(new Job(this, itemSysKey, transition, agent, transitions.get(transition))); } } return jobs; } public void pushJobsToAgents(int itemSysKey) { String agentRole = getCurrentAgentRole(); if (agentRole == null || agentRole.length()==0) return; LDAPRoleManager roleMan = Gateway.getLDAPLookup().getRoleManager(); RolePath myRole; try { myRole = roleMan.getRolePath(agentRole); } catch (ObjectNotFoundException ex) { // non-existent role Logger.msg(7, "Activity.pushJobsToAgents() - Activity role '"+agentRole+" not found."); return; } if (myRole.hasJobList()) new JobPusher(this, itemSysKey, myRole).start(); } /** * Returns the startDate. * * @return GTimeStamp */ public GTimeStamp getStateDate() { return mStateDate; } public void setStateDate(GTimeStamp startDate) { mStateDate = startDate; } @Deprecated public void setActiveDate(GTimeStamp date) { } @Deprecated public void setStartDate(GTimeStamp date) { setStateDate(date); } /** * Returns the type. * * @return String */ public String getType() { return mType; } /** * Sets the type. * * @param type * The type to set */ public void setType(String type) { mType = type; } }