001/** 002 * Copyright 2005-2015 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kew.engine.node.service.impl; 017 018import org.apache.commons.collections.ComparatorUtils; 019import org.kuali.rice.kew.doctype.bo.DocumentType; 020import org.kuali.rice.kew.engine.RouteHelper; 021import org.kuali.rice.kew.engine.node.Branch; 022import org.kuali.rice.kew.engine.node.BranchState; 023import org.kuali.rice.kew.engine.node.NodeGraphContext; 024import org.kuali.rice.kew.engine.node.NodeGraphSearchCriteria; 025import org.kuali.rice.kew.engine.node.NodeGraphSearchResult; 026import org.kuali.rice.kew.engine.node.NodeMatcher; 027import org.kuali.rice.kew.engine.node.NodeState; 028import org.kuali.rice.kew.engine.node.ProcessDefinitionBo; 029import org.kuali.rice.kew.engine.node.RouteNode; 030import org.kuali.rice.kew.engine.node.RouteNodeInstance; 031import org.kuali.rice.kew.engine.node.RouteNodeUtils; 032import org.kuali.rice.kew.engine.node.dao.RouteNodeDAO; 033import org.kuali.rice.kew.engine.node.service.RouteNodeService; 034import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 035import org.kuali.rice.kew.service.KEWServiceLocator; 036import org.kuali.rice.krad.data.DataObjectService; 037import org.kuali.rice.krad.data.PersistenceOption; 038import org.springframework.beans.factory.annotation.Required; 039 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.Collection; 043import java.util.Collections; 044import java.util.Comparator; 045import java.util.HashMap; 046import java.util.HashSet; 047import java.util.Iterator; 048import java.util.List; 049import java.util.Map; 050import java.util.Set; 051 052 053 054public class RouteNodeServiceImpl implements RouteNodeService { 055 056 protected final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(getClass()); 057 058 public static final String REVOKED_NODE_INSTANCES_STATE_KEY = "NodeInstances.Revoked"; 059 060 private static final Comparator NODE_INSTANCE_FORWARD_SORT = new NodeInstanceIdSorter(); 061 private static final Comparator NODE_INSTANCE_BACKWARD_SORT = 062 ComparatorUtils.reversedComparator(NODE_INSTANCE_FORWARD_SORT); 063 private RouteHelper helper = new RouteHelper(); 064 private RouteNodeDAO routeNodeDAO; 065 066 private DataObjectService dataObjectService; 067 068 public RouteNode save(RouteNode node) { 069 return dataObjectService.save(node); 070 } 071 072 public RouteNodeInstance save(RouteNodeInstance nodeInstance) { 073 return dataObjectService.save(nodeInstance); 074 } 075 076 public void save(NodeState nodeState) { 077 dataObjectService.save(nodeState); 078 } 079 080 public Branch save(Branch branch) { 081 return dataObjectService.save(branch); 082 } 083 084 public RouteNode findRouteNodeById(String nodeId) { 085 return dataObjectService.find(RouteNode.class,nodeId); 086 } 087 088 public RouteNodeInstance findRouteNodeInstanceById(String nodeInstanceId) { 089 return routeNodeDAO.findRouteNodeInstanceById(nodeInstanceId); 090 } 091 092 public RouteNodeInstance findRouteNodeInstanceById(String nodeInstanceId, DocumentRouteHeaderValue document) { 093 return RouteNodeUtils.findRouteNodeInstanceById(nodeInstanceId, document); 094 } 095 096 public List<RouteNodeInstance> getCurrentNodeInstances(String documentId) { 097 List<RouteNodeInstance> currentNodeInstances = getActiveNodeInstances(documentId); 098 if (currentNodeInstances.isEmpty()) { 099 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}