View Javadoc

1   /**
2    * Copyright 2005-2015 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.apache.ojb.broker.PersistenceBroker;
20  import org.apache.ojb.broker.accesslayer.LookupException;
21  import org.apache.ojb.broker.query.Criteria;
22  import org.apache.ojb.broker.query.QueryByCriteria;
23  import org.kuali.rice.core.api.delegation.DelegationType;
24  import org.kuali.rice.kew.actionitem.ActionItem;
25  import org.kuali.rice.kew.actionitem.ActionItemActionListExtension;
26  import org.kuali.rice.kew.actionitem.OutboxItemActionListExtension;
27  import org.kuali.rice.kew.actionlist.ActionListFilter;
28  import org.kuali.rice.kew.actionlist.dao.ActionListDAO;
29  import org.kuali.rice.kew.api.WorkflowRuntimeException;
30  import org.kuali.rice.kew.doctype.bo.DocumentType;
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  import org.springmodules.orm.ojb.PersistenceBrokerCallback;
35  import org.springmodules.orm.ojb.support.PersistenceBrokerDaoSupport;
36  
37  import java.sql.Connection;
38  import java.sql.PreparedStatement;
39  import java.sql.ResultSet;
40  import java.sql.SQLException;
41  import java.sql.Timestamp;
42  import java.util.ArrayList;
43  import java.util.Calendar;
44  import java.util.Collection;
45  import java.util.Date;
46  import java.util.HashMap;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Map;
50  
51  /**
52   * OJB implementation of the {@link ActionListDAO}.
53   *
54   * @author Kuali Rice Team (rice.collab@kuali.org)
55   */
56  public class ActionListDAOOjbImpl extends PersistenceBrokerDaoSupport implements ActionListDAO {
57  
58      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ActionListDAOOjbImpl.class);
59  
60      public Collection<ActionItemActionListExtension> getActionList(String principalId, ActionListFilter filter) {
61          return getActionItemsInActionList(ActionItemActionListExtension.class, principalId, filter);
62      }
63  
64      public Collection<ActionItemActionListExtension> getActionListForSingleDocument(String documentId) {
65          LOG.debug("getting action list for document id " + documentId);
66          Criteria crit = new Criteria();
67          crit.addEqualTo("documentId", documentId);
68          Collection<ActionItemActionListExtension> collection = this.getPersistenceBrokerTemplate().getCollectionByQuery(new QueryByCriteria(ActionItemActionListExtension.class, crit));
69          LOG.debug("found " + collection.size() + " action items for document id " + documentId);
70          return createActionListForRouteHeader(collection);
71      }
72  
73      private Criteria setUpActionListCriteria(String principalId, ActionListFilter filter) {
74          LOG.debug("setting up Action List criteria");
75          Criteria crit = new Criteria();
76          boolean filterOn = false;
77          String filteredByItems = "";
78  
79          if (filter.getActionRequestCd() != null && !"".equals(filter.getActionRequestCd().trim()) && !filter.getActionRequestCd().equals(KewApiConstants.ALL_CODE)) {
80              if (filter.isExcludeActionRequestCd()) {
81                  crit.addNotEqualTo("actionRequestCd", filter.getActionRequestCd());
82              } else {
83                  crit.addEqualTo("actionRequestCd", filter.getActionRequestCd());
84              }
85              filteredByItems += filteredByItems.length() > 0 ? ", " : "";
86              filteredByItems += "Action Requested";
87          }
88  
89          if (filter.getCreateDateFrom() != null || filter.getCreateDateTo() != null) {
90              if (filter.isExcludeCreateDate()) {
91                  if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() != null) {
92                      crit.addNotBetween("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()), new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
93                  } else if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() == null) {
94                      crit.addLessOrEqualThan("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()));
95                  } else if (filter.getCreateDateFrom() == null && filter.getCreateDateTo() != null) {
96                      crit.addGreaterOrEqualThan("routeHeader.createDate", new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
97                  }
98              } else {
99                  if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() != null) {
100                     crit.addBetween("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()), new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
101                 } else if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() == null) {
102                     crit.addGreaterOrEqualThan("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()));
103                 } else if (filter.getCreateDateFrom() == null && filter.getCreateDateTo() != null) {
104                     crit.addLessOrEqualThan("routeHeader.createDate", new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
105                 }
106             }
107             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
108             filteredByItems += "Date Created";
109         }
110 
111         if (filter.getDocRouteStatus() != null && !"".equals(filter.getDocRouteStatus().trim()) && !filter.getDocRouteStatus().equals(KewApiConstants.ALL_CODE)) {
112             if (filter.isExcludeRouteStatus()) {
113                 crit.addNotEqualTo("routeHeader.docRouteStatus", filter.getDocRouteStatus());
114             } else {
115                 crit.addEqualTo("routeHeader.docRouteStatus", filter.getDocRouteStatus());
116             }
117             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
118             filteredByItems += "Document Route Status";
119         }
120 
121         if (filter.getDocumentTitle() != null && !"".equals(filter.getDocumentTitle().trim())) {
122             String docTitle = filter.getDocumentTitle();
123             if (docTitle.trim().endsWith("*")) {
124                 docTitle = docTitle.substring(0, docTitle.length() - 1);
125             }
126 
127             if (filter.isExcludeDocumentTitle()) {
128                 crit.addNotLike("docTitle", "%" + docTitle + "%");
129             } else {
130                 crit.addLike("docTitle", "%" + docTitle + "%");
131             }
132             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
133             filteredByItems += "Document Title";
134         }
135 
136         if (filter.getDocumentType() != null && !"".equals(filter.getDocumentType().trim())) {
137             if (filter.isExcludeDocumentType()) {
138                 crit.addNotLike("docName", "%" + filter.getDocumentType() + "%");
139             } else {
140                 String documentTypeName = filter.getDocumentType();
141                 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
142                 if (documentType == null) {
143                     crit.addLike("docName", "%" + filter.getDocumentType() + "%");
144                 } else {
145                     // search this document type plus it's children
146                     Criteria docTypeCrit = new Criteria();
147                     constructDocumentTypeCriteria(docTypeCrit, documentType);
148                     crit.addAndCriteria(docTypeCrit);
149                 }
150             }
151             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
152             filteredByItems += "Document Type";
153         }
154 
155         if (filter.getLastAssignedDateFrom() != null || filter.getLastAssignedDateTo() != null) {
156             if (filter.isExcludeLastAssignedDate()) {
157                 if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() != null) {
158                     crit.addNotBetween("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()), new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
159                 } else if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() == null) {
160                     crit.addLessOrEqualThan("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()));
161                 } else if (filter.getLastAssignedDateFrom() == null && filter.getLastAssignedDateTo() != null) {
162                     crit.addGreaterOrEqualThan("dateAssigned", new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
163                 }
164             } else {
165                 if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() != null) {
166                     crit.addBetween("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()), new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
167                 } else if (filter.getLastAssignedDateFrom() != null && filter.getLastAssignedDateTo() == null) {
168                     crit.addGreaterOrEqualThan("dateAssigned", new Timestamp(beginningOfDay(filter.getLastAssignedDateFrom()).getTime()));
169                 } else if (filter.getLastAssignedDateFrom() == null && filter.getLastAssignedDateTo() != null) {
170                     crit.addLessOrEqualThan("dateAssigned", new Timestamp(endOfDay(filter.getLastAssignedDateTo()).getTime()));
171                 }
172             }
173             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
174             filteredByItems += "Date Last Assigned";
175         }
176 
177         filter.setGroupId(null);
178         if (filter.getGroupIdString() != null && !"".equals(filter.getGroupIdString().trim()) && !filter.getGroupIdString().trim().equals(KewApiConstants.NO_FILTERING)) {
179 
180             filter.setGroupId(filter.getGroupIdString().trim());
181 
182             if (filter.isExcludeGroupId()) {
183                 Criteria critNotEqual = new Criteria();
184                 critNotEqual.addNotEqualTo("groupId", filter.getGroupId());
185                 Criteria critNull = new Criteria();
186                 critNull.addIsNull("groupId");
187                 critNotEqual.addOrCriteria(critNull);
188                 crit.addAndCriteria(critNotEqual);
189             } else {
190                 crit.addEqualTo("groupId", filter.getGroupId());
191             }
192             filteredByItems += filteredByItems.length() > 0 ? ", " : "";
193             filteredByItems += "Action Request Workgroup";
194         }
195 
196         if (filteredByItems.length() > 0) {
197             filterOn = true;
198         }
199 
200         boolean addedDelegationCriteria = false;
201         if (StringUtils.isBlank(filter.getDelegationType()) && StringUtils.isBlank(filter.getPrimaryDelegateId()) && StringUtils.isBlank(filter.getDelegatorId())) {
202             crit.addEqualTo("principalId", principalId);
203             addedDelegationCriteria = true;
204         } else if ((StringUtils.isNotBlank(filter.getDelegationType()) && DelegationType.PRIMARY.getCode().equals(filter.getDelegationType()))
205                 || StringUtils.isNotBlank(filter.getPrimaryDelegateId())) {
206             // using a primary delegation
207             if ((StringUtils.isBlank(filter.getPrimaryDelegateId())) || (filter.getPrimaryDelegateId().trim().equals(KewApiConstants.ALL_CODE))) {
208                 // user wishes to see all primary delegations
209                 Criteria userCrit = new Criteria();
210                 Criteria groupCrit = new Criteria();
211                 Criteria orCrit = new Criteria();
212                 userCrit.addEqualTo("delegatorPrincipalId", principalId);
213                 List<String> delegatorGroupIds = KimApiServiceLocator.getGroupService().getGroupIdsByPrincipalId(
214                         principalId);
215                 if (delegatorGroupIds != null && !delegatorGroupIds.isEmpty()) {
216                     groupCrit.addIn("delegatorGroupId", delegatorGroupIds);
217                 }
218                 orCrit.addOrCriteria(userCrit);
219                 orCrit.addOrCriteria(groupCrit);
220                 crit.addAndCriteria(orCrit);
221                 crit.addEqualTo("delegationType", DelegationType.PRIMARY.getCode());
222                 filter.setDelegationType(DelegationType.PRIMARY.getCode());
223                 filter.setExcludeDelegationType(false);
224                 addToFilterDescription(filteredByItems, "Primary Delegator Id");
225                 addedDelegationCriteria = true;
226                 filterOn = true;
227             } else if (!filter.getPrimaryDelegateId().trim().equals(KewApiConstants.PRIMARY_DELEGATION_DEFAULT)) {
228                 // user wishes to see primary delegation for a single user
229                 crit.addEqualTo("principalId", filter.getPrimaryDelegateId());
230                 Criteria userCrit = new Criteria();
231                 Criteria groupCrit = new Criteria();
232                 Criteria orCrit = new Criteria();
233                 userCrit.addEqualTo("delegatorPrincipalId", principalId);
234                 List<String> delegatorGroupIds = KimApiServiceLocator.getGroupService().getGroupIdsByPrincipalId(
235                         principalId);
236                 if (delegatorGroupIds != null && !delegatorGroupIds.isEmpty()) {
237                     groupCrit.addIn("delegatorGroupId", delegatorGroupIds);
238                 }
239                 orCrit.addOrCriteria(userCrit);
240                 orCrit.addOrCriteria(groupCrit);
241                 crit.addAndCriteria(orCrit);
242                 crit.addEqualTo("delegationType", DelegationType.PRIMARY.getCode());
243                 filter.setDelegationType(DelegationType.PRIMARY.getCode());
244                 filter.setExcludeDelegationType(false);
245                 addToFilterDescription(filteredByItems, "Primary Delegator Id");
246                 addedDelegationCriteria = true;
247                 filterOn = true;
248             }
249         }
250         if (!addedDelegationCriteria && ( (StringUtils.isNotBlank(filter.getDelegationType()) && DelegationType.SECONDARY.getCode().equals(filter.getDelegationType()))
251                 || StringUtils.isNotBlank(filter.getDelegatorId()) )) {
252             // using a secondary delegation
253             crit.addEqualTo("principalId", principalId);
254             if (StringUtils.isBlank(filter.getDelegatorId())) {
255                 filter.setDelegationType(DelegationType.SECONDARY.getCode());
256                 // if isExcludeDelegationType() we want to show the default action list which is set up later in this method
257                 if (!filter.isExcludeDelegationType()) {
258                     crit.addEqualTo("delegationType", DelegationType.SECONDARY.getCode());
259                     addToFilterDescription(filteredByItems, "Secondary Delegator Id");
260                     addedDelegationCriteria = true;
261                     filterOn = true;
262                 }
263             } else if (filter.getDelegatorId().trim().equals(KewApiConstants.ALL_CODE)) {
264                 // user wishes to see all secondary delegations
265                 crit.addEqualTo("delegationType", DelegationType.SECONDARY.getCode());
266                 filter.setDelegationType(DelegationType.SECONDARY.getCode());
267                 filter.setExcludeDelegationType(false);
268                 addToFilterDescription(filteredByItems, "Secondary Delegator Id");
269                 addedDelegationCriteria = true;
270                 filterOn = true;
271             } else if (!filter.getDelegatorId().trim().equals(
272                     KewApiConstants.DELEGATION_DEFAULT)) {
273                 // user has specified an id to see for secondary delegation
274                 filter.setDelegationType(DelegationType.SECONDARY.getCode());
275                 filter.setExcludeDelegationType(false);
276                 Criteria userCrit = new Criteria();
277                 Criteria groupCrit = new Criteria();
278                 if (filter.isExcludeDelegatorId()) {
279                     Criteria userNull = new Criteria();
280                     userCrit.addNotEqualTo("delegatorPrincipalId", filter.getDelegatorId());
281                     userNull.addIsNull("delegatorPrincipalId");
282                     userCrit.addOrCriteria(userNull);
283                     Criteria groupNull = new Criteria();
284                     groupCrit.addNotEqualTo("delegatorGroupId", filter.getDelegatorId());
285                     groupNull.addIsNull("delegatorGroupId");
286                     groupCrit.addOrCriteria(groupNull);
287                     crit.addAndCriteria(userCrit);
288                     crit.addAndCriteria(groupCrit);
289                 } else {
290                     Criteria orCrit = new Criteria();
291                     userCrit.addEqualTo("delegatorPrincipalId", filter.getDelegatorId());
292                     groupCrit.addEqualTo("delegatorGroupId", filter.getDelegatorId());
293                     orCrit.addOrCriteria(userCrit);
294                     orCrit.addOrCriteria(groupCrit);
295                     crit.addAndCriteria(orCrit);
296                 }
297                 addToFilterDescription(filteredByItems, "Secondary Delegator Id");
298                 addedDelegationCriteria = true;
299                 filterOn = true;
300             }
301         }
302 
303         // if we haven't added delegation criteria then use the default criteria below
304         if (!addedDelegationCriteria) {
305             crit.addEqualTo("principalId", principalId);
306             filter.setDelegationType(DelegationType.SECONDARY.getCode());
307             filter.setExcludeDelegationType(true);
308             Criteria critNotEqual = new Criteria();
309             Criteria critNull = new Criteria();
310             critNotEqual.addNotEqualTo("delegationType", DelegationType.SECONDARY.getCode());
311             critNull.addIsNull("delegationType");
312             critNotEqual.addOrCriteria(critNull);
313             crit.addAndCriteria(critNotEqual);
314         }
315 
316         if (! "".equals(filteredByItems)) {
317             filteredByItems = "Filtered by " + filteredByItems;
318         }
319         filter.setFilterLegend(filteredByItems);
320         filter.setFilterOn(filterOn);
321 
322         LOG.debug("returning from Action List criteria");
323         return crit;
324     }
325 
326     private void constructDocumentTypeCriteria(Criteria criteria, DocumentType documentType) {
327         // search this document type plus it's children
328         Criteria docTypeBaseCrit = new Criteria();
329         docTypeBaseCrit.addEqualTo("docName", documentType.getName());
330         criteria.addOrCriteria(docTypeBaseCrit);
331         Collection children = documentType.getChildrenDocTypes();
332         if (children != null) {
333             for (Iterator iterator = children.iterator(); iterator.hasNext();) {
334                 DocumentType childDocumentType = (DocumentType) iterator.next();
335                 constructDocumentTypeCriteria(criteria, childDocumentType);
336             }
337         }
338     }
339 
340     private void addToFilterDescription(String filterDescription, String labelToAdd) {
341         filterDescription += filterDescription.length() > 0 ? ", " : "";
342         filterDescription += labelToAdd;
343     }
344 
345     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')";
346 
347     public int getCount(final String workflowId) {
348         return (Integer)getPersistenceBrokerTemplate().execute(new PersistenceBrokerCallback() {
349             public Object doInPersistenceBroker(PersistenceBroker broker) {
350                 PreparedStatement statement = null;
351                 ResultSet resultSet = null;
352                 try {
353                     Connection connection = broker.serviceConnectionManager().getConnection();
354                     statement = connection.prepareStatement(ACTION_LIST_COUNT_QUERY);
355                     statement.setString(1, workflowId);
356                     resultSet = statement.executeQuery();
357                     if (!resultSet.next()) {
358                         throw new WorkflowRuntimeException("Error determining Action List Count.");
359                     }
360                     return resultSet.getInt(1);
361                 } catch (SQLException e) {
362                     throw new WorkflowRuntimeException("Error determining Action List Count.", e);
363                 } catch (LookupException e) {
364                     throw new WorkflowRuntimeException("Error determining Action List Count.", e);
365                 } finally {
366                     if (statement != null) {
367                         try {
368                             statement.close();
369                         } catch (SQLException e) {}
370                     }
371                     if (resultSet != null) {
372                         try {
373                             resultSet.close();
374                         } catch (SQLException e) {}
375                     }
376                 }
377             }
378         });
379     }
380 
381     private static final String MAX_ACTION_ITEM_DATE_ASSIGNED_AND_ACTION_LIST_COUNT_AND_QUERY = "select max(ASND_DT) as max_date, count(distinct(doc_hdr_id)) as total_records"
382             + "  from ("
383             + "       select ASND_DT,doc_hdr_id  "
384             + "         from KREW_ACTN_ITM_T   where    prncpl_id=? "
385             + "         group by  ASND_DT,doc_hdr_id "
386             + "       ) T";
387 
388 
389     /**
390      * Gets the max action item id and count doe a given user.
391      *
392      * @return A List with the first value being the maxActionItemId and the second value being the count
393      */
394     public List<Object> getMaxActionItemDateAssignedAndCountForUser(final String principalId){
395         return (List<Object>)getPersistenceBrokerTemplate().execute(new PersistenceBrokerCallback() {
396             public Object doInPersistenceBroker(PersistenceBroker broker) {
397                 PreparedStatement statement = null;
398                 ResultSet resultSet = null;
399                 List<Object> result = new ArrayList<Object>();
400                 try {
401                     Connection connection = broker.serviceConnectionManager().getConnection();
402                     statement = connection.prepareStatement(MAX_ACTION_ITEM_DATE_ASSIGNED_AND_ACTION_LIST_COUNT_AND_QUERY);
403                     statement.setString(1, principalId);
404                     resultSet = statement.executeQuery();
405                     if (!resultSet.next()) {
406                         throw new WorkflowRuntimeException("Error determining Action List Count and Max Action Item Id.");
407                     }
408                     else{
409                         result.add(resultSet.getTimestamp(1));
410                         result.add(resultSet.getInt(2));
411                     }
412                     return result;
413                 } catch (SQLException e) {
414                     throw new WorkflowRuntimeException("Error determining Action List Count and Max Action Item Id.", e);
415                 } catch (LookupException e) {
416                     throw new WorkflowRuntimeException("Error determining Action List Count and Max Action Item Id.", e);
417                 } finally {
418                     if (statement != null) {
419                         try {
420                             statement.close();
421                         } catch (SQLException e) {}
422                     }
423                     if (resultSet != null) {
424                         try {
425                             resultSet.close();
426                         } catch (SQLException e) {}
427                     }
428                 }
429             }
430         });
431     }
432 
433     /**
434      * Creates an Action List from the given collection of Action Items.  The Action List should
435      * contain only one action item per document.  The action item chosen should be the most "critical"
436      * or "important" one on the document.
437      *
438      * @return the Action List as a Collection of ActionItems
439      */
440     private <T extends ActionItemActionListExtension> Collection<T> createActionListForUser(Collection<T> actionItems) {
441         Map<String, T> actionItemMap = new HashMap<String, T>();
442         ActionListPriorityComparator comparator = new ActionListPriorityComparator();
443         for (T potentialActionItem: actionItems) {
444             T existingActionItem = actionItemMap.get(potentialActionItem.getDocumentId());
445             if (existingActionItem == null || comparator.compare(potentialActionItem, existingActionItem) > 0) {
446                 actionItemMap.put(potentialActionItem.getDocumentId(), potentialActionItem);
447             }
448         }
449         return actionItemMap.values();
450     }
451 
452     /**
453      * Creates an Action List from the given collection of Action Items.  The Action List should
454      * contain only one action item per user.  The action item chosen should be the most "critical"
455      * or "important" one on the document.
456      *
457      * @return the Action List as a Collection of ActionItems
458      */
459     private Collection<ActionItemActionListExtension> createActionListForRouteHeader(Collection<ActionItemActionListExtension> actionItems) {
460         Map<String, ActionItemActionListExtension> actionItemMap = new HashMap<String, ActionItemActionListExtension>();
461         ActionListPriorityComparator comparator = new ActionListPriorityComparator();
462         for (ActionItemActionListExtension potentialActionItem: actionItems) {
463             ActionItemActionListExtension existingActionItem = actionItemMap.get(potentialActionItem.getPrincipalId());
464             if (existingActionItem == null || comparator.compare(potentialActionItem, existingActionItem) > 0) {
465                 actionItemMap.put(potentialActionItem.getPrincipalId(), potentialActionItem);
466             }
467         }
468         return actionItemMap.values();
469     }
470 
471     private <T extends ActionItemActionListExtension> Collection<T> getActionItemsInActionList(Class<T> objectsToRetrieve, String principalId, ActionListFilter filter) {
472         LOG.debug("getting action list for user " + principalId);
473         Criteria crit = null;
474         if (filter == null) {
475             crit = new Criteria();
476             crit.addEqualTo("principalId", principalId);
477         } else {
478             crit = setUpActionListCriteria(principalId, filter);
479         }
480         LOG.debug("running query to get action list for criteria " + crit);
481         Collection<T> collection = this.getPersistenceBrokerTemplate().getCollectionByQuery(new QueryByCriteria(objectsToRetrieve, crit));
482         LOG.debug("found " + collection.size() + " action items for user " + principalId);
483         return createActionListForUser(collection);
484     }
485 
486     public Collection<OutboxItemActionListExtension> getOutbox(String principalId, ActionListFilter filter) {
487         return getActionItemsInActionList(OutboxItemActionListExtension.class, principalId, filter);
488     }
489 
490     /**
491      *
492      * Deletes all outbox items specified by the list of ids
493      *
494      * @see org.kuali.rice.kew.actionlist.dao.ActionListDAO#removeOutboxItems(java.lang.String, java.util.List)
495      */
496     public void removeOutboxItems(String principalId, List<String> outboxItems) {
497         Criteria crit = new Criteria();
498         crit.addIn("id", outboxItems);
499         getPersistenceBrokerTemplate().deleteByQuery(new QueryByCriteria(OutboxItemActionListExtension.class, crit));
500     }
501 
502     /**
503      * Saves an outbox item
504      *
505      * @see org.kuali.rice.kew.actionlist.dao.ActionListDAO#saveOutboxItem(org.kuali.rice.kew.actionitem.OutboxItemActionListExtension)
506      */
507     public void saveOutboxItem(OutboxItemActionListExtension outboxItem) {
508         this.getPersistenceBrokerTemplate().store(outboxItem);
509     }
510 
511     /**
512      * Gets the outbox item associated with the document id
513      *
514      * @see org.kuali.rice.kew.actionlist.dao.ActionListDAO#getOutboxByDocumentId(java.lang.String)
515      */
516     public OutboxItemActionListExtension getOutboxByDocumentId(String documentId) {
517         Criteria crit = new Criteria();
518         crit.addEqualTo("documentId", documentId);
519         return (OutboxItemActionListExtension)getPersistenceBrokerTemplate().getObjectByQuery(new QueryByCriteria(OutboxItemActionListExtension.class, crit));
520     }
521 
522     public OutboxItemActionListExtension getOutboxByDocumentIdUserId(String documentId, String userId) {
523         Criteria crit = new Criteria();
524         crit.addEqualTo("documentId", documentId);
525         crit.addEqualTo("principalId", userId);
526         return (OutboxItemActionListExtension)getPersistenceBrokerTemplate().getObjectByQuery(new QueryByCriteria(OutboxItemActionListExtension.class, crit));
527     }
528 
529     private Date beginningOfDay(Date date) {
530         Calendar cal = Calendar.getInstance();
531         cal.setTime(date);
532         cal.set(Calendar.HOUR_OF_DAY, 0);
533         cal.set(Calendar.MINUTE, 0);
534         cal.set(Calendar.SECOND, 0);
535         return cal.getTime();
536     }
537 
538     private Date endOfDay(Date date) {
539         Calendar cal = Calendar.getInstance();
540         cal.setTime(date);
541         cal.set(Calendar.HOUR_OF_DAY, 23);
542         cal.set(Calendar.MINUTE, 59);
543         cal.set(Calendar.SECOND, 59);
544         return cal.getTime();
545     }
546 
547 }