0001: package org.apache.ojb.broker.accesslayer;
0002:
0003: /* Copyright 2002-2005 The Apache Software Foundation
0004: *
0005: * Licensed under the Apache License, Version 2.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: import java.lang.ref.WeakReference;
0019: import java.sql.ResultSet;
0020: import java.sql.SQLException;
0021: import java.util.Collection;
0022: import java.util.HashMap;
0023: import java.util.List;
0024: import java.util.Map;
0025: import java.util.NoSuchElementException;
0026: import java.util.Vector;
0027:
0028: import org.apache.ojb.broker.Identity;
0029: import org.apache.ojb.broker.OJBRuntimeException;
0030: import org.apache.ojb.broker.PBLifeCycleEvent;
0031: import org.apache.ojb.broker.PBStateEvent;
0032: import org.apache.ojb.broker.PBStateListener;
0033: import org.apache.ojb.broker.PersistenceBrokerException;
0034: import org.apache.ojb.broker.PersistenceBrokerInternal;
0035: import org.apache.ojb.broker.PersistenceBrokerSQLException;
0036: import org.apache.ojb.broker.cache.MaterializationCache;
0037: import org.apache.ojb.broker.cache.ObjectCacheInternal;
0038: import org.apache.ojb.broker.core.PersistenceBrokerImpl;
0039: import org.apache.ojb.broker.metadata.ClassDescriptor;
0040: import org.apache.ojb.broker.metadata.DescriptorRepository;
0041: import org.apache.ojb.broker.metadata.FieldDescriptor;
0042: import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
0043: import org.apache.ojb.broker.query.Query;
0044: import org.apache.ojb.broker.query.QueryBySQL;
0045: import org.apache.ojb.broker.util.logging.Logger;
0046: import org.apache.ojb.broker.util.logging.LoggerFactory;
0047:
0048: /**
0049: * RsIterator can be used to iterate over a jdbc ResultSet to retrieve
0050: * persistent objects step-by-step and not all at once. In fact the
0051: * PersistenceBroker::getCollectionByQuery(...) method uses a RsIterator
0052: * internally to build up a Collection of objects
0053: *
0054: * <p>
0055: * NOTE: OJB is very strict in handling <tt>RsIterator</tt> instances. <tt>RsIterator</tt> is
0056: * bound very closely to the used {@link org.apache.ojb.broker.PersistenceBroker} instance.
0057: * Thus if you do a
0058: * <br/> - {@link org.apache.ojb.broker.PersistenceBroker#close}
0059: * <br/> - {@link org.apache.ojb.broker.PersistenceBroker#commitTransaction}
0060: * <br/> - {@link org.apache.ojb.broker.PersistenceBroker#abortTransaction}
0061: * <br/>
0062: * call, the current <tt>RsIterator</tt> instance resources will be cleaned up automatic
0063: * and invalidate current instance.
0064: * </p>
0065: *
0066: * <p>
0067: * NOTE: this code uses features that only JDBC 2.0 compliant Drivers support.
0068: * It will NOT work with JDBC 1.0 Drivers (e.g. SUN's JdbcOdbcDriver) If you
0069: * are forced to use such a driver, you can use code from the 0.1.30 release.
0070: * </p>
0071: * @author <a href="mailto:thma@apache.org">Thomas Mahler <a>
0072: * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird <a>- added the
0073: * support for extents mapped to single table - added the .size
0074: * functionality - added cursor control
0075: *
0076: * @version $Id: RsIterator.java,v 1.63.2.19 2005/12/21 22:22:58 tomdz Exp $
0077: */
0078: public class RsIterator implements OJBIterator {
0079: protected Logger logger = LoggerFactory.getLogger(this .getClass());
0080: private static final String INFO_MSG = "Resources already cleaned up, recommend to set"
0081: + " this flag before first use of the iterator";
0082: /*
0083: * arminw: to improve performance we only use this instance to fire events
0084: * and set the target object on every use TODO: Find a better solution
0085: */
0086: private PBLifeCycleEvent afterLookupEvent;
0087: private MaterializationCache m_cache;
0088:
0089: /**
0090: * reference to the PersistenceBroker
0091: */
0092: private PersistenceBrokerImpl m_broker;
0093:
0094: /**
0095: * the underlying resultset
0096: */
0097: private ResultSetAndStatement m_rsAndStmt;
0098:
0099: /**
0100: * the underlying query object
0101: */
0102: private RsQueryObject m_queryObject;
0103:
0104: /**
0105: * the proxy class to be used or null
0106: */
0107: private Class m_itemProxyClass;
0108:
0109: /**
0110: * the top-level class of the item objects
0111: */
0112: private Class m_itemTopLevelClass = null;
0113:
0114: /**
0115: * this container holds the values of the current ro during materialisation
0116: */
0117: private Map m_row = null;
0118:
0119: /**
0120: * flag that indicates wether hasNext on m_rs has allready been called
0121: */
0122: private boolean m_hasCalledCheck = false;
0123:
0124: /**
0125: * prefetch relationship: inBatchedMode true prevents releasing of
0126: * DbResources IN_LIMIT defines the max number of values of sql (IN) , -1
0127: * for no limits
0128: */
0129: private boolean m_inBatchedMode = false;
0130:
0131: /**
0132: * return value of the previously called hasNext from m_rs
0133: */
0134: private boolean hasNext = false;
0135:
0136: private boolean advancedJDBCSupport = false;
0137: private boolean JDBCSupportAssessed = false;
0138: private int m_current_row = 0;
0139: /**
0140: * Tracks whether or not the resources that are used by this class have been released.
0141: */
0142: private boolean resourcesAreReleased = false;
0143:
0144: /**
0145: * Flag that indicates if the automatic resource cleanup should be
0146: * done or not. Default is <tt>true</tt>.
0147: */
0148: private boolean autoRelease = true;
0149: private ResourceWrapper resourceListener;
0150:
0151: /** if true do not fire PBLifeCycleEvent. */
0152: private boolean disableLifeCycleEvents = false;
0153:
0154: /**
0155: * RsIterator constructor.
0156: *
0157: * @param queryObject query object
0158: * @param broker the broker we should use.
0159: */
0160: public RsIterator(RsQueryObject queryObject,
0161: final PersistenceBrokerImpl broker) {
0162: setCache(broker.getInternalCache());
0163: setRow(new HashMap());
0164: setBroker(broker);
0165: setQueryObject(queryObject);
0166:
0167: Class classToPrefetch = broker.getReferenceBroker()
0168: .getClassToPrefetch();
0169: if ((classToPrefetch != null)
0170: && classToPrefetch.isAssignableFrom(queryObject
0171: .getClassDescriptor().getClassOfObject())) {
0172: setItemProxyClass(null);
0173: } else {
0174: setItemProxyClass(queryObject.getClassDescriptor()
0175: .getProxyClass());
0176: }
0177:
0178: /*
0179: * arminw: to improve performance we only use this instance to fire
0180: * events and set the target object on every use TODO: Find a better
0181: * solution
0182: */
0183: setAfterLookupEvent(new PBLifeCycleEvent(getBroker(),
0184: PBLifeCycleEvent.Type.AFTER_LOOKUP));
0185:
0186: try {
0187: setRsAndStmt(queryObject.performQuery(broker
0188: .serviceJdbcAccess()));
0189: /*
0190: * TODO: how does prefetchRelationships handle QueryBySQL instances? Is
0191: * it ok to pass query object?
0192: */
0193: prefetchRelationships(queryObject.getQuery());
0194: if (logger.isDebugEnabled()) {
0195: logger.debug("RsIterator[" + queryObject
0196: + "] initialized");
0197: }
0198: } catch (RuntimeException e) {
0199: autoReleaseDbResources();
0200: throw e;
0201: }
0202:
0203: /*
0204: now RsIterator instance is created, we wrap this instance with a
0205: PBStateListener to make sure that resources of this instance will be
0206: released. Add this as temporary PBStateListener.
0207: */
0208: resourceListener = new ResourceWrapper(this );
0209: m_broker.addListener(resourceListener);
0210: }
0211:
0212: protected Class getTopLevelClass() {
0213: if (m_itemTopLevelClass == null) {
0214: m_itemTopLevelClass = getBroker().getTopLevelClass(
0215: getQueryObject().getClassDescriptor()
0216: .getClassOfObject());
0217: }
0218: return m_itemTopLevelClass;
0219: }
0220:
0221: /**
0222: * returns true if there are still more rows in the underlying ResultSet.
0223: * Returns false if ResultSet is exhausted.
0224: */
0225: public synchronized boolean hasNext() {
0226: try {
0227: if (!isHasCalledCheck()) {
0228: setHasCalledCheck(true);
0229: setHasNext(getRsAndStmt().m_rs.next());
0230: if (!getHasNext()) {
0231: autoReleaseDbResources();
0232: }
0233: }
0234: } catch (Exception ex) {
0235: setHasNext(false);
0236: autoReleaseDbResources();
0237: if (ex instanceof ResourceClosedException) {
0238: throw (ResourceClosedException) ex;
0239: }
0240: if (ex instanceof SQLException) {
0241: throw new PersistenceBrokerSQLException(
0242: "Calling ResultSet.next() failed",
0243: (SQLException) ex);
0244: } else {
0245: throw new PersistenceBrokerException(
0246: "Can't get next row from ResultSet", ex);
0247: }
0248: }
0249: if (logger.isDebugEnabled())
0250: logger.debug("hasNext() -> " + getHasNext());
0251:
0252: return getHasNext();
0253: }
0254:
0255: /**
0256: * moves to the next row of the underlying ResultSet and returns the
0257: * corresponding Object materialized from this row.
0258: */
0259: public synchronized Object next() throws NoSuchElementException {
0260: try {
0261: if (!isHasCalledCheck()) {
0262: hasNext();
0263: }
0264: setHasCalledCheck(false);
0265: if (getHasNext()) {
0266: Object obj = getObjectFromResultSet();
0267: m_current_row++;
0268:
0269: // Invoke events on PersistenceBrokerAware instances and listeners
0270: // set target object
0271: if (!disableLifeCycleEvents) {
0272: getAfterLookupEvent().setTarget(obj);
0273: getBroker().fireBrokerEvent(getAfterLookupEvent());
0274: getAfterLookupEvent().setTarget(null);
0275: }
0276: return obj;
0277: } else {
0278: throw new NoSuchElementException(
0279: "inner hasNext was false");
0280: }
0281: } catch (ResourceClosedException ex) {
0282: autoReleaseDbResources();
0283: throw ex;
0284: } catch (NoSuchElementException ex) {
0285: autoReleaseDbResources();
0286: logger.error("Error while iterate ResultSet for query "
0287: + m_queryObject, ex);
0288: throw new NoSuchElementException(
0289: "Could not obtain next object: " + ex.getMessage());
0290: }
0291: }
0292:
0293: /**
0294: * removing is not supported
0295: */
0296: public void remove() {
0297: throw new UnsupportedOperationException(
0298: "removing not supported by RsIterator");
0299: }
0300:
0301: /**
0302: * read all objects of this iterator. objects will be placed in cache
0303: */
0304: private Collection getOwnerObjects() {
0305: Collection owners = new Vector();
0306: while (hasNext()) {
0307: owners.add(next());
0308: }
0309: return owners;
0310: }
0311:
0312: /**
0313: * prefetch defined relationships requires JDBC level 2.0, does not work
0314: * with Arrays
0315: */
0316: private void prefetchRelationships(Query query) {
0317: List prefetchedRel;
0318: Collection owners;
0319: String relName;
0320: RelationshipPrefetcher[] prefetchers;
0321:
0322: if (query == null || query.getPrefetchedRelationships() == null
0323: || query.getPrefetchedRelationships().isEmpty()) {
0324: return;
0325: }
0326:
0327: if (!supportsAdvancedJDBCCursorControl()) {
0328: logger
0329: .info("prefetching relationships requires JDBC level 2.0");
0330: return;
0331: }
0332:
0333: // prevent releasing of DBResources
0334: setInBatchedMode(true);
0335:
0336: prefetchedRel = query.getPrefetchedRelationships();
0337: prefetchers = new RelationshipPrefetcher[prefetchedRel.size()];
0338:
0339: // disable auto retrieve for all prefetched relationships
0340: for (int i = 0; i < prefetchedRel.size(); i++) {
0341: relName = (String) prefetchedRel.get(i);
0342: prefetchers[i] = getBroker()
0343: .getRelationshipPrefetcherFactory()
0344: .createRelationshipPrefetcher(
0345: getQueryObject().getClassDescriptor(),
0346: relName);
0347: prefetchers[i].prepareRelationshipSettings();
0348: }
0349:
0350: // materialize ALL owners of this Iterator
0351: owners = getOwnerObjects();
0352:
0353: // prefetch relationships and associate with owners
0354: for (int i = 0; i < prefetchedRel.size(); i++) {
0355: prefetchers[i].prefetchRelationship(owners);
0356: }
0357:
0358: // reset auto retrieve for all prefetched relationships
0359: for (int i = 0; i < prefetchedRel.size(); i++) {
0360: prefetchers[i].restoreRelationshipSettings();
0361: }
0362:
0363: try {
0364: getRsAndStmt().m_rs.beforeFirst(); // reposition resultset jdbc 2.0
0365: } catch (SQLException e) {
0366: logger.error("beforeFirst failed !", e);
0367: }
0368:
0369: setInBatchedMode(false);
0370: setHasCalledCheck(false);
0371: }
0372:
0373: /**
0374: * returns an Identity object representing the current resultset row
0375: */
0376: protected Identity getIdentityFromResultSet()
0377: throws PersistenceBrokerException {
0378: // fill primary key values from Resultset
0379: FieldDescriptor fld;
0380: FieldDescriptor[] pkFields = getQueryObject()
0381: .getClassDescriptor().getPkFields();
0382: Object[] pkValues = new Object[pkFields.length];
0383:
0384: for (int i = 0; i < pkFields.length; i++) {
0385: fld = pkFields[i];
0386: pkValues[i] = getRow().get(fld.getColumnName());
0387: }
0388:
0389: // return identity object build up from primary keys
0390: return getBroker().serviceIdentity().buildIdentity(
0391: getQueryObject().getClassDescriptor()
0392: .getClassOfObject(), getTopLevelClass(),
0393: pkValues);
0394: }
0395:
0396: /**
0397: * returns a fully materialized Object from the current row of the
0398: * underlying resultset. Works as follows: - read Identity from the primary
0399: * key values of current row - check if Object is in cache - return cached
0400: * object or read it from current row and put it in cache
0401: */
0402: protected Object getObjectFromResultSet()
0403: throws PersistenceBrokerException {
0404: getRow().clear();
0405: /**
0406: * MBAIRD if a proxy is to be used, return a proxy instance and dont
0407: * perfom a full materialization. NOTE: Potential problem here with
0408: * multi-mapped table. The itemProxyClass is for the m_cld
0409: * classdescriptor. The object you are materializing might not be of
0410: * that type, it could be a subclass. We should get the concrete class
0411: * type out of the resultset then check the proxy from that.
0412: * itemProxyClass should NOT be a member variable.
0413: */
0414:
0415: RowReader rowReader = getQueryObject().getClassDescriptor()
0416: .getRowReader();
0417: // in any case we need the PK values of result set row
0418: // provide m_row with primary key data of current row
0419: rowReader.readPkValuesFrom(getRsAndStmt(), getRow());
0420:
0421: if (getItemProxyClass() != null) {
0422: // assert: m_row is filled with primary key values from db
0423: return getProxyFromResultSet();
0424: } else {
0425: // 1.read Identity
0426: Identity oid = getIdentityFromResultSet();
0427: Object result;
0428:
0429: // 2. check if Object is in cache. if so return cached version.
0430: result = getCache().lookup(oid);
0431: if (result == null) {
0432:
0433: // map all field values from the current result set
0434: rowReader.readObjectArrayFrom(getRsAndStmt(), getRow());
0435: // 3. If Object is not in cache
0436: // materialize Object with primitive attributes filled from current row
0437: result = rowReader.readObjectFrom(getRow());
0438: // result may still be null!
0439: if (result != null) {
0440: /*
0441: * synchronize on result so the ODMG-layer can take a
0442: * snapshot only of fully cached (i.e. with all references +
0443: * collections) objects
0444: */
0445: synchronized (result) {
0446: getCache().enableMaterializationCache();
0447: try {
0448: getCache()
0449: .doInternalCache(
0450: oid,
0451: result,
0452: ObjectCacheInternal.TYPE_NEW_MATERIALIZED);
0453: /**
0454: * MBAIRD if you have multiple classes mapped to a
0455: * table, and you query on the base class you could get
0456: * back NON base class objects, so we shouldn't pass
0457: * m_cld, but rather the class descriptor for the
0458: * actual class.
0459: */
0460: // fill reference and collection attributes
0461: ClassDescriptor cld = getBroker()
0462: .getClassDescriptor(
0463: result.getClass());
0464: // don't force loading of reference
0465: final boolean unforced = false;
0466: // Maps ReferenceDescriptors to HashSets of owners
0467: getBroker().getReferenceBroker()
0468: .retrieveReferences(result, cld,
0469: unforced);
0470: getBroker().getReferenceBroker()
0471: .retrieveCollections(result, cld,
0472: unforced);
0473: getCache().disableMaterializationCache();
0474: } catch (RuntimeException e) {
0475: // catch runtime exc. to guarantee clearing of internal buffer on failure
0476: getCache().doLocalClear();
0477: throw e;
0478: }
0479: }
0480: }
0481: } else // Object is in cache
0482: {
0483: ClassDescriptor cld = getBroker().getClassDescriptor(
0484: result.getClass());
0485: // if refresh is required, read all values from the result set and
0486: // update the cache instance from the db
0487: if (cld.isAlwaysRefresh()) {
0488: // map all field values from the current result set
0489: rowReader.readObjectArrayFrom(getRsAndStmt(),
0490: getRow());
0491: rowReader.refreshObject(result, getRow());
0492: }
0493: getBroker().checkRefreshRelationships(result, oid, cld);
0494: }
0495:
0496: return result;
0497: }
0498: }
0499:
0500: /**
0501: * Reads primary key information from current RS row and generates a
0502: *
0503: * corresponding Identity, and returns a proxy from the Identity.
0504: *
0505: * @throws PersistenceBrokerException
0506: * if there was an error creating the proxy class
0507: */
0508: protected Object getProxyFromResultSet()
0509: throws PersistenceBrokerException {
0510: // 1. get Identity of current row:
0511: Identity oid = getIdentityFromResultSet();
0512:
0513: // 2. return a Proxy instance:
0514: return getBroker().createProxy(getItemProxyClass(), oid);
0515: }
0516:
0517: /**
0518: * with a new batch of JDBC 3.0 drivers coming out we can't just check for
0519: * begins with 2, we need to check the actual version and see if it's
0520: * greater than or equal to 2.
0521: */
0522: private boolean supportsAdvancedJDBCCursorControl() {
0523: if (!JDBCSupportAssessed) {
0524: if (getConnectionDescriptor().getJdbcLevel() >= 2.0)
0525: advancedJDBCSupport = true;
0526: JDBCSupportAssessed = true;
0527: }
0528: return advancedJDBCSupport;
0529: }
0530:
0531: /**
0532: * Answer the counted size
0533: *
0534: * @return int
0535: */
0536: protected int countedSize() throws PersistenceBrokerException {
0537: Query countQuery = getBroker().serviceBrokerHelper()
0538: .getCountQuery(getQueryObject().getQuery());
0539: ResultSetAndStatement rsStmt;
0540: ClassDescriptor cld = getQueryObject().getClassDescriptor();
0541: int count = 0;
0542:
0543: // BRJ: do not use broker.getCount() because it's extent-aware
0544: // the count we need here must not include extents !
0545: if (countQuery instanceof QueryBySQL) {
0546: String countSql = ((QueryBySQL) countQuery).getSql();
0547: rsStmt = getBroker().serviceJdbcAccess().executeSQL(
0548: countSql, cld, Query.NOT_SCROLLABLE);
0549: } else {
0550: rsStmt = getBroker().serviceJdbcAccess().executeQuery(
0551: countQuery, cld);
0552: }
0553:
0554: try {
0555: if (rsStmt.m_rs.next()) {
0556: count = rsStmt.m_rs.getInt(1);
0557: }
0558: } catch (SQLException e) {
0559: throw new PersistenceBrokerException(e);
0560: } finally {
0561: rsStmt.close();
0562: }
0563:
0564: return count;
0565: }
0566:
0567: /**
0568: * @return the size of the iterator, aka the number of rows in this
0569: * iterator.
0570: */
0571: public int size() throws PersistenceBrokerException {
0572: int retval = 0; // default size is 0;
0573: boolean forwardOnly = true;
0574: try {
0575: forwardOnly = getRsAndStmt().m_stmt.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY;
0576: } catch (SQLException e) {
0577: //ignore it
0578: }
0579: if (!supportsAdvancedJDBCCursorControl()
0580: || getBroker().serviceConnectionManager()
0581: .getSupportedPlatform()
0582: .useCountForResultsetSize() || forwardOnly) {
0583: /**
0584: * MBAIRD: doesn't support the .last .getRow method, use the
0585: * .getCount on the persistenceBroker which executes a count(*)
0586: * query.
0587: */
0588: if (logger.isDebugEnabled())
0589: logger.debug("Executing count(*) to get size()");
0590: retval = countedSize();
0591: } else {
0592: /**
0593: * Use the .last .getRow method of finding size. The reason for
0594: * supplying an alternative method is effeciency, some driver/db
0595: * combos are a lot more efficient at just moving the cursor and
0596: * returning the row in a real (not -1) number.
0597: */
0598: int whereIAm; // first
0599: try {
0600: if (getRsAndStmt().m_rs != null) {
0601: whereIAm = getRsAndStmt().m_rs.getRow();
0602: if (getRsAndStmt().m_rs.last()) {
0603: retval = getRsAndStmt().m_rs.getRow();
0604: } else {
0605: retval = 0;
0606: }
0607: // go back from whence I came.
0608: if (whereIAm > 0) {
0609: getRsAndStmt().m_rs.absolute(whereIAm);
0610: } else {
0611: getRsAndStmt().m_rs.beforeFirst();
0612: }
0613: }
0614: } catch (SQLException se) {
0615: advancedJDBCSupport = false;
0616: }
0617: }
0618: return retval;
0619: }
0620:
0621: /* (non-Javadoc)
0622: * @see org.apache.ojb.broker.accesslayer.OJBIterator#fullSize()
0623: */
0624: public int fullSize() throws PersistenceBrokerException {
0625: return size();
0626: }
0627:
0628: /**
0629: * Moves the cursor to the given row number in the iterator. If the row
0630: * number is positive, the cursor moves to the given row number with
0631: * respect to the beginning of the iterator. The first row is row 1, the
0632: * second is row 2, and so on.
0633: *
0634: * @param row the row to move to in this iterator, by absolute number
0635: */
0636: public boolean absolute(int row) throws PersistenceBrokerException {
0637: boolean retval;
0638: if (supportsAdvancedJDBCCursorControl()) {
0639: retval = absoluteAdvanced(row);
0640: } else {
0641: retval = absoluteBasic(row);
0642: }
0643: return retval;
0644: }
0645:
0646: /**
0647: * absolute for basicJDBCSupport
0648: * @param row
0649: */
0650: private boolean absoluteBasic(int row) {
0651: boolean retval = false;
0652:
0653: if (row > m_current_row) {
0654: try {
0655: while (m_current_row < row
0656: && getRsAndStmt().m_rs.next()) {
0657: m_current_row++;
0658: }
0659: if (m_current_row == row) {
0660: retval = true;
0661: } else {
0662: setHasCalledCheck(true);
0663: setHasNext(false);
0664: retval = false;
0665: autoReleaseDbResources();
0666: }
0667: } catch (Exception ex) {
0668: setHasCalledCheck(true);
0669: setHasNext(false);
0670: retval = false;
0671: }
0672: } else {
0673: logger
0674: .info("Your driver does not support advanced JDBC Functionality, "
0675: + "you cannot call absolute() with a position < current");
0676: }
0677: return retval;
0678: }
0679:
0680: /**
0681: * absolute for advancedJDBCSupport
0682: * @param row
0683: */
0684: private boolean absoluteAdvanced(int row) {
0685: boolean retval = false;
0686:
0687: try {
0688: if (getRsAndStmt().m_rs != null) {
0689: if (row == 0) {
0690: getRsAndStmt().m_rs.beforeFirst();
0691: } else {
0692: retval = getRsAndStmt().m_rs.absolute(row);
0693: }
0694: m_current_row = row;
0695: setHasCalledCheck(false);
0696: }
0697: } catch (SQLException e) {
0698: advancedJDBCSupport = false;
0699: }
0700: return retval;
0701: }
0702:
0703: /**
0704: * Moves the cursor a relative number of rows, either positive or negative.
0705: * Attempting to move beyond the first/last row in the iterator positions
0706: * the cursor before/after the the first/last row. Calling relative(0) is
0707: * valid, but does not change the cursor position.
0708: *
0709: * @param row
0710: * the row to move to in this iterator, by relative number
0711: */
0712: public boolean relative(int row) throws PersistenceBrokerException {
0713: boolean retval = false;
0714: if (supportsAdvancedJDBCCursorControl()) {
0715: try {
0716: if (getRsAndStmt().m_rs != null) {
0717: retval = getRsAndStmt().m_rs.relative(row);
0718: m_current_row += row;
0719: }
0720: } catch (SQLException e) {
0721: advancedJDBCSupport = false;
0722: }
0723: } else {
0724: if (row >= 0) {
0725: return absolute(m_current_row + row);
0726: } else {
0727: logger
0728: .info("Your driver does not support advanced JDBC Functionality, you cannot call relative() with a negative value");
0729: }
0730: }
0731: return retval;
0732: }
0733:
0734: /**
0735: * Release all internally used Database resources of the iterator. Clients
0736: * must call this methods explicitely if the iterator is not exhausted by
0737: * the client application. If the Iterator is exhauseted this method will
0738: * be called implicitely.
0739: */
0740: public void releaseDbResources() {
0741: release(true);
0742: }
0743:
0744: void release(boolean removeResourceListener) {
0745: if (!isInBatchedMode()) // resources are reused in batched mode
0746: {
0747: // If we haven't released resources yet, then do so.
0748: if (!this .resourcesAreReleased) {
0749: // remove the resource listener
0750: if (removeResourceListener && resourceListener != null) {
0751: try {
0752: /*
0753: when RsIterator is closed, the resource listener
0754: was no longer needed to listen on PB events for clean up.
0755: */
0756: m_broker.removeListener(resourceListener);
0757: this .resourceListener = null;
0758: } catch (Exception e) {
0759: logger
0760: .error(
0761: "Error when try to remove RsIterator resource listener",
0762: e);
0763: }
0764: }
0765: this .resourcesAreReleased = true;
0766: if (m_rsAndStmt != null) {
0767: m_rsAndStmt.close();
0768: m_rsAndStmt = null;
0769: }
0770: }
0771: }
0772: }
0773:
0774: /**
0775: * Internally used by this class to close used resources
0776: * as soon as possible.
0777: */
0778: protected void autoReleaseDbResources() {
0779: if (autoRelease) {
0780: releaseDbResources();
0781: }
0782: }
0783:
0784: /**
0785: * Allows user to switch off/on automatic resource cleanup.
0786: * Set <tt>false</tt> to take responsibility of resource cleanup
0787: * for this class, means after use it's mandatory to call
0788: * {@link #releaseDbResources}.
0789: * <br/> By default it's <tt>true</tt> and resource cleanup is done
0790: * automatic.
0791: */
0792: public void setAutoRelease(boolean autoRelease) {
0793: /*
0794: arminw:
0795: this method should be declared in OJBIterator interface till
0796: OJB 1.1 and PersistenceBroker interface should only return
0797: OJBIterator instead of Iterator instances
0798: */
0799: if (resourcesAreReleased && !autoRelease) {
0800: logger.info(INFO_MSG);
0801: }
0802: this .autoRelease = autoRelease;
0803: }
0804:
0805: /**
0806: * Return the DescriptorRepository
0807: */
0808: protected DescriptorRepository getDescriptorRepository() {
0809: return getBroker().getDescriptorRepository();
0810: }
0811:
0812: protected JdbcConnectionDescriptor getConnectionDescriptor() {
0813: return getBroker().serviceConnectionManager()
0814: .getConnectionDescriptor();
0815: }
0816:
0817: /**
0818: * safety just in case someone leaks.
0819: */
0820: protected void finalize() {
0821: if (m_rsAndStmt != null) {
0822: logger
0823: .info("Found unclosed resources while finalize (causer class: "
0824: + this .getClass().getName()
0825: + ")"
0826: + " Do automatic cleanup");
0827: releaseDbResources();
0828: }
0829: try {
0830: super .finalize();
0831: } catch (Throwable throwable) {
0832: throwable.printStackTrace();
0833: }
0834: }
0835:
0836: public String toString() {
0837: return super .toString();
0838: }
0839:
0840: /**
0841: * @return Returns the cld.
0842: */
0843: public ClassDescriptor getClassDescriptor() {
0844: return getQueryObject().getClassDescriptor();
0845: }
0846:
0847: protected void setBroker(PersistenceBrokerImpl broker) {
0848: m_broker = broker;
0849: }
0850:
0851: protected PersistenceBrokerInternal getBroker() {
0852: return m_broker;
0853: }
0854:
0855: protected void setRsAndStmt(ResultSetAndStatement rsAndStmt) {
0856: if (m_rsAndStmt != null) {
0857: throw new ResourceNotClosedException(
0858: "Unclosed resources found, please release resources"
0859: + " before set new ones");
0860: }
0861: resourcesAreReleased = false;
0862: m_rsAndStmt = rsAndStmt;
0863: }
0864:
0865: protected ResultSetAndStatement getRsAndStmt() {
0866: if (resourcesAreReleased) {
0867: throw new ResourceClosedException(
0868: "Resources no longer reachable, RsIterator will be automatic"
0869: + " cleaned up on PB.close/.commitTransaction/.abortTransaction");
0870: }
0871: return m_rsAndStmt;
0872: }
0873:
0874: protected void setQueryObject(RsQueryObject queryObject) {
0875: this .m_queryObject = queryObject;
0876: }
0877:
0878: protected RsQueryObject getQueryObject() {
0879: return m_queryObject;
0880: }
0881:
0882: protected void setItemProxyClass(Class itemProxyClass) {
0883: this .m_itemProxyClass = itemProxyClass;
0884: }
0885:
0886: protected Class getItemProxyClass() {
0887: return m_itemProxyClass;
0888: }
0889:
0890: protected void setRow(Map row) {
0891: m_row = row;
0892: }
0893:
0894: protected Map getRow() {
0895: return m_row;
0896: }
0897:
0898: protected void setCache(MaterializationCache cache) {
0899: this .m_cache = cache;
0900: }
0901:
0902: protected MaterializationCache getCache() {
0903: return m_cache;
0904: }
0905:
0906: protected void setAfterLookupEvent(PBLifeCycleEvent afterLookupEvent) {
0907: this .afterLookupEvent = afterLookupEvent;
0908: }
0909:
0910: protected PBLifeCycleEvent getAfterLookupEvent() {
0911: return afterLookupEvent;
0912: }
0913:
0914: protected void setHasCalledCheck(boolean hasCalledCheck) {
0915: this .m_hasCalledCheck = hasCalledCheck;
0916: }
0917:
0918: protected boolean isHasCalledCheck() {
0919: return m_hasCalledCheck;
0920: }
0921:
0922: protected void setHasNext(boolean hasNext) {
0923: this .hasNext = hasNext;
0924: }
0925:
0926: protected boolean getHasNext() {
0927: return hasNext;
0928: }
0929:
0930: protected void setInBatchedMode(boolean inBatchedMode) {
0931: this .m_inBatchedMode = inBatchedMode;
0932: }
0933:
0934: protected boolean isInBatchedMode() {
0935: return m_inBatchedMode;
0936: }
0937:
0938: //***********************************************************
0939: // inner classes
0940: //***********************************************************
0941: /**
0942: * Wraps a {@link RsIterator} instance as {@link WeakReference}.
0943: */
0944: public static class ResourceWrapper implements PBStateListener {
0945: /*
0946: arminw:
0947: we do register a PBStateListener to PB instance
0948: to make sure that this instance will be cleaned up at PB.close() call.
0949: If PB was in tx, we cleanup resources on PB.commit/abort, because
0950: commit/abort close the current used connection and all Statement/ResultSet
0951: instances will become invalid.
0952: */
0953: WeakReference ref;
0954:
0955: public ResourceWrapper(RsIterator rs) {
0956: ref = new WeakReference(rs);
0957: }
0958:
0959: public void beforeClose(PBStateEvent event) {
0960: if (ref != null) {
0961: RsIterator rs = (RsIterator) ref.get();
0962: if (rs != null)
0963: rs.release(false);
0964: ref = null;
0965: }
0966: }
0967:
0968: public void beforeRollback(PBStateEvent event) {
0969: if (ref != null) {
0970: RsIterator rs = (RsIterator) ref.get();
0971: if (rs != null)
0972: rs.release(false);
0973: ref = null;
0974: }
0975: }
0976:
0977: public void beforeCommit(PBStateEvent event) {
0978: if (ref != null) {
0979: RsIterator rs = (RsIterator) ref.get();
0980: if (rs != null)
0981: rs.release(false);
0982: ref = null;
0983: }
0984: }
0985:
0986: public void afterCommit(PBStateEvent event) {
0987: //do nothing
0988: }
0989:
0990: public void afterRollback(PBStateEvent event) {
0991: //do nothing
0992: }
0993:
0994: public void afterBegin(PBStateEvent event) {
0995: //do nothing
0996: }
0997:
0998: public void beforeBegin(PBStateEvent event) {
0999: //do nothing
1000: }
1001:
1002: public void afterOpen(PBStateEvent event) {
1003: //do nothing
1004: }
1005: }
1006:
1007: public static class ResourceClosedException extends
1008: OJBRuntimeException {
1009: public ResourceClosedException(String msg) {
1010: super (msg);
1011: }
1012:
1013: public ResourceClosedException(String msg, Throwable cause) {
1014: super (msg, cause);
1015: }
1016: }
1017:
1018: public static class ResourceNotClosedException extends
1019: OJBRuntimeException {
1020: public ResourceNotClosedException(String msg) {
1021: super (msg);
1022: }
1023:
1024: public ResourceNotClosedException(String msg, Throwable cause) {
1025: super (msg, cause);
1026: }
1027: }
1028:
1029: /**
1030: * @see org.apache.ojb.broker.accesslayer.OJBIterator#disableLifeCycleEvents()
1031: */
1032: public void disableLifeCycleEvents() {
1033: disableLifeCycleEvents = true;
1034: }
1035: }
|