View Javadoc

1   /*
2    * Copyright 2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 1.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl1.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.impl.document;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.LinkedHashSet;
22  import java.util.List;
23  import java.util.Set;
24  
25  import javax.jws.WebParam;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.log4j.Logger;
29  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
30  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
31  import org.kuali.rice.kew.actiontaken.ActionTakenValue;
32  import org.kuali.rice.kew.api.action.ActionRequest;
33  import org.kuali.rice.kew.api.action.ActionTaken;
34  import org.kuali.rice.kew.api.document.Document;
35  import org.kuali.rice.kew.api.document.DocumentContent;
36  import org.kuali.rice.kew.api.document.DocumentDetail;
37  import org.kuali.rice.kew.api.document.DocumentLink;
38  import org.kuali.rice.kew.api.document.RouteNodeInstance;
39  import org.kuali.rice.kew.api.document.WorkflowDocumentService;
40  import org.kuali.rice.kew.dto.DTOConverter;
41  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
42  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent;
43  import org.kuali.rice.kew.service.KEWServiceLocator;
44  
45  /**
46   * TODO
47   * 
48   * @author Kuali Rice Team (rice.collab@kuali.org)
49   *
50   */
51  public class WorkflowDocumentServiceImpl implements WorkflowDocumentService {
52  
53  	private static final Logger LOG = Logger.getLogger(WorkflowDocumentServiceImpl.class);
54  	
55  	@Override
56  	public Document getDocument(String documentId) {
57  		if (StringUtils.isBlank(documentId)) {
58  			throw new RiceIllegalArgumentException("documentId was blank or null");
59  		}
60  		DocumentRouteHeaderValue documentBo = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
61  		return DocumentRouteHeaderValue.to(documentBo);
62  	}
63  
64  	@Override
65  	public boolean doesDocumentExist(String documentId) {
66  	    if (StringUtils.isBlank(documentId)) {
67              throw new RiceIllegalArgumentException("documentId was blank or null");
68          }
69  	    DocumentRouteHeaderValue documentBo = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
70  	    return documentBo != null;
71  	}
72  	
73  	@Override
74  	public DocumentContent getDocumentContent(String documentId) {
75  		if (StringUtils.isBlank(documentId)) {
76  			throw new RiceIllegalArgumentException("documentId was blank or null");
77  		}
78  		DocumentRouteHeaderValueContent content = KEWServiceLocator.getRouteHeaderService().getContent(documentId);
79  		return DocumentRouteHeaderValueContent.to(content);
80  	}
81  
82  	@Override
83  	public List<ActionRequest> getRootActionRequests(String documentId) {
84  		List<ActionRequest> actionRequests = new ArrayList<ActionRequest>();
85  		List<ActionRequestValue> actionRequestBos = KEWServiceLocator.getActionRequestService().findAllRootActionRequestsByDocumentId(documentId);
86  		for (ActionRequestValue actionRequestBo : actionRequestBos) {
87  			actionRequests.add(ActionRequestValue.to(actionRequestBo));
88  		}
89  		return Collections.unmodifiableList(actionRequests);
90  	}
91  	
92  	@Override
93  	public List<ActionRequest> getActionRequests(String documentId, String nodeName, String principalId) {
94          if (StringUtils.isBlank(documentId)) {
95          	throw new RiceIllegalArgumentException("documentId was null or blank");
96          }
97          if ( LOG.isDebugEnabled() ) {
98          	LOG.debug("Fetching ActionRequests [docId="+documentId+", nodeName="+nodeName+", principalId="+principalId+"]");
99          }
100         List<ActionRequestValue> actionRequestBos = KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(documentId);
101         List<ActionRequestValue> matchingActionRequests = new ArrayList<ActionRequestValue>();
102         for (ActionRequestValue actionRequestValue : actionRequestBos) {
103             if (actionRequestMatches(actionRequestValue, nodeName, principalId)) {
104                 matchingActionRequests.add(actionRequestValue);
105             }
106         }
107         List<ActionRequest> actionRequests = new ArrayList<ActionRequest>(matchingActionRequests.size());
108         for (ActionRequestValue matchingActionRequest : matchingActionRequests) {
109         	actionRequests.add(ActionRequestValue.to(matchingActionRequest));
110         }
111         return actionRequests;
112     }
113 	
114 
115     protected boolean actionRequestMatches(ActionRequestValue actionRequest, String nodeName, String principalId) {
116         boolean matchesUserId = true;  // assume a match in case user is empty
117         boolean matchesNodeName = true;  // assume a match in case node name is empty
118         if (StringUtils.isNotBlank(nodeName)) {
119             matchesNodeName = nodeName.equals(actionRequest.getPotentialNodeName());
120         }
121         if (principalId != null) {
122             matchesUserId = actionRequest.isRecipientRoutedRequest(principalId);
123         }
124         return matchesNodeName && matchesUserId;
125     }
126 
127 
128 	@Override
129 	public List<ActionTaken> getActionsTaken(String documentId) {
130 		List<ActionTaken> actionTakens = new ArrayList<ActionTaken>();
131 		Collection<ActionTakenValue> actionTakenBos = KEWServiceLocator.getActionTakenService().findByDocumentId(documentId);
132 		for (ActionTakenValue actionTakenBo : actionTakenBos) {
133 			actionTakens.add(ActionTakenValue.to(actionTakenBo));
134 		}
135 		return actionTakens;
136 	}
137 	
138 	@Override
139 	public DocumentDetail getDocumentDetail(@WebParam(name = "documentId") String documentId) {
140 		if (StringUtils.isBlank(documentId)) {
141             throw new IllegalArgumentException("documentId was null or blank");
142         }
143         if ( LOG.isDebugEnabled() ) {
144         	LOG.debug("Fetching DocumentDetail [id="+documentId+"]");
145         }
146         DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
147         if (document == null) {
148         	return null;
149         }
150         DocumentDetail documentDetailVO = DTOConverter.convertDocumentDetailNew(document);
151         if ( LOG.isDebugEnabled() ) {
152         	LOG.debug("Returning DocumentDetailVO [id=" + documentId + "]");
153         }
154         return documentDetailVO;
155 	}
156 	
157 	@Override
158 	public List<RouteNodeInstance> getRouteNodeInstances(String documentId) {
159     	if ( LOG.isDebugEnabled() ) {
160     		LOG.debug("Fetching RouteNodeInstances [documentId=" + documentId + "]");
161     	}
162     	DocumentRouteHeaderValue documentBo = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
163     	if (documentBo == null) {
164     		return Collections.emptyList();
165     	}
166     	return convertRouteNodeInstances(KEWServiceLocator.getRouteNodeService().getFlattenedNodeInstances(documentBo, true));
167     }	
168 	
169 	@Override
170 	public List<RouteNodeInstance> getActiveRouteNodeInstances(String documentId) {
171 		if ( LOG.isDebugEnabled() ) {
172     		LOG.debug("Fetching active RouteNodeInstances [documentId=" + documentId + "]");
173     	}
174         return convertRouteNodeInstances(KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(documentId));
175 	}
176 
177 	private List<RouteNodeInstance> convertRouteNodeInstances(List<org.kuali.rice.kew.engine.node.RouteNodeInstance> routeNodeInstanceBos) {
178 		List<RouteNodeInstance> routeNodeInstances = new ArrayList<RouteNodeInstance>();
179         for (org.kuali.rice.kew.engine.node.RouteNodeInstance routeNodeInstanceBo : routeNodeInstanceBos) {
180         	routeNodeInstances.add(org.kuali.rice.kew.engine.node.RouteNodeInstance.to(routeNodeInstanceBo));
181         }
182         return Collections.unmodifiableList(routeNodeInstances);
183 	}
184 	
185 	@Override
186 	public List<String> getPreviousRouteNodeNames(String documentId) {
187 		if ( LOG.isDebugEnabled() ) {
188 			LOG.debug("Fetching previous node names [documentId=" + documentId + "]");
189 		}
190 		DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
191 		
192 		// TODO validate that the doc is null or not instead of just throwing NPE?
193 		
194 		//going conservative for now.  if the doc isn't enroute or exception nothing will be returned.
195 		if (document.isEnroute() || document.isInException()) {
196 
197 			// TODO: KULRICE-5329 verify that the rewrite of the numeric logic below is reasonable -- I'm guessing it's not -- this one's a fairly radical change since I had to throw
198 			// away the whole premise of using the longValue of the id as a strategy, so I think I'm massively oversimplifying the original goal of the logic
199 			List<org.kuali.rice.kew.engine.node.RouteNodeInstance> routeNodeInstances = KEWServiceLocator.getRouteNodeService().getFlattenedNodeInstances(document, false);
200 			Set<String> routeNodeNames = new LinkedHashSet<String>();
201 			if (routeNodeInstances != null) {
202 				for (org.kuali.rice.kew.engine.node.RouteNodeInstance routeNodeInstance : routeNodeInstances) {
203 					if (routeNodeInstance.isComplete()) {
204 						routeNodeNames.add(routeNodeInstance.getName());
205 					}
206 				}
207 			}
208 //			List<org.kuali.rice.kew.engine.node.RouteNodeInstance> activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document);
209 //			long largetActivatedNodeId = 0;
210 //			for (org.kuali.rice.kew.engine.node.RouteNodeInstance routeNodeInstance : activeNodeInstances) {
211 //				if (routeNodeInstance.getRouteNode().getRouteNodeId().longValue() > largetActivatedNodeId) {
212 //					largetActivatedNodeId = routeNodeInstance.getRouteNode().getRouteNodeId().longValue();
213 //				}
214 //			}
215 //
216 //			List<org.kuali.rice.kew.engine.node.RouteNodeInstance> routeNodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodeInstances(document, false);
217 //			List<String> nodeNames = new ArrayList<String>();
218 //
219 //			for (org.kuali.rice.kew.engine.node.RouteNodeInstance routeNode : routeNodes) {
220 //				if (routeNode.isComplete() && !nodeNames.contains(routeNode.getName())) {
221 //					//if the prototype of the nodeInstance we're analyzing is less than the largest id of all our active prototypes
222 //					//then add it to the list.  This is an attempt to account for return to previous hitting a single node multiple times
223 //					if (routeNode.getRouteNode().getRouteNodeId().longValue() < largetActivatedNodeId) {
224 //						nodeNames.add(routeNode.getName());
225 //					}
226 //				}
227 //			}
228 			return Collections.unmodifiableList(new ArrayList<String>(routeNodeNames));
229 		} else {
230 			return Collections.emptyList();
231 		}
232 	}
233 	
234 	@Override
235 	public DocumentLink addDocumentLink(DocumentLink documentLink) throws RiceIllegalArgumentException {
236 		if (documentLink == null) {
237 			throw new RiceIllegalArgumentException("documentLink was null");
238 		}
239 		if (documentLink.getId() != null) {
240 			throw new RiceIllegalArgumentException("the given documentLink already has an id, cannot add a document link with an existing id");
241 		}
242 		org.kuali.rice.kew.documentlink.DocumentLink documentLinkBo = org.kuali.rice.kew.documentlink.DocumentLink.from(documentLink);
243 		KEWServiceLocator.getDocumentLinkService().saveDocumentLink(documentLinkBo);
244 		return org.kuali.rice.kew.documentlink.DocumentLink.to(documentLinkBo);
245 	}
246 
247 	@Override
248 	public DocumentLink deleteDocumentLink(String documentLinkId) throws RiceIllegalArgumentException {
249 		if (StringUtils.isBlank(documentLinkId)) {
250 			throw new RiceIllegalArgumentException("documentLinkId was null or blank");
251 		}
252 		org.kuali.rice.kew.documentlink.DocumentLink documentLinkBo = KEWServiceLocator.getDocumentLinkService().getDocumentLink(Long.valueOf(documentLinkId));
253 		if (documentLinkBo == null) {
254 			throw new RiceIllegalArgumentException("Failed to locate document link with the given documentLinkId: " + documentLinkId);
255 		}
256 		KEWServiceLocator.getDocumentLinkService().deleteDocumentLink(documentLinkBo);
257 		return org.kuali.rice.kew.documentlink.DocumentLink.to(documentLinkBo);
258 	}
259 	    
260 	@Override
261 	public List<DocumentLink> deleteDocumentLinksByDocumentId(String originatingDocumentId) throws RiceIllegalArgumentException {
262 		if (StringUtils.isBlank(originatingDocumentId)) {
263 			throw new RiceIllegalArgumentException("originatingDocumentId was null or blank");
264 		}
265 		List<org.kuali.rice.kew.documentlink.DocumentLink> documentLinkBos = KEWServiceLocator.getDocumentLinkService().getLinkedDocumentsByDocId(originatingDocumentId);
266 		if (documentLinkBos == null || documentLinkBos.isEmpty()) {
267 			return Collections.emptyList();
268 		}
269 		List<DocumentLink> deletedDocumentLinks = new ArrayList<DocumentLink>();
270 		for (org.kuali.rice.kew.documentlink.DocumentLink documentLinkBo : documentLinkBos) {
271 			deletedDocumentLinks.add(org.kuali.rice.kew.documentlink.DocumentLink.to(documentLinkBo));
272 			KEWServiceLocator.getDocumentLinkService().deleteDocumentLink(documentLinkBo);
273 		}
274 		return Collections.unmodifiableList(deletedDocumentLinks);
275     }
276 	    
277 	@Override
278 	public List<DocumentLink> getOutgoingDocumentLinks(String originatingDocumentId) throws RiceIllegalArgumentException {
279 		if (StringUtils.isBlank(originatingDocumentId)) {
280 			throw new RiceIllegalArgumentException("originatingDocumentId was null or blank");
281 		}
282 		List<org.kuali.rice.kew.documentlink.DocumentLink> outgoingDocumentLinkBos = KEWServiceLocator.getDocumentLinkService().getLinkedDocumentsByDocId(originatingDocumentId);
283 		List<DocumentLink> outgoingDocumentLinks = new ArrayList<DocumentLink>();
284 		for (org.kuali.rice.kew.documentlink.DocumentLink outgoingDocumentLinkBo : outgoingDocumentLinkBos) {
285 			outgoingDocumentLinks.add(org.kuali.rice.kew.documentlink.DocumentLink.to(outgoingDocumentLinkBo));
286 		}
287 		return Collections.unmodifiableList(outgoingDocumentLinks);
288     }
289 	
290 	@Override
291 	public List<DocumentLink> getIncomingDocumentLinks(String destinationDocumentId) throws RiceIllegalArgumentException {
292 		if (StringUtils.isBlank(destinationDocumentId)) {
293 			throw new RiceIllegalArgumentException("destinationDocumentId was null or blank");
294 		}
295 		List<org.kuali.rice.kew.documentlink.DocumentLink> incomingDocumentLinkBos = KEWServiceLocator.getDocumentLinkService().getOutgoingLinkedDocumentsByDocId(destinationDocumentId);
296 		List<DocumentLink> incomingDocumentLinks = new ArrayList<DocumentLink>();
297 		for (org.kuali.rice.kew.documentlink.DocumentLink incomingDocumentLinkBo : incomingDocumentLinkBos) {
298 			incomingDocumentLinks.add(org.kuali.rice.kew.documentlink.DocumentLink.to(incomingDocumentLinkBo));
299 		}
300 		return Collections.unmodifiableList(incomingDocumentLinks);
301     }
302 	    
303 	@Override
304 	public DocumentLink getDocumentLink(String documentLinkId) throws RiceIllegalArgumentException {
305 		if (StringUtils.isBlank(documentLinkId)) {
306 			throw new RiceIllegalArgumentException("documentLinkId was null or blank");
307 		}
308 		org.kuali.rice.kew.documentlink.DocumentLink documentLinkBo = KEWServiceLocator.getDocumentLinkService().getDocumentLink(Long.valueOf(documentLinkId));
309 		return org.kuali.rice.kew.documentlink.DocumentLink.to(documentLinkBo);
310     }
311 	
312 }