Package org.jbpm.jpdl.xml

Source Code of org.jbpm.jpdl.xml.JpdlXmlReader

/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.jpdl.xml;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.jbpm.JbpmConfiguration;
import org.jbpm.context.def.VariableAccess;
import org.jbpm.graph.action.ActionTypes;
import org.jbpm.graph.def.Action;
import org.jbpm.graph.def.Event;
import org.jbpm.graph.def.ExceptionHandler;
import org.jbpm.graph.def.GraphElement;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.NodeCollection;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.node.NodeTypes;
import org.jbpm.graph.node.StartState;
import org.jbpm.graph.node.TaskNode;
import org.jbpm.instantiation.Delegation;
import org.jbpm.jpdl.JpdlException;
import org.jbpm.mail.Mail;
import org.jbpm.scheduler.def.CancelTimerAction;
import org.jbpm.scheduler.def.CreateTimerAction;
import org.jbpm.taskmgmt.def.Swimlane;
import org.jbpm.taskmgmt.def.Task;
import org.jbpm.taskmgmt.def.TaskController;
import org.jbpm.taskmgmt.def.TaskMgmtDefinition;
import org.xml.sax.InputSource;

public class JpdlXmlReader implements ProblemListener
{

  private static final long serialVersionUID = 1L;

  protected InputSource inputSource = null;
  protected List<Problem> problems = new ArrayList<Problem>();
  protected ProblemListener problemListener = null;
  protected ProcessDefinition processDefinition = null;
  protected String initialNodeName = null;
  protected Collection<Object[]> unresolvedTransitionDestinations = null;
  protected Collection<Object[]> unresolvedActionReferences = null;

  /*
   * the parsed process definition as DOM tree (available after readProcessDefinition)
   */
  protected Document document;

  /*  for autonumbering anonymous timers. */
  private int timerNumber;

  public JpdlXmlReader(InputSource inputSource)
  {
    this.inputSource = inputSource;
  }

  public JpdlXmlReader(InputSource inputSource, ProblemListener problemListener)
  {
    this.inputSource = inputSource;
    this.problemListener = problemListener;
  }

  public JpdlXmlReader(Reader reader)
  {
    this(new InputSource(reader));
  }

  public void close() throws IOException
  {
    InputStream byteStream = inputSource.getByteStream();
    if (byteStream != null)
      byteStream.close();
    else
    {
      Reader charStream = inputSource.getCharacterStream();
      if (charStream != null)
        charStream.close();
    }
    document = null;
  }

  public ProcessDefinition getProcessDefinition()
  {
    return processDefinition;
  }

  public void addProblem(Problem problem)
  {
    problems.add(problem);
    if (problemListener != null)
      problemListener.addProblem(problem);
  }

  public void addError(String description)
  {
    log.error("invalid process xml: " + description);
    addProblem(new Problem(Problem.LEVEL_ERROR, description));
  }

  public void addError(String description, Throwable exception)
  {
    log.error("invalid process xml: " + description, exception);
    addProblem(new Problem(Problem.LEVEL_ERROR, description, exception));
  }

  public void addWarning(String description)
  {
    log.warn("process xml warning: " + description);
    addProblem(new Problem(Problem.LEVEL_WARNING, description));
  }

  public ProcessDefinition readProcessDefinition()
  {
    // create a new definition
    processDefinition = ProcessDefinition.createNewProcessDefinition();

    // initialize lists
    problems = new ArrayList<Problem>();
    unresolvedTransitionDestinations = new ArrayList<Object[]>();
    unresolvedActionReferences = new ArrayList<Object[]>();

    try
    {
      // parse the document into a dom tree
      document = JpdlParser.parse(inputSource, this);
      Element root = document.getRootElement();

      // read the process name
      parseProcessDefinitionAttributes(root);

      // get the process description
      String description = root.elementTextTrim("description");
      if (description != null)
      {
        processDefinition.setDescription(description);
      }

      // first pass: read most content
      readSwimlanes(root);
      readActions(root, null, null);
      readNodes(root, processDefinition);
      readEvents(root, processDefinition);
      readExceptionHandlers(root, processDefinition);
      readTasks(root, null);

      // second pass processing
      resolveTransitionDestinations();
      resolveActionReferences();
      verifySwimlaneAssignments();

    }
    catch (Exception e)
    {
      log.error("couldn't parse process definition", e);
      addProblem(new Problem(Problem.LEVEL_ERROR, "couldn't parse process definition", e));
    }

    if (Problem.containsProblemsOfLevel(problems, Problem.LEVEL_ERROR))
    {
      throw new JpdlException(problems);
    }

    if (problems != null)
    {
      for (Problem problem : problems)
      {
        log.warn("process parse warning: " + problem.getDescription());
      }
    }

    return processDefinition;
  }

