View Javadoc

1   /*
2    * Copyright 2005-2008 The Kuali Foundation
3    *
4    *
5    * Licensed under the Educational Community License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.opensource.org/licenses/ecl2.php
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.kuali.rice.kew.actionlist.dao.impl;
18  
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.framework.persistence.jpa.OrmUtils;
21  import org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria;
22  import org.kuali.rice.core.framework.persistence.jpa.criteria.QueryByCriteria;
23  import org.kuali.rice.kew.actionitem.ActionItem;
24  import org.kuali.rice.kew.actionitem.ActionItemActionListExtension;
25  import org.kuali.rice.kew.actionitem.OutboxItemActionListExtension;
26  import org.kuali.rice.kew.actionlist.ActionListFilter;
27  import org.kuali.rice.kew.actionlist.dao.ActionListDAO;
28  import org.kuali.rice.kew.api.action.DelegationType;
29  import org.kuali.rice.kew.doctype.bo.DocumentType;
30  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
31  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueActionListExtension;
32  import org.kuali.rice.kew.service.KEWServiceLocator;
33  import org.kuali.rice.kew.util.KEWConstants;
34  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
35  
36  import javax.persistence.EntityManager;
37  import javax.persistence.PersistenceContext;
38  import java.sql.Timestamp;
39  import java.util.ArrayList;
40  import java.util.Calendar;
41  import java.util.Collection;
42  import java.util.Date;
43  import java.util.HashMap;
44  import java.util.Iterator;
45  import java.util.List;
46  import java.util.Map;
47  
48  /**
49   * OJB implementation of the {@link ActionListDAO}.
50   *
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   */
53  public class ActionListDAOJpaImpl implements ActionListDAO {
54  
55      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ActionListDAOJpaImpl.class);
56  	
57      @PersistenceContext(unitName="kew-unit")
58  	private EntityManager entityManager;
59  	
60      public Collection<ActionItem> getActionList(String principalId, ActionListFilter filter) {
61          return toActionItemActionListExtensions(getActionItemsInActionList(ActionItem.class, principalId, filter));
62      }
63      
64      /**
65  	 * This method ...
66  	 * 
67  	 * @param actionItems
68  	 * @return actionItemActionListExtensions
69  	 */
70  	private Collection<ActionItem> toActionItemActionListExtensions(
71  			Collection<ActionItem> actionItems) {
72  		List<ActionItem> actionItemActionListExtensions = new ArrayList<ActionItem>();
73  		for(ActionItem actionItem:actionItems){
74  			actionItemActionListExtensions.add(toActionItemActionListExtension(actionItem));
75  		}
76  		return actionItemActionListExtensions;
77  	}
78  
79  	/**
80  	 * This method ...
81  	 * 
82  	 * @param actionItem
83  	 * @return
84  	 */
85  	private ActionItemActionListExtension toActionItemActionListExtension(
86  			ActionItem actionItem) {
87  
88  		if(actionItem==null){
89  			return null;
90  		}
91  		
92  		ActionItemActionListExtension actionItemExt = new ActionItemActionListExtension();
93  		
94  		actionItemExt.setActionItemId(actionItem.getActionItemId());
95  		actionItemExt.setPrincipalId(actionItem.getPrincipalId());
96  		actionItemExt.setDateAssigned(actionItem.getDateAssigned());
97  		actionItemExt.setActionRequestCd(actionItem.getActionRequestCd());
98  		actionItemExt.setActionRequestId(actionItem.getActionRequestId());
99  		actionItemExt.setDocumentId(actionItem.getDocumentId());
100 		actionItemExt.setResponsibilityId(actionItem.getResponsibilityId());
101 		actionItemExt.setGroupId(actionItem.getGroupId());
102 		actionItemExt.setRoleName(actionItem.getRoleName());
103 		actionItemExt.setDocTitle(actionItem.getDocTitle());
104 		actionItemExt.setDocLabel(actionItem.getDocLabel());
105 		actionItemExt.setDocHandlerURL(actionItem.getDocHandlerURL());
106 		actionItemExt.setDocName(actionItem.getDocName());
107 		actionItemExt.setDelegatorWorkflowId(actionItem.getDelegatorWorkflowId());
108 		actionItemExt.setDelegatorGroupId(actionItem.getDelegatorGroupId());
109 		actionItemExt.setDelegationType(actionItem.getDelegationType());
110 		actionItemExt.setLockVerNbr(actionItem.getLockVerNbr());
111 		actionItemExt.setDocumentId(actionItem.getDocumentId());
112 		actionItemExt.setRequestLabel(actionItem.getRequestLabel());
113 		//actionItemExt.setRouteHeader(toDocumentRouteHeaderValueActionListExtension(actionItem.getRouteHeader()));
114 		
115 		// These properties are not mapped in OJB-repository-kew.xml
116 		// actionItemExt.setActionItemIndex(actionItem.getActionItemIndex());
117 		// actionItemExt.setActionToTake(actionItem.getActionToTake());
118 		// actionItemExt.setCustomActions(actionItem.getCustomActions());
119 		// actionItemExt.setDateAssignedString(actionItem.getDateAssignedString());
120 		// actionItemExt.setDelegatorGroup();
121 		// actionItemExt.setDisplayParameters();
122 		// FIXME this causes null pointer - actionItemExt.setGroup(actionItem.getGroup());
123 		// actionItemExt.setLastApprovedDate(actionItem.getLastApprovedDate());
124 		// actionItemExt.setRowStyleClass();
125 		
126 		return actionItemExt;
127 	}
128 
129 	/**
130 	 * This method ...
131 	 * 
132 	 * @param routeHeader
133 	 * @return
134 	 */
135 	private DocumentRouteHeaderValueActionListExtension toDocumentRouteHeaderValueActionListExtension(
136 			DocumentRouteHeaderValue routeHeader) {
137 
138 		if(routeHeader==null){
139 			return null;
140 		}
141 		
142 		DocumentRouteHeaderValueActionListExtension extension = new DocumentRouteHeaderValueActionListExtension();
143 		
144 		extension.setDocumentId(routeHeader.getDocumentId());
145 		extension.setDocumentTypeId(routeHeader.getDocumentTypeId());
146 		extension.setDocRouteStatus(routeHeader.getDocRouteStatus());
147 		extension.setDocRouteLevel(routeHeader.getDocRouteLevel());
148 		extension.setStatusModDate(routeHeader.getStatusModDate());
149 		extension.setCreateDate(routeHeader.getCreateDate());
150 		extension.setApprovedDate(routeHeader.getApprovedDate());
151 		extension.setFinalizedDate(routeHeader.getFinalizedDate());
152 		extension.setRouteStatusDate(routeHeader.getRouteStatusDate());
153 		extension.setRouteLevelDate(routeHeader.getRouteLevelDate());
154 		extension.setDocTitle(routeHeader.getDocTitle());
155 		extension.setAppDocId(routeHeader.getAppDocId());
156 		extension.setDocVersion(routeHeader.getDocVersion());
157 		extension.setInitiatorWorkflowId(routeHeader.getInitiatorWorkflowId());
158 		extension.setVersionNumber(routeHeader.getVersionNumber());
159 		extension.setAppDocStatus(routeHeader.getAppDocStatus());
160 		extension.setAppDocStatusDate(routeHeader.getAppDocStatusDate());
161 
162 		return extension;
163 	}
164 
165 	public Collection<ActionItem> getActionListForSingleDocument(String documentId) {
166         LOG.debug("getting action list for document id " + documentId);
167         Criteria crit = new Criteria(ActionItem.class.getName());
168         crit.eq("documentId", documentId);
169         crit.eq("TYPE(__JPA_ALIAS[[0]]__)", ActionItem.class);
170         Collection<ActionItem> collection = new QueryByCriteria(entityManager, crit).toQuery().getResultList();
171         LOG.debug("found " + collection.size() + " action items for document id " + documentId);
172         return toActionItemActionListExtensions(createActionListForRouteHeader(collection));
173     }
174     
175     private Criteria setUpActionListCriteria(Class objectsToRetrieve, String principalId, ActionListFilter filter) {
176         LOG.debug("setting up Action List criteria");
177         Criteria crit = new Criteria(objectsToRetrieve.getName());
178         boolean filterOn = false;
179         String filteredByItems = "";
180         
181         if (filter.getActionRequestCd() != null && !"".equals(filter.getActionRequestCd().trim()) && !filter.getActionRequestCd().equals(KEWConstants.ALL_CODE)) {
182             if (filter.isExcludeActionRequestCd()) {
183                 crit.ne("actionRequestCd", filter.getActionRequestCd());
184             } else {
185                 crit.eq("actionRequestCd", filter.getActionRequestCd());
186             }
187             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
188             filteredByItems += "Action Requested";
189         }
190 
191         if (filter.getCreateDateFrom() != null || filter.getCreateDateTo() != null) {
192             if (filter.isExcludeCreateDate()) {
193                 if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() != null) {
194                     crit.notBetween("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()), new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
195                 } else if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() == null) {
196                     crit.lte("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()));
197                 } else if (filter.getCreateDateFrom() == null && filter.getCreateDateTo() != null) {
198                     crit.gte("routeHeader.createDate", new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
199                 }
200             } else {
201                 if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() != null) {
202                     crit.between("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()), new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
203                 } else if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() == null) {
204                     crit.gte("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()));
205                 } else if (filter.getCreateDateFrom() == null && filter.getCreateDateTo() != null) {
206                     crit.lte("routeHeader.createDate", new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
207                 }
208             }
209             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
210             filteredByItems += "Date Created";
211         }
212 
213         if (filter.getDocRouteStatus() != null && !"".equals(filter.getDocRouteStatus().trim()) && !filter.getDocRouteStatus().equals(KEWConstants.ALL_CODE)) {
214             if (filter.isExcludeRouteStatus()) {
215                 crit.ne("routeHeader.docRouteStatus", filter.getDocRouteStatus());
216             } else {
217                 crit.eq("routeHeader.docRouteStatus", filter.getDocRouteStatus());
218             }
219             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
220             filteredByItems += "Document Route Status";
221         }
222 
223         if (filter.getDocumentTitle() != null && !"".equals(filter.getDocumentTitle().trim())) {
224             String docTitle = filter.getDocumentTitle();
225             if (docTitle.trim().endsWith("*")) {
226                 docTitle = docTitle.substring(0, docTitle.length() - 1);
227             }
228 
229             if (filter.isExcludeDocumentTitle()) {
230                 crit.notLike("docTitle", "%" + docTitle + "%");
231             } else {
232                 crit.like("docTitle", "%" + docTitle + "%");
233             }
234             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
235             filteredByItems += "Document Title";
236         }
237 
238         if (filter.getDocumentType() != null && !"".equals(filter.getDocumentType().trim())) {
239             if (filter.isExcludeDocumentType()) {
240                 crit.notLike("docName", "%" + filter.getDocumentType() + "%");
241             } else {
242             	String documentTypeName = filter.getDocumentType();
243             	DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
244             	if (documentType == null) {
245             	    crit.like("docName", "%" + filter.getDocumentType() + "%");
246             	} else {
247             	    // search this document type plus it's children
248             	    Criteria docTypeCrit = new Criteria(objectsToRetrieve.getName());
249             	    constructDocumentTypeCriteria(objectsToRetrieve.getName(), docTypeCrit, documentType);
250             	    crit.and(docTypeCrit);
251             	}
252             }
253             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
254             filteredByItems += "Document Type";
255         }
256 
257         if (filter.getLastAssignedDateFrom() != null || filter.getLastAssignedDateTo() != null) {
258             if (filter.isExcludeLastAssignedDate()) {
259                 if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() != null) {
260                     crit.notBetween("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()), new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
261                 } else if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() == null) {
262                     crit.lte("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()));
263                 } else if (filter.getLastAssignedDateFrom() == null && filter.getLastAssignedDateTo() != null) {
264                     crit.gte("dateAssigned", new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
265                 }
266             } else {
267                 if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() != null) {
268                     crit.between("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()), new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
269                 } else if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() == null) {
270                     crit.gte("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()));
271                 } else if (filter.getLastAssignedDateFrom() == null && filter.getLastAssignedDateTo() != null) {
272                     crit.lte("dateAssigned", new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
273                 }
274             }
275             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
276             filteredByItems += "Date Last Assigned";
277         }
278 
279         filter.setGroupId(null);
280         if (filter.getGroupIdString() != null && !"".equals(filter.getGroupIdString().trim()) && !filter.getGroupIdString().trim().equals(KEWConstants.NO_FILTERING)) {
281             filter.setGroupId(filter.getGroupId());
282             if (filter.isExcludeGroupId()) {
283                 Criteria critNotEqual = new Criteria(objectsToRetrieve.getName());
284                 critNotEqual.ne("groupId", filter.getGroupId());
285                 Criteria critNull = new Criteria(objectsToRetrieve.getName());
286                 critNull.isNull("groupId");
287                 critNotEqual.or(critNull);
288                 crit.and(critNotEqual);
289             } else {
290                 crit.eq("groupId", filter.getGroupId());
291             }
292             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
293             filteredByItems += "Action Request Workgroup";
294         }
295 
296         if (filteredByItems.length() > 0) {
297             filterOn = true;
298         }
299         
300         boolean addedDelegationCriteria = false;
301         if (StringUtils.isBlank(filter.getDelegationType()) && StringUtils.isBlank(filter.getPrimaryDelegateId()) && StringUtils.isBlank(filter.getDelegatorId())) {
302             crit.eq("principalId", principalId);
303             addedDelegationCriteria = true;
304         } else if ((StringUtils.isNotBlank(filter.getDelegationType()) && DelegationType.PRIMARY.getCode().equals(filter.getDelegationType()))
305                 || StringUtils.isNotBlank(filter.getPrimaryDelegateId())) {
306             // using a primary delegation
307             if ((StringUtils.isBlank(filter.getPrimaryDelegateId())) || (filter.getPrimaryDelegateId().trim().equals(KEWConstants.ALL_CODE))) {
308                 // user wishes to see all primary delegations
309                 Criteria userCrit = new Criteria(objectsToRetrieve.getName());
310                 Criteria groupCrit = new Criteria(objectsToRetrieve.getName());
311                 Criteria orCrit = new Criteria(objectsToRetrieve.getName());
312                 userCrit.eq("delegatorWorkflowId", principalId);
313                 
314                 List<String> userGroupIds = new ArrayList<String>();
315                 for(String id: KimApiServiceLocator.getGroupService().getGroupIdsForPrincipal(principalId)){
316                 	userGroupIds.add(id);
317                 }
318                 if (!userGroupIds.isEmpty()) {
319                 	groupCrit.in("delegatorGroupId", userGroupIds);
320                 }
321                 orCrit.or(userCrit);
322                 orCrit.or(groupCrit);
323                 crit.and(orCrit);
324                 crit.eq("delegationType", DelegationType.PRIMARY.getCode());
325                 filter.setDelegationType(DelegationType.PRIMARY.getCode());
326                 filter.setExcludeDelegationType(false);
327                 addToFilterDescription(filteredByItems, "Primary Delegator Id");
328                 addedDelegationCriteria = true;
329                 filterOn = true;
330             } else if (!filter.getPrimaryDelegateId().trim().equals(KEWConstants.PRIMARY_DELEGATION_DEFAULT)) {
331                 // user wishes to see primary delegation for a single user
332                 crit.eq("principalId", filter.getPrimaryDelegateId());
333                 Criteria userCrit = new Criteria(objectsToRetrieve.getName());
334                 Criteria groupCrit = new Criteria(objectsToRetrieve.getName());
335                 Criteria orCrit = new Criteria(objectsToRetrieve.getName());
336                 userCrit.eq("delegatorWorkflowId", principalId);
337                 List<String> userGroupIds = new ArrayList<String>();
338                 for(String id: KimApiServiceLocator.getGroupService().getGroupIdsForPrincipal(principalId)){
339                 	userGroupIds.add(id);
340                 }
341                 if (!userGroupIds.isEmpty()) {
342                 	groupCrit.in("delegatorGroupId", userGroupIds);
343                 }
344                 orCrit.or(userCrit);
345                 orCrit.or(groupCrit);
346                 crit.and(orCrit);
347                 crit.eq("delegationType", DelegationType.PRIMARY.getCode());
348                 filter.setDelegationType(DelegationType.PRIMARY.getCode());
349                 filter.setExcludeDelegationType(false);
350                 addToFilterDescription(filteredByItems, "Primary Delegator Id");
351                 addedDelegationCriteria = true;
352                 filterOn = true;
353             }
354         }
355         if (!addedDelegationCriteria && ( (StringUtils.isNotBlank(filter.getDelegationType()) && DelegationType.SECONDARY.getCode().equals(filter.getDelegationType()))
356                 || StringUtils.isNotBlank(filter.getDelegatorId()) )) {
357             // using a secondary delegation
358             crit.eq("principalId", principalId);
359             if (StringUtils.isBlank(filter.getDelegatorId())) {
360                 filter.setDelegationType(DelegationType.SECONDARY.getCode());
361                 // if isExcludeDelegationType() we want to show the default aciton list which is set up later in this method
362                 if (!filter.isExcludeDelegationType()) {
363                     crit.eq("delegationType", DelegationType.SECONDARY.getCode());
364                     addToFilterDescription(filteredByItems, "Secondary Delegator Id");
365                     addedDelegationCriteria = true;
366                     filterOn = true;
367                 }
368             } else if (filter.getDelegatorId().trim().equals(KEWConstants.ALL_CODE)) {
369                 // user wishes to see all secondary delegations
370                 crit.eq("delegationType", DelegationType.SECONDARY.getCode());
371                 filter.setDelegationType(DelegationType.SECONDARY.getCode());
372                 filter.setExcludeDelegationType(false);
373                 addToFilterDescription(filteredByItems, "Secondary Delegator Id");
374                 addedDelegationCriteria = true;
375                 filterOn = true;
376             } else if (!filter.getDelegatorId().trim().equals(
377                     KEWConstants.DELEGATION_DEFAULT)) {
378                 // user has specified an id to see for secondary delegation
379                 filter.setDelegationType(DelegationType.SECONDARY.getCode());
380                 filter.setExcludeDelegationType(false);
381                 Criteria userCrit = new Criteria(objectsToRetrieve.getName());
382                 Criteria groupCrit = new Criteria(objectsToRetrieve.getName());
383                 if (filter.isExcludeDelegatorId()) {
384                     Criteria userNull = new Criteria(objectsToRetrieve.getName());
385                     userCrit.ne("delegatorWorkflowId", filter.getDelegatorId());
386                     userNull.isNull("delegatorWorkflowId");
387                     userCrit.or(userNull);
388                     Criteria groupNull = new Criteria(objectsToRetrieve.getName());
389                     groupCrit.ne("delegatorGroupId", filter.getDelegatorId());
390                     groupNull.isNull("delegatorGroupId");
391                     groupCrit.or(groupNull);
392                     crit.and(userCrit);
393                     crit.and(groupCrit);
394                 } else {
395                     userCrit.eq("delegatorWorkflowId", filter.getDelegatorId());
396                     groupCrit.eq("delegatorGroupId", filter.getDelegatorId());
397                     userCrit.or(groupCrit);
398                     crit.and(userCrit);
399                 }
400                 addToFilterDescription(filteredByItems, "Secondary Delegator Id");
401                 addedDelegationCriteria = true;
402                 filterOn = true;
403             }
404         }
405         
406         // if we haven't added delegation criteria then use the default criteria below
407         if (!addedDelegationCriteria) {
408             crit.eq("principalId", principalId);
409             filter.setDelegationType(DelegationType.SECONDARY.getCode());
410             filter.setExcludeDelegationType(true);
411             Criteria critNotEqual = new Criteria(objectsToRetrieve.getName());
412             Criteria critNull = new Criteria(objectsToRetrieve.getName());
413             critNotEqual.ne("delegationType", DelegationType.SECONDARY.getCode());
414             critNull.isNull("delegationType");
415             critNotEqual.or(critNull);
416             crit.and(critNotEqual);
417         }
418 
419  
420         if (! "".equals(filteredByItems)) {
421             filteredByItems = "Filtered by " + filteredByItems;
422         }
423         filter.setFilterLegend(filteredByItems);
424         filter.setFilterOn(filterOn);
425 
426         LOG.debug("returning from Action List criteria");
427         return crit;
428     }
429     
430     private void constructDocumentTypeCriteria(String entityName, Criteria criteria, DocumentType documentType) {
431     	// search this document type plus it's children
432     	Criteria docTypeBaseCrit = new Criteria(entityName);
433     	docTypeBaseCrit.eq("docName", documentType.getName());
434     	criteria.or(docTypeBaseCrit);
435     	Collection children = documentType.getChildrenDocTypes();
436     	if (children != null) {
437     	    for (Iterator iterator = children.iterator(); iterator.hasNext();) {
438     	    	DocumentType childDocumentType = (DocumentType) iterator.next();
439     	    	constructDocumentTypeCriteria(entityName, criteria, childDocumentType);
440     	    }
441     	}
442     }
443     
444     private void addToFilterDescription(String filterDescription, String labelToAdd) {
445         filterDescription += filterDescription.length() > 0 ? ", " : "";
446         filterDescription += labelToAdd;
447     }
448 
449     private static final String ACTION_LIST_COUNT_QUERY = "select count(distinct(ai.doc_hdr_id)) from krew_actn_itm_t ai where ai.PRNCPL_ID = ? and (ai.dlgn_typ is null or ai.dlgn_typ = 'P')";
450 
451     public int getCount(final String workflowId) {
452     	
453     	javax.persistence.Query q = entityManager.createNativeQuery(ACTION_LIST_COUNT_QUERY);
454     	q.setParameter(1, workflowId);
455     	Number result = (Number)q.getSingleResult();
456     	return result.intValue();
457     }
458 
459     /**
460      * Creates an Action List from the given collection of Action Items.  The Action List should
461      * contain only one action item per document.  The action item chosen should be the most "critical"
462      * or "important" one on the document.
463      *
464      * @return the Action List as a Collection of ActionItems
465      */
466     private <T extends ActionItem> Collection<T> createActionListForUser(Collection<T> actionItems) {
467         Map<String, T> actionItemMap = new HashMap<String, T>();
468         ActionListPriorityComparator comparator = new ActionListPriorityComparator();
469         for (T potentialActionItem: actionItems) {
470             T existingActionItem = actionItemMap.get(potentialActionItem.getDocumentId());
471             if (existingActionItem == null || comparator.compare(potentialActionItem, existingActionItem) > 0) {
472                 actionItemMap.put(potentialActionItem.getDocumentId(), potentialActionItem);
473             }
474         }
475         return actionItemMap.values();
476     }
477 
478     /**
479      * Creates an Action List from the given collection of Action Items.  The Action List should
480      * contain only one action item per user.  The action item chosen should be the most "critical"
481      * or "important" one on the document.
482      *
483      * @return the Action List as a Collection of ActionItems
484      */
485     private Collection<ActionItem> createActionListForRouteHeader(Collection<ActionItem> actionItems) {
486         Map<String, ActionItem> actionItemMap = new HashMap<String, ActionItem>();
487         ActionListPriorityComparator comparator = new ActionListPriorityComparator();
488         for (ActionItem potentialActionItem: actionItems) {
489             ActionItem existingActionItem = actionItemMap.get(potentialActionItem.getPrincipalId());
490             if (existingActionItem == null || comparator.compare(potentialActionItem, existingActionItem) > 0) {
491                 actionItemMap.put(potentialActionItem.getPrincipalId(), potentialActionItem);
492             }
493         }
494         return actionItemMap.values();
495     }
496     
497     private <T extends ActionItem> Collection<ActionItem> getActionItemsInActionList(Class<T> objectsToRetrieve, String principalId, ActionListFilter filter) {
498         LOG.debug("getting action list for user " + principalId);
499         Criteria crit = null;
500         if (filter == null) {
501             crit = new Criteria(objectsToRetrieve.getName());
502             crit.eq("principalId", principalId);
503         } else {
504             crit = setUpActionListCriteria(objectsToRetrieve, principalId, filter);
505         }
506         LOG.debug("running query to get action list for criteria " + crit);
507         Collection<ActionItem> collection = new QueryByCriteria(entityManager, crit).toQuery().getResultList();
508         LOG.debug("found " + collection.size() + " action items for user " + principalId);
509         return createActionListForUser(collection);
510     }
511 
512     public Collection<ActionItem> getOutbox(String principalId, ActionListFilter filter) {
513         return getActionItemsInActionList(OutboxItemActionListExtension.class, principalId, filter);
514     }
515 
516     /**
517      * Deletes all outbox items specified by the list of ids
518      * 
519      * @see org.kuali.rice.kew.actionlist.dao.ActionListDAO#removeOutboxItems(String, java.util.List)
520      */
521     public void removeOutboxItems(String principalId, List<String> outboxItems) {
522         Criteria crit = new Criteria(OutboxItemActionListExtension.class.getName());
523         crit.in("actionItemId", outboxItems);
524         for(Object entity:new QueryByCriteria(entityManager, crit).toQuery().getResultList()){
525         	entityManager.remove(entity);
526         }
527     }
528 
529     /**
530      * Saves an outbox item
531      * 
532      * @see org.kuali.rice.kew.actionlist.dao.ActionListDAO#saveOutboxItem(org.kuali.rice.kew.actionitem.OutboxItemActionListExtension)
533      */
534     public void saveOutboxItem(OutboxItemActionListExtension outboxItem) {
535     	if(outboxItem.getActionItemId()==null){
536     		entityManager.persist(outboxItem);
537     	}else{
538     	  //TODO, merge will not update the outboxitem pointer to the merged entity
539     		OrmUtils.merge(entityManager, outboxItem);
540     	}
541     	entityManager.flush();
542     }
543 
544     /**
545      * Gets the outbox item associated with the document id
546      * 
547      * @see org.kuali.rice.kew.actionlist.dao.ActionListDAO#getOutboxByDocumentId(java.lang.Long)
548      */
549     public OutboxItemActionListExtension getOutboxByDocumentId(String documentId) {
550         Criteria crit = new Criteria(OutboxItemActionListExtension.class.getName());
551         crit.eq("documentId", documentId);
552         return (OutboxItemActionListExtension) new QueryByCriteria(entityManager, crit).toQuery().getSingleResult();
553     }
554     
555     /**
556      * This overridden method ...
557      * 
558      * @see org.kuali.rice.kew.actionlist.dao.ActionListDAO#getOutboxByDocumentIdUserId(Long, String)
559      */
560     public OutboxItemActionListExtension getOutboxByDocumentIdUserId(String documentId, String userId) {
561         Criteria crit = new Criteria(OutboxItemActionListExtension.class.getName());
562         crit.eq("documentId", documentId);
563         crit.eq("principalId", userId);
564        	return (OutboxItemActionListExtension) new QueryByCriteria(entityManager, crit).toQuery().getSingleResult();
565     }
566     
567     private Date beginningOfDay(Date date) {
568         Calendar cal = Calendar.getInstance();
569         cal.setTime(date);
570         cal.set(Calendar.HOUR_OF_DAY, 0);
571         cal.set(Calendar.MINUTE, 0);
572         cal.set(Calendar.SECOND, 0);
573         return cal.getTime();
574     }
575     
576     private Date endOfDay(Date date) {
577         Calendar cal = Calendar.getInstance();
578         cal.setTime(date);
579         cal.set(Calendar.HOUR_OF_DAY, 23);
580         cal.set(Calendar.MINUTE, 59);
581         cal.set(Calendar.SECOND, 59);
582         return cal.getTime();        
583     }
584 
585     public EntityManager getEntityManager() {
586         return this.entityManager;
587     }
588 
589     public void setEntityManager(EntityManager entityManager) {
590         this.entityManager = entityManager;
591     }
592 
593 
594     
595 }