1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.workflow.service.impl;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.commons.lang.time.StopWatch;
20 import org.kuali.rice.core.api.CoreApiServiceLocator;
21 import org.kuali.rice.core.api.exception.RiceRuntimeException;
22 import org.kuali.rice.core.api.util.RiceKeyConstants;
23 import org.kuali.rice.kew.api.KewApiServiceLocator;
24 import org.kuali.rice.kew.api.WorkflowDocument;
25 import org.kuali.rice.kew.api.WorkflowDocumentFactory;
26 import org.kuali.rice.kew.api.action.ActionRequestType;
27 import org.kuali.rice.kew.api.action.ActionType;
28 import org.kuali.rice.kew.api.document.node.RouteNodeInstance;
29 import org.kuali.rice.kew.api.exception.WorkflowException;
30 import org.kuali.rice.kew.api.exception.InvalidActionTakenException;
31 import org.kuali.rice.kew.api.KewApiConstants;
32 import org.kuali.rice.kim.api.group.Group;
33 import org.kuali.rice.kim.api.identity.Person;
34 import org.kuali.rice.kim.api.identity.principal.Principal;
35 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
36 import org.kuali.rice.krad.bo.AdHocRoutePerson;
37 import org.kuali.rice.krad.bo.AdHocRouteRecipient;
38 import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
39 import org.kuali.rice.krad.exception.UnknownDocumentIdException;
40 import org.kuali.rice.krad.service.KRADServiceLocator;
41 import org.kuali.rice.krad.util.GlobalVariables;
42 import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
43 import org.springframework.transaction.annotation.Transactional;
44
45 import java.text.MessageFormat;
46 import java.util.ArrayList;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Set;
50
51
52
53
54
55
56
57 @Transactional
58 public class WorkflowDocumentServiceImpl implements WorkflowDocumentService {
59 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(WorkflowDocumentServiceImpl.class);
60
61 @Override
62 public boolean workflowDocumentExists(String documentId) {
63 boolean exists = false;
64
65 if (StringUtils.isBlank(documentId)) {
66 throw new IllegalArgumentException("invalid (blank) documentId");
67 }
68
69 exists = KewApiServiceLocator.getWorkflowDocumentService().doesDocumentExist(documentId);
70
71 return exists;
72 }
73
74 @Override
75 public WorkflowDocument createWorkflowDocument(String documentTypeName, Person person) {
76 String watchName = "createWorkflowDocument";
77 StopWatch watch = new StopWatch();
78 watch.start();
79 if (LOG.isDebugEnabled()) {
80 LOG.debug(watchName + ": started");
81 }
82 if (StringUtils.isBlank(documentTypeName)) {
83 throw new IllegalArgumentException("invalid (blank) documentTypeName");
84 }
85 if (person == null) {
86 throw new IllegalArgumentException("invalid (null) person");
87 }
88
89 if (StringUtils.isBlank(person.getPrincipalName())) {
90 throw new IllegalArgumentException("invalid (empty) PrincipalName");
91 }
92
93 if (LOG.isDebugEnabled()) {
94 LOG.debug("creating workflowDoc(" + documentTypeName + "," + person.getPrincipalName() + ")");
95 }
96
97 WorkflowDocument document = WorkflowDocumentFactory.createDocument(person.getPrincipalId(), documentTypeName);
98 watch.stop();
99 if (LOG.isDebugEnabled()) {
100 LOG.debug(watchName + ": " + watch.toString());
101 }
102
103 return document;
104 }
105
106 @Override
107 public WorkflowDocument loadWorkflowDocument(String documentId, Person user) {
108 if (documentId == null) {
109 throw new IllegalArgumentException("invalid (null) documentHeaderId");
110 }
111 if (user == null) {
112 throw new IllegalArgumentException("invalid (null) workflowUser");
113 }
114 else if (StringUtils.isEmpty(user.getPrincipalName())) {
115 throw new IllegalArgumentException("invalid (empty) workflowUser");
116 }
117
118 if (LOG.isDebugEnabled()) {
119 LOG.debug("retrieving document(" + documentId + "," + user.getPrincipalName() + ")");
120 }
121
122 try {
123 return WorkflowDocumentFactory.loadDocument(user.getPrincipalId(), documentId);
124 } catch (IllegalArgumentException e) {
125
126 throw new UnknownDocumentIdException("unable to locate document with documentHeaderId '" + documentId + "'");
127 }
128 }
129
130 @Override
131 public void acknowledge(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
132 if (LOG.isDebugEnabled()) {
133 LOG.debug("acknowleding document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
134 }
135
136 handleAdHocRouteRequests(workflowDocument, annotation, filterAdHocRecipients(adHocRecipients, new String[] { KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KewApiConstants.ACTION_REQUEST_FYI_REQ }));
137 workflowDocument.acknowledge(annotation);
138 }
139
140 @Override
141 public void approve(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
142 if (LOG.isDebugEnabled()) {
143 LOG.debug("approving document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
144 }
145
146 handleAdHocRouteRequests(workflowDocument, annotation, filterAdHocRecipients(adHocRecipients, new String[] { KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KewApiConstants.ACTION_REQUEST_FYI_REQ, KewApiConstants.ACTION_REQUEST_APPROVE_REQ }));
147 workflowDocument.approve(annotation);
148 }
149
150
151 @Override
152 public void superUserApprove(WorkflowDocument workflowDocument, String annotation) throws WorkflowException {
153 if ( LOG.isInfoEnabled() ) {
154 LOG.info("super user approve document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
155 }
156 workflowDocument.superUserBlanketApprove(annotation);
157 }
158
159 @Override
160 public void superUserCancel(WorkflowDocument workflowDocument, String annotation) throws WorkflowException {
161 LOG.info("super user cancel document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
162 workflowDocument.superUserCancel(annotation);
163 }
164
165 @Override
166 public void superUserDisapprove(WorkflowDocument workflowDocument, String annotation) throws WorkflowException {
167 if ( LOG.isInfoEnabled() ) {
168 LOG.info("super user disapprove document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
169 }
170 workflowDocument.superUserDisapprove(annotation);
171 }
172
173 @Override
174 public void blanketApprove(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
175 if (LOG.isDebugEnabled()) {
176 LOG.debug("blanket approving document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
177 }
178
179 handleAdHocRouteRequests(workflowDocument, annotation, filterAdHocRecipients(adHocRecipients, new String[] { KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KewApiConstants.ACTION_REQUEST_FYI_REQ }));
180 workflowDocument.blanketApprove(annotation);
181 }
182
183 @Override
184 public void cancel(WorkflowDocument workflowDocument, String annotation) throws WorkflowException {
185 if (LOG.isDebugEnabled()) {
186 LOG.debug("canceling document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
187 }
188
189 workflowDocument.cancel(annotation);
190 }
191
192 @Override
193 public void recall(WorkflowDocument workflowDocument, String annotation, boolean cancel) throws WorkflowException {
194 if (LOG.isDebugEnabled()) {
195 LOG.debug("recalling document(" + workflowDocument.getDocumentId() + ",'" + annotation + "', '" + cancel + "')");
196 }
197
198 workflowDocument.recall(annotation, cancel);
199 }
200
201 @Override
202 public void clearFyi(WorkflowDocument workflowDocument, List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
203 if (LOG.isDebugEnabled()) {
204 LOG.debug("clearing FYI for document(" + workflowDocument.getDocumentId() + ")");
205 }
206
207 handleAdHocRouteRequests(workflowDocument, "", filterAdHocRecipients(adHocRecipients, new String[] { KewApiConstants.ACTION_REQUEST_FYI_REQ }));
208 workflowDocument.fyi();
209 }
210
211 @Override
212 public void sendWorkflowNotification(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
213 sendWorkflowNotification(workflowDocument, annotation, adHocRecipients, null);
214 }
215
216 @Override
217 public void sendWorkflowNotification(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients, String notificationLabel) throws WorkflowException {
218 if (LOG.isDebugEnabled()) {
219 LOG.debug("sending FYI for document(" + workflowDocument.getDocumentId() + ")");
220 }
221
222 handleAdHocRouteRequests(workflowDocument, annotation, adHocRecipients, notificationLabel);
223 }
224
225 @Override
226 public void disapprove(WorkflowDocument workflowDocument, String annotation) throws WorkflowException {
227 if (LOG.isDebugEnabled()) {
228 LOG.debug("disapproving document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
229 }
230
231 workflowDocument.disapprove(annotation);
232 }
233
234 @Override
235 public void route(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
236 if (LOG.isDebugEnabled()) {
237 LOG.debug("routing document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
238 }
239
240 handleAdHocRouteRequests(workflowDocument, annotation, filterAdHocRecipients(adHocRecipients, new String[] { KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KewApiConstants.ACTION_REQUEST_FYI_REQ, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ }));
241 workflowDocument.route(annotation);
242 }
243
244 @Override
245 public void save(WorkflowDocument workflowDocument, String annotation) throws WorkflowException {
246 if (workflowDocument.isValidAction(ActionType.SAVE)) {
247 if (LOG.isDebugEnabled()) {
248 LOG.debug("saving document(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
249 }
250
251 workflowDocument.saveDocument(annotation);
252 }
253 else {
254 this.saveRoutingData(workflowDocument);
255 }
256 }
257
258 @Override
259 public void saveRoutingData(WorkflowDocument workflowDocument) throws WorkflowException {
260 if (LOG.isDebugEnabled()) {
261 LOG.debug("saving document(" + workflowDocument.getDocumentId() + ")");
262 }
263
264 workflowDocument.saveDocumentData();
265 }
266
267 @Override
268 public String getCurrentRouteLevelName(WorkflowDocument workflowDocument) throws WorkflowException {
269 if (LOG.isDebugEnabled()) {
270 LOG.debug("getting current route level name for document(" + workflowDocument.getDocumentId());
271 }
272
273 WorkflowDocument freshCopyWorkflowDoc = loadWorkflowDocument(workflowDocument.getDocumentId(), GlobalVariables.getUserSession().getPerson());
274 return getCurrentRouteNodeNames(freshCopyWorkflowDoc);
275 }
276
277
278
279 @Override
280 public String getCurrentRouteNodeNames(WorkflowDocument workflowDocument) {
281 Set<String> nodeNames = workflowDocument.getNodeNames();
282 if (nodeNames.isEmpty()) {
283 return "";
284 }
285 StringBuilder builder = new StringBuilder();
286 for (String nodeName : nodeNames) {
287 builder.append(nodeName).append(", ");
288 }
289 builder.setLength(builder.length() - 2);
290 return builder.toString();
291 }
292
293 private void handleAdHocRouteRequests(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
294 handleAdHocRouteRequests(workflowDocument, annotation, adHocRecipients, null);
295 }
296
297
298
299
300
301
302
303
304 private void handleAdHocRouteRequests(WorkflowDocument workflowDocument, String annotation, List<AdHocRouteRecipient> adHocRecipients, String notificationLabel) throws WorkflowException {
305
306 if (adHocRecipients != null && adHocRecipients.size() > 0) {
307 String currentNode = null;
308 Set<String> currentNodes = workflowDocument.getNodeNames();
309 if (currentNodes.isEmpty()) {
310 List<RouteNodeInstance> nodes = KewApiServiceLocator.getWorkflowDocumentService().getTerminalRouteNodeInstances(
311 workflowDocument.getDocumentId());
312 currentNodes = new HashSet<String>();
313 for (RouteNodeInstance node : nodes) {
314 currentNodes.add(node.getName());
315 }
316 }
317
318 currentNode = currentNodes.iterator().next();
319
320 List<AdHocRoutePerson> adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
321 List<AdHocRouteWorkgroup> adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>();
322
323 for (AdHocRouteRecipient recipient : adHocRecipients) {
324 if (StringUtils.isNotEmpty(recipient.getId())) {
325 String newAnnotation = annotation;
326 if ( StringUtils.isBlank( annotation ) ) {
327 try {
328 String message = CoreApiServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
329 RiceKeyConstants.MESSAGE_ADHOC_ANNOTATION);
330 newAnnotation = MessageFormat.format(message, GlobalVariables.getUserSession().getPrincipalName() );
331 } catch ( Exception ex ) {
332 LOG.warn("Unable to set annotation", ex );
333 }
334 }
335 if (AdHocRouteRecipient.PERSON_TYPE.equals(recipient.getType())) {
336
337 Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(recipient.getName());
338 if (principal == null) {
339 if(((AdHocRoutePerson)recipient)!=null && ((AdHocRoutePerson)recipient).getPerson()!=null){
340 principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(((AdHocRoutePerson)recipient).getPerson().getPrincipalName());
341 }
342 if(principal == null){
343 throw new RiceRuntimeException("Could not locate principal with name '" + recipient.getId() + "'");
344 }
345 }
346 workflowDocument.adHocToPrincipal(ActionRequestType.fromCode(recipient.getActionRequested()), currentNode, newAnnotation, principal.getPrincipalId(), "", true, notificationLabel);
347 AdHocRoutePerson personRecipient = (AdHocRoutePerson)recipient;
348 adHocRoutePersons.add(personRecipient);
349 }
350 else {
351 Group group = KimApiServiceLocator.getGroupService().getGroup(recipient.getId());
352 if (group == null) {
353 throw new RiceRuntimeException("Could not locate group with id '" + recipient.getId() + "'");
354 }
355 workflowDocument.adHocToGroup(ActionRequestType.fromCode(recipient.getActionRequested()), currentNode, newAnnotation, group.getId() , "", true, notificationLabel);
356 AdHocRouteWorkgroup groupRecipient = (AdHocRouteWorkgroup)recipient;
357 adHocRouteWorkgroups.add(groupRecipient);
358 }
359 }
360 }
361 KRADServiceLocator.getBusinessObjectService().delete(adHocRoutePersons);
362 KRADServiceLocator.getBusinessObjectService().delete(adHocRouteWorkgroups);
363 }
364 }
365
366
367
368
369
370
371
372 private List<AdHocRouteRecipient> filterAdHocRecipients(List<AdHocRouteRecipient> adHocRecipients, String[] validTypes) {
373
374 List<AdHocRouteRecipient> realAdHocRecipients = new ArrayList<AdHocRouteRecipient>();
375 if (adHocRecipients != null) {
376 for (AdHocRouteRecipient proposedRecipient : adHocRecipients) {
377 if (StringUtils.isNotBlank(proposedRecipient.getActionRequested())) {
378 for (int i = 0; i < validTypes.length; i++) {
379 if (validTypes[i].equals(proposedRecipient.getActionRequested())) {
380 realAdHocRecipients.add(proposedRecipient);
381 }
382 }
383 }
384 }
385 }
386 return realAdHocRecipients;
387 }
388
389
390
391
392
393
394 public void complete(WorkflowDocument workflowDocument, String annotation, List adHocRecipients) throws WorkflowException {
395 if (LOG.isDebugEnabled()) {
396 LOG.debug("routing flexDoc(" + workflowDocument.getDocumentId() + ",'" + annotation + "')");
397 }
398 handleAdHocRouteRequests(workflowDocument, annotation, filterAdHocRecipients(adHocRecipients, new String[] { KewApiConstants.ACTION_REQUEST_COMPLETE_REQ,KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KewApiConstants.ACTION_REQUEST_FYI_REQ, KewApiConstants.ACTION_REQUEST_APPROVE_REQ }));
399 workflowDocument.complete(annotation);
400 }
401 }