1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
package org.kuali.rice.kew.actions; |
17 | |
|
18 | |
import org.apache.log4j.MDC; |
19 | |
import org.kuali.rice.kew.actionrequest.ActionRequestFactory; |
20 | |
import org.kuali.rice.kew.actionrequest.ActionRequestValue; |
21 | |
import org.kuali.rice.kew.actionrequest.Recipient; |
22 | |
import org.kuali.rice.kew.actiontaken.ActionTakenValue; |
23 | |
import org.kuali.rice.kew.api.WorkflowRuntimeException; |
24 | |
import org.kuali.rice.kew.api.exception.InvalidActionTakenException; |
25 | |
import org.kuali.rice.kew.engine.CompatUtils; |
26 | |
import org.kuali.rice.kew.engine.RouteHelper; |
27 | |
import org.kuali.rice.kew.engine.node.NodeGraphSearchCriteria; |
28 | |
import org.kuali.rice.kew.engine.node.NodeGraphSearchResult; |
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.service.RouteNodeService; |
32 | |
import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange; |
33 | |
import org.kuali.rice.kew.framework.postprocessor.PostProcessor; |
34 | |
import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport; |
35 | |
import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; |
36 | |
import org.kuali.rice.kew.service.KEWServiceLocator; |
37 | |
import org.kuali.rice.kew.api.KewApiConstants; |
38 | |
import org.kuali.rice.kim.api.identity.principal.PrincipalContract; |
39 | |
|
40 | |
|
41 | |
import java.util.ArrayList; |
42 | |
import java.util.Collection; |
43 | |
import java.util.Iterator; |
44 | |
import java.util.List; |
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
|
53 | |
|
54 | |
|
55 | |
public class ReturnToPreviousNodeAction extends ActionTakenEvent { |
56 | |
|
57 | 0 | protected final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(getClass()); |
58 | |
|
59 | 0 | private RouteHelper helper = new RouteHelper(); |
60 | |
private String nodeName; |
61 | |
private boolean superUserUsage; |
62 | 0 | private boolean sendNotifications = true; |
63 | |
|
64 | |
public ReturnToPreviousNodeAction(DocumentRouteHeaderValue routeHeader, PrincipalContract principal) { |
65 | 0 | super(KewApiConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD, routeHeader, principal); |
66 | 0 | } |
67 | |
|
68 | |
public ReturnToPreviousNodeAction(DocumentRouteHeaderValue routeHeader, PrincipalContract principal, String annotation, String nodeName, boolean sendNotifications) { |
69 | 0 | super(KewApiConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD, routeHeader, principal, annotation); |
70 | 0 | this.nodeName = nodeName; |
71 | 0 | this.sendNotifications = sendNotifications; |
72 | 0 | } |
73 | |
|
74 | |
public ReturnToPreviousNodeAction(DocumentRouteHeaderValue routeHeader, PrincipalContract principal, String annotation, String nodeName, boolean sendNotifications, boolean runPostProcessorLogic) { |
75 | 0 | super(KewApiConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD, routeHeader, principal, annotation, runPostProcessorLogic); |
76 | 0 | this.nodeName = nodeName; |
77 | 0 | this.sendNotifications = sendNotifications; |
78 | 0 | } |
79 | |
|
80 | |
|
81 | |
|
82 | |
|
83 | |
protected ReturnToPreviousNodeAction(String overrideActionTakenCode, DocumentRouteHeaderValue routeHeader, PrincipalContract principal, String annotation, String nodeName, boolean sendNotifications) { |
84 | 0 | super(overrideActionTakenCode, routeHeader, principal, annotation); |
85 | 0 | this.nodeName = nodeName; |
86 | 0 | this.sendNotifications = sendNotifications; |
87 | 0 | } |
88 | |
|
89 | |
|
90 | |
|
91 | |
|
92 | |
public ReturnToPreviousNodeAction(String overrideActionTakenCode, DocumentRouteHeaderValue routeHeader, PrincipalContract principal, String annotation, String nodeName, boolean sendNotifications, boolean runPostProcessorLogic) { |
93 | 0 | super(overrideActionTakenCode, routeHeader, principal, annotation, runPostProcessorLogic); |
94 | 0 | this.nodeName = nodeName; |
95 | 0 | this.sendNotifications = sendNotifications; |
96 | 0 | } |
97 | |
|
98 | |
|
99 | |
|
100 | |
|
101 | |
private void revokePendingRequests(List<ActionRequestValue> pendingRequests, ActionTakenValue actionTaken, Recipient delegator) { |
102 | 0 | revokeRequests(pendingRequests); |
103 | 0 | getActionRequestService().deactivateRequests(actionTaken, pendingRequests); |
104 | 0 | if (sendNotifications) { |
105 | 0 | ActionRequestFactory arFactory = new ActionRequestFactory(getRouteHeader()); |
106 | 0 | List<ActionRequestValue> notificationRequests = arFactory.generateNotifications(pendingRequests, getPrincipal(), delegator, KewApiConstants.ACTION_REQUEST_FYI_REQ, getActionTakenCode()); |
107 | 0 | getActionRequestService().activateRequests(notificationRequests); |
108 | |
} |
109 | 0 | } |
110 | |
|
111 | |
|
112 | |
|
113 | |
|
114 | |
private void revokeRequests(List<ActionRequestValue> actionRequests) { |
115 | 0 | for (Iterator<ActionRequestValue> iterator = actionRequests.iterator(); iterator.hasNext();) { |
116 | 0 | ActionRequestValue actionRequest = iterator.next(); |
117 | 0 | actionRequest.setCurrentIndicator(Boolean.FALSE); |
118 | 0 | if (actionRequest.getActionTaken() != null) { |
119 | 0 | actionRequest.getActionTaken().setCurrentIndicator(Boolean.FALSE); |
120 | 0 | KEWServiceLocator.getActionTakenService().saveActionTaken(actionRequest.getActionTaken()); |
121 | |
} |
122 | 0 | revokeRequests(actionRequest.getChildrenRequests()); |
123 | 0 | KEWServiceLocator.getActionRequestService().saveActionRequest(actionRequest); |
124 | 0 | } |
125 | 0 | } |
126 | |
|
127 | |
private void processReturnToInitiator(RouteNodeInstance newNodeInstance) { |
128 | |
|
129 | 0 | RouteNode initialNode = newNodeInstance.getRouteNode().getDocumentType().getPrimaryProcess().getInitialRouteNode(); |
130 | 0 | if (newNodeInstance.getRouteNode().getRouteNodeId().equals(initialNode.getRouteNodeId())) { |
131 | 0 | LOG.debug("Document was returned to initiator"); |
132 | 0 | ActionRequestFactory arFactory = new ActionRequestFactory(getRouteHeader(), newNodeInstance); |
133 | 0 | ActionRequestValue notificationRequest = arFactory.createNotificationRequest(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, getRouteHeader().getInitiatorPrincipal(), getActionTakenCode(), getPrincipal(), "Document initiator"); |
134 | 0 | getActionRequestService().activateRequest(notificationRequest); |
135 | |
} |
136 | 0 | } |
137 | |
|
138 | |
|
139 | |
|
140 | |
|
141 | |
@Override |
142 | |
public String validateActionRules() { |
143 | 0 | return validateActionRules(getActionRequestService().findAllPendingRequests(routeHeader.getDocumentId())); |
144 | |
} |
145 | |
|
146 | |
public String validateActionRules(List<ActionRequestValue> actionRequests) { |
147 | 0 | if (!getRouteHeader().isValidActionToTake(getActionPerformedCode())) { |
148 | 0 | String docStatus = getRouteHeader().getDocRouteStatus(); |
149 | 0 | return "Document of status '" + docStatus + "' cannot taken action '" + KewApiConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS + "' to node name "+nodeName; |
150 | |
} |
151 | 0 | List<ActionRequestValue> filteredActionRequests = filterActionRequestsByCode(actionRequests, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ); |
152 | 0 | if (! isActionCompatibleRequest(filteredActionRequests) && ! isSuperUserUsage()) { |
153 | 0 | return "No request for the user is compatible with the RETURN TO PREVIOUS NODE action"; |
154 | |
} |
155 | 0 | return ""; |
156 | |
} |
157 | |
|
158 | |
|
159 | |
|
160 | |
|
161 | |
@Override |
162 | |
public boolean isActionCompatibleRequest(List<ActionRequestValue> requests) { |
163 | 0 | String actionTakenCode = getActionPerformedCode(); |
164 | |
|
165 | |
|
166 | 0 | if (KewApiConstants.ACTION_TAKEN_MOVE_CD.equals(actionTakenCode)) { |
167 | 0 | return true; |
168 | |
} |
169 | |
|
170 | |
|
171 | 0 | if (routeHeader.isStateInitiated() || routeHeader.isStateSaved()) { |
172 | 0 | return true; |
173 | |
} |
174 | |
|
175 | 0 | boolean actionCompatible = false; |
176 | 0 | Iterator<ActionRequestValue> ars = requests.iterator(); |
177 | 0 | ActionRequestValue actionRequest = null; |
178 | |
|
179 | 0 | while (ars.hasNext()) { |
180 | 0 | actionRequest = ars.next(); |
181 | |
|
182 | |
|
183 | |
|
184 | |
|
185 | |
|
186 | |
|
187 | |
|
188 | 0 | String request = actionRequest.getActionRequested(); |
189 | |
|
190 | 0 | if ( (KewApiConstants.ACTION_REQUEST_FYI_REQ.equals(request)) || |
191 | |
(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ.equals(request)) || |
192 | |
(KewApiConstants.ACTION_REQUEST_APPROVE_REQ.equals(request)) || |
193 | |
(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ.equals(request)) ) { |
194 | 0 | actionCompatible = true; |
195 | 0 | break; |
196 | |
} |
197 | |
|
198 | |
|
199 | 0 | if (KewApiConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD.equals(actionTakenCode) && |
200 | |
(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ.equals(request) || KewApiConstants.ACTION_REQUEST_APPROVE_REQ.equals(request))) { |
201 | 0 | actionCompatible = true; |
202 | |
} |
203 | 0 | } |
204 | |
|
205 | 0 | return actionCompatible; |
206 | |
} |
207 | |
|
208 | |
public void recordAction() throws InvalidActionTakenException { |
209 | 0 | MDC.put("docId", getRouteHeader().getDocumentId()); |
210 | 0 | updateSearchableAttributesIfPossible(); |
211 | 0 | LOG.debug("Returning document " + getRouteHeader().getDocumentId() + " to previous node: " + nodeName + ", annotation: " + annotation); |
212 | |
|
213 | 0 | List actionRequests = getActionRequestService().findAllValidRequests(getPrincipal().getPrincipalId(), getDocumentId(), KewApiConstants.ACTION_REQUEST_COMPLETE_REQ); |
214 | 0 | String errorMessage = validateActionRules(actionRequests); |
215 | 0 | if (!org.apache.commons.lang.StringUtils.isEmpty(errorMessage)) { |
216 | 0 | throw new InvalidActionTakenException(errorMessage); |
217 | |
} |
218 | |
|
219 | 0 | Collection activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(getRouteHeader().getDocumentId()); |
220 | 0 | NodeGraphSearchCriteria criteria = new NodeGraphSearchCriteria(NodeGraphSearchCriteria.SEARCH_DIRECTION_BACKWARD, activeNodeInstances, nodeName); |
221 | 0 | NodeGraphSearchResult result = KEWServiceLocator.getRouteNodeService().searchNodeGraph(criteria); |
222 | 0 | validateReturnPoint(nodeName, activeNodeInstances, result); |
223 | |
|
224 | 0 | LOG.debug("Record the returnToPreviousNode action"); |
225 | 0 | Recipient delegator = findDelegatorForActionRequests(actionRequests); |
226 | 0 | ActionTakenValue actionTaken = saveActionTaken(Boolean.FALSE, delegator); |
227 | |
|
228 | 0 | LOG.debug("Finding requests in return path and setting current indicator to FALSE"); |
229 | 0 | List<ActionRequestValue> doneRequests = new ArrayList<ActionRequestValue>(); |
230 | 0 | List<ActionRequestValue> pendingRequests = new ArrayList<ActionRequestValue>(); |
231 | 0 | for (RouteNodeInstance nodeInstance : (List<RouteNodeInstance>)result.getPath()) { |
232 | |
|
233 | 0 | KEWServiceLocator.getRouteNodeService().revokeNodeInstance(getRouteHeader(), nodeInstance); |
234 | 0 | List<ActionRequestValue> nodeRequests = getActionRequestService().findRootRequestsByDocIdAtRouteNode(getRouteHeader().getDocumentId(), nodeInstance.getRouteNodeInstanceId()); |
235 | 0 | for (ActionRequestValue request : nodeRequests) { |
236 | 0 | if (request.isDone()) { |
237 | 0 | doneRequests.add(request); |
238 | |
} else { |
239 | 0 | pendingRequests.add(request); |
240 | |
} |
241 | |
} |
242 | 0 | } |
243 | 0 | revokeRequests(doneRequests); |
244 | 0 | LOG.debug("Change pending requests to FYI and activate for docId " + getRouteHeader().getDocumentId()); |
245 | 0 | revokePendingRequests(pendingRequests, actionTaken, delegator); |
246 | 0 | notifyActionTaken(actionTaken); |
247 | 0 | executeNodeChange(activeNodeInstances, result); |
248 | 0 | } |
249 | |
|
250 | |
|
251 | |
|
252 | |
|
253 | |
|
254 | |
private void validateReturnPoint(String nodeName, Collection activeNodeInstances, NodeGraphSearchResult result) throws InvalidActionTakenException { |
255 | 0 | RouteNodeInstance resultNodeInstance = result.getResultNodeInstance(); |
256 | 0 | if (result.getResultNodeInstance() == null) { |
257 | 0 | throw new InvalidActionTakenException("Could not locate return point for node name '"+nodeName+"'."); |
258 | |
} |
259 | 0 | assertValidNodeType(resultNodeInstance); |
260 | 0 | assertValidBranch(resultNodeInstance, activeNodeInstances); |
261 | 0 | assertValidProcess(resultNodeInstance, activeNodeInstances); |
262 | 0 | assertFinalApprovalNodeNotInPath(result.getPath()); |
263 | 0 | } |
264 | |
|
265 | |
private void assertValidNodeType(RouteNodeInstance resultNodeInstance) throws InvalidActionTakenException { |
266 | |
|
267 | 0 | if (!helper.isSimpleNode(resultNodeInstance.getRouteNode()) && !helper.isSplitNode(resultNodeInstance.getRouteNode())) { |
268 | 0 | throw new InvalidActionTakenException("Can only return to a simple or a split node, attempting to return to " + resultNodeInstance.getRouteNode().getNodeType()); |
269 | |
} |
270 | 0 | } |
271 | |
|
272 | |
private void assertValidBranch(RouteNodeInstance resultNodeInstance, Collection activeNodeInstances) throws InvalidActionTakenException { |
273 | |
|
274 | 0 | boolean inValidBranch = false; |
275 | 0 | if (resultNodeInstance.getBranch().getParentBranch() == null) { |
276 | 0 | inValidBranch = true; |
277 | |
} else { |
278 | 0 | for (Iterator iterator = activeNodeInstances.iterator(); iterator.hasNext(); ) { |
279 | 0 | RouteNodeInstance nodeInstance = (RouteNodeInstance) iterator.next(); |
280 | 0 | if (nodeInstance.getBranch().getBranchId().equals(resultNodeInstance.getBranch().getBranchId())) { |
281 | 0 | inValidBranch = true; |
282 | 0 | break; |
283 | |
} |
284 | 0 | } |
285 | |
} |
286 | 0 | if (!inValidBranch) { |
287 | 0 | throw new InvalidActionTakenException("Returning to an illegal branch, can only return to node within the same branch as an active node or to the primary branch."); |
288 | |
} |
289 | 0 | } |
290 | |
|
291 | |
private void assertValidProcess(RouteNodeInstance resultNodeInstance, Collection activeNodeInstances) throws InvalidActionTakenException { |
292 | |
|
293 | 0 | if (resultNodeInstance.isInProcess()) { |
294 | 0 | boolean inValidProcess = false; |
295 | 0 | for (Iterator iterator = activeNodeInstances.iterator(); iterator.hasNext(); ) { |
296 | 0 | RouteNodeInstance nodeInstance = (RouteNodeInstance) iterator.next(); |
297 | 0 | if (nodeInstance.isInProcess() && nodeInstance.getProcess().getRouteNodeInstanceId().equals(nodeInstance.getProcess().getRouteNodeInstanceId())) { |
298 | 0 | inValidProcess = true; |
299 | 0 | break; |
300 | |
} |
301 | 0 | } |
302 | 0 | if (!inValidProcess) { |
303 | 0 | throw new InvalidActionTakenException("Returning into an illegal process, cannot return to node within a previously executing process."); |
304 | |
} |
305 | |
} |
306 | 0 | } |
307 | |
|
308 | |
|
309 | |
|
310 | |
|
311 | |
|
312 | |
|
313 | |
private void assertFinalApprovalNodeNotInPath(List path) throws InvalidActionTakenException { |
314 | 0 | for (Iterator iterator = path.iterator(); iterator.hasNext(); ) { |
315 | 0 | RouteNodeInstance nodeInstance = (RouteNodeInstance ) iterator.next(); |
316 | |
|
317 | 0 | if (nodeInstance.isComplete() && Boolean.TRUE.equals(nodeInstance.getRouteNode().getFinalApprovalInd())) { |
318 | 0 | throw new InvalidActionTakenException("Cannot return past or through the final approval node '"+nodeInstance.getName()+"'."); |
319 | |
} |
320 | 0 | } |
321 | 0 | } |
322 | |
|
323 | |
private void executeNodeChange(Collection activeNodes, NodeGraphSearchResult result) throws InvalidActionTakenException { |
324 | 0 | Integer oldRouteLevel = null; |
325 | 0 | Integer newRouteLevel = null; |
326 | 0 | if (CompatUtils.isRouteLevelCompatible(getRouteHeader())) { |
327 | 0 | int returnPathLength = result.getPath().size()-1; |
328 | 0 | oldRouteLevel = getRouteHeader().getDocRouteLevel(); |
329 | 0 | newRouteLevel = oldRouteLevel - returnPathLength; |
330 | 0 | LOG.debug("Changing route header "+ getRouteHeader().getDocumentId()+" route level for backward compatibility to "+newRouteLevel); |
331 | 0 | getRouteHeader().setDocRouteLevel(newRouteLevel); |
332 | 0 | KEWServiceLocator.getRouteHeaderService().saveRouteHeader(routeHeader); |
333 | |
} |
334 | 0 | List<RouteNodeInstance> startingNodes = determineStartingNodes(result.getPath(), activeNodes); |
335 | 0 | RouteNodeInstance newNodeInstance = materializeReturnPoint(startingNodes, result); |
336 | 0 | for (RouteNodeInstance activeNode : startingNodes) |
337 | |
{ |
338 | 0 | notifyNodeChange(oldRouteLevel, newRouteLevel, activeNode, newNodeInstance); |
339 | |
} |
340 | 0 | processReturnToInitiator(newNodeInstance); |
341 | 0 | } |
342 | |
|
343 | |
private void notifyNodeChange(Integer oldRouteLevel, Integer newRouteLevel, RouteNodeInstance oldNodeInstance, RouteNodeInstance newNodeInstance) throws InvalidActionTakenException { |
344 | |
try { |
345 | 0 | LOG.debug("Notifying post processor of route node change '"+oldNodeInstance.getName()+"'->'"+newNodeInstance.getName()); |
346 | 0 | PostProcessor postProcessor = routeHeader.getDocumentType().getPostProcessor(); |
347 | 0 | KEWServiceLocator.getRouteHeaderService().saveRouteHeader(getRouteHeader()); |
348 | 0 | DocumentRouteLevelChange routeNodeChange = new DocumentRouteLevelChange(routeHeader.getDocumentId(), |
349 | |
routeHeader.getAppDocId(), |
350 | |
oldRouteLevel, newRouteLevel, |
351 | |
oldNodeInstance.getName(), newNodeInstance.getName(), |
352 | |
oldNodeInstance.getRouteNodeInstanceId(), newNodeInstance.getRouteNodeInstanceId()); |
353 | 0 | ProcessDocReport report = postProcessor.doRouteLevelChange(routeNodeChange); |
354 | 0 | setRouteHeader(KEWServiceLocator.getRouteHeaderService().getRouteHeader(getDocumentId())); |
355 | 0 | if (!report.isSuccess()) { |
356 | 0 | LOG.warn(report.getMessage(), report.getProcessException()); |
357 | 0 | throw new InvalidActionTakenException(report.getMessage()); |
358 | |
} |
359 | 0 | } catch (Exception ex) { |
360 | 0 | throw new WorkflowRuntimeException(ex.getMessage()); |
361 | 0 | } |
362 | 0 | } |
363 | |
|
364 | |
private List<RouteNodeInstance> determineStartingNodes(List path, Collection<RouteNodeInstance> activeNodes) { |
365 | 0 | List<RouteNodeInstance> startingNodes = new ArrayList<RouteNodeInstance>(); |
366 | 0 | for (RouteNodeInstance activeNodeInstance : activeNodes) |
367 | |
{ |
368 | 0 | if (isInPath(activeNodeInstance, path)) |
369 | |
{ |
370 | 0 | startingNodes.add(activeNodeInstance); |
371 | |
} |
372 | |
} |
373 | 0 | return startingNodes; |
374 | |
} |
375 | |
|
376 | |
private boolean isInPath(RouteNodeInstance nodeInstance, List<RouteNodeInstance> path) { |
377 | 0 | for (RouteNodeInstance pathNodeInstance : path) |
378 | |
{ |
379 | 0 | if (pathNodeInstance.getRouteNodeInstanceId().equals(nodeInstance.getRouteNodeInstanceId())) |
380 | |
{ |
381 | 0 | return true; |
382 | |
} |
383 | |
} |
384 | 0 | return false; |
385 | |
} |
386 | |
|
387 | |
private RouteNodeInstance materializeReturnPoint(Collection<RouteNodeInstance> startingNodes, NodeGraphSearchResult result) { |
388 | 0 | RouteNodeService nodeService = KEWServiceLocator.getRouteNodeService(); |
389 | 0 | RouteNodeInstance returnInstance = result.getResultNodeInstance(); |
390 | 0 | RouteNodeInstance newNodeInstance = helper.getNodeFactory().createRouteNodeInstance(getDocumentId(), returnInstance.getRouteNode()); |
391 | 0 | newNodeInstance.setBranch(returnInstance.getBranch()); |
392 | 0 | newNodeInstance.setProcess(returnInstance.getProcess()); |
393 | 0 | newNodeInstance.setComplete(false); |
394 | 0 | newNodeInstance.setActive(true); |
395 | 0 | nodeService.save(newNodeInstance); |
396 | 0 | for (RouteNodeInstance activeNodeInstance : startingNodes) { |
397 | |
|
398 | 0 | activeNodeInstance.setComplete(true); |
399 | 0 | activeNodeInstance.setActive(false); |
400 | 0 | activeNodeInstance.setInitial(false); |
401 | 0 | activeNodeInstance.addNextNodeInstance(newNodeInstance); |
402 | |
} |
403 | 0 | for (RouteNodeInstance activeNodeInstance : startingNodes) |
404 | |
{ |
405 | 0 | nodeService.save(activeNodeInstance); |
406 | |
} |
407 | |
|
408 | |
|
409 | |
|
410 | 0 | return newNodeInstance; |
411 | |
} |
412 | |
|
413 | |
public boolean isSuperUserUsage() { |
414 | 0 | return superUserUsage; |
415 | |
} |
416 | |
public void setSuperUserUsage(boolean superUserUsage) { |
417 | 0 | this.superUserUsage = superUserUsage; |
418 | 0 | } |
419 | |
|
420 | |
} |