View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.engine.node.service.impl;
17  
18  import org.apache.commons.collections.ComparatorUtils;
19  import org.kuali.rice.kew.doctype.bo.DocumentType;
20  import org.kuali.rice.kew.engine.RouteHelper;
21  import org.kuali.rice.kew.engine.node.Branch;
22  import org.kuali.rice.kew.engine.node.BranchState;
23  import org.kuali.rice.kew.engine.node.NodeGraphContext;
24  import org.kuali.rice.kew.engine.node.NodeGraphSearchCriteria;
25  import org.kuali.rice.kew.engine.node.NodeGraphSearchResult;
26  import org.kuali.rice.kew.engine.node.NodeMatcher;
27  import org.kuali.rice.kew.engine.node.NodeState;
28  import org.kuali.rice.kew.engine.node.ProcessDefinitionBo;
29  import org.kuali.rice.kew.engine.node.RouteNode;
30  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
31  import org.kuali.rice.kew.engine.node.RouteNodeUtils;
32  import org.kuali.rice.kew.engine.node.dao.RouteNodeDAO;
33  import org.kuali.rice.kew.engine.node.service.RouteNodeService;
34  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
35  import org.kuali.rice.kew.service.KEWServiceLocator;
36  import org.kuali.rice.krad.data.DataObjectService;
37  import org.kuali.rice.krad.data.PersistenceOption;
38  import org.springframework.beans.factory.annotation.Required;
39  
40  import java.util.ArrayList;
41  import java.util.Arrays;
42  import java.util.Collection;
43  import java.util.Collections;
44  import java.util.Comparator;
45  import java.util.HashMap;
46  import java.util.HashSet;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Map;
50  import java.util.Set;
51  
52  
53  
54  public class RouteNodeServiceImpl implements RouteNodeService {
55  
56  	protected final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(getClass());
57  
58  	public static final String REVOKED_NODE_INSTANCES_STATE_KEY = "NodeInstances.Revoked";
59  
60  	private static final Comparator NODE_INSTANCE_FORWARD_SORT = new NodeInstanceIdSorter();
61  	private static final Comparator NODE_INSTANCE_BACKWARD_SORT =
62  		ComparatorUtils.reversedComparator(NODE_INSTANCE_FORWARD_SORT);
63      private RouteHelper helper = new RouteHelper();
64  	private RouteNodeDAO routeNodeDAO;
65  
66      private DataObjectService dataObjectService;
67  
68      public RouteNode save(RouteNode node) {
69      	return dataObjectService.save(node);
70      }
71  
72      public RouteNodeInstance save(RouteNodeInstance nodeInstance) {
73          return dataObjectService.save(nodeInstance);
74      }
75  
76      public void save(NodeState nodeState) {
77          dataObjectService.save(nodeState);
78      }
79  
80      public Branch save(Branch branch) {
81          return dataObjectService.save(branch);
82      }
83  
84      public RouteNode findRouteNodeById(String nodeId) {
85      	return dataObjectService.find(RouteNode.class,nodeId);
86      }
87  
88      public RouteNodeInstance findRouteNodeInstanceById(String nodeInstanceId) {
89      	return routeNodeDAO.findRouteNodeInstanceById(nodeInstanceId);
90      }
91  
92      public RouteNodeInstance findRouteNodeInstanceById(String nodeInstanceId, DocumentRouteHeaderValue document) {
93      	return RouteNodeUtils.findRouteNodeInstanceById(nodeInstanceId, document);
94      }
95  
96      public List<RouteNodeInstance> getCurrentNodeInstances(String documentId) {
97          List<RouteNodeInstance> currentNodeInstances = getActiveNodeInstances(documentId);
98          if (currentNodeInstances.isEmpty()) {
99              currentNodeInstances = getTerminalNodeInstances(documentId);
100         }
101         return currentNodeInstances;
102     }
103 
104     public List<RouteNodeInstance> getActiveNodeInstances(String documentId) {
105     	return routeNodeDAO.getActiveNodeInstances(documentId);
106     }
107 
108     public List<RouteNodeInstance> getActiveNodeInstances(DocumentRouteHeaderValue document) {
109        List<RouteNodeInstance> flattenedNodeInstances = getFlattenedNodeInstances(document, true);
110         List<RouteNodeInstance> activeNodeInstances = new ArrayList<RouteNodeInstance>();
111         for (RouteNodeInstance nodeInstance : flattenedNodeInstances) {
112             if (nodeInstance.isActive()) {
113                 activeNodeInstances.add(nodeInstance);
114             }
115         }
116         return activeNodeInstances;
117     }
118 
119     @Override
120     public List<String> getCurrentRouteNodeNames(String documentId) {
121        	return routeNodeDAO.getCurrentRouteNodeNames(documentId);
122     }
123 
124     @Override
125 	public List<String> getActiveRouteNodeNames(String documentId) {
126     	return routeNodeDAO.getActiveRouteNodeNames(documentId);
127     }
128 
129     public List<RouteNodeInstance> getTerminalNodeInstances(String documentId) {
130         return routeNodeDAO.getTerminalNodeInstances(documentId);
131     }
132 
133     @Override
134 	public List<String> getTerminalRouteNodeNames(String documentId) {
135     	return routeNodeDAO.getTerminalRouteNodeNames(documentId);
136     }
137 
138     public List getInitialNodeInstances(String documentId) {
139     	return routeNodeDAO.getInitialNodeInstances(documentId);
140     }
141 
142     public NodeState findNodeState(Long nodeInstanceId, String key) {
143         return routeNodeDAO.findNodeState(nodeInstanceId, key);
144     }
145 
146     public RouteNode findRouteNodeByName(String documentTypeId, String name) {
147         return routeNodeDAO.findRouteNodeByName(documentTypeId, name);
148     }
149 
150     public List<RouteNode> findFinalApprovalRouteNodes(String documentTypeId) {
151         DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findById(documentTypeId);
152         documentType = documentType.getRouteDefiningDocumentType();
153         return routeNodeDAO.findFinalApprovalRouteNodes(documentType.getDocumentTypeId());
154     }
155 
156     public List findNextRouteNodesInPath(RouteNodeInstance nodeInstance, String nodeName) {
157         List<RouteNode> nodesInPath = new ArrayList<RouteNode>();
158         for (Iterator<RouteNode> iterator = nodeInstance.getRouteNode().getNextNodes().iterator(); iterator.hasNext();) {
159             RouteNode nextNode = iterator.next();
160             nodesInPath.addAll(findNextRouteNodesInPath(nodeName, nextNode, new HashSet<String>()));
161         }
162         return nodesInPath;
163     }
164 
165     private List<RouteNode> findNextRouteNodesInPath(String nodeName, RouteNode node, Set<String> inspected) {
166         List<RouteNode> nextNodesInPath = new ArrayList<RouteNode>();
167         if (inspected.contains(node.getRouteNodeId())) {
168             return nextNodesInPath;
169         }
170         inspected.add(node.getRouteNodeId());
171         if (node.getRouteNodeName().equals(nodeName)) {
172             nextNodesInPath.add(node);
173         } else {
174             if (helper.isSubProcessNode(node)) {
175                 ProcessDefinitionBo subProcess = node.getDocumentType().getNamedProcess(node.getRouteNodeName());
176                 RouteNode subNode = subProcess.getInitialRouteNode();
177                 if (subNode != null) {
178                     nextNodesInPath.addAll(findNextRouteNodesInPath(nodeName, subNode, inspected));
179                 }
180             }
181             for (Iterator<RouteNode> iterator = node.getNextNodes().iterator(); iterator.hasNext();) {
182                 RouteNode nextNode = iterator.next();
183                 nextNodesInPath.addAll(findNextRouteNodesInPath(nodeName, nextNode, inspected));
184             }
185         }
186         return nextNodesInPath;
187     }
188 
189     public boolean isNodeInPath(DocumentRouteHeaderValue document, String nodeName) {
190         boolean isInPath = false;
191         Collection<RouteNodeInstance> activeNodes = getActiveNodeInstances(document.getDocumentId());
192         for (Iterator<RouteNodeInstance> iterator = activeNodes.iterator(); iterator.hasNext();) {
193             RouteNodeInstance nodeInstance = iterator.next();
194             List nextNodesInPath = findNextRouteNodesInPath(nodeInstance, nodeName);
195             isInPath = isInPath || !nextNodesInPath.isEmpty();
196         }
197         return isInPath;
198     }
199 
200     public List findRouteNodeInstances(String documentId) {
201         return this.routeNodeDAO.findRouteNodeInstances(documentId);
202     }
203 
204 	public void setRouteNodeDAO(RouteNodeDAO dao) {
205 		this.routeNodeDAO = dao;
206 	}
207 
208     public List findProcessNodeInstances(RouteNodeInstance process) {
209        return this.routeNodeDAO.findProcessNodeInstances(process);
210     }
211 
212     public List<String> findPreviousNodeNames(String documentId) {
213         DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
214         List<String> revokedIds = Collections.emptyList();
215 
216         List<String> nodeNames = new ArrayList<String>();
217         if(document.getRootBranch() != null) {
218             String revoked = document.getRootBranch().getBranchState(REVOKED_NODE_INSTANCES_STATE_KEY) == null ? null : document.getRootBranch().getBranchState(REVOKED_NODE_INSTANCES_STATE_KEY).getValue();
219             if (revoked != null) {
220                 revokedIds = Arrays.asList(revoked.split(","));
221             }
222             List <RouteNodeInstance> currentNodeInstances = KEWServiceLocator.getRouteNodeService().getCurrentNodeInstances(documentId);
223             List<RouteNodeInstance> nodeInstances = new ArrayList<RouteNodeInstance>();
224             for (RouteNodeInstance nodeInstance : currentNodeInstances) {
225                 nodeInstances.addAll(nodeInstance.getPreviousNodeInstances());
226             }
227 
228             while (!nodeInstances.isEmpty()) {
229                 RouteNodeInstance nodeInstance = nodeInstances.remove(0);
230                 if (!revokedIds.contains(nodeInstance.getRouteNodeInstanceId())) {
231                     nodeNames.add(nodeInstance.getName());
232                 }
233                 nodeInstances.addAll(nodeInstance.getPreviousNodeInstances());
234             }
235 
236             //reverse the order, because it was built last to first
237             Collections.reverse(nodeNames);
238         }
239 
240         return nodeNames;
241     }
242 
243     public List<String> findFutureNodeNames(String documentId) {
244         List currentNodeInstances = KEWServiceLocator.getRouteNodeService().getCurrentNodeInstances(documentId);
245         List<RouteNode> nodes = new ArrayList<RouteNode>();
246         for (Iterator iterator = currentNodeInstances.iterator(); iterator.hasNext();) {
247             RouteNodeInstance nodeInstance = (RouteNodeInstance) iterator.next();
248             nodes.addAll(nodeInstance.getRouteNode().getNextNodes());
249         }
250         List<String> nodeNames = new ArrayList<String>();
251         while (!nodes.isEmpty()) {
252             RouteNode node = nodes.remove(0);
253             if (!nodeNames.contains(node.getRouteNodeName())) {
254         	nodeNames.add(node.getRouteNodeName());
255             }
256             nodes.addAll(node.getNextNodes());
257         }
258         return nodeNames;
259     }
260 
261     public List<RouteNode> getFlattenedNodes(DocumentType documentType, boolean climbHierarchy) {
262         List<RouteNode> nodes = new ArrayList<RouteNode>();
263         if (!documentType.isRouteInherited() || climbHierarchy) {
264             for (Iterator iterator = documentType.getProcesses().iterator(); iterator.hasNext();) {
265                 ProcessDefinitionBo process = (ProcessDefinitionBo) iterator.next();
266                 nodes.addAll(getFlattenedNodes(process));
267             }
268         }
269         Collections.sort(nodes, new RouteNodeSorter());
270         return nodes;
271     }
272 
273     public List<RouteNode> getFlattenedNodes(ProcessDefinitionBo process) {
274         Map<String, RouteNode> nodesMap = new HashMap<String, RouteNode>();
275         if (process.getInitialRouteNode() != null) {
276             flattenNodeGraph(nodesMap, process.getInitialRouteNode());
277             List<RouteNode> nodes = new ArrayList<RouteNode>(nodesMap.values());
278             Collections.sort(nodes, new RouteNodeSorter());
279             return nodes;
280         } else {
281             List<RouteNode> nodes = new ArrayList<RouteNode>();
282             nodes.add(new RouteNode());
283             return nodes;
284         }
285 
286     }
287 
288     /**
289      * Recursively walks the node graph and builds up the map.  Uses a map because we will
290      * end up walking through duplicates, as is the case with Join nodes.
291      */
292     private void flattenNodeGraph(Map<String, RouteNode> nodes, RouteNode node) {
293         if (node != null) {
294             if (nodes.containsKey(node.getRouteNodeName())) {
295                 return;
296             }
297             nodes.put(node.getRouteNodeName(), node);
298             for (Iterator<RouteNode> iterator = node.getNextNodes().iterator(); iterator.hasNext();) {
299                 RouteNode nextNode = iterator.next();
300                 flattenNodeGraph(nodes, nextNode);
301             }
302         } else {
303             return;
304         }
305     }
306 
307     public List<RouteNodeInstance> getFlattenedNodeInstances(DocumentRouteHeaderValue document, boolean includeProcesses) {
308         List<RouteNodeInstance> nodeInstances = new ArrayList<RouteNodeInstance>();
309         Set<String> visitedNodeInstanceIds = new HashSet<String>();
310         for (Iterator<RouteNodeInstance> iterator = document.getInitialRouteNodeInstances().iterator(); iterator.hasNext();) {
311             RouteNodeInstance initialNodeInstance = iterator.next();
312             flattenNodeInstanceGraph(nodeInstances, visitedNodeInstanceIds, initialNodeInstance, includeProcesses);
313         }
314         return nodeInstances;
315     }
316 
317 	private void flattenNodeInstanceGraph(List<RouteNodeInstance> nodeInstances, Set<String> visitedNodeInstanceIds, RouteNodeInstance nodeInstance, boolean includeProcesses) {
318 
319 		if (nodeInstance != null) {
320 			if (visitedNodeInstanceIds.contains(nodeInstance.getRouteNodeInstanceId())) {
321 				return;
322 			}
323 			if (includeProcesses && nodeInstance.getProcess() != null) {
324 				flattenNodeInstanceGraph(nodeInstances, visitedNodeInstanceIds, nodeInstance.getProcess(), includeProcesses);
325 			}
326 			visitedNodeInstanceIds.add(nodeInstance.getRouteNodeInstanceId());
327 			nodeInstances.add(nodeInstance);
328 			for (Iterator<RouteNodeInstance> iterator = nodeInstance.getNextNodeInstances().iterator(); iterator.hasNext();) {
329 				RouteNodeInstance nextNodeInstance = iterator.next();
330 				flattenNodeInstanceGraph(nodeInstances, visitedNodeInstanceIds, nextNodeInstance, includeProcesses);
331 			}
332 
333 		}
334 
335     }
336 
337     public NodeGraphSearchResult searchNodeGraph(NodeGraphSearchCriteria criteria) {
338     	NodeGraphContext context = new NodeGraphContext();
339     	if (criteria.getSearchDirection() == NodeGraphSearchCriteria.SEARCH_DIRECTION_BACKWARD) {
340         	searchNodeGraphBackward(context, criteria.getMatcher(), null, criteria.getStartingNodeInstances());
341     	} else {
342     		throw new UnsupportedOperationException("Search feature can only search backward currently.");
343     	}
344     	List exactPath = determineExactPath(context, criteria.getSearchDirection(), criteria.getStartingNodeInstances());
345         return new NodeGraphSearchResult(context.getCurrentNodeInstance(), exactPath);
346     }
347 
348     private void searchNodeGraphBackward(NodeGraphContext context, NodeMatcher matcher, RouteNodeInstance previousNodeInstance, Collection nodeInstances) {
349         if (nodeInstances == null) {
350             return;
351         }
352     	for (Iterator iterator = nodeInstances.iterator(); iterator.hasNext();) {
353             RouteNodeInstance nodeInstance = (RouteNodeInstance) iterator.next();
354             context.setPreviousNodeInstance(previousNodeInstance);
355             context.setCurrentNodeInstance(nodeInstance);
356             searchNodeGraphBackward(context, matcher);
357             if (context.getResultNodeInstance() != null) {
358             	// we've located the node instance we're searching for, we're done
359             	break;
360             }
361         }
362     }
363 
364     private void searchNodeGraphBackward(NodeGraphContext context, NodeMatcher matcher) {
365         RouteNodeInstance current = context.getCurrentNodeInstance();
366         int numBranches = current.getNextNodeInstances().size();
367         // if this is a split node, we want to wait here, until all branches join back to us
368         if (numBranches > 1) {
369         	// determine the number of branches that have joined back to the split thus far
370             Integer joinCount = (Integer)context.getSplitState().get(current.getRouteNodeInstanceId());
371             if (joinCount == null) {
372                 joinCount = new Integer(0);
373             }
374             // if this split is not a leaf node we increment the count
375             if (context.getPreviousNodeInstance() != null) {
376                 joinCount = new Integer(joinCount.intValue()+1);
377             }
378             context.getSplitState().put(current.getRouteNodeInstanceId(), joinCount);
379             // if not all branches have joined, stop and wait for other branches to join
380             if (joinCount.intValue() != numBranches) {
381                 return;
382             }
383         }
384         if (matcher.isMatch(context)) {
385             context.setResultNodeInstance(current);
386         } else {
387             context.getVisited().put(current.getRouteNodeInstanceId(), current);
388             searchNodeGraphBackward(context, matcher, current, current.getPreviousNodeInstances());
389         }
390     }
391 
392     public List<RouteNodeInstance> getActiveNodeInstances(DocumentRouteHeaderValue document, String nodeName) {
393 		Collection<RouteNodeInstance> activeNodes = getActiveNodeInstances(document.getDocumentId());
394 		List<RouteNodeInstance> foundNodes = new ArrayList<RouteNodeInstance>();
395         for (Iterator<RouteNodeInstance> iterator = activeNodes.iterator(); iterator.hasNext();) {
396             RouteNodeInstance nodeInstance = iterator.next();
397             if (nodeInstance.getName().equals(nodeName)) {
398             	foundNodes.add(nodeInstance);
399             }
400         }
401         return foundNodes;
402     }
403 
404     private List determineExactPath(NodeGraphContext context, int searchDirection, Collection<RouteNodeInstance> startingNodeInstances) {
405     	List<RouteNodeInstance> exactPath = new ArrayList<RouteNodeInstance>();
406     	if (context.getResultNodeInstance() == null) {
407     		exactPath.addAll(context.getVisited().values());
408     	} else {
409     		determineExactPath(exactPath, new HashMap<String, RouteNodeInstance>(), startingNodeInstances, context.getResultNodeInstance());
410     	}
411     	if (NodeGraphSearchCriteria.SEARCH_DIRECTION_FORWARD == searchDirection) {
412     		Collections.sort(exactPath, NODE_INSTANCE_BACKWARD_SORT);
413     	} else {
414     		Collections.sort(exactPath, NODE_INSTANCE_FORWARD_SORT);
415     	}
416     	return exactPath;
417     }
418 
419     private void determineExactPath(List<RouteNodeInstance> exactPath, Map<String, RouteNodeInstance> visited, Collection<RouteNodeInstance> startingNodeInstances, RouteNodeInstance nodeInstance) {
420     	if (nodeInstance == null) {
421     		return;
422     	}
423     	if (visited.containsKey(nodeInstance.getRouteNodeInstanceId())) {
424     		return;
425     	}
426     	visited.put(nodeInstance.getRouteNodeInstanceId(), nodeInstance);
427     	exactPath.add(nodeInstance);
428     	for (RouteNodeInstance startingNode : startingNodeInstances) {
429 			if (startingNode.getRouteNodeInstanceId().equals(nodeInstance.getRouteNodeInstanceId())) {
430 				return;
431 			}
432 		}
433     	for (Iterator<RouteNodeInstance> iterator = nodeInstance.getNextNodeInstances().iterator(); iterator.hasNext(); ) {
434 			RouteNodeInstance nextNodeInstance = iterator.next();
435 			determineExactPath(exactPath, visited, startingNodeInstances, nextNodeInstance);
436 		}
437     }
438 
439 
440     /**
441      * Sorts by RouteNodeId or the order the nodes will be evaluated in *roughly*.  This is
442      * for display purposes when rendering a flattened list of nodes.
443      *
444  * @author Kuali Rice Team (rice.collab@kuali.org)
445      */
446     private static class RouteNodeSorter implements Comparator {
447         public int compare(Object arg0, Object arg1) {
448             RouteNode rn1 = (RouteNode)arg0;
449             RouteNode rn2 = (RouteNode)arg1;
450             return rn1.getRouteNodeId().compareTo(rn2.getRouteNodeId());
451         }
452     }
453 
454     private static class NodeInstanceIdSorter implements Comparator {
455         public int compare(Object arg0, Object arg1) {
456             RouteNodeInstance nodeInstance1 = (RouteNodeInstance)arg0;
457             RouteNodeInstance nodeInstance2 = (RouteNodeInstance)arg1;
458             return nodeInstance1.getRouteNodeInstanceId().compareTo(nodeInstance2.getRouteNodeInstanceId());
459         }
460     }
461 
462 
463     public void deleteByRouteNodeInstance(RouteNodeInstance routeNodeInstance){
464     	//update the route node instance link table to cancel the relationship between the to-be-deleted instance and the previous node instances
465     	routeNodeDAO.deleteLinksToPreNodeInstances(routeNodeInstance);
466     	//delete the routeNodeInstance and its next node instances
467     	routeNodeDAO.deleteRouteNodeInstancesHereAfter(routeNodeInstance);
468     }
469 
470     public void deleteNodeStateById(Long nodeStateId){
471     	routeNodeDAO.deleteNodeStateById(nodeStateId);
472     }
473 
474     public void deleteNodeStates(List statesToBeDeleted){
475     	routeNodeDAO.deleteNodeStates(statesToBeDeleted);
476     }
477 
478     /**
479      * Records the revocation in the root BranchState of the document.
480      */
481     public void revokeNodeInstance(DocumentRouteHeaderValue document, RouteNodeInstance nodeInstance) {
482     	if (document == null) {
483     		throw new IllegalArgumentException("Document must not be null.");
484     	}
485 		if (nodeInstance == null || nodeInstance.getRouteNodeInstanceId() == null) {
486 			throw new IllegalArgumentException("In order to revoke a final approval node the node instance must be persisent and have an id.");
487 		}
488 		// get the initial node instance, the root branch is where we will store the state
489     	Branch rootBranch = document.getRootBranch();
490     	BranchState state = null;
491     	if (rootBranch != null) {
492     	    state = rootBranch.getBranchState(REVOKED_NODE_INSTANCES_STATE_KEY);
493     	}
494     	if (state == null) {
495     		state = new BranchState();
496     		state.setKey(REVOKED_NODE_INSTANCES_STATE_KEY);
497     		state.setValue("");
498     		rootBranch.addBranchState(state);
499     	}
500     	if (state.getValue() == null) {
501     		state.setValue("");
502     	}
503     	state.setValue(state.getValue() + nodeInstance.getRouteNodeInstanceId() + ",");
504     	save(rootBranch);
505 	}
506 
507     /**
508      * Queries the list of revoked node instances from the root BranchState of the Document
509      * and returns a List of revoked RouteNodeInstances.
510      */
511 	public List getRevokedNodeInstances(DocumentRouteHeaderValue document) {
512 		if (document == null) {
513     		throw new IllegalArgumentException("Document must not be null.");
514     	}
515 		List<RouteNodeInstance> revokedNodeInstances = new ArrayList<RouteNodeInstance>();
516 
517     	Branch rootBranch = document.getRootBranch();
518     	BranchState state = null;
519     	if (rootBranch != null) {
520     	    state = rootBranch.getBranchState(REVOKED_NODE_INSTANCES_STATE_KEY);
521     	}
522     	if (state == null || org.apache.commons.lang.StringUtils.isEmpty(state.getValue())) {
523     		return revokedNodeInstances;
524     	}
525     	String[] revokedNodes = state.getValue().split(",");
526     	for (int index = 0; index < revokedNodes.length; index++) {
527 			String revokedNodeInstanceId = revokedNodes[index];
528 			RouteNodeInstance revokedNodeInstance = findRouteNodeInstanceById(revokedNodeInstanceId);
529 			if (revokedNodeInstance == null) {
530 				LOG.warn("Could not locate revoked RouteNodeInstance with the given id: " + revokedNodeInstanceId);
531 			} else {
532 				revokedNodeInstances.add(revokedNodeInstance);
533 			}
534 		}
535     	return revokedNodeInstances;
536 	}
537 
538 
539     public DataObjectService getDataObjectService() {
540         return dataObjectService;
541     }
542 
543     @Required
544     public void setDataObjectService(DataObjectService dataObjectService) {
545         this.dataObjectService = dataObjectService;
546     }
547 
548 
549 }