001 package org.apache.ojb.otm.core;
002
003 /* Copyright 2003-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.lang.reflect.Array;
019 import java.math.BigDecimal;
020 import java.util.ArrayList;
021 import java.util.Arrays;
022 import java.util.Collection;
023 import java.util.Comparator;
024 import java.util.Iterator;
025 import java.util.HashMap;
026 import java.util.HashSet;
027 import java.util.Date;
028 import java.util.List;
029 import java.util.Set;
030 import java.util.Stack;
031
032 import org.apache.commons.collections.iterators.ArrayIterator;
033 import org.apache.ojb.broker.Identity;
034 import org.apache.ojb.broker.OJBRuntimeException;
035 import org.apache.ojb.broker.PBKey;
036 import org.apache.ojb.broker.PersistenceBroker;
037 import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
038 import org.apache.ojb.broker.cache.ObjectCache;
039 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
040 import org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl;
041 import org.apache.ojb.broker.core.proxy.SetProxyDefaultImpl;
042 import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
043 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
044 import org.apache.ojb.broker.core.proxy.MaterializationListener;
045 import org.apache.ojb.broker.core.proxy.ProxyHelper;
046 import org.apache.ojb.broker.metadata.ClassDescriptor;
047 import org.apache.ojb.broker.metadata.CollectionDescriptor;
048 import org.apache.ojb.broker.metadata.FieldDescriptor;
049 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
050 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
051
052 import org.apache.ojb.otm.EditingContext;
053 import org.apache.ojb.otm.OTMKit;
054 import org.apache.ojb.otm.copy.ObjectCopyStrategy;
055 import org.apache.ojb.otm.lock.LockManager;
056 import org.apache.ojb.otm.lock.LockType;
057 import org.apache.ojb.otm.lock.LockingException;
058 import org.apache.ojb.otm.states.State;
059 import org.apache.ojb.otm.swizzle.Swizzling;
060
061 /**
062 *
063 * Concrete implementation of EditingContext.
064 *
065 * @author <a href="mailto:rraghuram@hotmail.com">Raghu Rajah</a>
066 * @see org.apache.ojb.otm.EditingContext
067 *
068 */
069 public class ConcreteEditingContext
070 implements EditingContext, MaterializationListener, ObjectCache
071 {
072 // for hasBidirectionalAssociation method
073 // Maps PBKeys to the sets of classes with/without
074 // bidirectional associations
075 private static HashMap _withBidirAsscMap = new HashMap();
076 private static HashMap _withoutBidirAsscMap = new HashMap();
077
078 private HashSet _withBidirAssc;
079 private HashSet _withoutBidirAssc;
080
081 private HashMap _objects;
082 private ArrayList _order;
083 private Transaction _tx;
084 private PersistenceBroker _pb;
085 private HashMap _original;
086 private HashMap _checkpointed;
087 private HashMap _colProxyListeners;
088
089 //////////////////////////////////////////
090 // Constructor
091 //////////////////////////////////////////
092
093 public ConcreteEditingContext(Transaction tx, PersistenceBroker pb)
094 {
095 PBKey pbkey;
096
097 _tx = tx;
098 _pb = pb;
099 _objects = new HashMap();
100 _order = new ArrayList();
101 _original = new HashMap();
102 _checkpointed = _original;
103 pbkey = _pb.getPBKey();
104 _withoutBidirAssc = (HashSet) _withoutBidirAsscMap.get(pbkey);
105 if (_withoutBidirAssc != null)
106 {
107 _withBidirAssc = (HashSet) _withBidirAsscMap.get(pbkey);
108 }
109 else
110 {
111 _withoutBidirAssc = new HashSet();
112 _withoutBidirAsscMap.put(pbkey, _withoutBidirAssc);
113 _withBidirAssc = new HashSet();
114 _withBidirAsscMap.put(pbkey, _withBidirAssc);
115 }
116 }
117
118 //////////////////////////////////////////
119 // EditingContext operations
120 //////////////////////////////////////////
121
122 /**
123 * @see org.apache.ojb.otm.EditingContext#insert(Identity, Object, int)
124 */
125 public void insert(Identity oid, Object userObject, int lock)
126 throws LockingException
127 {
128 ContextEntry entry;
129
130 entry = insertInternal(oid, userObject, lock, true, null, new Stack());
131 if ((entry != null) && entry.state.needsDelete()) {
132 // Undelete it
133 entry.state = State.PERSISTENT_CLEAN;
134 }
135 }
136
137 private ContextEntry insertInternal(Identity oid, Object userObject,
138 int lock, boolean canCreate, Identity insertBeforeThis, Stack stack)
139 throws LockingException
140 {
141 ContextEntry entry;
142 LockManager lockManager;
143 Swizzling swizzlingStrategy;
144 IndirectionHandler handler = null;
145 OTMKit kit = _tx.getKit();
146 // Are we building object's relations for the userObject in the transaction?
147 // Otherwise we just get data from the "userObject" and put it into
148 // the previously loaded/created object in the transaction
149 boolean buildingObject = false;
150 boolean lazySwizzle = false;
151
152 if (lock == LockType.NO_LOCK)
153 {
154 return null;
155 }
156
157 entry = (ContextEntry) _objects.get(oid);
158
159 if (userObject == null)
160 {
161 // invalidating object...
162 _original.remove(oid);
163 _checkpointed.remove(oid);
164 if (entry != null)
165 {
166 entry.userObject = null;
167 entry.cacheObject = null;
168 }
169 return entry;
170 }
171
172 lockManager = LockManager.getInstance();
173 swizzlingStrategy = kit.getSwizzlingStrategy();
174
175 handler = ProxyHelper.getIndirectionHandler(userObject);
176 if ((handler != null) && handler.alreadyMaterialized())
177 {
178 userObject = handler.getRealSubject();
179 handler = null;
180 }
181
182 if ((entry == null) || (entry.userObject == null))
183 {
184 // first insertion of the userObject into editing context
185 Object swizzledObject = swizzlingStrategy.swizzle(userObject, null, _pb, this);
186 entry = new ContextEntry(swizzledObject);
187 if (entry.handler != null)
188 {
189 ObjectCopyStrategy copyStrategy = _tx.getKit().getCopyStrategy(oid);
190 entry.cacheObject = copyStrategy.copy(userObject, _pb);
191 // Assume that object exists, otherwise were the proxy came from?
192 _objects.put(oid, entry);
193 lockManager.ensureLock(oid, _tx, lock, _pb); // lock after _objects.put to avoid hanged locks
194 entry.handler.addListener(this);
195 }
196 else
197 {
198 Object origCacheObj = _pb.getObjectByIdentity(oid);
199
200 if ((origCacheObj == null) && !canCreate)
201 {
202 // we don't create the objects by reachability
203 throw new IllegalStateException("Related object is neither persistent, nor otm-depentent: " + oid);
204 }
205 if (origCacheObj != null)
206 {
207 entry.cacheObject = origCacheObj;
208 }
209 buildingObject = true;
210 _objects.put(oid, entry);
211 lockManager.ensureLock(oid, _tx, lock, _pb); // lock after _objects.put to avoid hanged locks
212
213 if (userObject != null)
214 {
215 if ((origCacheObj == null) && canCreate)
216 {
217 ObjectCopyStrategy copyStrategy = _tx.getKit().getCopyStrategy(oid);
218 entry.cacheObject = copyStrategy.copy(userObject, _pb);
219 entry.state = State.PERSISTENT_NEW;
220 if (kit.isEagerInsert(userObject)
221 || hasBidirectionalAssociation(userObject.getClass()))
222 {
223 _pb.store(entry.cacheObject, entry.state);
224 entry.state = State.PERSISTENT_CLEAN;
225 origCacheObj = entry.cacheObject;
226 }
227 }
228
229 if (origCacheObj != null)
230 {
231 _original.put(oid, getFields(userObject, false, true));
232 }
233 }
234 }
235 if (insertBeforeThis != null)
236 {
237 int insertIndex = _order.indexOf(insertBeforeThis);
238 _order.add(insertIndex, oid);
239 }
240 else
241 {
242 _order.add(oid);
243 }
244 }
245 else
246 {
247 // The object in context is the same object attempted an insert on
248 // Ensure we have the correct lock level
249 lockManager.ensureLock(oid, _tx, lock, _pb);
250
251 if (handler == null)
252 {
253 if (!swizzlingStrategy.isSameInstance(entry.userObject, userObject))
254 {
255 // the new object contains data to deal with
256 if (entry.handler != null)
257 {
258 // materialize old object even if it is not
259 // materialized yet, because we need a place
260 // to copy the data from the new object
261 entry.userObject = entry.handler.getRealSubject();
262 entry.handler = null;
263 }
264 // swizzle after lockReachableObjects(), when all related objects
265 // will be in the editing context
266 lazySwizzle = true;
267 }
268 }
269 }
270
271 // perform automatic read lock for all reachable objects
272 // if the inserted object is materialized
273 if ((handler == null) && !stack.contains(userObject))
274 {
275 stack.push(userObject);
276 lockReachableObjects(oid, userObject, entry.cacheObject, lock, stack, buildingObject);
277 stack.pop();
278 if (lazySwizzle)
279 {
280 entry.userObject = swizzlingStrategy.swizzle(userObject, entry.userObject, _pb, this);
281 }
282 }
283
284 return entry;
285 }
286
287 /**
288 * Lock all objects reachable via 1:N and N:1 relations,
289 * @param lock The lock type to use
290 */
291 private void lockReachableObjects(Identity oid, Object userObject,
292 Object cacheObject, int lock, Stack stack, boolean buildingObject)
293 throws LockingException
294 {
295 ContextEntry entry;
296 boolean onlyDependants = !_tx.getKit().isImplicitLockingUsed();
297 ClassDescriptor mif = _pb.getClassDescriptor(userObject.getClass());
298
299 // N:1 relations
300 Iterator iter = mif.getObjectReferenceDescriptors().iterator();
301 ObjectReferenceDescriptor rds = null;
302 PersistentField f;
303 Object relUserObj;
304 Identity relOid;
305 boolean isDependent;
306
307 while (iter.hasNext())
308 {
309 rds = (ObjectReferenceDescriptor) iter.next();
310 isDependent = rds.getOtmDependent();
311 if (onlyDependants && !isDependent)
312 {
313 continue;
314 }
315 f = rds.getPersistentField();
316 relUserObj = f.get(userObject);
317 if (relUserObj != null)
318 {
319 relOid = new Identity(relUserObj, _pb);
320 entry = (ContextEntry) _objects.get(relOid);
321 if ((entry == null) || (entry.userObject != relUserObj))
322 {
323 entry = insertInternal(relOid, relUserObj, lock, isDependent,
324 oid, stack);
325 if (buildingObject && (entry != null))
326 {
327 f.set(userObject, entry.userObject);
328 f.set(cacheObject, entry.cacheObject);
329 }
330 }
331 }
332 }
333
334 // 1:N relations
335 Iterator collections = mif.getCollectionDescriptors().iterator();
336 CollectionDescriptor cds;
337 Object userCol;
338 Iterator userColIterator;
339 Class type;
340 ArrayList newUserCol = null;
341 ArrayList newCacheCol = null;
342
343 while (collections.hasNext())
344 {
345 cds = (CollectionDescriptor) collections.next();
346 f = cds.getPersistentField();
347 type = f.getType();
348 isDependent = cds.getOtmDependent();
349 if (onlyDependants && !isDependent)
350 {
351 continue;
352 }
353 userCol = f.get(userObject);
354 if (userCol != null)
355 {
356 if ((userCol instanceof CollectionProxyDefaultImpl)
357 && !((CollectionProxyDefaultImpl) userCol).isLoaded())
358 {
359 continue;
360 }
361
362 if (buildingObject)
363 {
364 newUserCol = new ArrayList();
365 newCacheCol = new ArrayList();
366 }
367
368 if (Collection.class.isAssignableFrom(type))
369 {
370 userColIterator = ((Collection) userCol).iterator();
371 }
372 else if (type.isArray())
373 {
374 userColIterator = new ArrayIterator(userCol);
375 }
376 else
377 {
378 throw new OJBRuntimeException(
379 userCol.getClass()
380 + " can not be managed by OJB OTM, use Array or Collection instead !");
381 }
382
383 while (userColIterator.hasNext())
384 {
385 relUserObj = userColIterator.next();
386 relOid = new Identity(relUserObj, _pb);
387 entry = (ContextEntry) _objects.get(relOid);
388 if ((entry == null) || (entry.userObject != relUserObj))
389 {
390 entry = insertInternal(relOid, relUserObj, lock,
391 isDependent, null, stack);
392 }
393 if (buildingObject && (entry != null))
394 {
395 newUserCol.add(entry.userObject);
396 newCacheCol.add(entry.cacheObject);
397 }
398 }
399 if (buildingObject)
400 {
401 setCollectionField(userObject, f, newUserCol);
402 setCollectionField(cacheObject, f, newCacheCol);
403 }
404 }
405 }
406 }
407
408 /**
409 * @see org.apache.ojb.otm.EditingContext#remove(Identity)
410 */
411 public void remove(Identity oid)
412 {
413 _objects.remove(oid);
414 _order.remove(oid);
415 LockManager.getInstance().releaseLock(oid, _tx);
416 }
417
418
419 public void deletePersistent(Identity oid, Object userObject)
420 throws LockingException
421 {
422 ContextEntry entry;
423
424 entry = insertInternal(oid, userObject, LockType.WRITE_LOCK, true, null,
425 new Stack());
426 if (entry != null)
427 {
428 entry.state = entry.state.deletePersistent();
429 }
430 _order.remove(oid);
431 _order.add(oid);
432 }
433
434 /**
435 * @see org.apache.ojb.otm.EditingContext#lookup(Identity)
436 */
437 public Object lookup(Identity oid)
438 {
439 ContextEntry entry = (ContextEntry) _objects.get(oid);
440 return (entry == null ? null : entry.userObject);
441 }
442
443 public boolean contains(Identity oid)
444 {
445 return lookup(oid) != null;
446 }
447
448 /**
449 * @see org.apache.ojb.otm.EditingContext#lookupState(Identity)
450 */
451 public State lookupState(Identity oid)
452 throws LockingException
453 {
454 State retval = null;
455 ContextEntry entry = (ContextEntry) _objects.get(oid);
456 if (entry != null)
457 {
458 /**
459 * possibly return a clone so we don't allow people to tweak states.
460 */
461 retval = entry.state;
462 }
463 return retval;
464 }
465
466 /**
467 * @see org.apache.ojb.otm.EditingContext#setState(Identity, State)
468 */
469 public void setState(Identity oid, State state)
470 {
471 ContextEntry entry = (ContextEntry) _objects.get(oid);
472 entry.state = state;
473 }
474
475 public Collection getAllObjectsInContext()
476 {
477 return _objects.values();
478 }
479
480 //////////////////////////////////////////
481 // MaterializationListener interface
482 //////////////////////////////////////////
483
484 public void beforeMaterialization(IndirectionHandler handler, Identity oid)
485 {
486 //noop
487 }
488
489 public void afterMaterialization(IndirectionHandler handler, Object cacheObject)
490 {
491 Identity oid = handler.getIdentity();
492 ContextEntry entry = (ContextEntry) _objects.get(oid);
493
494 if (entry == null)
495 {
496 return;
497 }
498
499 int lock = LockManager.getInstance().getLockHeld(oid, _tx);
500 ObjectCopyStrategy copyStrategy = _tx.getKit().getCopyStrategy(oid);
501 Object userObject = copyStrategy.copy(cacheObject, _pb);
502 handler.setRealSubject(userObject);
503 _original.put(oid, getFields(userObject, false, true));
504
505 // replace the proxy object with the real one
506 entry.userObject = userObject;
507 entry.cacheObject = cacheObject;
508 entry.handler.removeListener(this);
509 entry.handler = null;
510
511 // perform automatic lock for all reachable objects
512 // if the inserted object is materialized
513 try
514 {
515 lockReachableObjects(oid, userObject, cacheObject, lock, new Stack(), true);
516 }
517 catch (LockingException ex)
518 {
519 throw new LockingPassthruException(ex);
520 }
521 }
522
523
524
525 //////////////////////////////////////////
526 // Other operations
527 //////////////////////////////////////////
528
529 /**
530 *
531 * Commit this context into the persistent store.
532 * The EditingContext is not usable after a commit.
533 *
534 */
535 public void commit() throws TransactionAbortedException
536 {
537 checkpointInternal(true);
538 releaseLocksAndClear();
539 }
540
541 private void releaseLocksAndClear()
542 {
543 releaseLocks();
544 removeMaterializationListener();
545 _objects.clear();
546 _order.clear();
547 _original.clear();
548 if (_checkpointed != _original)
549 {
550 _checkpointed.clear();
551 }
552 }
553
554 /**
555 *
556 * Writes all changes in this context into the persistent store.
557 *
558 */
559 public void checkpoint() throws TransactionAbortedException
560 {
561 checkpointInternal(false);
562 _checkpointed = new HashMap();
563 for (Iterator iterator = _order.iterator(); iterator.hasNext();)
564 {
565 Identity oid = (Identity) iterator.next();
566 ContextEntry entry = (ContextEntry) _objects.get(oid);
567 if (entry.handler == null)
568 {
569 _checkpointed.put(oid, getFields(entry.userObject, false, true));
570 }
571 }
572 }
573
574 /**
575 *
576 * Writes all changes in this context into the persistent store.
577 *
578 */
579 private void checkpointInternal(boolean isCommit)
580 throws TransactionAbortedException
581 {
582 if (_order.size() == 0)
583 {
584 return;
585 }
586
587 removeCollectionProxyListeners();
588
589 ConnectionManagerIF connMan = _pb.serviceConnectionManager();
590 boolean saveBatchMode = connMan.isBatchMode();
591 Swizzling swizzlingStrategy = _tx.getKit().getSwizzlingStrategy();
592 LockManager lockManager = LockManager.getInstance();
593 Identity[] lockOrder = (Identity[]) _order.toArray(new Identity[_order.size()]);
594 ObjectCache cache = _pb.serviceObjectCache();
595 boolean isInsertVerified = _tx.getKit().isInsertVerified();
596 ArrayList changedCollections = new ArrayList();
597
598 // sort objects in the order of oid.hashCode to avoid deadlocks
599 Arrays.sort(lockOrder, new Comparator()
600 {
601 public int compare(Object o1, Object o2)
602 {
603 return o1.hashCode() - o2.hashCode();
604 }
605
606 public boolean equals(Object obj)
607 {
608 return false;
609 }
610 });
611
612 try {
613 // mark dirty objects and lock them for write
614 // also handle dependent objects and if there were inserted once,
615 // repeat this process for their dependants ("cascade create")
616 ArrayList newObjects = new ArrayList();
617 int countNewObjects;
618 do
619 {
620 newObjects.clear();
621 countNewObjects = 0;
622 for (int i = 0; i < lockOrder.length; i++)
623 {
624 Identity oid = lockOrder[i];
625 ContextEntry entry = (ContextEntry) _objects.get(oid);
626 State state = entry.state;
627
628 if (entry.userObject == null) // invalidated
629 {
630 continue;
631 }
632
633 if (entry.handler == null) // materialized
634 {
635 if (!state.isDeleted())
636 {
637 Object[][] origFields = (Object[][]) _checkpointed.get(oid);
638 Object[][] newFields = getFields(entry.userObject, true, !isCommit);
639
640 if (origFields == null)
641 {
642 entry.needsCacheSwizzle = true;
643 newObjects.addAll(
644 handleDependentReferences(oid, entry.userObject,
645 null, newFields[0], newFields[2]));
646 newObjects.addAll(
647 handleDependentCollections(oid, entry.userObject,
648 null, newFields[1], newFields[3]));
649 }
650 else
651 {
652 if (isModified(origFields[0], newFields[0]))
653 {
654 entry.state = state.markDirty();
655 entry.needsCacheSwizzle = true;
656 lockManager.ensureLock(oid, _tx, LockType.WRITE_LOCK, _pb);
657 newObjects.addAll(
658 handleDependentReferences(oid, entry.userObject,
659 origFields[0], newFields[0], newFields[2]));
660 }
661
662 if (isModified(origFields[1], newFields[1]))
663 {
664 // there are modified collections,
665 // so we need to lock the object and to swizzle it to cache
666 entry.needsCacheSwizzle = true;
667 lockManager.ensureLock(oid, _tx, LockType.WRITE_LOCK, _pb);
668 newObjects.addAll(
669 handleDependentCollections(oid, entry.userObject,
670 origFields[1], newFields[1], newFields[3]));
671 changedCollections.add(oid);
672 }
673 }
674 }
675 }
676 }
677 countNewObjects = newObjects.size();
678 if (countNewObjects > 0)
679 {
680 // new objects are not locked, so we don't need to ensure the order
681 lockOrder = (Identity[]) newObjects.toArray(
682 new Identity[countNewObjects]);
683 }
684 }
685 while (countNewObjects > 0);
686
687 // Swizzle the context objects and the cache objects
688 for (Iterator it = _order.iterator(); it.hasNext(); )
689 {
690 Identity oid = (Identity) it.next();
691 ContextEntry entry = (ContextEntry) _objects.get(oid);
692
693 if (entry.needsCacheSwizzle)
694 {
695 entry.userObject = swizzlingStrategy.getRealTarget(entry.userObject);
696 entry.cacheObject = swizzlingStrategy.swizzle(
697 // we create the special ObjectCache implememntation
698 // that returns cacheObject, not userObject
699 entry.userObject, entry.cacheObject, _pb, new ObjectCache()
700 {
701 public Object lookup(Identity anOid)
702 {
703 ContextEntry ent = (ContextEntry) _objects.get(anOid);
704 return (ent == null ? null : ent.cacheObject);
705 }
706
707 public boolean contains(Identity oid)
708 {
709 return lookup(oid) != null;
710 }
711
712 public void cache(Identity anOid, Object obj)
713 {
714 // do nothing
715 }
716
717 public boolean cacheIfNew(Identity oid, Object obj)
718 {
719 return false;
720 }
721
722 public void clear()
723 {
724 // do nothing
725 }
726
727 public void remove(Identity anOid)
728 {
729 // do nothing
730 }
731 });
732 }
733 }
734
735 // Cascade delete for dependent objects
736 int countCascadeDeleted;
737 do
738 {
739 countCascadeDeleted = 0;
740 // Use intermediate new ArrayList(_order) because _order
741 // may be changed during cascade delete
742 for (Iterator it = (new ArrayList(_order)).iterator(); it.hasNext(); )
743 {
744 Identity oid = (Identity) it.next();
745 ContextEntry entry = (ContextEntry) _objects.get(oid);
746
747 if (entry.state.isDeleted())
748 {
749 countCascadeDeleted += doCascadeDelete(oid, entry.userObject);
750 }
751 }
752 }
753 while (countCascadeDeleted > 0);
754
755 // perform database operations
756 connMan.setBatchMode(true);
757 try
758 {
759 for (Iterator it = _order.iterator(); it.hasNext(); )
760 {
761 Identity oid = (Identity) it.next();
762 ContextEntry entry = (ContextEntry) _objects.get(oid);
763 State state = entry.state;
764
765 if (!state.needsInsert() && !state.needsUpdate()
766 && !state.needsDelete())
767 {
768 if (changedCollections.contains(oid)) {
769 _pb.store(entry.cacheObject, state);
770 }
771 continue;
772 }
773
774 if (state.needsInsert())
775 {
776 if (isInsertVerified)
777 {
778 // PB verifies object existence by default
779 _pb.store(entry.cacheObject);
780 }
781 else
782 {
783 // PB migth already created the object by auto-update
784 if (cache.lookup(oid) == null) {
785 _pb.store(entry.cacheObject, state);
786 }
787 }
788
789 }
790 else if (state.needsUpdate())
791 {
792 _pb.store(entry.cacheObject, state);
793 }
794 else if (state.needsDelete())
795 {
796 _pb.delete(entry.cacheObject);
797 }
798 entry.state = state.commit();
799 }
800 connMan.executeBatch();
801 }
802 finally
803 {
804 connMan.setBatchMode(saveBatchMode);
805 }
806 } catch (Throwable ex) {
807 ex.printStackTrace();
808 throw new TransactionAbortedException(ex);
809 }
810 }
811
812 /**
813 *
814 * Rollback all changes made during this transaction. The EditingContext is not usable after
815 * a rollback.
816 *
817 */
818 public void rollback()
819 {
820 for (Iterator iterator = _order.iterator(); iterator.hasNext();)
821 {
822 Identity oid = (Identity) iterator.next();
823 ContextEntry entry = (ContextEntry) _objects.get(oid);
824 entry.state = entry.state.rollback();
825 Object[][] origFields = (Object[][]) _original.get(oid);
826 if (origFields != null)
827 {
828 setFields(entry.userObject, origFields);
829 setFields(entry.cacheObject, origFields);
830 }
831 }
832 releaseLocksAndClear();
833 }
834
835 /**
836 *
837 * Rollback all changes made during this transaction to the given object.
838 *
839 */
840 public void refresh(Identity oid, Object object)
841 {
842 ContextEntry entry = (ContextEntry) _objects.get(oid);
843 Object[][] origFields = (Object[][]) _original.get(oid);
844 if (origFields != null)
845 {
846 setFields(entry.userObject, origFields);
847 if (object != entry.userObject)
848 {
849 setFields(object, origFields);
850 }
851 }
852 entry.state = entry.state.refresh();
853 }
854
855 private void removeMaterializationListener()
856 {
857 for (Iterator it = _order.iterator(); it.hasNext();)
858 {
859 Identity oid = (Identity) it.next();
860 ContextEntry entry = (ContextEntry) _objects.get(oid);
861 if (entry.handler != null)
862 {
863 entry.handler.removeListener(this);
864 }
865 }
866 }
867
868 private void removeCollectionProxyListeners()
869 {
870 if (_colProxyListeners != null)
871 {
872 for (Iterator it = _colProxyListeners.keySet().iterator(); it.hasNext();)
873 {
874 CollectionProxyListener listener = (CollectionProxyListener) it.next();
875 CollectionProxyDefaultImpl colProxy = (CollectionProxyDefaultImpl) _colProxyListeners.get(listener);
876 colProxy.removeListener(listener);
877 }
878 _colProxyListeners.clear();
879 }
880 }
881
882 private void releaseLocks()
883 {
884 LockManager lockManager = LockManager.getInstance();
885
886 for (Iterator it = _objects.keySet().iterator(); it.hasNext(); )
887 {
888 Identity oid = (Identity) it.next();
889 lockManager.releaseLock(oid, _tx);
890 }
891 _tx.getKit().getLockMap().gc();
892 }
893
894 /**
895 * This method compared simple field values:
896 * there are some tricks...
897 */
898 private boolean isEqual(Object fld1, Object fld2)
899 {
900 if (fld1 == null || fld2 == null)
901 {
902 return (fld1 == fld2);
903 }
904 else if ((fld1 instanceof BigDecimal) && (fld2 instanceof BigDecimal))
905 {
906 return (((BigDecimal) fld1).compareTo((BigDecimal) fld2) == 0);
907 }
908 else if ((fld1 instanceof Date) && (fld2 instanceof Date))
909 {
910 return (((Date) fld1).getTime() == ((Date) fld2).getTime());
911 }
912 else
913 {
914 return fld1.equals(fld2);
915 }
916 }
917
918 private boolean isModified(Object[] newFields, Object[] oldFields)
919 {
920 if (newFields.length != oldFields.length)
921 {
922 return true;
923 }
924
925 for (int i = 0; i < newFields.length; i++)
926 {
927 if (!isEqual(newFields[i], oldFields[i]))
928 {
929 return true;
930 }
931 }
932
933 return false;
934 }
935
936 /**
937 * @param addListeners Whether to add CollectionProxy listeners
938 * @return four arrays of field values:
939 * 1) The class, simple fields, identities of references
940 * 2) collections of identities
941 * 3) references (parallel to identities of references in 1)
942 * 4) collections of objects (parallel to collections of identities in 2)
943 * if "withObjects" parameter is "false", then returns nulls
944 * in places of 3) and 4)
945 */
946 private Object[][] getFields(Object obj, boolean withObjects, boolean addListeners)
947 {
948 ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
949 FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
950 Collection refDescs = mif.getObjectReferenceDescriptors();
951 Collection colDescs = mif.getCollectionDescriptors();
952 int count = 0;
953 Object[] fields = new Object[1 + fieldDescs.length + refDescs.size()];
954 ArrayList[] collections = new ArrayList[colDescs.size()];
955 Object[] references = null;
956 ArrayList[] collectionsOfObjects = null;
957 int lockForListeners = LockType.NO_LOCK;
958
959 if (withObjects)
960 {
961 references = new Object[refDescs.size()];
962 collectionsOfObjects = new ArrayList[colDescs.size()];
963 }
964
965 if (addListeners)
966 {
967 lockForListeners = LockManager.getInstance().getLockHeld(
968 new Identity(obj, _pb), _tx);
969 }
970
971 fields[0] = obj.getClass(); // we must notice if the object class changes
972 count++;
973
974 for (int i = 0; i < fieldDescs.length; i++)
975 {
976 FieldDescriptor fd = fieldDescs[i];
977 PersistentField f = fd.getPersistentField();
978 fields[count] = f.get(obj);
979 count++;
980 }
981
982 int countRefs = 0;
983 for (Iterator it = refDescs.iterator(); it.hasNext(); count++, countRefs++)
984 {
985 ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it.next();
986 PersistentField f = rds.getPersistentField();
987 Object relObj = f.get(obj);
988 if (relObj != null)
989 {
990 fields[count] = new Identity(relObj, _pb);
991 if (withObjects)
992 {
993 references[countRefs] = relObj;
994 }
995 }
996 }
997
998 count = 0;
999 for (Iterator it = colDescs.iterator(); it.hasNext(); count++)
1000 {
1001 CollectionDescriptor cds = (CollectionDescriptor) it.next();
1002 PersistentField f = cds.getPersistentField();
1003 Class type = f.getType();
1004 Object col = f.get(obj);
1005
1006 if ((col != null) && (col instanceof CollectionProxyDefaultImpl)
1007 && !((CollectionProxyDefaultImpl) col).isLoaded())
1008 {
1009 if (addListeners)
1010 {
1011 OTMCollectionProxyListener listener =
1012 new OTMCollectionProxyListener(cds, collections,
1013 count, lockForListeners);
1014
1015 ((CollectionProxyDefaultImpl) col).addListener(listener);
1016 if (_colProxyListeners == null)
1017 {
1018 _colProxyListeners = new HashMap();
1019 }
1020 _colProxyListeners.put(listener, col);
1021 }
1022 continue;
1023 }
1024
1025 if (col != null)
1026 {
1027 ArrayList list = new ArrayList();
1028 ArrayList listOfObjects = null;
1029 Iterator colIterator;
1030
1031 collections[count] = list;
1032 if (withObjects)
1033 {
1034 listOfObjects = new ArrayList();
1035 collectionsOfObjects[count] = listOfObjects;
1036 }
1037
1038 if (Collection.class.isAssignableFrom(type))
1039 {
1040 colIterator = ((Collection) col).iterator();
1041 }
1042 else if (type.isArray())
1043 {
1044 colIterator = new ArrayIterator(col);
1045 }
1046 else
1047 {
1048 continue;
1049 }
1050
1051 while (colIterator.hasNext())
1052 {
1053 Object relObj = colIterator.next();
1054 list.add(new Identity(relObj, _pb));
1055 if (withObjects)
1056 {
1057 listOfObjects.add(relObj);
1058 }
1059 }
1060 }
1061 }
1062
1063 return new Object[][] {fields, collections, references, collectionsOfObjects};
1064 }
1065
1066 private void setFields(Object obj, Object[][] fieldsAndCollections)
1067 {
1068 ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
1069 FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
1070 Collection refDescs = mif.getObjectReferenceDescriptors();
1071 Collection colDescs = mif.getCollectionDescriptors();
1072 Object[] fields = fieldsAndCollections[0];
1073 ArrayList[] collections = (ArrayList[]) fieldsAndCollections[1];
1074 int count = 0;
1075
1076 if (!fields[0].equals(obj.getClass()))
1077 {
1078 System.err.println("Can't restore the object fields "
1079 + "since its class changed during transaction from "
1080 + fields[0] + " to " + obj.getClass());
1081 return;
1082 }
1083 count++;
1084
1085 for (int i = 0; i < fieldDescs.length; i++)
1086 {
1087 FieldDescriptor fd = fieldDescs[i];
1088 PersistentField f = fd.getPersistentField();
1089 f.set(obj, fields[count]);
1090 count++;
1091 }
1092
1093 for (Iterator it = refDescs.iterator(); it.hasNext(); count++)
1094 {
1095 ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it.next();
1096 PersistentField f = rds.getPersistentField();
1097 Identity oid = (Identity) fields[count];
1098 Object relObj;
1099 if (oid == null)
1100 {
1101 relObj = null;
1102 }
1103 else
1104 {
1105 relObj = _pb.getObjectByIdentity(oid);
1106 }
1107 f.set(obj, relObj);
1108 }
1109
1110 count = 0;
1111 for (Iterator it = colDescs.iterator(); it.hasNext(); count++)
1112 {
1113 CollectionDescriptor cds = (CollectionDescriptor) it.next();
1114 PersistentField f = cds.getPersistentField();
1115 ArrayList list = collections[count];
1116 ArrayList newCol;
1117
1118 if (list == null)
1119 {
1120 f.set(obj, null);
1121 }
1122 else
1123 {
1124 newCol = new ArrayList();
1125 for (Iterator it2 = list.iterator(); it2.hasNext(); )
1126 {
1127 Identity relOid = (Identity) it2.next();
1128 Object relObj = _pb.getObjectByIdentity(relOid);
1129
1130 if (relObj != null)
1131 {
1132 newCol.add(relObj);
1133 }
1134 }
1135 setCollectionField(obj, f, newCol);
1136 }
1137 }
1138 }
1139
1140 private void setCollectionField(Object obj, PersistentField f, List newCol)
1141 {
1142 Class type = f.getType();
1143
1144 if (Collection.class.isAssignableFrom(type))
1145 {
1146 Collection col = (Collection) f.get(obj);
1147
1148 if (col == null)
1149 {
1150 if (type == List.class || type == Collection.class)
1151 {
1152 col = new ArrayList();
1153 }
1154 else if (type == Set.class)
1155 {
1156 col = new HashSet();
1157 }
1158 else
1159 {
1160 try
1161 {
1162 col = (Collection) type.newInstance();
1163 }
1164 catch (Throwable ex)
1165 {
1166 System.err.println("Cannot instantiate collection field: " + f);
1167 ex.printStackTrace();
1168 return;
1169 }
1170 }
1171 }
1172 else
1173 {
1174 if (col instanceof CollectionProxyDefaultImpl)
1175 {
1176 CollectionProxyDefaultImpl cp = (CollectionProxyDefaultImpl) col;
1177 if (col instanceof List)
1178 {
1179 col = new ListProxyDefaultImpl(_pb.getPBKey(), cp.getData().getClass(), null);
1180 }
1181 else if (col instanceof Set)
1182 {
1183 col = new SetProxyDefaultImpl(_pb.getPBKey(), cp.getData().getClass(), null);
1184 }
1185 else
1186 {
1187 col = new CollectionProxyDefaultImpl(_pb.getPBKey(), cp.getData().getClass(), null);
1188 }
1189 col.clear();
1190 }
1191 else
1192 {
1193 try
1194 {
1195 col = (Collection) col.getClass().newInstance();
1196 }
1197 catch (Exception ex)
1198 {
1199 System.err.println("Cannot instantiate collection field: " + f);
1200 ex.printStackTrace();
1201 return;
1202 }
1203 }
1204 }
1205 col.addAll(newCol);
1206 f.set(obj, col);
1207 }
1208 else if (type.isArray())
1209 {
1210 int length = newCol.size();
1211 Object array = Array.newInstance(type.getComponentType(), length);
1212
1213 for (int i = 0; i < length; i++)
1214 {
1215 Array.set(array, i, newCol.get(i));
1216 }
1217 f.set(obj, array);
1218 }
1219 }
1220
1221 /**
1222 * Does the given class has bidirectional assiciation
1223 * with some other class?
1224 */
1225 private boolean hasBidirectionalAssociation(Class clazz)
1226 {
1227 ClassDescriptor cdesc;
1228 Collection refs;
1229 boolean hasBidirAssc;
1230
1231 if (_withoutBidirAssc.contains(clazz))
1232 {
1233 return false;
1234 }
1235
1236 if (_withBidirAssc.contains(clazz))
1237 {
1238 return true;
1239 }
1240
1241 // first time we meet this class, let's look at metadata
1242 cdesc = _pb.getClassDescriptor(clazz);
1243 refs = cdesc.getObjectReferenceDescriptors();
1244 hasBidirAssc = false;
1245 REFS_CYCLE:
1246 for (Iterator it = refs.iterator(); it.hasNext(); )
1247 {
1248 ObjectReferenceDescriptor ord;
1249 ClassDescriptor relCDesc;
1250 Collection relRefs;
1251
1252 ord = (ObjectReferenceDescriptor) it.next();
1253 relCDesc = _pb.getClassDescriptor(ord.getItemClass());
1254 relRefs = relCDesc.getObjectReferenceDescriptors();
1255 for (Iterator relIt = relRefs.iterator(); relIt.hasNext(); )
1256 {
1257 ObjectReferenceDescriptor relOrd;
1258
1259 relOrd = (ObjectReferenceDescriptor) relIt.next();
1260 if (relOrd.getItemClass().equals(clazz))
1261 {
1262 hasBidirAssc = true;
1263 break REFS_CYCLE;
1264 }
1265 }
1266 }
1267 if (hasBidirAssc)
1268 {
1269 _withBidirAssc.add(clazz);
1270 }
1271 else
1272 {
1273 _withoutBidirAssc.add(clazz);
1274 }
1275
1276 return hasBidirAssc;
1277 }
1278
1279 /**
1280 * @return number of deleted objects: 1 or 0 (if the object is already deleted)
1281 */
1282 private int markDelete(Identity oid, Identity mainOid, boolean isCollection)
1283 {
1284 ContextEntry entry = (ContextEntry) _objects.get(oid);
1285
1286 if (entry == null)
1287 {
1288 throw new IllegalStateException("markDelete failed: the dependent object "
1289 + oid + " is not in the editing context");
1290 }
1291
1292 if (entry.state.isDeleted())
1293 {
1294 return 0;
1295 }
1296 else
1297 {
1298 entry.state = entry.state.deletePersistent();
1299 if (mainOid != null)
1300 {
1301 int dependentIndex = _order.indexOf(oid);
1302 int mainIndex = _order.indexOf(mainOid);
1303
1304 if (isCollection) // remove collection item before main obj
1305 {
1306 if (dependentIndex > mainIndex)
1307 {
1308 _order.remove(dependentIndex);
1309 _order.add(mainIndex, oid);
1310 }
1311 }
1312 else // remove reference after main obj
1313 {
1314 if (dependentIndex < mainIndex)
1315 {
1316 _order.remove(dependentIndex); // this causes mainIndex--
1317 _order.add(mainIndex, oid);
1318 }
1319 }
1320
1321 }
1322 return 1;
1323 }
1324 }
1325
1326 /**
1327 * Mark for creation all newly introduced dependent references.
1328 * Mark for deletion all nullified dependent references.
1329 * @return the list of created objects
1330 */
1331 private ArrayList handleDependentReferences(Identity oid, Object userObject,
1332 Object[] origFields, Object[] newFields, Object[] newRefs)
1333 throws LockingException
1334 {
1335 ClassDescriptor mif = _pb.getClassDescriptor(userObject.getClass());
1336 FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
1337 Collection refDescs = mif.getObjectReferenceDescriptors();
1338 int count = 1 + fieldDescs.length;
1339 ArrayList newObjects = new ArrayList();
1340 int countRefs = 0;
1341
1342 for (Iterator it = refDescs.iterator(); it.hasNext(); count++, countRefs++)
1343 {
1344 ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it.next();
1345 Identity origOid = (origFields == null ? null : (Identity) origFields[count]);
1346 Identity newOid = (Identity) newFields[count];
1347
1348 if (rds.getOtmDependent())
1349 {
1350 if ((origOid == null) && (newOid != null))
1351 {
1352 ContextEntry entry = (ContextEntry) _objects.get(newOid);
1353
1354 if (entry == null)
1355 {
1356 Object relObj = newRefs[countRefs];
1357 insertInternal(newOid, relObj, LockType.WRITE_LOCK,
1358 true, oid, new Stack());
1359 newObjects.add(newOid);
1360 }
1361 }
1362 else if ((origOid != null) &&
1363 ((newOid == null) || !newOid.equals(origOid)))
1364 {
1365 markDelete(origOid, oid, false);
1366 }
1367 }
1368 }
1369
1370 return newObjects;
1371 }
1372
1373 /**
1374 * Mark for creation all objects that were included into dependent collections.
1375 * Mark for deletion all objects that were excluded from dependent collections.
1376 */
1377 private ArrayList handleDependentCollections(Identity oid, Object obj,
1378 Object[] origCollections, Object[] newCollections,
1379 Object[] newCollectionsOfObjects)
1380 throws LockingException
1381 {
1382 ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
1383 Collection colDescs = mif.getCollectionDescriptors();
1384 ArrayList newObjects = new ArrayList();
1385 int count = 0;
1386
1387 for (Iterator it = colDescs.iterator(); it.hasNext(); count++)
1388 {
1389 CollectionDescriptor cds = (CollectionDescriptor) it.next();
1390
1391 if (cds.getOtmDependent())
1392 {
1393 ArrayList origList = (origCollections == null ? null
1394 : (ArrayList) origCollections[count]);
1395 ArrayList newList = (ArrayList) newCollections[count];
1396
1397 if (origList != null)
1398 {
1399 for (Iterator it2 = origList.iterator(); it2.hasNext(); )
1400 {
1401 Identity origOid = (Identity) it2.next();
1402
1403 if ((newList == null) || !newList.contains(origOid))
1404 {
1405 markDelete(origOid, oid, true);
1406 }
1407 }
1408 }
1409
1410 if (newList != null)
1411 {
1412 int countElem = 0;
1413 for (Iterator it2 = newList.iterator(); it2.hasNext(); countElem++)
1414 {
1415 Identity newOid = (Identity) it2.next();
1416
1417 if ((origList == null) || !origList.contains(newOid))
1418 {
1419 ContextEntry entry = (ContextEntry) _objects.get(newOid);
1420
1421 if (entry == null)
1422 {
1423 ArrayList relCol = (ArrayList)
1424 newCollectionsOfObjects[count];
1425 Object relObj = relCol.get(countElem);
1426 insertInternal(newOid, relObj, LockType.WRITE_LOCK,
1427 true, null, new Stack());
1428 newObjects.add(newOid);
1429 }
1430 }
1431 }
1432 }
1433 }
1434 }
1435
1436 return newObjects;
1437 }
1438
1439 /**
1440 * Mark for deletion all dependent objects (via references and collections).
1441 * @return the number of deleted objects
1442 */
1443 private int doCascadeDelete(Identity oid, Object obj)
1444 {
1445 ClassDescriptor mif = _pb.getClassDescriptor(ProxyHelper.getRealClass(obj));
1446 Collection refDescs = mif.getObjectReferenceDescriptors();
1447 Collection colDescs = mif.getCollectionDescriptors();
1448 int countCascadeDeleted = 0;
1449
1450 for (Iterator it = refDescs.iterator(); it.hasNext(); )
1451 {
1452 ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it.next();
1453
1454 if (rds.getOtmDependent())
1455 {
1456 PersistentField f = rds.getPersistentField();
1457 Object relObj = f.get(obj);
1458
1459 if (relObj != null)
1460 {
1461 countCascadeDeleted +=
1462 markDelete(new Identity(relObj, _pb), oid, false);
1463 }
1464 }
1465 }
1466
1467 for (Iterator it = colDescs.iterator(); it.hasNext(); )
1468 {
1469 CollectionDescriptor cds = (CollectionDescriptor) it.next();
1470
1471 if (cds.getOtmDependent())
1472 {
1473 PersistentField f = cds.getPersistentField();
1474 Class type = f.getType();
1475 Object col = f.get(obj);
1476
1477 if (col != null)
1478 {
1479 Iterator colIterator;
1480
1481 if (Collection.class.isAssignableFrom(type))
1482 {
1483 colIterator = ((Collection) col).iterator();
1484 }
1485 else if (type.isArray())
1486 {
1487 colIterator = new ArrayIterator(col);
1488 }
1489 else
1490 {
1491 continue;
1492 }
1493
1494 while (colIterator.hasNext())
1495 {
1496
1497 countCascadeDeleted +=
1498 markDelete(new Identity(colIterator.next(), _pb), oid, true);
1499 }
1500 }
1501 }
1502 }
1503
1504 return countCascadeDeleted;
1505 }
1506
1507 /*
1508 * The rest of ObjectCache implementation for swizling
1509 * All methods except lookup() are never used by swizzling,
1510 * remove() appeared to already exist in this class
1511 * with the same signature as in ObjectCache interface,
1512 * other methods are unsupported.
1513 */
1514 public void cache(Identity oid, Object obj)
1515 {
1516 throw new UnsupportedOperationException();
1517 }
1518
1519 public boolean cacheIfNew(Identity oid, Object obj)
1520 {
1521 // not implemented
1522 throw new UnsupportedOperationException("Not implemented");
1523 }
1524
1525 public void clear()
1526 {
1527 throw new UnsupportedOperationException();
1528 }
1529
1530
1531 //////////////////////////////////////////
1532 // Inner classes
1533 //////////////////////////////////////////
1534
1535 private static class ContextEntry
1536 {
1537 Object userObject;
1538 Object cacheObject;
1539 State state = State.PERSISTENT_CLEAN;
1540
1541 /**
1542 * Handler the proxy object, null if the object is real
1543 */
1544 IndirectionHandler handler;
1545
1546 /**
1547 * This flag is used during commit/checkpoint
1548 */
1549 boolean needsCacheSwizzle;
1550
1551 ContextEntry(Object theUserObject)
1552 {
1553 userObject = theUserObject;
1554 if (userObject != null)
1555 {
1556 handler = ProxyHelper.getIndirectionHandler(userObject);
1557 if ((handler != null) && handler.alreadyMaterialized())
1558 {
1559 userObject = handler.getRealSubject();
1560 handler = null;
1561 }
1562 }
1563 }
1564 }
1565
1566 private class OTMCollectionProxyListener implements CollectionProxyListener
1567 {
1568 private final CollectionDescriptor _cds;
1569 private final ArrayList[] _collections;
1570 private final int _index;
1571 private final int _lock;
1572
1573 OTMCollectionProxyListener(CollectionDescriptor cds,
1574 ArrayList[] collections, int index, int lock)
1575 {
1576 _cds = cds;
1577 _collections = collections;
1578 _index = index;
1579 _lock = lock;
1580 }
1581
1582 public void beforeLoading(CollectionProxyDefaultImpl colProxy)
1583 {
1584 // do nothing
1585 }
1586
1587 /**
1588 * The collection proxy contains PB cache objects. We have to replace it
1589 * with a collection of user objects.
1590 */
1591 public void afterLoading(CollectionProxyDefaultImpl colProxy)
1592 {
1593 ArrayList list = new ArrayList();
1594 ArrayList newUserCol = new ArrayList();
1595 LockManager lockManager = LockManager.getInstance();
1596 _collections[_index] = list;
1597
1598 for (Iterator it = colProxy.iterator(); it.hasNext(); )
1599 {
1600 Object relUserObj;
1601 Object relCacheObj = it.next();
1602 Identity relOid = new Identity(relCacheObj, _pb);
1603 ContextEntry entry;
1604
1605 list.add(relOid);
1606 entry = (ContextEntry) _objects.get(relOid);
1607 if (entry != null)
1608 {
1609 relUserObj = entry.userObject;
1610 }
1611 else
1612 {
1613 ObjectCopyStrategy copyStrategy;
1614
1615 copyStrategy = _tx.getKit().getCopyStrategy(relOid);
1616 relUserObj = copyStrategy.copy(relCacheObj, _pb);
1617 try
1618 {
1619 entry = insertInternal(relOid, relUserObj, _lock,
1620 _cds.getOtmDependent(), null, new Stack());
1621 if (entry != null)
1622 {
1623 relUserObj = entry.userObject;
1624 }
1625 }
1626 catch (LockingException ex)
1627 {
1628 throw new LockingPassthruException(ex);
1629 }
1630 }
1631 newUserCol.add(relUserObj);
1632 }
1633 colProxy.clear();
1634 colProxy.addAll(newUserCol);
1635 }
1636 }
1637 }