0001: package org.apache.ojb.odmg;
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 javax.transaction.Status;
0019: import java.util.ArrayList;
0020: import java.util.Collection;
0021: import java.util.Enumeration;
0022: import java.util.HashMap;
0023: import java.util.Iterator;
0024: import java.util.List;
0025:
0026: import org.apache.commons.lang.SystemUtils;
0027: import org.apache.ojb.broker.Identity;
0028: import org.apache.ojb.broker.OJBRuntimeException;
0029: import org.apache.ojb.broker.PBFactoryException;
0030: import org.apache.ojb.broker.PersistenceBroker;
0031: import org.apache.ojb.broker.PersistenceBrokerException;
0032: import org.apache.ojb.broker.PersistenceBrokerInternal;
0033: import org.apache.ojb.broker.core.PersistenceBrokerFactoryFactory;
0034: import org.apache.ojb.broker.core.proxy.CollectionProxy;
0035: import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
0036: import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
0037: import org.apache.ojb.broker.core.proxy.IndirectionHandler;
0038: import org.apache.ojb.broker.core.proxy.MaterializationListener;
0039: import org.apache.ojb.broker.core.proxy.ProxyHelper;
0040: import org.apache.ojb.broker.metadata.ClassDescriptor;
0041: import org.apache.ojb.broker.metadata.CollectionDescriptor;
0042: import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
0043: import org.apache.ojb.broker.util.BrokerHelper;
0044: import org.apache.ojb.broker.util.GUID;
0045: import org.apache.ojb.broker.util.configuration.Configurable;
0046: import org.apache.ojb.broker.util.configuration.Configuration;
0047: import org.apache.ojb.broker.util.configuration.ConfigurationException;
0048: import org.apache.ojb.broker.util.logging.Logger;
0049: import org.apache.ojb.broker.util.logging.LoggerFactory;
0050: import org.apache.ojb.odmg.locking.LockManager;
0051: import org.odmg.DatabaseClosedException;
0052: import org.odmg.LockNotGrantedException;
0053: import org.odmg.ODMGRuntimeException;
0054: import org.odmg.Transaction;
0055: import org.odmg.TransactionAbortedException;
0056: import org.odmg.TransactionNotInProgressException;
0057:
0058: /**
0059: *
0060: * Implementation of Transaction for org.odmg.Transaction.
0061: *
0062: * @author Thomas Mahler & David Dixon-Peugh
0063: * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
0064: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
0065: * @author <a href="mailto:brianm@apache.org">Brian McCallister</a>
0066: * @version $Id: TransactionImpl.java,v 1.59.2.25 2005/12/21 22:29:21 tomdz Exp $
0067: *
0068: */
0069: public class TransactionImpl implements Transaction,
0070: MaterializationListener, Configurable, CollectionProxyListener,
0071: TransactionExt {
0072: private Logger log = LoggerFactory.getLogger(TransactionImpl.class);
0073: private boolean impliciteWriteLocks;
0074: private boolean implicitLocking;
0075: private boolean ordering;
0076:
0077: private String txGUID;
0078: protected PersistenceBrokerInternal broker = null;
0079: private ArrayList registrationList = new ArrayList();
0080: private ImplementationImpl implementation;
0081: private NamedRootsMap namedRootsMap;
0082:
0083: /**
0084: * The status of the current transaction, as specified by the
0085: * javax.transaction package.
0086: * See {@link javax.transaction.Status} for list of valid values.
0087: */
0088: private int txStatus = Status.STATUS_NO_TRANSACTION;
0089:
0090: /**
0091: * the internal table containing all Objects "touched" by this tx and their
0092: * respective transactional state
0093: */
0094: protected ObjectEnvelopeTable objectEnvelopeTable = null;
0095:
0096: /**
0097: * reference to the currently opened database
0098: */
0099: private DatabaseImpl curDB;
0100: /**
0101: * The tx may me listening to a number of IndirectionHandlers.
0102: * on abort or commit these Handlers must be informed to remove
0103: * tx from their List of Listeners.
0104: */
0105: private ArrayList registeredIndirectionHandlers = new ArrayList();
0106:
0107: /**
0108: * Unloaded collection proxies which will be registered with tx when
0109: * collection is loaded
0110: */
0111: private ArrayList registeredCollectionProxies = new ArrayList();
0112:
0113: /**
0114: * list of proxy objects that were locked, but haven't been materialized yet.
0115: * This is necessary so the locks can be released on closing the transaction
0116: */
0117: private ArrayList unmaterializedLocks = new ArrayList();
0118:
0119: /**
0120: * Creates new Transaction
0121: * @param implementation The odmg Implementation class
0122: */
0123: public TransactionImpl(ImplementationImpl implementation) {
0124: this .implementation = implementation;
0125: this .impliciteWriteLocks = implementation
0126: .isImpliciteWriteLocks();
0127: this .implicitLocking = implementation.isImplicitLocking();
0128: this .ordering = implementation.isOrdering();
0129: //this.noteUserOrdering = implementation.isNoteUserOrder();
0130:
0131: // assign a globally uniqe id to this tx
0132: txGUID = new GUID().toString();
0133: curDB = implementation.getCurrentDatabase();
0134: namedRootsMap = new NamedRootsMap(this );
0135: }
0136:
0137: public ImplementationImpl getImplementation() {
0138: return implementation;
0139: }
0140:
0141: public NamedRootsMap getNamedRootsMap() {
0142: return namedRootsMap;
0143: }
0144:
0145: /**
0146: * Returns the associated database
0147: */
0148: public DatabaseImpl getAssociatedDatabase() {
0149: return this .curDB;
0150: }
0151:
0152: protected int getStatus() {
0153: return txStatus;
0154: }
0155:
0156: protected void setStatus(int status) {
0157: this .txStatus = status;
0158: }
0159:
0160: private void checkForDB() {
0161: if (curDB == null || !curDB.isOpen()) {
0162: log
0163: .error("Transaction without a associated open Database.");
0164: throw new TransactionAbortedExceptionOJB(
0165: "No open database found. Open the database before handling transactions");
0166: }
0167: }
0168:
0169: /**
0170: * Determine whether the transaction is open or not. A transaction is open if
0171: * a call has been made to <code>begin</code> , but a subsequent call to
0172: * either <code>commit</code> or <code>abort</code> has not been made.
0173: * @return True if the transaction is open, otherwise false.
0174: */
0175: public boolean isOpen() {
0176: return (getStatus() == Status.STATUS_ACTIVE
0177: || getStatus() == Status.STATUS_MARKED_ROLLBACK
0178: || getStatus() == Status.STATUS_PREPARED
0179: || getStatus() == Status.STATUS_PREPARING || getStatus() == Status.STATUS_COMMITTING);
0180: }
0181:
0182: private void checkOpen() {
0183: if (!isOpen()) {
0184: throw new TransactionNotInProgressException(
0185: "Transaction was not open, call tx.begin() before perform action, current status is: "
0186: + TxUtil.getStatusString(getStatus()));
0187: }
0188: }
0189:
0190: /**
0191: * Attach the caller's thread to this <code>Transaction</code> and detach the
0192: * thread from any former <code>Transaction</code> the thread may have been
0193: * associated with.
0194: */
0195: public void join() {
0196: checkOpen();
0197: implementation.getTxManager().deregisterTx(this );
0198: implementation.getTxManager().registerTx(this );
0199: }
0200:
0201: /**
0202: * Upgrade the lock on the given object to the given lock mode. The call has
0203: * no effect if the object's current lock is already at or above that level of
0204: * lock mode.
0205: *
0206: * @param obj object to acquire a lock on.
0207: * @param lockMode lock mode to acquire. The lock modes
0208: * are <code>READ</code> , <code>UPGRADE</code> , and <code>WRITE</code> .
0209: *
0210: * @exception LockNotGrantedException Description of Exception
0211: */
0212: public void lock(Object obj, int lockMode)
0213: throws LockNotGrantedException {
0214: if (log.isDebugEnabled())
0215: log.debug("lock object was called on tx " + this
0216: + ", object is " + obj.toString());
0217: checkOpen();
0218: RuntimeObject rtObject = new RuntimeObject(obj, this );
0219: lockAndRegister(rtObject, lockMode, isImplicitLocking(),
0220: getRegistrationList());
0221: // if(isImplicitLocking()) moveToLastInOrderList(rtObject.getIdentity());
0222: }
0223:
0224: /**
0225: * Returns an empty List for registration of processed object Identity.
0226: */
0227: public ArrayList getRegistrationList() {
0228: clearRegistrationList();
0229: return registrationList;
0230: }
0231:
0232: /**
0233: * Clears the list of processed object Identity.
0234: */
0235: public void clearRegistrationList() {
0236: registrationList.clear();
0237: }
0238:
0239: /**
0240: * Lock and register the specified object, make sure that when cascading locking and register
0241: * is enabled to specify a List to register the already processed object Identiy.
0242: */
0243: public void lockAndRegister(RuntimeObject rtObject, int lockMode,
0244: List registeredObjects) {
0245: lockAndRegister(rtObject, lockMode, isImplicitLocking(),
0246: registeredObjects);
0247: }
0248:
0249: /**
0250: * Lock and register the specified object, make sure that when cascading locking and register
0251: * is enabled to specify a List to register the already processed object Identiy.
0252: */
0253: public synchronized void lockAndRegister(RuntimeObject rtObject,
0254: int lockMode, boolean cascade, List registeredObjects) {
0255: if (log.isDebugEnabled())
0256: log.debug("Lock and register called for "
0257: + rtObject.getIdentity());
0258: // if current object was already locked, do nothing
0259: // avoid endless loops when circular object references are used
0260: if (!registeredObjects.contains(rtObject.getIdentity())) {
0261: if (cascade) {
0262: // if implicite locking is enabled, first add the current object to
0263: // list of registered objects to avoid endless loops on circular objects
0264: registeredObjects.add(rtObject.getIdentity());
0265: // lock and register 1:1 references first
0266: //
0267: // If implicit locking is used, we have materialize the main object
0268: // to lock the referenced objects too
0269: lockAndRegisterReferences(rtObject.getCld(), rtObject
0270: .getObjMaterialized(), lockMode,
0271: registeredObjects);
0272: }
0273: try {
0274: // perform the lock on the object
0275: // we don't need to lock new objects
0276: if (!rtObject.isNew()) {
0277: doSingleLock(rtObject.getCld(), rtObject.getObj(),
0278: rtObject.getIdentity(), lockMode);
0279: }
0280: // after we locked the object, register it to detect status and changes while tx
0281: doSingleRegister(rtObject, lockMode);
0282: } catch (Throwable t) {
0283: //log.error("Locking of obj " + rtObject.getIdentity() + " failed", t);
0284: // if registering of object fails release lock on object, because later we don't
0285: // get a change to do this.
0286: implementation.getLockManager().releaseLock(this ,
0287: rtObject.getIdentity(), rtObject.getObj());
0288: if (t instanceof LockNotGrantedException) {
0289: throw (LockNotGrantedException) t;
0290: } else {
0291: log.error("Unexpected failure while locking", t);
0292: throw new LockNotGrantedException(
0293: "Locking failed for "
0294: + rtObject.getIdentity()
0295: + ", nested exception is: ["
0296: + t.getClass().getName() + ": "
0297: + t.getMessage() + "]");
0298: }
0299: }
0300: if (cascade) {
0301: // perform locks and register 1:n and m:n references
0302: // If implicit locking is used, we have materialize the main object
0303: // to lock the referenced objects too
0304: lockAndRegisterCollections(rtObject.getCld(), rtObject
0305: .getObjMaterialized(), lockMode,
0306: registeredObjects);
0307: }
0308: }
0309: }
0310:
0311: /**
0312: * Only lock the specified object, represented by
0313: * the {@link RuntimeObject} instance.
0314: *
0315: * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor}
0316: * of the object to acquire a lock on.
0317: * @param oid The {@link org.apache.ojb.broker.Identity} of the object to lock.
0318: * @param lockMode lock mode to acquire. The lock modes
0319: * are <code>READ</code> , <code>UPGRADE</code> , and <code>WRITE</code>.
0320: *
0321: * @exception LockNotGrantedException Description of Exception
0322: */
0323: void doSingleLock(ClassDescriptor cld, Object obj, Identity oid,
0324: int lockMode) throws LockNotGrantedException {
0325: LockManager lm = implementation.getLockManager();
0326: if (cld.isAcceptLocks()) {
0327: if (lockMode == Transaction.READ) {
0328: if (log.isDebugEnabled())
0329: log.debug("Do READ lock on object: " + oid);
0330: if (!lm.readLock(this , oid, obj)) {
0331: throw new LockNotGrantedException(
0332: "Can not lock for READ: " + oid);
0333: }
0334: } else if (lockMode == Transaction.WRITE) {
0335: if (log.isDebugEnabled())
0336: log.debug("Do WRITE lock on object: " + oid);
0337: if (!lm.writeLock(this , oid, obj)) {
0338: throw new LockNotGrantedException(
0339: "Can not lock for WRITE: " + oid);
0340: }
0341: } else if (lockMode == Transaction.UPGRADE) {
0342: if (log.isDebugEnabled())
0343: log.debug("Do UPGRADE lock on object: " + oid);
0344: if (!lm.upgradeLock(this , oid, obj)) {
0345: throw new LockNotGrantedException(
0346: "Can not lock for UPGRADE: " + oid);
0347: }
0348: }
0349: } else {
0350: if (log.isDebugEnabled()) {
0351: log
0352: .debug("Class '"
0353: + cld.getClassNameOfObject()
0354: + "' doesn't accept locks"
0355: + " (accept-locks=false) when implicite locked, so OJB skip this object: "
0356: + oid);
0357: }
0358: }
0359: }
0360:
0361: /**
0362: * Detach the caller's thread from this <code>Transaction</code> , but do not
0363: * attach the thread to another <code>Transaction</code> .
0364: */
0365: public void leave() {
0366: checkOpen();
0367: implementation.getTxManager().deregisterTx(this );
0368: }
0369:
0370: /**
0371: * Write objects to data store, but don't release the locks.
0372: * I don't know what we should do if we are in a checkpoint and
0373: * we need to abort.
0374: */
0375: protected synchronized void doWriteObjects(boolean isFlush)
0376: throws TransactionAbortedException, LockNotGrantedException {
0377: /*
0378: arminw:
0379: if broker isn't in PB-tx, start tx
0380: */
0381: if (!getBroker().isInTransaction()) {
0382: if (log.isDebugEnabled())
0383: log.debug("call beginTransaction() on PB instance");
0384: broker.beginTransaction();
0385: }
0386:
0387: // Notify objects of impending commits.
0388: performTransactionAwareBeforeCommit();
0389:
0390: // Now perfom the real work
0391: objectEnvelopeTable.writeObjects(isFlush);
0392: // now we have to perform the named objects
0393: namedRootsMap.performDeletion();
0394: namedRootsMap.performInsert();
0395: namedRootsMap.afterWriteCleanup();
0396: }
0397:
0398: /**
0399: * Do the Aborts, but don't release the locks.
0400: * Do the aborts on the NamedRootsMap first, then
0401: * abort the other stuff.
0402: */
0403: protected synchronized void doAbort() {
0404: // Notify objects of impending aborts.
0405: performTransactionAwareBeforeRollback();
0406:
0407: // Now, we abort everything. . .
0408: objectEnvelopeTable.rollback();
0409:
0410: // Now, we notify everything the abort is done.
0411: performTransactionAwareAfterRollback();
0412: }
0413:
0414: /**
0415: * Close a transaction and do all the cleanup associated with it.
0416: */
0417: protected synchronized void doClose() {
0418: try {
0419: LockManager lm = getImplementation().getLockManager();
0420: Enumeration en = objectEnvelopeTable.elements();
0421: while (en.hasMoreElements()) {
0422: ObjectEnvelope oe = (ObjectEnvelope) en.nextElement();
0423: lm.releaseLock(this , oe.getIdentity(), oe.getObject());
0424: }
0425:
0426: //remove locks for objects which haven't been materialized yet
0427: for (Iterator it = unmaterializedLocks.iterator(); it
0428: .hasNext();) {
0429: lm.releaseLock(this , it.next());
0430: }
0431:
0432: // this tx is no longer interested in materialization callbacks
0433: unRegisterFromAllIndirectionHandlers();
0434: unRegisterFromAllCollectionProxies();
0435: } finally {
0436: /**
0437: * MBAIRD: Be nice and close the table to release all refs
0438: */
0439: if (log.isDebugEnabled())
0440: log.debug("Close Transaction and release current PB "
0441: + broker + " on tx " + this );
0442: // remove current thread from LocalTxManager
0443: // to avoid problems for succeeding calls of the same thread
0444: implementation.getTxManager().deregisterTx(this );
0445: // now cleanup and prepare for reuse
0446: refresh();
0447: }
0448: }
0449:
0450: /**
0451: * cleanup tx and prepare for reuse
0452: */
0453: protected void refresh() {
0454: if (log.isDebugEnabled())
0455: log.debug("Refresh this transaction for reuse: " + this );
0456: try {
0457: // we reuse ObjectEnvelopeTable instance
0458: objectEnvelopeTable.refresh();
0459: } catch (Exception e) {
0460: if (log.isDebugEnabled()) {
0461: log.debug("error closing object envelope table : "
0462: + e.getMessage());
0463: e.printStackTrace();
0464: }
0465: }
0466: cleanupBroker();
0467: // clear the temporary used named roots map
0468: // we should do that, because same tx instance
0469: // could be used several times
0470: broker = null;
0471: clearRegistrationList();
0472: unmaterializedLocks.clear();
0473: txStatus = Status.STATUS_NO_TRANSACTION;
0474: }
0475:
0476: /**
0477: * Commit the transaction, but reopen the transaction, retaining all locks.
0478: * Calling <code>checkpoint</code> commits persistent object modifications
0479: * made within the transaction since the last checkpoint to the database. The
0480: * transaction retains all locks it held on those objects at the time the
0481: * checkpoint was invoked.
0482: */
0483: public void checkpoint() {
0484: if (log.isDebugEnabled())
0485: log
0486: .debug("Checkpoint was called, commit changes hold locks on tx "
0487: + this );
0488: try {
0489: checkOpen();
0490: doWriteObjects(true);
0491: // do commit on PB
0492: if (hasBroker() && broker.isInTransaction())
0493: broker.commitTransaction();
0494: } catch (Throwable t) {
0495: log
0496: .error(
0497: "Checkpoint call failed, do abort transaction",
0498: t);
0499: txStatus = Status.STATUS_MARKED_ROLLBACK;
0500: abort();
0501: if (!(t instanceof ODMGRuntimeException)) {
0502: throw new TransactionAbortedExceptionOJB(
0503: "Can't tx.checkpoint() objects: "
0504: + t.getMessage(), t);
0505: } else {
0506: throw (ODMGRuntimeException) t;
0507: }
0508: }
0509: }
0510:
0511: /**
0512: * @see org.apache.ojb.odmg.TransactionExt#flush
0513: */
0514: public void flush() {
0515: if (log.isDebugEnabled()) {
0516: log
0517: .debug("Flush was called - write changes to database, do not commit, hold locks on tx "
0518: + this );
0519: }
0520:
0521: try {
0522: checkOpen();
0523: doWriteObjects(true);
0524: } catch (Throwable t) {
0525: log.error("Calling method 'tx.flush()' failed", t);
0526: txStatus = Status.STATUS_MARKED_ROLLBACK;
0527: abort();
0528: if (!(t instanceof ODMGRuntimeException)) {
0529: throw new TransactionAbortedExceptionOJB(
0530: "Can't tx.flush() objects: " + t.getMessage(),
0531: t);
0532: } else {
0533: throw (ODMGRuntimeException) t;
0534: }
0535: }
0536: }
0537:
0538: /**
0539: * @see org.apache.ojb.odmg.TransactionExt#markDelete
0540: */
0541: public void markDelete(Object anObject) {
0542: ObjectEnvelope otw = objectEnvelopeTable.get(anObject, false);
0543: // not needed on delete - or?
0544: //otw.refreshObjectIfNeeded(anObject);
0545: otw.setModificationState(otw.getModificationState()
0546: .markDelete());
0547: }
0548:
0549: public void deletePersistent(RuntimeObject rt) {
0550: // if(rt.isNew())
0551: // {
0552: // throw new ObjectNotPersistentException("Object " + rt.getIdentity() + " is not yet persistent");
0553: // }
0554: if (rt.isProxy()) {
0555: Object realObj = rt.getHandler().getRealSubject();
0556: rt = new RuntimeObject(realObj, rt.getIdentity(), this ,
0557: false);
0558: }
0559: lockAndRegister(rt, Transaction.WRITE, getRegistrationList());
0560: ObjectEnvelope oe = objectEnvelopeTable.getByIdentity(rt
0561: .getIdentity());
0562: // TODO: not needed on delete - or? When optimistic locking is used we should always use the
0563: // specified object instance to use the last version of the object
0564: oe.refreshObjectIfNeeded(rt.getObj());
0565: oe.setModificationState(oe.getModificationState().markDelete());
0566: }
0567:
0568: /**
0569: * @see org.apache.ojb.odmg.TransactionExt#markDirty
0570: */
0571: public void markDirty(Object anObject) {
0572: ObjectEnvelope otw = objectEnvelopeTable.get(anObject, false);
0573: otw.refreshObjectIfNeeded(anObject);
0574: otw
0575: .setModificationState(otw.getModificationState()
0576: .markDirty());
0577: }
0578:
0579: void markDirty(RuntimeObject rt) {
0580: ObjectEnvelope otw = objectEnvelopeTable.get(rt.getIdentity(),
0581: rt.getObj(), rt.isNew());
0582: otw.refreshObjectIfNeeded(rt.getObj());
0583: otw
0584: .setModificationState(otw.getModificationState()
0585: .markDirty());
0586: }
0587:
0588: void markPersistent(RuntimeObject rtObj) {
0589: ObjectEnvelope oe = objectEnvelopeTable.getByIdentity(rtObj
0590: .getIdentity());
0591: if (oe == null) {
0592: oe = objectEnvelopeTable.get(rtObj.getIdentity(), rtObj
0593: .getObj(), rtObj.isNew());
0594: }
0595: if (oe.needsDelete()) {
0596: oe
0597: .setModificationState(oe.getModificationState()
0598: .markNew());
0599: } else {
0600: oe.setModificationState(oe.getModificationState()
0601: .markDirty());
0602: }
0603: oe.refreshObjectIfNeeded(rtObj.getObj());
0604: }
0605:
0606: void makePersistent(RuntimeObject rt) {
0607: try {
0608: lockAndRegister(rt, Transaction.WRITE,
0609: getRegistrationList());
0610: markPersistent(rt);
0611: } catch (org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException ex) {
0612: log.error("Can't persist object: " + rt.getIdentity(), ex);
0613: throw new org.odmg.ClassNotPersistenceCapableException(ex
0614: .getMessage());
0615: }
0616: }
0617:
0618: /**
0619: * @see org.apache.ojb.odmg.TransactionExt#isDeleted(org.apache.ojb.broker.Identity)
0620: */
0621: public boolean isDeleted(Identity id) {
0622: ObjectEnvelope envelope = objectEnvelopeTable.getByIdentity(id);
0623: return (envelope != null && envelope.needsDelete());
0624: }
0625:
0626: /**
0627: * Upgrade the lock on the given object to the given lock mode. Method <code>
0628: * tryLock</code> is the same as <code>lock</code> except it returns a boolean
0629: * indicating whether the lock was granted instead of generating an exception.
0630: * @param obj Description of Parameter
0631: * @param lockMode Description of Parameter
0632: * @return Description of the Returned Value
0633: * </code>, <code>UPGRADE</code> , and <code>WRITE</code> .
0634: * @return true if the lock has been acquired, otherwise false.
0635: */
0636: public boolean tryLock(Object obj, int lockMode) {
0637: if (log.isDebugEnabled())
0638: log.debug("Try to lock object was called on tx " + this );
0639: checkOpen();
0640: try {
0641: lock(obj, lockMode);
0642: return true;
0643: } catch (LockNotGrantedException ex) {
0644: return false;
0645: }
0646: }
0647:
0648: /**
0649: * Commit and close the transaction. Calling <code>commit</code> commits to
0650: * the database all persistent object modifications within the transaction and
0651: * releases any locks held by the transaction. A persistent object
0652: * modification is an update of any field of an existing persistent object, or
0653: * an update or creation of a new named object in the database. If a
0654: * persistent object modification results in a reference from an existing
0655: * persistent object to a transient object, the transient object is moved to
0656: * the database, and all references to it updated accordingly. Note that the
0657: * act of moving a transient object to the database may create still more
0658: * persistent references to transient objects, so its referents must be
0659: * examined and moved as well. This process continues until the database
0660: * contains no references to transient objects, a condition that is guaranteed
0661: * as part of transaction commit. Committing a transaction does not remove
0662: * from memory transient objects created during the transaction.
0663: *
0664: * The updateObjectList contains a list of all objects for which this transaction
0665: * has write privledge to. We need to update these objects.
0666: */
0667: public void commit() {
0668: checkOpen();
0669: try {
0670: prepareCommit();
0671: checkForCommit();
0672:
0673: txStatus = Status.STATUS_COMMITTING;
0674: if (log.isDebugEnabled())
0675: log.debug("Commit transaction " + this );
0676: // now do real commit on broker
0677: if (hasBroker())
0678: getBroker().commitTransaction();
0679:
0680: // Now, we notify everything the commit is done.
0681: performTransactionAwareAfterCommit();
0682:
0683: doClose();
0684: txStatus = Status.STATUS_COMMITTED;
0685: } catch (Exception ex) {
0686: log.error("Error while commit objects, do abort tx " + this
0687: + ", " + ex.getMessage(), ex);
0688: txStatus = Status.STATUS_MARKED_ROLLBACK;
0689: abort();
0690: if (!(ex instanceof ODMGRuntimeException)) {
0691: throw new TransactionAbortedExceptionOJB(
0692: "Can't commit objects: " + ex.getMessage(), ex);
0693: } else {
0694: throw (ODMGRuntimeException) ex;
0695: }
0696: }
0697: }
0698:
0699: protected void checkForCommit() {
0700: // Never commit transaction that has been marked for rollback
0701: if (txStatus == Status.STATUS_MARKED_ROLLBACK)
0702: throw new TransactionAbortedExceptionOJB(
0703: "Illegal tx-status: tx is already markedRollback");
0704: // Don't commit if not prepared
0705: if (txStatus != Status.STATUS_PREPARED)
0706: throw new IllegalStateException(
0707: "Illegal tx-status: Do prepare commit before commit");
0708: }
0709:
0710: /**
0711: * Prepare does the actual work of moving the changes at the object level
0712: * into storage (the underlying rdbms for instance). prepare Can be called multiple times, and
0713: * does not release locks.
0714: *
0715: * @throws TransactionAbortedException if the transaction has been aborted
0716: * for any reason.
0717: * @throws IllegalStateException Method called if transaction is
0718: * not in the proper state to perform this operation
0719: * @throws TransactionNotInProgressException if the transaction is closed.
0720: */
0721: protected void prepareCommit() throws TransactionAbortedException,
0722: LockNotGrantedException {
0723: if (txStatus == Status.STATUS_MARKED_ROLLBACK) {
0724: throw new TransactionAbortedExceptionOJB(
0725: "Prepare Transaction: tx already marked for rollback");
0726: }
0727: if (txStatus != Status.STATUS_ACTIVE) {
0728: throw new IllegalStateException(
0729: "Prepare Transaction: tx status is not 'active', status is "
0730: + TxUtil.getStatusString(txStatus));
0731: }
0732: try {
0733: txStatus = Status.STATUS_PREPARING;
0734: doWriteObjects(false);
0735: txStatus = Status.STATUS_PREPARED;
0736: } catch (RuntimeException e) {
0737: log.error("Could not prepare for commit", e);
0738: txStatus = Status.STATUS_MARKED_ROLLBACK;
0739: throw e;
0740: }
0741: }
0742:
0743: /**
0744: * Abort and close the transaction. Calling abort abandons all persistent
0745: * object modifications and releases the associated locks. Aborting a
0746: * transaction does not restore the state of modified transient objects
0747: */
0748: public void abort() {
0749: /*
0750: do nothing if already rolledback
0751: */
0752: if (txStatus == Status.STATUS_NO_TRANSACTION
0753: || txStatus == Status.STATUS_UNKNOWN
0754: || txStatus == Status.STATUS_ROLLEDBACK) {
0755: log.info("Nothing to abort, tx is not active - status is "
0756: + TxUtil.getStatusString(txStatus));
0757: return;
0758: }
0759: // check status of tx
0760: if (txStatus != Status.STATUS_ACTIVE
0761: && txStatus != Status.STATUS_PREPARED
0762: && txStatus != Status.STATUS_MARKED_ROLLBACK) {
0763: throw new IllegalStateException(
0764: "Illegal state for abort call, state was '"
0765: + TxUtil.getStatusString(txStatus) + "'");
0766: }
0767: if (log.isEnabledFor(Logger.INFO)) {
0768: log.info("Abort transaction was called on tx " + this );
0769: }
0770: try {
0771: try {
0772: doAbort();
0773: } catch (Exception e) {
0774: log
0775: .error(
0776: "Error while abort transaction, will be skipped",
0777: e);
0778: }
0779:
0780: // used in managed environments, ignored in non-managed
0781: this .implementation.getTxManager().abortExternalTx(this );
0782:
0783: try {
0784: if (hasBroker() && getBroker().isInTransaction()) {
0785: getBroker().abortTransaction();
0786: }
0787: } catch (Exception e) {
0788: log
0789: .error(
0790: "Error while do abort used broker instance, will be skipped",
0791: e);
0792: }
0793: } finally {
0794: txStatus = Status.STATUS_ROLLEDBACK;
0795: // cleanup things, e.g. release all locks
0796: doClose();
0797: }
0798: }
0799:
0800: /**
0801: * Start a transaction. Calling <code>begin</code> multiple times on the same
0802: * transaction object, without an intervening call to <code>commit</code> or
0803: * <code>abort</code> , causes the exception <code>
0804: * TransactionInProgressException</code> to be thrown on the second and
0805: * subsequent calls. Operations executed before a transaction has been opened,
0806: * or before reopening after a transaction is aborted or committed, have
0807: * undefined results; these may throw a <code>
0808: * TransactionNotInProgressException</code> exception.
0809: */
0810: public synchronized void begin() {
0811: checkForBegin();
0812: if (log.isDebugEnabled())
0813: log.debug("Begin transaction was called on tx " + this );
0814: // initialize the ObjectEnvelope table
0815: objectEnvelopeTable = new ObjectEnvelopeTable(this );
0816: // register transaction
0817: implementation.getTxManager().registerTx(this );
0818: // mark tx as active (open)
0819: txStatus = Status.STATUS_ACTIVE;
0820: }
0821:
0822: protected void checkForBegin() {
0823: /**
0824: * Is the associated database non-null and open? ODMG 3.0 says it must be.
0825: */
0826: if ((curDB == null) || !curDB.isOpen()) {
0827: throw new DatabaseClosedException(
0828: "Database is not open. Must have an open DB to begin the Tx.");
0829: }
0830: if (isOpen()) {
0831: log.error("Transaction is already open");
0832: throw new org.odmg.TransactionInProgressException(
0833: "Impossible to call begin on already opened tx");
0834: }
0835: }
0836:
0837: public String getGUID() {
0838: return txGUID;
0839: }
0840:
0841: /**
0842: * Get object by identity. First lookup among objects registered in the
0843: * transaction, then in persistent storage.
0844: * @param id The identity
0845: * @return The object
0846: * @throws PersistenceBrokerException
0847: */
0848: public Object getObjectByIdentity(Identity id)
0849: throws PersistenceBrokerException {
0850: checkOpen();
0851: ObjectEnvelope envelope = objectEnvelopeTable.getByIdentity(id);
0852: if (envelope != null) {
0853: return (envelope.needsDelete() ? null : envelope
0854: .getObject());
0855: } else {
0856: return getBroker().getObjectByIdentity(id);
0857: }
0858: }
0859:
0860: /**
0861: * Registers the object (without locking) with this transaction. This method
0862: * expects that the object was already locked, no check is done!!!
0863: */
0864: void doSingleRegister(RuntimeObject rtObject, int lockMode)
0865: throws LockNotGrantedException, PersistenceBrokerException {
0866: if (log.isDebugEnabled())
0867: log.debug("Register object " + rtObject.getIdentity());
0868: Object objectToRegister = rtObject.getObj();
0869: /*
0870: if the object is a Proxy there are two options:
0871: 1. The proxies real subject has already been materialized:
0872: we take this real subject as the object to register and proceed
0873: as if it were a ordinary object.
0874: 2. The real subject has not been materialized: Then there is nothing
0875: to be registered now!
0876: Of course we might just materialize the real subject to have something
0877: to register. But this would make proxies useless for ODMG as proxies would
0878: get materialized even if their real subjects were not used by the
0879: client app.
0880: Thus we register the current transaction as a Listener to the IndirectionHandler
0881: of the Proxy.
0882: Only when the IndirectionHandler performs the materialization of the real subject
0883: at some later point in time it invokes callbacks on all it's listeners.
0884: Using this callback we can defer the registering until it's really needed.
0885: */
0886: if (rtObject.isProxy()) {
0887: IndirectionHandler handler = rtObject.getHandler();
0888: if (handler == null) {
0889: throw new OJBRuntimeException(
0890: "Unexpected error, expect an proxy object as indicated: "
0891: + rtObject);
0892: }
0893: if (handler.alreadyMaterialized()) {
0894: objectToRegister = handler.getRealSubject();
0895: } else {
0896: registerToIndirectionHandler(handler);
0897: registerUnmaterializedLocks(rtObject.getObj());
0898: // all work is done, so set to null
0899: objectToRegister = null;
0900: }
0901: }
0902: // no Proxy and is not null, register real object
0903: if (objectToRegister != null) {
0904: ObjectEnvelope envelope = objectEnvelopeTable
0905: .getByIdentity(rtObject.getIdentity());
0906: // if we found an envelope, object is already registered --> we do nothing
0907: // than refreshing the object!
0908: if ((envelope == null)) {
0909: // register object itself
0910: envelope = objectEnvelopeTable.get(rtObject
0911: .getIdentity(), objectToRegister, rtObject
0912: .isNew());
0913: } else {
0914: /*
0915: arminw:
0916: if an different instance of the same object was locked again
0917: we should replace the old instance with new one to make
0918: accessible the changed fields
0919: */
0920: envelope.refreshObjectIfNeeded(objectToRegister);
0921: }
0922: /*
0923: arminw:
0924: For better performance we check if this object has already a write lock
0925: in this case we don't need to acquire a write lock on commit
0926: */
0927: if (lockMode == Transaction.WRITE) {
0928: // signal ObjectEnvelope that a WRITE lock is already acquired
0929: envelope.setWriteLocked(true);
0930: }
0931: }
0932: }
0933:
0934: /**
0935: * we only use the registrationList map if the object is not a proxy. During the
0936: * reference locking, we will materialize objects and they will enter the registered for
0937: * lock map.
0938: */
0939: private void lockAndRegisterReferences(ClassDescriptor cld,
0940: Object sourceObject, int lockMode, List registeredObjects)
0941: throws LockNotGrantedException {
0942: if (implicitLocking) {
0943: Iterator i = cld.getObjectReferenceDescriptors(true)
0944: .iterator();
0945: while (i.hasNext()) {
0946: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i
0947: .next();
0948: Object refObj = rds.getPersistentField().get(
0949: sourceObject);
0950: if (refObj != null) {
0951: boolean isProxy = ProxyHelper.isProxy(refObj);
0952: RuntimeObject rt = isProxy ? new RuntimeObject(
0953: refObj, this , false) : new RuntimeObject(
0954: refObj, this );
0955: if (!registrationList.contains(rt.getIdentity())) {
0956: lockAndRegister(rt, lockMode, registeredObjects);
0957: }
0958: }
0959: }
0960: }
0961: }
0962:
0963: private void lockAndRegisterCollections(ClassDescriptor cld,
0964: Object sourceObject, int lockMode, List registeredObjects)
0965: throws LockNotGrantedException {
0966: if (implicitLocking) {
0967: Iterator i = cld.getCollectionDescriptors(true).iterator();
0968: while (i.hasNext()) {
0969: CollectionDescriptor cds = (CollectionDescriptor) i
0970: .next();
0971: Object col = cds.getPersistentField().get(sourceObject);
0972: if (col != null) {
0973: CollectionProxy proxy = ProxyHelper
0974: .getCollectionProxy(col);
0975: if (proxy != null) {
0976: if (!proxy.isLoaded()) {
0977: if (log.isDebugEnabled())
0978: log
0979: .debug("adding self as listener to collection proxy");
0980: proxy.addListener(this );
0981: registeredCollectionProxies.add(proxy);
0982: continue;
0983: }
0984: }
0985: Iterator colIterator = BrokerHelper
0986: .getCollectionIterator(col);
0987: Object item = null;
0988: try {
0989: while (colIterator.hasNext()) {
0990: item = colIterator.next();
0991: RuntimeObject rt = new RuntimeObject(item,
0992: this );
0993: if (rt.isProxy()) {
0994: IndirectionHandler handler = ProxyHelper
0995: .getIndirectionHandler(item);
0996: if (!handler.alreadyMaterialized()) {
0997: registerToIndirectionHandler(handler);
0998: continue;
0999: } else {
1000: // @todo consider registering to hear when this is
1001: // derefernced instead of just loading here -bmc
1002: item = handler.getRealSubject();
1003: }
1004: }
1005: if (!registrationList.contains(rt
1006: .getIdentity())) {
1007: lockAndRegister(rt, lockMode,
1008: registeredObjects);
1009: }
1010: }
1011: } catch (LockNotGrantedException e) {
1012: String eol = SystemUtils.LINE_SEPARATOR;
1013: log
1014: .error(
1015: "Lock not granted, while lock collection references["
1016: + eol
1017: + "current reference descriptor:"
1018: + eol
1019: + cds.toXML()
1020: + eol
1021: + "object to lock: "
1022: + item
1023: + eol
1024: + "main object class: "
1025: + sourceObject
1026: .getClass()
1027: .getName()
1028: + eol + "]", e);
1029: throw e;
1030: }
1031: }
1032: }
1033: }
1034: }
1035:
1036: /**
1037: * this callback is invoked before an Object is materialized
1038: * within an IndirectionHandler.
1039: * @param handler the invoking handler
1040: * @param oid the identity of the object to be materialized
1041: */
1042: public void beforeMaterialization(IndirectionHandler handler,
1043: Identity oid) {
1044: //noop
1045: }
1046:
1047: /**
1048: * this callback is invoked after an Object is materialized
1049: * within an IndirectionHandler.
1050: * this callback allows to defer registration of objects until
1051: * it's really neccessary.
1052: * @param handler the invoking handler
1053: * @param materializedObject the materialized Object
1054: */
1055: public void afterMaterialization(IndirectionHandler handler,
1056: Object materializedObject) {
1057: try {
1058: Identity oid = handler.getIdentity();
1059: if (log.isDebugEnabled())
1060: log.debug("deferred registration: " + oid);
1061: if (!isOpen()) {
1062: log
1063: .error("Proxy object materialization outside of a running tx, obj="
1064: + oid);
1065: try {
1066: throw new Exception(
1067: "Proxy object materialization outside of a running tx, obj="
1068: + oid);
1069: } catch (Exception e) {
1070: e.printStackTrace();
1071: }
1072: }
1073: ClassDescriptor cld = getBroker().getClassDescriptor(
1074: materializedObject.getClass());
1075: RuntimeObject rt = new RuntimeObject(materializedObject,
1076: oid, cld, false, false);
1077: lockAndRegister(rt, Transaction.READ, isImplicitLocking(),
1078: getRegistrationList());
1079: } catch (Throwable t) {
1080: log.error(
1081: "Register materialized object with this tx failed",
1082: t);
1083: throw new LockNotGrantedException(t.getMessage());
1084: }
1085: unregisterFromIndirectionHandler(handler);
1086: }
1087:
1088: protected synchronized void unRegisterFromAllIndirectionHandlers() {
1089: // unregistering manipulates the registeredIndirectionHandlers vector
1090: // we have to loop through this vector to avoid index proplems.
1091: for (int i = registeredIndirectionHandlers.size() - 1; i >= 0; i--) {
1092: unregisterFromIndirectionHandler((IndirectionHandler) registeredIndirectionHandlers
1093: .get(i));
1094: }
1095: }
1096:
1097: protected synchronized void unRegisterFromAllCollectionProxies() {
1098: for (int i = registeredCollectionProxies.size() - 1; i >= 0; i--) {
1099: unregisterFromCollectionProxy((CollectionProxy) registeredCollectionProxies
1100: .get(i));
1101: }
1102: }
1103:
1104: protected synchronized void unregisterFromCollectionProxy(
1105: CollectionProxy handler) {
1106: handler.removeListener(this );
1107: registeredCollectionProxies.remove(handler);
1108: }
1109:
1110: protected synchronized void unregisterFromIndirectionHandler(
1111: IndirectionHandler handler) {
1112: handler.removeListener(this );
1113: registeredIndirectionHandlers.remove(handler);
1114: }
1115:
1116: protected synchronized void registerToIndirectionHandler(
1117: IndirectionHandler handler) {
1118: handler.addListener(this );
1119: registeredIndirectionHandlers.add(handler);
1120: }
1121:
1122: /**
1123: * register proxy objects that were locked but haven't been materialized yet
1124: * so they can be unlocked when closing the transaction
1125: */
1126: protected void registerUnmaterializedLocks(Object obj) {
1127: unmaterializedLocks.add(obj);
1128: }
1129:
1130: /**
1131: * Gets the broker associated with the transaction.
1132: * MBAIRD: only return the associated broker if the transaction is open,
1133: * if it's closed, throw a TransactionNotInProgressException. If we allow
1134: * brokers to be reaquired by an already closed transaction, there is a
1135: * very good chance the broker will be leaked as the doClose() method of
1136: * transactionImpl will never be called and thus the broker will never
1137: * be closed and returned to the pool.
1138: * @return Returns a PersistenceBroker
1139: * @throws TransactionNotInProgressException is the transaction is not open;
1140: */
1141: public PersistenceBrokerInternal getBrokerInternal() {
1142: if (broker == null || broker.isClosed()) {
1143: checkOpen();
1144: try {
1145: checkForDB();
1146: broker = PersistenceBrokerFactoryFactory.instance()
1147: .createPersistenceBroker(curDB.getPBKey());
1148: } catch (PBFactoryException e) {
1149: log
1150: .error(
1151: "Cannot obtain PersistenceBroker from PersistenceBrokerFactory, "
1152: + "found PBKey was "
1153: + curDB.getPBKey(), e);
1154: throw new PersistenceBrokerException(e);
1155: }
1156: }
1157: return broker;
1158: }
1159:
1160: public PersistenceBroker getBroker() {
1161: return getBrokerInternal();
1162: }
1163:
1164: /**
1165: * Returns true if an {@link org.apache.ojb.broker.PersistenceBroker} was associated with this
1166: * tx instance.
1167: */
1168: protected boolean hasBroker() {
1169: return broker != null && !broker.isClosed();
1170: }
1171:
1172: protected void cleanupBroker() {
1173: if (hasBroker()) {
1174: try {
1175: if (broker.isInTransaction()) {
1176: broker.abortTransaction();
1177: }
1178: } finally {
1179: broker.close();
1180: broker = null;
1181: }
1182: }
1183: }
1184:
1185: /*
1186: * @see Configurable#configure(Configuration)
1187: */
1188: public void configure(Configuration config)
1189: throws ConfigurationException {
1190: }
1191:
1192: /**
1193: * @see org.apache.ojb.odmg.TransactionExt#setImplicitLocking(boolean)
1194: */
1195: public synchronized void setImplicitLocking(boolean value) {
1196: implicitLocking = value;
1197: }
1198:
1199: public boolean isImplicitLocking() {
1200: return implicitLocking;
1201: }
1202:
1203: /**
1204: * noop -- here for interface
1205: */
1206: public void beforeLoading(CollectionProxyDefaultImpl colProxy) {
1207: // noop
1208: }
1209:
1210: /**
1211: * Remove colProxy from list of pending collections and
1212: * register its contents with the transaction.
1213: */
1214: public void afterLoading(CollectionProxyDefaultImpl colProxy) {
1215: if (log.isDebugEnabled())
1216: log.debug("loading a proxied collection a collection: "
1217: + colProxy);
1218: Collection data = colProxy.getData();
1219: for (Iterator iterator = data.iterator(); iterator.hasNext();) {
1220: Object o = iterator.next();
1221: if (!isOpen()) {
1222: log
1223: .error("Collection proxy materialization outside of a running tx, obj="
1224: + o);
1225: try {
1226: throw new Exception(
1227: "Collection proxy materialization outside of a running tx, obj="
1228: + o);
1229: } catch (Exception e) {
1230: e.printStackTrace();
1231: }
1232: } else {
1233: Identity oid = getBroker().serviceIdentity()
1234: .buildIdentity(o);
1235: ClassDescriptor cld = getBroker().getClassDescriptor(
1236: ProxyHelper.getRealClass(o));
1237: RuntimeObject rt = new RuntimeObject(o, oid, cld,
1238: false, ProxyHelper.isProxy(o));
1239: lockAndRegister(rt, Transaction.READ,
1240: isImplicitLocking(), getRegistrationList());
1241: }
1242: }
1243: unregisterFromCollectionProxy(colProxy);
1244: }
1245:
1246: protected void performTransactionAwareBeforeCommit() {
1247: Enumeration en = objectEnvelopeTable.elements();
1248: while (en.hasMoreElements()) {
1249: ((ObjectEnvelope) en.nextElement()).beforeCommit();
1250: }
1251: }
1252:
1253: protected void performTransactionAwareAfterCommit() {
1254: Enumeration en = objectEnvelopeTable.elements();
1255: try {
1256: while (en.hasMoreElements()) {
1257: ((ObjectEnvelope) en.nextElement()).afterCommit();
1258: }
1259: } catch (Exception e) {
1260: log
1261: .error(
1262: "Unexpected error while perform 'TransactionAware#afterCommit()' listener after commit of objects,"
1263: + " after commit you can't rollback - exception will be skipped.",
1264: e);
1265: }
1266: }
1267:
1268: protected void performTransactionAwareBeforeRollback() {
1269: Enumeration en = objectEnvelopeTable.elements();
1270: while (en.hasMoreElements()) {
1271: try {
1272: ((ObjectEnvelope) en.nextElement()).beforeAbort();
1273: } catch (Exception e) {
1274: log
1275: .error(
1276: "Unexpected error while perform 'TransactionAware#beforeAbort()' listener before rollback of objects"
1277: + " - exception will be skipped to complete rollback.",
1278: e);
1279: }
1280: }
1281: }
1282:
1283: protected void performTransactionAwareAfterRollback() {
1284: Enumeration en = objectEnvelopeTable.elements();
1285: try {
1286: while (en.hasMoreElements()) {
1287: ((ObjectEnvelope) en.nextElement()).afterAbort();
1288: }
1289: } catch (Exception e) {
1290: log
1291: .error(
1292: "Unexpected error while perform 'TransactionAware#afterAbort()' listener after rollback of objects"
1293: + " - exception will be skipped.",
1294: e);
1295: }
1296: }
1297:
1298: /**
1299: * Detect new objects.
1300: */
1301: protected boolean isTransient(ClassDescriptor cld, Object obj,
1302: Identity oid) {
1303: // if the Identity is transient we assume a non-persistent object
1304: boolean isNew = oid != null && oid.isTransient();
1305: /*
1306: detection of new objects is costly (select of ID in DB to check if object
1307: already exists) we do:
1308: a. check if the object has nullified PK field
1309: b. check if the object is already registered
1310: c. lookup from cache and if not found, last option select on DB
1311: */
1312: if (!isNew) {
1313: final PersistenceBroker pb = getBroker();
1314: if (cld == null) {
1315: cld = pb.getClassDescriptor(obj.getClass());
1316: }
1317: isNew = pb.serviceBrokerHelper().hasNullPKField(cld, obj);
1318: if (!isNew) {
1319: if (oid == null) {
1320: oid = pb.serviceIdentity().buildIdentity(cld, obj);
1321: }
1322: final ObjectEnvelope mod = objectEnvelopeTable
1323: .getByIdentity(oid);
1324: if (mod != null) {
1325: // already registered object, use current state
1326: isNew = mod.needsInsert();
1327: } else {
1328: // if object was found cache, assume it's old
1329: // else make costly check against the DB
1330: isNew = pb.serviceObjectCache().lookup(oid) == null
1331: && !pb.serviceBrokerHelper().doesExist(cld,
1332: oid, obj);
1333: }
1334: }
1335: }
1336: return isNew;
1337: }
1338:
1339: /**
1340: * Allows to change the <em>cascading delete</em> behavior of the specified reference
1341: * of the target class while this transaction is in use.
1342: *
1343: * @param target The class to change cascading delete behavior of the references.
1344: * @param referenceField The field name of the 1:1, 1:n or 1:n reference.
1345: * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled.
1346: */
1347: public void setCascadingDelete(Class target, String referenceField,
1348: boolean doCascade) {
1349: ClassDescriptor cld = getBroker().getClassDescriptor(target);
1350: ObjectReferenceDescriptor ord = cld
1351: .getObjectReferenceDescriptorByName(referenceField);
1352: if (ord == null) {
1353: ord = cld.getCollectionDescriptorByName(referenceField);
1354: }
1355: if (ord == null) {
1356: throw new CascadeSettingException(
1357: "Invalid reference field name '"
1358: + referenceField
1359: + "', can't find 1:1, 1:n or m:n relation with that name in "
1360: + target);
1361: }
1362: runtimeCascadeDeleteMap.put(ord, (doCascade ? Boolean.TRUE
1363: : Boolean.FALSE));
1364: }
1365:
1366: /**
1367: * Allows to change the <em>cascading delete</em> behavior of all references of the
1368: * specified class while this transaction is in use - if the specified class is an
1369: * interface, abstract class or class with "extent" classes the cascading flag will
1370: * be propagated.
1371: *
1372: * @param target The class to change cascading delete behavior of all references.
1373: * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled.
1374: */
1375: public void setCascadingDelete(Class target, boolean doCascade) {
1376: ClassDescriptor cld = getBroker().getClassDescriptor(target);
1377: List extents = cld.getExtentClasses();
1378: Boolean result = doCascade ? Boolean.TRUE : Boolean.FALSE;
1379: setCascadingDelete(cld, result);
1380: if (extents != null && extents.size() > 0) {
1381: for (int i = 0; i < extents.size(); i++) {
1382: Class extent = (Class) extents.get(i);
1383: ClassDescriptor tmp = getBroker().getClassDescriptor(
1384: extent);
1385: setCascadingDelete(tmp, result);
1386: }
1387: }
1388: }
1389:
1390: private void setCascadingDelete(ClassDescriptor cld, Boolean cascade) {
1391: List singleRefs = cld.getObjectReferenceDescriptors(true);
1392: for (int i = 0; i < singleRefs.size(); i++) {
1393: Object o = singleRefs.get(i);
1394: runtimeCascadeDeleteMap.put(o, cascade);
1395: }
1396: List collectionRefs = cld.getCollectionDescriptors(true);
1397: for (int i = 0; i < collectionRefs.size(); i++) {
1398: Object o = collectionRefs.get(i);
1399: runtimeCascadeDeleteMap.put(o, cascade);
1400: }
1401: }
1402:
1403: private HashMap runtimeCascadeDeleteMap = new HashMap();
1404:
1405: /**
1406: * Returns <em>true</em> if cascading delete is enabled for the specified
1407: * single or collection descriptor.
1408: */
1409: protected boolean cascadeDeleteFor(ObjectReferenceDescriptor ord) {
1410: boolean result;
1411: Boolean runtimeSetting = (Boolean) runtimeCascadeDeleteMap
1412: .get(ord);
1413: if (runtimeSetting == null) {
1414: /*
1415: arminw: Here we use the auto-delete flag defined in metadata
1416: */
1417: result = ord.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT;
1418: } else {
1419: result = runtimeSetting.booleanValue();
1420: }
1421: return result;
1422: }
1423:
1424: int getImpliciteLockType(int parentLockMode) {
1425: return (parentLockMode == Transaction.WRITE && impliciteWriteLocks) ? Transaction.WRITE
1426: : Transaction.READ;
1427: }
1428:
1429: /**
1430: * Return <em>true</em> if the OJB ordering algorithm is enabled.
1431: * @see #setOrdering(boolean)
1432: */
1433: public boolean isOrdering() {
1434: return ordering;
1435: }
1436:
1437: /**
1438: * Allows to enable/disable the OJB persistent object ordering algorithm. If
1439: * <em>true</em> OJB try to order the modified/new/deleted objects in a correct order
1440: * (based on a algorithm) before the objects are written to the persistent storage.
1441: * <br/>
1442: * If <em>false</em> the order of the objects rely on the order specified by
1443: * the user and on settings like {@link #setImplicitLocking(boolean)}.
1444: *
1445: * @param ordering Set <em>true</em> to enable object ordering on commit.
1446: */
1447: public void setOrdering(boolean ordering) {
1448: this .ordering = ordering;
1449: }
1450:
1451: //============================================================
1452: // inner class
1453: //============================================================
1454: /**
1455: * This was thrown when something wrong with the cascading delete setting.
1456: */
1457: static class CascadeSettingException extends OJBRuntimeException {
1458: public CascadeSettingException() {
1459: }
1460:
1461: public CascadeSettingException(String msg) {
1462: super (msg);
1463: }
1464:
1465: public CascadeSettingException(Throwable cause) {
1466: super (cause);
1467: }
1468:
1469: public CascadeSettingException(String msg, Throwable cause) {
1470: super(msg, cause);
1471: }
1472: }
1473: }
|