  protected void parseProcessDefinitionAttributes(Element root)
  {
    processDefinition.setName(root.attributeValue("name"));
    initialNodeName = root.attributeValue("initial");
  }

  protected void readSwimlanes(Element processDefinitionElement)
  {
    Iterator<?> iter = processDefinitionElement.elementIterator("swimlane");
    TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
    while (iter.hasNext())
    {
      Element swimlaneElement = (Element)iter.next();
      String swimlaneName = swimlaneElement.attributeValue("name");
      if (swimlaneName == null)
      {
        addWarning("there's a swimlane without a name");
      }
      else
      {
        Swimlane swimlane = new Swimlane(swimlaneName);
        Element assignmentElement = swimlaneElement.element("assignment");

        if (assignmentElement != null)
        {

          if ((assignmentElement.attribute("actor-id") != null) || (assignmentElement.attribute("pooled-actors") != null))
          {
            swimlane.setActorIdExpression(assignmentElement.attributeValue("actor-id"));
            swimlane.setPooledActorsExpression(assignmentElement.attributeValue("pooled-actors"));

          }
          else
          {
            Delegation assignmentDelegation = readAssignmentDelegation(assignmentElement);
            swimlane.setAssignmentDelegation(assignmentDelegation);
          }
        }
        taskMgmtDefinition.addSwimlane(swimlane);
      }
    }
  }

  public void readNodes(Element element, NodeCollection nodeCollection)
  {
    Iterator<?> nodeElementIter = element.elementIterator();
    while (nodeElementIter.hasNext())
    {
      Element nodeElement = (Element)nodeElementIter.next();
      String nodeName = nodeElement.getName();
      // get the node type
      Class<?> nodeType = NodeTypes.getNodeType(nodeName);
      if (nodeType != null)
      {

        Node node = null;
        try
        {
          // create a new instance
          node = (Node)nodeType.newInstance();
        }
        catch (Exception e)
        {
          log.error("couldn't instantiate node '" + nodeName + "', of type '" + nodeType.getName() + "'", e);
          continue;
        }

        node.setProcessDefinition(processDefinition);

        // check for duplicate start-states
        if ((node instanceof StartState) && (processDefinition.getStartState() != null))
        {
          addError("max one start-state allowed in a process");

        }
        else
        {
          // read the common node parts of the element
          readNode(nodeElement, node, nodeCollection);

          // if the node is parsable
          // (meaning: if the node has special configuration to parse, other then the
          // common node data)
          node.read(nodeElement, this);
        }
      }
    }
  }

  public void readTasks(Element element, TaskNode taskNode)
  {
    List<?> elements = element.elements("task");
    TaskMgmtDefinition tmd = processDefinition.getTaskMgmtDefinition();
    if (elements.size() > 0)
    {
      if (tmd == null)
      {
        tmd = new TaskMgmtDefinition();
      }
      processDefinition.addDefinition(tmd);

      for (Object taskElement : elements)
      {
        readTask((Element)taskElement, tmd, taskNode);
      }
    }
  }

