KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > controller > virtualdatabase > ControllerResultSet


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2005 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Diego Malpica.
23  */

24
25 package org.objectweb.cjdbc.controller.virtualdatabase;
26
27 import java.io.Serializable JavaDoc;
28 import java.sql.ResultSet JavaDoc;
29 import java.sql.ResultSetMetaData JavaDoc;
30 import java.sql.SQLException JavaDoc;
31 import java.sql.Statement JavaDoc;
32 import java.util.ArrayList JavaDoc;
33
34 import org.objectweb.cjdbc.common.sql.AbstractRequest;
35 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
36 import org.objectweb.cjdbc.driver.DriverResultSet;
37 import org.objectweb.cjdbc.driver.Field;
38
39 /**
40  * A <code>ControllerResultSet</code> is a lightweight ResultSet for the
41  * controller side. It only contains row data and column metadata. The real
42  * ResultSet is constructed on by the driver on the client side from the
43  * ControllerResultSet information.
44  *
45  * @see org.objectweb.cjdbc.driver.DriverResultSet
46  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
47  * @version 1.0
48  */

49 public class ControllerResultSet implements Serializable JavaDoc
50 {
51   private static final ArrayList JavaDoc EMPTY_LIST = new ArrayList JavaDoc(0);
52   /** The results */
53   private ArrayList JavaDoc data = EMPTY_LIST;
54   /** The fields */
55   private Field[] fields = null;
56   /** Cursor name for this ResultSet (not used yet) */
57   private String JavaDoc cursorName = null;
58   /** Fetch size if we need to fetch only a subset of the ResultSet */
59   private int fetchSize = 0;
60   /** Backend ResultSet. We need to hold it when streaming. */
61   private transient ResultSet JavaDoc dbResultSet = null;
62   /** Optional statement dbResultSet is attached to if in streaming mode */
63   private transient Statement JavaDoc owningStatement = null;
64   /** True if the underlying database ResultSet is closed */
65   private boolean dbResultSetClosed = true;
66   /** True if there is still more data to fetch from dbResultSet */
67   private boolean hasMoreData = false;
68   /** Maximum number of rows remaining to fetch */
69   private int maxRows = 0;
70   /**
71    * When streaming, hold a reference to the DriverResultSet we built so we
72    * don't have to re-compute everything.
73    */

74   DriverResultSet driverResultSet;
75
76   /**
77    * Build a C-JDBC ResultSet from a database specific ResultSet. The metadata
78    * can be retrieved from the MetadataCache if provided. If a metadata cache is
79    * provided but the data is not in the cache, the MetadataCache is updated
80    * accordingly. The remaining code is a straightforward copy of both metadata
81    * and data.
82    * <p>
83    * The statement used to execute the query will be closed when the ResultSet
84    * has been completely copied or when the ResultSet is closed while in
85    * streaming mode.
86    *
87    * @param request Request to which this ResultSet belongs
88    * @param rs The database specific ResultSet
89    * @param metadataCache MetadataCache (null if none)
90    * @param s Statement used to get rs
91    * @throws SQLException if an error occurs
92    */

93   public ControllerResultSet(AbstractRequest request, java.sql.ResultSet JavaDoc rs,
94       MetadataCache metadataCache, Statement JavaDoc s) throws SQLException JavaDoc
95   {
96     this.owningStatement = s;
97     try
98     {
99       if (rs == null)
100         throw new SQLException JavaDoc("Null ResultSet");
101
102       // This is already a result coming from another controller.
103
//if (rs instanceof org.objectweb.cjdbc.driver.ResultSet)
104
// return (org.objectweb.cjdbc.driver.ResultSet) rs;
105

106       // Build the ResultSet metaData
107
int nbColumn;
108       if (metadataCache != null)
109         fields = metadataCache.getMetadata(request);
110
111       if (fields == null)
112       { // Metadata Cache miss
113
// Build the fields from the MetaData
114
java.sql.ResultSetMetaData JavaDoc metaData = rs.getMetaData();
115         if (metaData == null)
116           throw new SQLException JavaDoc("Unable to fetch metadata");
117         nbColumn = metaData.getColumnCount();
118         fields = new Field[nbColumn];
119         for (int i = 0; i < nbColumn; i++)
120         {
121           // 1st column is 1
122
String JavaDoc columnName = metaData.getColumnName(i + 1);
123           String JavaDoc tableName = null;
124           try
125           {
126             tableName = metaData.getTableName(i + 1);
127           }
128           catch (Exception JavaDoc ignore)
129           {
130           }
131           if (metadataCache != null)
132           { // Check Field cache
133
fields[i] = metadataCache.getField(tableName + "." + columnName);
134             if (fields[i] != null)
135               continue; // Cache hit
136
}
137           // Field cache miss
138
int columnDisplaySize = 0;
139           try
140           {
141             columnDisplaySize = metaData.getColumnDisplaySize(i + 1);
142           }
143           catch (Exception JavaDoc ignore)
144           {
145           }
146           int columnType = -1;
147           try
148           {
149             columnType = metaData.getColumnType(i + 1);
150           }
151           catch (Exception JavaDoc ignore)
152           {
153           }
154           String JavaDoc columnTypeName = null;
155           try
156           {
157             columnTypeName = metaData.getColumnTypeName(i + 1);
158           }
159           catch (Exception JavaDoc ignore)
160           {
161           }
162           String JavaDoc columnClassName = null;
163           try
164           {
165             columnClassName = metaData.getColumnClassName(i + 1);
166           }
167           catch (Exception JavaDoc ignore)
168           {
169           }
170           boolean isAutoIncrement = false;
171           try
172           {
173             isAutoIncrement = metaData.isAutoIncrement(i + 1);
174           }
175           catch (Exception JavaDoc ignore)
176           {
177           }
178           boolean isCaseSensitive = false;
179           try
180           {
181             isCaseSensitive = metaData.isCaseSensitive(i + 1);
182           }
183           catch (Exception JavaDoc ignore)
184           {
185           }
186           boolean isCurrency = false;
187           try
188           {
189             isCurrency = metaData.isCurrency(i + 1);
190           }
191           catch (Exception JavaDoc ignore)
192           {
193           }
194           int isNullable = ResultSetMetaData.columnNullableUnknown;
195           try
196           {
197             isNullable = metaData.isNullable(i + 1);
198           }
199           catch (Exception JavaDoc ignore)
200           {
201           }
202           boolean isReadOnly = false;
203           try
204           {
205             isReadOnly = metaData.isReadOnly(i + 1);
206           }
207           catch (Exception JavaDoc ignore)
208           {
209           }
210           boolean isWritable = false;
211           try
212           {
213             isWritable = metaData.isWritable(i + 1);
214           }
215           catch (Exception JavaDoc ignore)
216           {
217           }
218           boolean isDefinitelyWritable = false;
219           try
220           {
221             isReadOnly = metaData.isDefinitelyWritable(i + 1);
222           }
223           catch (Exception JavaDoc ignore)
224           {
225           }
226           boolean isSearchable = false;
227           try
228           {
229             isSearchable = metaData.isSearchable(i + 1);
230           }
231           catch (Exception JavaDoc ignore)
232           {
233           }
234           boolean isSigned = false;
235           try
236           {
237             isSigned = metaData.isSigned(i + 1);
238           }
239           catch (Exception JavaDoc ignore)
240           {
241           }
242           int precision = 0;
243           try
244           {
245             precision = metaData.getPrecision(i + 1);
246           }
247           catch (Exception JavaDoc ignore)
248           {
249           }
250           int scale = 0;
251           try
252           {
253             scale = metaData.getScale(i + 1);
254           }
255           catch (Exception JavaDoc ignore)
256           {
257           }
258           fields[i] = new Field(tableName, columnName, columnDisplaySize,
259               columnType, columnTypeName, columnClassName, isAutoIncrement,
260               isCaseSensitive, isCurrency, isNullable, isReadOnly, isWritable,
261               isDefinitelyWritable, isSearchable, isSigned, precision, scale);
262
263           if (metadataCache != null)
264             // Add field to cache
265
metadataCache.addField(tableName + "." + columnName, fields[i]);
266         } // for
267
if (metadataCache != null)
268           metadataCache.addMetadata(request, fields);
269       }
270       else
271         nbColumn = fields.length;
272
273       // Build the ResultSet data
274
if (rs.next())
275       {
276         cursorName = request.getCursorName();
277         fetchSize = request.getFetchSize();
278         maxRows = request.getMaxRows();
279         if (maxRows == 0)
280           maxRows = Integer.MAX_VALUE; // Infinite number of rows
281

282         // Note that fetchData updates the data field
283
dbResultSet = rs;
284         fetchData();
285         if (hasMoreData && (cursorName == null))
286           // hashCode() is not guaranteed to be injective in theory,
287
// but returns the address of the object in practice.
288
cursorName = String.valueOf(dbResultSet.hashCode());
289       }
290       else
291       {
292         hasMoreData = false;
293         dbResultSet = null;
294         dbResultSetClosed = true;
295         rs.close();
296         if (owningStatement != null)
297         {
298           try
299           {
300             owningStatement.close();
301           }
302           catch (SQLException JavaDoc ignore)
303           {
304           }
305           owningStatement = null;
306         }
307       }
308     }
309     catch (SQLException JavaDoc e)
310     {
311       throw (SQLException JavaDoc) new SQLException JavaDoc(
312           "Error while building C-JDBC ResultSet (" + e.getLocalizedMessage()
313               + ")").initCause(e);
314     }
315   }
316
317   /**
318    * Sets the fetch size and calls fetchData()
319    *
320    * @param fetchSizeParam the number of rows to fetch
321    * @throws SQLException if an error occurs
322    * @see #fetchData()
323    */

324   public void fetchData(int fetchSizeParam) throws SQLException JavaDoc
325   {
326     this.fetchSize = fetchSizeParam;
327     fetchData();
328     if (!hasMoreData)
329     {
330       if (owningStatement != null)
331       {
332         try
333         {
334           owningStatement.close();
335         }
336         catch (SQLException JavaDoc ignore)
337         {
338         }
339         owningStatement = null;
340       }
341     }
342   }
343
344   /**
345    * Fetch the next rows of data from dbResultSet according to fetchSize and
346    * maxRows parameters. This methods directly updates the data and hasMoreData
347    * fields returned by getData() and hadMoreData() accessors.
348    *
349    * @throws SQLException from the backend or if dbResultSet is closed. Maybe
350    * we should use a different type internally.
351    */

352   public void fetchData() throws SQLException JavaDoc
353   {
354     if (dbResultSet == null)
355       throw new SQLException JavaDoc("Backend ResultSet is closed");
356
357     Object JavaDoc[] row;
358     // We directly update the data field
359
if (data == EMPTY_LIST)
360       data = new ArrayList JavaDoc();
361     else
362       // Re-use the existing ArrayList with the good size to be more efficient
363
data.clear();
364     int toFetch;
365     if (fetchSize > 0)
366     {
367       toFetch = fetchSize < maxRows ? fetchSize : maxRows;
368       // instead of remembering how much we sent, it's simpler to decrease how
369
// much we still may send.
370
maxRows -= toFetch;
371     }
372     else
373       toFetch = maxRows;
374     int nbColumn = fields.length;
375     Object JavaDoc object;
376     do
377     {
378       row = new Object JavaDoc[nbColumn];
379       for (int i = 0; i < nbColumn; i++)
380       {
381         object = dbResultSet.getObject(i + 1);
382         // Convert database native Clob/Blob to String/byte[] that are
383
// Serializable
384
if (object != null)
385         {
386           if (object instanceof java.sql.Clob JavaDoc)
387           {
388             java.sql.Clob JavaDoc clob = (java.sql.Clob JavaDoc) object;
389             object = clob.getSubString(1, (int) clob.length());
390           }
391           else if (object instanceof java.sql.Blob JavaDoc)
392           {
393             java.sql.Blob JavaDoc blob = (java.sql.Blob JavaDoc) object;
394             object = blob.getBytes(1, (int) blob.length());
395           }
396         }
397         row[i] = object;
398       }
399       data.add(row);
400       toFetch--;
401       hasMoreData = dbResultSet.next();
402     }
403     while (hasMoreData && (toFetch > 0));
404     if (hasMoreData && (fetchSize > 0) && (maxRows > 0))
405     { // More data to fetch later on
406
maxRows += toFetch;
407       dbResultSetClosed = false;
408     }
409     else
410     {
411       hasMoreData = false;
412       dbResultSet.close();
413       if (owningStatement != null)
414         owningStatement.close();
415       dbResultSet = null;
416       dbResultSetClosed = true;
417     }
418   }
419
420   /**
421    * Returns the data value.
422    *
423    * @return Returns the data.
424    */

425   public ArrayList JavaDoc getData()
426   {
427     return data;
428   }
429
430   /**
431    * Returns the fields value.
432    *
433    * @return Returns the fields.
434    */

435   public Field[] getFields()
436   {
437     return fields;
438   }
439
440   /**
441    * Get the name of the SQL cursor used by this ResultSet
442    *
443    * @return the ResultSet's SQL cursor name.
444    */

445   public String JavaDoc getCursorName()
446   {
447     return cursorName;
448   }
449
450   /**
451    * Returns the hasMoreData value.
452    *
453    * @return Returns the hasMoreData.
454    */

455   public boolean hasMoreData()
456   {
457     return hasMoreData;
458   }
459
460   /**
461    * Closes the database ResultSet to release the resource and garbage collect
462    * data.
463    */

464   public void closeResultSet()
465   {
466     if ((dbResultSet != null) && !dbResultSetClosed)
467     {
468       try
469       {
470         dbResultSet.close();
471       }
472       catch (SQLException JavaDoc ignore)
473       {
474       }
475       dbResultSet = null; // to allow GC to work properly
476
if (owningStatement != null)
477       {
478         try
479         {
480           owningStatement.close();
481         }
482         catch (SQLException JavaDoc ignore)
483         {
484         }
485         owningStatement = null;
486       }
487     }
488   }
489
490 }
Popular Tags