WorkflowDescriptor.java :  » Workflow-Engines » OSWorkflow » com » opensymphony » workflow » loader » Java Open Source

Java Open Source » Workflow Engines » OSWorkflow 
OSWorkflow » com » opensymphony » workflow » loader » WorkflowDescriptor.java
/*
 * Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
package com.opensymphony.workflow.loader;

import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
import com.opensymphony.workflow.util.Validatable;

import org.w3c.dom.*;

import org.xml.sax.*;

import java.io.*;

import java.util.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;


/**
 * Describes a single workflow
 *
 * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
 */
public class WorkflowDescriptor extends AbstractDescriptor implements Validatable {
    //~ Static fields/initializers /////////////////////////////////////////////

    public static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    public static final String DOCTYPE_DECL = "<!DOCTYPE workflow PUBLIC \"-//OpenSymphony Group//DTD OSWorkflow 2.8//EN\" \"http://www.opensymphony.com/osworkflow/workflow_2_8.dtd\">";

    //~ Instance fields ////////////////////////////////////////////////////////

    protected ConditionsDescriptor globalConditions = null;
    protected List commonActionsList = new ArrayList(); // for preserving order
    protected List globalActions = new ArrayList();
    protected List initialActions = new ArrayList();
    protected List joins = new ArrayList();
    protected List registers = new ArrayList();
    protected List splits = new ArrayList();
    protected List steps = new ArrayList();
    protected Map commonActions = new HashMap();
    protected Map metaAttributes = new HashMap();
    protected Map timerFunctions = new HashMap();
    protected String workflowName = null;

    //~ Constructors ///////////////////////////////////////////////////////////

    /**
     * @deprecated use {@link DescriptorFactory} instead
     */
    public WorkflowDescriptor() {
    }

    /**
     * @deprecated use {@link DescriptorFactory} instead
     */
    public WorkflowDescriptor(Element root) {
        init(root);
    }

    //~ Methods ////////////////////////////////////////////////////////////////

    public ActionDescriptor getAction(int id) {
        // check global actions
        for (Iterator iterator = globalActions.iterator(); iterator.hasNext();) {
            ActionDescriptor actionDescriptor = (ActionDescriptor) iterator.next();

            if (actionDescriptor.getId() == id) {
                return actionDescriptor;
            }
        }

        // check steps
        for (Iterator iterator = steps.iterator(); iterator.hasNext();) {
            StepDescriptor stepDescriptor = (StepDescriptor) iterator.next();
            ActionDescriptor actionDescriptor = stepDescriptor.getAction(id);

            if (actionDescriptor != null) {
                return actionDescriptor;
            }
        }

        //check initial actions, which we now must have unique id's
        for (Iterator iterator = initialActions.iterator(); iterator.hasNext();) {
            ActionDescriptor actionDescriptor = (ActionDescriptor) iterator.next();

            if (actionDescriptor.getId() == id) {
                return actionDescriptor;
            }
        }

        return null;
    }

    /**
     * Get a Map of the common actions specified, keyed on actionId (an Integer)
     * @return A list of {@link ActionDescriptor} objects
     */
    public Map getCommonActions() {
        return commonActions;
    }

    /**
     * Get a List of the global actions specified
     * @return A list of {@link ActionDescriptor} objects
     */
    public List getGlobalActions() {
        return globalActions;
    }

    public ConditionsDescriptor getGlobalConditions() {
        return globalConditions;
    }

    public ActionDescriptor getInitialAction(int id) {
        for (Iterator iterator = initialActions.iterator(); iterator.hasNext();) {
            ActionDescriptor actionDescriptor = (ActionDescriptor) iterator.next();

            if (actionDescriptor.getId() == id) {
                return actionDescriptor;
            }
        }

        return null;
    }

    /**
     * Get a List of initial steps for this workflow
     * @return A list of {@link ActionDescriptor} objects
     */
    public List getInitialActions() {
        return initialActions;
    }

    public JoinDescriptor getJoin(int id) {
        for (Iterator iterator = joins.iterator(); iterator.hasNext();) {
            JoinDescriptor joinDescriptor = (JoinDescriptor) iterator.next();

            if (joinDescriptor.getId() == id) {
                return joinDescriptor;
            }
        }

        return null;
    }

    /**
     * Get a List of initial steps for this workflow
     * @return A list of {@link JoinDescriptor} objects
     */
    public List getJoins() {
        return joins;
    }

    public Map getMetaAttributes() {
        return metaAttributes;
    }