  public Task readTask(Element taskElement, TaskMgmtDefinition taskMgmtDefinition, TaskNode taskNode)
  {
    Task task = new Task();
    task.setProcessDefinition(processDefinition);

    // get the task name
    String name = taskElement.attributeValue("name");
    if (name != null)
    {
      task.setName(name);
      taskMgmtDefinition.addTask(task);
    }
    else if (taskNode != null)
    {
      task.setName(taskNode.getName());
      taskMgmtDefinition.addTask(task);
    }

    // get the task description
    String description = taskElement.elementTextTrim("description");
    if (description != null)
    {
      task.setDescription(description);
    }
    else
    {
      task.setDescription(taskElement.attributeValue("description"));
    }

    // get the condition
    String condition = taskElement.elementTextTrim("condition");
    if (condition != null)
    {
      task.setCondition(condition);
    }
    else
    {
      task.setCondition(taskElement.attributeValue("condition"));
    }

    // parse common subelements
    readTaskTimers(taskElement, task);
    readEvents(taskElement, task);
    readExceptionHandlers(taskElement, task);

    // duedate and priority
    String duedateText = taskElement.attributeValue("duedate");
    if (duedateText == null)
    {
      duedateText = taskElement.attributeValue("dueDate");
    }
    task.setDueDate(duedateText);
    String priorityText = taskElement.attributeValue("priority");
    if (priorityText != null)
    {
      task.setPriority(Task.parsePriority(priorityText));
    }

    // if this task is in the context of a taskNode, associate them
    if (taskNode != null)
    {
      taskNode.addTask(task);
    }

    // blocking
    String blockingText = taskElement.attributeValue("blocking");
    if (blockingText != null)
    {
      if (("true".equalsIgnoreCase(blockingText)) || ("yes".equalsIgnoreCase(blockingText)) || ("on".equalsIgnoreCase(blockingText)))
      {
        task.setBlocking(true);
      }
    }

    // signalling
    String signallingText = taskElement.attributeValue("signalling");
    if (signallingText != null)
    {
      if (("false".equalsIgnoreCase(signallingText)) || ("no".equalsIgnoreCase(signallingText)) || ("off".equalsIgnoreCase(signallingText)))
      {
        task.setSignalling(false);
      }
    }

    // assignment
    String swimlaneName = taskElement.attributeValue("swimlane");
    Element assignmentElement = taskElement.element("assignment");

    // if there is a swimlane attribute specified
    if (swimlaneName != null)
    {
      Swimlane swimlane = taskMgmtDefinition.getSwimlane(swimlaneName);
      if (swimlane == null)
      {
        addWarning("task references unknown swimlane '" + swimlaneName + "':" + taskElement.asXML());
      }
      else
      {
        task.setSwimlane(swimlane);
      }

      // else if there is a direct assignment specified
    }
    else if (assignmentElement != null)
    {
      if ((assignmentElement.attribute("actor-id") != null) || (assignmentElement.attribute("pooled-actors") != null))
      {
        task.setActorIdExpression(assignmentElement.attributeValue("actor-id"));
        task.setPooledActorsExpression(assignmentElement.attributeValue("pooled-actors"));

      }
      else
      {
        Delegation assignmentDelegation = readAssignmentDelegation(assignmentElement);
        task.setAssignmentDelegation(assignmentDelegation);
      }

      // if no assignment or swimlane is specified
    }
    else
    {
      // the user has to manage assignment manually, so we better inform him/her.
      log.info("process xml information: no swimlane or assignment specified for task '" + taskElement.asXML() + "'");
    }

    // notify
    String notificationsText = taskElement.attributeValue("notify");
    if (notificationsText != null
        && ("true".equalsIgnoreCase(notificationsText) || "on".equalsIgnoreCase(notificationsText) || "yes".equalsIgnoreCase(notificationsText)))
    {
      String notificationEvent = Event.EVENTTYPE_TASK_ASSIGN;
      Event event = task.getEvent(notificationEvent);
      if (event == null)
      {
        event = new Event(notificationEvent);
        task.addEvent(event);
      }
      Delegation delegation = createMailDelegation(notificationEvent, null, null, null, null);
      Action action = new Action(delegation);
      action.setProcessDefinition(processDefinition);
      action.setName(task.getName());
      event.addAction(action);
    }

    // task controller
    Element taskControllerElement = taskElement.element("controller");
    if (taskControllerElement != null)
    {
      task.setTaskController(readTaskController(taskControllerElement));
    }

    return task;
  }

  protected Delegation readAssignmentDelegation(Element assignmentElement)
  {
    Delegation assignmentDelegation = new Delegation();
    String expression = assignmentElement.attributeValue("expression");
    String actorId = assignmentElement.attributeValue("actor-id");
    String pooledActors = assignmentElement.attributeValue("pooled-actors");

    if (expression != null)
    {
      assignmentDelegation.setProcessDefinition(processDefinition);
      assignmentDelegation.setClassName("org.jbpm.identity.assignment.ExpressionAssignmentHandler");
      assignmentDelegation.setConfiguration("<expression>" + expression + "</expression>");

    }
    else if ((actorId != null) || (pooledActors != null))
    {
      assignmentDelegation.setProcessDefinition(processDefinition);
      assignmentDelegation.setClassName("org.jbpm.taskmgmt.assignment.ActorAssignmentHandler");
      String configuration = "";
      if (actorId != null)
      {
        configuration += "<actorId>" + actorId + "</actorId>";
      }
      if (pooledActors != null)
      {
        configuration += "<pooledActors>" + pooledActors + "</pooledActors>";
      }
      assignmentDelegation.setConfiguration(configuration);

    }
    else
    {
      assignmentDelegation = new Delegation();
      assignmentDelegation.read(assignmentElement, this);
    }

    return assignmentDelegation;
  }

