KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > odmg > TransactionImpl


1 package org.apache.ojb.odmg;
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 javax.transaction.Status JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.Enumeration JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25
26 import org.apache.commons.lang.SystemUtils;
27 import org.apache.ojb.broker.Identity;
28 import org.apache.ojb.broker.OJBRuntimeException;
29 import org.apache.ojb.broker.PBFactoryException;
30 import org.apache.ojb.broker.PersistenceBroker;
31 import org.apache.ojb.broker.PersistenceBrokerException;
32 import org.apache.ojb.broker.PersistenceBrokerInternal;
33 import org.apache.ojb.broker.core.PersistenceBrokerFactoryFactory;
34 import org.apache.ojb.broker.core.proxy.CollectionProxy;
35 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
36 import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
37 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
38 import org.apache.ojb.broker.core.proxy.MaterializationListener;
39 import org.apache.ojb.broker.core.proxy.ProxyHelper;
40 import org.apache.ojb.broker.metadata.ClassDescriptor;
41 import org.apache.ojb.broker.metadata.CollectionDescriptor;
42 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
43 import org.apache.ojb.broker.util.BrokerHelper;
44 import org.apache.ojb.broker.util.GUID;
45 import org.apache.ojb.broker.util.configuration.Configurable;
46 import org.apache.ojb.broker.util.configuration.Configuration;
47 import org.apache.ojb.broker.util.configuration.ConfigurationException;
48 import org.apache.ojb.broker.util.logging.Logger;
49 import org.apache.ojb.broker.util.logging.LoggerFactory;
50 import org.apache.ojb.odmg.locking.LockManager;
51 import org.odmg.DatabaseClosedException;
52 import org.odmg.LockNotGrantedException;
53 import org.odmg.ODMGRuntimeException;
54 import org.odmg.Transaction;
55 import org.odmg.TransactionAbortedException;
56 import org.odmg.TransactionNotInProgressException;
57
58 /**
59  *
60  * Implementation of Transaction for org.odmg.Transaction.
61  *
62  * @author Thomas Mahler & David Dixon-Peugh
63  * @author <a HREF="mailto:mattbaird@yahoo.com">Matthew Baird</a>
64  * @author <a HREF="mailto:armin@codeAuLait.de">Armin Waibel</a>
65  * @author <a HREF="mailto:brianm@apache.org">Brian McCallister</a>
66  * @version $Id: TransactionImpl.java,v 1.59.2.25 2005/12/21 22:29:21 tomdz Exp $
67  *
68  */

