001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kew.actionlist.dao.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.ojb.broker.PersistenceBroker;
020import org.apache.ojb.broker.accesslayer.LookupException;
021import org.apache.ojb.broker.query.Criteria;
022import org.apache.ojb.broker.query.QueryByCriteria;
023import org.kuali.rice.core.api.delegation.DelegationType;
024import org.kuali.rice.kew.actionitem.ActionItem;
025import org.kuali.rice.kew.actionitem.ActionItemActionListExtension;
026import org.kuali.rice.kew.actionitem.OutboxItemActionListExtension;
027import org.kuali.rice.kew.actionlist.ActionListFilter;
028import org.kuali.rice.kew.actionlist.dao.ActionListDAO;
029import org.kuali.rice.kew.api.WorkflowRuntimeException;
030import org.kuali.rice.kew.doctype.bo.DocumentType;
031import org.kuali.rice.kew.service.KEWServiceLocator;
032import org.kuali.rice.kew.api.KewApiConstants;
033import org.kuali.rice.kim.api.services.KimApiServiceLocator;
034import org.springmodules.orm.ojb.PersistenceBrokerCallback;
035import org.springmodules.orm.ojb.support.PersistenceBrokerDaoSupport;
036
037import java.sql.Connection;
038import java.sql.PreparedStatement;
039import java.sql.ResultSet;
040import java.sql.SQLException;
041import java.sql.Timestamp;
042import java.util.ArrayList;
043import java.util.Calendar;
044import java.util.Collection;
045import java.util.Date;
046import java.util.HashMap;
047import java.util.Iterator;
048import java.util.List;
049import java.util.Map;
050
051/**
052 * OJB implementation of the {@link ActionListDAO}.
053 *
054 * @author Kuali Rice Team (rice.collab@kuali.org)
055 */
056public class ActionListDAOOjbImpl extends PersistenceBrokerDaoSupport implements ActionListDAO {
057
058    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ActionListDAOOjbImpl.class);
059
060    public Collection<ActionItemActionListExtension> getActionList(String principalId, ActionListFilter filter) {
061        return getActionItemsInActionList(ActionItemActionListExtension.class, principalId, filter);
062    }
063
064    public Collection<ActionItemActionListExtension> getActionListForSingleDocument(String documentId) {
065        LOG.debug("getting action list for document id " + documentId);
066        Criteria crit = new Criteria();
067        crit.addEqualTo("documentId", documentId);
068        Collection<ActionItemActionListExtension> collection = this.getPersistenceBrokerTemplate().getCollectionByQuery(new QueryByCriteria(ActionItemActionListExtension.class, crit));
069        LOG.debug("found " + collection.size() + " action items for document id " + documentId);
070        return createActionListForRouteHeader(collection);
071    }
072
073    private Criteria setUpActionListCriteria(String principalId, ActionListFilter filter) {
074        LOG.debug("setting up Action List criteria");
075        Criteria crit = new Criteria();
076        boolean filterOn = false;
077        String filteredByItems = "";
078
079        if (filter.getActionRequestCd() != null && !"".equals(filter.getActionRequestCd().trim()) && !filter.getActionRequestCd().equals(KewApiConstants.ALL_CODE)) {
080            if (filter.isExcludeActionRequestCd()) {
081                crit.addNotEqualTo("actionRequestCd", filter.getActionRequestCd());
082            } else {
083                crit.addEqualTo("actionRequestCd", filter.getActionRequestCd());
084            }
085            filteredByItems += filteredByItems.length() > 0 ? ", " : "";
086            filteredByItems += "Action Requested";
087        }
088
089        if (filter.getCreateDateFrom() != null || filter.getCreateDateTo() != null) {
090            if (filter.isExcludeCreateDate()) {
091                if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() != null) {
092                    crit.addNotBetween("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()), new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
093                } else if (filter.getCreateDateFrom() != null && filter.getCreateDateTo() == null) {
094                    crit.addLessOrEqualThan("routeHeader.createDate", new Timestamp(beginningOfDay(filter.getCreateDateFrom()).getTime()));
095                } else if (filter.getCreateDateFrom() == null && filter.getCreateDateTo() != null) {
096                    crit.addGreaterOrEqualThan("routeHeader.createDate", new Timestamp(endOfDay(filter.getCreateDateTo()).getTime()));
097                }
098            } else {
099                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}