    public void setName(String name) {
        workflowName = name;
    }

    public String getName() {
        return workflowName;
    }

    public List getRegisters() {
        return registers;
    }

    public SplitDescriptor getSplit(int id) {
        for (Iterator iterator = splits.iterator(); iterator.hasNext();) {
            SplitDescriptor splitDescriptor = (SplitDescriptor) iterator.next();

            if (splitDescriptor.getId() == id) {
                return splitDescriptor;
            }
        }

        return null;
    }

    /**
     * Get a List of initial steps for this workflow
     * @return A list of {@link SplitDescriptor} objects
     */
    public List getSplits() {
        return splits;
    }

    public StepDescriptor getStep(int id) {
        for (Iterator iterator = steps.iterator(); iterator.hasNext();) {
            StepDescriptor step = (StepDescriptor) iterator.next();

            if (step.getId() == id) {
                return step;
            }
        }

        return null;
    }

    /**
     * Get a List of steps in this workflow
     * @return a List of {@link StepDescriptor} objects
     */
    public List getSteps() {
        return steps;
    }

    /**
     * Update a trigger function
     * @param id The id for the trigger function
     * @param descriptor The descriptor for the trigger function
     * @return The old trigger function with the specified ID, if any existed
     */
    public FunctionDescriptor setTriggerFunction(int id, FunctionDescriptor descriptor) {
        return (FunctionDescriptor) timerFunctions.put(new Integer(id), descriptor);
    }

    public FunctionDescriptor getTriggerFunction(int id) {
        return (FunctionDescriptor) this.timerFunctions.get(new Integer(id));
    }

    /**
     * Get a Map of all trigger functions in this workflow
     * @return a Map with Integer keys and {@link FunctionDescriptor} values
     */
    public Map getTriggerFunctions() {
        return timerFunctions;
    }

    /**
     * Add a common action
     * @param descriptor The action descriptor to add
     * @throws IllegalArgumentException if the descriptor's ID already exists in the workflow
     */
    public void addCommonAction(ActionDescriptor descriptor) {
        descriptor.setCommon(true);
        addAction(commonActions, descriptor);
        addAction(commonActionsList, descriptor);
    }

    /**
     * Add a global action
     * @param descriptor The action descriptor to add
     * @throws IllegalArgumentException if the descriptor's ID already exists in the workflow
     */
    public void addGlobalAction(ActionDescriptor descriptor) {
        addAction(globalActions, descriptor);
    }

    /**
     * Add an initial action
     * @param descriptor The action descriptor to add
     * @throws IllegalArgumentException if the descriptor's ID already exists in the workflow
     */
    public void addInitialAction(ActionDescriptor descriptor) {
        addAction(initialActions, descriptor);
    }

    /**
     * Add a join
     * @param descriptor The join descriptor to add
     * @throws IllegalArgumentException if the descriptor's ID already exists in the workflow
     */
    public void addJoin(JoinDescriptor descriptor) {
        if (getJoin(descriptor.getId()) != null) {
            throw new IllegalArgumentException("Join with id " + descriptor.getId() + " already exists");
        }

        joins.add(descriptor);
    }

    /**
     * Add a split
     * @param descriptor The split descriptor to add
     * @throws IllegalArgumentException if the descriptor's ID already exists in the workflow
     */
    public void addSplit(SplitDescriptor descriptor) {
        if (getSplit(descriptor.getId()) != null) {
            throw new IllegalArgumentException("Split with id " + descriptor.getId() + " already exists");
        }

        splits.add(descriptor);
    }

    /**
     * Add a step
     * @param descriptor The step descriptor to add
     * @throws IllegalArgumentException if the descriptor's ID already exists in the workflow
     */
    public void addStep(StepDescriptor descriptor) {
        if (getStep(descriptor.getId()) != null) {
            throw new IllegalArgumentException("Step with id " + descriptor.getId() + " already exists");
        }

        steps.add(descriptor);
    }

    /**
     * Remove an action from this workflow completely.
     * <p>
     * This method will check global actions and all steps.
     *
     * @return true if the action was successfully removed, false if it was not found
     */
    public boolean removeAction(ActionDescriptor actionToRemove) {
        // global actions
        for (Iterator iterator = getGlobalActions().iterator();
                iterator.hasNext();) {
            ActionDescriptor actionDescriptor = (ActionDescriptor) iterator.next();

            if (actionDescriptor.getId() == actionToRemove.getId()) {
                getGlobalActions().remove(actionDescriptor);

                return true;
            }
        }

        // steps
        for (Iterator iterator = getSteps().iterator(); iterator.hasNext();) {
            StepDescriptor stepDescriptor = (StepDescriptor) iterator.next();
            ActionDescriptor actionDescriptor = stepDescriptor.getAction(actionToRemove.getId());

            if (actionDescriptor != null) {
                stepDescriptor.getActions().remove(actionDescriptor);

                return true;
            }
        }

        return false;
    }

