All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jbpm.jpdl.xml.JpdlXmlReader Maven / Gradle / Ivy

The newest version!
/*
 * 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.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.jbpm.JbpmConfiguration.Configs;
import org.jbpm.JbpmException;
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;
  protected List problems = new ArrayList();
  protected ProblemListener problemListener;
  protected ProcessDefinition processDefinition;
  protected String initialNodeName;
  protected Collection unresolvedTransitionDestinations = new ArrayList();
  protected Collection unresolvedActionReferences = new ArrayList();

  /** process document, available after {@link #readProcessDefinition()} */
  protected Document document;

  /** for auto-numbering anonymous timers. */
  private int timerNumber;

  private XMLWriter xmlWriter;

  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) {
    addProblem(new Problem(Problem.LEVEL_ERROR, description));
  }

  public void addError(String description, Throwable exception) {
    addProblem(new Problem(Problem.LEVEL_ERROR, description, exception));
  }

  public void addWarning(String description) {
    addProblem(new Problem(Problem.LEVEL_WARNING, description));
  }

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

    // clear lists
    problems.clear();
    unresolvedTransitionDestinations.clear();
    unresolvedActionReferences.clear();

    try {
      // parse the document into a dom tree
      document = JpdlParser.parse(inputSource, this);
    }
    catch (DocumentException e) {
      throw new JpdlException("failed to parse process document", e);
    }

    // read the process name
    Element root = document.getRootElement();
    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();

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

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

  protected void readSwimlanes(Element processDefinitionElement) {
    TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
    for (Iterator iter = processDefinitionElement.elementIterator("swimlane"); iter.hasNext();) {
      Element swimlaneElement = (Element) iter.next();
      String swimlaneName = swimlaneElement.attributeValue("name");
      if (swimlaneName == null) {
        addWarning("unnamed swimlane detected");
      }
      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);
          }
        }
        else {
          Task startTask = taskMgmtDefinition.getStartTask();
          if (startTask == null || startTask.getSwimlane() != swimlane) {
            addWarning("swimlane '" + swimlaneName + "' does not have an assignment");
          }
        }
        taskMgmtDefinition.addSwimlane(swimlane);
      }
    }
  }

  public void readNodes(Element element, NodeCollection nodeCollection) {
    for (Iterator iter = element.elementIterator(); iter.hasNext();) {
      Element nodeElement = (Element) iter.next();
      String nodeName = nodeElement.getName();
      // get the node type
      Class nodeType = NodeTypes.getNodeType(nodeName);
      if (nodeType != null) {
        try {
          // instantiate the node
          Node node = (Node) nodeType.newInstance();
          node.setProcessDefinition(processDefinition);

          // check for duplicate start-states
          if (node instanceof StartState && processDefinition.getStartState() != null) {
            addError("only one start-state is allowed");
          }
          else {
            // read the common node parts of the element
            readNode(nodeElement, node, nodeCollection);
            // let node parse its specific configuration
            node.read(nodeElement, this);
          }
        }
        catch (InstantiationException e) {
          throw new JbpmException("failed to instantiate " + nodeType, e);
        }
        catch (IllegalAccessException e) {
          throw new JbpmException(getClass() + " has no access to " + nodeType, e);
        }
      }
    }
  }

  public void readTasks(Element element, TaskNode taskNode) {
    List elements = element.elements("task");
    if (elements.size() > 0) {
      TaskMgmtDefinition tmd = processDefinition.getTaskMgmtDefinition();
      for (Iterator iter = elements.iterator(); iter.hasNext();) {
        Element taskElement = (Element) iter.next();
        readTask(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) description = taskElement.attributeValue("description");
    task.setDescription(description);

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

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

    // due date
    String duedateText = taskElement.attributeValue("duedate");
    if (duedateText == null) duedateText = taskElement.attributeValue("dueDate");
    task.setDueDate(duedateText);

    // priority
    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");
    task.setBlocking(readBoolean(blockingText, false));

    // signalling
    String signallingText = taskElement.attributeValue("signalling");
    task.setSignalling(readBoolean(signallingText, true));

    // 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: " + taskElement.getPath());
      }
      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 {
      // user has to manage assignment manually, so better inform him/her
      addProblem(new Problem(Problem.LEVEL_INFO, "no assignment specified for task: "
        + taskElement.getPath()));
    }

    // notify
    String notificationsText = taskElement.attributeValue("notify");
    if (readBoolean(notificationsText, false)) {
      // create mail action
      Delegation delegation = createMailDelegation(Event.EVENTTYPE_TASK_ASSIGN);
      Action action = new Action(delegation);
      action.setProcessDefinition(processDefinition);
      action.setName(task.getName());

      // attach action to task assign event
      addAction(task, Event.EVENTTYPE_TASK_ASSIGN, action);
    }

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

    return task;
  }

  public boolean readBoolean(String text, boolean defaultValue) {
    if (text == null) return defaultValue;
    if ("true".equalsIgnoreCase(text) || "yes".equalsIgnoreCase(text)
      || "on".equalsIgnoreCase(text)) return true;
    if ("false".equalsIgnoreCase(text) || "no".equalsIgnoreCase(text)
      || "off".equalsIgnoreCase(text)) return false;
    return defaultValue;
  }

  protected Delegation readAssignmentDelegation(Element assignmentElement) {
    Delegation assignmentDelegation = new Delegation();
    assignmentDelegation.setProcessDefinition(processDefinition);

    String expression = assignmentElement.attributeValue("expression");
    if (expression != null) {
      // read assigment expression
      Element config = DocumentHelper.createElement("expression");
      config.setText(expression);

      assignmentDelegation.setClassName("org.jbpm.identity.assignment.ExpressionAssignmentHandler");
      assignmentDelegation.setConfiguration(writeElement(config));
    }
    else {
      String actorId = assignmentElement.attributeValue("actor-id");
      String pooledActors = assignmentElement.attributeValue("pooled-actors");
      if (actorId != null || pooledActors != null) {
        // read assignment actors
        Element config = DocumentHelper.createElement("configuration");
        if (actorId != null) config.addElement("actorId").setText(actorId);
        if (pooledActors != null) config.addElement("pooledActors").setText(pooledActors);

        assignmentDelegation.setClassName("org.jbpm.taskmgmt.assignment.ActorAssignmentHandler");
        assignmentDelegation.setConfiguration(writeElementContent(config));
      }
      else {
        // parse custom assignment handler
        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 variableAccesses = readVariableAccesses(taskControllerElement);
      taskController.setVariableAccesses(variableAccesses);
    }
    return taskController;
  }

  public List readVariableAccesses(Element element) {
    List variableAccesses = new ArrayList();
    for (Iterator iter = element.elementIterator("variable"); iter.hasNext();) {
      Element variableElement = (Element) iter.next();
      // name
      String variableName = variableElement.attributeValue("name");
      if (variableName == null) {
        addWarning("variable name not specified: " + variableElement.getPath());
      }
      // access
      String access = variableElement.attributeValue("access", "read,write");
      // mapped name
      String mappedName = variableElement.attributeValue("mapped-name");
      // variable access
      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 ("exclusive".equalsIgnoreCase(asyncText)) {
      node.setAsyncExclusive(true);
    }
    else if (readBoolean(asyncText, false)) {
      node.setAsync(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) {
    for (Iterator iter = nodeElement.elementIterator("timer"); 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 = new CreateTimerAction();
    createTimerAction.read(timerElement, this);
    createTimerAction.setTimerName(name);
    createTimerAction.setTimerAction(readSingleAction(timerElement));
    addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);

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

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

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

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

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

    // read the cancel-event types
    String cancelEventText = timerElement.attributeValue("cancel-event");
    if (cancelEventText != null) {
      // cancel-event is a comma separated list of events
      String[] cancelEventTypes = cancelEventText.split("[\\s,]+");
      if (cancelEventTypes.length > 1 || cancelEventTypes[0].length() > 0) {
        for (int i = 0; i < cancelEventTypes.length; i++) {
          CancelTimerAction cancelTimerAction = new CancelTimerAction();
          cancelTimerAction.setTimerName(name);
          addAction(task, cancelEventTypes[i], cancelTimerAction);
        }
      }
    }
    else {
      // cancel on task end by default
      CancelTimerAction cancelTimerAction = new CancelTimerAction();
      cancelTimerAction.setTimerName(name);
      addAction(task, Event.EVENTTYPE_TASK_END, cancelTimerAction);
    }
  }

  protected void readEvents(Element parentElement, GraphElement graphElement) {
    for (Iterator iter = parentElement.elementIterator("event"); iter.hasNext();) {
      Element eventElement = (Element) iter.next();
      // register event of the defined type
      String eventType = eventElement.attributeValue("type");
      if (!graphElement.hasEvent(eventType)) graphElement.addEvent(new Event(eventType));
      // parse any actions associated to the event
      readActions(eventElement, graphElement, eventType);
    }
  }

  /** Reads actions associated to the given event. */
  public void readActions(Element eventElement, GraphElement graphElement, String eventType) {
    // for all the elements in the event element
    for (Iterator iter = eventElement.elementIterator(); iter.hasNext();) {
      Element actionElement = (Element) iter.next();
      if (ActionTypes.hasActionName(actionElement.getName())) {
        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);
  }

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

  /** Instantiates and configures an action. */
  public Action createAction(Element actionElement) {
    String actionName = actionElement.getName();
    Class actionType = ActionTypes.getActionType(actionName);
    try {
      // instantiate action
      Action action = (Action) actionType.newInstance();
      // read the common action parts of the element
      readAction(actionElement, action);
      // let action parse its specific configuration
      action.read(actionElement, this);
      return action;
    }
    catch (InstantiationException e) {
      throw new JbpmException("failed to instantiate " + actionType, e);
    }
    catch (IllegalAccessException e) {
      throw new JbpmException(getClass() + " has no access to " + actionType, e);
    }
  }

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

    String acceptPropagatedEvents = actionElement.attributeValue("accept-propagated-events");
    action.setPropagationAllowed(readBoolean(acceptPropagatedEvents, true));

    String asyncText = actionElement.attributeValue("async");
    if ("exclusive".equalsIgnoreCase(asyncText)) {
      action.setAsyncExclusive(true);
    }
    else if (readBoolean(asyncText, false)) {
      action.setAsync(true);
    }
  }

  protected void readExceptionHandlers(Element graphDomElement, GraphElement graphElement) {
    for (Iterator iter = graphDomElement.elementIterator("exception-handler"); 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
    for (Iterator iter = exceptionHandlerElement.elementIterator(); 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 (Iterator iter = unresolvedTransitionDestinations.iterator(); iter.hasNext();) {
      Object[] unresolvedTransition = (Object[]) iter.next();
      Element nodeElement = (Element) unresolvedTransition[0];
      Node node = (Node) unresolvedTransition[1];
      resolveTransitionDestinations(nodeElement.elements("transition"), node);
    }
  }

  public void resolveTransitionDestinations(List transitionElements, Node node) {
    for (Iterator iter = transitionElements.iterator(); iter.hasNext();) {
      Element transitionElement = (Element) iter.next();
      resolveTransitionDestination(transitionElement, node);
    }
  }

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

    Transition transition = new Transition();
    transition.setProcessDefinition(processDefinition);
    transition.setName(transitionName);
    transition.setDescription(transitionElement.elementTextTrim("description"));

    // read transition condition
    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);

    // register transition in origin
    node.addLeavingTransition(transition);

    // register transition in destination
    String toName = transitionElement.attributeValue("to");
    if (toName == null) {
      addWarning("node '" + node.getFullyQualifiedName()
        + "' has a transition without a 'to'-attribute");
    }
    else {
      NodeCollection parent = (NodeCollection) node.getParent();
      Node to = parent.findNode(toName);
      if (to == null) {
        addWarning("failed to resolve destination '" + toName + "' of transition '"
          + transitionName + "' leaving from " + node);
      }
      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 (Iterator iter = unresolvedActionReferences.iterator(); iter.hasNext();) {
      Object[] unresolvedActionReference = (Object[]) iter.next();
      Element actionElement = (Element) unresolvedActionReference[0];
      Action refAction = processDefinition.getAction(actionElement.attributeValue("ref-name"));
      if (refAction == null) {
        addWarning("referenced action not found: " + actionElement.getPath());
      }
      else {
        Action action = (Action) unresolvedActionReference[1];
        action.setReferencedAction(refAction);
      }
    }
  }

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

      for (Iterator iter = swimlanes.values().iterator(); iter.hasNext();) {
        Swimlane swimlane = (Swimlane) iter.next();
        if (swimlane.getAssignmentDelegation() == null && swimlane != startTaskSwimlane) {
          addWarning("swimlane '" + swimlane.getName() + "' does not have an assignment");
        }
      }
    }
  }

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

  private Delegation createMailDelegation(Element config) {
    // read mail class name
    String mailClassName;
    if (Configs.hasObject("jbpm.mail.class.name")) {
      mailClassName = Configs.getString("jbpm.mail.class.name");
    }
    else {
      mailClassName = Mail.class.getName();
    }

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

  private Delegation createMailDelegation(String template) {
    Element config = DocumentHelper.createElement("config");
    config.addElement("template").setText(template);
    return createMailDelegation(config);
  }

  /** @deprecated call {@link #readMailDelegation(Element)} instead */
  public Delegation createMailDelegation(String template, String actors, String to,
    String subject, String text) {
    Element config = DocumentHelper.createElement("config");
    // template
    if (template != null) config.addElement("template").setText(template);
    // to - addresses
    if (to != null) config.addElement("to").setText(to);
    // to - actors
    if (actors != null) config.addElement("actors").setText(actors);
    // subject
    if (subject != null) config.addElement("subject").setText(subject);
    // text
    if (text != null) config.addElement("text").setText(text);

    return createMailDelegation(config);
  }

  public Delegation readMailDelegation(Element element) {
    Element config = DocumentHelper.createElement("config");
    // template
    String template = element.attributeValue("template");
    if (template != null) config.addElement("template").setText(template);
    // to - addresses
    String to = element.attributeValue("to");
    if (to != null) config.addElement("to").setText(to);
    // to - actors
    String actors = element.attributeValue("actors");
    if (actors != null) config.addElement("actors").setText(actors);
    // cc - addresses
    String cc = element.attributeValue("cc");
    if (cc != null) config.addElement("cc").setText(cc);
    // cc - actors
    String ccActors = element.attributeValue("cc-actors");
    if (ccActors != null) config.addElement("ccActors").setText(ccActors);
    // bcc - addresses
    String bcc = element.attributeValue("bcc");
    if (bcc != null) config.addElement("bcc").setText(bcc);
    // bcc - actors
    String bccActors = element.attributeValue("bcc-actors");
    if (bccActors != null) config.addElement("bccActors").setText("bcc-actors");
    // subject
    String subject = getProperty("subject", element);
    if (subject != null) config.addElement("subject").setText(subject);
    // text
    String text = getProperty("text", element);
    if (text != null) config.addElement("text").setText(text);

    return createMailDelegation(config);
  }

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

  private XMLWriter getXmlWriter() {
    if (xmlWriter == null) {
      try {
        // configuration is saved to database, write in a compact format
        xmlWriter = new XMLWriter(OutputFormat.createCompactFormat());
      }
      catch (UnsupportedEncodingException e) {
        // WTF?
        throw new JbpmException("failed to create xml writer", e);
      }
    }
    return xmlWriter;
  }

  private String writeElement(Element element) {
    StringWriter stringWriter = new StringWriter();
    XMLWriter xmlWriter = getXmlWriter();
    xmlWriter.setWriter(stringWriter);
    try {
      xmlWriter.write(element);
    }
    catch (IOException e) {
      // cannot happen, writing to memory
      addError("failed to write mail configuration", e);
    }
    return stringWriter.toString();
  }

  public String writeElementContent(Element element) {
    StringWriter stringWriter = new StringWriter();
    XMLWriter xmlWriter = getXmlWriter();
    xmlWriter.setWriter(stringWriter);
    try {
      for (Iterator i = element.content().iterator(); i.hasNext();) {
        xmlWriter.write((org.dom4j.Node) i.next());
      }
    }
    catch (IOException e) {
      // cannot happen, writing to memory
      addError("failed to write mail configuration", e);
    }
    return stringWriter.toString().trim();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy