Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

ControllerResultSet.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2005 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet. 
00022  * Contributor(s): Diego Malpica.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.virtualdatabase;
00026 
00027 import java.io.Serializable;
00028 import java.sql.ResultSet;
00029 import java.sql.ResultSetMetaData;
00030 import java.sql.SQLException;
00031 import java.sql.Statement;
00032 import java.util.ArrayList;
00033 
00034 import org.objectweb.cjdbc.common.sql.AbstractRequest;
00035 import org.objectweb.cjdbc.common.sql.SelectRequest;
00036 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
00037 import org.objectweb.cjdbc.driver.Field;
00038 
00039 /**
00040  * A <code>ControllerResultSet</code> is a lightweight ResultSet for the
00041  * controller side. It only contains row data and column metadata. The real
00042  * ResultSet is constructed on by the driver on the client side from the
00043  * ControllerResultSet information.
00044  * 
00045  * @see org.objectweb.cjdbc.driver.DriverResultSet
00046  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00047  * @version 1.0
00048  */
00049 public class ControllerResultSet implements Serializable
00050 {
00051   /** The results */
00052   private ArrayList           data              = null;
00053   /** The fields */
00054   private Field[]             fields            = null;
00055   /** Cursor name for this ResultSet (not used yet) */
00056   private String              cursorName        = null;
00057   /** Fetch size if we need to fetch only a subset of the ResultSet */
00058   private int                 fetchSize         = 0;
00059   /** Database ResultSet if we need to fetch data later on */
00060   private transient ResultSet dbResultSet       = null;
00061   /** Optional statement dbResultSet is attached to if in streaming mode */
00062   private transient Statement owningStatement   = null;
00063   /** True if the underlying database ResultSet is closed */
00064   private boolean             dbResultSetClosed = true;
00065   /** True if there is still more data to fetch from dbResultSet */
00066   private boolean             hasMoreData       = false;
00067   /** Maximum number of rows remaining to fetch */
00068   private int                 maxRows           = 0;
00069 
00070   /**
00071    * Build a C-JDBC ResultSet from a database specific ResultSet. The metadata
00072    * can be retrieved from the MetadataCache if provided. If a metadata cache is
00073    * provided but the data is not in the cache, the MetadataCache is updated
00074    * accordingly. The remaining code is a straightforward copy of both metadata
00075    * and data.
00076    * <p>
00077    * The statement used to execute the query will be closed when the ResultSet
00078    * has been completely copied or when the ResultSet is closed while in
00079    * streaming mode.
00080    * 
00081    * @param request Request to which this ResultSet belongs
00082    * @param rs The database specific ResultSet
00083    * @param metadataCache MetadataCache (null if none)
00084    * @param s Statement used to get rs
00085    * @throws SQLException if an error occurs
00086    */
00087   public ControllerResultSet(AbstractRequest request, java.sql.ResultSet rs,
00088       MetadataCache metadataCache, Statement s) throws SQLException
00089   {
00090     this.owningStatement = s;
00091     try
00092     {
00093       if (rs == null)
00094         throw new SQLException("Null ResultSet");
00095 
00096       // This is already a result coming from another controller.
00097       //if (rs instanceof org.objectweb.cjdbc.driver.ResultSet)
00098       //  return (org.objectweb.cjdbc.driver.ResultSet) rs;
00099 
00100       // Build the ResultSet metaData
00101       int nbColumn;
00102       if (metadataCache != null)
00103         fields = metadataCache.getMetadata(request);
00104 
00105       if (fields == null)
00106       { // Metadata Cache miss
00107         // Build the fields from the MetaData
00108         java.sql.ResultSetMetaData metaData = rs.getMetaData();
00109         if (metaData == null)
00110           throw new SQLException("Unable to fetch metadata");
00111         nbColumn = metaData.getColumnCount();
00112         fields = new Field[nbColumn];
00113         for (int i = 0; i < nbColumn; i++)
00114         {
00115           // 1st column is 1
00116           String columnName = metaData.getColumnName(i + 1);
00117           String tableName = null;
00118           try
00119           {
00120             tableName = metaData.getTableName(i + 1);
00121           }
00122           catch (Exception ignore)
00123           {
00124           }
00125           if (metadataCache != null)
00126           { // Check Field cache
00127             fields[i] = metadataCache.getField(tableName + "." + columnName);
00128             if (fields[i] != null)
00129               continue; // Cache hit
00130           }
00131           // Field cache miss
00132           int columnDisplaySize = 0;
00133           try
00134           {
00135             columnDisplaySize = metaData.getColumnDisplaySize(i + 1);
00136           }
00137           catch (Exception ignore)
00138           {
00139           }
00140           int columnType = -1;
00141           try
00142           {
00143             columnType = metaData.getColumnType(i + 1);
00144           }
00145           catch (Exception ignore)
00146           {
00147           }
00148           String columnTypeName = null;
00149           try
00150           {
00151             columnTypeName = metaData.getColumnTypeName(i + 1);
00152           }
00153           catch (Exception ignore)
00154           {
00155           }
00156           String columnClassName = null;
00157           try
00158           {
00159             columnClassName = metaData.getColumnClassName(i + 1);
00160           }
00161           catch (Exception ignore)
00162           {
00163           }
00164           boolean isAutoIncrement = false;
00165           try
00166           {
00167             isAutoIncrement = metaData.isAutoIncrement(i + 1);
00168           }
00169           catch (Exception ignore)
00170           {
00171           }
00172           boolean isCaseSensitive = false;
00173           try
00174           {
00175             isCaseSensitive = metaData.isCaseSensitive(i + 1);
00176           }
00177           catch (Exception ignore)
00178           {
00179           }
00180           boolean isCurrency = false;
00181           try
00182           {
00183             isCurrency = metaData.isCurrency(i + 1);
00184           }
00185           catch (Exception ignore)
00186           {
00187           }
00188           int isNullable = ResultSetMetaData.columnNullableUnknown;
00189           try
00190           {
00191             isNullable = metaData.isNullable(i + 1);
00192           }
00193           catch (Exception ignore)
00194           {
00195           }
00196           boolean isReadOnly = false;
00197           try
00198           {
00199             isReadOnly = metaData.isReadOnly(i + 1);
00200           }
00201           catch (Exception ignore)
00202           {
00203           }
00204           boolean isWritable = false;
00205           try
00206           {
00207             isWritable = metaData.isWritable(i + 1);
00208           }
00209           catch (Exception ignore)
00210           {
00211           }
00212           boolean isDefinitelyWritable = false;
00213           try
00214           {
00215             isReadOnly = metaData.isDefinitelyWritable(i + 1);
00216           }
00217           catch (Exception ignore)
00218           {
00219           }
00220           boolean isSearchable = false;
00221           try
00222           {
00223             isSearchable = metaData.isSearchable(i + 1);
00224           }
00225           catch (Exception ignore)
00226           {
00227           }
00228           boolean isSigned = false;
00229           try
00230           {
00231             isSigned = metaData.isSigned(i + 1);
00232           }
00233           catch (Exception ignore)
00234           {
00235           }
00236           int precision = 0;
00237           try
00238           {
00239             precision = metaData.getPrecision(i + 1);
00240           }
00241           catch (Exception ignore)
00242           {
00243           }
00244           int scale = 0;
00245           try
00246           {
00247             scale = metaData.getScale(i + 1);
00248           }
00249           catch (Exception ignore)
00250           {
00251           }
00252           fields[i] = new Field(tableName, columnName, columnDisplaySize,
00253               columnType, columnTypeName, columnClassName, isAutoIncrement,
00254               isCaseSensitive, isCurrency, isNullable, isReadOnly, isWritable,
00255               isDefinitelyWritable, isSearchable, isSigned, precision, scale);
00256 
00257           if (metadataCache != null)
00258             // Add field to cache
00259             metadataCache.addField(tableName + "." + columnName, fields[i]);
00260         } // for
00261         if (metadataCache != null)
00262           metadataCache.addMetadata(request, fields);
00263       }
00264       else
00265         nbColumn = fields.length;
00266 
00267       // Build the ResultSet data
00268       if (rs.next())
00269       {
00270         if (request instanceof SelectRequest)
00271         {
00272           SelectRequest select = (SelectRequest) request;
00273           cursorName = select.getCursorName();
00274           fetchSize = select.getFetchSize();
00275         }
00276         maxRows = request.getMaxRows();
00277         if (maxRows == 0)
00278           maxRows = Integer.MAX_VALUE; // Infinite number of rows
00279 
00280         // Note that fetchData updates the data field
00281         dbResultSet = rs;
00282         fetchData();
00283         if (hasMoreData && (cursorName == null))
00284           cursorName = String.valueOf(dbResultSet.hashCode());
00285       }
00286       else
00287       {
00288         hasMoreData = false;
00289         dbResultSet = null;
00290         dbResultSetClosed = true;
00291         rs.close();
00292         if (owningStatement != null)
00293         {
00294           try
00295           {
00296             owningStatement.close();
00297           }
00298           catch (SQLException ignore)
00299           {
00300           }
00301           owningStatement = null;
00302         }
00303       }
00304     }
00305     catch (SQLException e)
00306     {
00307       throw new SQLException("Error while building C-JDBC ResultSet (" + e
00308           + ")");
00309     }
00310   }
00311 
00312   /**
00313    * Sets the fetch size and calls fetchData()
00314    * 
00315    * @param fetchSize the number of rows to fetch
00316    * @return the fetched data
00317    * @throws SQLException if an error occurs
00318    * @see #fetchData()
00319    */
00320   public ArrayList fetchData(int fetchSize) throws SQLException
00321   {
00322     this.fetchSize = fetchSize;
00323     ArrayList rows = fetchData();
00324     if (!hasMoreData)
00325     {
00326       if (owningStatement != null)
00327       {
00328         try
00329         {
00330           owningStatement.close();
00331         }
00332         catch (SQLException ignore)
00333         {
00334         }
00335         owningStatement = null;
00336       }
00337     }
00338     return rows;
00339   }
00340 
00341   /**
00342    * Fetch the next rows of data from dbResultSet according to fetchSize and
00343    * maxRows parameters. This methods directly updates the data and hasMoreData
00344    * fields and returns the same Object as subsequent calls to getData().
00345    * 
00346    * @return ArrayList with freshly loaded data or null if underlying ResultSet
00347    *         is closed.
00348    * @throws SQLException if an error occurs
00349    */
00350   public ArrayList fetchData() throws SQLException
00351   {
00352     if (dbResultSet == null)
00353       return null;
00354 
00355     Object[] row;
00356     // We directly update the data field
00357     data = new ArrayList();
00358     int toFetch;
00359     if (fetchSize > 0)
00360     {
00361       toFetch = fetchSize < maxRows ? fetchSize : maxRows;
00362       maxRows -= toFetch;
00363     }
00364     else
00365       toFetch = maxRows;
00366     int nbColumn = fields.length;
00367     Object object;
00368     do
00369     {
00370       row = new Object[nbColumn];
00371       for (int i = 0; i < nbColumn; i++)
00372       {
00373         object = dbResultSet.getObject(i + 1);
00374         // Convert database native Clob and Blob to C-JDBC Clob/Blobs that are
00375         // Serializable
00376         if (object != null)
00377         {
00378           if (object instanceof java.sql.Clob)
00379           {
00380             java.sql.Clob clob = (java.sql.Clob) object;
00381             object = clob.getSubString(1, (int) clob.length());
00382           }
00383           else if (object instanceof java.sql.Blob)
00384           {
00385             java.sql.Blob blob = (java.sql.Blob) object;
00386             object = blob.getBytes(1, (int) blob.length());
00387           }
00388         }
00389         row[i] = object;
00390       }
00391       data.add(row);
00392       toFetch--;
00393       hasMoreData = dbResultSet.next();
00394     }
00395     while (hasMoreData && (toFetch > 0));
00396     if (hasMoreData && (fetchSize > 0) && (maxRows > 0))
00397     { // More data to fetch later on
00398       maxRows += toFetch;
00399       dbResultSetClosed = false;
00400     }
00401     else
00402     {
00403       hasMoreData = false;
00404       dbResultSet.close();
00405       if (owningStatement != null)
00406         owningStatement.close();
00407       dbResultSet = null;
00408       dbResultSetClosed = true;
00409     }
00410     return data;
00411   }
00412 
00413   /**
00414    * Returns the data value.
00415    * 
00416    * @return Returns the data.
00417    */
00418   public ArrayList getData()
00419   {
00420     return data;
00421   }
00422 
00423   /**
00424    * Returns the fields value.
00425    * 
00426    * @return Returns the fields.
00427    */
00428   public Field[] getFields()
00429   {
00430     return fields;
00431   }
00432 
00433   /**
00434    * Get the name of the SQL cursor used by this ResultSet
00435    * 
00436    * @return the ResultSet's SQL cursor name.
00437    */
00438   public String getCursorName()
00439   {
00440     return cursorName;
00441   }
00442 
00443   /**
00444    * Returns the hasMoreData value.
00445    * 
00446    * @return Returns the hasMoreData.
00447    */
00448   public boolean hasMoreData()
00449   {
00450     return hasMoreData;
00451   }
00452 
00453   /**
00454    * Closes the database ResultSet to release the resource and garbage collect
00455    * data.
00456    */
00457   public void closeResultSet()
00458   {
00459     if ((dbResultSet != null) && !dbResultSetClosed)
00460     {
00461       try
00462       {
00463         dbResultSet.close();
00464       }
00465       catch (SQLException ignore)
00466       {
00467       }
00468       dbResultSet = null; // to allow GC to work properly
00469       if (owningStatement != null)
00470       {
00471         try
00472         {
00473           owningStatement.close();
00474         }
00475         catch (SQLException ignore)
00476         {
00477         }
00478         owningStatement = null;
00479       }
00480     }
00481   }
00482 
00483 }

Generated on Mon Apr 11 22:01:30 2005 for C-JDBC by  doxygen 1.3.9.1