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.routeheader.dao.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.commons.lang.exception.ExceptionUtils;
020import org.apache.ojb.broker.OptimisticLockException;
021import org.apache.ojb.broker.PersistenceBroker;
022import org.apache.ojb.broker.accesslayer.LookupException;
023import org.apache.ojb.broker.query.Criteria;
024import org.apache.ojb.broker.query.QueryByCriteria;
025import org.apache.ojb.broker.query.QueryFactory;
026import org.apache.ojb.broker.query.ReportQueryByCriteria;
027import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
028import org.kuali.rice.core.api.util.RiceConstants;
029import org.kuali.rice.core.framework.persistence.platform.DatabasePlatform;
030import org.kuali.rice.kew.actionitem.ActionItem;
031import org.kuali.rice.kew.actionlist.service.ActionListService;
032import org.kuali.rice.kew.api.WorkflowRuntimeException;
033import org.kuali.rice.kew.api.action.ActionRequestStatus;
034import org.kuali.rice.kew.api.exception.LockingException;
035import org.kuali.rice.kew.docsearch.SearchableAttributeValue;
036import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
037import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent;
038import org.kuali.rice.kew.routeheader.dao.DocumentRouteHeaderDAO;
039import org.kuali.rice.kew.service.KEWServiceLocator;
040import org.springframework.dao.CannotAcquireLockException;
041import org.springmodules.orm.ojb.OjbFactoryUtils;
042import org.springmodules.orm.ojb.PersistenceBrokerCallback;
043import org.springmodules.orm.ojb.support.PersistenceBrokerDaoSupport;
044
045import java.sql.Connection;
046import java.sql.PreparedStatement;
047import java.sql.ResultSet;
048import java.sql.SQLException;
049import java.sql.Statement;
050import java.util.ArrayList;
051import java.util.Collection;
052import java.util.Iterator;
053import java.util.List;
054import java.util.Set;
055
056public class DocumentRouteHeaderDAOOjbImpl extends PersistenceBrokerDaoSupport implements DocumentRouteHeaderDAO {
057
058    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentRouteHeaderDAOOjbImpl.class);
059
060    public void saveRouteHeader(DocumentRouteHeaderValue routeHeader) {
061        if ( LOG.isDebugEnabled() ) {
062            LOG.debug( "About to Save the route Header: " + routeHeader.getDocumentId() + " / version=" + routeHeader.getVersionNumber() );
063            DocumentRouteHeaderValue currHeader = findRouteHeader(routeHeader.getDocumentId());
064            if ( currHeader != null ) {
065                LOG.debug( "Current Header Version: " + currHeader.getVersionNumber() );
066//                for ( SearchableAttributeValue s : currHeader.get() ) {
067//                    LOG.debug( "SA: " + s.getSearchableAttributeValueId() + " / version=" + s.get )
068//                }
069            } else {
070                LOG.debug( "Current Header: null" );
071            }
072            LOG.debug( ExceptionUtils.getStackTrace(new Throwable()) );
073        }
074        try {
075            getPersistenceBrokerTemplate().store(routeHeader);
076            routeHeader.getDocumentContent().setDocumentId(routeHeader.getDocumentId());
077            getPersistenceBrokerTemplate().store(routeHeader.getDocumentContent());
078        } catch ( RuntimeException ex ) {
079            if ( ex.getCause() instanceof OptimisticLockException ) {
080                 LOG.error( "Optimistic Locking Exception saving document header or content. Offending object: " + ((OptimisticLockException)ex.getCause()).getSourceObject() 
081                 + "; DocumentId = " + routeHeader.getDocumentId() + " ;  Version Number = " + routeHeader.getVersionNumber());
082            }
083            LOG.error( "Unable to save document header or content. Route Header: " + routeHeader, ex );
084            throw ex;
085        }
086    }
087
088    public DocumentRouteHeaderValueContent getContent(String documentId) {
089        Criteria crit = new Criteria();
090        crit.addEqualTo("documentId", documentId);
091        return (DocumentRouteHeaderValueContent)this.getPersistenceBrokerTemplate().getObjectByQuery(new QueryByCriteria(DocumentRouteHeaderValueContent.class, crit));
092    }
093
094    public void clearRouteHeaderSearchValues(String documentId) {
095        Criteria crit = new Criteria();
096        crit.addEqualTo("documentId", documentId);
097        QueryByCriteria query = new QueryByCriteria(SearchableAttributeValue.class, crit);
098        query.addOrderByAscending("searchableAttributeValueId");
099        Collection<SearchableAttributeValue> results = this.getPersistenceBrokerTemplate().getCollectionByQuery(query);
100        if (!results.isEmpty()) {
101            for (SearchableAttributeValue srchAttrVal: results) {
102                this.getPersistenceBrokerTemplate().delete(srchAttrVal);
103            }
104        }
105    }
106
107    public Collection<SearchableAttributeValue> findSearchableAttributeValues(String documentId) {
108        Criteria crit = new Criteria();
109        crit.addEqualTo("documentId", documentId);
110        QueryByCriteria query = new QueryByCriteria(SearchableAttributeValue.class, crit);
111        query.addOrderByAscending("searchableAttributeValueId");
112        return this.getPersistenceBrokerTemplate().getCollectionByQuery(query);
113    }
114
115    public void lockRouteHeader(final String documentId, final boolean wait) {
116
117        /*
118         * String sql = (wait ? LOCK_SQL_WAIT : LOCK_SQL_NOWAIT); try { getJdbcTemplate().update(sql, new Object[] { documentId }); } catch (CannotAcquireLockException e) { throw new LockingException("Could not aquire lock on document, documentId=" + documentId, e); }
119         */
120
121        this.getPersistenceBrokerTemplate().execute(new PersistenceBrokerCallback() {
122            public Object doInPersistenceBroker(PersistenceBroker broker) {
123                PreparedStatement statement = null;
124                try {
125                    Connection connection = broker.serviceConnectionManager().getConnection();
126                    String sql = getPlatform().getLockRouteHeaderQuerySQL(documentId, wait);
127                    statement = connection.prepareStatement(sql);
128                    statement.setString(1, documentId);
129                    statement.execute();
130                    return null;
131                } catch (SQLException e) {
132                    throw new LockingException("Could not aquire lock on document, documentId=" + documentId, e);
133                } catch (LookupException e) {
134                    throw new LockingException("Could not aquire lock on document, documentId=" + documentId, e);
135                } catch (CannotAcquireLockException e) {
136                    throw new LockingException("Could not aquire lock on document, documentId=" + documentId, e);
137                } finally {
138                    if (statement != null) {
139                        try {
140                            statement.close();
141                        } catch (SQLException e) {
142                        }
143                    }
144                }
145            }
146        });
147
148    }
149
150    public DocumentRouteHeaderValue findRouteHeader(String documentId) {
151        return findRouteHeader(documentId, false);
152    }
153
154    public DocumentRouteHeaderValue findRouteHeader(String documentId, boolean clearCache) {
155        Criteria crit = new Criteria();
156        crit.addEqualTo("documentId", documentId);
157        if (clearCache) {
158                this.getPersistenceBrokerTemplate().clearCache();
159        }
160        return (DocumentRouteHeaderValue) this.getPersistenceBrokerTemplate().getObjectByQuery(new QueryByCriteria(DocumentRouteHeaderValue.class, crit));
161    }
162
163    public Collection<DocumentRouteHeaderValue> findRouteHeaders(Collection<String> documentIds) {
164        return findRouteHeaders(documentIds, false);
165    }
166    
167    public Collection<DocumentRouteHeaderValue> findRouteHeaders(Collection<String> documentIds, boolean clearCache) {
168        if (documentIds == null || documentIds.isEmpty()) {
169                return null;
170        }
171        Criteria crit = new Criteria();
172        crit.addIn("documentId", documentIds);
173        if (clearCache) {
174                this.getPersistenceBrokerTemplate().clearCache();
175        }
176        return (Collection<DocumentRouteHeaderValue>) this.getPersistenceBrokerTemplate().getCollectionByQuery(new QueryByCriteria(DocumentRouteHeaderValue.class, crit));
177    }
178
179    public void deleteRouteHeader(DocumentRouteHeaderValue routeHeader) {
180        this.getPersistenceBrokerTemplate().delete(routeHeader);
181    }
182
183    public String getNextDocumentId() {
184        return (String)this.getPersistenceBrokerTemplate().execute(new PersistenceBrokerCallback() {
185            public Object doInPersistenceBroker(PersistenceBroker broker) {
186                return getPlatform().getNextValSQL("KREW_DOC_HDR_S", broker).toString();
187                    }
188        });
189    }
190
191    protected DatabasePlatform getPlatform() {
192        return (DatabasePlatform)GlobalResourceLoader.getService(RiceConstants.DB_PLATFORM);
193    }
194
195    public Collection<String> findPendingByResponsibilityIds(Set<String> responsibilityIds) {
196        Collection<String> documentIds = new ArrayList();
197        if (responsibilityIds.isEmpty()) {
198            return documentIds;
199        }
200        PersistenceBroker broker = null;
201        Connection conn = null;
202        Statement statement = null;
203        ResultSet rs = null;
204        try {
205            broker = getPersistenceBroker(false);
206            conn = broker.serviceConnectionManager().getConnection();
207            String respIds = "('";
208            int index = 0;
209            for (String responsibilityId : responsibilityIds) {
210                respIds += responsibilityId + (index == responsibilityIds.size()-1 ? "" : "','");
211                index++;
212            }
213            respIds += "')";
214            String query = "SELECT DISTINCT(doc_hdr_id) FROM KREW_ACTN_RQST_T "+
215                "WHERE (STAT_CD='" +
216                ActionRequestStatus.INITIALIZED.getCode()+
217                "' OR STAT_CD='"+
218                ActionRequestStatus.ACTIVATED.getCode()+
219                "') AND RSP_ID IN "+respIds;
220            LOG.debug("Query to find pending documents for requeue: " + query);
221            statement = conn.createStatement();
222            rs = statement.executeQuery(query);
223            while (rs.next()) {
224                documentIds.add(rs.getString(1));
225            }
226        } catch (SQLException sqle) {
227            LOG.error("SQLException: " + sqle.getMessage(), sqle);
228            throw new WorkflowRuntimeException(sqle);
229        } catch (LookupException le) {
230            LOG.error("LookupException: " + le.getMessage(), le);
231            throw new WorkflowRuntimeException(le);
232        } finally {
233                if (rs != null) {
234                try {
235                    rs.close();
236                } catch (SQLException e) {
237                    LOG.warn("Could not close result set.");
238                }
239            }
240            if (statement != null) {
241                try {
242                    statement.close();
243                } catch (SQLException e) {
244                    LOG.warn("Could not close statement.");
245                }
246            }
247            try {
248                if (broker != null) {
249                    OjbFactoryUtils.releasePersistenceBroker(broker, this.getPersistenceBrokerTemplate().getPbKey());
250                }
251            } catch (Exception e) {
252                LOG.error("Failed closing connection: " + e.getMessage(), e);
253            }
254        }
255        return documentIds;
256    }
257
258    public boolean hasSearchableAttributeValue(String documentId, String searchableAttributeKey, String searchableAttributeValue) {
259        Criteria crit = new Criteria();
260        crit.addEqualTo("documentId", documentId);
261        crit.addEqualTo("searchableAttributeKey", searchableAttributeKey);
262        Collection results = getPersistenceBrokerTemplate().getCollectionByQuery(new QueryByCriteria(SearchableAttributeValue.class, crit));
263        if (!results.isEmpty()) {
264            for (Iterator iterator = results.iterator(); iterator.hasNext();) {
265                SearchableAttributeValue attribute = (SearchableAttributeValue) iterator.next();
266                if (StringUtils.equals(attribute.getSearchableAttributeDisplayValue(), searchableAttributeValue)) {
267                    return true;
268                }
269            }
270        }
271        return false;
272    }
273
274    public String getApplicationIdByDocumentId(String documentId) {
275        if (documentId == null) {
276                throw new IllegalArgumentException("Encountered a null document ID.");
277        }
278        String applicationId = null;
279        PersistenceBroker broker = null;
280        Connection conn = null;
281        PreparedStatement statement = null;
282        ResultSet rs = null;
283        try {
284            broker = this.getPersistenceBroker(false);
285            conn = broker.serviceConnectionManager().getConnection();
286            String query = "SELECT DT.APPL_ID FROM KREW_DOC_TYP_T DT, KREW_DOC_HDR_T DH "+
287                "WHERE DH.DOC_TYP_ID=DT.DOC_TYP_ID AND "+
288                "DH.DOC_HDR_ID=?";
289            statement = conn.prepareStatement(query);
290            statement.setString(1, documentId);
291            rs = statement.executeQuery();
292            if (rs.next()) {
293                applicationId = rs.getString(1);
294                if (rs.wasNull()) {
295                        applicationId = null;
296                }
297            }
298        } catch (SQLException sqle) {
299            LOG.error("SQLException: " + sqle.getMessage(), sqle);
300            throw new WorkflowRuntimeException(sqle);
301        } catch (LookupException le) {
302            LOG.error("LookupException: " + le.getMessage(), le);
303            throw new WorkflowRuntimeException(le);
304        } finally {
305                if (rs != null) {
306                try {
307                    rs.close();
308                } catch (SQLException e) {
309                    LOG.warn("Could not close result set.");
310                }
311            }
312            if (statement != null) {
313                try {
314                    statement.close();
315                } catch (SQLException e) {
316                    LOG.warn("Could not close statement.");
317                }
318            }
319            try {
320                if (broker != null) {
321                    OjbFactoryUtils.releasePersistenceBroker(broker, this.getPersistenceBrokerTemplate().getPbKey());
322                }
323            } catch (Exception e) {
324                LOG.error("Failed closing connection: " + e.getMessage(), e);
325            }
326        }
327        return applicationId;
328    }
329
330    public String getDocumentStatus(String documentId) {
331        Criteria crit = new Criteria();
332        crit.addEqualTo("documentId", documentId);
333        ReportQueryByCriteria query = QueryFactory.newReportQuery(DocumentRouteHeaderValue.class, crit);
334        query.setAttributes(new String[] { "docRouteStatus" });
335        String status = null;
336        Iterator iter = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(query);
337        while (iter.hasNext()) {
338            Object[] row = (Object[]) iter.next();
339            status = (String)row[0];
340        }
341        return status;
342    }
343    
344    public String getAppDocId(String documentId) {
345                Criteria crit = new Criteria();
346                crit.addEqualTo("documentId", documentId);
347                ReportQueryByCriteria query = QueryFactory.newReportQuery(DocumentRouteHeaderValue.class, crit);
348                query.setAttributes(new String[] { "appDocId" });
349                String appDocId = null;
350                Iterator iter = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(query);
351                while (iter.hasNext()) {
352                        Object[] row = (Object[]) iter.next();
353                        appDocId = (String)row[0];
354                }
355                return appDocId;
356         }
357
358    public String getAppDocStatus(String documentId) {
359        Criteria crit = new Criteria();
360        crit.addEqualTo("documentId", documentId);
361        ReportQueryByCriteria query = QueryFactory.newReportQuery(DocumentRouteHeaderValue.class, crit);
362        query.setAttributes(new String[] { "appDocStatus" });
363        String appDocStatus = null;
364        Iterator iter = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(query);
365        while (iter.hasNext()) {
366            Object[] row = (Object[]) iter.next();
367            appDocStatus = (String)row[0];
368        }
369        return appDocStatus;
370    }
371
372    public void save(SearchableAttributeValue searchableAttributeValue) {
373        getPersistenceBrokerTemplate().store(searchableAttributeValue);
374    }
375
376        public Collection findByDocTypeAndAppId(String documentTypeName,
377                        String appId) {
378        Collection documentIds = new ArrayList();
379
380        PersistenceBroker broker = null;
381        Connection conn = null;
382        ResultSet rs = null;
383        try {
384            broker = getPersistenceBroker(false);
385            conn = broker.serviceConnectionManager().getConnection();
386
387            String query = 
388                        "SELECT DISTINCT " +
389                        "    (docHdr.doc_hdr_id) " +
390                        "FROM " +
391                        "    KREW_DOC_HDR_T docHdr, " +
392                        "    KREW_DOC_TYP_T docTyp " +
393                        "WHERE " +
394                        "    docHdr.APP_DOC_ID     = ? " +
395                        "    AND docHdr.DOC_TYP_ID = docTyp.DOC_TYP_ID " +
396                        "    AND docTyp.DOC_TYP_NM = ?";
397            
398            LOG.debug("Query to find documents by app id: " + query);
399            
400            PreparedStatement stmt = conn.prepareStatement(query);
401            stmt.setString(1, appId);
402            stmt.setString(2, documentTypeName);
403            rs = stmt.executeQuery();
404            
405            while (rs.next()) {
406                documentIds.add(new String(rs.getString(1)));
407            }
408            rs.close();
409        } catch (SQLException sqle) {
410            LOG.error("SQLException: " + sqle.getMessage(), sqle);
411            throw new WorkflowRuntimeException(sqle);
412        } catch (LookupException le) {
413            LOG.error("LookupException: " + le.getMessage(), le);
414            throw new WorkflowRuntimeException(le);
415        } finally {
416            try {
417                if (broker != null) {
418                    OjbFactoryUtils.releasePersistenceBroker(broker, this.getPersistenceBrokerTemplate().getPbKey());
419                }
420            } catch (Exception e) {
421                LOG.error("Failed closing connection: " + e.getMessage(), e);
422            }
423        }
424        return documentIds;
425        }
426
427}