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