View Javadoc

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