KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jbpm > jpdl > xml > JpdlXmlReader


1 package org.jbpm.jpdl.xml;
2
3 import java.io.IOException JavaDoc;
4 import java.io.Reader JavaDoc;
5 import java.util.ArrayList JavaDoc;
6 import java.util.Collection JavaDoc;
7 import java.util.Iterator JavaDoc;
8 import java.util.List JavaDoc;
9 import java.util.StringTokenizer JavaDoc;
10
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.dom4j.Document;
14 import org.dom4j.Element;
15 import org.jbpm.context.def.VariableAccess;
16 import org.jbpm.db.JbpmSession;
17 import org.jbpm.graph.action.ActionTypes;
18 import org.jbpm.graph.def.Action;
19 import org.jbpm.graph.def.Event;
20 import org.jbpm.graph.def.ExceptionHandler;
21 import org.jbpm.graph.def.GraphElement;
22 import org.jbpm.graph.def.Node;
23 import org.jbpm.graph.def.NodeCollection;
24 import org.jbpm.graph.def.ProcessDefinition;
25 import org.jbpm.graph.def.Transition;
26 import org.jbpm.graph.node.NodeTypes;
27 import org.jbpm.graph.node.StartState;
28 import org.jbpm.graph.node.TaskNode;
29 import org.jbpm.instantiation.Delegation;
30 import org.jbpm.jpdl.JpdlException;
31 import org.jbpm.scheduler.def.CancelTimerAction;
32 import org.jbpm.scheduler.def.CreateTimerAction;
33 import org.jbpm.taskmgmt.def.Swimlane;
34 import org.jbpm.taskmgmt.def.Task;
35 import org.jbpm.taskmgmt.def.TaskController;
36 import org.jbpm.taskmgmt.def.TaskMgmtDefinition;
37
38
39 public class JpdlXmlReader {
40   
41   Reader JavaDoc reader = null;
42   List JavaDoc problems = new ArrayList JavaDoc();
43   JbpmSession jbpmSession = null;
44   ProcessDefinition processDefinition = null;
45   Collection JavaDoc unresolvedTransitionDestinations = null;
46   Collection JavaDoc unresolvedActionReferences = null;
47   
48   private static final String JavaDoc JPDL_SCHEMA_NAME = "jpdl-3.0.xsd";
49   
50   public JpdlXmlReader(Reader JavaDoc reader) {
51     this.reader = reader;
52   }
53
54   public JpdlXmlReader(Reader JavaDoc reader, JbpmSession jbpmSession) {
55     this.reader = reader;
56     this.jbpmSession = jbpmSession;
57   }
58
59   public JbpmSession getJbpmSession() {
60     return jbpmSession;
61   }
62   
63   public void close() throws IOException JavaDoc {
64     reader.close();
65   }
66
67   public int read(char[] cbuf, int off, int len) throws IOException JavaDoc {
68     return reader.read(cbuf, off, len);
69   }
70
71   public ProcessDefinition getProcessDefinition() {
72     return processDefinition;
73   }
74
75   public void addProblem(Problem p) {
76     problems.add(p);
77   }
78   
79   public void addError(String JavaDoc description) {
80     log.error("invalid process xml: "+description);
81     addProblem(new Problem(Problem.LEVEL_ERROR, description));
82   }
83
84   public void addError(String JavaDoc description, Throwable JavaDoc exception) {
85     log.error("invalid process xml: "+description, exception);
86     addProblem(new Problem(Problem.LEVEL_ERROR, description, exception));
87   }
88
89   public void addWarning(String JavaDoc description) {
90     log.warn("process xml warning: "+description);
91     addProblem(new Problem(Problem.LEVEL_WARNING, description));
92   }
93
94   public ProcessDefinition readProcessDefinition() {
95     // create a new definition
96
processDefinition = ProcessDefinition.createNewProcessDefinition();
97
98     // initialize lists
99
problems = new ArrayList JavaDoc();
100     unresolvedTransitionDestinations = new ArrayList JavaDoc();
101     unresolvedActionReferences = new ArrayList JavaDoc();
102         
103     // parse the document into a dom tree
104
Document document = null;
105
106     // validate the document using the process definition schema
107
SchemaValidationHelper helper = new SchemaValidationHelper(reader, JPDL_SCHEMA_NAME, "process definition");
108
109     if (helper.isValid()) {
110       document = helper.getDocument();
111
112     } else { // schema validation problems were encountered
113
for (Iterator JavaDoc i = helper.getProblems().iterator(); i.hasNext();) {
114         Problem problem = (Problem) i.next();
115         this.addProblem(problem);
116         log.debug(problem.getDescription());
117       }
118
119       throw new JpdlException(problems);
120     }
121       
122     Element root = document.getRootElement();
123             
124     // read the process name
125
processDefinition.setName(root.attributeValue("name"));
126
127     // first pass: read most content
128
readSwimlanes(root);
129     readActions(root, null, null);
130     readNodes(root, processDefinition);
131     readEvents(root, processDefinition);
132     readExceptionHandlers(root, processDefinition);
133     readTasks(root, null);
134
135     // second pass processing
136
resolveTransitionDestinations();
137     resolveActionReferences();
138     verifySwimlaneAssignments();
139     
140     if (Problem.containsProblemsOfLevel(problems, Problem.LEVEL_ERROR)) {
141       throw new JpdlException(problems);
142     }
143
144     return processDefinition;
145   }
146   
147   private void readSwimlanes(Element processDefinitionElement) {
148     Iterator JavaDoc iter = processDefinitionElement.elementIterator("swimlane");
149     TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
150     while (iter.hasNext()) {
151       Element swimlaneElement = (Element) iter.next();
152       String JavaDoc swimlaneName = swimlaneElement.attributeValue("name");
153       if (swimlaneName==null) {
154         addWarning("there's a swimlane without a name");
155       } else {
156         Swimlane swimlane = new Swimlane(swimlaneName);
157         Element assignmentElement = swimlaneElement.element("assignment");
158
159         if (assignmentElement!=null) {
160           Delegation assignmentDelegation = readAssignmentDelegation(assignmentElement);
161           swimlane.setAssignmentDelegation(assignmentDelegation);
162           
163         } else {
164           Task startTask = taskMgmtDefinition.getStartTask();
165           if ( (startTask==null)
166                || (startTask.getSwimlane()!=swimlane)
167              ) {
168             addWarning("swimlane '"+swimlaneName+"' does not have an assignment");
169           }
170         }
171         taskMgmtDefinition.addSwimlane(swimlane);
172       }
173     }
174   }
175
176   public void readNodes(Element element, NodeCollection nodeCollection) {
177     Iterator JavaDoc nodeElementIter = element.elementIterator();
178     while (nodeElementIter.hasNext()) {
179       Element nodeElement = (Element) nodeElementIter.next();
180       String JavaDoc nodeName = nodeElement.getName();
181       // get the node type
182
Class JavaDoc nodeType = NodeTypes.getNodeType(nodeName);
183       if (nodeType!=null) {
184         try {
185           // create a new instance
186
Node node = (Node) nodeType.newInstance();
187           node.setProcessDefinition(processDefinition);
188           // read the common node parts of the element
189
readNode(nodeElement, node, nodeCollection);
190         
191           // if the node is parsable
192
// (meaning: if the node has special configuration to parse, other then the
193
// common node data)
194
node.read(nodeElement, this);
195
196         } catch (Exception JavaDoc e) {
197           log.error("couldn't instantiate node '"+nodeName+"', of type '"+nodeType.getName()+"'", e);
198         }
199       }
200     }
201   }
202
203   public void readTasks(Element element, TaskNode taskNode) {
204     List JavaDoc elements = element.elements("task");
205     TaskMgmtDefinition tmd = (TaskMgmtDefinition) processDefinition.getDefinition(TaskMgmtDefinition.class);
206     if (elements.size()>0) {
207       if (tmd==null) {
208         tmd = new TaskMgmtDefinition();
209       }
210       processDefinition.addDefinition(tmd);
211       
212       Iterator JavaDoc iter = elements.iterator();
213       while (iter.hasNext()) {
214         Element taskElement = (Element) iter.next();
215         readTask(taskElement, tmd, taskNode);
216       }
217     }
218   }
219
220   public Task readTask(Element taskElement, TaskMgmtDefinition taskMgmtDefinition, TaskNode taskNode) {
221     Task task = new Task();
222     task.setProcessDefinition(processDefinition);
223     
224     // get the task name
225
String JavaDoc name = taskElement.attributeValue("name");
226     if (name!=null) {
227       task.setName(name);
228       taskMgmtDefinition.addTask(task);
229     } else if (taskNode!=null) {
230       task.setName(taskNode.getName());
231       taskMgmtDefinition.addTask(task);
232     }
233     
234     // parse common subelements
235
readTaskTimers(taskElement, task);
236     readEvents(taskElement, task);
237     readExceptionHandlers(taskElement, task);
238
239     // description and duration
240
task.setDescription(taskElement.attributeValue("description"));
241     task.setDueDate(taskElement.attributeValue("dueDate"));
242     String JavaDoc priorityText = taskElement.attributeValue("priority");
243     if (priorityText!=null) {
244       task.setPriority(Task.parsePriority(priorityText));
245     }
246     
247     // if this task is in the context of a taskNode, associate them
248
if (taskNode!=null) {
249       taskNode.addTask(task);
250     }
251
252     // blocking
253
String JavaDoc blockingText = taskElement.attributeValue("blocking");
254     if (blockingText!=null) {
255       if ( ("true".equalsIgnoreCase(blockingText))
256            || ("yes".equalsIgnoreCase(blockingText))
257            || ("on".equalsIgnoreCase(blockingText)) ) {
258         task.setBlocking(true);
259       }
260     }
261     
262     // assignment
263
String JavaDoc swimlaneName = taskElement.attributeValue("swimlane");
264     Element assignmentElement = taskElement.element("assignment");
265
266     // if there is a swimlane attribute specified
267
if (swimlaneName!=null) {
268       Swimlane swimlane = taskMgmtDefinition.getSwimlane(swimlaneName);
269       if (swimlane==null) {
270         addWarning("task references unknown swimlane '"+swimlaneName+"':"+taskElement.asXML());
271       } else {
272         task.setSwimlane(swimlane);
273       }
274
275     // else if there is a direct assignment specified
276
} else if (assignmentElement!=null) {
277       Delegation assignmentDelegation = readAssignmentDelegation(assignmentElement);
278       task.setAssignmentDelegation(assignmentDelegation);
279
280     // if no assignment or swimlane is specified
281
} else {
282       // the user has to manage assignment manually, so we better warn him/her.
283
addWarning("warning: no swimlane or assignment specified for task '"+taskElement.asXML()+"'");
284     }
285     
286     // task controller
287
Element taskControllerElement = taskElement.element("controller");
288     if (taskControllerElement!=null) {
289       task.setTaskController(readTaskController(taskControllerElement));
290     }
291     
292     return task;
293   }
294
295   private Delegation readAssignmentDelegation(Element assignmentElement) {
296     Delegation assignmentDelegation = new Delegation();
297     String JavaDoc expression = assignmentElement.attributeValue("expression");
298     if (expression!=null) {
299       assignmentDelegation.setProcessDefinition(processDefinition);
300       assignmentDelegation.setClassName("org.jbpm.identity.assignment.ExpressionAssignmentHandler");
301       assignmentDelegation.setConfiguration("<expression>"+expression+"</expression>");
302       
303     } else {
304       assignmentDelegation.read(assignmentElement, this);
305     }
306     return assignmentDelegation;
307   }
308
309   private TaskController readTaskController(Element taskControllerElement) {
310     TaskController taskController = new TaskController();
311
312     if (taskControllerElement.attributeValue("class")!=null) {
313       Delegation taskControllerDelegation = new Delegation();
314       taskControllerDelegation.read(taskControllerElement, this);
315       taskController.setTaskControllerDelegation(taskControllerDelegation);
316
317     } else {
318       List JavaDoc variableAccesses = readVariableAccesses(taskControllerElement);
319       taskController.setVariableAccesses(variableAccesses);
320     }
321     return taskController;
322   }
323   
324   public List JavaDoc readVariableAccesses(Element element) {
325     List JavaDoc variableAccesses = new ArrayList JavaDoc();
326     Iterator JavaDoc iter = element.elementIterator("variable");
327     while (iter.hasNext()) {
328       Element variableElement = (Element) iter.next();
329       
330       String JavaDoc variableName = variableElement.attributeValue("name");
331       if (variableName==null) {
332         addProblem(new Problem(Problem.LEVEL_WARNING, "the name attribute of a variable element is required: "+variableElement.asXML()));
333       }
334       String JavaDoc access = variableElement.attributeValue("access", "read,write");
335       String JavaDoc mappedName = variableElement.attributeValue("mapped-name");
336       
337       variableAccesses.add(new VariableAccess(variableName, access, mappedName));
338     }
339     return variableAccesses;
340   }
341
342   public void readStartStateTask(Element startTaskElement, StartState startState) {
343     TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
344     Task startTask = readTask(startTaskElement, taskMgmtDefinition, null);
345     startTask.setStartState(startState);
346     if (startTask.getName()==null) {
347       startTask.setName(startState.getName());
348     }
349     taskMgmtDefinition.setStartTask(startTask);
350   }
351
352   public void readNode(Element nodeElement, Node node, NodeCollection nodeCollection) {
353     // get the action name
354
String JavaDoc name = nodeElement.attributeValue("name");
355     if (name!=null) {
356       node.setName(name);
357     }
358
359     // add the node to the parent
360
nodeCollection.addNode(node);
361
362     // parse common subelements
363
readNodeTimers(nodeElement, node);
364     readEvents(nodeElement, node);
365     readExceptionHandlers(nodeElement, node);
366
367     // save the transitions and parse them at the end
368
addUnresolvedTransitionDestination(nodeElement, node);
369   }
370
371   private void readNodeTimers(Element nodeElement, Node node) {
372     Iterator JavaDoc iter = nodeElement.elementIterator("timer");
373     while (iter.hasNext()) {
374       Element timerElement = (Element) iter.next();
375       readNodeTimer(timerElement, node);
376     }
377   }
378
379   private void readNodeTimer(Element timerElement, Node node) {
380     String JavaDoc name = timerElement.attributeValue("name", node.getName());
381     
382     CreateTimerAction createTimerAction = new CreateTimerAction();
383     createTimerAction.read(timerElement, this);
384     createTimerAction.setTimerName(name);
385     createTimerAction.setTimerAction(readSingleAction(timerElement));
386     addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);
387     
388     CancelTimerAction cancelTimerAction = new CancelTimerAction();
389     cancelTimerAction.setTimerName(name);
390     addAction(node, Event.EVENTTYPE_NODE_LEAVE, cancelTimerAction);
391   }
392   
393   private void readTaskTimers(Element taskElement, Task task) {
394     Iterator JavaDoc iter = taskElement.elementIterator("timer");
395     while (iter.hasNext()) {
396       Element timerElement = (Element) iter.next();
397       readTaskTimer(timerElement, task);
398     }
399   }
400
401   private void readTaskTimer(Element timerElement, Task task) {
402     String JavaDoc name = timerElement.attributeValue("name", task.getName());
403     if (name==null) name = "timer-for-task-"+task.getId();
404     
405     CreateTimerAction createTimerAction = new CreateTimerAction();
406     createTimerAction.read(timerElement, this);
407     createTimerAction.setTimerName(name);
408     createTimerAction.setTimerAction(readSingleAction(timerElement));
409     addAction(task, Event.EVENTTYPE_TASK_CREATE, createTimerAction);
410
411     // read the cancel-event types
412
Collection JavaDoc cancelEventTypes = new ArrayList JavaDoc();
413
414     String JavaDoc cancelEventTypeText = timerElement.attributeValue("cancel-event");
415     if (cancelEventTypeText!=null) {
416       // cancel-event is a comma separated list of events
417
StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(cancelEventTypeText, ",");
418       while (tokenizer.hasMoreTokens()) {
419         cancelEventTypes.add(tokenizer.nextToken().trim());
420       }
421     } else {
422       // set the default
423
cancelEventTypes.add(Event.EVENTTYPE_TASK_END);
424     }
425     
426     Iterator JavaDoc iter = cancelEventTypes.iterator();
427     while (iter.hasNext()) {
428       String JavaDoc cancelEventType = (String JavaDoc) iter.next();
429       CancelTimerAction cancelTimerAction = new CancelTimerAction();
430       cancelTimerAction.setTimerName(name);
431       addAction(task, cancelEventType, cancelTimerAction);
432     }
433   }
434   
435   private void readEvents(Element parentElement, GraphElement graphElement) {
436     Iterator JavaDoc iter = parentElement.elementIterator("event");
437     while (iter.hasNext()) {
438       Element eventElement = (Element) iter.next();
439       String JavaDoc eventType = eventElement.attributeValue("type");
440       if (!graphElement.hasEvent(eventType)) {
441         graphElement.addEvent(new Event(eventType));
442       }
443       readActions(eventElement, graphElement, eventType);
444     }
445   }
446
447   public void readActions(Element eventElement, GraphElement graphElement, String JavaDoc eventType) {
448     // for all the elements in the event element
449
Iterator JavaDoc nodeElementIter = eventElement.elementIterator();
450     while (nodeElementIter.hasNext()) {
451       Element actionElement = (Element) nodeElementIter.next();
452       String JavaDoc actionName = actionElement.getName();
453       if (ActionTypes.hasActionName(actionName)) {
454         Action action = createAction(actionElement);
455         if ( (graphElement!=null)
456              && (eventType!=null)
457            ) {
458           // add the action to the event
459
addAction(graphElement, eventType, action);
460         }
461       }
462     }
463   }
464
465   private void addAction(GraphElement graphElement, String JavaDoc eventType, Action action) {
466     Event event = graphElement.getEvent(eventType);
467     if (event==null) {
468       event = new Event(eventType);
469       graphElement.addEvent(event);
470     }
471     event.addAction(action);
472   }
473   
474   public Action readSingleAction(Element nodeElement) {
475     Action action = null;
476     // search for the first action element in the node
477
Iterator JavaDoc iter = nodeElement.elementIterator();
478     while (iter.hasNext() && (action==null)) {
479       Element candidate = (Element) iter.next();
480       if (ActionTypes.hasActionName(candidate.getName())) {
481         // parse the action and assign it to this node
482
action = createAction(candidate);
483       }
484     }
485     return action;
486   }
487
488   public Action createAction(Element actionElement) {
489     // create a new instance of the action
490
Action action = null;
491     String JavaDoc actionName = actionElement.getName();
492     Class JavaDoc actionType = ActionTypes.getActionType(actionName);
493     try {
494       action = (Action) actionType.newInstance();
495     } catch (Exception JavaDoc e) {
496       log.error("couldn't instantiate action '"+actionName+"', of type '"+actionType.getName()+"'", e);
497     }
498
499     // read the common node parts of the action
500
readAction(actionElement, action);
501     
502     return action;
503   }
504
505   public void readAction(Element element, Action action) {
506     // if a name is specified for this action
507
String JavaDoc actionName = element.attributeValue("name");
508     if (actionName!=null) {
509       action.setName(actionName);
510       // add the action to the named process action repository
511
processDefinition.addAction(action);
512     }
513
514     // if the action is parsable
515
// (meaning: if the action has special configuration to parse, other then the common node data)
516
action.read(element, this);
517   }
518
519   private void readExceptionHandlers(Element graphElementElement, GraphElement graphElement) {
520     Iterator JavaDoc iter = graphElementElement.elementIterator("exception-handler");
521     while (iter.hasNext()) {
522       Element exceptionHandlerElement = (Element) iter.next();
523       readExceptionHandler(exceptionHandlerElement, graphElement);
524     }
525   }
526
527   private void readExceptionHandler(Element exceptionHandlerElement, GraphElement graphElement) {
528     // create the exception handler
529
ExceptionHandler exceptionHandler = new ExceptionHandler();
530     exceptionHandler.setExceptionClassName(exceptionHandlerElement.attributeValue("exception-class"));
531     // add it to the graph element
532
graphElement.addExceptionHandler(exceptionHandler);
533
534     // read the actions in the body of the exception-handler element
535
Iterator JavaDoc iter = exceptionHandlerElement.elementIterator();
536     while (iter.hasNext()) {
537       Element childElement = (Element) iter.next();
538       if (ActionTypes.hasActionName(childElement.getName())) {
539         Action action = createAction(childElement);
540         exceptionHandler.addAction(action);
541       }
542     }
543   }
544
545   // transition destinations are parsed in a second pass //////////////////////
546

