KickJava   Java API By Example, From Geeks To Geeks.

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


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 java.util.ArrayList JavaDoc;
19 import java.util.Enumeration JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.apache.commons.lang.builder.ToStringBuilder;
26 import org.apache.commons.lang.builder.ToStringStyle;
27 import org.apache.commons.lang.SystemUtils;
28 import org.apache.ojb.broker.Identity;
29 import org.apache.ojb.broker.OJBRuntimeException;
30 import org.apache.ojb.broker.OptimisticLockException;
31 import org.apache.ojb.broker.PersistenceBroker;
32 import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
33 import org.apache.ojb.broker.core.proxy.CollectionProxy;
34 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
35 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
36 import org.apache.ojb.broker.core.proxy.ProxyHelper;
37 import org.apache.ojb.broker.metadata.ClassDescriptor;
38 import org.apache.ojb.broker.metadata.CollectionDescriptor;
39 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
40 import org.apache.ojb.broker.util.BrokerHelper;
41 import org.apache.ojb.broker.util.logging.Logger;
42 import org.apache.ojb.broker.util.logging.LoggerFactory;
43 import org.apache.ojb.odmg.link.LinkEntry;
44 import org.apache.ojb.odmg.link.LinkEntryMtoN;
45 import org.apache.ojb.odmg.states.StateOldClean;
46 import org.odmg.LockNotGrantedException;
47 import org.odmg.ODMGRuntimeException;
48 import org.odmg.Transaction;
49 import org.odmg.TransactionAbortedException;
50
51 /**
52  * manages all ObjectEnvelopes included by a transaction.
53  * Performs commit, and rollack operations on all included Envelopes.
54  *
55  * @author Thomas Mahler
56  * @author <a HREF="mailto:mattbaird@yahoo.com">Matthew Baird</a>
57  *
58  * MBAIRD: added explicit closing and de-referencing to prevent any
59  * GC issues.
60  */

