001: /*
002: * Copyright (C) 1999-2004 <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</a>
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018: package org.mandarax.sql;
019:
020: import java.sql.Connection;
021: import java.sql.ResultSet;
022: import java.sql.Statement;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Vector;
026:
027: import org.apache.commons.collections.iterators.SingletonIterator;
028: import org.mandarax.kernel.AbstractPropertiesSupport;
029: import org.mandarax.kernel.Clause;
030: import org.mandarax.kernel.ClauseSet;
031: import org.mandarax.kernel.ClauseSetChangeListener;
032: import org.mandarax.kernel.ClauseSetException;
033: import org.mandarax.kernel.Predicate;
034: import org.mandarax.util.logging.LogCategories;
035:
036: /**
037: * An SQL clause set is basically a wrapper around a ResultSet of an SQL query where each
038: * row in the result set is considered as a single clause (fact).
039: * @author <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</A>
040: * @version 3.4 <7 March 05>
041: * @since 1.5
042: */
043: public class SQLClauseSet extends AbstractPropertiesSupport implements
044: ClauseSet, LogCategories {
045:
046: private String whereClause = "";
047: private SQLPredicate predicate = null;
048: private String query = null;
049: private List cache = null;
050: private long cacheTimeout = NO_CACHE;
051: private long cacheTimestamp = 0;
052: public static final int NO_CACHE = -1;
053: private boolean closeConnection = true;
054:
055: /**
056: * Constructor.
057: */
058: public SQLClauseSet() {
059: super ();
060: }
061:
062: /**
063: * Constructor.
064: * @p a predicate
065: */
066: public SQLClauseSet(SQLPredicate p) {
067: this (p, NO_CACHE);
068: }
069:
070: /**
071: * Constructor.
072: * @param p a predicate
073: * @param where a where clause
074: */
075: public SQLClauseSet(SQLPredicate p, String where) {
076: this (p, where, NO_CACHE);
077: }
078:
079: /**
080: * Constructor.
081: * @param p a predicate
082: * @param timeout the timeout after which the cache expires, if NO_CACHE , no caching is done
083: */
084: public SQLClauseSet(SQLPredicate p, long timeout) {
085: this (p, "", timeout);
086: }
087:
088: /**
089: * Constructor.
090: * @param p a predicate
091: * @param where a where clause
092: * @param timeout the timeout after which the cache expires, if NO_CACHE , no caching is done
093: */
094: public SQLClauseSet(SQLPredicate p, String where, long timeout) {
095: super ();
096: predicate = p;
097: whereClause = where;
098: cacheTimeout = timeout;
099: }
100:
101: /**
102: * Add a clause set listener.
103: * @param l the listener
104: */
105: public void addClauseSetChangeListener(ClauseSetChangeListener l) {
106: }
107:
108: /**
109: * Iterate over all clauses.
110: * @return a clause iterator
111: * @throws ClauseSetException
112: */
113: public org.mandarax.util.ClauseIterator clauses()
114: throws ClauseSetException {
115: try {
116: if (useCache()) {
117: return new CachedClauseIterator(cache);
118: } else {
119: String query = getQuery();
120: Connection con = predicate.getDataSource()
121: .getConnection();
122: LOG_SQL.debug("Execute SQL: " + query);
123: Statement stmt = con.createStatement();
124: ResultSet rs = stmt.executeQuery(query);
125:
126: // next line bugfix 1.9
127: if (cacheTimeout != NO_CACHE) {
128: cache = new Vector();
129: cacheTimestamp = System.currentTimeMillis();
130: }
131: return new SQLClauseIterator(predicate, rs, cache,
132: closeConnection, con);
133: }
134: } catch (Exception x) {
135: LOG_SQL.error("Exception executing SQL", x);
136: throw new ClauseSetException(
137: "Exception building clause from SQL", x);
138: }
139:
140: }
141:
142: /**
143: * Get an iterator for clauses. The clause passed represents a query
144: * which can be used to restrict the clause set. E.g., if the clause
145: * iterator is built from database objects, and some of the variables
146: * in the query clause are already replaced by constants, these constants
147: * might be used to form a <code>WHERE</code> clause restricting the set of database objects
148: * used to establish the clauses. The additional parameter provides a general
149: * purpose interface to pass additional information, e.g. to sort the clause
150: * set.
151: * @return a clause iterator
152: * @param query the query clause
153: * @param additionalParameter an optional additional parameter
154: * @throws ClauseSetException
155: */
156: public org.mandarax.util.ClauseIterator clauses(Clause query,
157: Object additionalParameter) throws ClauseSetException {
158: return clauses();
159: }
160:
161: /**
162: * Get a key for indexing. The knowledge base is supposed to
163: * contain only clauses having the same key object. This method
164: * returns this key object. Note that this behavior is not enforced!
165: * @see org.mandarax.kernel.Fact#getKey
166: * @see org.mandarax.kernel.Rule#getKey
167: * @see org.mandarax.kernel.KnowledgeBase
168: * @return the key object
169: */
170: public Object getKey() {
171: return predicate;
172: }
173:
174: /**
175: * Get the predicate.
176: * @return a predicate
177: */
178: public Predicate getPredicate() {
179: return predicate;
180: }
181:
182: /**
183: * Get the query build from the SELECT statement from the predicate
184: * plus the additional WHERE clause set here.
185: * @return a query
186: */
187: public String getQuery() {
188: if (query == null) {
189: String q = predicate.getQuery().trim();
190: String w = getWhereClause();
191: if (w != null) {
192: w = w.trim();
193:
194: if (w.length() > 0) {
195: if (w.toLowerCase().indexOf("where") == 0) {
196: q = q + " " + w;
197: } else {
198: q = q + " where " + w;
199: }
200: }
201: }
202: query = q;
203: }
204: return query;
205: }
206:
207: /**
208: * Get the where clause
209: * @return the where clause
210: */
211: public String getWhereClause() {
212: return whereClause;
213: }
214:
215: /**
216: * Remove a clause set listener.
217: * @param l the listener
218: */
219: public void removeClauseSetChangeListener(ClauseSetChangeListener l) {
220: }
221:
222: /**
223: * Set a predicate.
224: * @param p a predicate
225: */
226: public void setPredicate(Predicate p) {
227: if (p instanceof SQLPredicate)
228: setSQLPredicate((SQLPredicate) p);
229: else
230: throw new IllegalArgumentException(
231: "SQLClauseSets expect SQLPredicates");
232: }
233:
234: /**
235: * Set an SQL predicate.
236: * @param p a predicate
237: */
238: public void setSQLPredicate(SQLPredicate p) {
239: predicate = p;
240: }
241:
242: /**
243: * Set a WHERE clause.
244: * @param p a predicate
245: */
246: public void setWhereClause(String c) {
247: whereClause = c;
248: query = null;
249: }
250:
251: /**
252: * Indicates whether the cache can be used.
253: * @return a boolean
254: */
255: private boolean useCache() {
256: if (cache == null) {
257: return false;
258: } else {
259: if (cacheTimeout == NO_CACHE) {
260: cache = null;
261: return false;
262: } else {
263: // check whether cache is expired
264: if ((System.currentTimeMillis() - cacheTimestamp) > (cacheTimeout * 1000)) {
265: cache = null;
266: return false;
267: } else {
268: return true;
269: }
270: }
271: }
272: }
273:
274: /**
275: * Sets whether to close the connection at the end.
276: * @param closeConnection a boolean
277: */
278: public void setCloseConnection(boolean closeConnection) {
279: this .closeConnection = closeConnection;
280: }
281:
282: /**
283: * Set the cache time out. The cache expires after this time.
284: * @param a timeout in milli seconds, use NO_CACHE in order to switch caching off
285: */
286: public void setCacheTimeout(long timeout) {
287: cacheTimeout = timeout;
288: }
289:
290: /**
291: * Get the cache time out. The cache expires after this time.
292: * @return a timeout in milli seconds, NO_CACHE indicates that there is no caching
293: */
294: public long getCacheTimeout() {
295: return cacheTimeout;
296: }
297:
298: /**
299: * Compare objects.
300: * @param obj another object
301: * @return a boolean
302: */
303: public boolean equals(Object obj) {
304: if ((obj != null) && (obj instanceof SQLClauseSet)) {
305: SQLClauseSet c = (SQLClauseSet) obj;
306: boolean result = true;
307:
308: // compare query, name and data source
309: // bugfix in 1.8 - thanks to chenjb@gsta.com
310: result = (predicate == null) ? (c.predicate == null)
311: : predicate.equals(c.predicate);
312: result = result
313: && ((whereClause == null) ? (c.whereClause == null)
314: : whereClause.equals(c.whereClause));
315: result = result && ((cacheTimeout == c.cacheTimeout));
316: result = result && (closeConnection == c.closeConnection);
317:
318: return result;
319: } else {
320: return false;
321: }
322: }
323:
324: /**
325: * Get the hashcode of the object.
326: * @return the hash code of the object
327: */
328: public int hashCode() {
329: return ((predicate == null) ? 0 : predicate.hashCode())
330: ^ ((whereClause == null) ? 0 : whereClause.hashCode());
331: }
332:
333: /**
334: * Indicates whether to close the connection at the end.
335: * @return a boolean
336: */
337: public boolean isCloseConnection() {
338: return closeConnection;
339: }
340:
341: /**
342: * Get an iterator iterating over the predicates contained in this clause set.
343: * @return an iterator
344: */
345: public Iterator predicates() {
346: return new SingletonIterator(predicate);
347: }
348:
349: }
|