0001: /*
0002: * Copyright (c) 2002-2003 by OpenSymphony
0003: * All rights reserved.
0004: */
0005: package com.opensymphony.workflow;
0006:
0007: import com.opensymphony.module.propertyset.PropertySet;
0008: import com.opensymphony.module.propertyset.PropertySetManager;
0009:
0010: import com.opensymphony.workflow.config.Configuration;
0011: import com.opensymphony.workflow.config.DefaultConfiguration;
0012: import com.opensymphony.workflow.loader.*;
0013: import com.opensymphony.workflow.query.WorkflowExpressionQuery;
0014: import com.opensymphony.workflow.query.WorkflowQuery;
0015: import com.opensymphony.workflow.spi.*;
0016: import com.opensymphony.workflow.util.VariableResolver;
0017:
0018: import org.apache.commons.logging.Log;
0019: import org.apache.commons.logging.LogFactory;
0020:
0021: import java.util.*;
0022:
0023: /**
0024: * Abstract workflow instance that serves as the base for specific implementations, such as EJB or SOAP.
0025: *
0026: * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
0027: * @author Hani Suleiman
0028: */
0029: public class AbstractWorkflow implements Workflow {
0030: //~ Static fields/initializers /////////////////////////////////////////////
0031:
0032: private static final Log log = LogFactory
0033: .getLog(AbstractWorkflow.class);
0034:
0035: //~ Instance fields ////////////////////////////////////////////////////////
0036:
0037: protected WorkflowContext context;
0038: private Configuration configuration;
0039: private ThreadLocal stateCache = new ThreadLocal();
0040: private TypeResolver typeResolver;
0041:
0042: //~ Constructors ///////////////////////////////////////////////////////////
0043:
0044: public AbstractWorkflow() {
0045: stateCache.set(new HashMap());
0046: }
0047:
0048: //~ Methods ////////////////////////////////////////////////////////////////
0049:
0050: /**
0051: * @ejb.interface-method
0052: * @deprecated use {@link #getAvailableActions(long, Map)} with an empty Map instead.
0053: */
0054: public int[] getAvailableActions(long id) {
0055: return getAvailableActions(id, new HashMap());
0056: }
0057:
0058: /**
0059: * Get the available actions for the specified workflow instance.
0060: * @ejb.interface-method
0061: * @param id The workflow instance id.
0062: * @param inputs The inputs map to pass on to conditions
0063: * @return An array of action id's that can be performed on the specified entry.
0064: * @throws IllegalArgumentException if the specified id does not exist, or if its workflow
0065: * descriptor is no longer available or has become invalid.
0066: */
0067: public int[] getAvailableActions(long id, Map inputs) {
0068: try {
0069: WorkflowStore store = getPersistence();
0070: WorkflowEntry entry = store.findEntry(id);
0071:
0072: if (entry == null) {
0073: throw new IllegalArgumentException(
0074: "No such workflow id " + id);
0075: }
0076:
0077: if (entry.getState() != WorkflowEntry.ACTIVATED) {
0078: return new int[0];
0079: }
0080:
0081: WorkflowDescriptor wf = getConfiguration().getWorkflow(
0082: entry.getWorkflowName());
0083:
0084: if (wf == null) {
0085: throw new IllegalArgumentException("No such workflow "
0086: + entry.getWorkflowName());
0087: }
0088:
0089: List l = new ArrayList();
0090: PropertySet ps = store.getPropertySet(id);
0091: Map transientVars = (inputs == null) ? new HashMap()
0092: : new HashMap(inputs);
0093: Collection currentSteps = store.findCurrentSteps(id);
0094:
0095: populateTransientMap(entry, transientVars, wf
0096: .getRegisters(), new Integer(0), currentSteps, ps);
0097:
0098: // get global actions
0099: List globalActions = wf.getGlobalActions();
0100:
0101: for (Iterator iterator = globalActions.iterator(); iterator
0102: .hasNext();) {
0103: ActionDescriptor action = (ActionDescriptor) iterator
0104: .next();
0105: RestrictionDescriptor restriction = action
0106: .getRestriction();
0107: ConditionsDescriptor conditions = null;
0108:
0109: transientVars.put("actionId", new Integer(action
0110: .getId()));
0111:
0112: if (restriction != null) {
0113: conditions = restriction.getConditionsDescriptor();
0114: }
0115:
0116: //todo verify that 0 is the right currentStepId
0117: if (passesConditions(wf.getGlobalConditions(),
0118: transientVars, ps, 0)
0119: && passesConditions(conditions, transientVars,
0120: ps, 0)) {
0121: l.add(new Integer(action.getId()));
0122: }
0123: }
0124:
0125: // get normal actions
0126: for (Iterator iterator = currentSteps.iterator(); iterator
0127: .hasNext();) {
0128: Step step = (Step) iterator.next();
0129: l.addAll(getAvailableActionsForStep(wf, step,
0130: transientVars, ps));
0131: }
0132:
0133: int[] actions = new int[l.size()];
0134:
0135: for (int i = 0; i < actions.length; i++) {
0136: actions[i] = ((Integer) l.get(i)).intValue();
0137: }
0138:
0139: return actions;
0140: } catch (Exception e) {
0141: log.error("Error checking available actions", e);
0142:
0143: return new int[0];
0144: }
0145: }
0146:
0147: /**
0148: * @ejb.interface-method
0149: */
0150: public void setConfiguration(Configuration configuration) {
0151: this .configuration = configuration;
0152: }
0153:
0154: /**
0155: * Get the configuration for this workflow.
0156: * This method also checks if the configuration has been initialized, and if not, initializes it.
0157: * @return The configuration that was set.
0158: * If no configuration was set, then the default (static) configuration is returned.
0159: *
0160: */
0161: public Configuration getConfiguration() {
0162: Configuration config = (configuration != null) ? configuration
0163: : DefaultConfiguration.INSTANCE;
0164:
0165: if (!config.isInitialized()) {
0166: try {
0167: config.load(null);
0168: } catch (FactoryException e) {
0169: log.fatal("Error initialising configuration", e);
0170:
0171: //fail fast, better to blow up with an NPE that hide the error
0172: return null;
0173: }
0174: }
0175:
0176: return config;
0177: }
0178:
0179: /**
0180: * @ejb.interface-method
0181: */
0182: public List getCurrentSteps(long id) {
0183: try {
0184: WorkflowStore store = getPersistence();
0185:
0186: return store.findCurrentSteps(id);
0187: } catch (StoreException e) {
0188: log.error("Error checking current steps for instance #"
0189: + id, e);
0190:
0191: return Collections.EMPTY_LIST;
0192: }
0193: }
0194:
0195: /**
0196: * @ejb.interface-method
0197: */
0198: public int getEntryState(long id) {
0199: try {
0200: WorkflowStore store = getPersistence();
0201:
0202: return store.findEntry(id).getState();
0203: } catch (StoreException e) {
0204: log.error("Error checking instance state for instance #"
0205: + id, e);
0206: }
0207:
0208: return WorkflowEntry.UNKNOWN;
0209: }
0210:
0211: /**
0212: * @ejb.interface-method
0213: */
0214: public List getHistorySteps(long id) {
0215: try {
0216: WorkflowStore store = getPersistence();
0217:
0218: return store.findHistorySteps(id);
0219: } catch (StoreException e) {
0220: log.error(
0221: "Error getting history steps for instance #" + id,
0222: e);
0223: }
0224:
0225: return Collections.EMPTY_LIST;
0226: }
0227:
0228: /**
0229: * @ejb.interface-method
0230: */
0231: public Properties getPersistenceProperties() {
0232: Properties p = new Properties();
0233: Iterator iter = getConfiguration().getPersistenceArgs()
0234: .entrySet().iterator();
0235:
0236: while (iter.hasNext()) {
0237: Map.Entry entry = (Map.Entry) iter.next();
0238: p.setProperty((String) entry.getKey(), (String) entry
0239: .getValue());
0240: }
0241:
0242: return p;
0243: }
0244:
0245: /**
0246: * Get the PropertySet for the specified workflow ID
0247: * @ejb.interface-method
0248: * @param id The workflow ID
0249: */
0250: public PropertySet getPropertySet(long id) {
0251: PropertySet ps = null;
0252:
0253: try {
0254: ps = getPersistence().getPropertySet(id);
0255: } catch (StoreException e) {
0256: log.error("Error getting propertyset for instance #" + id,
0257: e);
0258: }
0259:
0260: return ps;
0261: }
0262:
0263: public void setResolver(TypeResolver resolver) {
0264: this .typeResolver = resolver;
0265: }
0266:
0267: public TypeResolver getResolver() {
0268: if (typeResolver == null) {
0269: typeResolver = TypeResolver.getResolver();
0270: }
0271:
0272: return typeResolver;
0273: }
0274:
0275: /**
0276: * @ejb.interface-method
0277: */
0278: public List getSecurityPermissions(long id) {
0279: return getSecurityPermissions(id, null);
0280: }
0281:
0282: /**
0283: * @ejb.interface-method
0284: */
0285: public List getSecurityPermissions(long id, Map inputs) {
0286: try {
0287: WorkflowStore store = getPersistence();
0288: WorkflowEntry entry = store.findEntry(id);
0289: WorkflowDescriptor wf = getConfiguration().getWorkflow(
0290: entry.getWorkflowName());
0291:
0292: PropertySet ps = store.getPropertySet(id);
0293: Map transientVars = (inputs == null) ? new HashMap()
0294: : new HashMap(inputs);
0295: Collection currentSteps = store.findCurrentSteps(id);
0296: populateTransientMap(entry, transientVars, wf
0297: .getRegisters(), null, currentSteps, ps);
0298:
0299: List s = new ArrayList();
0300:
0301: for (Iterator interator = currentSteps.iterator(); interator
0302: .hasNext();) {
0303: Step step = (Step) interator.next();
0304:
0305: int stepId = step.getStepId();
0306:
0307: StepDescriptor xmlStep = wf.getStep(stepId);
0308:
0309: List securities = xmlStep.getPermissions();
0310:
0311: for (Iterator iterator2 = securities.iterator(); iterator2
0312: .hasNext();) {
0313: PermissionDescriptor security = (PermissionDescriptor) iterator2
0314: .next();
0315:
0316: // to have the permission, the condition must be met or not specified
0317: // securities can't have restrictions based on inputs, so it's null
0318: if (security.getRestriction() != null) {
0319: if (passesConditions(security.getRestriction()
0320: .getConditionsDescriptor(),
0321: transientVars, ps, xmlStep.getId())) {
0322: s.add(security.getName());
0323: }
0324: }
0325: }
0326: }
0327:
0328: return s;
0329: } catch (Exception e) {
0330: log.error(
0331: "Error getting security permissions for instance #"
0332: + id, e);
0333: }
0334:
0335: return Collections.EMPTY_LIST;
0336: }
0337:
0338: /**
0339: * Returns a workflow definition object associated with the given name.
0340: *
0341: * @param workflowName the name of the workflow
0342: * @return the object graph that represents a workflow definition
0343: * @ejb.interface-method
0344: */
0345: public WorkflowDescriptor getWorkflowDescriptor(String workflowName) {
0346: try {
0347: return getConfiguration().getWorkflow(workflowName);
0348: } catch (FactoryException e) {
0349: log.error("Error loading workflow " + workflowName, e);
0350: }
0351:
0352: return null;
0353: }
0354:
0355: /**
0356: * @ejb.interface-method
0357: */
0358: public String getWorkflowName(long id) {
0359: try {
0360: WorkflowStore store = getPersistence();
0361: WorkflowEntry entry = store.findEntry(id);
0362:
0363: if (entry != null) {
0364: return entry.getWorkflowName();
0365: }
0366: } catch (StoreException e) {
0367: log.error(
0368: "Error getting instance name for instance #" + id,
0369: e);
0370: }
0371:
0372: return null;
0373: }
0374:
0375: /**
0376: * Get a list of workflow names available
0377: * @return String[] an array of workflow names.
0378: * @ejb.interface-method
0379: */
0380: public String[] getWorkflowNames() {
0381: try {
0382: return getConfiguration().getWorkflowNames();
0383: } catch (FactoryException e) {
0384: log.error("Error getting workflow names", e);
0385: }
0386:
0387: return new String[0];
0388: }
0389:
0390: /**
0391: * @ejb.interface-method
0392: */
0393: public boolean canInitialize(String workflowName, int initialAction) {
0394: return canInitialize(workflowName, initialAction, null);
0395: }
0396:
0397: /**
0398: * @ejb.interface-method
0399: * @param workflowName the name of the workflow to check
0400: * @param initialAction The initial action to check
0401: * @param inputs the inputs map
0402: * @return true if the workflow can be initialized
0403: */
0404: public boolean canInitialize(String workflowName,
0405: int initialAction, Map inputs) {
0406: final String mockWorkflowName = workflowName;
0407: WorkflowEntry mockEntry = new WorkflowEntry() {
0408: public long getId() {
0409: return 0;
0410: }
0411:
0412: public String getWorkflowName() {
0413: return mockWorkflowName;
0414: }
0415:
0416: public boolean isInitialized() {
0417: return false;
0418: }
0419:
0420: public int getState() {
0421: return WorkflowEntry.CREATED;
0422: }
0423: };
0424:
0425: // since no state change happens here, a memory instance is just fine
0426: PropertySet ps = PropertySetManager.getInstance("memory", null);
0427: Map transientVars = new HashMap();
0428:
0429: if (inputs != null) {
0430: transientVars.putAll(inputs);
0431: }
0432:
0433: try {
0434: populateTransientMap(mockEntry, transientVars,
0435: Collections.EMPTY_LIST, new Integer(initialAction),
0436: Collections.EMPTY_LIST, ps);
0437:
0438: return canInitialize(workflowName, initialAction,
0439: transientVars, ps);
0440: } catch (InvalidActionException e) {
0441: log.error(e.getMessage());
0442:
0443: return false;
0444: } catch (WorkflowException e) {
0445: log.error("Error checking canInitialize", e);
0446:
0447: return false;
0448: }
0449: }
0450:
0451: /**
0452: * @ejb.interface-method
0453: */
0454: public boolean canModifyEntryState(long id, int newState) {
0455: try {
0456: WorkflowStore store = getPersistence();
0457: WorkflowEntry entry = store.findEntry(id);
0458: int currentState = entry.getState();
0459: boolean result = false;
0460:
0461: switch (newState) {
0462: case WorkflowEntry.COMPLETED:
0463:
0464: if (currentState == WorkflowEntry.ACTIVATED) {
0465: result = true;
0466: }
0467:
0468: break;
0469:
0470: case WorkflowEntry.CREATED:
0471: result = false;
0472:
0473: case WorkflowEntry.ACTIVATED:
0474:
0475: if ((currentState == WorkflowEntry.CREATED)
0476: || (currentState == WorkflowEntry.SUSPENDED)) {
0477: result = true;
0478: }
0479:
0480: break;
0481:
0482: case WorkflowEntry.SUSPENDED:
0483:
0484: if (currentState == WorkflowEntry.ACTIVATED) {
0485: result = true;
0486: }
0487:
0488: break;
0489:
0490: case WorkflowEntry.KILLED:
0491:
0492: if ((currentState == WorkflowEntry.CREATED)
0493: || (currentState == WorkflowEntry.ACTIVATED)
0494: || (currentState == WorkflowEntry.SUSPENDED)) {
0495: result = true;
0496: }
0497:
0498: break;
0499:
0500: default:
0501: result = false;
0502:
0503: break;
0504: }
0505:
0506: return result;
0507: } catch (StoreException e) {
0508: log.error("Error checking state modifiable for instance #"
0509: + id, e);
0510: }
0511:
0512: return false;
0513: }
0514:
0515: public void changeEntryState(long id, int newState)
0516: throws WorkflowException {
0517: WorkflowStore store = getPersistence();
0518: WorkflowEntry entry = store.findEntry(id);
0519:
0520: if (entry.getState() == newState) {
0521: return;
0522: }
0523:
0524: if (canModifyEntryState(id, newState)) {
0525: if ((newState == WorkflowEntry.KILLED)
0526: || (newState == WorkflowEntry.COMPLETED)) {
0527: Collection currentSteps = getCurrentSteps(id);
0528:
0529: if (currentSteps.size() > 0) {
0530: completeEntry(null, id, currentSteps, newState);
0531: }
0532: }
0533:
0534: store.setEntryState(id, newState);
0535: } else {
0536: throw new InvalidEntryStateException(
0537: "Can't transition workflow instance #" + id
0538: + ". Current state is " + entry.getState()
0539: + ", requested state is " + newState);
0540: }
0541:
0542: if (log.isDebugEnabled()) {
0543: log.debug(entry.getId() + " : State is now : "
0544: + entry.getState());
0545: }
0546: }
0547:
0548: public void doAction(long id, int actionId, Map inputs)
0549: throws WorkflowException {
0550: WorkflowStore store = getPersistence();
0551: WorkflowEntry entry = store.findEntry(id);
0552:
0553: if (entry.getState() != WorkflowEntry.ACTIVATED) {
0554: return;
0555: }
0556:
0557: WorkflowDescriptor wf = getConfiguration().getWorkflow(
0558: entry.getWorkflowName());
0559:
0560: List currentSteps = store.findCurrentSteps(id);
0561: ActionDescriptor action = null;
0562:
0563: PropertySet ps = store.getPropertySet(id);
0564: Map transientVars = new HashMap();
0565:
0566: if (inputs != null) {
0567: transientVars.putAll(inputs);
0568: }
0569:
0570: populateTransientMap(entry, transientVars, wf.getRegisters(),
0571: new Integer(actionId), currentSteps, ps);
0572:
0573: boolean validAction = false;
0574:
0575: //check global actions
0576: for (Iterator gIter = wf.getGlobalActions().iterator(); !validAction
0577: && gIter.hasNext();) {
0578: ActionDescriptor actionDesc = (ActionDescriptor) gIter
0579: .next();
0580:
0581: if (actionDesc.getId() == actionId) {
0582: action = actionDesc;
0583:
0584: if (isActionAvailable(action, transientVars, ps, 0)) {
0585: validAction = true;
0586: }
0587: }
0588: }
0589:
0590: for (Iterator iter = currentSteps.iterator(); !validAction
0591: && iter.hasNext();) {
0592: Step step = (Step) iter.next();
0593: StepDescriptor s = wf.getStep(step.getStepId());
0594:
0595: for (Iterator iterator = s.getActions().iterator(); !validAction
0596: && iterator.hasNext();) {
0597: ActionDescriptor actionDesc = (ActionDescriptor) iterator
0598: .next();
0599:
0600: if (actionDesc.getId() == actionId) {
0601: action = actionDesc;
0602:
0603: if (isActionAvailable(action, transientVars, ps, s
0604: .getId())) {
0605: validAction = true;
0606: }
0607: }
0608: }
0609: }
0610:
0611: if (!validAction) {
0612: throw new InvalidActionException("Action " + actionId
0613: + " is invalid");
0614: }
0615:
0616: try {
0617: //transition the workflow, if it wasn't explicitly finished, check for an implicit finish
0618: if (!transitionWorkflow(entry, currentSteps, store, wf,
0619: action, transientVars, inputs, ps)) {
0620: checkImplicitFinish(action, id);
0621: }
0622: } catch (WorkflowException e) {
0623: context.setRollbackOnly();
0624: throw e;
0625: }
0626: }
0627:
0628: public void executeTriggerFunction(long id, int triggerId)
0629: throws WorkflowException {
0630: WorkflowStore store = getPersistence();
0631: WorkflowEntry entry = store.findEntry(id);
0632:
0633: if (entry == null) {
0634: log.warn("Cannot execute trigger #" + triggerId
0635: + " on non-existent workflow id#" + id);
0636:
0637: return;
0638: }
0639:
0640: WorkflowDescriptor wf = getConfiguration().getWorkflow(
0641: entry.getWorkflowName());
0642:
0643: PropertySet ps = store.getPropertySet(id);
0644: Map transientVars = new HashMap();
0645: populateTransientMap(entry, transientVars, wf.getRegisters(),
0646: null, store.findCurrentSteps(id), ps);
0647: executeFunction(wf.getTriggerFunction(triggerId),
0648: transientVars, ps);
0649: }
0650:
0651: public long initialize(String workflowName, int initialAction,
0652: Map inputs) throws InvalidRoleException,
0653: InvalidInputException, WorkflowException {
0654: WorkflowDescriptor wf = getConfiguration().getWorkflow(
0655: workflowName);
0656:
0657: WorkflowStore store = getPersistence();
0658: WorkflowEntry entry = store.createEntry(workflowName);
0659:
0660: // start with a memory property set, but clone it after we have an ID
0661: PropertySet ps = store.getPropertySet(entry.getId());
0662: Map transientVars = new HashMap();
0663:
0664: if (inputs != null) {
0665: transientVars.putAll(inputs);
0666: }
0667:
0668: populateTransientMap(entry, transientVars, wf.getRegisters(),
0669: new Integer(initialAction), Collections.EMPTY_LIST, ps);
0670:
0671: if (!canInitialize(workflowName, initialAction, transientVars,
0672: ps)) {
0673: context.setRollbackOnly();
0674: throw new InvalidRoleException(
0675: "You are restricted from initializing this workflow");
0676: }
0677:
0678: ActionDescriptor action = wf.getInitialAction(initialAction);
0679:
0680: try {
0681: transitionWorkflow(entry, Collections.EMPTY_LIST, store,
0682: wf, action, transientVars, inputs, ps);
0683: } catch (WorkflowException e) {
0684: context.setRollbackOnly();
0685: throw e;
0686: }
0687:
0688: long entryId = entry.getId();
0689:
0690: // now clone the memory PS to the real PS
0691: //PropertySetManager.clone(ps, store.getPropertySet(entryId));
0692: return entryId;
0693: }
0694:
0695: /**
0696: * @ejb.interface-method
0697: */
0698: public List query(WorkflowQuery query) throws StoreException {
0699: return getPersistence().query(query);
0700: }
0701:
0702: /**
0703: * @ejb.interface-method
0704: */
0705: public List query(WorkflowExpressionQuery query)
0706: throws WorkflowException {
0707: return getPersistence().query(query);
0708: }
0709:
0710: /**
0711: * @ejb.interface-method
0712: */
0713: public boolean removeWorkflowDescriptor(String workflowName)
0714: throws FactoryException {
0715: return getConfiguration().removeWorkflow(workflowName);
0716: }
0717:
0718: /**
0719: * @ejb.interface-method
0720: */
0721: public boolean saveWorkflowDescriptor(String workflowName,
0722: WorkflowDescriptor descriptor, boolean replace)
0723: throws FactoryException {
0724: boolean success = getConfiguration().saveWorkflow(workflowName,
0725: descriptor, replace);
0726:
0727: return success;
0728: }
0729:
0730: protected List getAvailableActionsForStep(WorkflowDescriptor wf,
0731: Step step, Map transientVars, PropertySet ps)
0732: throws WorkflowException {
0733: List l = new ArrayList();
0734: StepDescriptor s = wf.getStep(step.getStepId());
0735:
0736: if (s == null) {
0737: log
0738: .warn("getAvailableActionsForStep called for non-existent step Id #"
0739: + step.getStepId());
0740:
0741: return l;
0742: }
0743:
0744: List actions = s.getActions();
0745:
0746: if ((actions == null) || (actions.size() == 0)) {
0747: return l;
0748: }
0749:
0750: for (Iterator iterator2 = actions.iterator(); iterator2
0751: .hasNext();) {
0752: ActionDescriptor action = (ActionDescriptor) iterator2
0753: .next();
0754: RestrictionDescriptor restriction = action.getRestriction();
0755: ConditionsDescriptor conditions = null;
0756:
0757: transientVars.put("actionId", new Integer(action.getId()));
0758:
0759: if (restriction != null) {
0760: conditions = restriction.getConditionsDescriptor();
0761: }
0762:
0763: if (passesConditions(wf.getGlobalConditions(), new HashMap(
0764: transientVars), ps, s.getId())
0765: && passesConditions(conditions, new HashMap(
0766: transientVars), ps, s.getId())) {
0767: l.add(new Integer(action.getId()));
0768: }
0769: }
0770:
0771: return l;
0772: }
0773:
0774: protected int[] getAvailableAutoActions(long id, Map inputs) {
0775: try {
0776: WorkflowStore store = getPersistence();
0777: WorkflowEntry entry = store.findEntry(id);
0778:
0779: if (entry == null) {
0780: throw new IllegalArgumentException(
0781: "No such workflow id " + id);
0782: }
0783:
0784: if (entry.getState() != WorkflowEntry.ACTIVATED) {
0785: log.debug("--> state is " + entry.getState());
0786:
0787: return new int[0];
0788: }
0789:
0790: WorkflowDescriptor wf = getConfiguration().getWorkflow(
0791: entry.getWorkflowName());
0792:
0793: if (wf == null) {
0794: throw new IllegalArgumentException("No such workflow "
0795: + entry.getWorkflowName());
0796: }
0797:
0798: List l = new ArrayList();
0799: PropertySet ps = store.getPropertySet(id);
0800: Map transientVars = (inputs == null) ? new HashMap()
0801: : new HashMap(inputs);
0802: Collection currentSteps = store.findCurrentSteps(id);
0803:
0804: populateTransientMap(entry, transientVars, wf
0805: .getRegisters(), new Integer(0), currentSteps, ps);
0806:
0807: // get global actions
0808: List globalActions = wf.getGlobalActions();
0809:
0810: for (Iterator iterator = globalActions.iterator(); iterator
0811: .hasNext();) {
0812: ActionDescriptor action = (ActionDescriptor) iterator
0813: .next();
0814:
0815: transientVars.put("actionId", new Integer(action
0816: .getId()));
0817:
0818: if (action.getAutoExecute()) {
0819: if (isActionAvailable(action, transientVars, ps, 0)) {
0820: l.add(new Integer(action.getId()));
0821: }
0822: }
0823: }
0824:
0825: // get normal actions
0826: for (Iterator iterator = currentSteps.iterator(); iterator
0827: .hasNext();) {
0828: Step step = (Step) iterator.next();
0829: l.addAll(getAvailableAutoActionsForStep(wf, step,
0830: transientVars, ps));
0831: }
0832:
0833: int[] actions = new int[l.size()];
0834:
0835: for (int i = 0; i < actions.length; i++) {
0836: actions[i] = ((Integer) l.get(i)).intValue();
0837: }
0838:
0839: return actions;
0840: } catch (Exception e) {
0841: log.error("Error checking available actions", e);
0842:
0843: return new int[0];
0844: }
0845: }
0846:
0847: /**
0848: * Get just auto action availables for a step
0849: */
0850: protected List getAvailableAutoActionsForStep(
0851: WorkflowDescriptor wf, Step step, Map transientVars,
0852: PropertySet ps) throws WorkflowException {
0853: List l = new ArrayList();
0854: StepDescriptor s = wf.getStep(step.getStepId());
0855:
0856: if (s == null) {
0857: log
0858: .warn("getAvailableAutoActionsForStep called for non-existent step Id #"
0859: + step.getStepId());
0860:
0861: return l;
0862: }
0863:
0864: List actions = s.getActions();
0865:
0866: if ((actions == null) || (actions.size() == 0)) {
0867: return l;
0868: }
0869:
0870: for (Iterator iterator2 = actions.iterator(); iterator2
0871: .hasNext();) {
0872: ActionDescriptor action = (ActionDescriptor) iterator2
0873: .next();
0874:
0875: transientVars.put("actionId", new Integer(action.getId()));
0876:
0877: //check auto
0878: if (action.getAutoExecute()) {
0879: if (isActionAvailable(action, transientVars, ps, s
0880: .getId())) {
0881: l.add(new Integer(action.getId()));
0882: }
0883: }
0884: }
0885:
0886: return l;
0887: }
0888:
0889: protected WorkflowStore getPersistence() throws StoreException {
0890: return getConfiguration().getWorkflowStore();
0891: }
0892:
0893: protected void checkImplicitFinish(ActionDescriptor action, long id)
0894: throws WorkflowException {
0895: WorkflowStore store = getPersistence();
0896: WorkflowEntry entry = store.findEntry(id);
0897:
0898: WorkflowDescriptor wf = getConfiguration().getWorkflow(
0899: entry.getWorkflowName());
0900:
0901: Collection currentSteps = store.findCurrentSteps(id);
0902:
0903: boolean isCompleted = true;
0904:
0905: for (Iterator iterator = currentSteps.iterator(); iterator
0906: .hasNext();) {
0907: Step step = (Step) iterator.next();
0908: StepDescriptor stepDes = wf.getStep(step.getStepId());
0909:
0910: // if at least on current step have an available action
0911: if (stepDes.getActions().size() > 0) {
0912: isCompleted = false;
0913: }
0914: }
0915:
0916: if (isCompleted) {
0917: completeEntry(action, id, currentSteps,
0918: WorkflowEntry.COMPLETED);
0919: }
0920: }
0921:
0922: /**
0923: * Mark the specified entry as completed, and move all current steps to history.
0924: */
0925: protected void completeEntry(ActionDescriptor action, long id,
0926: Collection currentSteps, int state) throws StoreException {
0927: getPersistence().setEntryState(id, state);
0928:
0929: Iterator i = new ArrayList(currentSteps).iterator();
0930:
0931: while (i.hasNext()) {
0932: Step step = (Step) i.next();
0933: String oldStatus = (action != null) ? action
0934: .getUnconditionalResult().getOldStatus()
0935: : "Finished";
0936: getPersistence().markFinished(step,
0937: (action != null) ? action.getId() : (-1),
0938: new Date(), oldStatus, context.getCaller());
0939: getPersistence().moveToHistory(step);
0940: }
0941: }
0942:
0943: /**
0944: * Executes a function.
0945: *
0946: * @param function the function to execute
0947: * @param transientVars the transientVars given by the end-user
0948: * @param ps the persistence variables
0949: */
0950: protected void executeFunction(FunctionDescriptor function,
0951: Map transientVars, PropertySet ps) throws WorkflowException {
0952: if (function != null) {
0953: String type = function.getType();
0954:
0955: Map args = new HashMap(function.getArgs());
0956:
0957: for (Iterator iterator = args.entrySet().iterator(); iterator
0958: .hasNext();) {
0959: Map.Entry mapEntry = (Map.Entry) iterator.next();
0960: mapEntry.setValue(getConfiguration()
0961: .getVariableResolver().translateVariables(
0962: (String) mapEntry.getValue(),
0963: transientVars, ps));
0964: }
0965:
0966: FunctionProvider provider = getResolver().getFunction(type,
0967: args);
0968:
0969: if (provider == null) {
0970: String message = "Could not load FunctionProvider class";
0971: context.setRollbackOnly();
0972: throw new WorkflowException(message);
0973: }
0974:
0975: try {
0976: provider.execute(transientVars, args, ps);
0977: } catch (WorkflowException e) {
0978: context.setRollbackOnly();
0979: throw e;
0980: }
0981: }
0982: }
0983:
0984: protected boolean passesCondition(
0985: ConditionDescriptor conditionDesc, Map transientVars,
0986: PropertySet ps, int currentStepId) throws WorkflowException {
0987: String type = conditionDesc.getType();
0988:
0989: Map args = new HashMap(conditionDesc.getArgs());
0990:
0991: for (Iterator iterator = args.entrySet().iterator(); iterator
0992: .hasNext();) {
0993: Map.Entry mapEntry = (Map.Entry) iterator.next();
0994: mapEntry.setValue(getConfiguration().getVariableResolver()
0995: .translateVariables((String) mapEntry.getValue(),
0996: transientVars, ps));
0997: }
0998:
0999: if (currentStepId != -1) {
1000: Object stepId = args.get("stepId");
1001:
1002: if ((stepId != null) && stepId.equals("-1")) {
1003: args.put("stepId", String.valueOf(currentStepId));
1004: }
1005: }
1006:
1007: Condition condition = getResolver().getCondition(type, args);
1008:
1009: if (condition == null) {
1010: context.setRollbackOnly();
1011: throw new WorkflowException("Could not load condition");
1012: }
1013:
1014: try {
1015: boolean passed = condition.passesCondition(transientVars,
1016: args, ps);
1017:
1018: if (conditionDesc.isNegate()) {
1019: passed = !passed;
1020: }
1021:
1022: return passed;
1023: } catch (Exception e) {
1024: context.setRollbackOnly();
1025:
1026: if (e instanceof WorkflowException) {
1027: throw (WorkflowException) e;
1028: }
1029:
1030: throw new WorkflowException(
1031: "Unknown exception encountered when checking condition "
1032: + condition, e);
1033: }
1034: }
1035:
1036: protected boolean passesConditions(String conditionType,
1037: List conditions, Map transientVars, PropertySet ps,
1038: int currentStepId) throws WorkflowException {
1039: if ((conditions == null) || (conditions.size() == 0)) {
1040: return true;
1041: }
1042:
1043: boolean and = "AND".equals(conditionType);
1044: boolean or = !and;
1045:
1046: for (Iterator iterator = conditions.iterator(); iterator
1047: .hasNext();) {
1048: AbstractDescriptor descriptor = (AbstractDescriptor) iterator
1049: .next();
1050: boolean result;
1051:
1052: if (descriptor instanceof ConditionsDescriptor) {
1053: ConditionsDescriptor conditionsDescriptor = (ConditionsDescriptor) descriptor;
1054: result = passesConditions(conditionsDescriptor
1055: .getType(), conditionsDescriptor
1056: .getConditions(), transientVars, ps,
1057: currentStepId);
1058: } else {
1059: result = passesCondition(
1060: (ConditionDescriptor) descriptor,
1061: transientVars, ps, currentStepId);
1062: }
1063:
1064: if (and && !result) {
1065: return false;
1066: } else if (or && result) {
1067: return true;
1068: }
1069: }
1070:
1071: if (and) {
1072: return true;
1073: } else if (or) {
1074: return false;
1075: } else {
1076: return false;
1077: }
1078: }
1079:
1080: protected boolean passesConditions(ConditionsDescriptor descriptor,
1081: Map transientVars, PropertySet ps, int currentStepId)
1082: throws WorkflowException {
1083: if (descriptor == null) {
1084: return true;
1085: }
1086:
1087: return passesConditions(descriptor.getType(), descriptor
1088: .getConditions(), transientVars, ps, currentStepId);
1089: }
1090:
1091: protected void populateTransientMap(WorkflowEntry entry,
1092: Map transientVars, List registers, Integer actionId,
1093: Collection currentSteps, PropertySet ps)
1094: throws WorkflowException {
1095: transientVars.put("context", context);
1096: transientVars.put("entry", entry);
1097: transientVars.put("store", getPersistence());
1098: transientVars.put("configuration", getConfiguration());
1099: transientVars.put("descriptor", getConfiguration().getWorkflow(
1100: entry.getWorkflowName()));
1101:
1102: if (actionId != null) {
1103: transientVars.put("actionId", actionId);
1104: }
1105:
1106: transientVars.put("currentSteps", new ArrayList(currentSteps));
1107:
1108: // now talk to the registers for any extra objects needed in scope
1109: for (Iterator iterator = registers.iterator(); iterator
1110: .hasNext();) {
1111: RegisterDescriptor register = (RegisterDescriptor) iterator
1112: .next();
1113: Map args = register.getArgs();
1114:
1115: String type = register.getType();
1116: Register r = getResolver().getRegister(type, args);
1117:
1118: if (r == null) {
1119: String message = "Could not load register class";
1120: context.setRollbackOnly();
1121: throw new WorkflowException(message);
1122: }
1123:
1124: try {
1125: transientVars.put(register.getVariableName(), r
1126: .registerVariable(context, entry, args, ps));
1127: } catch (Exception e) {
1128: context.setRollbackOnly();
1129:
1130: if (e instanceof WorkflowException) {
1131: throw (WorkflowException) e;
1132: }
1133:
1134: throw new WorkflowException(
1135: "An unknown exception occured while registering variable using register "
1136: + r, e);
1137: }
1138: }
1139: }
1140:
1141: /**
1142: * @return true if the instance has been explicitly completed is this transition, false otherwise
1143: * @throws WorkflowException
1144: */
1145: protected boolean transitionWorkflow(WorkflowEntry entry,
1146: List currentSteps, WorkflowStore store,
1147: WorkflowDescriptor wf, ActionDescriptor action,
1148: Map transientVars, Map inputs, PropertySet ps)
1149: throws WorkflowException {
1150: Map cache = (Map) stateCache.get();
1151:
1152: if (cache != null) {
1153: cache.clear();
1154: } else {
1155: stateCache.set(new HashMap());
1156: }
1157:
1158: Step step = getCurrentStep(wf, action.getId(), currentSteps,
1159: transientVars, ps);
1160:
1161: if (action.getValidators().size() > 0) {
1162: verifyInputs(entry, action.getValidators(), Collections
1163: .unmodifiableMap(transientVars), ps);
1164: }
1165:
1166: //we're leaving the current step, so let's execute its post-functions
1167: //check if we actually have a current step
1168: if (step != null) {
1169: List stepPostFunctions = wf.getStep(step.getStepId())
1170: .getPostFunctions();
1171:
1172: for (Iterator iterator = stepPostFunctions.iterator(); iterator
1173: .hasNext();) {
1174: FunctionDescriptor function = (FunctionDescriptor) iterator
1175: .next();
1176: executeFunction(function, transientVars, ps);
1177: }
1178: }
1179:
1180: // preFunctions
1181: List preFunctions = action.getPreFunctions();
1182:
1183: for (Iterator iterator = preFunctions.iterator(); iterator
1184: .hasNext();) {
1185: FunctionDescriptor function = (FunctionDescriptor) iterator
1186: .next();
1187: executeFunction(function, transientVars, ps);
1188: }
1189:
1190: // check each conditional result
1191: List conditionalResults = action.getConditionalResults();
1192: List extraPreFunctions = null;
1193: List extraPostFunctions = null;
1194: ResultDescriptor[] theResults = new ResultDescriptor[1];
1195:
1196: for (Iterator iterator = conditionalResults.iterator(); iterator
1197: .hasNext();) {
1198: ConditionalResultDescriptor conditionalResult = (ConditionalResultDescriptor) iterator
1199: .next();
1200:
1201: if (passesConditions(null, conditionalResult
1202: .getConditions(), Collections
1203: .unmodifiableMap(transientVars), ps,
1204: (step != null) ? step.getStepId() : (-1))) {
1205: //if (evaluateExpression(conditionalResult.getCondition(), entry, wf.getRegisters(), null, transientVars)) {
1206: theResults[0] = conditionalResult;
1207:
1208: if (conditionalResult.getValidators().size() > 0) {
1209: verifyInputs(entry, conditionalResult
1210: .getValidators(), Collections
1211: .unmodifiableMap(transientVars), ps);
1212: }
1213:
1214: extraPreFunctions = conditionalResult.getPreFunctions();
1215: extraPostFunctions = conditionalResult
1216: .getPostFunctions();
1217:
1218: break;
1219: }
1220: }
1221:
1222: // use unconditional-result if a condition hasn't been met
1223: if (theResults[0] == null) {
1224: theResults[0] = action.getUnconditionalResult();
1225: verifyInputs(entry, theResults[0].getValidators(),
1226: Collections.unmodifiableMap(transientVars), ps);
1227: extraPreFunctions = theResults[0].getPreFunctions();
1228: extraPostFunctions = theResults[0].getPostFunctions();
1229: }
1230:
1231: if (log.isDebugEnabled()) {
1232: log.debug("theResult=" + theResults[0].getStep() + ' '
1233: + theResults[0].getStatus());
1234: }
1235:
1236: if ((extraPreFunctions != null)
1237: && (extraPreFunctions.size() > 0)) {
1238: // run any extra pre-functions that haven't been run already
1239: for (Iterator iterator = extraPreFunctions.iterator(); iterator
1240: .hasNext();) {
1241: FunctionDescriptor function = (FunctionDescriptor) iterator
1242: .next();
1243: executeFunction(function, transientVars, ps);
1244: }
1245: }
1246:
1247: // go to next step
1248: if (theResults[0].getSplit() != 0) {
1249: // the result is a split request, handle it correctly
1250: SplitDescriptor splitDesc = wf.getSplit(theResults[0]
1251: .getSplit());
1252: Collection results = splitDesc.getResults();
1253: List splitPreFunctions = new ArrayList();
1254: List splitPostFunctions = new ArrayList();
1255:
1256: //check all results in the split and verify the input against any validators specified
1257: //also build up all the pre and post functions that should be called.
1258: for (Iterator iterator = results.iterator(); iterator
1259: .hasNext();) {
1260: ResultDescriptor resultDescriptor = (ResultDescriptor) iterator
1261: .next();
1262:
1263: if (resultDescriptor.getValidators().size() > 0) {
1264: verifyInputs(entry, resultDescriptor
1265: .getValidators(), Collections
1266: .unmodifiableMap(transientVars), ps);
1267: }
1268:
1269: splitPreFunctions.addAll(resultDescriptor
1270: .getPreFunctions());
1271: splitPostFunctions.addAll(resultDescriptor
1272: .getPostFunctions());
1273: }
1274:
1275: // now execute the pre-functions
1276: for (Iterator iterator = splitPreFunctions.iterator(); iterator
1277: .hasNext();) {
1278: FunctionDescriptor function = (FunctionDescriptor) iterator
1279: .next();
1280: executeFunction(function, transientVars, ps);
1281: }
1282:
1283: if (!action.isFinish()) {
1284: // now make these steps...
1285: boolean moveFirst = true;
1286:
1287: theResults = new ResultDescriptor[results.size()];
1288: results.toArray(theResults);
1289:
1290: for (Iterator iterator = results.iterator(); iterator
1291: .hasNext();) {
1292: ResultDescriptor resultDescriptor = (ResultDescriptor) iterator
1293: .next();
1294: Step moveToHistoryStep = null;
1295:
1296: if (moveFirst) {
1297: moveToHistoryStep = step;
1298: }
1299:
1300: long[] previousIds = null;
1301:
1302: if (step != null) {
1303: previousIds = new long[] { step.getId() };
1304: }
1305:
1306: createNewCurrentStep(resultDescriptor, entry,
1307: store, action.getId(), moveToHistoryStep,
1308: previousIds, transientVars, ps);
1309: moveFirst = false;
1310: }
1311: }
1312:
1313: // now execute the post-functions
1314: for (Iterator iterator = splitPostFunctions.iterator(); iterator
1315: .hasNext();) {
1316: FunctionDescriptor function = (FunctionDescriptor) iterator
1317: .next();
1318: executeFunction(function, transientVars, ps);
1319: }
1320: } else if (theResults[0].getJoin() != 0) {
1321: // this is a join, finish this step...
1322: JoinDescriptor joinDesc = wf.getJoin(theResults[0]
1323: .getJoin());
1324: step = store.markFinished(step, action.getId(), new Date(),
1325: theResults[0].getOldStatus(), context.getCaller());
1326: store.moveToHistory(step);
1327:
1328: // ... now check to see if the expression evaluates
1329: // (get only current steps that have a result to this join)
1330: Collection joinSteps = new ArrayList();
1331: joinSteps.add(step);
1332:
1333: //currentSteps = store.findCurrentSteps(id); // shouldn't need to refresh the list
1334: for (Iterator iterator = currentSteps.iterator(); iterator
1335: .hasNext();) {
1336: Step currentStep = (Step) iterator.next();
1337:
1338: if (currentStep.getId() != step.getId()) {
1339: StepDescriptor stepDesc = wf.getStep(currentStep
1340: .getStepId());
1341:
1342: if (stepDesc.resultsInJoin(theResults[0].getJoin())) {
1343: joinSteps.add(currentStep);
1344: }
1345: }
1346: }
1347:
1348: //we also need to check history steps that were finished before this one
1349: //that might be part of the join
1350: List historySteps = store.findHistorySteps(entry.getId());
1351:
1352: for (Iterator i = historySteps.iterator(); i.hasNext();) {
1353: Step historyStep = (Step) i.next();
1354:
1355: if (historyStep.getId() != step.getId()) {
1356: StepDescriptor stepDesc = wf.getStep(historyStep
1357: .getStepId());
1358:
1359: if (stepDesc.resultsInJoin(theResults[0].getJoin())) {
1360: joinSteps.add(historyStep);
1361: }
1362: }
1363: }
1364:
1365: JoinNodes jn = new JoinNodes(joinSteps);
1366: transientVars.put("jn", jn);
1367:
1368: //todo verify that 0 is the right value for currentstep here
1369: if (passesConditions(null, joinDesc.getConditions(),
1370: Collections.unmodifiableMap(transientVars), ps, 0)) {
1371: // move the rest without creating a new step ...
1372: ResultDescriptor joinresult = joinDesc.getResult();
1373:
1374: if (joinresult.getValidators().size() > 0) {
1375: verifyInputs(entry, joinresult.getValidators(),
1376: Collections.unmodifiableMap(transientVars),
1377: ps);
1378: }
1379:
1380: // now execute the pre-functions
1381: for (Iterator iterator = joinresult.getPreFunctions()
1382: .iterator(); iterator.hasNext();) {
1383: FunctionDescriptor function = (FunctionDescriptor) iterator
1384: .next();
1385: executeFunction(function, transientVars, ps);
1386: }
1387:
1388: long[] previousIds = new long[joinSteps.size()];
1389: int i = 1;
1390:
1391: for (Iterator iterator = joinSteps.iterator(); iterator
1392: .hasNext();) {
1393: Step currentStep = (Step) iterator.next();
1394:
1395: if (currentStep.getId() != step.getId()) {
1396: //if this is already a history step (eg, for all join steps completed prior to this one),
1397: //we don't move it, since it's already history.
1398: if (!historySteps.contains(currentStep)) {
1399: store.moveToHistory(currentStep);
1400: }
1401:
1402: previousIds[i] = currentStep.getId();
1403: i++;
1404: }
1405: }
1406:
1407: if (!action.isFinish()) {
1408: // ... now finish this step normally
1409: previousIds[0] = step.getId();
1410: theResults[0] = joinDesc.getResult();
1411:
1412: //we pass in null for the current step since we've already moved it to history above
1413: createNewCurrentStep(joinDesc.getResult(), entry,
1414: store, action.getId(), null, previousIds,
1415: transientVars, ps);
1416: }
1417:
1418: // now execute the post-functions
1419: for (Iterator iterator = joinresult.getPostFunctions()
1420: .iterator(); iterator.hasNext();) {
1421: FunctionDescriptor function = (FunctionDescriptor) iterator
1422: .next();
1423: executeFunction(function, transientVars, ps);
1424: }
1425: }
1426: } else {
1427: // normal finish, no splits or joins
1428: long[] previousIds = null;
1429:
1430: if (step != null) {
1431: previousIds = new long[] { step.getId() };
1432: }
1433:
1434: if (!action.isFinish()) {
1435: createNewCurrentStep(theResults[0], entry, store,
1436: action.getId(), step, previousIds,
1437: transientVars, ps);
1438: }
1439: }
1440:
1441: // postFunctions (BOTH)
1442: if (extraPostFunctions != null) {
1443: for (Iterator iterator = extraPostFunctions.iterator(); iterator
1444: .hasNext();) {
1445: FunctionDescriptor function = (FunctionDescriptor) iterator
1446: .next();
1447: executeFunction(function, transientVars, ps);
1448: }
1449: }
1450:
1451: List postFunctions = action.getPostFunctions();
1452:
1453: for (Iterator iterator = postFunctions.iterator(); iterator
1454: .hasNext();) {
1455: FunctionDescriptor function = (FunctionDescriptor) iterator
1456: .next();
1457: executeFunction(function, transientVars, ps);
1458: }
1459:
1460: //if executed action was an initial action then workflow is activated
1461: if ((wf.getInitialAction(action.getId()) != null)
1462: && (entry.getState() != WorkflowEntry.ACTIVATED)) {
1463: changeEntryState(entry.getId(), WorkflowEntry.ACTIVATED);
1464: }
1465:
1466: //if it's a finish action, then we halt
1467: if (action.isFinish()) {
1468: completeEntry(action, entry.getId(), getCurrentSteps(entry
1469: .getId()), WorkflowEntry.COMPLETED);
1470:
1471: return true;
1472: }
1473:
1474: //get available autoexec actions
1475: int[] availableAutoActions = getAvailableAutoActions(entry
1476: .getId(), inputs);
1477:
1478: //we perform the first autoaction that applies, not all of them.
1479: if (availableAutoActions.length > 0) {
1480: doAction(entry.getId(), availableAutoActions[0], inputs);
1481: }
1482:
1483: return false;
1484: }
1485:
1486: /**
1487: * Validates input against a list of ValidatorDescriptor objects.
1488: *
1489: * @param entry the workflow instance
1490: * @param validators the list of ValidatorDescriptors
1491: * @param transientVars the transientVars
1492: * @param ps the persistence variables
1493: * @throws InvalidInputException if the input is deemed invalid by any validator
1494: */
1495: protected void verifyInputs(WorkflowEntry entry, List validators,
1496: Map transientVars, PropertySet ps) throws WorkflowException {
1497: for (Iterator iterator = validators.iterator(); iterator
1498: .hasNext();) {
1499: ValidatorDescriptor input = (ValidatorDescriptor) iterator
1500: .next();
1501:
1502: if (input != null) {
1503: String type = input.getType();
1504: HashMap args = new HashMap(input.getArgs());
1505:
1506: for (Iterator iterator2 = args.entrySet().iterator(); iterator2
1507: .hasNext();) {
1508: Map.Entry mapEntry = (Map.Entry) iterator2.next();
1509: mapEntry.setValue(getConfiguration()
1510: .getVariableResolver().translateVariables(
1511: (String) mapEntry.getValue(),
1512: transientVars, ps));
1513: }
1514:
1515: Validator validator = getResolver().getValidator(type,
1516: args);
1517:
1518: if (validator == null) {
1519: String message = "Could not load validator class";
1520: context.setRollbackOnly();
1521: throw new WorkflowException(message);
1522: }
1523:
1524: try {
1525: validator.validate(transientVars, args, ps);
1526: } catch (InvalidInputException e) {
1527: throw e;
1528: } catch (Exception e) {
1529: context.setRollbackOnly();
1530:
1531: if (e instanceof WorkflowException) {
1532: throw (WorkflowException) e;
1533: }
1534:
1535: String message = "An unknown exception occured executing Validator "
1536: + validator;
1537: throw new WorkflowException(message, e);
1538: }
1539: }
1540: }
1541: }
1542:
1543: /**
1544: * check if an action is available or not
1545: * @param action The action descriptor
1546: * @return true if the action is available
1547: */
1548: private boolean isActionAvailable(ActionDescriptor action,
1549: Map transientVars, PropertySet ps, int stepId)
1550: throws WorkflowException {
1551: if (action == null) {
1552: return false;
1553: }
1554:
1555: WorkflowDescriptor wf = getWorkflowDescriptorForAction(action);
1556:
1557: Map cache = (Map) stateCache.get();
1558:
1559: Boolean result = null;
1560:
1561: if (cache != null) {
1562: result = (Boolean) cache.get(action);
1563: } else {
1564: cache = new HashMap();
1565: stateCache.set(cache);
1566: }
1567:
1568: if (result == null) {
1569: RestrictionDescriptor restriction = action.getRestriction();
1570: ConditionsDescriptor conditions = null;
1571:
1572: if (restriction != null) {
1573: conditions = restriction.getConditionsDescriptor();
1574: }
1575:
1576: result = new Boolean(passesConditions(wf
1577: .getGlobalConditions(), new HashMap(transientVars),
1578: ps, stepId)
1579: && passesConditions(conditions, new HashMap(
1580: transientVars), ps, stepId));
1581: cache.put(action, result);
1582: }
1583:
1584: return result.booleanValue();
1585: }
1586:
1587: private Step getCurrentStep(WorkflowDescriptor wfDesc,
1588: int actionId, List currentSteps, Map transientVars,
1589: PropertySet ps) throws WorkflowException {
1590: if (currentSteps.size() == 1) {
1591: return (Step) currentSteps.get(0);
1592: }
1593:
1594: for (Iterator iterator = currentSteps.iterator(); iterator
1595: .hasNext();) {
1596: Step step = (Step) iterator.next();
1597: ActionDescriptor action = wfDesc.getStep(step.getStepId())
1598: .getAction(actionId);
1599:
1600: //$AR init
1601: if (isActionAvailable(action, transientVars, ps, step
1602: .getStepId())) {
1603: return step;
1604: }
1605:
1606: //$AR end
1607: }
1608:
1609: return null;
1610: }
1611:
1612: private WorkflowDescriptor getWorkflowDescriptorForAction(
1613: ActionDescriptor action) {
1614: AbstractDescriptor objWfd = action;
1615:
1616: while (!(objWfd instanceof WorkflowDescriptor)) {
1617: objWfd = objWfd.getParent();
1618: }
1619:
1620: WorkflowDescriptor wf = (WorkflowDescriptor) objWfd;
1621:
1622: return wf;
1623: }
1624:
1625: private boolean canInitialize(String workflowName,
1626: int initialAction, Map transientVars, PropertySet ps)
1627: throws WorkflowException {
1628: WorkflowDescriptor wf = getConfiguration().getWorkflow(
1629: workflowName);
1630:
1631: ActionDescriptor actionDescriptor = wf
1632: .getInitialAction(initialAction);
1633:
1634: if (actionDescriptor == null) {
1635: throw new InvalidActionException("Invalid Initial Action #"
1636: + initialAction);
1637: }
1638:
1639: RestrictionDescriptor restriction = actionDescriptor
1640: .getRestriction();
1641: ConditionsDescriptor conditions = null;
1642:
1643: if (restriction != null) {
1644: conditions = restriction.getConditionsDescriptor();
1645: }
1646:
1647: return passesConditions(conditions, new HashMap(transientVars),
1648: ps, 0);
1649: }
1650:
1651: private Step createNewCurrentStep(ResultDescriptor theResult,
1652: WorkflowEntry entry, WorkflowStore store, int actionId,
1653: Step currentStep, long[] previousIds, Map transientVars,
1654: PropertySet ps) throws WorkflowException {
1655: try {
1656: int nextStep = theResult.getStep();
1657:
1658: if (nextStep == -1) {
1659: if (currentStep != null) {
1660: nextStep = currentStep.getStepId();
1661: } else {
1662: throw new StoreException(
1663: "Illegal argument: requested new current step same as current step, but current step not specified");
1664: }
1665: }
1666:
1667: if (log.isDebugEnabled()) {
1668: log.debug("Outcome: stepId="
1669: + nextStep
1670: + ", status="
1671: + theResult.getStatus()
1672: + ", owner="
1673: + theResult.getOwner()
1674: + ", actionId="
1675: + actionId
1676: + ", currentStep="
1677: + ((currentStep != null) ? currentStep
1678: .getStepId() : 0));
1679: }
1680:
1681: if (previousIds == null) {
1682: previousIds = new long[0];
1683: }
1684:
1685: String owner = theResult.getOwner();
1686:
1687: VariableResolver variableResolver = getConfiguration()
1688: .getVariableResolver();
1689:
1690: if (owner != null) {
1691: Object o = variableResolver.translateVariables(owner,
1692: transientVars, ps);
1693: owner = (o != null) ? o.toString() : null;
1694: }
1695:
1696: String oldStatus = theResult.getOldStatus();
1697: oldStatus = variableResolver.translateVariables(oldStatus,
1698: transientVars, ps).toString();
1699:
1700: String status = theResult.getStatus();
1701: status = variableResolver.translateVariables(status,
1702: transientVars, ps).toString();
1703:
1704: if (currentStep != null) {
1705: store.markFinished(currentStep, actionId, new Date(),
1706: oldStatus, context.getCaller());
1707: store.moveToHistory(currentStep);
1708:
1709: //store.moveToHistory(actionId, new Date(), currentStep, oldStatus, context.getCaller());
1710: }
1711:
1712: // construct the start date and optional due date
1713: Date startDate = new Date();
1714: Date dueDate = null;
1715:
1716: if ((theResult.getDueDate() != null)
1717: && (theResult.getDueDate().length() > 0)) {
1718: Object dueDateObject = variableResolver
1719: .translateVariables(theResult.getDueDate(),
1720: transientVars, ps);
1721:
1722: if (dueDateObject instanceof Date) {
1723: dueDate = (Date) dueDateObject;
1724: } else if (dueDateObject instanceof String) {
1725: long offset = 0;
1726:
1727: try {
1728: offset = Long.parseLong((String) dueDateObject);
1729: } catch (NumberFormatException e) {
1730: }
1731:
1732: if (offset > 0) {
1733: dueDate = new Date(startDate.getTime() + offset);
1734: }
1735: } else if (dueDateObject instanceof Number) {
1736: Number num = (Number) dueDateObject;
1737: long offset = num.longValue();
1738:
1739: if (offset > 0) {
1740: dueDate = new Date(startDate.getTime() + offset);
1741: }
1742: }
1743: }
1744:
1745: Step newStep = store.createCurrentStep(entry.getId(),
1746: nextStep, owner, startDate, dueDate, status,
1747: previousIds);
1748: transientVars.put("createdStep", newStep);
1749:
1750: if ((previousIds != null) && (previousIds.length == 0)
1751: && (currentStep == null)) {
1752: // At this point, it must be a brand new workflow, so we'll overwrite the empty currentSteps
1753: // with an array of just this current step
1754: List currentSteps = new ArrayList();
1755: currentSteps.add(newStep);
1756: transientVars.put("currentSteps", new ArrayList(
1757: currentSteps));
1758: }
1759:
1760: WorkflowDescriptor descriptor = (WorkflowDescriptor) transientVars
1761: .get("descriptor");
1762: StepDescriptor step = descriptor.getStep(nextStep);
1763:
1764: if (step == null) {
1765: throw new WorkflowException("step #" + nextStep
1766: + " does not exist");
1767: }
1768:
1769: List preFunctions = step.getPreFunctions();
1770:
1771: for (Iterator iterator = preFunctions.iterator(); iterator
1772: .hasNext();) {
1773: FunctionDescriptor function = (FunctionDescriptor) iterator
1774: .next();
1775: executeFunction(function, transientVars, ps);
1776: }
1777:
1778: return newStep;
1779: } catch (WorkflowException e) {
1780: context.setRollbackOnly();
1781: throw e;
1782: }
1783: }
1784: }
|