001package org.kuali.ole.olekrad.service; 002 003import org.apache.commons.lang.StringUtils; 004import org.apache.commons.lang.time.StopWatch; 005import org.kuali.rice.core.api.CoreApiServiceLocator; 006import org.kuali.rice.core.api.exception.RiceRuntimeException; 007import org.kuali.rice.core.api.util.RiceKeyConstants; 008import org.kuali.rice.kew.api.KewApiConstants; 009import org.kuali.rice.kew.api.KewApiServiceLocator; 010import org.kuali.rice.kew.api.WorkflowDocument; 011import org.kuali.rice.kew.api.WorkflowDocumentFactory; 012import org.kuali.rice.kew.api.action.ActionRequestType; 013import org.kuali.rice.kew.api.action.ActionType; 014import org.kuali.rice.kew.api.document.node.RouteNodeInstance; 015import org.kuali.rice.kew.api.exception.WorkflowException; 016import org.kuali.rice.kim.api.group.Group; 017import org.kuali.rice.kim.api.identity.Person; 018import org.kuali.rice.kim.api.identity.principal.Principal; 019import org.kuali.rice.kim.api.services.KimApiServiceLocator; 020import org.kuali.rice.krad.bo.AdHocRoutePerson; 021import org.kuali.rice.krad.bo.AdHocRouteRecipient; 022import org.kuali.rice.krad.bo.AdHocRouteWorkgroup; 023import org.kuali.rice.krad.exception.UnknownDocumentIdException; 024import org.kuali.rice.krad.service.KRADServiceLocator; 025import org.kuali.rice.krad.util.GlobalVariables; 026import org.kuali.rice.krad.workflow.service.impl.WorkflowDocumentServiceImpl; 027import org.springframework.transaction.annotation.Transactional; 028 029import java.text.MessageFormat; 030import java.util.ArrayList; 031import java.util.HashSet; 032import java.util.List; 033import java.util.Set; 034 035/** 036 * Created with IntelliJ IDEA. 037 * User: sheiksalahudeenm 038 * Date: 12/16/13 039 * Time: 4:08 PM 040 * To change this template use File | Settings | File Templates. 041 */ 042public class OLEWorkflowDocumentServiceImpl extends WorkflowDocumentServiceImpl { 043 044 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(OLEWorkflowDocumentServiceImpl.class); 045 046 @Override 047 public boolean workflowDocumentExists(String documentId) { 048 boolean exists = false; 049 050 if (StringUtils.isBlank(documentId)) { 051 throw new IllegalArgumentException("invalid (blank) documentId"); 052 } 053 054 exists = KewApiServiceLocator.getWorkflowDocumentService().doesDocumentExist(documentId); 055 056 return exists; 057 } 058 059 @Override 060 public WorkflowDocument createWorkflowDocument(String documentTypeName, Person person) { 061 String watchName = "createWorkflowDocument"; 062 StopWatch watch = new StopWatch(); 063 watch.start(); 064 if (LOG.isDebugEnabled()) { 065 LOG.debug(watchName + ": started"); 066 } 067 if (StringUtils.isBlank(documentTypeName)) { 068 throw new IllegalArgumentException("invalid (blank) documentTypeName"); 069 } 070 if (person == null) { 071 throw new IllegalArgumentException("invalid (null) person"); 072 } 073 074 if (StringUtils.isBlank(person.getPrincipalName())) { 075 throw new IllegalArgumentException("invalid (empty) PrincipalName"); 076 } 077 078 if (LOG.isDebugEnabled()) { 079 LOG.debug("creating workflowDoc(" + documentTypeName + "," + person.getPrincipalName() + ")"); 080 } 081 082 WorkflowDocument document = WorkflowDocumentFactory.createDocument(person.getPrincipalId(), documentTypeName); 083 watch.stop(); 084 if (LOG.isDebugEnabled()) { 085 LOG.debug(watchName + ": " + watch.toString()); 086 } 087 088 return document; 089 } 090 091 @Override 092 public WorkflowDocument loadWorkflowDocument(String documentId, Person user) { 093 if (documentId == null) { 094 throw new IllegalArgumentException("invalid (null) documentHeaderId"); 095 } 096 if (user == null) { 097 throw new IllegalArgumentException("invalid (null) workflowUser"); 098 } 099 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}