KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > accesslayer > RsIterator


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 JavaDoc;
19 import java.sql.ResultSet JavaDoc;
20 import java.sql.SQLException JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.NoSuchElementException JavaDoc;
26 import java.util.Vector JavaDoc;
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.63.2.19 2005/12/21 22:22:58 tomdz Exp $
77  */

78 public class RsIterator implements OJBIterator
79 {
80     protected Logger logger = LoggerFactory.getLogger(this.getClass());
81     private static final String JavaDoc 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 JavaDoc m_itemProxyClass;
109
110     /**
111      * the top-level class of the item objects
112      */

113     private Class JavaDoc m_itemTopLevelClass = null;
114
115     /**
116      * this container holds the values of the current ro during materialisation
117      */

118     private Map JavaDoc 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 JavaDoc());
165         setBroker(broker);
166         setQueryObject(queryObject);
167
168         Class JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc ex)
241         {
242             setHasNext(false);
243             autoReleaseDbResources();
244             if(ex instanceof ResourceClosedException)
245             {
246                 throw (ResourceClosedException)ex;
247             }
248             if(ex instanceof SQLException JavaDoc)
249             {
250                 throw new PersistenceBrokerSQLException("Calling ResultSet.next() failed", (SQLException JavaDoc) 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 JavaDoc next() throws NoSuchElementException JavaDoc
268     {
269         try
270         {
271             if (!isHasCalledCheck())
272             {
273                 hasNext();
274             }
275             setHasCalledCheck(false);
276             if (getHasNext())
277             {
278                 Object JavaDoc 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 JavaDoc("inner hasNext was false");
294             }
295         }
296         catch (ResourceClosedException ex)
297         {
298             autoReleaseDbResources();
299             throw ex;
300         }
301         catch (NoSuchElementException JavaDoc ex)
302         {
303             autoReleaseDbResources();
304             logger.error("Error while iterate ResultSet for query " + m_queryObject, ex);
305             throw new NoSuchElementException JavaDoc("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 JavaDoc("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 JavaDoc getOwnerObjects()
321     {
322         Collection JavaDoc owners = new Vector JavaDoc();
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 JavaDoc prefetchedRel;
337         Collection JavaDoc owners;
338         String JavaDoc 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 JavaDoc) 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 JavaDoc 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 JavaDoc[] pkValues = new Object JavaDoc[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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc throwable)
918         {
919             throwable.printStackTrace();
920         }
921     }
922
923     public String JavaDoc 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 JavaDoc itemProxyClass)
978     {
979         this.m_itemProxyClass = itemProxyClass;
980     }
981
982     protected Class JavaDoc getItemProxyClass()
983     {
984         return m_itemProxyClass;
985     }
986
987     protected void setRow(Map JavaDoc row)
988     {
989         m_row = row;
990     }
991
992     protected Map JavaDoc 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 JavaDoc ref;
1064
1065        public ResourceWrapper(RsIterator rs)
1066        {
1067            ref = new WeakReference JavaDoc(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 JavaDoc msg)
1125        {
1126            super(msg);
1127        }
1128
1129        public ResourceClosedException(String JavaDoc msg, Throwable JavaDoc cause)
1130        {
1131            super(msg, cause);
1132        }
1133    }
1134
1135    public static class ResourceNotClosedException extends OJBRuntimeException
1136    {
1137        public ResourceNotClosedException(String JavaDoc msg)
1138        {
1139            super(msg);
1140        }
1141
1142        public ResourceNotClosedException(String JavaDoc msg, Throwable JavaDoc 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}
1156
Popular Tags