001: /*
002: * Copyright 2004-2006 the original author or authors.
003: *
004: * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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:
017: package org.compass.gps.device.jdbc;
018:
019: import java.lang.reflect.Array;
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import javax.sql.DataSource;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.compass.core.CompassException;
029: import org.compass.core.CompassSession;
030: import org.compass.gps.CompassGpsException;
031: import org.compass.gps.device.AbstractGpsDevice;
032: import org.compass.gps.device.jdbc.dialect.DefaultJdbcDialect;
033: import org.compass.gps.device.jdbc.dialect.JdbcDialect;
034:
035: /**
036: * A helper base class for Jdbc Gps Device. Provides supprot for
037: * <code>DataSource</code> and
038: * {@link org.compass.gps.device.jdbc.dialect.JdbcDialect}. Also
039: * provides template like support for processing database indexing using the
040: * <code>IndexExecution</code> object hint, and a set of callback methods:
041: * {@link #processResultSet(Object, ResultSet, CompassSession)},
042: * {@link #processRow(Object, ResultSet, CompassSession)}, and
043: * {@link #processRowValue(Object, ResultSet, CompassSession)}. One of the
044: * callback mehtods should be overriden by the derived class otherwize the class
045: * won't index anyhting.
046: *
047: * @author kimchy
048: */
049: public abstract class AbstractJdbcGpsDevice extends AbstractGpsDevice
050: implements JdbcGpsDevice {
051:
052: protected Log log = LogFactory.getLog(getClass());
053:
054: /**
055: * A hint object which provides the statement query to execute or the actual
056: * <code>PreparedStatement</code>. It also provides a general data holder
057: * called <code>description</code>.
058: *
059: * @author kimchy
060: */
061: public static class IndexExecution {
062: private Object description;
063:
064: private PreparedStatement statement;
065:
066: private String statementQuery;
067:
068: public IndexExecution(Object description) {
069: this .description = description;
070: }
071:
072: public IndexExecution(Object description, String statementQuery) {
073: this .description = description;
074: this .statementQuery = statementQuery;
075: }
076:
077: public IndexExecution(Object description,
078: PreparedStatement statement) {
079: this .description = description;
080: this .statement = statement;
081: }
082:
083: public Object getDescription() {
084: return description;
085: }
086:
087: public PreparedStatement getStatement() {
088: return statement;
089: }
090:
091: public String getStatementQuery() {
092: return statementQuery;
093: }
094:
095: public void setStatementQuery(String statementQuery) {
096: this .statementQuery = statementQuery;
097: }
098:
099: public void setStatement(PreparedStatement statement) {
100: this .statement = statement;
101: }
102:
103: public void setDescription(Object description) {
104: this .description = description;
105: }
106: }
107:
108: protected DataSource dataSource;
109:
110: protected JdbcDialect dialect = new DefaultJdbcDialect();
111:
112: protected void doStart() throws CompassGpsException {
113: if (dataSource == null) {
114: throw new IllegalArgumentException(
115: "dataSource property must be set");
116: }
117: }
118:
119: /**
120: * If this variable is set to a non-zero value, it will be used for setting
121: * the fetchSize property on statements used for query processing.
122: */
123: private int fetchSize = 0;
124:
125: /**
126: * Performs the indexing operation.
127: * <p/>
128: * Calls the abstract {@link #doGetIndexExecutions(Connection)} method with
129: * an open connection to get the list of {@link IndexExecution} to perform.
130: * <p/>
131: * For each {@link IndexExecution}, executes the select query, and calls
132: * the {@link #processResultSet(Object, ResultSet, CompassSession)} for the
133: * returned <code>ResultSet</code>.
134: */
135: protected void doIndex(CompassSession session)
136: throws CompassGpsException {
137: if (log.isInfoEnabled()) {
138: log.info("{" + getName()
139: + "}: Indexing the database with fetch size ["
140: + fetchSize + "]");
141: }
142: Connection connection = JdbcUtils.getConnection(dataSource);
143: PreparedStatement ps = null;
144: ResultSet rs = null;
145: try {
146: IndexExecution[] indexExecutions = doGetIndexExecutions(connection);
147: for (IndexExecution indexExecution : indexExecutions) {
148: if (!isRunning()) {
149: return;
150: }
151: ps = indexExecution.getStatement();
152: if (ps == null) {
153: if (log.isDebugEnabled()) {
154: log.debug("{" + getName()
155: + "} Executing select query ["
156: + indexExecution.getStatementQuery()
157: + "]");
158: }
159: ps = connection.prepareStatement(indexExecution
160: .getStatementQuery());
161: }
162: if (getFetchSize() > 0) {
163: ps.setFetchSize(getFetchSize());
164: }
165: rs = ps.executeQuery();
166: processResultSet(indexExecution.getDescription(), rs,
167: session);
168: }
169: } catch (CompassException e) {
170: log.error("Failed to index database", e);
171: throw e;
172: } catch (Exception e) {
173: log.error("Failed to index database", e);
174: throw new JdbcGpsDeviceException(
175: "Failed to index database", e);
176: } finally {
177: JdbcUtils.closeResultSet(rs);
178: JdbcUtils.closeStatement(ps);
179: JdbcUtils.closeConnection(connection);
180: }
181:
182: if (log.isInfoEnabled()) {
183: log.info("{" + getName()
184: + "}: Finished indexing the database");
185: }
186: }
187:
188: /**
189: * Called for each {@link IndexExecution} returned from the
190: * {@link #doGetIndexExecutions(Connection)} with the <code>ResultSet</code>.
191: * Can be override by derived classes, if not override, than iterates threw
192: * the <code>ResultSet</code> and calls
193: * {@link #processRow(Object, ResultSet, CompassSession)} for each row.
194: */
195: protected void processResultSet(Object description, ResultSet rs,
196: CompassSession session) throws SQLException,
197: CompassException {
198: while (rs.next()) {
199: processRow(description, rs, session);
200: }
201: }
202:
203: /**
204: * Called for each row in the <code>ResultSet</code> which maps to an
205: * {@link IndexExecution}. Can be override by derived classes, if not
206: * override, than calls
207: * {@link #processRowValue(Object, ResultSet, CompassSession)} and uses it's
208: * return value to save it in the <code>CompassSession</code>. The return
209: * value can be an OSEM enables object, a <code>Resource</code>, or an
210: * array of one of them.
211: */
212: protected void processRow(Object description, ResultSet rs,
213: CompassSession session) throws SQLException,
214: CompassException {
215: if (!isRunning()) {
216: return;
217: }
218: Object value = processRowValue(description, rs, session);
219: if (value != null) {
220: if (value instanceof Object[]) {
221: int length = Array.getLength(value);
222: for (int i = 0; i < length; i++) {
223: Object value1 = Array.get(value, i);
224: session.create(value1);
225: }
226: } else {
227: session.create(value);
228: }
229: }
230: }
231:
232: /**
233: * Called for each row in the <code>ResultSet</code> which maps to an
234: * {@link IndexExecution}. Can be override by derived classes, and should
235: * return the actual data to be saved using the <code>CompassSession</code>.
236: * The return value can be either an OSEM enables object, a
237: * <code>Resource</code>, or an array of one of them.
238: */
239: protected Object processRowValue(Object description, ResultSet rs,
240: CompassSession session) throws SQLException,
241: CompassException {
242: return null;
243: }
244:
245: /**
246: * Returns an array of the {@link IndexExecution} that should be executed
247: * it's respective <code>ResultSet</code> should be indexed.
248: */
249: protected abstract IndexExecution[] doGetIndexExecutions(
250: Connection connection) throws SQLException,
251: JdbcGpsDeviceException;
252:
253: public DataSource getDataSource() {
254: return dataSource;
255: }
256:
257: public void setDataSource(DataSource dataSource) {
258: this .dataSource = dataSource;
259: }
260:
261: public int getFetchSize() {
262: return fetchSize;
263: }
264:
265: public void setFetchSize(int fetchSize) {
266: this .fetchSize = fetchSize;
267: }
268:
269: public JdbcDialect getDialect() {
270: return dialect;
271: }
272:
273: public void setDialect(JdbcDialect dialect) {
274: this.dialect = dialect;
275: }
276: }
|