001 /**
002 * Copyright 2005-2014 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.docsearch.dao.impl;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.kuali.rice.core.api.uif.RemotableAttributeField;
020 import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
021 import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
022 import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
023 import org.kuali.rice.kew.impl.document.search.DocumentSearchGenerator;
024 import org.kuali.rice.kew.docsearch.dao.DocumentSearchDAO;
025 import org.kuali.rice.kew.api.KewApiConstants;
026 import org.kuali.rice.kew.util.PerformanceLogger;
027 import org.kuali.rice.krad.util.KRADConstants;
028 import org.springframework.dao.DataAccessException;
029 import org.springframework.jdbc.core.ConnectionCallback;
030 import org.springframework.jdbc.core.JdbcTemplate;
031 import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
032
033 import javax.sql.DataSource;
034 import java.sql.Connection;
035 import java.sql.ResultSet;
036 import java.sql.SQLException;
037 import java.sql.Statement;
038 import java.util.List;
039
040 /**
041 * Spring JdbcTemplate implementation of DocumentSearchDAO
042 *
043 * @author Kuali Rice Team (rice.collab@kuali.org)
044 *
045 */
046 public class DocumentSearchDAOJdbcImpl implements DocumentSearchDAO {
047
048 public static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentSearchDAOJdbcImpl.class);
049 private static final int DEFAULT_FETCH_MORE_ITERATION_LIMIT = 10;
050
051 private DataSource dataSource;
052
053 public void setDataSource(DataSource dataSource) {
054 this.dataSource = new TransactionAwareDataSourceProxy(dataSource);
055 }
056
057 @Override
058 public DocumentSearchResults.Builder findDocuments(final DocumentSearchGenerator documentSearchGenerator, final DocumentSearchCriteria criteria, final boolean criteriaModified, final List<RemotableAttributeField> searchFields) {
059 final int maxResultCap = getMaxResultCap(criteria);
060 try {
061 final JdbcTemplate template = new JdbcTemplate(dataSource);
062
063 return template.execute(new ConnectionCallback<DocumentSearchResults.Builder>() {
064 @Override
065 public DocumentSearchResults.Builder doInConnection(final Connection con) throws SQLException {
066 final Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
067 try {
068 final int fetchIterationLimit = getFetchMoreIterationLimit();
069 final int fetchLimit = fetchIterationLimit * maxResultCap;
070 statement.setFetchSize(maxResultCap + 1);
071 statement.setMaxRows(fetchLimit + 1);
072
073 PerformanceLogger perfLog = new PerformanceLogger();
074 String sql = documentSearchGenerator.generateSearchSql(criteria, searchFields);
075 perfLog.log("Time to generate search sql from documentSearchGenerator class: " + documentSearchGenerator
076 .getClass().getName(), true);
077 LOG.info("Executing document search with statement max rows: " + statement.getMaxRows());
078 LOG.info("Executing document search with statement fetch size: " + statement.getFetchSize());
079 perfLog = new PerformanceLogger();
080 final ResultSet rs = statement.executeQuery(sql);
081 try {
082 perfLog.log("Time to execute doc search database query.", true);
083 final Statement searchAttributeStatement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
084 try {
085 return documentSearchGenerator.processResultSet(criteria, criteriaModified, searchAttributeStatement, rs, maxResultCap, fetchLimit);
086 } finally {
087 try {
088 searchAttributeStatement.close();
089 } catch (SQLException e) {
090 LOG.warn("Could not close search attribute statement.");
091 }
092 }
093 } finally {
094 try {
095 rs.close();
096 } catch (SQLException e) {
097 LOG.warn("Could not close result set.");
098 }
099 }
100 } finally {
101 try {
102 statement.close();
103 } catch (SQLException e) {
104 LOG.warn("Could not close statement.");
105 }
106 }
107 }
108 });
109
110 } catch (DataAccessException dae) {
111 String errorMsg = "DataAccessException: " + dae.getMessage();
112 LOG.error("getList() " + errorMsg, dae);
113 throw new RuntimeException(errorMsg, dae);
114 } catch (Exception e) {
115 String errorMsg = "LookupException: " + e.getMessage();
116 LOG.error("getList() " + errorMsg, e);
117 throw new RuntimeException(errorMsg, e);
118 }
119 }
120
121 /**
122 * Returns the maximum number of results that should be returned from the document search.
123 *
124 * @param criteria the criteria in which to check for a max results value
125 * @return the maximum number of results that should be returned from a document search
126 */
127 public int getMaxResultCap(DocumentSearchCriteria criteria) {
128 int systemLimit = KewApiConstants.DOCUMENT_LOOKUP_DEFAULT_RESULT_CAP;
129 String resultCapValue = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.DOCUMENT_SEARCH_DETAIL_TYPE, KewApiConstants.DOC_SEARCH_RESULT_CAP);
130 if (StringUtils.isNotBlank(resultCapValue)) {
131 try {
132 int configuredLimit = Integer.parseInt(resultCapValue);
133 if (configuredLimit <= 0) {
134 LOG.warn(KewApiConstants.DOC_SEARCH_RESULT_CAP + " was less than or equal to zero. Please use a positive integer.");
135 } else {
136 systemLimit = configuredLimit;
137 }
138 } catch (NumberFormatException e) {
139 LOG.warn(KewApiConstants.DOC_SEARCH_RESULT_CAP + " is not a valid number. Value was " + resultCapValue + ". Using default: " + KewApiConstants.DOCUMENT_LOOKUP_DEFAULT_RESULT_CAP);
140 }
141 }
142 int maxResults = systemLimit;
143 if (criteria.getMaxResults() != null) {
144 int criteriaLimit = criteria.getMaxResults().intValue();
145 if (criteriaLimit > systemLimit) {
146 LOG.warn("Result set cap of " + criteriaLimit + " is greater than system value of " + systemLimit);
147 } else {
148 if (criteriaLimit < 0) {
149 LOG.warn("Criteria results limit was less than zero.");
150 criteriaLimit = 0;
151 }
152 maxResults = criteriaLimit;
153 }
154 }
155 return maxResults;
156 }
157
158 public int getFetchMoreIterationLimit() {
159 int fetchMoreLimit = DEFAULT_FETCH_MORE_ITERATION_LIMIT;
160 String fetchMoreLimitValue = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.DOCUMENT_SEARCH_DETAIL_TYPE, KewApiConstants.DOC_SEARCH_FETCH_MORE_ITERATION_LIMIT);
161 if (!StringUtils.isBlank(fetchMoreLimitValue)) {
162 try {
163 fetchMoreLimit = Integer.parseInt(fetchMoreLimitValue);
164 if (fetchMoreLimit < 0) {
165 LOG.warn(KewApiConstants.DOC_SEARCH_FETCH_MORE_ITERATION_LIMIT + " was less than zero. Please use a value greater than or equal to zero.");
166 fetchMoreLimit = DEFAULT_FETCH_MORE_ITERATION_LIMIT;
167 }
168 } catch (NumberFormatException e) {
169 LOG.warn(KewApiConstants.DOC_SEARCH_FETCH_MORE_ITERATION_LIMIT + " is not a valid number. Value was " + fetchMoreLimitValue);
170 }
171 }
172 return fetchMoreLimit;
173 }
174
175 }