69 public class TransactionImpl
70         implements Transaction, MaterializationListener, Configurable, CollectionProxyListener, TransactionExt
71 {
72     private Logger log = LoggerFactory.getLogger(TransactionImpl.class);
73     private boolean impliciteWriteLocks;
74     private boolean implicitLocking;
75     private boolean ordering;
76
77     private String JavaDoc txGUID;
78     protected PersistenceBrokerInternal broker = null;
79     private ArrayList JavaDoc registrationList = new ArrayList JavaDoc();
80     private ImplementationImpl implementation;
81     private NamedRootsMap namedRootsMap;
82
83     /**
84      * The status of the current transaction, as specified by the
85      * javax.transaction package.
86      * See {@link javax.transaction.Status} for list of valid values.
87      */

88     private int txStatus = Status.STATUS_NO_TRANSACTION;
89
90     /**
91      * the internal table containing all Objects "touched" by this tx and their
92      * respective transactional state
93      */

94     protected ObjectEnvelopeTable objectEnvelopeTable = null;
95
96     /**
97      * reference to the currently opened database
98      */

99     private DatabaseImpl curDB;
100     /**
101      * The tx may me listening to a number of IndirectionHandlers.
102      * on abort or commit these Handlers must be informed to remove
103      * tx from their List of Listeners.
104      */

105     private ArrayList JavaDoc registeredIndirectionHandlers = new ArrayList JavaDoc();
106
107     /**
108      * Unloaded collection proxies which will be registered with tx when
109      * collection is loaded
110      */

111     private ArrayList JavaDoc registeredCollectionProxies = new ArrayList JavaDoc();
112
113     /**
114      * list of proxy objects that were locked, but haven't been materialized yet.
115      * This is necessary so the locks can be released on closing the transaction
116      */

117     private ArrayList JavaDoc unmaterializedLocks = new ArrayList JavaDoc();
118
119     /**
120      * Creates new Transaction
121      * @param implementation The odmg Implementation class
122      */

123     public TransactionImpl(ImplementationImpl implementation)
124     {
125         this.implementation = implementation;
126         this.impliciteWriteLocks = implementation.isImpliciteWriteLocks();
127         this.implicitLocking = implementation.isImplicitLocking();
128         this.ordering = implementation.isOrdering();
129         //this.noteUserOrdering = implementation.isNoteUserOrder();
130

131         // assign a globally uniqe id to this tx
132
txGUID = new GUID().toString();
133         curDB = implementation.getCurrentDatabase();
134         namedRootsMap = new NamedRootsMap(this);
135     }
136
137     public ImplementationImpl getImplementation()
138     {
139         return implementation;
140     }
141
142     public NamedRootsMap getNamedRootsMap()
143     {
144         return namedRootsMap;
145     }
146
147     /**
148      * Returns the associated database
149      */

150     public DatabaseImpl getAssociatedDatabase()
151     {
152         return this.curDB;
153     }
154
155     protected int getStatus()
156     {
157         return txStatus;
158     }
159
160     protected void setStatus(int status)
161     {
162         this.txStatus = status;
163     }
164
165     private void checkForDB()
166     {
167         if (curDB == null || !curDB.isOpen())
168         {
169             log.error("Transaction without a associated open Database.");
170             throw new TransactionAbortedExceptionOJB(
171                     "No open database found. Open the database before handling transactions");
172         }
173     }
174
175     /**
176      * Determine whether the transaction is open or not. A transaction is open if
177      * a call has been made to <code>begin</code> , but a subsequent call to
178      * either <code>commit</code> or <code>abort</code> has not been made.
179      * @return True if the transaction is open, otherwise false.
180      */

181     public boolean isOpen()
182     {
183         return (getStatus() == Status.STATUS_ACTIVE ||
184                 getStatus() == Status.STATUS_MARKED_ROLLBACK ||
185                 getStatus() == Status.STATUS_PREPARED ||
186                 getStatus() == Status.STATUS_PREPARING ||
187                 getStatus() == Status.STATUS_COMMITTING);
188     }
189
190     private void checkOpen()
191     {
192         if (!isOpen())
193         {
194             throw new TransactionNotInProgressException(
195                     "Transaction was not open, call tx.begin() before perform action, current status is: " +
196                     TxUtil.getStatusString(getStatus()));
197         }
198     }
199
200
201     /**
202      * Attach the caller's thread to this <code>Transaction</code> and detach the
203      * thread from any former <code>Transaction</code> the thread may have been
204      * associated with.
205      */

206     public void join()
207     {
208         checkOpen();
209         implementation.getTxManager().deregisterTx(this);
210         implementation.getTxManager().registerTx(this);
211     }
212
213     /**
214      * Upgrade the lock on the given object to the given lock mode. The call has
215      * no effect if the object's current lock is already at or above that level of
216      * lock mode.
217      *
218      * @param obj object to acquire a lock on.
219      * @param lockMode lock mode to acquire. The lock modes
220      * are <code>READ</code> , <code>UPGRADE</code> , and <code>WRITE</code> .
221      *
222      * @exception LockNotGrantedException Description of Exception
223      */

224     public void lock(Object JavaDoc obj, int lockMode) throws LockNotGrantedException
225     {
226         if (log.isDebugEnabled()) log.debug("lock object was called on tx " + this + ", object is " + obj.toString());
227         checkOpen();
228         RuntimeObject rtObject = new RuntimeObject(obj, this);
229         lockAndRegister(rtObject, lockMode, isImplicitLocking(), getRegistrationList());
230 // if(isImplicitLocking()) moveToLastInOrderList(rtObject.getIdentity());
231
}
232
233     /**
234      * Returns an empty List for registration of processed object Identity.
235      */

236     public ArrayList JavaDoc getRegistrationList()
237     {
238         clearRegistrationList();
239         return registrationList;
240     }
241
242     /**
243      * Clears the list of processed object Identity.
244      */

245     public void clearRegistrationList()
246     {
247         registrationList.clear();
248     }
249
250     /**
251      * Lock and register the specified object, make sure that when cascading locking and register
252      * is enabled to specify a List to register the already processed object Identiy.
253      */

254     public void lockAndRegister(RuntimeObject rtObject, int lockMode, List JavaDoc registeredObjects)
255     {
256         lockAndRegister(rtObject, lockMode, isImplicitLocking(), registeredObjects);
257     }
258
259     /**
260      * Lock and register the specified object, make sure that when cascading locking and register
261      * is enabled to specify a List to register the already processed object Identiy.
262      */

263     public synchronized void lockAndRegister(RuntimeObject rtObject, int lockMode, boolean cascade, List JavaDoc registeredObjects)
264     {
265         if(log.isDebugEnabled()) log.debug("Lock and register called for " + rtObject.getIdentity());
266         // if current object was already locked, do nothing
267
// avoid endless loops when circular object references are used
268
if(!registeredObjects.contains(rtObject.getIdentity()))
269         {
270             if(cascade)
271             {
272                 // if implicite locking is enabled, first add the current object to
273
// list of registered objects to avoid endless loops on circular objects
274
registeredObjects.add(rtObject.getIdentity());
275                 // lock and register 1:1 references first
276
//
277
// If implicit locking is used, we have materialize the main object
278
// to lock the referenced objects too
279
lockAndRegisterReferences(rtObject.getCld(), rtObject.getObjMaterialized(), lockMode, registeredObjects);
280             }
281             try
282             {
283                 // perform the lock on the object
284
// we don't need to lock new objects
285
if(!rtObject.isNew())
286                 {
287                     doSingleLock(rtObject.getCld(), rtObject.getObj(), rtObject.getIdentity(), lockMode);
288                 }
289                 // after we locked the object, register it to detect status and changes while tx
290
doSingleRegister(rtObject, lockMode);
291             }
292             catch (Throwable JavaDoc t)
293             {
294                 //log.error("Locking of obj " + rtObject.getIdentity() + " failed", t);
295
// if registering of object fails release lock on object, because later we don't
296
// get a change to do this.
297
implementation.getLockManager().releaseLock(this, rtObject.getIdentity(), rtObject.getObj());
298                 if(t instanceof LockNotGrantedException)
299                 {
300                     throw (LockNotGrantedException) t;
301                 }
302                 else
303                 {
304                     log.error("Unexpected failure while locking", t);
305                     throw new LockNotGrantedException("Locking failed for "
306                             + rtObject.getIdentity()+ ", nested exception is: [" + t.getClass().getName()
307                             + ": " + t.getMessage() + "]");
308                 }
309             }
310             if(cascade)
311             {
312                 // perform locks and register 1:n and m:n references
313
// If implicit locking is used, we have materialize the main object
314
// to lock the referenced objects too
315
lockAndRegisterCollections(rtObject.getCld(), rtObject.getObjMaterialized(), lockMode, registeredObjects);
316             }
317         }
318     }
319
320     /**
321      * Only lock the specified object, represented by
322      * the {@link RuntimeObject} instance.
323      *
324      * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor}
325      * of the object to acquire a lock on.
326      * @param oid The {@link org.apache.ojb.broker.Identity} of the object to lock.
327      * @param lockMode lock mode to acquire. The lock modes
328      * are <code>READ</code> , <code>UPGRADE</code> , and <code>WRITE</code>.
329      *
330      * @exception LockNotGrantedException Description of Exception
331      */

332     void doSingleLock(ClassDescriptor cld, Object JavaDoc obj, Identity oid, int lockMode) throws LockNotGrantedException
333     {
334         LockManager lm = implementation.getLockManager();
335         if (cld.isAcceptLocks())
336         {
337             if (lockMode == Transaction.READ)
338             {
339                 if (log.isDebugEnabled()) log.debug("Do READ lock on object: " + oid);
340                 if(!lm.readLock(this, oid, obj))
341                 {
342                     throw new LockNotGrantedException("Can not lock for READ: " + oid);
343                 }
344             }
345             else if (lockMode == Transaction.WRITE)
346             {
347                 if (log.isDebugEnabled()) log.debug("Do WRITE lock on object: " + oid);
348                 if(!lm.writeLock(this, oid, obj))
349                 {
350                     throw new LockNotGrantedException("Can not lock for WRITE: " + oid);
351                 }
352             }
353             else if (lockMode == Transaction.UPGRADE)
354             {
355                 if (log.isDebugEnabled()) log.debug("Do UPGRADE lock on object: " + oid);
356                 if(!lm.upgradeLock(this, oid, obj))
357                 {
358                     throw new LockNotGrantedException("Can not lock for UPGRADE: " + oid);
359                 }
360             }
361         }
362         else
363         {
364             if (log.isDebugEnabled())
365             {
366                 log.debug("Class '" + cld.getClassNameOfObject() + "' doesn't accept locks" +
367                         " (accept-locks=false) when implicite locked, so OJB skip this object: " + oid);
368             }
369         }
370     }
371
372     /**
373      * Detach the caller's thread from this <code>Transaction</code> , but do not
374      * attach the thread to another <code>Transaction</code> .
375      */

376     public void leave()
377     {
378         checkOpen();
379         implementation.getTxManager().deregisterTx(this);
380     }
381
382     /**
383      * Write objects to data store, but don't release the locks.
384      * I don't know what we should do if we are in a checkpoint and
385      * we need to abort.
386      */

387     protected synchronized void doWriteObjects(boolean isFlush) throws TransactionAbortedException, LockNotGrantedException
388     {
389         /*
390         arminw:
391         if broker isn't in PB-tx, start tx
392         */

393         if (!getBroker().isInTransaction())
394         {
395             if (log.isDebugEnabled()) log.debug("call beginTransaction() on PB instance");
396             broker.beginTransaction();
397         }
398
399         // Notify objects of impending commits.
400
performTransactionAwareBeforeCommit();
401
402         // Now perfom the real work
403
objectEnvelopeTable.writeObjects(isFlush);
404         // now we have to perform the named objects
405
namedRootsMap.performDeletion();
406         namedRootsMap.performInsert();
407         namedRootsMap.afterWriteCleanup();
408     }
409
410     /**
411      * Do the Aborts, but don't release the locks.
412      * Do the aborts on the NamedRootsMap first, then
413      * abort the other stuff.
414      */

415     protected synchronized void doAbort()
416     {
417         // Notify objects of impending aborts.
418
performTransactionAwareBeforeRollback();
419
420         // Now, we abort everything. . .
421
objectEnvelopeTable.rollback();
422
423         // Now, we notify everything the abort is done.
424
performTransactionAwareAfterRollback();
425     }
426
427     /**
428      * Close a transaction and do all the cleanup associated with it.
429      */

430     protected synchronized void doClose()
431     {
432         try
433         {
434             LockManager lm = getImplementation().getLockManager();
435             Enumeration JavaDoc en = objectEnvelopeTable.elements();
436             while (en.hasMoreElements())
437             {
438                 ObjectEnvelope oe = (ObjectEnvelope) en.nextElement();
439                 lm.releaseLock(this, oe.getIdentity(), oe.getObject());
440             }
441
442             //remove locks for objects which haven't been materialized yet
443
for (Iterator JavaDoc it = unmaterializedLocks.iterator(); it.hasNext();)
444             {
445                 lm.releaseLock(this, it.next());
446             }
447
448             // this tx is no longer interested in materialization callbacks
449
unRegisterFromAllIndirectionHandlers();
450             unRegisterFromAllCollectionProxies();
451         }
452         finally
453         {
454             /**
455              * MBAIRD: Be nice and close the table to release all refs
456              */

457             if (log.isDebugEnabled())
458                 log.debug("Close Transaction and release current PB " + broker + " on tx " + this);
459             // remove current thread from LocalTxManager
460
// to avoid problems for succeeding calls of the same thread
461
implementation.getTxManager().deregisterTx(this);
462             // now cleanup and prepare for reuse
463
refresh();
464         }
465     }
466
467     /**
468      * cleanup tx and prepare for reuse
469      */

470     protected void refresh()
471     {
472         if (log.isDebugEnabled())
473                 log.debug("Refresh this transaction for reuse: " + this);
474         try
475         {
476             // we reuse ObjectEnvelopeTable instance
477
objectEnvelopeTable.refresh();
478         }
479         catch (Exception JavaDoc e)
480         {
481             if (log.isDebugEnabled())
482             {
483                 log.debug("error closing object envelope table : " + e.getMessage());
484                 e.printStackTrace();
485             }
486         }
487         cleanupBroker();
488         // clear the temporary used named roots map
489
// we should do that, because same tx instance
490
// could be used several times
491
broker = null;
492         clearRegistrationList();
493         unmaterializedLocks.clear();
494         txStatus = Status.STATUS_NO_TRANSACTION;
495     }
496
497     /**
498      * Commit the transaction, but reopen the transaction, retaining all locks.
499      * Calling <code>checkpoint</code> commits persistent object modifications
500      * made within the transaction since the last checkpoint to the database. The
501      * transaction retains all locks it held on those objects at the time the
502      * checkpoint was invoked.
503      */

504     public void checkpoint()
505     {
506         if (log.isDebugEnabled()) log.debug("Checkpoint was called, commit changes hold locks on tx " + this);
507         try
508         {
509             checkOpen();
510             doWriteObjects(true);
511             // do commit on PB
512
if (hasBroker() && broker.isInTransaction()) broker.commitTransaction();
513         }
514         catch (Throwable JavaDoc t)
515         {
516             log.error("Checkpoint call failed, do abort transaction", t);
517             txStatus = Status.STATUS_MARKED_ROLLBACK;
518             abort();
519             if(!(t instanceof ODMGRuntimeException))
520             {
521                 throw new TransactionAbortedExceptionOJB("Can't tx.checkpoint() objects: " + t.getMessage(), t);
522             }
523             else
524             {
525                 throw (ODMGRuntimeException) t;
526             }
527         }
528     }
529
530     /**
531      * @see org.apache.ojb.odmg.TransactionExt#flush
532      */

533     public void flush()
534     {
535         if (log.isDebugEnabled())
536         {
537             log.debug("Flush was called - write changes to database, do not commit, hold locks on tx " + this);
538         }
539
540         try
541         {
542             checkOpen();
543             doWriteObjects(true);
544         }
545         catch (Throwable JavaDoc t)
546         {
547             log.error("Calling method 'tx.flush()' failed", t);
548             txStatus = Status.STATUS_MARKED_ROLLBACK;
549             abort();
550             if(!(t instanceof ODMGRuntimeException))
551             {
552                 throw new TransactionAbortedExceptionOJB("Can't tx.flush() objects: " + t.getMessage(), t);
553             }
554             else
555             {
556                 throw (ODMGRuntimeException) t;
557             }
558         }
559     }
560
561     /**
562      * @see org.apache.ojb.odmg.TransactionExt#markDelete
563      */

564     public void markDelete(Object JavaDoc anObject)
565     {
566         ObjectEnvelope otw = objectEnvelopeTable.get(anObject, false);
567         // not needed on delete - or?
568
//otw.refreshObjectIfNeeded(anObject);
569
otw.setModificationState(otw.getModificationState().markDelete());
570     }
571
572     public void deletePersistent(RuntimeObject rt)
573     {
574 // if(rt.isNew())
575
// {
576
// throw new ObjectNotPersistentException("Object " + rt.getIdentity() + " is not yet persistent");
577
// }
578
if(rt.isProxy())
579         {
580             Object JavaDoc realObj = rt.getHandler().getRealSubject();
581             rt = new RuntimeObject(realObj, rt.getIdentity(), this, false);
582         }
583         lockAndRegister(rt, Transaction.WRITE, getRegistrationList());
584         ObjectEnvelope oe = objectEnvelopeTable.getByIdentity(rt.getIdentity());
585         // TODO: not needed on delete - or? When optimistic locking is used we should always use the
586
// specified object instance to use the last version of the object
587
oe.refreshObjectIfNeeded(rt.getObj());
588         oe.setModificationState(oe.getModificationState().markDelete());
589     }
590
591     /**
592      * @see org.apache.ojb.odmg.TransactionExt#markDirty
593      */

594     public void markDirty(Object JavaDoc anObject)
595     {
596         ObjectEnvelope otw = objectEnvelopeTable.get(anObject, false);
597         otw.refreshObjectIfNeeded(anObject);
598         otw.setModificationState(otw.getModificationState().markDirty());
599     }
600
601     void markDirty(RuntimeObject rt)
602     {
603         ObjectEnvelope otw = objectEnvelopeTable.get(rt.getIdentity(), rt.getObj(), rt.isNew());
604         otw.refreshObjectIfNeeded(rt.getObj());
605         otw.setModificationState(otw.getModificationState().markDirty());
606     }
607
608     void markPersistent(RuntimeObject rtObj)
609     {
610         ObjectEnvelope oe = objectEnvelopeTable.getByIdentity(rtObj.getIdentity());
611         if(oe == null)
612         {
613             oe = objectEnvelopeTable.get(rtObj.getIdentity(), rtObj.getObj(), rtObj.isNew());
614         }
615         if(oe.needsDelete())
616         {
617             oe.setModificationState(oe.getModificationState().markNew());
618         }
619         else
620         {
621             oe.setModificationState(oe.getModificationState().markDirty());
622         }
623         oe.refreshObjectIfNeeded(rtObj.getObj());
624     }
625
626     void makePersistent(RuntimeObject rt)
627     {
628         try
629         {
630             lockAndRegister(rt, Transaction.WRITE, getRegistrationList());
631             markPersistent(rt);
632         }
633         catch (org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException ex)
634         {
635             log.error("Can't persist object: " + rt.getIdentity(), ex);
636             throw new org.odmg.ClassNotPersistenceCapableException(ex.getMessage());
637         }
638     }
639
640     /**
641      * @see org.apache.ojb.odmg.TransactionExt#isDeleted(org.apache.ojb.broker.Identity)
642      */

643     public boolean isDeleted(Identity id)
644     {
645         ObjectEnvelope envelope = objectEnvelopeTable.getByIdentity(id);
646         return (envelope != null && envelope.needsDelete());
647     }
648
649     /**
650      * Upgrade the lock on the given object to the given lock mode. Method <code>
651      * tryLock</code> is the same as <code>lock</code> except it returns a boolean
652      * indicating whether the lock was granted instead of generating an exception.
653      * @param obj Description of Parameter
654      * @param lockMode Description of Parameter
655      * @return Description of the Returned Value
656      * </code>, <code>UPGRADE</code> , and <code>WRITE</code> .
657      * @return true if the lock has been acquired, otherwise false.
658      */

659     public boolean tryLock(Object JavaDoc obj, int lockMode)
660     {
661         if (log.isDebugEnabled()) log.debug("Try to lock object was called on tx " + this);
662         checkOpen();
663         try
664         {
665             lock(obj, lockMode);
666             return true;
667         }
668         catch (LockNotGrantedException ex)
669         {
670             return false;
671         }
672     }
673
674     /**
675      * Commit and close the transaction. Calling <code>commit</code> commits to
676      * the database all persistent object modifications within the transaction and
677      * releases any locks held by the transaction. A persistent object
678      * modification is an update of any field of an existing persistent object, or
679      * an update or creation of a new named object in the database. If a
680      * persistent object modification results in a reference from an existing
681      * persistent object to a transient object, the transient object is moved to
682      * the database, and all references to it updated accordingly. Note that the
683      * act of moving a transient object to the database may create still more
684      * persistent references to transient objects, so its referents must be
685      * examined and moved as well. This process continues until the database
686      * contains no references to transient objects, a condition that is guaranteed
687      * as part of transaction commit. Committing a transaction does not remove
688      * from memory transient objects created during the transaction.
689      *
690      * The updateObjectList contains a list of all objects for which this transaction
691      * has write privledge to. We need to update these objects.
692      */

693     public void commit()
694     {
695         checkOpen();
696         try
697         {
698             prepareCommit();
699             checkForCommit();
700
701             txStatus = Status.STATUS_COMMITTING;
702             if (log.isDebugEnabled()) log.debug("Commit transaction " + this);
703             // now do real commit on broker
704
if(hasBroker()) getBroker().commitTransaction();
705
706             // Now, we notify everything the commit is done.
707
performTransactionAwareAfterCommit();
708
709             doClose();
710             txStatus = Status.STATUS_COMMITTED;
711         }
712         catch(Exception JavaDoc ex)
713         {
714             log.error("Error while commit objects, do abort tx " + this + ", " + ex.getMessage(), ex);
715             txStatus = Status.STATUS_MARKED_ROLLBACK;
716             abort();
717             if(!(ex instanceof ODMGRuntimeException))
718             {
719                 throw new TransactionAbortedExceptionOJB("Can't commit objects: " + ex.getMessage(), ex);
720             }
721             else
722             {
723                 throw (ODMGRuntimeException) ex;
724             }
725         }
726     }
727
728     protected void checkForCommit()
729     {
730         // Never commit transaction that has been marked for rollback
731
if (txStatus == Status.STATUS_MARKED_ROLLBACK)
732             throw new TransactionAbortedExceptionOJB("Illegal tx-status: tx is already markedRollback");
733         // Don't commit if not prepared
734
if (txStatus != Status.STATUS_PREPARED)
735             throw new IllegalStateException JavaDoc("Illegal tx-status: Do prepare commit before commit");
736     }
737
738     /**
739      * Prepare does the actual work of moving the changes at the object level
740      * into storage (the underlying rdbms for instance). prepare Can be called multiple times, and
741      * does not release locks.
742      *
743      * @throws TransactionAbortedException if the transaction has been aborted
744      * for any reason.
745      * @throws IllegalStateException Method called if transaction is
746      * not in the proper state to perform this operation
747      * @throws TransactionNotInProgressException if the transaction is closed.
748      */

749     protected void prepareCommit() throws TransactionAbortedException, LockNotGrantedException
750     {
751         if (txStatus == Status.STATUS_MARKED_ROLLBACK)
752         {
753             throw new TransactionAbortedExceptionOJB("Prepare Transaction: tx already marked for rollback");
754         }
755         if (txStatus != Status.STATUS_ACTIVE)
756         {
757             throw new IllegalStateException JavaDoc("Prepare Transaction: tx status is not 'active', status is " + TxUtil.getStatusString(txStatus));
758         }
759         try
760         {
761             txStatus = Status.STATUS_PREPARING;
762             doWriteObjects(false);
763             txStatus = Status.STATUS_PREPARED;
764         }
765         catch (RuntimeException JavaDoc e)
766         {
767             log.error("Could not prepare for commit", e);
768             txStatus = Status.STATUS_MARKED_ROLLBACK;
769             throw e;
770         }
771     }
772
773     /**
774      * Abort and close the transaction. Calling abort abandons all persistent
775      * object modifications and releases the associated locks. Aborting a
776      * transaction does not restore the state of modified transient objects
777      */

778     public void abort()
779     {
780         /*
781         do nothing if already rolledback
782         */

783         if (txStatus == Status.STATUS_NO_TRANSACTION
784                 || txStatus == Status.STATUS_UNKNOWN
785                 || txStatus == Status.STATUS_ROLLEDBACK)
786         {
787             log.info("Nothing to abort, tx is not active - status is " + TxUtil.getStatusString(txStatus));
788             return;
789         }
790         // check status of tx
791
if (txStatus != Status.STATUS_ACTIVE && txStatus != Status.STATUS_PREPARED &&
792                 txStatus != Status.STATUS_MARKED_ROLLBACK)
793         {
794             throw new IllegalStateException JavaDoc("Illegal state for abort call, state was '" + TxUtil.getStatusString(txStatus) + "'");
795         }
796         if(log.isEnabledFor(Logger.INFO))
797         {
798             log.info("Abort transaction was called on tx " + this);
799         }
800         try
801         {
802             try
803             {
804                 doAbort();
805             }
806             catch(Exception JavaDoc e)
807             {
808                 log.error("Error while abort transaction, will be skipped", e);
809             }
810
811             // used in managed environments, ignored in non-managed
812
this.implementation.getTxManager().abortExternalTx(this);
813
814             try
815             {
816                 if(hasBroker() && getBroker().isInTransaction())
817                 {
818                     getBroker().abortTransaction();
819                 }
820             }
821             catch(Exception JavaDoc e)
822             {
823                 log.error("Error while do abort used broker instance, will be skipped", e);
824             }
825         }
826         finally
827         {
828             txStatus = Status.STATUS_ROLLEDBACK;
829             // cleanup things, e.g. release all locks
830
doClose();
831         }
832     }
833
834     /**
835      * Start a transaction. Calling <code>begin</code> multiple times on the same
836      * transaction object, without an intervening call to <code>commit</code> or
837      * <code>abort</code> , causes the exception <code>
838      * TransactionInProgressException</code> to be thrown on the second and
839      * subsequent calls. Operations executed before a transaction has been opened,
840      * or before reopening after a transaction is aborted or committed, have
841      * undefined results; these may throw a <code>
842      * TransactionNotInProgressException</code> exception.
843      */

844     public synchronized void begin()
845     {
846         checkForBegin();
847         if (log.isDebugEnabled()) log.debug("Begin transaction was called on tx " + this);
848         // initialize the ObjectEnvelope table
849
objectEnvelopeTable = new ObjectEnvelopeTable(this);
850         // register transaction
851
implementation.getTxManager().registerTx(this);
852         // mark tx as active (open)
853
txStatus = Status.STATUS_ACTIVE;
854     }
855
856     protected void checkForBegin()
857     {
858         /**
859          * Is the associated database non-null and open? ODMG 3.0 says it must be.
860          */

861         if ((curDB == null) || !curDB.isOpen())
862         {
863             throw new DatabaseClosedException("Database is not open. Must have an open DB to begin the Tx.");
864         }
865         if (isOpen())
866         {
867             log.error("Transaction is already open");
868             throw new org.odmg.TransactionInProgressException("Impossible to call begin on already opened tx");
869         }
870     }
871
872     public String JavaDoc getGUID()
873     {
874         return txGUID;
875     }
876
877     /**
878      * Get object by identity. First lookup among objects registered in the
879      * transaction, then in persistent storage.
880      * @param id The identity
881      * @return The object
882      * @throws PersistenceBrokerException
883      */

884     public Object JavaDoc getObjectByIdentity(Identity id)
885             throws PersistenceBrokerException
886     {
887         checkOpen();
888         ObjectEnvelope envelope = objectEnvelopeTable.getByIdentity(id);
889         if (envelope != null)
890         {
891             return (envelope.needsDelete() ? null : envelope.getObject());
892         }
893         else
894         {
895             return getBroker().getObjectByIdentity(id);
896         }
897     }
898
899     /**
900      * Registers the object (without locking) with this transaction. This method
901      * expects that the object was already locked, no check is done!!!
902      */

903     void doSingleRegister(RuntimeObject rtObject, int lockMode)
904             throws LockNotGrantedException, PersistenceBrokerException
905     {
906         if(log.isDebugEnabled()) log.debug("Register object " + rtObject.getIdentity());
907         Object JavaDoc objectToRegister = rtObject.getObj();
908         /*
909         if the object is a Proxy there are two options:
910         1. The proxies real subject has already been materialized:
911            we take this real subject as the object to register and proceed
912            as if it were a ordinary object.
913         2. The real subject has not been materialized: Then there is nothing
914            to be registered now!
915            Of course we might just materialize the real subject to have something
916            to register. But this would make proxies useless for ODMG as proxies would
917            get materialized even if their real subjects were not used by the
918            client app.
919            Thus we register the current transaction as a Listener to the IndirectionHandler
920            of the Proxy.
921            Only when the IndirectionHandler performs the materialization of the real subject
922            at some later point in time it invokes callbacks on all it's listeners.
923            Using this callback we can defer the registering until it's really needed.
924         */

925         if(rtObject.isProxy())
926         {
927             IndirectionHandler handler = rtObject.getHandler();
928             if(handler == null)
929             {
930                 throw new OJBRuntimeException("Unexpected error, expect an proxy object as indicated: " + rtObject);
931             }
932             if (handler.alreadyMaterialized())
933             {
934                 objectToRegister = handler.getRealSubject();
935             }
936             else
937             {
938                 registerToIndirectionHandler(handler);
939                 registerUnmaterializedLocks(rtObject.getObj());
940                 // all work is done, so set to null
941
objectToRegister = null;
942             }
943         }
944         // no Proxy and is not null, register real object
945
if (objectToRegister != null)
946         {
947             ObjectEnvelope envelope = objectEnvelopeTable.getByIdentity(rtObject.getIdentity());
948             // if we found an envelope, object is already registered --> we do nothing
949
// than refreshing the object!
950
if ((envelope == null))
951             {
952                 // register object itself
953
envelope = objectEnvelopeTable.get(rtObject.getIdentity(), objectToRegister, rtObject.isNew());
954             }
955             else
956             {
957                 /*
958                 arminw:
959                 if an different instance of the same object was locked again
960                 we should replace the old instance with new one to make
961                 accessible the changed fields
962                 */

963                 envelope.refreshObjectIfNeeded(objectToRegister);
964             }
965             /*
966             arminw:
967             For better performance we check if this object has already a write lock
968             in this case we don't need to acquire a write lock on commit
969             */

970             if(lockMode == Transaction.WRITE)
971             {
972                 // signal ObjectEnvelope that a WRITE lock is already acquired
973
envelope.setWriteLocked(true);
974             }
975         }
976     }
977
978     /**
979      * we only use the registrationList map if the object is not a proxy. During the
980      * reference locking, we will materialize objects and they will enter the registered for
981      * lock map.
982      */

983     private void lockAndRegisterReferences(ClassDescriptor cld, Object JavaDoc sourceObject, int lockMode, List JavaDoc registeredObjects) throws LockNotGrantedException
984     {
985         if (implicitLocking)
986         {
987             Iterator JavaDoc i = cld.getObjectReferenceDescriptors(true).iterator();
988             while (i.hasNext())
989             {
990                 ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
991                 Object JavaDoc refObj = rds.getPersistentField().get(sourceObject);
992                 if (refObj != null)
993                 {
994                     boolean isProxy = ProxyHelper.isProxy(refObj);
995                     RuntimeObject rt = isProxy ? new RuntimeObject(refObj, this, false) : new RuntimeObject(refObj, this);
996                     if (!registrationList.contains(rt.getIdentity()))
997                     {
998                         lockAndRegister(rt, lockMode, registeredObjects);
999                     }
1000                }
1001            }
1002        }
1003    }
1004
1005    private void lockAndRegisterCollections(ClassDescriptor cld, Object JavaDoc sourceObject, int lockMode, List JavaDoc registeredObjects) throws LockNotGrantedException
1006    {
1007        if (implicitLocking)
1008        {
1009            Iterator JavaDoc i = cld.getCollectionDescriptors(true).iterator();
1010            while (i.hasNext())
1011            {
1012                CollectionDescriptor cds = (CollectionDescriptor) i.next();
1013                Object JavaDoc col = cds.getPersistentField().get(sourceObject);
1014                if (col != null)
1015                {
1016                    CollectionProxy proxy = ProxyHelper.getCollectionProxy(col);
1017                    if (proxy != null)
1018                    {
1019                        if (!proxy.isLoaded())
1020                        {
1021                            if (log.isDebugEnabled()) log.debug("adding self as listener to collection proxy");
1022                            proxy.addListener(this);
1023                            registeredCollectionProxies.add(proxy);
1024                            continue;
1025                        }
1026                    }
1027                    Iterator JavaDoc colIterator = BrokerHelper.getCollectionIterator(col);
1028                    Object JavaDoc item = null;
1029                    try
1030                    {
1031                        while (colIterator.hasNext())
1032                        {
1033                            item = colIterator.next();
1034                            RuntimeObject rt = new RuntimeObject(item, this);
1035                            if (rt.isProxy())
1036                            {
1037                                IndirectionHandler handler = ProxyHelper.getIndirectionHandler(item);
1038                                if (!handler.alreadyMaterialized())
1039                                {
1040                                    registerToIndirectionHandler(handler);
1041                                    continue;
1042                                }
1043                                else
1044                                {
1045                                    // @todo consider registering to hear when this is
1046
// derefernced instead of just loading here -bmc
1047
item = handler.getRealSubject();
1048                                }
1049                            }
1050                            if (!registrationList.contains(rt.getIdentity()))
1051                            {
1052                                lockAndRegister(rt, lockMode, registeredObjects);
1053                            }
1054                        }
1055                    }
1056                    catch (LockNotGrantedException e)
1057                    {
1058                        String JavaDoc eol = SystemUtils.LINE_SEPARATOR;
1059                        log.error("Lock not granted, while lock collection references[" +
1060                                eol + "current reference descriptor:" +
1061                                eol + cds.toXML() +
1062                                eol + "object to lock: " + item +
1063                                eol + "main object class: " + sourceObject.getClass().getName() +
1064                                eol + "]", e);
1065                        throw e;
1066                    }
1067                }
1068            }
1069        }
1070    }
1071
1072    /**
1073     * this callback is invoked before an Object is materialized
1074     * within an IndirectionHandler.
1075     * @param handler the invoking handler
1076     * @param oid the identity of the object to be materialized
1077     */

1078    public void beforeMaterialization(IndirectionHandler handler, Identity oid)
1079    {
1080        //noop
1081
}
1082
1083    /**
1084     * this callback is invoked after an Object is materialized
1085     * within an IndirectionHandler.
1086     * this callback allows to defer registration of objects until
1087     * it's really neccessary.
1088     * @param handler the invoking handler
1089     * @param materializedObject the materialized Object
1090     */

1091    public void afterMaterialization(IndirectionHandler handler, Object JavaDoc materializedObject)
1092    {
1093        try
1094        {
1095            Identity oid = handler.getIdentity();
1096            if (log.isDebugEnabled())
1097            log.debug("deferred registration: " + oid);
1098            if(!isOpen())
1099            {
1100                log.error("Proxy object materialization outside of a running tx, obj=" + oid);
1101                try{throw new Exception JavaDoc("Proxy object materialization outside of a running tx, obj=" + oid);}catch(Exception JavaDoc e)
1102                {
1103                e.printStackTrace();
1104                }
1105            }
1106            ClassDescriptor cld = getBroker().getClassDescriptor(materializedObject.getClass());
1107            RuntimeObject rt = new RuntimeObject(materializedObject, oid, cld, false, false);
1108            lockAndRegister(rt, Transaction.READ, isImplicitLocking(), getRegistrationList());
1109        }
1110        catch (Throwable JavaDoc t)
1111        {
1112            log.error("Register materialized object with this tx failed", t);
1113            throw new LockNotGrantedException(t.getMessage());
1114        }
1115        unregisterFromIndirectionHandler(handler);
1116    }
1117
1118    protected synchronized void unRegisterFromAllIndirectionHandlers()
1119    {
1120        // unregistering manipulates the registeredIndirectionHandlers vector
1121
// we have to loop through this vector to avoid index proplems.
1122
for (int i = registeredIndirectionHandlers.size() - 1; i >= 0; i--)
1123        {
1124            unregisterFromIndirectionHandler((IndirectionHandler) registeredIndirectionHandlers.get(i));
1125        }
1126    }
1127
1128    protected synchronized void unRegisterFromAllCollectionProxies()
1129    {
1130        for (int i = registeredCollectionProxies.size() - 1; i >= 0; i--)
1131        {
1132            unregisterFromCollectionProxy((CollectionProxy) registeredCollectionProxies.get(i));
1133        }
1134    }
1135
1136    protected synchronized void unregisterFromCollectionProxy(CollectionProxy handler)
1137    {
1138        handler.removeListener(this);
1139        registeredCollectionProxies.remove(handler);
1140    }
1141
1142    protected synchronized void unregisterFromIndirectionHandler(IndirectionHandler handler)
1143    {
1144        handler.removeListener(this);
1145        registeredIndirectionHandlers.remove(handler);
1146    }
1147
1148    protected synchronized void registerToIndirectionHandler(IndirectionHandler handler)
1149    {
1150        handler.addListener(this);
1151        registeredIndirectionHandlers.add(handler);
1152    }
1153
1154    /**
1155     * register proxy objects that were locked but haven't been materialized yet
1156     * so they can be unlocked when closing the transaction
1157     */

1158    protected void registerUnmaterializedLocks(Object JavaDoc obj)
1159    {
1160        unmaterializedLocks.add(obj);
1161    }
1162
1163    /**
1164     * Gets the broker associated with the transaction.
1165     * MBAIRD: only return the associated broker if the transaction is open,
1166     * if it's closed, throw a TransactionNotInProgressException. If we allow
1167     * brokers to be reaquired by an already closed transaction, there is a
1168     * very good chance the broker will be leaked as the doClose() method of
1169     * transactionImpl will never be called and thus the broker will never
1170     * be closed and returned to the pool.
1171     * @return Returns a PersistenceBroker
1172     * @throws TransactionNotInProgressException is the transaction is not open;
1173     */

1174    public PersistenceBrokerInternal getBrokerInternal()
1175    {
1176        if (broker == null || broker.isClosed())
1177        {
1178            checkOpen();
1179            try
1180            {
1181                checkForDB();
1182                broker = PersistenceBrokerFactoryFactory.instance().createPersistenceBroker(curDB.getPBKey());
1183            }
1184            catch (PBFactoryException e)
1185            {
1186                log.error("Cannot obtain PersistenceBroker from PersistenceBrokerFactory, " +
1187                        "found PBKey was " + curDB.getPBKey(), e);
1188                throw new PersistenceBrokerException(e);
1189            }
1190        }
1191        return broker;
1192    }
1193
1194    public PersistenceBroker getBroker()
1195    {
1196        return getBrokerInternal();
1197    }
1198
1199    /**
1200     * Returns true if an {@link org.apache.ojb.broker.PersistenceBroker} was associated with this
1201     * tx instance.
1202     */

1203    protected boolean hasBroker()
1204    {
1205        return broker != null && !broker.isClosed();
1206    }
1207
1208    protected void cleanupBroker()
1209    {
1210        if(hasBroker())
1211        {
1212            try
1213            {
1214                if(broker.isInTransaction())
1215                {
1216                    broker.abortTransaction();
1217                }
1218            }
1219            finally
1220            {
1221                broker.close();
1222                broker = null;
1223            }
1224        }
1225    }
1226
1227    /*
1228     * @see Configurable#configure(Configuration)
1229     */

1230    public void configure(Configuration config) throws ConfigurationException
1231    {
1232    }
1233
1234    /**
1235     * @see org.apache.ojb.odmg.TransactionExt#setImplicitLocking(boolean)
1236     */

1237    public synchronized void setImplicitLocking(boolean value)
1238    {
1239        implicitLocking = value;
1240    }
1241
1242    public boolean isImplicitLocking()
1243    {
1244        return implicitLocking;
1245    }
1246
1247    /**
1248     * noop -- here for interface
1249     */

1250    public void beforeLoading(CollectionProxyDefaultImpl colProxy)
1251    {
1252        // noop
1253
}
1254
1255    /**
1256     * Remove colProxy from list of pending collections and
1257     * register its contents with the transaction.
1258     */

1259    public void afterLoading(CollectionProxyDefaultImpl colProxy)
1260    {
1261        if (log.isDebugEnabled()) log.debug("loading a proxied collection a collection: " + colProxy);
1262        Collection JavaDoc data = colProxy.getData();
1263        for (Iterator JavaDoc iterator = data.iterator(); iterator.hasNext();)
1264        {
1265            Object JavaDoc o = iterator.next();
1266            if(!isOpen())
1267            {
1268                log.error("Collection proxy materialization outside of a running tx, obj=" + o);
1269                try{throw new Exception JavaDoc("Collection proxy materialization outside of a running tx, obj=" + o);}
1270                catch(Exception JavaDoc e)
1271                {e.printStackTrace();}
1272            }
1273            else
1274            {
1275                Identity oid = getBroker().serviceIdentity().buildIdentity(o);
1276                ClassDescriptor cld = getBroker().getClassDescriptor(ProxyHelper.getRealClass(o));
1277                RuntimeObject rt = new RuntimeObject(o, oid, cld, false, ProxyHelper.isProxy(o));
1278                lockAndRegister(rt, Transaction.READ, isImplicitLocking(), getRegistrationList());
1279            }
1280        }
1281        unregisterFromCollectionProxy(colProxy);
1282    }
1283
1284    protected void performTransactionAwareBeforeCommit()
1285    {
1286        Enumeration JavaDoc en = objectEnvelopeTable.elements();
1287        while (en.hasMoreElements())
1288        {
1289            ((ObjectEnvelope) en.nextElement()).beforeCommit();
1290        }
1291    }
1292    protected void performTransactionAwareAfterCommit()
1293    {
1294        Enumeration JavaDoc en = objectEnvelopeTable.elements();
1295        try
1296        {
1297            while (en.hasMoreElements())
1298            {
1299                ((ObjectEnvelope) en.nextElement()).afterCommit();
1300            }
1301        }
1302        catch(Exception JavaDoc e)
1303        {
1304            log.error("Unexpected error while perform 'TransactionAware#afterCommit()' listener after commit of objects," +
1305                    " after commit you can't rollback - exception will be skipped.", e);
1306        }
1307    }
1308    protected void performTransactionAwareBeforeRollback()
1309    {
1310        Enumeration JavaDoc en = objectEnvelopeTable.elements();
1311        while (en.hasMoreElements())
1312        {
1313            try
1314            {
1315                ((ObjectEnvelope) en.nextElement()).beforeAbort();
1316            }
1317            catch(Exception JavaDoc e)
1318            {
1319                log.error("Unexpected error while perform 'TransactionAware#beforeAbort()' listener before rollback of objects" +
1320                    " - exception will be skipped to complete rollback.", e);
1321            }
1322        }
1323    }
1324    protected void performTransactionAwareAfterRollback()
1325    {
1326        Enumeration JavaDoc en = objectEnvelopeTable.elements();
1327        try
1328        {
1329            while (en.hasMoreElements())
1330            {
1331                ((ObjectEnvelope) en.nextElement()).afterAbort();
1332            }
1333        }
1334        catch(Exception JavaDoc e)
1335        {
1336            log.error("Unexpected error while perform 'TransactionAware#afterAbort()' listener after rollback of objects" +
1337                    " - exception will be skipped.", e);
1338        }
1339    }
1340
1341    /**
1342     * Detect new objects.
1343     */

1344    protected boolean isTransient(ClassDescriptor cld, Object JavaDoc obj, Identity oid)
1345    {
1346        // if the Identity is transient we assume a non-persistent object
1347
boolean isNew = oid != null && oid.isTransient();
1348        /*
1349        detection of new objects is costly (select of ID in DB to check if object
1350        already exists) we do:
1351        a. check if the object has nullified PK field
1352        b. check if the object is already registered
1353        c. lookup from cache and if not found, last option select on DB
1354        */

1355        if(!isNew)
1356        {
1357            final PersistenceBroker pb = getBroker();
1358            if(cld == null)
1359            {
1360                cld = pb.getClassDescriptor(obj.getClass());
1361            }
1362            isNew = pb.serviceBrokerHelper().hasNullPKField(cld, obj);
1363            if(!isNew)
1364            {
1365                if(oid == null)
1366                {
1367                    oid = pb.serviceIdentity().buildIdentity(cld, obj);
1368                }
1369                final ObjectEnvelope mod = objectEnvelopeTable.getByIdentity(oid);
1370                if(mod != null)
1371                {
1372                    // already registered object, use current state
1373
isNew = mod.needsInsert();
1374                }
1375                else
1376                {
1377                    // if object was found cache, assume it's old
1378
// else make costly check against the DB
1379
isNew = pb.serviceObjectCache().lookup(oid) == null
1380                            && !pb.serviceBrokerHelper().doesExist(cld, oid, obj);
1381                }
1382            }
1383        }
1384        return isNew;
1385    }
1386
1387    /**
1388     * Allows to change the <em>cascading delete</em> behavior of the specified reference
1389     * of the target class while this transaction is in use.
1390     *
1391     * @param target The class to change cascading delete behavior of the references.
1392     * @param referenceField The field name of the 1:1, 1:n or 1:n reference.
1393     * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled.
1394     */

1395    public void setCascadingDelete(Class JavaDoc target, String JavaDoc referenceField, boolean doCascade)
1396    {
1397        ClassDescriptor cld = getBroker().getClassDescriptor(target);
1398        ObjectReferenceDescriptor ord = cld.getObjectReferenceDescriptorByName(referenceField);
1399        if(ord == null)
1400        {
1401            ord = cld.getCollectionDescriptorByName(referenceField);
1402        }
1403        if(ord == null)
1404        {
1405            throw new CascadeSettingException("Invalid reference field name '" + referenceField
1406                    + "', can't find 1:1, 1:n or m:n relation with that name in " + target);
1407        }
1408        runtimeCascadeDeleteMap.put(ord, (doCascade ? Boolean.TRUE : Boolean.FALSE));
1409    }
1410
1411    /**
1412     * Allows to change the <em>cascading delete</em> behavior of all references of the
1413     * specified class while this transaction is in use - if the specified class is an
1414     * interface, abstract class or class with "extent" classes the cascading flag will
1415     * be propagated.
1416     *
1417     * @param target The class to change cascading delete behavior of all references.
1418     * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled.
1419     */

1420    public void setCascadingDelete(Class JavaDoc target, boolean doCascade)
1421    {
1422        ClassDescriptor cld = getBroker().getClassDescriptor(target);
1423        List JavaDoc extents = cld.getExtentClasses();
1424        Boolean JavaDoc result = doCascade ? Boolean.TRUE : Boolean.FALSE;
1425        setCascadingDelete(cld, result);
1426        if(extents != null && extents.size() > 0)
1427        {
1428            for(int i = 0; i < extents.size(); i++)
1429            {
1430                Class JavaDoc extent = (Class JavaDoc) extents.get(i);
1431                ClassDescriptor tmp = getBroker().getClassDescriptor(extent);
1432                setCascadingDelete(tmp, result);
1433            }
1434        }
1435    }
1436
1437    private void setCascadingDelete(ClassDescriptor cld, Boolean JavaDoc cascade)
1438    {
1439        List JavaDoc singleRefs = cld.getObjectReferenceDescriptors(true);
1440        for(int i = 0; i < singleRefs.size(); i++)
1441        {
1442            Object JavaDoc o = singleRefs.get(i);
1443            runtimeCascadeDeleteMap.put(o, cascade);
1444        }
1445        List JavaDoc collectionRefs = cld.getCollectionDescriptors(true);
1446        for(int i = 0; i < collectionRefs.size(); i++)
1447        {
1448            Object JavaDoc o = collectionRefs.get(i);
1449            runtimeCascadeDeleteMap.put(o, cascade);
1450        }
1451    }
1452
1453    private HashMap JavaDoc runtimeCascadeDeleteMap = new HashMap JavaDoc();
1454    /**
1455     * Returns <em>true</em> if cascading delete is enabled for the specified
1456     * single or collection descriptor.
1457     */

1458    protected boolean cascadeDeleteFor(ObjectReferenceDescriptor ord)
1459    {
1460        boolean result;
1461        Boolean JavaDoc runtimeSetting = (Boolean JavaDoc) runtimeCascadeDeleteMap.get(ord);
1462        if(runtimeSetting == null)
1463        {
1464            /*
1465            arminw: Here we use the auto-delete flag defined in metadata
1466            */

1467            result = ord.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT;
1468        }
1469        else
1470        {
1471            result = runtimeSetting.booleanValue();
1472        }
1473        return result;
1474    }
1475
1476    int getImpliciteLockType(int parentLockMode)
1477    {
1478        return (parentLockMode == Transaction.WRITE && impliciteWriteLocks) ? Transaction.WRITE : Transaction.READ;
1479    }
1480
1481    /**
1482     * Return <em>true</em> if the OJB ordering algorithm is enabled.
1483     * @see #setOrdering(boolean)
1484     */

1485    public boolean isOrdering()
1486    {
1487        return ordering;
1488    }
1489
1490    /**
1491     * Allows to enable/disable the OJB persistent object ordering algorithm. If
1492     * <em>true</em> OJB try to order the modified/new/deleted objects in a correct order
1493     * (based on a algorithm) before the objects are written to the persistent storage.
1494     * <br/>
1495     * If <em>false</em> the order of the objects rely on the order specified by
1496     * the user and on settings like {@link #setImplicitLocking(boolean)}.
1497     *
1498     * @param ordering Set <em>true</em> to enable object ordering on commit.
1499     */

1500    public void setOrdering(boolean ordering)
1501    {
1502        this.ordering = ordering;
1503    }
1504
1505
1506    //============================================================
1507
// inner class
1508
//============================================================
1509
/**
1510     * This was thrown when something wrong with the cascading delete setting.
1511     */

1512    static class CascadeSettingException extends OJBRuntimeException
1513    {
1514        public CascadeSettingException()
1515        {
1516        }
1517
1518        public CascadeSettingException(String JavaDoc msg)
1519        {
1520            super(msg);
1521        }
1522
1523        public CascadeSettingException(Throwable JavaDoc cause)
1524        {
1525            super(cause);
1526        }
1527
1528        public CascadeSettingException(String JavaDoc msg, Throwable JavaDoc cause)
1529        {
1530            super(msg, cause);
1531        }
1532    }
1533}
1534
Popular Tags