547   public void addUnresolvedTransitionDestination(Element nodeElement, Node node) {
548     unresolvedTransitionDestinations.add(new Object JavaDoc[]{nodeElement, node});
549   }
550
551   public void resolveTransitionDestinations() {
552     Iterator JavaDoc iter = unresolvedTransitionDestinations.iterator();
553     while (iter.hasNext()) {
554       Object JavaDoc[] unresolvedTransition = (Object JavaDoc[]) iter.next();
555       Element nodeElement = (Element) unresolvedTransition[0];
556       Node node = (Node) unresolvedTransition[1];
557       resolveTransitionDestinations(nodeElement.elements("transition"), node);
558     }
559   }
560
561   public void resolveTransitionDestinations(List JavaDoc transitionElements, Node node) {
562     Iterator JavaDoc iter = transitionElements.iterator();
563     while (iter.hasNext()) {
564       Element transitionElement = (Element) iter.next();
565       resolveTransitionDestination(transitionElement, node);
566     }
567   }
568
569   public void resolveTransitionDestination(Element transitionElement, Node node) {
570     Transition transition = new Transition();
571     transition.setProcessDefinition(processDefinition);
572     
573     // get the action name
574
String JavaDoc name = transitionElement.attributeValue("name");
575     if (name!=null) {
576       transition.setName(name);
577     }
578
579     // add the transition to the node
580
node.addLeavingTransition(transition);
581
582     // set destinationNode of the transition
583
String JavaDoc toName = transitionElement.attributeValue("to");
584     if (toName==null) {
585       addWarning("node '"+node.getFullyQualifiedName()+"' has a transition without a 'to'-attribute to specify its destinationNode");
586     } else {
587       Node to = ((NodeCollection)node.getParent()).findNode(toName);
588       if (to==null) {
589         addWarning("transition to='"+toName+"' on node '"+node.getFullyQualifiedName()+"' cannot be resolved");
590       } else {
591         to.addArrivingTransition(transition);
592       }
593     }
594     
595     // read the actions
596
readActions(transitionElement, transition, Event.EVENTTYPE_TRANSITION);
597     
598     readExceptionHandlers(transitionElement, transition);
599   }
600   
601   // action references are parsed in a second pass ////////////////////////////
602

