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