    public void validate() throws InvalidWorkflowDescriptorException {
        ValidationHelper.validate(this.getRegisters());
        ValidationHelper.validate(this.getTriggerFunctions().values());
        ValidationHelper.validate(this.getGlobalActions());
        ValidationHelper.validate(this.getInitialActions());
        ValidationHelper.validate(this.getCommonActions().values());
        ValidationHelper.validate(this.getSteps());
        ValidationHelper.validate(this.getSplits());
        ValidationHelper.validate(this.getJoins());

        Set actions = new HashSet();
        Iterator i = globalActions.iterator();

        while (i.hasNext()) {
            ActionDescriptor action = (ActionDescriptor) i.next();
            actions.add(new Integer(action.getId()));
        }

        i = getSteps().iterator();

        while (i.hasNext()) {
            StepDescriptor step = (StepDescriptor) i.next();
            Iterator j = step.getActions().iterator();

            while (j.hasNext()) {
                ActionDescriptor action = (ActionDescriptor) j.next();

                // check to see if it's a common action (dups are ok, since that's the point of common actions!)
                if (!action.isCommon()) {
                    if (!actions.add(new Integer(action.getId()))) {
                        throw new InvalidWorkflowDescriptorException("Duplicate occurance of action ID " + action.getId() + " found in step " + step.getId());
                    }
                }
            }
        }

        //now we have all our unique actions, let's check that no common action id's exist in them
        i = commonActions.keySet().iterator();

        while (i.hasNext()) {
            Integer action = (Integer) i.next();

            if (actions.contains(action)) {
                throw new InvalidWorkflowDescriptorException("common-action ID " + action + " is duplicated in a step action");
            }
        }

        i = initialActions.iterator();

        while (i.hasNext()) {
            ActionDescriptor action = (ActionDescriptor) i.next();

            if (actions.contains(new Integer(action.getId()))) {
                throw new InvalidWorkflowDescriptorException("initial-action ID " + action + " is duplicated in a step action");
            }
        }

        validateDTD();
    }

    public void writeXML(PrintWriter out, int indent) {
        XMLUtil.printIndent(out, indent++);
        out.println("<workflow>");

        Iterator iter = metaAttributes.entrySet().iterator();

        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            XMLUtil.printIndent(out, indent);
            out.print("<meta name=\"");
            out.print(XMLUtil.encode(entry.getKey()));
            out.print("\">");
            out.print(XMLUtil.encode(entry.getValue()));
            out.println("</meta>");
        }

        if (registers.size() > 0) {
            XMLUtil.printIndent(out, indent++);
            out.println("<registers>");

            for (int i = 0; i < registers.size(); i++) {
                RegisterDescriptor register = (RegisterDescriptor) registers.get(i);
                register.writeXML(out, indent);
            }

            XMLUtil.printIndent(out, --indent);
            out.println("</registers>");
        }

        if (timerFunctions.size() > 0) {
            XMLUtil.printIndent(out, indent++);
            out.println("<trigger-functions>");

            Iterator iterator = timerFunctions.entrySet().iterator();

            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator.next();
                XMLUtil.printIndent(out, indent++);
                out.println("<trigger-function id=\"" + entry.getKey() + "\">");

                FunctionDescriptor trigger = (FunctionDescriptor) entry.getValue();
                trigger.writeXML(out, indent);
                XMLUtil.printIndent(out, --indent);
                out.println("</trigger-function>");
            }

            while (iterator.hasNext()) {
            }