603   public void addUnresolvedActionReference(Element actionElement, Action action) {
604     unresolvedActionReferences.add(new Object JavaDoc[]{actionElement, action});
605   }
606
607   public void resolveActionReferences() {
608     Iterator JavaDoc iter = unresolvedActionReferences.iterator();
609     while (iter.hasNext()) {
610       Object JavaDoc[] unresolvedActionReference = (Object JavaDoc[]) iter.next();
611       Element actionElement = (Element) unresolvedActionReference[0];
612       Action action = (Action) unresolvedActionReference[1];
613       String JavaDoc referencedActionName = actionElement.attributeValue("ref-name");
614       Action referencedAction = processDefinition.getAction(referencedActionName);
615       if (referencedAction==null) {
616         addWarning("couldn't resolve action reference in "+actionElement.asXML());
617       }
618       action.setReferencedAction(referencedAction);
619     }
620   }
621
622   // verify swimlane assignments in second pass ///////////////////////////////
623
public void verifySwimlaneAssignments() {
624     TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
625     if ( (taskMgmtDefinition!=null)
626          && (taskMgmtDefinition.getSwimlanes()!=null)
627        ) {
628       Iterator JavaDoc iter = taskMgmtDefinition.getSwimlanes().values().iterator();
629       while (iter.hasNext()) {
630         Swimlane swimlane = (Swimlane) iter.next();
631         
632         Task startTask = taskMgmtDefinition.getStartTask();
633         Swimlane startTaskSwimlane = (startTask!=null ? startTask.getSwimlane() : null);
634         
635         if ( (swimlane.getAssignmentDelegation()==null)
636              && (swimlane!=startTaskSwimlane)
637            ) {
638           addWarning("swimlane '"+swimlane.getName()+"' does not have an assignment");
639         }
640       }
641     }
642   }
643
644   private static final Log log = LogFactory.getLog(JpdlXmlReader.class);
645 }
646
Popular Tags