/*
* 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);
}