001: /*
002: * JBoss, Home of Professional Open Source
003: * Copyright 2005, JBoss Inc., and individual contributors as indicated
004: * by the @authors tag. See the copyright.txt in the distribution for a
005: * full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jbpm.jpdl.xml;
023:
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.Reader;
027: import java.util.ArrayList;
028: import java.util.Collection;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.StringTokenizer;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.dom4j.Document;
036: import org.dom4j.Element;
037: import org.jbpm.JbpmConfiguration;
038: import org.jbpm.context.def.VariableAccess;
039: import org.jbpm.graph.action.ActionTypes;
040: import org.jbpm.graph.def.Action;
041: import org.jbpm.graph.def.Event;
042: import org.jbpm.graph.def.ExceptionHandler;
043: import org.jbpm.graph.def.GraphElement;
044: import org.jbpm.graph.def.Node;
045: import org.jbpm.graph.def.NodeCollection;
046: import org.jbpm.graph.def.ProcessDefinition;
047: import org.jbpm.graph.def.Transition;
048: import org.jbpm.graph.node.NodeTypes;
049: import org.jbpm.graph.node.StartState;
050: import org.jbpm.graph.node.TaskNode;
051: import org.jbpm.instantiation.Delegation;
052: import org.jbpm.jpdl.JpdlException;
053: import org.jbpm.mail.Mail;
054: import org.jbpm.scheduler.def.CancelTimerAction;
055: import org.jbpm.scheduler.def.CreateTimerAction;
056: import org.jbpm.taskmgmt.def.Swimlane;
057: import org.jbpm.taskmgmt.def.Task;
058: import org.jbpm.taskmgmt.def.TaskController;
059: import org.jbpm.taskmgmt.def.TaskMgmtDefinition;
060: import org.xml.sax.InputSource;
061:
062: public class JpdlXmlReader implements ProblemListener {
063:
064: private static final long serialVersionUID = 1L;
065:
066: protected InputSource inputSource = null;
067: protected List problems = new ArrayList();
068: protected ProblemListener problemListener = null;
069: protected ProcessDefinition processDefinition = null;
070: protected String initialNodeName = null;
071: protected Collection unresolvedTransitionDestinations = null;
072: protected Collection unresolvedActionReferences = null;
073:
074: public JpdlXmlReader(InputSource inputSource) {
075: this .inputSource = inputSource;
076: }
077:
078: public JpdlXmlReader(InputSource inputSource,
079: ProblemListener problemListener) {
080: this .inputSource = inputSource;
081: this .problemListener = problemListener;
082: }
083:
084: public JpdlXmlReader(Reader reader) {
085: this (new InputSource(reader));
086: }
087:
088: public void close() throws IOException {
089: InputStream byteStream = inputSource.getByteStream();
090: if (byteStream != null)
091: byteStream.close();
092: else {
093: Reader charStream = inputSource.getCharacterStream();
094: if (charStream != null)
095: charStream.close();
096: }
097: }
098:
099: /**
100: * @deprecated Originally, this class extended java.io.Reader. This method
101: * is reminiscent of those days.
102: */
103: public int read(char[] cbuf, int off, int len) throws IOException {
104: return 0;
105: }
106:
107: public ProcessDefinition getProcessDefinition() {
108: return processDefinition;
109: }
110:
111: public void addProblem(Problem problem) {
112: problems.add(problem);
113: if (problemListener != null)
114: problemListener.addProblem(problem);
115: }
116:
117: public void addError(String description) {
118: log.error("invalid process xml: " + description);
119: addProblem(new Problem(Problem.LEVEL_ERROR, description));
120: }
121:
122: public void addError(String description, Throwable exception) {
123: log.error("invalid process xml: " + description, exception);
124: addProblem(new Problem(Problem.LEVEL_ERROR, description,
125: exception));
126: }
127:
128: public void addWarning(String description) {
129: log.warn("process xml warning: " + description);
130: addProblem(new Problem(Problem.LEVEL_WARNING, description));
131: }
132:
133: public ProcessDefinition readProcessDefinition() {
134: // create a new definition
135: processDefinition = ProcessDefinition
136: .createNewProcessDefinition();
137:
138: // initialize lists
139: problems = new ArrayList();
140: unresolvedTransitionDestinations = new ArrayList();
141: unresolvedActionReferences = new ArrayList();
142:
143: try {
144: // parse the document into a dom tree
145: Document document = JpdlParser.parse(inputSource, this );
146: Element root = document.getRootElement();
147:
148: // read the process name
149: parseProcessDefinitionAttributes(root);
150:
151: // get the process description
152: String description = root.elementTextTrim("description");
153: if (description != null) {
154: processDefinition.setDescription(description);
155: }
156:
157: // first pass: read most content
158: readSwimlanes(root);
159: readActions(root, null, null);
160: readNodes(root, processDefinition);
161: readEvents(root, processDefinition);
162: readExceptionHandlers(root, processDefinition);
163: readTasks(root, null);
164:
165: // second pass processing
166: resolveTransitionDestinations();
167: resolveActionReferences();
168: verifySwimlaneAssignments();
169:
170: } catch (Exception e) {
171: log.error("couldn't parse process definition", e);
172: addProblem(new Problem(Problem.LEVEL_ERROR,
173: "couldn't parse process definition", e));
174: }
175:
176: if (Problem.containsProblemsOfLevel(problems,
177: Problem.LEVEL_ERROR)) {
178: throw new JpdlException(problems);
179: }
180:
181: if (problems != null) {
182: Iterator iter = problems.iterator();
183: while (iter.hasNext()) {
184: Problem problem = (Problem) iter.next();
185: log.warn("process parse warning: "
186: + problem.getDescription());
187: }
188: }
189:
190: return processDefinition;
191: }
192:
193: protected void parseProcessDefinitionAttributes(Element root) {
194: processDefinition.setName(root.attributeValue("name"));
195: initialNodeName = root.attributeValue("initial");
196: }
197:
198: protected void readSwimlanes(Element processDefinitionElement) {
199: Iterator iter = processDefinitionElement
200: .elementIterator("swimlane");
201: TaskMgmtDefinition taskMgmtDefinition = processDefinition
202: .getTaskMgmtDefinition();
203: while (iter.hasNext()) {
204: Element swimlaneElement = (Element) iter.next();
205: String swimlaneName = swimlaneElement
206: .attributeValue("name");
207: if (swimlaneName == null) {
208: addWarning("there's a swimlane without a name");
209: } else {
210: Swimlane swimlane = new Swimlane(swimlaneName);
211: Element assignmentElement = swimlaneElement
212: .element("assignment");
213:
214: if (assignmentElement != null) {
215:
216: if ((assignmentElement.attribute("actor-id") != null)
217: || (assignmentElement
218: .attribute("pooled-actors") != null)) {
219: swimlane.setActorIdExpression(assignmentElement
220: .attributeValue("actor-id"));
221: swimlane
222: .setPooledActorsExpression(assignmentElement
223: .attributeValue("pooled-actors"));
224:
225: } else {
226: Delegation assignmentDelegation = readAssignmentDelegation(assignmentElement);
227: swimlane
228: .setAssignmentDelegation(assignmentDelegation);
229: }
230: } else {
231: Task startTask = taskMgmtDefinition.getStartTask();
232: if ((startTask == null)
233: || (startTask.getSwimlane() != swimlane)) {
234: addWarning("swimlane '" + swimlaneName
235: + "' does not have an assignment");
236: }
237: }
238: taskMgmtDefinition.addSwimlane(swimlane);
239: }
240: }
241: }
242:
243: public void readNodes(Element element, NodeCollection nodeCollection) {
244: Iterator nodeElementIter = element.elementIterator();
245: while (nodeElementIter.hasNext()) {
246: Element nodeElement = (Element) nodeElementIter.next();
247: String nodeName = nodeElement.getName();
248: // get the node type
249: Class nodeType = NodeTypes.getNodeType(nodeName);
250: if (nodeType != null) {
251:
252: Node node = null;
253: try {
254: // create a new instance
255: node = (Node) nodeType.newInstance();
256: } catch (Exception e) {
257: log.error(
258: "couldn't instantiate node '" + nodeName
259: + "', of type '"
260: + nodeType.getName() + "'", e);
261: }
262:
263: node.setProcessDefinition(processDefinition);
264:
265: // check for duplicate start-states
266: if ((node instanceof StartState)
267: && (processDefinition.getStartState() != null)) {
268: addError("max one start-state allowed in a process");
269:
270: } else {
271: // read the common node parts of the element
272: readNode(nodeElement, node, nodeCollection);
273:
274: // if the node is parsable
275: // (meaning: if the node has special configuration to parse, other then the
276: // common node data)
277: node.read(nodeElement, this );
278: }
279: }
280: }
281: }
282:
283: public void readTasks(Element element, TaskNode taskNode) {
284: List elements = element.elements("task");
285: TaskMgmtDefinition tmd = (TaskMgmtDefinition) processDefinition
286: .getDefinition(TaskMgmtDefinition.class);
287: if (elements.size() > 0) {
288: if (tmd == null) {
289: tmd = new TaskMgmtDefinition();
290: }
291: processDefinition.addDefinition(tmd);
292:
293: Iterator iter = elements.iterator();
294: while (iter.hasNext()) {
295: Element taskElement = (Element) iter.next();
296: readTask(taskElement, tmd, taskNode);
297: }
298: }
299: }
300:
301: public Task readTask(Element taskElement,
302: TaskMgmtDefinition taskMgmtDefinition, TaskNode taskNode) {
303: Task task = new Task();
304: task.setProcessDefinition(processDefinition);
305:
306: // get the task name
307: String name = taskElement.attributeValue("name");
308: if (name != null) {
309: task.setName(name);
310: taskMgmtDefinition.addTask(task);
311: } else if (taskNode != null) {
312: task.setName(taskNode.getName());
313: taskMgmtDefinition.addTask(task);
314: }
315:
316: // get the task description
317: String description = taskElement.elementTextTrim("description");
318: if (description != null) {
319: task.setDescription(description);
320: } else {
321: task.setDescription(taskElement
322: .attributeValue("description"));
323: }
324:
325: // get the condition
326: String condition = taskElement.elementTextTrim("condition");
327: if (condition != null) {
328: task.setCondition(condition);
329: } else {
330: task.setCondition(taskElement.attributeValue("condition"));
331: }
332:
333: // parse common subelements
334: readTaskTimers(taskElement, task);
335: readEvents(taskElement, task);
336: readExceptionHandlers(taskElement, task);
337:
338: // duedate and priority
339: String duedateText = taskElement.attributeValue("duedate");
340: if (duedateText == null) {
341: duedateText = taskElement.attributeValue("dueDate");
342: }
343: task.setDueDate(duedateText);
344: String priorityText = taskElement.attributeValue("priority");
345: if (priorityText != null) {
346: task.setPriority(Task.parsePriority(priorityText));
347: }
348:
349: // if this task is in the context of a taskNode, associate them
350: if (taskNode != null) {
351: taskNode.addTask(task);
352: }
353:
354: // blocking
355: String blockingText = taskElement.attributeValue("blocking");
356: if (blockingText != null) {
357: if (("true".equalsIgnoreCase(blockingText))
358: || ("yes".equalsIgnoreCase(blockingText))
359: || ("on".equalsIgnoreCase(blockingText))) {
360: task.setBlocking(true);
361: }
362: }
363:
364: // signalling
365: String signallingText = taskElement
366: .attributeValue("signalling");
367: if (signallingText != null) {
368: if (("false".equalsIgnoreCase(signallingText))
369: || ("no".equalsIgnoreCase(signallingText))
370: || ("off".equalsIgnoreCase(signallingText))) {
371: task.setSignalling(false);
372: }
373: }
374:
375: // assignment
376: String swimlaneName = taskElement.attributeValue("swimlane");
377: Element assignmentElement = taskElement.element("assignment");
378:
379: // if there is a swimlane attribute specified
380: if (swimlaneName != null) {
381: Swimlane swimlane = taskMgmtDefinition
382: .getSwimlane(swimlaneName);
383: if (swimlane == null) {
384: addWarning("task references unknown swimlane '"
385: + swimlaneName + "':" + taskElement.asXML());
386: } else {
387: task.setSwimlane(swimlane);
388: }
389:
390: // else if there is a direct assignment specified
391: } else if (assignmentElement != null) {
392: if ((assignmentElement.attribute("actor-id") != null)
393: || (assignmentElement.attribute("pooled-actors") != null)) {
394: task.setActorIdExpression(assignmentElement
395: .attributeValue("actor-id"));
396: task.setPooledActorsExpression(assignmentElement
397: .attributeValue("pooled-actors"));
398:
399: } else {
400: Delegation assignmentDelegation = readAssignmentDelegation(assignmentElement);
401: task.setAssignmentDelegation(assignmentDelegation);
402: }
403:
404: // if no assignment or swimlane is specified
405: } else {
406: // the user has to manage assignment manually, so we better warn him/her.
407: addWarning("warning: no swimlane or assignment specified for task '"
408: + taskElement.asXML() + "'");
409: }
410:
411: // notify
412: String notificationsText = taskElement.attributeValue("notify");
413: if (notificationsText != null
414: && ("true".equalsIgnoreCase(notificationsText)
415: || "on".equalsIgnoreCase(notificationsText) || "yes"
416: .equalsIgnoreCase(notificationsText))) {
417: String notificationEvent = Event.EVENTTYPE_TASK_ASSIGN;
418: Event event = task.getEvent(notificationEvent);
419: if (event == null) {
420: event = new Event(notificationEvent);
421: task.addEvent(event);
422: }
423: Delegation delegation = createMailDelegation(
424: notificationEvent, null, null, null, null);
425: Action action = new Action(delegation);
426: action.setProcessDefinition(processDefinition);
427: action.setName(task.getName());
428: event.addAction(action);
429: }
430:
431: // task controller
432: Element taskControllerElement = taskElement
433: .element("controller");
434: if (taskControllerElement != null) {
435: task
436: .setTaskController(readTaskController(taskControllerElement));
437: }
438:
439: return task;
440: }
441:
442: protected Delegation readAssignmentDelegation(
443: Element assignmentElement) {
444: Delegation assignmentDelegation = new Delegation();
445: String expression = assignmentElement
446: .attributeValue("expression");
447: String actorId = assignmentElement.attributeValue("actor-id");
448: String pooledActors = assignmentElement
449: .attributeValue("pooled-actors");
450:
451: if (expression != null) {
452: assignmentDelegation
453: .setProcessDefinition(processDefinition);
454: assignmentDelegation
455: .setClassName("org.jbpm.identity.assignment.ExpressionAssignmentHandler");
456: assignmentDelegation.setConfiguration("<expression>"
457: + expression + "</expression>");
458:
459: } else if ((actorId != null) || (pooledActors != null)) {
460: assignmentDelegation
461: .setProcessDefinition(processDefinition);
462: assignmentDelegation
463: .setClassName("org.jbpm.taskmgmt.assignment.ActorAssignmentHandler");
464: String configuration = "";
465: if (actorId != null) {
466: configuration += "<actorId>" + actorId + "</actorId>";
467: }
468: if (pooledActors != null) {
469: configuration += "<pooledActors>" + pooledActors
470: + "</pooledActors>";
471: }
472: assignmentDelegation.setConfiguration(configuration);
473:
474: } else {
475: assignmentDelegation = new Delegation();
476: assignmentDelegation.read(assignmentElement, this );
477: }
478:
479: return assignmentDelegation;
480: }
481:
482: protected TaskController readTaskController(
483: Element taskControllerElement) {
484: TaskController taskController = new TaskController();
485:
486: if (taskControllerElement.attributeValue("class") != null) {
487: Delegation taskControllerDelegation = new Delegation();
488: taskControllerDelegation.read(taskControllerElement, this );
489: taskController
490: .setTaskControllerDelegation(taskControllerDelegation);
491:
492: } else {
493: List variableAccesses = readVariableAccesses(taskControllerElement);
494: taskController.setVariableAccesses(variableAccesses);
495: }
496: return taskController;
497: }
498:
499: public List readVariableAccesses(Element element) {
500: List variableAccesses = new ArrayList();
501: Iterator iter = element.elementIterator("variable");
502: while (iter.hasNext()) {
503: Element variableElement = (Element) iter.next();
504:
505: String variableName = variableElement
506: .attributeValue("name");
507: if (variableName == null) {
508: addProblem(new Problem(Problem.LEVEL_WARNING,
509: "the name attribute of a variable element is required: "
510: + variableElement.asXML()));
511: }
512: String access = variableElement.attributeValue("access",
513: "read,write");
514: String mappedName = variableElement
515: .attributeValue("mapped-name");
516:
517: variableAccesses.add(new VariableAccess(variableName,
518: access, mappedName));
519: }
520: return variableAccesses;
521: }
522:
523: public void readStartStateTask(Element startTaskElement,
524: StartState startState) {
525: TaskMgmtDefinition taskMgmtDefinition = processDefinition
526: .getTaskMgmtDefinition();
527: Task startTask = readTask(startTaskElement, taskMgmtDefinition,
528: null);
529: startTask.setStartState(startState);
530: if (startTask.getName() == null) {
531: startTask.setName(startState.getName());
532: }
533: taskMgmtDefinition.setStartTask(startTask);
534: }
535:
536: public void readNode(Element nodeElement, Node node,
537: NodeCollection nodeCollection) {
538:
539: // first put the node in its collection. this is done so that the
540: // setName later on will be able to differentiate between nodes contained in
541: // processDefinitions and nodes contained in superstates
542: nodeCollection.addNode(node);
543:
544: // get the node name
545: String name = nodeElement.attributeValue("name");
546: if (name != null) {
547: node.setName(name);
548:
549: // check if this is the initial node
550: if ((initialNodeName != null)
551: && (initialNodeName.equals(node
552: .getFullyQualifiedName()))) {
553: processDefinition.setStartState(node);
554: }
555: }
556:
557: // get the node description
558: String description = nodeElement.elementTextTrim("description");
559: if (description != null) {
560: node.setDescription(description);
561: }
562:
563: String asyncText = nodeElement.attributeValue("async");
564: if ("true".equalsIgnoreCase(asyncText)) {
565: node.setAsync(true);
566: } else if ("exclusive".equalsIgnoreCase(asyncText)) {
567: node.setAsync(true);
568: node.setAsyncExclusive(true);
569: }
570:
571: // parse common subelements
572: readNodeTimers(nodeElement, node);
573: readEvents(nodeElement, node);
574: readExceptionHandlers(nodeElement, node);
575:
576: // save the transitions and parse them at the end
577: addUnresolvedTransitionDestination(nodeElement, node);
578: }
579:
580: protected void readNodeTimers(Element nodeElement, Node node) {
581: Iterator iter = nodeElement.elementIterator("timer");
582: while (iter.hasNext()) {
583: Element timerElement = (Element) iter.next();
584: readNodeTimer(timerElement, node);
585: }
586: }
587:
588: protected void readNodeTimer(Element timerElement, Node node) {
589: String name = timerElement.attributeValue("name", node
590: .getName());
591:
592: CreateTimerAction createTimerAction = new CreateTimerAction();
593: createTimerAction.read(timerElement, this );
594: createTimerAction.setTimerName(name);
595: createTimerAction
596: .setTimerAction(readSingleAction(timerElement));
597: addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);
598:
599: CancelTimerAction cancelTimerAction = new CancelTimerAction();
600: cancelTimerAction.setTimerName(name);
601: addAction(node, Event.EVENTTYPE_NODE_LEAVE, cancelTimerAction);
602: }
603:
604: protected void readTaskTimers(Element taskElement, Task task) {
605: Iterator iter = taskElement.elementIterator();
606: while (iter.hasNext()) {
607: Element element = (Element) iter.next();
608: if (("timer".equals(element.getName()))
609: || ("reminder".equals(element.getName()))) {
610: readTaskTimer(element, task);
611: }
612: }
613: }
614:
615: protected void readTaskTimer(Element timerElement, Task task) {
616: String name = timerElement.attributeValue("name", task
617: .getName());
618: if (name == null)
619: name = "timer-for-task-" + task.getId();
620:
621: CreateTimerAction createTimerAction = new CreateTimerAction();
622: createTimerAction.read(timerElement, this );
623: createTimerAction.setTimerName(name);
624: Action action = null;
625: if ("timer".equals(timerElement.getName())) {
626: action = readSingleAction(timerElement);
627: } else {
628: Delegation delegation = createMailDelegation(
629: "task-reminder", null, null, null, null);
630: action = new Action(delegation);
631: }
632: createTimerAction.setTimerAction(action);
633: addAction(task, Event.EVENTTYPE_TASK_CREATE, createTimerAction);
634:
635: // read the cancel-event types
636: Collection cancelEventTypes = new ArrayList();
637:
638: String cancelEventTypeText = timerElement
639: .attributeValue("cancel-event");
640: if (cancelEventTypeText != null) {
641: // cancel-event is a comma separated list of events
642: StringTokenizer tokenizer = new StringTokenizer(
643: cancelEventTypeText, ",");
644: while (tokenizer.hasMoreTokens()) {
645: cancelEventTypes.add(tokenizer.nextToken().trim());
646: }
647: } else {
648: // set the default
649: cancelEventTypes.add(Event.EVENTTYPE_TASK_END);
650: }
651:
652: Iterator iter = cancelEventTypes.iterator();
653: while (iter.hasNext()) {
654: String cancelEventType = (String) iter.next();
655: CancelTimerAction cancelTimerAction = new CancelTimerAction();
656: cancelTimerAction.setTimerName(name);
657: addAction(task, cancelEventType, cancelTimerAction);
658: }
659: }
660:
661: protected void readEvents(Element parentElement,
662: GraphElement graphElement) {
663: Iterator iter = parentElement.elementIterator("event");
664: while (iter.hasNext()) {
665: Element eventElement = (Element) iter.next();
666: String eventType = eventElement.attributeValue("type");
667: if (!graphElement.hasEvent(eventType)) {
668: graphElement.addEvent(new Event(eventType));
669: }
670: readActions(eventElement, graphElement, eventType);
671: }
672: }
673:
674: public void readActions(Element eventElement,
675: GraphElement graphElement, String eventType) {
676: // for all the elements in the event element
677: Iterator nodeElementIter = eventElement.elementIterator();
678: while (nodeElementIter.hasNext()) {
679: Element actionElement = (Element) nodeElementIter.next();
680: String actionName = actionElement.getName();
681: if (ActionTypes.hasActionName(actionName)) {
682: Action action = createAction(actionElement);
683: if ((graphElement != null) && (eventType != null)) {
684: // add the action to the event
685: addAction(graphElement, eventType, action);
686: }
687: }
688: }
689: }
690:
691: protected void addAction(GraphElement graphElement,
692: String eventType, Action action) {
693: Event event = graphElement.getEvent(eventType);
694: if (event == null) {
695: event = new Event(eventType);
696: graphElement.addEvent(event);
697: }
698: event.addAction(action);
699: }
700:
701: public Action readSingleAction(Element nodeElement) {
702: Action action = null;
703: // search for the first action element in the node
704: Iterator iter = nodeElement.elementIterator();
705: while (iter.hasNext() && (action == null)) {
706: Element candidate = (Element) iter.next();
707: if (ActionTypes.hasActionName(candidate.getName())) {
708: // parse the action and assign it to this node
709: action = createAction(candidate);
710: }
711: }
712: return action;
713: }
714:
715: public Action createAction(Element actionElement) {
716: // create a new instance of the action
717: Action action = null;
718: String actionName = actionElement.getName();
719: Class actionType = ActionTypes.getActionType(actionName);
720: try {
721: action = (Action) actionType.newInstance();
722: } catch (Exception e) {
723: log.error("couldn't instantiate action '" + actionName
724: + "', of type '" + actionType.getName() + "'", e);
725: }
726:
727: // read the common node parts of the action
728: readAction(actionElement, action);
729:
730: return action;
731: }
732:
733: public void readAction(Element element, Action action) {
734: // if a name is specified for this action
735: String actionName = element.attributeValue("name");
736: if (actionName != null) {
737: action.setName(actionName);
738: // add the action to the named process action repository
739: processDefinition.addAction(action);
740: }
741:
742: // if the action is parsable
743: // (meaning: if the action has special configuration to parse, other then the common node data)
744: action.read(element, this );
745: }
746:
747: protected void readExceptionHandlers(Element graphElementElement,
748: GraphElement graphElement) {
749: Iterator iter = graphElementElement
750: .elementIterator("exception-handler");
751: while (iter.hasNext()) {
752: Element exceptionHandlerElement = (Element) iter.next();
753: readExceptionHandler(exceptionHandlerElement, graphElement);
754: }
755: }
756:
757: protected void readExceptionHandler(
758: Element exceptionHandlerElement, GraphElement graphElement) {
759: // create the exception handler
760: ExceptionHandler exceptionHandler = new ExceptionHandler();
761: exceptionHandler.setExceptionClassName(exceptionHandlerElement
762: .attributeValue("exception-class"));
763: // add it to the graph element
764: graphElement.addExceptionHandler(exceptionHandler);
765:
766: // read the actions in the body of the exception-handler element
767: Iterator iter = exceptionHandlerElement.elementIterator();
768: while (iter.hasNext()) {
769: Element childElement = (Element) iter.next();
770: if (ActionTypes.hasActionName(childElement.getName())) {
771: Action action = createAction(childElement);
772: exceptionHandler.addAction(action);
773: }
774: }
775: }
776:
777: // transition destinations are parsed in a second pass //////////////////////
778:
779: public void addUnresolvedTransitionDestination(Element nodeElement,
780: Node node) {
781: unresolvedTransitionDestinations.add(new Object[] {
782: nodeElement, node });
783: }
784:
785: public void resolveTransitionDestinations() {
786: Iterator iter = unresolvedTransitionDestinations.iterator();
787: while (iter.hasNext()) {
788: Object[] unresolvedTransition = (Object[]) iter.next();
789: Element nodeElement = (Element) unresolvedTransition[0];
790: Node node = (Node) unresolvedTransition[1];
791: resolveTransitionDestinations(nodeElement
792: .elements("transition"), node);
793: }
794: }
795:
796: public void resolveTransitionDestinations(List transitionElements,
797: Node node) {
798: Iterator iter = transitionElements.iterator();
799: while (iter.hasNext()) {
800: Element transitionElement = (Element) iter.next();
801: resolveTransitionDestination(transitionElement, node);
802: }
803: }
804:
805: public void resolveTransitionDestination(Element transitionElement,
806: Node node) {
807: Transition transition = new Transition();
808: transition.setProcessDefinition(processDefinition);
809:
810: transition.setName(transitionElement.attributeValue("name"));
811: transition.setDescription(transitionElement
812: .elementTextTrim("description"));
813:
814: String condition = transitionElement
815: .attributeValue("condition");
816: if (condition == null) {
817: Element conditionElement = transitionElement
818: .element("condition");
819: if (conditionElement != null) {
820: condition = conditionElement.getTextTrim();
821: // for backwards compatibility
822: if ((condition == null) || (condition.length() == 0)) {
823: condition = conditionElement
824: .attributeValue("expression");
825: }
826: }
827: }
828: transition.setCondition(condition);
829:
830: // add the transition to the node
831: node.addLeavingTransition(transition);
832:
833: // set destinationNode of the transition
834: String toName = transitionElement.attributeValue("to");
835: if (toName == null) {
836: addWarning("node '"
837: + node.getFullyQualifiedName()
838: + "' has a transition without a 'to'-attribute to specify its destinationNode");
839: } else {
840: Node to = ((NodeCollection) node.getParent())
841: .findNode(toName);
842: if (to == null) {
843: addWarning("transition to='" + toName + "' on node '"
844: + node.getFullyQualifiedName()
845: + "' cannot be resolved");
846: } else {
847: to.addArrivingTransition(transition);
848: }
849: }
850:
851: // read the actions
852: readActions(transitionElement, transition,
853: Event.EVENTTYPE_TRANSITION);
854:
855: readExceptionHandlers(transitionElement, transition);
856: }
857:
858: // action references are parsed in a second pass ////////////////////////////
859:
860: public void addUnresolvedActionReference(Element actionElement,
861: Action action) {
862: unresolvedActionReferences.add(new Object[] { actionElement,
863: action });
864: }
865:
866: public void resolveActionReferences() {
867: Iterator iter = unresolvedActionReferences.iterator();
868: while (iter.hasNext()) {
869: Object[] unresolvedActionReference = (Object[]) iter.next();
870: Element actionElement = (Element) unresolvedActionReference[0];
871: Action action = (Action) unresolvedActionReference[1];
872: String referencedActionName = actionElement
873: .attributeValue("ref-name");
874: Action referencedAction = processDefinition
875: .getAction(referencedActionName);
876: if (referencedAction == null) {
877: addWarning("couldn't resolve action reference in "
878: + actionElement.asXML());
879: }
880: action.setReferencedAction(referencedAction);
881: }
882: }
883:
884: // verify swimlane assignments in second pass ///////////////////////////////
885: public void verifySwimlaneAssignments() {
886: TaskMgmtDefinition taskMgmtDefinition = processDefinition
887: .getTaskMgmtDefinition();
888: if ((taskMgmtDefinition != null)
889: && (taskMgmtDefinition.getSwimlanes() != null)) {
890: Iterator iter = taskMgmtDefinition.getSwimlanes().values()
891: .iterator();
892: while (iter.hasNext()) {
893: Swimlane swimlane = (Swimlane) iter.next();
894:
895: Task startTask = taskMgmtDefinition.getStartTask();
896: Swimlane startTaskSwimlane = (startTask != null ? startTask
897: .getSwimlane()
898: : null);
899:
900: if ((swimlane.getAssignmentDelegation() == null)
901: && (swimlane != startTaskSwimlane)) {
902: addWarning("swimlane '" + swimlane.getName()
903: + "' does not have an assignment");
904: }
905: }
906: }
907: }
908:
909: // mail delegations /////////////////////////////////////////////////////////
910:
911: public Delegation createMailDelegation(String template,
912: String actors, String to, String subject, String text) {
913: StringBuffer config = new StringBuffer();
914: if (template != null) {
915: config.append("<template>");
916: config.append(template);
917: config.append("</template>");
918: }
919: if (actors != null) {
920: config.append("<actors>");
921: config.append(actors);
922: config.append("</actors>");
923: }
924: if (to != null) {
925: config.append("<to>");
926: config.append(to);
927: config.append("</to>");
928: }
929: if (subject != null) {
930: config.append("<subject>");
931: config.append(subject);
932: config.append("</subject>");
933: }
934: if (text != null) {
935: config.append("<text>");
936: config.append(text);
937: config.append("</text>");
938: }
939:
940: String mailClassName = Mail.class.getName();
941: if (JbpmConfiguration.Configs.hasObject("jbpm.mail.class.name")) {
942: mailClassName = JbpmConfiguration.Configs
943: .getString("jbpm.mail.class.name");
944: } else if (JbpmConfiguration.Configs
945: .hasObject("mail.class.name")) {
946: mailClassName = JbpmConfiguration.Configs
947: .getString("mail.class.name");
948: }
949:
950: Delegation delegation = new Delegation(mailClassName);
951: delegation.setProcessDefinition(processDefinition);
952: delegation.setConfiguration(config.toString());
953: return delegation;
954: }
955:
956: public String getProperty(String property, Element element) {
957: String value = element.attributeValue(property);
958: if (value == null) {
959: Element propertyElement = element.element(property);
960: if (propertyElement != null) {
961: value = propertyElement.getText();
962: }
963: }
964: return value;
965: }
966:
967: private static final Log log = LogFactory
968: .getLog(JpdlXmlReader.class);
969: }
|