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