1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.rice.kew.engine.node;
17  
18  import java.util.ArrayList;
19  import java.util.HashSet;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
27  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
28  import org.kuali.rice.kew.api.exception.WorkflowException;
29  import org.kuali.rice.kew.api.rule.Rule;
30  import org.kuali.rice.kew.engine.RouteContext;
31  import org.kuali.rice.kew.engine.RouteHelper;
32  import org.kuali.rice.kew.exception.RouteManagerException;
33  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
34  import org.kuali.rice.kew.rule.FlexRM;
35  import org.kuali.rice.kew.rule.KRAMetaRuleEngine;
36  import org.kuali.rice.kew.rule.RuleExpressionResult;
37  import org.kuali.rice.kew.service.KEWServiceLocator;
38  import org.kuali.rice.kew.util.Utilities;
39  
40  
41  
42  
43  
44  
45  
46  public class KRAMetaRuleNode extends IteratedRequestActivationNode {
47  	private static String SUPPRESS_POLICY_ERRORS_KEY = "_suppressPolicyErrorsRequestActivationNode";
48  
49  	protected List<ActionRequestValue> generateUninitializedRequests(ActionRequestFactory arFactory, RouteContext context, RuleExpressionResult result) throws WorkflowException {
50  		FlexRM flexRM = new FlexRM();
51  		flexRM.makeActionRequests(arFactory, result.getResponsibilities(), context, Rule.Builder.create(result.getRule().getDefinition()).build(), context.getDocument(), null, null);
52  		return new ArrayList<ActionRequestValue>(arFactory.getRequestGraphs());
53  	}
54  
55  	protected List<ActionRequestValue> initializeRequests(List<ActionRequestValue> requests, RouteContext context) {
56  		
57  		List<ActionRequestValue> newRequests = new ArrayList<ActionRequestValue>();
58  		for (Iterator iterator = requests.iterator(); iterator.hasNext();) {
59  			ActionRequestValue actionRequest = (ActionRequestValue) iterator.next();
60  			actionRequest = KEWServiceLocator.getActionRequestService().initializeActionRequestGraph(actionRequest, context.getDocument(), context.getNodeInstance());
61  			actionRequest = saveActionRequest(context, actionRequest);
62  			newRequests.add(actionRequest);
63  		}
64  		return newRequests;
65  	}
66  
67  	@Override
68  	protected boolean generateNewRequests(boolean initial, RouteContext context, RouteHelper routeHelper)
69  	throws WorkflowException, Exception {
70  		RouteNodeInstance nodeInstance = context.getNodeInstance();
71  		RouteNode nodeDef = nodeInstance.getRouteNode();
72  
73  		
74  		Map<String, String> cfg = Utilities.getKeyValueCollectionAsMap(nodeDef.getConfigParams());
75  		String expression = cfg.get("expression");
76  		if (StringUtils.isEmpty(expression)) {
77  			throw new WorkflowException("No meta-rule expression supplied in node " + nodeDef);
78  		}
79  		KRAMetaRuleEngine engine = new KRAMetaRuleEngine(expression);
80  
81  		
82  		int curStatement = getCurStatement(nodeInstance);
83  
84  		engine.setCurStatement(curStatement);
85  
86  		if (engine.isDone()) {
87  			return false;
88  		}
89  
90  		
91  		RuleExpressionResult result = engine.processSingleStatement(context);
92  		if (!result.isSuccess()) {
93  			return false;
94  		}
95  
96  		boolean suppressPolicyErrors = isSuppressingPolicyErrors(context);
97  		boolean pastFinalApprover = isPastFinalApprover(context.getDocument(), nodeInstance);
98  
99  		
100 		
101 		ActionRequestFactory arFactory = new ActionRequestFactory(context.getDocument(), context.getNodeInstance());
102 		List<ActionRequestValue> requests = generateUninitializedRequests(arFactory, context, result);
103 		requests = initializeRequests(requests, context);
104 		
105 		if ((requests.isEmpty()) && nodeDef.getMandatoryRouteInd().booleanValue() && ! suppressPolicyErrors) {
106 			LOG.warn("no requests generated for mandatory route - " + nodeDef.getRouteNodeName());
107 			throw new RouteManagerException("No requests generated for mandatory route " + nodeDef.getRouteNodeName() + ":" + nodeDef.getRouteMethodName(), context);
108 		}
109 		
110 		boolean hasApproveRequest = false;
111 		for (Iterator iter = requests.iterator(); iter.hasNext();) {
112 			ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
113 			hasApproveRequest = actionRequest.isApproveOrCompleteRequest() || hasApproveRequest;
114 		}
115 		
116 		if (nodeDef.getFinalApprovalInd().booleanValue()) {
117 			
118 			if (!hasApproveRequest && ! suppressPolicyErrors) {
119 				throw new RuntimeException("No Approve Request generated after final approver");
120 			}
121 		} else if (pastFinalApprover) {
122 			
123 			if (hasApproveRequest && ! suppressPolicyErrors) {
124 				throw new RuntimeException("Approve Request generated after final approver");
125 			}
126 		}
127 
128 		
129 		nodeInstance.getNodeState("stmt").setValue(String.valueOf(curStatement + 1));
130 		return !requests.isEmpty();
131 	}
132 
133 	
134 
135 
136 
137 	protected static int getCurStatement(RouteNodeInstance nodeInstance) {
138 		int statement = 0;
139 		NodeState nodeState = nodeInstance.getNodeState("stmt");
140 		if (nodeState == null) {
141 			nodeState = new NodeState();
142 			nodeState.setKey("stmt");
143 			nodeState.setNodeInstance(nodeInstance);
144 			nodeInstance.addNodeState(nodeState);
145 		}
146 		if (StringUtils.isEmpty(nodeState.getValue())) {
147 			nodeState.setValue("0");
148 		} else {
149 			statement = Integer.parseInt(nodeState.getValue());
150 		}
151 		return statement;
152 	}
153 
154 	@Override
155 	protected RequestFulfillmentCriteria getRequestFulfillmentCriteria(RouteContext routeContext) {
156 		return super.getRequestFulfillmentCriteria(routeContext);
157 	}
158 
159 
160 	
161 
162 	
163 
164 
165 	private Object getKey(RouteNodeInstance nodeInstance) {
166 		String id = nodeInstance.getRouteNodeInstanceId();
167 		return (id != null ? (Object) id : (Object) nodeInstance);
168 	}
169 
170 	
171 
172 
173 
174 	private boolean isPastFinalApprover(DocumentRouteHeaderValue document, RouteNodeInstance nodeInstance) {
175 		FinalApproverContext context = new FinalApproverContext();
176 		List revokedNodeInstances = KEWServiceLocator.getRouteNodeService().getRevokedNodeInstances(document);
177 		Set revokedNodeInstanceIds = new HashSet();
178 		for (Iterator iterator = revokedNodeInstances.iterator(); iterator.hasNext(); ) {
179 			RouteNodeInstance revokedNodeInstance = (RouteNodeInstance) iterator.next();
180 			revokedNodeInstanceIds.add(revokedNodeInstance.getRouteNodeInstanceId());
181 		}
182 		isPastFinalApprover(nodeInstance.getPreviousNodeInstances(), context, revokedNodeInstanceIds);
183 		return context.isPast;
184 	}
185 
186 	private void isPastFinalApprover(List previousNodeInstances, FinalApproverContext context, Set revokedNodeInstanceIds) {
187 		if (previousNodeInstances != null && !previousNodeInstances.isEmpty()) {
188 			for (Iterator iterator = previousNodeInstances.iterator(); iterator.hasNext();) {
189 				if (context.isPast) {
190 					return;
191 				}
192 				RouteNodeInstance nodeInstance = (RouteNodeInstance) iterator.next();
193 				if (context.inspected.contains(getKey(nodeInstance))) {
194 					continue;
195 				} else {
196 					context.inspected.add(getKey(nodeInstance));
197 				}
198 				if (Boolean.TRUE.equals(nodeInstance.getRouteNode().getFinalApprovalInd())) {
199 					
200 					
201 					
202 					if (!revokedNodeInstanceIds.contains(nodeInstance.getRouteNodeInstanceId())) {
203 						context.isPast = true;
204 					}
205 					return;
206 				}
207 				isPastFinalApprover(nodeInstance.getPreviousNodeInstances(), context, revokedNodeInstanceIds);
208 			}
209 		}
210 	}
211 	public static boolean isSuppressingPolicyErrors(RouteContext routeContext) {
212 		Boolean suppressPolicyErrors = (Boolean)routeContext.getParameters().get(SUPPRESS_POLICY_ERRORS_KEY);
213 		if (suppressPolicyErrors == null || ! suppressPolicyErrors) {
214 			return false;
215 		}
216 		return true;
217 	}
218 
219 	private class FinalApproverContext {
220 		public Set inspected = new HashSet();
221 		public boolean isPast = false;
222 	}
223 
224 }