  protected TaskController readTaskController(Element taskControllerElement)
  {
    TaskController taskController = new TaskController();

    if (taskControllerElement.attributeValue("class") != null)
    {
      Delegation taskControllerDelegation = new Delegation();
      taskControllerDelegation.read(taskControllerElement, this);
      taskController.setTaskControllerDelegation(taskControllerDelegation);

    }
    else
    {
      List<VariableAccess> variableAccesses = readVariableAccesses(taskControllerElement);
      taskController.setVariableAccesses(variableAccesses);
    }
    return taskController;
  }

  public List<VariableAccess> readVariableAccesses(Element element)
  {
    List<VariableAccess> variableAccesses = new ArrayList<VariableAccess>();
    Iterator<?> iter = element.elementIterator("variable");
    while (iter.hasNext())
    {
      Element variableElement = (Element)iter.next();

      String variableName = variableElement.attributeValue("name");
      if (variableName == null)
      {
        addProblem(new Problem(Problem.LEVEL_WARNING, "the name attribute of a variable element is required: " + variableElement.asXML()));
      }
      String access = variableElement.attributeValue("access", "read,write");
      String mappedName = variableElement.attributeValue("mapped-name");

      variableAccesses.add(new VariableAccess(variableName, access, mappedName));
    }
    return variableAccesses;
  }

  public void readStartStateTask(Element startTaskElement, StartState startState)
  {
    TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
    Task startTask = readTask(startTaskElement, taskMgmtDefinition, null);
    startTask.setStartState(startState);
    if (startTask.getName() == null)
    {
      startTask.setName(startState.getName());
    }
    taskMgmtDefinition.setStartTask(startTask);
  }

  public void readNode(Element nodeElement, Node node, NodeCollection nodeCollection)
  {

    // first put the node in its collection. this is done so that the
    // setName later on will be able to differentiate between nodes contained in
    // processDefinitions and nodes contained in superstates
    nodeCollection.addNode(node);

    // get the node name
    String name = nodeElement.attributeValue("name");
    if (name != null)
    {
      node.setName(name);

      // check if this is the initial node
      if ((initialNodeName != null) && (initialNodeName.equals(node.getFullyQualifiedName())))
      {
        processDefinition.setStartState(node);
      }
    }

    // get the node description
    String description = nodeElement.elementTextTrim("description");
    if (description != null)
    {
      node.setDescription(description);
    }

    String asyncText = nodeElement.attributeValue("async");
    if ("true".equalsIgnoreCase(asyncText))
    {
      node.setAsync(true);
    }
    else if ("exclusive".equalsIgnoreCase(asyncText))
    {
      node.setAsync(true);
      node.setAsyncExclusive(true);
    }

    // parse common subelements
    readNodeTimers(nodeElement, node);
    readEvents(nodeElement, node);
    readExceptionHandlers(nodeElement, node);

    // save the transitions and parse them at the end
    addUnresolvedTransitionDestination(nodeElement, node);
  }

  protected void readNodeTimers(Element nodeElement, Node node)
  {
    Iterator<?> iter = nodeElement.elementIterator("timer");
    while (iter.hasNext())
    {
      Element timerElement = (Element)iter.next();
      readNodeTimer(timerElement, node);
    }
  }

  protected void readNodeTimer(Element timerElement, Node node)
  {
    String name = timerElement.attributeValue("name", node.getName());
    if (name == null)
      name = generateTimerName();

    CreateTimerAction createTimerAction = instantiateCreateTimerAction();
    createTimerAction.read(timerElement, this);
    createTimerAction.setTimerName(name);
    createTimerAction.setTimerAction(readSingleAction(timerElement));
    addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);

