View Javadoc

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