View Javadoc
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   * Created with IntelliJ IDEA.
37   * User: sheiksalahudeenm
38   * Date: 12/16/13
39   * Time: 4:08 PM
40   * To change this template use File | Settings | File Templates.
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             // TODO do we really need to do this or just let the IllegalArgument propogate?
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 //        return KEWServiceLocator.getRouteHeaderService().getRouteHeader(workflowDocument.getDocumentId()).getCurrentRouteLevelName();
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             // for now just pick a node and go with it...
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                         // TODO make the 1 a constant
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      * Convenience method to filter out any ad hoc recipients that should not be allowed given the action requested of the user that
342      * is taking action on the document
343      *
344      * @param adHocRecipients
345      */
346     private List<AdHocRouteRecipient> filterAdHocRecipients(List<AdHocRouteRecipient> adHocRecipients, String[] validTypes) {
347         // now filter out any but ack or fyi from the ad hoc list
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      * Completes workflow document
365      *
366      * @see org.kuali.rice.krad.workflow.service.WorkflowDocumentService#complete(org.kuali.rice.kew.api.WorkflowDocument, String, java.util.List)
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 }