    CancelTimerAction cancelTimerAction = instantiateCancelTimerAction();
    cancelTimerAction.setTimerName(name);
    addAction(node, Event.EVENTTYPE_NODE_LEAVE, cancelTimerAction);
  }

  /*
   * instantiate {@link CreateTimerAction} object from configured class in action.types.xml (if configured). If not the default jbpm class is used.
   */
  private CreateTimerAction instantiateCreateTimerAction()
  {
    if (ActionTypes.hasActionName("create-timer"))
    {
      Class actionType = ActionTypes.getActionType("create-timer");
      try
      {
        return (CreateTimerAction)actionType.newInstance();
      }
      catch (Exception e)
      {
        log.error("couldn't instantiate 'create-timer' action of type '" + actionType.getName() + "'. Using default CreateTimerAction.", e);
      }
    }
    // default
    return new CreateTimerAction();
  }

  /*
   * instantiate {@link CancelTimerAction} object from configured class in action.types.xml (if configured). If not the default jbpm class is used.
   */
  private CancelTimerAction instantiateCancelTimerAction()
  {
    if (ActionTypes.hasActionName("cancel-timer"))
    {
      Class actionType = ActionTypes.getActionType("cancel-timer");
      try
      {
        return (CancelTimerAction)actionType.newInstance();
      }
      catch (Exception e)
      {
        log.error("couldn't instantiate 'cancel-timer' action of type '" + actionType.getName() + "'. Using default CancelTimerAction.", e);
      }
    }
    // default
    return new CancelTimerAction();
  }

  private String generateTimerName()
  {
    return "timer-" + (timerNumber++);
  }

  protected void readTaskTimers(Element taskElement, Task task)
  {
    Iterator<?> iter = taskElement.elementIterator();
    while (iter.hasNext())
    {
      Element element = (Element)iter.next();
      if (("timer".equals(element.getName())) || ("reminder".equals(element.getName())))
      {
        readTaskTimer(element, task);
      }
    }
  }

  protected void readTaskTimer(Element timerElement, Task task)
  {
    String name = timerElement.attributeValue("name", task.getName());
    if (name == null)
      name = generateTimerName();

    CreateTimerAction createTimerAction = instantiateCreateTimerAction();
    createTimerAction.read(timerElement, this);
    createTimerAction.setTimerName(name);
    Action action = null;
    if ("timer".equals(timerElement.getName()))
    {
      action = readSingleAction(timerElement);
    }
    else
    {
      Delegation delegation = createMailDelegation("task-reminder", null, null, null, null);
      action = new Action(delegation);
    }
    createTimerAction.setTimerAction(action);
    addAction(task, Event.EVENTTYPE_TASK_CREATE, createTimerAction);

    // read the cancel-event types
    Collection<String> cancelEventTypes = new ArrayList<String>();

    String cancelEventTypeText = timerElement.attributeValue("cancel-event");
    if (cancelEventTypeText != null)
    {
      // cancel-event is a comma separated list of events
      StringTokenizer tokenizer = new StringTokenizer(cancelEventTypeText, ",");
      while (tokenizer.hasMoreTokens())
      {
        cancelEventTypes.add(tokenizer.nextToken().trim());
      }
    }
    else
    {
      // set the default
      cancelEventTypes.add(Event.EVENTTYPE_TASK_END);
    }

    for (String cancelEventType : cancelEventTypes)
    {
      CancelTimerAction cancelTimerAction = instantiateCancelTimerAction();
      cancelTimerAction.setTimerName(name);
      addAction(task, cancelEventType, cancelTimerAction);
    }
  }

  protected void readEvents(Element parentElement, GraphElement graphElement)
  {
    Iterator<?> iter = parentElement.elementIterator("event");
    while (iter.hasNext())
    {
      Element eventElement = (Element)iter.next();
      String eventType = eventElement.attributeValue("type");
      if (!graphElement.hasEvent(eventType))
      {
        graphElement.addEvent(new Event(eventType));
      }
      readActions(eventElement, graphElement, eventType);
    }
  }

  public void readActions(Element eventElement, GraphElement graphElement, String eventType)
  {
    // for all the elements in the event element
    Iterator<?> nodeElementIter = eventElement.elementIterator();
    while (nodeElementIter.hasNext())
    {
      Element actionElement = (Element)nodeElementIter.next();
      String actionName = actionElement.getName();
      if (ActionTypes.hasActionName(actionName))
      {
        Action action = createAction(actionElement);
        if ((graphElement != null) && (eventType != null))
        {
          // add the action to the event
          addAction(graphElement, eventType, action);
        }
      }
    }
  }

  protected void addAction(GraphElement graphElement, String eventType, Action action)
  {
    Event event = graphElement.getEvent(eventType);
    if (event == null)
    {
      event = new Event(eventType);
      graphElement.addEvent(event);
    }
    event.addAction(action);
  }

  public Action readSingleAction(Element nodeElement)
  {
    Action action = null;
    // search for the first action element in the node
    Iterator<?> iter = nodeElement.elementIterator();
    while (iter.hasNext() && (action == null))
    {
      Element candidate = (Element)iter.next();
      if (ActionTypes.hasActionName(candidate.getName()))
      {
        // parse the action and assign it to this node
        action = createAction(candidate);
      }
    }
    return action;
  }

  public Action createAction(Element actionElement)
  {
    // create a new instance of the action
    Action action = null;
    String actionName = actionElement.getName();
    Class<? extends Action> actionType = ActionTypes.getActionType(actionName);
    try
    {
      action = actionType.newInstance();
    }
    catch (Exception e)
    {
      log.error("couldn't instantiate action '" + actionName + "', of type '" + actionType.getName() + "'", e);
    }

    // read the common node parts of the action
    readAction(actionElement, action);

    return action;
  }

  public void readAction(Element element, Action action)
  {
    // if a name is specified for this action
    String actionName = element.attributeValue("name");
    if (actionName != null)
    {
      action.setName(actionName);
      // add the action to the named process action repository
      processDefinition.addAction(action);
    }

    // if the action is parsable
    // (meaning: if the action has special configuration to parse, other then the common node data)
    action.read(element, this);
  }

  protected void readExceptionHandlers(Element graphElementElement, GraphElement graphElement)
  {
    Iterator<?> iter = graphElementElement.elementIterator("exception-handler");
    while (iter.hasNext())
    {
      Element exceptionHandlerElement = (Element)iter.next();
      readExceptionHandler(exceptionHandlerElement, graphElement);
    }
  }

  protected void readExceptionHandler(Element exceptionHandlerElement, GraphElement graphElement)
  {
    // create the exception handler
    ExceptionHandler exceptionHandler = new ExceptionHandler();
    exceptionHandler.setExceptionClassName(exceptionHandlerElement.attributeValue("exception-class"));
    // add it to the graph element
    graphElement.addExceptionHandler(exceptionHandler);

    // read the actions in the body of the exception-handler element
    Iterator<?> iter = exceptionHandlerElement.elementIterator();
    while (iter.hasNext())
    {
      Element childElement = (Element)iter.next();
      if (ActionTypes.hasActionName(childElement.getName()))
      {
        Action action = createAction(childElement);
        exceptionHandler.addAction(action);
      }
    }
  }

  // transition destinations are parsed in a second pass //////////////////////

  public void addUnresolvedTransitionDestination(Element nodeElement, Node node)
  {
    unresolvedTransitionDestinations.add(new Object[] { nodeElement, node });
  }

  public void resolveTransitionDestinations()
  {
    for (Object[] unresolvedTransition : unresolvedTransitionDestinations)
    {
      Element nodeElement = (Element)unresolvedTransition[0];
      Node node = (Node)unresolvedTransition[1];
      resolveTransitionDestinations(nodeElement.elements("transition"), node);
    }
  }

  public void resolveTransitionDestinations(List<?> transitionElements, Node node)
  {
    for (Object transitionElement : transitionElements)
    {
      resolveTransitionDestination((Element)transitionElement, node);
    }
  }

  /*
   * creates the transition object and configures it by the read attributes
   * @return the created <code>org.jbpm.graph.def.Transition</code> object (useful, if you want to override this method to read additional configuration properties)
   */
  public Transition resolveTransitionDestination(Element transitionElement, Node node)
  {
    Transition transition = new Transition();
    transition.setProcessDefinition(processDefinition);

    transition.setName(transitionElement.attributeValue("name"));
    transition.setDescription(transitionElement.elementTextTrim("description"));

    String condition = transitionElement.attributeValue("condition");
    if (condition == null)
    {
      Element conditionElement = transitionElement.element("condition");
      if (conditionElement != null)
      {
        condition = conditionElement.getTextTrim();
        // for backwards compatibility
        if ((condition == null) || (condition.length() == 0))
        {
          condition = conditionElement.attributeValue("expression");
        }
      }
    }
    transition.setCondition(condition);

    // add the transition to the node
    node.addLeavingTransition(transition);

    // set destinationNode of the transition
    String toName = transitionElement.attributeValue("to");
    if (toName == null)
    {
      addWarning("node '" + node.getFullyQualifiedName() + "' has a transition without a 'to'-attribute to specify its destinationNode");
    }
    else
    {
      Node to = ((NodeCollection)node.getParent()).findNode(toName);
      if (to == null)
      {
        addWarning("transition to='" + toName + "' on node '" + node.getFullyQualifiedName() + "' cannot be resolved");
      }
      else
      {
        to.addArrivingTransition(transition);
      }
    }

    // read the actions
    readActions(transitionElement, transition, Event.EVENTTYPE_TRANSITION);

    readExceptionHandlers(transitionElement, transition);

    return transition;
  }

  // action references are parsed in a second pass ////////////////////////////

  public void addUnresolvedActionReference(Element actionElement, Action action)
  {
    unresolvedActionReferences.add(new Object[] { actionElement, action });
  }

  public void resolveActionReferences()
  {
    for (Object[] unresolvedActionReference : unresolvedActionReferences)
    {
      Element actionElement = (Element)unresolvedActionReference[0];
      Action action = (Action)unresolvedActionReference[1];
      String referencedActionName = actionElement.attributeValue("ref-name");
      Action referencedAction = processDefinition.getAction(referencedActionName);
      if (referencedAction == null)
      {
        addWarning("couldn't resolve action reference in " + actionElement.asXML());
      }
      action.setReferencedAction(referencedAction);
    }
  }

  // verify swimlane assignments in second pass ///////////////////////////////
  public void verifySwimlaneAssignments()
  {
    TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
    if ((taskMgmtDefinition != null) && (taskMgmtDefinition.getSwimlanes() != null))
    {
      for (Swimlane swimlane : taskMgmtDefinition.getSwimlanes().values())
      {
        Task startTask = taskMgmtDefinition.getStartTask();
        Swimlane startTaskSwimlane = (startTask != null ? startTask.getSwimlane() : null);

        if ((swimlane.getAssignmentDelegation() == null)
             && (swimlane.getActorIdExpression()==null)
             && (swimlane.getPooledActorsExpression()==null)
             && (swimlane != startTaskSwimlane))
        {
          addWarning("swimlane '" + swimlane.getName() + "' does not have an assignment");
        }
      }
    }
  }

  // mail delegations /////////////////////////////////////////////////////////

  public Delegation createMailDelegation(String template, String actors, String to, String subject, String text)
  {
    StringBuffer config = new StringBuffer();
    if (template != null)
    {
      config.append("<template>");
      config.append(template);
      config.append("</template>");
    }
    if (actors != null)
    {
      config.append("<actors>");
      config.append(actors);
      config.append("</actors>");
    }
    if (to != null)
    {
      config.append("<to>");
      config.append(to);
      config.append("</to>");
    }
    if (subject != null)
    {
      config.append("<subject>");
      config.append(subject);
      config.append("</subject>");
    }
    if (text != null)
    {
      config.append("<text>");
      config.append(text);
      config.append("</text>");
    }

    String mailClassName = Mail.class.getName();
    if (JbpmConfiguration.Configs.hasObject("jbpm.mail.class.name"))
    {
      mailClassName = JbpmConfiguration.Configs.getString("jbpm.mail.class.name");
    }
    else if (JbpmConfiguration.Configs.hasObject("mail.class.name"))
    {
      mailClassName = JbpmConfiguration.Configs.getString("mail.class.name");
    }

    Delegation delegation = new Delegation(mailClassName);
    delegation.setProcessDefinition(processDefinition);
    delegation.setConfiguration(config.toString());
    return delegation;
  }

  public String getProperty(String property, Element element)
  {
    String value = element.attributeValue(property);
    if (value == null)
    {
      Element propertyElement = element.element(property);
      if (propertyElement != null)
      {
        value = propertyElement.getText();
      }
    }
    return value;
  }

  private static final Log log = LogFactory.getLog(JpdlXmlReader.class);
}
TOP

Related Classes of org.jbpm.jpdl.xml.JpdlXmlReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.