Coverage Report - org.apache.ojb.broker.accesslayer.RsIterator
 
Classes in this File Line Coverage Branch Coverage Complexity
RsIterator
N/A
N/A
2.541
RsIterator$ResourceClosedException
N/A
N/A
2.541
RsIterator$ResourceNotClosedException
N/A
N/A
2.541
RsIterator$ResourceWrapper
N/A
N/A
2.541
 
 1  
 package org.apache.ojb.broker.accesslayer;
 2  
 
 3  
 /* Copyright 2002-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import java.lang.ref.WeakReference;
 19  
 import java.sql.ResultSet;
 20  
 import java.sql.SQLException;
 21  
 import java.util.Collection;
 22  
 import java.util.HashMap;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 import java.util.NoSuchElementException;
 26  
 import java.util.Vector;
 27  
 
 28  
 import org.apache.ojb.broker.Identity;
 29  
 import org.apache.ojb.broker.OJBRuntimeException;
 30  
 import org.apache.ojb.broker.PBLifeCycleEvent;
 31  
 import org.apache.ojb.broker.PBStateEvent;
 32  
 import org.apache.ojb.broker.PBStateListener;
 33  
 import org.apache.ojb.broker.PersistenceBrokerException;
 34  
 import org.apache.ojb.broker.PersistenceBrokerInternal;
 35  
 import org.apache.ojb.broker.PersistenceBrokerSQLException;
 36  
 import org.apache.ojb.broker.cache.MaterializationCache;
 37  
 import org.apache.ojb.broker.cache.ObjectCacheInternal;
 38  
 import org.apache.ojb.broker.core.PersistenceBrokerImpl;
 39  
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 40  
 import org.apache.ojb.broker.metadata.DescriptorRepository;
 41  
 import org.apache.ojb.broker.metadata.FieldDescriptor;
 42  
 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
 43  
 import org.apache.ojb.broker.query.Query;
 44  
 import org.apache.ojb.broker.query.QueryBySQL;
 45  
 import org.apache.ojb.broker.util.logging.Logger;
 46  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 47  
 
 48  
 /**
 49  
  * RsIterator can be used to iterate over a jdbc ResultSet to retrieve
 50  
  * persistent objects step-by-step and not all at once. In fact the
 51  
  * PersistenceBroker::getCollectionByQuery(...) method uses a RsIterator
 52  
  * internally to build up a Collection of objects
 53  
  *
 54  
  * <p>
 55  
  * NOTE: OJB is very strict in handling <tt>RsIterator</tt> instances. <tt>RsIterator</tt> is
 56  
  * bound very closely to the used {@link org.apache.ojb.broker.PersistenceBroker} instance.
 57  
  * Thus if you do a
 58  
  * <br/> - {@link org.apache.ojb.broker.PersistenceBroker#close}
 59  
  * <br/> - {@link org.apache.ojb.broker.PersistenceBroker#commitTransaction}
 60  
  * <br/> - {@link org.apache.ojb.broker.PersistenceBroker#abortTransaction}
 61  
  * <br/>
 62  
  * call, the current <tt>RsIterator</tt> instance resources will be cleaned up automatic
 63  
  * and invalidate current instance.
 64  
  * </p>
 65  
  *
 66  
  * <p>
 67  
  * NOTE: this code uses features that only JDBC 2.0 compliant Drivers support.
 68  
  * It will NOT work with JDBC 1.0 Drivers (e.g. SUN's JdbcOdbcDriver) If you
 69  
  * are forced to use such a driver, you can use code from the 0.1.30 release.
 70  
  * </p>
 71  
  * @author <a href="mailto:thma@apache.org">Thomas Mahler <a>
 72  
  * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird <a>- added the
 73  
  *         support for extents mapped to single table - added the .size
 74  
  *         functionality - added cursor control
 75  
  *
 76  
  * @version $Id: RsIterator.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
 77  
  */
 78  
 public class RsIterator implements OJBIterator
 79  
 {
 80  
     protected Logger logger = LoggerFactory.getLogger(this.getClass());
 81  
     private static final String INFO_MSG = "Resources already cleaned up, recommend to set" +
 82  
             " this flag before first use of the iterator";
 83  
     /*
 84  
          * arminw: to improve performance we only use this instance to fire events
 85  
          * and set the target object on every use TODO: Find a better solution
 86  
          */
 87  
     private PBLifeCycleEvent afterLookupEvent;
 88  
     private MaterializationCache m_cache;
 89  
 
 90  
     /**
 91  
      * reference to the PersistenceBroker
 92  
      */
 93  
     private PersistenceBrokerImpl m_broker;
 94  
 
 95  
     /**
 96  
      * the underlying resultset
 97  
      */
 98  
     private ResultSetAndStatement m_rsAndStmt;
 99  
 
 100  
     /**
 101  
      * the underlying query object
 102  
      */
 103  
     private RsQueryObject m_queryObject;
 104  
 
 105  
     /**
 106  
      * the proxy class to be used or null
 107  
      */
 108  
     private Class m_itemProxyClass;
 109  
 
 110  
     /**
 111  
      * the top-level class of the item objects
 112  
      */
 113  
     private Class m_itemTopLevelClass = null;
 114  
 
 115  
     /**
 116  
      * this container holds the values of the current ro during materialisation
 117  
      */
 118  
     private Map m_row = null;
 119  
 
 120  
     /**
 121  
      * flag that indicates wether hasNext on m_rs has allready been called
 122  
      */
 123  
     private boolean m_hasCalledCheck = false;
 124  
 
 125  
     /**
 126  
      * prefetch relationship: inBatchedMode true prevents releasing of
 127  
      * DbResources IN_LIMIT defines the max number of values of sql (IN) , -1
 128  
      * for no limits
 129  
      */
 130  
     private boolean m_inBatchedMode = false;
 131  
 
 132  
     /**
 133  
      * return value of the previously called hasNext from m_rs
 134  
      */
 135  
     private boolean hasNext = false;
 136  
 
 137  
     private boolean advancedJDBCSupport = false;
 138  
     private boolean JDBCSupportAssessed = false;
 139  
     private int m_current_row = 0;
 140  
     /**
 141  
      * Tracks whether or not the resources that are used by this class have been released.
 142  
      */
 143  
     private boolean resourcesAreReleased = false;
 144  
 
 145  
     /**
 146  
      * Flag that indicates if the automatic resource cleanup should be
 147  
      * done or not. Default is <tt>true</tt>.
 148  
      */
 149  
     private boolean autoRelease = true;
 150  
     private ResourceWrapper resourceListener;
 151  
     
 152  
     /** if true do not fire PBLifeCycleEvent. */
 153  
     private boolean disableLifeCycleEvents = false;
 154  
 
 155  
     /**
 156  
      * RsIterator constructor.
 157  
      *
 158  
      * @param queryObject query object
 159  
      * @param broker the broker we should use.
 160  
      */
 161  
     public RsIterator(RsQueryObject queryObject, final PersistenceBrokerImpl broker)
 162  
     {
 163  
         setCache(broker.getInternalCache());
 164  
         setRow(new HashMap());
 165  
         setBroker(broker);
 166  
         setQueryObject(queryObject);
 167  
 
 168  
         Class classToPrefetch = broker.getReferenceBroker().getClassToPrefetch();
 169  
         if ((classToPrefetch != null) && classToPrefetch.isAssignableFrom(queryObject.getClassDescriptor().getClassOfObject()))
 170  
         {
 171  
             setItemProxyClass(null);
 172  
         }
 173  
         else
 174  
         {
 175  
             setItemProxyClass(queryObject.getClassDescriptor().getProxyClass());
 176  
         }
 177  
 
 178  
         /*
 179  
                  * arminw: to improve performance we only use this instance to fire
 180  
                  * events and set the target object on every use TODO: Find a better
 181  
                  * solution
 182  
                  */
 183  
         setAfterLookupEvent(new PBLifeCycleEvent(getBroker(), PBLifeCycleEvent.Type.AFTER_LOOKUP));
 184  
 
 185  
         try
 186  
         {
 187  
             setRsAndStmt(queryObject.performQuery(broker.serviceJdbcAccess()));
 188  
             /*
 189  
              * TODO: how does prefetchRelationships handle QueryBySQL instances? Is
 190  
              * it ok to pass query object?
 191  
              */
 192  
             prefetchRelationships(queryObject.getQuery());
 193  
             if (logger.isDebugEnabled())
 194  
             {
 195  
                 logger.debug("RsIterator[" + queryObject + "] initialized");
 196  
             }
 197  
         }
 198  
         catch (RuntimeException e)
 199  
         {
 200  
             autoReleaseDbResources();
 201  
             throw e;
 202  
         }
 203  
 
 204  
         /*
 205  
         now RsIterator instance is created, we wrap this instance with a
 206  
         PBStateListener to make sure that resources of this instance will be
 207  
         released. Add this as temporary PBStateListener.
 208  
         */
 209  
         resourceListener = new ResourceWrapper(this);
 210  
         m_broker.addListener(resourceListener);
 211  
     }
 212  
 
 213  
     protected Class getTopLevelClass()
 214  
     {
 215  
         if (m_itemTopLevelClass == null)
 216  
         {
 217  
             m_itemTopLevelClass = getBroker().getTopLevelClass(getQueryObject().getClassDescriptor().getClassOfObject());
 218  
         }
 219  
         return m_itemTopLevelClass;
 220  
     }
 221  
 
 222  
     /**
 223  
      * returns true if there are still more rows in the underlying ResultSet.
 224  
      * Returns false if ResultSet is exhausted.
 225  
      */
 226  
     public synchronized boolean hasNext()
 227  
     {
 228  
         try
 229  
         {
 230  
             if (!isHasCalledCheck())
 231  
             {
 232  
                 setHasCalledCheck(true);
 233  
                 setHasNext(getRsAndStmt().m_rs.next());
 234  
                 if (!getHasNext())
 235  
                 {
 236  
                     autoReleaseDbResources();
 237  
                 }
 238  
             }
 239  
         }
 240  
         catch (Exception ex)
 241  
         {
 242  
             setHasNext(false);
 243  
             autoReleaseDbResources();
 244  
             if(ex instanceof ResourceClosedException)
 245  
             {
 246  
                 throw (ResourceClosedException)ex;
 247  
             }
 248  
             if(ex instanceof SQLException)
 249  
             {
 250  
                 throw new PersistenceBrokerSQLException("Calling ResultSet.next() failed", (SQLException) ex);
 251  
             }
 252  
             else
 253  
             {
 254  
                throw new PersistenceBrokerException("Can't get next row from ResultSet", ex);
 255  
             }
 256  
         }
 257  
         if (logger.isDebugEnabled())
 258  
             logger.debug("hasNext() -> " + getHasNext());
 259  
 
 260  
         return getHasNext();
 261  
     }
 262  
 
 263  
     /**
 264  
      * moves to the next row of the underlying ResultSet and returns the
 265  
      * corresponding Object materialized from this row.
 266  
      */
 267  
     public synchronized Object next() throws NoSuchElementException
 268  
     {
 269  
         try
 270  
         {
 271  
             if (!isHasCalledCheck())
 272  
             {
 273  
                 hasNext();
 274  
             }
 275  
             setHasCalledCheck(false);
 276  
             if (getHasNext())
 277  
             {
 278  
                 Object obj = getObjectFromResultSet();
 279  
                 m_current_row++;
 280  
 
 281  
                 // Invoke events on PersistenceBrokerAware instances and listeners
 282  
                 // set target object
 283  
                 if (!disableLifeCycleEvents)
 284  
                 {
 285  
                     getAfterLookupEvent().setTarget(obj);
 286  
                     getBroker().fireBrokerEvent(getAfterLookupEvent());
 287  
                     getAfterLookupEvent().setTarget(null);
 288  
                 }    
 289  
                 return obj;
 290  
             }
 291  
             else
 292  
             {
 293  
                 throw new NoSuchElementException("inner hasNext was false");
 294  
             }
 295  
         }
 296  
         catch (ResourceClosedException ex)
 297  
         {
 298  
             autoReleaseDbResources();
 299  
             throw ex;
 300  
         }
 301  
         catch (NoSuchElementException ex)
 302  
         {
 303  
             autoReleaseDbResources();
 304  
             logger.error("Error while iterate ResultSet for query " + m_queryObject, ex);
 305  
             throw new NoSuchElementException("Could not obtain next object: " + ex.getMessage());
 306  
         }
 307  
     }
 308  
 
 309  
     /**
 310  
      * removing is not supported
 311  
      */
 312  
     public void remove()
 313  
     {
 314  
         throw new UnsupportedOperationException("removing not supported by RsIterator");
 315  
     }
 316  
 
 317  
     /**
 318  
      * read all objects of this iterator. objects will be placed in cache
 319  
      */
 320  
     private Collection getOwnerObjects()
 321  
     {
 322  
         Collection owners = new Vector();
 323  
         while (hasNext())
 324  
         {
 325  
             owners.add(next());
 326  
         }
 327  
         return owners;
 328  
     }
 329  
 
 330  
     /**
 331  
      * prefetch defined relationships requires JDBC level 2.0, does not work
 332  
      * with Arrays
 333  
      */
 334  
     private void prefetchRelationships(Query query)
 335  
     {
 336  
         List prefetchedRel;
 337  
         Collection owners;
 338  
         String relName;
 339  
         RelationshipPrefetcher[] prefetchers;
 340  
 
 341  
         if (query == null || query.getPrefetchedRelationships() == null || query.getPrefetchedRelationships().isEmpty())
 342  
         {
 343  
             return;
 344  
         }
 345  
 
 346  
         if (!supportsAdvancedJDBCCursorControl())
 347  
         {
 348  
             logger.info("prefetching relationships requires JDBC level 2.0");
 349  
             return;
 350  
         }
 351  
 
 352  
         // prevent releasing of DBResources
 353  
         setInBatchedMode(true);
 354  
 
 355  
         prefetchedRel = query.getPrefetchedRelationships();
 356  
         prefetchers = new RelationshipPrefetcher[prefetchedRel.size()];
 357  
 
 358  
         // disable auto retrieve for all prefetched relationships
 359  
         for (int i = 0; i < prefetchedRel.size(); i++)
 360  
         {
 361  
             relName = (String) prefetchedRel.get(i);
 362  
             prefetchers[i] = getBroker().getRelationshipPrefetcherFactory()
 363  
                     .createRelationshipPrefetcher(getQueryObject().getClassDescriptor(), relName);
 364  
             prefetchers[i].prepareRelationshipSettings();
 365  
         }
 366  
 
 367  
         // materialize ALL owners of this Iterator
 368  
         owners = getOwnerObjects();
 369  
 
 370  
         // prefetch relationships and associate with owners
 371  
         for (int i = 0; i < prefetchedRel.size(); i++)
 372  
         {
 373  
             prefetchers[i].prefetchRelationship(owners);
 374  
         }
 375  
 
 376  
         // reset auto retrieve for all prefetched relationships
 377  
         for (int i = 0; i < prefetchedRel.size(); i++)
 378  
         {
 379  
             prefetchers[i].restoreRelationshipSettings();
 380  
         }
 381  
 
 382  
         try
 383  
         {
 384  
             getRsAndStmt().m_rs.beforeFirst(); // reposition resultset jdbc 2.0
 385  
         }
 386  
         catch (SQLException e)
 387  
         {
 388  
             logger.error("beforeFirst failed !", e);
 389  
         }
 390  
 
 391  
         setInBatchedMode(false);
 392  
         setHasCalledCheck(false);
 393  
     }
 394  
 
 395  
     /**
 396  
      * returns an Identity object representing the current resultset row
 397  
      */
 398  
     protected Identity getIdentityFromResultSet() throws PersistenceBrokerException
 399  
     {
 400  
         // fill primary key values from Resultset
 401  
         FieldDescriptor fld;
 402  
         FieldDescriptor[] pkFields = getQueryObject().getClassDescriptor().getPkFields();
 403  
         Object[] pkValues = new Object[pkFields.length];
 404  
 
 405  
         for (int i = 0; i < pkFields.length; i++)
 406  
         {
 407  
             fld = pkFields[i];
 408  
             pkValues[i] = getRow().get(fld.getColumnName());
 409  
         }
 410  
 
 411  
         // return identity object build up from primary keys
 412  
         return getBroker().serviceIdentity().buildIdentity(
 413  
                 getQueryObject().getClassDescriptor().getClassOfObject(), getTopLevelClass(), pkValues);
 414  
     }
 415  
 
 416  
     /**
 417  
      * returns a fully materialized Object from the current row of the
 418  
      * underlying resultset. Works as follows: - read Identity from the primary
 419  
      * key values of current row - check if Object is in cache - return cached
 420  
      * object or read it from current row and put it in cache
 421  
      */
 422  
     protected Object getObjectFromResultSet() throws PersistenceBrokerException
 423  
     {
 424  
         getRow().clear();
 425  
         /**
 426  
          * MBAIRD if a proxy is to be used, return a proxy instance and dont
 427  
          * perfom a full materialization. NOTE: Potential problem here with
 428  
          * multi-mapped table. The itemProxyClass is for the m_cld
 429  
          * classdescriptor. The object you are materializing might not be of
 430  
          * that type, it could be a subclass. We should get the concrete class
 431  
          * type out of the resultset then check the proxy from that.
 432  
          * itemProxyClass should NOT be a member variable.
 433  
          */
 434  
 
 435  
         RowReader rowReader = getQueryObject().getClassDescriptor().getRowReader();
 436  
         // in any case we need the PK values of result set row
 437  
         // provide m_row with primary key data of current row
 438  
         rowReader.readPkValuesFrom(getRsAndStmt(), getRow());
 439  
 
 440  
         if (getItemProxyClass() != null)
 441  
         {
 442  
             // assert: m_row is filled with primary key values from db
 443  
             return getProxyFromResultSet();
 444  
         }
 445  
         else
 446  
         {
 447  
             // 1.read Identity
 448  
             Identity oid = getIdentityFromResultSet();
 449  
             Object result;
 450  
 
 451  
             // 2. check if Object is in cache. if so return cached version.
 452  
             result = getCache().lookup(oid);
 453  
             if (result == null)
 454  
             {
 455  
 
 456  
                 // map all field values from the current result set
 457  
                 rowReader.readObjectArrayFrom(getRsAndStmt(), getRow());
 458  
                 // 3. If Object is not in cache
 459  
                 // materialize Object with primitive attributes filled from current row
 460  
                 result = rowReader.readObjectFrom(getRow());
 461  
                 // result may still be null!
 462  
                 if (result != null)
 463  
                 {
 464  
                     /*
 465  
                                          * synchronize on result so the ODMG-layer can take a
 466  
                                          * snapshot only of fully cached (i.e. with all references +
 467  
                                          * collections) objects
 468  
                                          */
 469  
                     synchronized (result)
 470  
                     {
 471  
                         getCache().enableMaterializationCache();
 472  
                         try
 473  
                         {
 474  
                             getCache().doInternalCache(oid, result, ObjectCacheInternal.TYPE_NEW_MATERIALIZED);
 475  
                             /**
 476  
                              * MBAIRD if you have multiple classes mapped to a
 477  
                              * table, and you query on the base class you could get
 478  
                              * back NON base class objects, so we shouldn't pass
 479  
                              * m_cld, but rather the class descriptor for the
 480  
                              * actual class.
 481  
                              */
 482  
                             // fill reference and collection attributes
 483  
                             ClassDescriptor cld = getBroker().getClassDescriptor(result.getClass());
 484  
                             // don't force loading of reference
 485  
                             final boolean unforced = false;
 486  
                             // Maps ReferenceDescriptors to HashSets of owners
 487  
                             getBroker().getReferenceBroker().retrieveReferences(result, cld, unforced);
 488  
                             getBroker().getReferenceBroker().retrieveCollections(result, cld, unforced);
 489  
                             getCache().disableMaterializationCache();
 490  
                         }
 491  
                         catch(RuntimeException e)
 492  
                         {
 493  
                             // catch runtime exc. to guarantee clearing of internal buffer on failure
 494  
                             getCache().doLocalClear();
 495  
                             throw e;
 496  
                         }
 497  
                     }
 498  
                 }
 499  
             }
 500  
             else // Object is in cache
 501  
             {
 502  
                 ClassDescriptor cld = getBroker().getClassDescriptor(result.getClass());
 503  
                 // if refresh is required, read all values from the result set and
 504  
                 // update the cache instance from the db
 505  
                 if (cld.isAlwaysRefresh())
 506  
                 {
 507  
                     // map all field values from the current result set
 508  
                     rowReader.readObjectArrayFrom(getRsAndStmt(), getRow());
 509  
                     rowReader.refreshObject(result, getRow());
 510  
                 }
 511  
                 getBroker().checkRefreshRelationships(result, oid, cld);
 512  
             }
 513  
 
 514  
             return result;
 515  
         }
 516  
     }
 517  
 
 518  
     /**
 519  
      * Reads primary key information from current RS row and generates a
 520  
      *
 521  
      * corresponding Identity, and returns a proxy from the Identity.
 522  
      *
 523  
      * @throws PersistenceBrokerException
 524  
      *             if there was an error creating the proxy class
 525  
      */
 526  
     protected Object getProxyFromResultSet() throws PersistenceBrokerException
 527  
     {
 528  
         // 1. get Identity of current row:
 529  
         Identity oid = getIdentityFromResultSet();
 530  
 
 531  
         // 2. return a Proxy instance:
 532  
         return getBroker().createProxy(getItemProxyClass(), oid);
 533  
     }
 534  
 
 535  
     /**
 536  
      * with a new batch of JDBC 3.0 drivers coming out we can't just check for
 537  
      * begins with 2, we need to check the actual version and see if it's
 538  
      * greater than or equal to 2.
 539  
      */
 540  
     private boolean supportsAdvancedJDBCCursorControl()
 541  
     {
 542  
         if (!JDBCSupportAssessed)
 543  
         {
 544  
             if (getConnectionDescriptor().getJdbcLevel() >= 2.0)
 545  
                 advancedJDBCSupport = true;
 546  
             JDBCSupportAssessed = true;
 547  
         }
 548  
         return advancedJDBCSupport;
 549  
     }
 550  
 
 551  
     /**
 552  
      * Answer the counted size
 553  
      *
 554  
      * @return int
 555  
      */
 556  
     protected int countedSize() throws PersistenceBrokerException
 557  
     {
 558  
         Query countQuery = getBroker().serviceBrokerHelper().getCountQuery(getQueryObject().getQuery());
 559  
         ResultSetAndStatement rsStmt;
 560  
         ClassDescriptor cld = getQueryObject().getClassDescriptor();
 561  
         int count = 0;
 562  
 
 563  
         // BRJ: do not use broker.getCount() because it's extent-aware
 564  
         // the count we need here must not include extents !
 565  
         if (countQuery instanceof QueryBySQL)
 566  
         {
 567  
             String countSql = ((QueryBySQL) countQuery).getSql();
 568  
             rsStmt = getBroker().serviceJdbcAccess().executeSQL(countSql, cld, Query.NOT_SCROLLABLE);
 569  
         }
 570  
         else
 571  
         {
 572  
             rsStmt = getBroker().serviceJdbcAccess().executeQuery(countQuery, cld);
 573  
         }
 574  
 
 575  
         try
 576  
         {
 577  
             if (rsStmt.m_rs.next())
 578  
             {
 579  
                 count = rsStmt.m_rs.getInt(1);
 580  
             }
 581  
         }
 582  
         catch (SQLException e)
 583  
         {
 584  
             throw new PersistenceBrokerException(e);
 585  
         }
 586  
         finally
 587  
         {
 588  
             rsStmt.close();
 589  
         }
 590  
 
 591  
         return count;
 592  
     }
 593  
 
 594  
     /**
 595  
      * @return the size of the iterator, aka the number of rows in this
 596  
      *         iterator.
 597  
      */
 598  
     public int size() throws PersistenceBrokerException
 599  
     {
 600  
         int retval = 0; // default size is 0;
 601  
         boolean forwardOnly = true;
 602  
         try
 603  
         {
 604  
             forwardOnly = getRsAndStmt().m_stmt.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY;
 605  
         }
 606  
         catch (SQLException e)
 607  
         {
 608  
             //ignore it
 609  
         }
 610  
         if (!supportsAdvancedJDBCCursorControl()
 611  
                 || getBroker().serviceConnectionManager().getSupportedPlatform().useCountForResultsetSize()
 612  
                 || forwardOnly)
 613  
         {
 614  
             /**
 615  
              * MBAIRD: doesn't support the .last .getRow method, use the
 616  
              * .getCount on the persistenceBroker which executes a count(*)
 617  
              * query.
 618  
              */
 619  
             if (logger.isDebugEnabled())
 620  
                 logger.debug("Executing count(*) to get size()");
 621  
             retval = countedSize();
 622  
         }
 623  
         else
 624  
         {
 625  
             /**
 626  
              * Use the .last .getRow method of finding size. The reason for
 627  
              * supplying an alternative method is effeciency, some driver/db
 628  
              * combos are a lot more efficient at just moving the cursor and
 629  
              * returning the row in a real (not -1) number.
 630  
              */
 631  
             int whereIAm; // first
 632  
             try
 633  
             {
 634  
                 if (getRsAndStmt().m_rs != null)
 635  
                 {
 636  
                     whereIAm = getRsAndStmt().m_rs.getRow();
 637  
                     if (getRsAndStmt().m_rs.last())
 638  
                     {
 639  
                         retval = getRsAndStmt().m_rs.getRow();
 640  
                     }
 641  
                     else
 642  
                     {
 643  
                         retval = 0;
 644  
                     }
 645  
                     // go back from whence I came.
 646  
                     if (whereIAm > 0)
 647  
                     {
 648  
                         getRsAndStmt().m_rs.absolute(whereIAm);
 649  
                     }
 650  
                     else
 651  
                     {
 652  
                         getRsAndStmt().m_rs.beforeFirst();
 653  
                     }
 654  
                 }
 655  
             }
 656  
             catch (SQLException se)
 657  
             {
 658  
                 advancedJDBCSupport = false;
 659  
             }
 660  
         }
 661  
         return retval;
 662  
     }
 663  
 
 664  
     /* (non-Javadoc)
 665  
      * @see org.apache.ojb.broker.accesslayer.OJBIterator#fullSize()
 666  
      */
 667  
     public int fullSize() throws PersistenceBrokerException
 668  
     {
 669  
         return size();
 670  
     }
 671  
 
 672  
     /**
 673  
      * Moves the cursor to the given row number in the iterator. If the row
 674  
      * number is positive, the cursor moves to the given row number with
 675  
      * respect to the beginning of the iterator. The first row is row 1, the
 676  
      * second is row 2, and so on.
 677  
      *
 678  
      * @param row  the row to move to in this iterator, by absolute number
 679  
      */
 680  
     public boolean absolute(int row) throws PersistenceBrokerException
 681  
     {
 682  
         boolean retval;
 683  
         if (supportsAdvancedJDBCCursorControl())
 684  
         {
 685  
             retval = absoluteAdvanced(row);
 686  
         }
 687  
         else
 688  
         {
 689  
             retval = absoluteBasic(row);
 690  
         }
 691  
         return retval;
 692  
     }
 693  
 
 694  
     /**
 695  
      * absolute for basicJDBCSupport
 696  
      * @param row
 697  
      */
 698  
     private boolean absoluteBasic(int row)
 699  
     {
 700  
         boolean retval = false;
 701  
         
 702  
         if (row > m_current_row)
 703  
         {
 704  
             try
 705  
             {
 706  
                 while (m_current_row < row && getRsAndStmt().m_rs.next())
 707  
                 {
 708  
                     m_current_row++;
 709  
                 }
 710  
                 if (m_current_row == row)
 711  
                 {
 712  
                     retval = true;
 713  
                 }
 714  
                 else
 715  
                 {
 716  
                     setHasCalledCheck(true);
 717  
                     setHasNext(false);
 718  
                     retval = false;
 719  
                     autoReleaseDbResources();
 720  
                 }
 721  
             }
 722  
             catch (Exception ex)
 723  
             {
 724  
                 setHasCalledCheck(true);
 725  
                 setHasNext(false);
 726  
                 retval = false;
 727  
             }
 728  
         }
 729  
         else
 730  
         {
 731  
             logger.info("Your driver does not support advanced JDBC Functionality, " +
 732  
                     "you cannot call absolute() with a position < current");
 733  
         }
 734  
         return retval;
 735  
     }
 736  
 
 737  
     /**
 738  
      * absolute for advancedJDBCSupport
 739  
      * @param row
 740  
      */
 741  
     private boolean absoluteAdvanced(int row)
 742  
     {
 743  
         boolean retval = false;
 744  
         
 745  
         try
 746  
         {
 747  
             if (getRsAndStmt().m_rs != null)
 748  
             {
 749  
                 if (row == 0)
 750  
                 {
 751  
                     getRsAndStmt().m_rs.beforeFirst();
 752  
                 }
 753  
                 else
 754  
                 {
 755  
                     retval = getRsAndStmt().m_rs.absolute(row);                        
 756  
                 }
 757  
                 m_current_row = row;
 758  
                 setHasCalledCheck(false);
 759  
             }
 760  
         }
 761  
         catch (SQLException e)
 762  
         {
 763  
             advancedJDBCSupport = false;
 764  
         }
 765  
         return retval;
 766  
     }
 767  
 
 768  
     /**
 769  
      * Moves the cursor a relative number of rows, either positive or negative.
 770  
      * Attempting to move beyond the first/last row in the iterator positions
 771  
      * the cursor before/after the the first/last row. Calling relative(0) is
 772  
      * valid, but does not change the cursor position.
 773  
      *
 774  
      * @param row
 775  
      *            the row to move to in this iterator, by relative number
 776  
      */
 777  
     public boolean relative(int row) throws PersistenceBrokerException
 778  
     {
 779  
         boolean retval = false;
 780  
         if (supportsAdvancedJDBCCursorControl())
 781  
         {
 782  
             try
 783  
             {
 784  
                 if (getRsAndStmt().m_rs != null)
 785  
                 {
 786  
                     retval = getRsAndStmt().m_rs.relative(row);
 787  
                     m_current_row += row;
 788  
                 }
 789  
             }
 790  
             catch (SQLException e)
 791  
             {
 792  
                 advancedJDBCSupport = false;
 793  
             }
 794  
         }
 795  
         else
 796  
         {
 797  
             if (row >= 0)
 798  
             {
 799  
                 return absolute(m_current_row + row);
 800  
             }
 801  
             else
 802  
             {
 803  
                 logger.info("Your driver does not support advanced JDBC Functionality, you cannot call relative() with a negative value");
 804  
             }
 805  
         }
 806  
         return retval;
 807  
     }
 808  
 
 809  
     /**
 810  
      * Release all internally used Database resources of the iterator. Clients
 811  
      * must call this methods explicitely if the iterator is not exhausted by
 812  
      * the client application. If the Iterator is exhauseted this method will
 813  
      * be called implicitely.
 814  
      */
 815  
     public void releaseDbResources()
 816  
     {
 817  
         release(true);
 818  
     }
 819  
 
 820  
     void release(boolean removeResourceListener)
 821  
     {
 822  
         if (!isInBatchedMode()) // resources are reused in batched mode
 823  
         {
 824  
             // If we haven't released resources yet, then do so.
 825  
             if (!this.resourcesAreReleased)
 826  
             {
 827  
                 // remove the resource listener
 828  
                 if(removeResourceListener && resourceListener != null)
 829  
                 {
 830  
                     try
 831  
                     {
 832  
                         /*
 833  
                         when RsIterator is closed, the resource listener
 834  
                         was no longer needed to listen on PB events for clean up.
 835  
                         */
 836  
                         m_broker.removeListener(resourceListener);
 837  
                         this.resourceListener = null;
 838  
                     }
 839  
                     catch(Exception e)
 840  
                     {
 841  
                         logger.error("Error when try to remove RsIterator resource listener", e);
 842  
                     }
 843  
                 }
 844  
                 this.resourcesAreReleased = true;
 845  
                 if (m_rsAndStmt != null)
 846  
                 {
 847  
                     m_rsAndStmt.close();
 848  
                     m_rsAndStmt = null;
 849  
                 }
 850  
             }
 851  
         }
 852  
     }
 853  
 
 854  
     /**
 855  
      * Internally used by this class to close used resources
 856  
      * as soon as possible.
 857  
      */
 858  
     protected void autoReleaseDbResources()
 859  
     {
 860  
         if(autoRelease)
 861  
         {
 862  
             releaseDbResources();
 863  
         }
 864  
     }
 865  
 
 866  
     /**
 867  
      * Allows user to switch off/on automatic resource cleanup.
 868  
      * Set <tt>false</tt> to take responsibility of resource cleanup
 869  
      * for this class, means after use it's mandatory to call
 870  
      * {@link #releaseDbResources}.
 871  
      * <br/> By default it's <tt>true</tt> and resource cleanup is done
 872  
      * automatic.
 873  
      */
 874  
     public void setAutoRelease(boolean autoRelease)
 875  
     {
 876  
         /*
 877  
         arminw:
 878  
         this method should be declared in OJBIterator interface till
 879  
         OJB 1.1 and PersistenceBroker interface should only return
 880  
         OJBIterator instead of Iterator instances
 881  
         */
 882  
         if(resourcesAreReleased && !autoRelease)
 883  
         {
 884  
             logger.info(INFO_MSG);
 885  
         }
 886  
         this.autoRelease = autoRelease;
 887  
     }
 888  
 
 889  
     /**
 890  
      * Return the DescriptorRepository
 891  
      */
 892  
     protected DescriptorRepository getDescriptorRepository()
 893  
     {
 894  
         return getBroker().getDescriptorRepository();
 895  
     }
 896  
 
 897  
     protected JdbcConnectionDescriptor getConnectionDescriptor()
 898  
     {
 899  
         return getBroker().serviceConnectionManager().getConnectionDescriptor();
 900  
     }
 901  
 
 902  
     /**
 903  
      * safety just in case someone leaks.
 904  
      */
 905  
     protected void finalize()
 906  
     {
 907  
         if (m_rsAndStmt != null)
 908  
         {
 909  
             logger.info("Found unclosed resources while finalize (causer class: " + this.getClass().getName() + ")" +
 910  
                     " Do automatic cleanup");
 911  
             releaseDbResources();
 912  
         }
 913  
         try
 914  
         {
 915  
             super.finalize();
 916  
         }
 917  
         catch(Throwable throwable)
 918  
         {
 919  
             throwable.printStackTrace();
 920  
         }
 921  
     }
 922  
 
 923  
     public String toString()
 924  
     {
 925  
         return super.toString();
 926  
     }
 927  
 
 928  
     /**
 929  
      * @return Returns the cld.
 930  
      */
 931  
     public ClassDescriptor getClassDescriptor()
 932  
     {
 933  
         return getQueryObject().getClassDescriptor();
 934  
     }
 935  
 
 936  
     protected void setBroker(PersistenceBrokerImpl broker)
 937  
     {
 938  
         m_broker = broker;
 939  
     }
 940  
 
 941  
     protected PersistenceBrokerInternal getBroker()
 942  
     {
 943  
         return m_broker;
 944  
     }
 945  
 
 946  
     protected void setRsAndStmt(ResultSetAndStatement rsAndStmt)
 947  
     {
 948  
         if(m_rsAndStmt != null)
 949  
         {
 950  
             throw new ResourceNotClosedException("Unclosed resources found, please release resources" +
 951  
                     " before set new ones");
 952  
         }
 953  
         resourcesAreReleased = false;
 954  
         m_rsAndStmt = rsAndStmt;
 955  
     }
 956  
 
 957  
     protected ResultSetAndStatement getRsAndStmt()
 958  
     {
 959  
         if(resourcesAreReleased)
 960  
         {
 961  
             throw new ResourceClosedException("Resources no longer reachable, RsIterator will be automatic" +
 962  
                     " cleaned up on PB.close/.commitTransaction/.abortTransaction");
 963  
         }
 964  
         return m_rsAndStmt;
 965  
     }
 966  
 
 967  
     protected void setQueryObject(RsQueryObject queryObject)
 968  
     {
 969  
         this.m_queryObject = queryObject;
 970  
     }
 971  
 
 972  
     protected RsQueryObject getQueryObject()
 973  
     {
 974  
         return m_queryObject;
 975  
     }
 976  
 
 977  
     protected void setItemProxyClass(Class itemProxyClass)
 978  
     {
 979  
         this.m_itemProxyClass = itemProxyClass;
 980  
     }
 981  
 
 982  
     protected Class getItemProxyClass()
 983  
     {
 984  
         return m_itemProxyClass;
 985  
     }
 986  
 
 987  
     protected void setRow(Map row)
 988  
     {
 989  
         m_row = row;
 990  
     }
 991  
 
 992  
     protected Map getRow()
 993  
     {
 994  
         return m_row;
 995  
     }
 996  
 
 997  
     protected void setCache(MaterializationCache cache)
 998  
     {
 999  
         this.m_cache = cache;
 1000  
     }
 1001  
 
 1002  
     protected MaterializationCache getCache()
 1003  
     {
 1004  
         return m_cache;
 1005  
     }
 1006  
 
 1007  
     protected void setAfterLookupEvent(PBLifeCycleEvent afterLookupEvent)
 1008  
     {
 1009  
         this.afterLookupEvent = afterLookupEvent;
 1010  
     }
 1011  
 
 1012  
     protected PBLifeCycleEvent getAfterLookupEvent()
 1013  
     {
 1014  
         return afterLookupEvent;
 1015  
     }
 1016  
 
 1017  
     protected void setHasCalledCheck(boolean hasCalledCheck)
 1018  
     {
 1019  
         this.m_hasCalledCheck = hasCalledCheck;
 1020  
     }
 1021  
 
 1022  
     protected boolean isHasCalledCheck()
 1023  
     {
 1024  
         return m_hasCalledCheck;
 1025  
     }
 1026  
 
 1027  
     protected void setHasNext(boolean hasNext)
 1028  
     {
 1029  
         this.hasNext = hasNext;
 1030  
     }
 1031  
 
 1032  
     protected boolean getHasNext()
 1033  
     {
 1034  
         return hasNext;
 1035  
     }
 1036  
 
 1037  
     protected void setInBatchedMode(boolean inBatchedMode)
 1038  
     {
 1039  
         this.m_inBatchedMode = inBatchedMode;
 1040  
     }
 1041  
 
 1042  
     protected boolean isInBatchedMode()
 1043  
     {
 1044  
         return m_inBatchedMode;
 1045  
     }
 1046  
 
 1047  
     //***********************************************************
 1048  
     // inner classes
 1049  
     //***********************************************************
 1050  
     /**
 1051  
      * Wraps a {@link RsIterator} instance as {@link WeakReference}.
 1052  
      */
 1053  
     public static class ResourceWrapper implements PBStateListener
 1054  
     {
 1055  
         /*
 1056  
         arminw:
 1057  
         we do register a PBStateListener to PB instance
 1058  
         to make sure that this instance will be cleaned up at PB.close() call.
 1059  
         If PB was in tx, we cleanup resources on PB.commit/abort, because
 1060  
         commit/abort close the current used connection and all Statement/ResultSet
 1061  
         instances will become invalid.
 1062  
         */
 1063  
         WeakReference ref;
 1064  
 
 1065  
         public ResourceWrapper(RsIterator rs)
 1066  
         {
 1067  
             ref = new WeakReference(rs);
 1068  
         }
 1069  
 
 1070  
         public void beforeClose(PBStateEvent event)
 1071  
         {
 1072  
             if(ref != null)
 1073  
             {
 1074  
                 RsIterator rs = (RsIterator) ref.get();
 1075  
                 if(rs != null) rs.release(false);
 1076  
                 ref = null;
 1077  
             }
 1078  
         }
 1079  
 
 1080  
         public void beforeRollback(PBStateEvent event)
 1081  
         {
 1082  
             if(ref != null)
 1083  
             {
 1084  
                 RsIterator rs = (RsIterator) ref.get();
 1085  
                 if(rs != null) rs.release(false);
 1086  
                 ref = null;
 1087  
             }
 1088  
         }
 1089  
 
 1090  
         public void beforeCommit(PBStateEvent event)
 1091  
         {
 1092  
             if(ref != null)
 1093  
             {
 1094  
                 RsIterator rs = (RsIterator) ref.get();
 1095  
                 if(rs != null) rs.release(false);
 1096  
                 ref = null;
 1097  
             }
 1098  
         }
 1099  
 
 1100  
         public void afterCommit(PBStateEvent event)
 1101  
         {
 1102  
             //do nothing
 1103  
         }
 1104  
         public void afterRollback(PBStateEvent event)
 1105  
         {
 1106  
             //do nothing
 1107  
         }
 1108  
         public void afterBegin(PBStateEvent event)
 1109  
         {
 1110  
             //do nothing
 1111  
         }
 1112  
         public void beforeBegin(PBStateEvent event)
 1113  
         {
 1114  
             //do nothing
 1115  
         }
 1116  
         public void afterOpen(PBStateEvent event)
 1117  
         {
 1118  
             //do nothing
 1119  
         }
 1120  
     }
 1121  
 
 1122  
     public static class ResourceClosedException extends OJBRuntimeException
 1123  
     {
 1124  
         public ResourceClosedException(String msg)
 1125  
         {
 1126  
             super(msg);
 1127  
         }
 1128  
 
 1129  
         public ResourceClosedException(String msg, Throwable cause)
 1130  
         {
 1131  
             super(msg, cause);
 1132  
         }
 1133  
     }
 1134  
 
 1135  
     public static class ResourceNotClosedException extends OJBRuntimeException
 1136  
     {
 1137  
         public ResourceNotClosedException(String msg)
 1138  
         {
 1139  
             super(msg);
 1140  
         }
 1141  
 
 1142  
         public ResourceNotClosedException(String msg, Throwable cause)
 1143  
         {
 1144  
             super(msg, cause);
 1145  
         }
 1146  
     }
 1147  
 
 1148  
     /**
 1149  
      * @see org.apache.ojb.broker.accesslayer.OJBIterator#disableLifeCycleEvents()
 1150  
      */
 1151  
     public void disableLifeCycleEvents()
 1152  
     {
 1153  
         disableLifeCycleEvents = true;
 1154  
     }
 1155  
 }