            XMLUtil.printIndent(out, --indent);
            out.println("</trigger-functions>");
        }

        if (getGlobalConditions() != null) {
            XMLUtil.printIndent(out, indent++);
            out.println("<global-conditions>");

            getGlobalConditions().writeXML(out, indent);

            out.println("</global-conditions>");
        }

        XMLUtil.printIndent(out, indent++);
        out.println("<initial-actions>");

        for (int i = 0; i < initialActions.size(); i++) {
            ActionDescriptor action = (ActionDescriptor) initialActions.get(i);
            action.writeXML(out, indent);
        }

        XMLUtil.printIndent(out, --indent);
        out.println("</initial-actions>");

        if (globalActions.size() > 0) {
            XMLUtil.printIndent(out, indent++);
            out.println("<global-actions>");

            for (int i = 0; i < globalActions.size(); i++) {
                ActionDescriptor action = (ActionDescriptor) globalActions.get(i);
                action.writeXML(out, indent);
            }

            XMLUtil.printIndent(out, --indent);
            out.println("</global-actions>");
        }

        if (commonActions.size() > 0) {
            XMLUtil.printIndent(out, indent++);
            out.println("<common-actions>");

            Iterator iterator = getCommonActions().values().iterator();

            while (iterator.hasNext()) {
                ActionDescriptor action = (ActionDescriptor) iterator.next();
                action.writeXML(out, indent);
            }

            XMLUtil.printIndent(out, --indent);
            out.println("</common-actions>");
        }

        XMLUtil.printIndent(out, indent++);
        out.println("<steps>");

        for (int i = 0; i < steps.size(); i++) {
            StepDescriptor step = (StepDescriptor) steps.get(i);
            step.writeXML(out, indent);
        }

        XMLUtil.printIndent(out, --indent);
        out.println("</steps>");

        if (splits.size() > 0) {
            XMLUtil.printIndent(out, indent++);
            out.println("<splits>");

            for (int i = 0; i < splits.size(); i++) {
                SplitDescriptor split = (SplitDescriptor) splits.get(i);
                split.writeXML(out, indent);
            }

            XMLUtil.printIndent(out, --indent);
            out.println("</splits>");
        }

        if (joins.size() > 0) {
            XMLUtil.printIndent(out, indent++);
            out.println("<joins>");

            for (int i = 0; i < joins.size(); i++) {
                JoinDescriptor join = (JoinDescriptor) joins.get(i);
                join.writeXML(out, indent);
            }

            XMLUtil.printIndent(out, --indent);
            out.println("</joins>");
        }

        XMLUtil.printIndent(out, --indent);
        out.println("</workflow>");
    }

    protected void init(Element root) {
        NodeList children = root.getChildNodes();

        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);

            if (child.getNodeName().equals("meta")) {
                Element meta = (Element) child;
                String value = XMLUtil.getText(meta);
                this.metaAttributes.put(meta.getAttribute("name"), value);
            }
        }

        // handle registers - OPTIONAL
        Element r = XMLUtil.getChildElement(root, "registers");

        if (r != null) {
            List registers = XMLUtil.getChildElements(r, "register");

            for (int i = 0; i < registers.size(); i++) {
                Element register = (Element) registers.get(i);
                RegisterDescriptor registerDescriptor = DescriptorFactory.getFactory().createRegisterDescriptor(register);
                registerDescriptor.setParent(this);
                this.registers.add(registerDescriptor);
            }
        }

        // handle global-conditions - OPTIONAL
        Element globalConditionsElement = XMLUtil.getChildElement(root, "global-conditions");

        if (globalConditionsElement != null) {
            Element globalConditions = XMLUtil.getChildElement(globalConditionsElement, "conditions");

            ConditionsDescriptor conditionsDescriptor = DescriptorFactory.getFactory().createConditionsDescriptor(globalConditions);
            conditionsDescriptor.setParent(this);
            this.globalConditions = conditionsDescriptor;
        }

        // handle initial-steps - REQUIRED
        Element intialActionsElement = XMLUtil.getChildElement(root, "initial-actions");
        List initialActions = XMLUtil.getChildElements(intialActionsElement, "action");

        for (int i = 0; i < initialActions.size(); i++) {
            Element initialAction = (Element) initialActions.get(i);
            ActionDescriptor actionDescriptor = DescriptorFactory.getFactory().createActionDescriptor(initialAction);
            actionDescriptor.setParent(this);
            this.initialActions.add(actionDescriptor);
        }

        // handle global-actions - OPTIONAL
        Element globalActionsElement = XMLUtil.getChildElement(root, "global-actions");

        if (globalActionsElement != null) {
            List globalActions = XMLUtil.getChildElements(globalActionsElement, "action");

            for (int i = 0; i < globalActions.size(); i++) {
                Element globalAction = (Element) globalActions.get(i);
                ActionDescriptor actionDescriptor = DescriptorFactory.getFactory().createActionDescriptor(globalAction);
                actionDescriptor.setParent(this);
                this.globalActions.add(actionDescriptor);
            }
        }

        // handle common-actions - OPTIONAL
        //   - Store actions in HashMap for now. When parsing Steps, we'll resolve
        //      any common actions into local references.
        Element commonActionsElement = XMLUtil.getChildElement(root, "common-actions");

        if (commonActionsElement != null) {
            List commonActions = XMLUtil.getChildElements(commonActionsElement, "action");

            for (int i = 0; i < commonActions.size(); i++) {
                Element commonAction = (Element) commonActions.get(i);
                ActionDescriptor actionDescriptor = DescriptorFactory.getFactory().createActionDescriptor(commonAction);
                actionDescriptor.setParent(this);
                addCommonAction(actionDescriptor);
            }
        }

        // handle timer-functions - OPTIONAL
        Element timerFunctionsElement = XMLUtil.getChildElement(root, "trigger-functions");

        if (timerFunctionsElement != null) {
            List timerFunctions = XMLUtil.getChildElements(timerFunctionsElement, "trigger-function");

            for (int i = 0; i < timerFunctions.size(); i++) {
                Element timerFunction = (Element) timerFunctions.get(i);
                Integer id = new Integer(timerFunction.getAttribute("id"));
                FunctionDescriptor function = DescriptorFactory.getFactory().createFunctionDescriptor(XMLUtil.getChildElement(timerFunction, "function"));
                function.setParent(this);
                this.timerFunctions.put(id, function);
            }
        }

        // handle steps - REQUIRED
        Element stepsElement = XMLUtil.getChildElement(root, "steps");
        List steps = XMLUtil.getChildElements(stepsElement, "step");

        for (int i = 0; i < steps.size(); i++) {
            Element step = (Element) steps.get(i);
            StepDescriptor stepDescriptor = DescriptorFactory.getFactory().createStepDescriptor(step, this);
            this.steps.add(stepDescriptor);
        }

        // handle splits - OPTIONAL
        Element splitsElement = XMLUtil.getChildElement(root, "splits");

        if (splitsElement != null) {
            List split = XMLUtil.getChildElements(splitsElement, "split");

            for (int i = 0; i < split.size(); i++) {
                Element s = (Element) split.get(i);
                SplitDescriptor splitDescriptor = DescriptorFactory.getFactory().createSplitDescriptor(s);
                splitDescriptor.setParent(this);
                this.splits.add(splitDescriptor);
            }
        }

        // handle joins - OPTIONAL:
        Element joinsElement = XMLUtil.getChildElement(root, "joins");

        if (joinsElement != null) {
            List join = XMLUtil.getChildElements(joinsElement, "join");

            for (int i = 0; i < join.size(); i++) {
                Element s = (Element) join.get(i);
                JoinDescriptor joinDescriptor = DescriptorFactory.getFactory().createJoinDescriptor(s);
                joinDescriptor.setParent(this);
                this.joins.add(joinDescriptor);
            }
        }
    }

    // refactored this out from the three addAction methods above
    private void addAction(Object actionsCollectionOrMap, ActionDescriptor descriptor) {
        if (getAction(descriptor.getId()) != null) {
            throw new IllegalArgumentException("action with id " + descriptor.getId() + " already exists for this step.");
        }

        if (actionsCollectionOrMap instanceof Map) {
            ((Map) actionsCollectionOrMap).put(new Integer(descriptor.getId()), descriptor);
        } else {
            ((Collection) actionsCollectionOrMap).add(descriptor);
        }
    }

    private void validateDTD() throws InvalidWorkflowDescriptorException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        dbf.setValidating(true);

        StringWriter sw = new StringWriter();
        PrintWriter writer = new PrintWriter(sw);
        writer.println(XML_HEADER);
        writer.println(DOCTYPE_DECL);
        writeXML(writer, 0);

        WorkflowLoader.AllExceptionsErrorHandler errorHandler = new WorkflowLoader.AllExceptionsErrorHandler();

        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            db.setEntityResolver(new DTDEntityResolver());

            db.setErrorHandler(errorHandler);
            db.parse(new InputSource(new StringReader(sw.toString())));

            if (errorHandler.getExceptions().size() > 0) {
                throw new InvalidWorkflowDescriptorException(errorHandler.getExceptions().toString());
            }
        } catch (InvalidWorkflowDescriptorException e) {
            throw e;
        } catch (Exception e) {
            throw new InvalidWorkflowDescriptorException(e.toString());
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.