1 package org.apache.ojb.odmg;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import java.util.ArrayList;
19 import java.util.Enumeration;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
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
53
54
55
56
57
58
59
60
61 public class ObjectEnvelopeTable
62 {
63 private Logger log = LoggerFactory.getLogger(ObjectEnvelopeTable.class);
64 private TransactionImpl transaction;
65
66
67
68
69
70
71
72 private List newAssociatedIdentites = new ArrayList();
73 private List m2nLinkList = new ArrayList();
74 private List m2nUnlinkList = new ArrayList();
75 private List markedForDeletionList = new ArrayList();
76 private List markedForInsertList = new ArrayList();
77
78
79 private Map mhtObjectEnvelopes = new HashMap();
80
81
82
83
84
85
86 private ArrayList mvOrderOfIds = new ArrayList();
87
88
89 private boolean needsCommit = false;
90
91
92 public ObjectEnvelopeTable(TransactionImpl myTransaction)
93 {
94 transaction = myTransaction;
95 }
96
97 TransactionImpl getTransaction()
98 {
99 return transaction;
100 }
101
102
103 public void refresh()
104 {
105 needsCommit = false;
106 mhtObjectEnvelopes = new HashMap();
107 mvOrderOfIds = new ArrayList();
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
122
123
124
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
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
150
151
152
153
154
155 connMan.setBatchMode(true);
156
157
158
159 checkAllEnvelopes(broker);
160
161
162 cascadingDependents();
163
164
165
166 upgradeLockIfNeeded();
167
168
169 reorder();
170
171
172
173
174
175
176
177
178 writeAllEnvelopes(reuse);
179
180
181 connMan.executeBatch();
182
183
184 prepareForReuse(reuse);
185
186
187 afterWriteCleanup();
188
189 }
190 catch(Exception e)
191 {
192 connMan.clearBatch();
193
194
195
196
197
198 if(e instanceof OptimisticLockException)
199 {
200
201 log.error("Optimistic lock exception while write objects", e);
202
203 Object 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))
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) e;
216 }
217 }
218 finally
219 {
220 needsCommit = false;
221 connMan.setBatchMode(saveBatchMode);
222 }
223 }
224
225
226 private void writeAllEnvelopes(boolean reuse)
227 {
228
229 performM2NUnlinkEntries();
230
231 Iterator iter;
232
233 iter = ((List) 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
249
250
251
252 mod.cleanup(reuse, insert);
253 }
254
255 performM2NLinkEntries();
256 }
257
258
259
260
261
262
263 private void checkAllEnvelopes(PersistenceBroker broker)
264 {
265 Iterator iter = ((List) mvOrderOfIds.clone()).iterator();
266 while(iter.hasNext())
267 {
268 ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
269
270 if(!mod.getModificationState().isTransient())
271 {
272 mod.markReferenceElements(broker);
273 }
274 }
275 }
276
277
278
279
280
281 private void prepareForReuse(boolean reuse)
282 {
283 if(reuse)
284 {
285
286 Iterator iter = ((List) 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
294 }
295 else
296 {
297 mod.setModificationState(mod.getModificationState().markClean());
298 }
299 }
300 }
301 }
302
303
304
305
306
307
308 private void upgradeLockIfNeeded()
309 {
310
311 Iterator iter = ((List) mvOrderOfIds.clone()).iterator();
312 TransactionImpl tx = getTransaction();
313 ObjectEnvelope mod;
314 while(iter.hasNext())
315 {
316 mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
317
318 if(!mod.getModificationState().isTransient())
319 {
320
321
322
323
324 if(!mod.needsInsert())
325 {
326 if((mod.needsDelete() || mod.needsUpdate()
327 || mod.hasChanged(tx.getBroker())))
328 {
329 needsCommit = true;
330
331 mod.setModificationState(mod.getModificationState().markDirty());
332 ClassDescriptor cld = mod.getClassDescriptor();
333
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
349 public void rollback()
350 {
351 try
352 {
353 Iterator 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
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
375 public void remove(Object 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
392
393
394
395
396 public Enumeration elements()
397 {
398 return java.util.Collections.enumeration(mhtObjectEnvelopes.values());
399 }
400
401
402 public ObjectEnvelope getByIdentity(Identity id)
403 {
404 return (ObjectEnvelope) mhtObjectEnvelopes.get(id);
405 }
406
407
408
409
410
411
412
413 public ObjectEnvelope get(Object 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
422
423
424
425
426 public ObjectEnvelope get(Identity oid, Object 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
441 public String toString()
442 {
443 ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
444 String eol = SystemUtils.LINE_SEPARATOR;
445 buf.append("# ObjectEnvelopeTable dump:" + eol + "start[");
446 Enumeration 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
457 public boolean contains(Identity oid)
458 {
459
460 return mhtObjectEnvelopes.containsKey(oid);
461 }
462
463
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 it = mhtObjectEnvelopes.values().iterator();
483 ObjectEnvelope mod;
484
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
499
500
501
502
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
524 private void cascadeMarkedForInsert()
525 {
526
527 List alreadyPrepared = new ArrayList();
528 for(int i = 0; i < markedForInsertList.size(); i++)
529 {
530 ObjectEnvelope mod = (ObjectEnvelope) markedForInsertList.get(i);
531
532 if(mod.needsInsert())
533 {
534 cascadeInsertFor(mod, alreadyPrepared);
535 alreadyPrepared.clear();
536 }
537 }
538 markedForInsertList.clear();
539 }
540
541
542
543
544
545 private void cascadeInsertFor(ObjectEnvelope mod, List alreadyPrepared)
546 {
547
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 refs = cld.getObjectReferenceDescriptors(true);
554 cascadeInsertSingleReferences(mod, refs, alreadyPrepared);
555
556 List colls = cld.getCollectionDescriptors(true);
557 cascadeInsertCollectionReferences(mod, colls, alreadyPrepared);
558 }
559
560 private void cascadeInsertSingleReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared)
561 {
562 for(int i = 0; i < descriptor.size(); i++)
563 {
564 ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptor.get(i);
565 Object depObj = ord.getPersistentField().get(source.getObject());
566
567 if(depObj != null)
568 {
569
570 source.addLinkOneToOne(ord, false);
571
572 IndirectionHandler handler = ProxyHelper.getIndirectionHandler(depObj);
573
574 if(handler == null || handler.alreadyMaterialized())
575 {
576 RuntimeObject rt;
577
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
591
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 descriptor, List alreadyPrepared)
605 {
606
607 for(int i = 0; i < descriptor.size(); i++)
608 {
609 CollectionDescriptor col = (CollectionDescriptor) descriptor.get(i);
610 Object collOrArray = col.getPersistentField().get(source.getObject());
611 CollectionProxy proxy = ProxyHelper.getCollectionProxy(collOrArray);
612
613
614
615
616
617 if(proxy == null && collOrArray != null)
618 {
619 Iterator it = BrokerHelper.getCollectionIterator(collOrArray);
620 while(it.hasNext())
621 {
622 Object colObj = it.next();
623 if(colObj != null)
624 {
625 RuntimeObject rt = new RuntimeObject(colObj, getTransaction());
626 Identity oid = rt.getIdentity();
627
628
629
630
631
632
633 if(source.needsInsert())
634 {
635
636
637
638
639
640
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
652 addM2NLinkEntry(col, source.getObject(), colObj);
653 }
654 else
655 {
656
657 oe.addLinkOneToN(col, source.getObject(), false);
658
659
660
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
678 private void cascadeMarkedForDeletion()
679 {
680 List alreadyPrepared = new ArrayList();
681 for(int i = 0; i < markedForDeletionList.size(); i++)
682 {
683 ObjectEnvelope mod = (ObjectEnvelope) markedForDeletionList.get(i);
684
685 if(!isNewAssociatedObject(mod.getIdentity()))
686 {
687 cascadeDeleteFor(mod, alreadyPrepared);
688 alreadyPrepared.clear();
689 }
690 }
691 markedForDeletionList.clear();
692 }
693
694
695
696
697
698 private void cascadeDeleteFor(ObjectEnvelope mod, List alreadyPrepared)
699 {
700
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 refs = cld.getObjectReferenceDescriptors(true);
708 cascadeDeleteSingleReferences(mod, refs, alreadyPrepared);
709
710 List colls = cld.getCollectionDescriptors(true);
711 cascadeDeleteCollectionReferences(mod, colls, alreadyPrepared);
712 }
713
714 private void cascadeDeleteSingleReferences(ObjectEnvelope source, List descriptor, List 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 depObj = ord.getPersistentField().get(source.getObject());
722 if(depObj != null)
723 {
724 Identity oid = getTransaction().getBroker().serviceIdentity().buildIdentity(depObj);
725
726
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 descriptor, List 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 collOrArray = col.getPersistentField().get(source.getObject());
746
747 CollectionProxyDefaultImpl proxy = (CollectionProxyDefaultImpl) ProxyHelper.getCollectionProxy(collOrArray);
748
749 if(proxy != null)
750 {
751 collOrArray = proxy.getData();
752 }
753 if(collOrArray != null)
754 {
755 Iterator it = BrokerHelper.getCollectionIterator(collOrArray);
756 while(it.hasNext())
757 {
758 Object 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 leftSource, Object 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 leftSource, Object 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
819
820
821
822
823
824
825 boolean replaceRegisteredIdentity(Identity newOid, Identity oldOid)
826 {
827
828
829
830 boolean result = false;
831 Object 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 }