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