61 public class ObjectEnvelopeTable
62 {
63     private Logger log = LoggerFactory.getLogger(ObjectEnvelopeTable.class);
64     private TransactionImpl transaction;
65
66     /**
67      * A list of {@link org.apache.ojb.broker.Identity} objects which are
68      * new associated with an object and should be protected from being marked
69      * as "delete". E.g. if a collection reference C is moved from object A1 to A2,
70      * then A1 wants to "delete" C and A2 wants to mark the new C object as "new".
71      */

72     private List JavaDoc newAssociatedIdentites = new ArrayList JavaDoc();
73     private List JavaDoc m2nLinkList = new ArrayList JavaDoc();
74     private List JavaDoc m2nUnlinkList = new ArrayList JavaDoc();
75     private List JavaDoc markedForDeletionList = new ArrayList JavaDoc();
76     private List JavaDoc markedForInsertList = new ArrayList JavaDoc();
77
78     /** the internal table mapping Objects to their ObjectTransactionWrappers */
79     private Map JavaDoc mhtObjectEnvelopes = new HashMap JavaDoc();
80
81     /**
82      * a vector containing the ObjectEnvelope objects representing modifications
83      * in the order they were added. If an ObjectEnvelope is added twice, only
84      * the the second addition is ignored.
85      */

86     private ArrayList JavaDoc mvOrderOfIds = new ArrayList JavaDoc();
87
88     /** marker used to avoid superfluous reordering and commiting */
89     private boolean needsCommit = false;
90
91     /** Creates new ObjectEnvelopeTable */
92     public ObjectEnvelopeTable(TransactionImpl myTransaction)
93     {
94         transaction = myTransaction;
95     }
96
97     TransactionImpl getTransaction()
98     {
99         return transaction;
100     }
101
102     /** prepare this instance for reuse */
103     public void refresh()
104     {
105         needsCommit = false;
106         mhtObjectEnvelopes = new HashMap JavaDoc();
107         mvOrderOfIds = new ArrayList JavaDoc();
108         afterWriteCleanup();
109     }
110
111     void afterWriteCleanup()
112     {
113         m2nLinkList.clear();
114         m2nUnlinkList.clear();
115         newAssociatedIdentites.clear();
116         markedForDeletionList.clear();
117         markedForInsertList.clear();
118     }
119
120     /**
121      * Perform write to DB on all registered object wrapper ({@link ObjectEnvelope})
122      *
123      * @param reuse When all registered objects be re-used after writing to
124      * DB set <em>true</em>, else set <em>false</em> to improve performance.
125      */

126     public void writeObjects(boolean reuse) throws TransactionAbortedException, LockNotGrantedException
127     {
128         PersistenceBroker broker = transaction.getBroker();
129         ConnectionManagerIF connMan = broker.serviceConnectionManager();
130         boolean saveBatchMode = connMan.isBatchMode();
131
132         try
133         {
134             if(log.isDebugEnabled())
135             {
136                 log.debug(
137                         "PB is in internal tx: "
138                                 + broker.isInTransaction()
139                                 + " broker was: "
140                                 + broker);
141             }
142             // all neccessary db operations are executed within a PersistenceBroker transaction:
143
if(!broker.isInTransaction())
144             {
145                 log.error("PB associated with current odmg-tx is not in tx");
146                 throw new TransactionAbortedException("Underlying PB is not in tx, was begin call done before commit?");
147             }
148
149             // Committing has to be done in two phases. First implicitly upgrade to lock on all related
150
// objects of objects in this transaction. Then the list of locked objects has to be
151
// reordered to solve referential integrity dependencies, then the objects are
152
// written into the database.
153

154             // 0. turn on the batch mode
155
connMan.setBatchMode(true);
156
157             // 1. mark objects no longer available in collection
158
// for delete and add new found objects
159
checkAllEnvelopes(broker);
160
161             // 2. mark all dependend objects for cascading insert/delete
162
cascadingDependents();
163
164             // 3. upgrade implicit locks.
165
//upgradeImplicitLocksAndCheckIfCommitIsNeeded();
166
upgradeLockIfNeeded();
167
168             // 4. Reorder objects
169
reorder();
170 // System.out.println("## ordering: ");
171
// for(int i = 0; i < mvOrderOfIds.size(); i++)
172
// {
173
// System.out.println("" + mvOrderOfIds.get(i));
174
// }
175
// System.out.println("## ordering end");
176

177             // 5. write objects.
178
writeAllEnvelopes(reuse);
179
180             // 6. execute batch
181
connMan.executeBatch();
182
183             // 7. Update all Envelopes to new CleanState
184
prepareForReuse(reuse);
185
186             // 6. commit cleanup
187
afterWriteCleanup();
188
189         }
190         catch(Exception JavaDoc e)
191         {
192             connMan.clearBatch();
193             /*
194             arminw:
195             log only a warn message, because in top-level methods
196             a error log will be done ditto
197             */

198             if(e instanceof OptimisticLockException)
199             {
200                 // make error log to show the full stack trace one time
201
log.error("Optimistic lock exception while write objects", e);
202                 // PB OptimisticLockException should be clearly signalled to the user
203
Object JavaDoc sourceObject = ((OptimisticLockException) e).getSourceObject();
204                 throw new LockNotGrantedException("Optimistic lock exception occur, source object was (" + sourceObject + ")," +
205                         " message was (" + e.getMessage() + ")");
206             }
207             else if(!(e instanceof RuntimeException JavaDoc))
208             {
209                 log.warn("Error while write objects for tx " + transaction, e);
210                 throw new ODMGRuntimeException("Unexpected error while write objects: " + e.getMessage());
211             }
212             else
213             {
214                 log.warn("Error while write objects for tx " + transaction, e);
215                 throw (RuntimeException JavaDoc) e;
216             }
217         }
218         finally
219         {
220             needsCommit = false;
221             connMan.setBatchMode(saveBatchMode);
222         }
223     }
224
225     /** commit all envelopes against the current broker */
226     private void writeAllEnvelopes(boolean reuse)
227     {
228         // perform remove of m:n indirection table entries first
229
performM2NUnlinkEntries();
230
231         Iterator JavaDoc iter;
232         // using clone to avoid ConcurentModificationException
233
iter = ((List JavaDoc) mvOrderOfIds.clone()).iterator();
234         while(iter.hasNext())
235         {
236             ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
237             boolean insert = false;
238             if(needsCommit)
239             {
240                 insert = mod.needsInsert();
241                 mod.getModificationState().commit(mod);
242                 if(reuse && insert)
243                 {
244                     getTransaction().doSingleLock(mod.getClassDescriptor(), mod.getObject(), mod.getIdentity(), Transaction.WRITE);
245                 }
246             }
247             /*
248             arminw: important to call this cleanup method for each registered
249             ObjectEnvelope, because this method will e.g. remove proxy listener
250             objects for registered objects.
251             */

252             mod.cleanup(reuse, insert);
253         }
254         // add m:n indirection table entries
255
performM2NLinkEntries();
256     }
257
258     /**
259      * Mark objects no longer available in collection for delete and new objects for insert.
260      *
261      * @param broker the PB to persist all objects
262      */

263     private void checkAllEnvelopes(PersistenceBroker broker)
264     {
265         Iterator JavaDoc iter = ((List JavaDoc) mvOrderOfIds.clone()).iterator();
266         while(iter.hasNext())
267         {
268             ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
269             // only non transient objects should be performed
270
if(!mod.getModificationState().isTransient())
271             {
272                 mod.markReferenceElements(broker);
273             }
274         }
275     }
276
277     /**
278      * This method have to be called to reuse all registered {@link ObjectEnvelope}
279      * objects after transaction commit/flush/checkpoint call.
280      */

281     private void prepareForReuse(boolean reuse)
282     {
283         if(reuse)
284         {
285             // using clone to avoid ConcurentModificationException
286
Iterator JavaDoc iter = ((List JavaDoc) mvOrderOfIds.clone()).iterator();
287             while(iter.hasNext())
288             {
289                 ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
290                 if(!needsCommit || (mod.getModificationState() == StateOldClean.getInstance()
291                         || mod.getModificationState().isTransient()))
292                 {
293                     // nothing to do
294
}
295                 else
296                 {
297                     mod.setModificationState(mod.getModificationState().markClean());
298                 }
299             }
300         }
301     }
302
303     /**
304      * Checks the status of all modified objects and
305      * upgrade the lock if needed, cleanup the {@link ObjectEnvelope}
306      * objects.
307      */

308     private void upgradeLockIfNeeded()
309     {
310         // using clone to avoid ConcurentModificationException
311
Iterator JavaDoc iter = ((List JavaDoc) mvOrderOfIds.clone()).iterator();
312         TransactionImpl tx = getTransaction();
313         ObjectEnvelope mod;
314         while(iter.hasNext())
315         {
316             mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
317             // ignore transient objects
318
if(!mod.getModificationState().isTransient())
319             {
320                 /*
321                 now we check if all modified objects has a write lock. On insert of new
322                 objects we don't need a write lock.
323                 */

324                 if(!mod.needsInsert())
325                 {
326                     if((mod.needsDelete() || mod.needsUpdate()
327                             || mod.hasChanged(tx.getBroker())))
328                     {
329                         needsCommit = true;
330                         // mark object dirty
331
mod.setModificationState(mod.getModificationState().markDirty());
332                         ClassDescriptor cld = mod.getClassDescriptor();
333                         // if the object isn't already locked, we will do it now
334
if(!mod.isWriteLocked())
335                         {
336                             tx.doSingleLock(cld, mod.getObject(), mod.getIdentity(), Transaction.WRITE);
337                         }
338                     }
339                 }
340                 else
341                 {
342                     needsCommit = true;
343                 }
344             }
345         }
346     }
347
348     /** perform rollback on all tx-states */
349     public void rollback()
350     {
351         try
352         {
353             Iterator JavaDoc iter = mvOrderOfIds.iterator();
354             while(iter.hasNext())
355             {
356                 ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
357                 if(log.isDebugEnabled())
358                     log.debug("rollback: " + mod);
359                 // if the Object has been modified by transaction, mark object as dirty
360
if(mod.hasChanged(transaction.getBroker()))
361                 {
362                     mod.setModificationState(mod.getModificationState().markDirty());
363                 }
364                 mod.getModificationState().rollback(mod);
365             }
366         }
367         finally
368         {
369             needsCommit = false;
370         }
371         afterWriteCleanup();
372     }
373
374     /** remove an objects entry from the object registry */
375     public void remove(Object JavaDoc pKey)
376     {
377         Identity id;
378         if(pKey instanceof Identity)
379         {
380             id = (Identity) pKey;
381         }
382         else
383         {
384             id = transaction.getBroker().serviceIdentity().buildIdentity(pKey);
385         }
386         mhtObjectEnvelopes.remove(id);
387         mvOrderOfIds.remove(id);
388     }
389
390     /**
391      * Get an enumeration of all the elements in this ObjectEnvelopeTable
392      * in random order.
393      *
394      * @return Enumeration an enumeration of all elements managed by this ObjectEnvelopeTable
395      */

396     public Enumeration JavaDoc elements()
397     {
398         return java.util.Collections.enumeration(mhtObjectEnvelopes.values());
399     }
400
401     /** retrieve an objects ObjectModification state from the hashtable */
402     public ObjectEnvelope getByIdentity(Identity id)
403     {
404         return (ObjectEnvelope) mhtObjectEnvelopes.get(id);
405     }
406
407     /**
408      * retrieve an objects ObjectEnvelope state from the hashtable.
409      * If no ObjectEnvelope is found, a new one is created and returned.
410      *
411      * @return the resulting ObjectEnvelope
412      */

413     public ObjectEnvelope get(Object JavaDoc pKey, boolean isNew)
414     {
415         PersistenceBroker broker = transaction.getBroker();
416         Identity oid = broker.serviceIdentity().buildIdentity(pKey);
417         return get(oid, pKey, isNew);
418     }
419
420     /**
421      * retrieve an objects ObjectEnvelope state from the hashtable.
422      * If no ObjectEnvelope is found, a new one is created and returned.
423      *
424      * @return the resulting ObjectEnvelope
425      */

426     public ObjectEnvelope get(Identity oid, Object JavaDoc pKey, boolean isNew)
427     {
428         ObjectEnvelope result = getByIdentity(oid);
429         if(result == null)
430         {
431             result = new ObjectEnvelope(this, oid, pKey, isNew);
432             mhtObjectEnvelopes.put(oid, result);
433             mvOrderOfIds.add(oid);
434             if(log.isDebugEnabled())
435                 log.debug("register: " + result);
436         }
437         return result;
438     }
439
440     /** Returns a String representation of this object */
441     public String JavaDoc toString()
442     {
443         ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
444         String JavaDoc eol = SystemUtils.LINE_SEPARATOR;
445         buf.append("# ObjectEnvelopeTable dump:" + eol + "start[");
446         Enumeration JavaDoc en = elements();
447         while(en.hasMoreElements())
448         {
449             ObjectEnvelope mod = (ObjectEnvelope) en.nextElement();
450             buf.append(mod.toString() + eol);
451         }
452         buf.append("]end");
453         return buf.toString();
454     }
455
456     /** retrieve an objects ObjectModification state from the hashtable */
457     public boolean contains(Identity oid)
458     {
459         //Integer keyInteger = new Integer(System.identityHashCode(key));
460
return mhtObjectEnvelopes.containsKey(oid);
461     }
462
463     /** Reorder the objects in the table to resolve referential integrity dependencies. */
464     private void reorder()
465     {
466         if(getTransaction().isOrdering() && needsCommit && mhtObjectEnvelopes.size() > 1)
467         {
468             ObjectEnvelopeOrdering ordering = new ObjectEnvelopeOrdering(mvOrderOfIds, mhtObjectEnvelopes);
469             ordering.reorder();
470             Identity[] newOrder = ordering.getOrdering();
471
472             mvOrderOfIds.clear();
473             for(int i = 0; i < newOrder.length; i++)
474             {
475                 mvOrderOfIds.add(newOrder[i]);
476             }
477         }
478     }
479
480     void cascadingDependents()
481     {
482         Iterator JavaDoc it = mhtObjectEnvelopes.values().iterator();
483         ObjectEnvelope mod;
484         // first we search for all deleted/insert objects
485
while(it.hasNext())
486         {
487             mod = (ObjectEnvelope) it.next();
488             if(mod.needsDelete())
489             {
490                 addForDeletionDependent(mod);
491             }
492             else if(mod.needsInsert())
493             {
494                 addForInsertDependent(mod);
495             }
496         }
497         /*
498         Now start cascade insert/delete work. The order of first delete
499         then insert is mandatory, because the user could move unmaterialized
500         collection proxy objects from one existing, which was deleted, to a new object. In this case
501         the proxy was materialized on deletion of the main object, but on performing
502         the cascading insert the collection objects will be found and assigned to the new object.
503         */

504         cascadeMarkedForDeletion();
505         cascadeMarkedForInsert();
506     }
507
508     void addNewAssociatedIdentity(Identity oid)
509     {
510         newAssociatedIdentites.add(oid);
511     }
512
513     boolean isNewAssociatedObject(Identity oid)
514     {
515         return newAssociatedIdentites.contains(oid);
516     }
517
518     void addForInsertDependent(ObjectEnvelope mod)
519     {
520         markedForInsertList.add(mod);
521     }
522
523     /** Starts recursive insert on all insert objects object graph */
524     private void cascadeMarkedForInsert()
525     {
526         // This list was used to avoid endless recursion on circular references
527
List JavaDoc alreadyPrepared = new ArrayList JavaDoc();
528         for(int i = 0; i < markedForInsertList.size(); i++)
529         {
530             ObjectEnvelope mod = (ObjectEnvelope) markedForInsertList.get(i);
531             // only if a new object was found we cascade to register the dependent objects
532
if(mod.needsInsert())
533             {
534                 cascadeInsertFor(mod, alreadyPrepared);
535                 alreadyPrepared.clear();
536             }
537         }
538         markedForInsertList.clear();
539     }
540
541     /**
542      * Walk through the object graph of the specified insert object. Was used for
543      * recursive object graph walk.
544      */

545     private void cascadeInsertFor(ObjectEnvelope mod, List JavaDoc alreadyPrepared)
546     {
547         // avoid endless recursion, so use List for registration
548
if(alreadyPrepared.contains(mod.getIdentity())) return;
549         alreadyPrepared.add(mod.getIdentity());
550
551         ClassDescriptor cld = getTransaction().getBroker().getClassDescriptor(mod.getObject().getClass());
552
553         List JavaDoc refs = cld.getObjectReferenceDescriptors(true);
554         cascadeInsertSingleReferences(mod, refs, alreadyPrepared);
555
556         List JavaDoc colls = cld.getCollectionDescriptors(true);
557         cascadeInsertCollectionReferences(mod, colls, alreadyPrepared);
558     }
559
560     private void cascadeInsertSingleReferences(ObjectEnvelope source, List JavaDoc descriptor, List JavaDoc alreadyPrepared)
561     {
562         for(int i = 0; i < descriptor.size(); i++)
563         {
564             ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptor.get(i);
565             Object JavaDoc depObj = ord.getPersistentField().get(source.getObject());
566
567             if(depObj != null)
568             {
569                 // in any case we have to link the source object when the object needs insert
570
source.addLinkOneToOne(ord, false);
571
572                 IndirectionHandler handler = ProxyHelper.getIndirectionHandler(depObj);
573                 // if the object is not materialized, nothing has changed
574
if(handler == null || handler.alreadyMaterialized())
575                 {
576                     RuntimeObject rt;
577                     // if materialized
578
if(handler != null)
579                     {
580                         rt = new RuntimeObject(handler.getRealSubject(), getTransaction(), false);
581                     }
582                     else
583                     {
584                         rt = new RuntimeObject(depObj, getTransaction());
585                     }
586                     Identity oid = rt.getIdentity();
587                     if(!alreadyPrepared.contains(oid))
588                     {
589                         ObjectEnvelope depMod = getByIdentity(oid);
590                         // if the object isn't registered and is a new object, register it
591
// else we have nothing to do
592
if(depMod == null && rt.isNew())
593                         {
594                             getTransaction().lockAndRegister(rt, Transaction.WRITE, false, getTransaction().getRegistrationList());
595                             depMod = getByIdentity(oid);
596                             cascadeInsertFor(depMod, alreadyPrepared);
597                         }
598                     }
599                 }
600             }
601         }
602     }
603
604     private void cascadeInsertCollectionReferences(ObjectEnvelope source, List JavaDoc descriptor, List JavaDoc alreadyPrepared)
605     {
606         // PersistenceBroker pb = getTransaction().getBroker();
607
for(int i = 0; i < descriptor.size(); i++)
608         {
609             CollectionDescriptor col = (CollectionDescriptor) descriptor.get(i);
610             Object JavaDoc collOrArray = col.getPersistentField().get(source.getObject());
611             CollectionProxy proxy = ProxyHelper.getCollectionProxy(collOrArray);
612             /*
613             on insert we perform only materialized collection objects. This should be
614             sufficient, because in method #cascadingDependents() we make sure that on
615             move of unmaterialized collection objects the proxy was materialized if needed.
616             */

617             if(proxy == null && collOrArray != null)
618             {
619                 Iterator JavaDoc it = BrokerHelper.getCollectionIterator(collOrArray);
620                 while(it.hasNext())
621                 {
622                     Object JavaDoc colObj = it.next();
623                     if(colObj != null)
624                     {
625                         RuntimeObject rt = new RuntimeObject(colObj, getTransaction());
626                         Identity oid = rt.getIdentity();
627                         /*
628                         arminw:
629                         only when the main object need insert we start with FK assignment
630                         of the 1:n and m:n relations. If the main objects need update (was already persisted)
631                         it should be handled by the object state detection in ObjectEnvelope
632                         */

633                         if(source.needsInsert())
634                         {
635                             /*
636                             arminw:
637                             TODO: what is the valid way to go, when the collection object itself is
638                             a unmaterialized proxy object? Think in this case we should materialize the
639                             object when the main object needs insert, because we have to assign the FK values
640                             to the main object
641                             */

642                             colObj = ProxyHelper.getRealObject(colObj);
643                             ObjectEnvelope oe = getByIdentity(oid);
644                             if(oe == null)
645                             {
646                                 getTransaction().lockAndRegister(rt, Transaction.WRITE, false, getTransaction().getRegistrationList());
647                                 oe = getByIdentity(oid);
648                             }
649                             if(col.isMtoNRelation())
650                             {
651                                 // the main objects needs insert, thus add new m:n link
652
addM2NLinkEntry(col, source.getObject(), colObj);
653                             }
654                             else
655                             {
656                                 // we mark collection reference for linking
657
oe.addLinkOneToN(col, source.getObject(), false);
658                                 /*
659                                 arminw: The referenced object could be already persisted, so we have
660                                 to dirty it to guarantee the setting of the FK (linking)
661                                 */

662                                 oe.setModificationState(oe.getModificationState().markDirty());
663                             }
664                             cascadeInsertFor(oe, alreadyPrepared);
665                         }
666                     }
667                 }
668             }
669         }
670     }
671
672     void addForDeletionDependent(ObjectEnvelope mod)
673     {
674         markedForDeletionList.add(mod);
675     }
676
677     /** Starts recursive delete on all delete objects object graph */
678     private void cascadeMarkedForDeletion()
679     {
680         List JavaDoc alreadyPrepared = new ArrayList JavaDoc();
681         for(int i = 0; i < markedForDeletionList.size(); i++)
682         {
683             ObjectEnvelope mod = (ObjectEnvelope) markedForDeletionList.get(i);
684             // if the object wasn't associated with another object, start cascade delete
685
if(!isNewAssociatedObject(mod.getIdentity()))
686             {
687                 cascadeDeleteFor(mod, alreadyPrepared);
688                 alreadyPrepared.clear();
689             }
690         }
691         markedForDeletionList.clear();
692     }
693
694     /**
695      * Walk through the object graph of the specified delete object. Was used for
696      * recursive object graph walk.
697      */

698     private void cascadeDeleteFor(ObjectEnvelope mod, List JavaDoc alreadyPrepared)
699     {
700         // avoid endless recursion
701
if(alreadyPrepared.contains(mod.getIdentity())) return;
702
703         alreadyPrepared.add(mod.getIdentity());
704
705         ClassDescriptor cld = getTransaction().getBroker().getClassDescriptor(mod.getObject().getClass());
706
707         List JavaDoc refs = cld.getObjectReferenceDescriptors(true);
708         cascadeDeleteSingleReferences(mod, refs, alreadyPrepared);
709
710         List JavaDoc colls = cld.getCollectionDescriptors(true);
711         cascadeDeleteCollectionReferences(mod, colls, alreadyPrepared);
712     }
713
714     private void cascadeDeleteSingleReferences(ObjectEnvelope source, List JavaDoc descriptor, List JavaDoc alreadyPrepared)
715     {
716         for(int i = 0; i < descriptor.size(); i++)
717         {
718             ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptor.get(i);
719             if(getTransaction().cascadeDeleteFor(ord))
720             {
721                 Object JavaDoc depObj = ord.getPersistentField().get(source.getObject());
722                 if(depObj != null)
723                 {
724                     Identity oid = getTransaction().getBroker().serviceIdentity().buildIdentity(depObj);
725                     // if(!isNewAssociatedObject(oid) && !alreadyPrepared.contains(oid))
726
// if the object has a new association with a different object, don't delete it
727
if(!isNewAssociatedObject(oid))
728                     {
729                         ObjectEnvelope depMod = get(oid, depObj, false);
730                         depMod.setModificationState(depMod.getModificationState().markDelete());
731                         cascadeDeleteFor(depMod, alreadyPrepared);
732                     }
733                 }
734             }
735         }
736     }
737
738     private void cascadeDeleteCollectionReferences(ObjectEnvelope source, List JavaDoc descriptor, List JavaDoc alreadyPrepared)
739     {
740         PersistenceBroker pb = getTransaction().getBroker();
741         for(int i = 0; i < descriptor.size(); i++)
742         {
743             CollectionDescriptor col = (CollectionDescriptor) descriptor.get(i);
744             boolean cascadeDelete = getTransaction().cascadeDeleteFor(col);
745             Object JavaDoc collOrArray = col.getPersistentField().get(source.getObject());
746             // TODO: remove cast
747
CollectionProxyDefaultImpl proxy = (CollectionProxyDefaultImpl) ProxyHelper.getCollectionProxy(collOrArray);
748             // on delete we have to materialize dependent objects
749
if(proxy != null)
750             {
751                 collOrArray = proxy.getData();
752             }
753             if(collOrArray != null)
754             {
755                 Iterator JavaDoc it = BrokerHelper.getCollectionIterator(collOrArray);
756                 while(it.hasNext())
757                 {
758                     Object JavaDoc colObj = ProxyHelper.getRealObject(it.next());
759                     Identity oid = pb.serviceIdentity().buildIdentity(colObj);
760                     ObjectEnvelope colMod = get(oid, colObj, false);
761                     if(cascadeDelete)
762                     {
763                         colMod.setModificationState(colMod.getModificationState().markDelete());
764                         cascadeDeleteFor(colMod, alreadyPrepared);
765                     }
766                     else
767                     {
768                         if(!col.isMtoNRelation())
769                         {
770                             colMod.addLinkOneToN(col, source.getObject(), true);
771                             colMod.setModificationState(colMod.getModificationState().markDirty());
772                         }
773                     }
774                     if(col.isMtoNRelation())
775                     {
776                         addM2NUnlinkEntry(col, source.getObject(), colObj);
777                     }
778                 }
779             }
780         }
781     }
782
783     void addM2NLinkEntry(CollectionDescriptor cod, Object JavaDoc leftSource, Object JavaDoc rightSource)
784     {
785         if(!cod.isMtoNRelation()) throw new OJBRuntimeException("Expect a m:n releation, but specified a 1:n");
786         m2nLinkList.add(new LinkEntryMtoN(leftSource, cod, rightSource, false));
787     }
788
789     void performM2NLinkEntries()
790     {
791         PersistenceBroker broker = getTransaction().getBroker();
792         LinkEntry entry;
793         for(int i = 0; i < m2nLinkList.size(); i++)
794         {
795             entry = (LinkEntry) m2nLinkList.get(i);
796             entry.execute(broker);
797         }
798     }
799
800     void addM2NUnlinkEntry(CollectionDescriptor cod, Object JavaDoc leftSource, Object JavaDoc rightSource)
801     {
802         if(!cod.isMtoNRelation()) throw new OJBRuntimeException("Expect a m:n releation, but specified a 1:n");
803         m2nUnlinkList.add(new LinkEntryMtoN(leftSource, cod, rightSource, true));
804     }
805
806     void performM2NUnlinkEntries()
807     {
808         PersistenceBroker broker = getTransaction().getBroker();
809         LinkEntry entry;
810         for(int i = 0; i < m2nUnlinkList.size(); i++)
811         {
812             entry = (LinkEntry) m2nUnlinkList.get(i);
813             entry.execute(broker);
814         }
815     }
816
817     /**
818      * Replace the {@link org.apache.ojb.broker.Identity}
819      * of a registered {@link ObjectEnvelope} object.
820      *
821      * @param newOid
822      * @param oldOid
823      * @return Returns <em>true</em> if successful.
824      */

825     boolean replaceRegisteredIdentity(Identity newOid, Identity oldOid)
826     {
827         /*
828         TODO: Find a better solution
829         */

830         boolean result = false;
831         Object JavaDoc oe = mhtObjectEnvelopes.remove(oldOid);
832         if(oe != null)
833         {
834             mhtObjectEnvelopes.put(newOid, oe);
835             int index = mvOrderOfIds.indexOf(oldOid);
836             mvOrderOfIds.remove(index);
837             mvOrderOfIds.add(index, newOid);
838             result = true;
839             if(log.isDebugEnabled()) log.debug("Replace identity: " + oldOid + " --replaced-by--> " + newOid);
840         }
841         else
842         {
843             log.warn("Can't replace unregistered object identity (" + oldOid + ") with new identity (" + newOid + ")");
844         }
845         return result;
846     }
847 }
Popular Tags