0001: package org.apache.ojb.otm.core;
0002:
0003: /* Copyright 2003-2005 The Apache Software Foundation
0004: *
0005: * Licensed under the Apache License, Version 2.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: import java.lang.reflect.Array;
0019: import java.math.BigDecimal;
0020: import java.util.ArrayList;
0021: import java.util.Arrays;
0022: import java.util.Collection;
0023: import java.util.Comparator;
0024: import java.util.Iterator;
0025: import java.util.HashMap;
0026: import java.util.HashSet;
0027: import java.util.Date;
0028: import java.util.List;
0029: import java.util.Set;
0030: import java.util.Stack;
0031:
0032: import org.apache.commons.collections.iterators.ArrayIterator;
0033: import org.apache.ojb.broker.Identity;
0034: import org.apache.ojb.broker.OJBRuntimeException;
0035: import org.apache.ojb.broker.PBKey;
0036: import org.apache.ojb.broker.PersistenceBroker;
0037: import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
0038: import org.apache.ojb.broker.cache.ObjectCache;
0039: import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
0040: import org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl;
0041: import org.apache.ojb.broker.core.proxy.SetProxyDefaultImpl;
0042: import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
0043: import org.apache.ojb.broker.core.proxy.IndirectionHandler;
0044: import org.apache.ojb.broker.core.proxy.MaterializationListener;
0045: import org.apache.ojb.broker.core.proxy.ProxyHelper;
0046: import org.apache.ojb.broker.metadata.ClassDescriptor;
0047: import org.apache.ojb.broker.metadata.CollectionDescriptor;
0048: import org.apache.ojb.broker.metadata.FieldDescriptor;
0049: import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
0050: import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
0051:
0052: import org.apache.ojb.otm.EditingContext;
0053: import org.apache.ojb.otm.OTMKit;
0054: import org.apache.ojb.otm.copy.ObjectCopyStrategy;
0055: import org.apache.ojb.otm.lock.LockManager;
0056: import org.apache.ojb.otm.lock.LockType;
0057: import org.apache.ojb.otm.lock.LockingException;
0058: import org.apache.ojb.otm.states.State;
0059: import org.apache.ojb.otm.swizzle.Swizzling;
0060:
0061: /**
0062: *
0063: * Concrete implementation of EditingContext.
0064: *
0065: * @author <a href="mailto:rraghuram@hotmail.com">Raghu Rajah</a>
0066: * @see org.apache.ojb.otm.EditingContext
0067: *
0068: */
0069: public class ConcreteEditingContext implements EditingContext,
0070: MaterializationListener, ObjectCache {
0071: // for hasBidirectionalAssociation method
0072: // Maps PBKeys to the sets of classes with/without
0073: // bidirectional associations
0074: private static HashMap _withBidirAsscMap = new HashMap();
0075: private static HashMap _withoutBidirAsscMap = new HashMap();
0076:
0077: private HashSet _withBidirAssc;
0078: private HashSet _withoutBidirAssc;
0079:
0080: private HashMap _objects;
0081: private ArrayList _order;
0082: private Transaction _tx;
0083: private PersistenceBroker _pb;
0084: private HashMap _original;
0085: private HashMap _checkpointed;
0086: private HashMap _colProxyListeners;
0087:
0088: //////////////////////////////////////////
0089: // Constructor
0090: //////////////////////////////////////////
0091:
0092: public ConcreteEditingContext(Transaction tx, PersistenceBroker pb) {
0093: PBKey pbkey;
0094:
0095: _tx = tx;
0096: _pb = pb;
0097: _objects = new HashMap();
0098: _order = new ArrayList();
0099: _original = new HashMap();
0100: _checkpointed = _original;
0101: pbkey = _pb.getPBKey();
0102: _withoutBidirAssc = (HashSet) _withoutBidirAsscMap.get(pbkey);
0103: if (_withoutBidirAssc != null) {
0104: _withBidirAssc = (HashSet) _withBidirAsscMap.get(pbkey);
0105: } else {
0106: _withoutBidirAssc = new HashSet();
0107: _withoutBidirAsscMap.put(pbkey, _withoutBidirAssc);
0108: _withBidirAssc = new HashSet();
0109: _withBidirAsscMap.put(pbkey, _withBidirAssc);
0110: }
0111: }
0112:
0113: //////////////////////////////////////////
0114: // EditingContext operations
0115: //////////////////////////////////////////
0116:
0117: /**
0118: * @see org.apache.ojb.otm.EditingContext#insert(Identity, Object, int)
0119: */
0120: public void insert(Identity oid, Object userObject, int lock)
0121: throws LockingException {
0122: ContextEntry entry;
0123:
0124: entry = insertInternal(oid, userObject, lock, true, null,
0125: new Stack());
0126: if ((entry != null) && entry.state.needsDelete()) {
0127: // Undelete it
0128: entry.state = State.PERSISTENT_CLEAN;
0129: }
0130: }
0131:
0132: private ContextEntry insertInternal(Identity oid,
0133: Object userObject, int lock, boolean canCreate,
0134: Identity insertBeforeThis, Stack stack)
0135: throws LockingException {
0136: ContextEntry entry;
0137: LockManager lockManager;
0138: Swizzling swizzlingStrategy;
0139: IndirectionHandler handler = null;
0140: OTMKit kit = _tx.getKit();
0141: // Are we building object's relations for the userObject in the transaction?
0142: // Otherwise we just get data from the "userObject" and put it into
0143: // the previously loaded/created object in the transaction
0144: boolean buildingObject = false;
0145: boolean lazySwizzle = false;
0146:
0147: if (lock == LockType.NO_LOCK) {
0148: return null;
0149: }
0150:
0151: entry = (ContextEntry) _objects.get(oid);
0152:
0153: if (userObject == null) {
0154: // invalidating object...
0155: _original.remove(oid);
0156: _checkpointed.remove(oid);
0157: if (entry != null) {
0158: entry.userObject = null;
0159: entry.cacheObject = null;
0160: }
0161: return entry;
0162: }
0163:
0164: lockManager = LockManager.getInstance();
0165: swizzlingStrategy = kit.getSwizzlingStrategy();
0166:
0167: handler = ProxyHelper.getIndirectionHandler(userObject);
0168: if ((handler != null) && handler.alreadyMaterialized()) {
0169: userObject = handler.getRealSubject();
0170: handler = null;
0171: }
0172:
0173: if ((entry == null) || (entry.userObject == null)) {
0174: // first insertion of the userObject into editing context
0175: Object swizzledObject = swizzlingStrategy.swizzle(
0176: userObject, null, _pb, this );
0177: entry = new ContextEntry(swizzledObject);
0178: if (entry.handler != null) {
0179: ObjectCopyStrategy copyStrategy = _tx.getKit()
0180: .getCopyStrategy(oid);
0181: entry.cacheObject = copyStrategy.copy(userObject, _pb);
0182: // Assume that object exists, otherwise were the proxy came from?
0183: _objects.put(oid, entry);
0184: lockManager.ensureLock(oid, _tx, lock, _pb); // lock after _objects.put to avoid hanged locks
0185: entry.handler.addListener(this );
0186: } else {
0187: Object origCacheObj = _pb.getObjectByIdentity(oid);
0188:
0189: if ((origCacheObj == null) && !canCreate) {
0190: // we don't create the objects by reachability
0191: throw new IllegalStateException(
0192: "Related object is neither persistent, nor otm-depentent: "
0193: + oid);
0194: }
0195: if (origCacheObj != null) {
0196: entry.cacheObject = origCacheObj;
0197: }
0198: buildingObject = true;
0199: _objects.put(oid, entry);
0200: lockManager.ensureLock(oid, _tx, lock, _pb); // lock after _objects.put to avoid hanged locks
0201:
0202: if (userObject != null) {
0203: if ((origCacheObj == null) && canCreate) {
0204: ObjectCopyStrategy copyStrategy = _tx.getKit()
0205: .getCopyStrategy(oid);
0206: entry.cacheObject = copyStrategy.copy(
0207: userObject, _pb);
0208: entry.state = State.PERSISTENT_NEW;
0209: if (kit.isEagerInsert(userObject)
0210: || hasBidirectionalAssociation(userObject
0211: .getClass())) {
0212: _pb.store(entry.cacheObject, entry.state);
0213: entry.state = State.PERSISTENT_CLEAN;
0214: origCacheObj = entry.cacheObject;
0215: }
0216: }
0217:
0218: if (origCacheObj != null) {
0219: _original.put(oid, getFields(userObject, false,
0220: true));
0221: }
0222: }
0223: }
0224: if (insertBeforeThis != null) {
0225: int insertIndex = _order.indexOf(insertBeforeThis);
0226: _order.add(insertIndex, oid);
0227: } else {
0228: _order.add(oid);
0229: }
0230: } else {
0231: // The object in context is the same object attempted an insert on
0232: // Ensure we have the correct lock level
0233: lockManager.ensureLock(oid, _tx, lock, _pb);
0234:
0235: if (handler == null) {
0236: if (!swizzlingStrategy.isSameInstance(entry.userObject,
0237: userObject)) {
0238: // the new object contains data to deal with
0239: if (entry.handler != null) {
0240: // materialize old object even if it is not
0241: // materialized yet, because we need a place
0242: // to copy the data from the new object
0243: entry.userObject = entry.handler
0244: .getRealSubject();
0245: entry.handler = null;
0246: }
0247: // swizzle after lockReachableObjects(), when all related objects
0248: // will be in the editing context
0249: lazySwizzle = true;
0250: }
0251: }
0252: }
0253:
0254: // perform automatic read lock for all reachable objects
0255: // if the inserted object is materialized
0256: if ((handler == null) && !stack.contains(userObject)) {
0257: stack.push(userObject);
0258: lockReachableObjects(oid, userObject, entry.cacheObject,
0259: lock, stack, buildingObject);
0260: stack.pop();
0261: if (lazySwizzle) {
0262: entry.userObject = swizzlingStrategy.swizzle(
0263: userObject, entry.userObject, _pb, this );
0264: }
0265: }
0266:
0267: return entry;
0268: }
0269:
0270: /**
0271: * Lock all objects reachable via 1:N and N:1 relations,
0272: * @param lock The lock type to use
0273: */
0274: private void lockReachableObjects(Identity oid, Object userObject,
0275: Object cacheObject, int lock, Stack stack,
0276: boolean buildingObject) throws LockingException {
0277: ContextEntry entry;
0278: boolean onlyDependants = !_tx.getKit().isImplicitLockingUsed();
0279: ClassDescriptor mif = _pb.getClassDescriptor(userObject
0280: .getClass());
0281:
0282: // N:1 relations
0283: Iterator iter = mif.getObjectReferenceDescriptors().iterator();
0284: ObjectReferenceDescriptor rds = null;
0285: PersistentField f;
0286: Object relUserObj;
0287: Identity relOid;
0288: boolean isDependent;
0289:
0290: while (iter.hasNext()) {
0291: rds = (ObjectReferenceDescriptor) iter.next();
0292: isDependent = rds.getOtmDependent();
0293: if (onlyDependants && !isDependent) {
0294: continue;
0295: }
0296: f = rds.getPersistentField();
0297: relUserObj = f.get(userObject);
0298: if (relUserObj != null) {
0299: relOid = new Identity(relUserObj, _pb);
0300: entry = (ContextEntry) _objects.get(relOid);
0301: if ((entry == null) || (entry.userObject != relUserObj)) {
0302: entry = insertInternal(relOid, relUserObj, lock,
0303: isDependent, oid, stack);
0304: if (buildingObject && (entry != null)) {
0305: f.set(userObject, entry.userObject);
0306: f.set(cacheObject, entry.cacheObject);
0307: }
0308: }
0309: }
0310: }
0311:
0312: // 1:N relations
0313: Iterator collections = mif.getCollectionDescriptors()
0314: .iterator();
0315: CollectionDescriptor cds;
0316: Object userCol;
0317: Iterator userColIterator;
0318: Class type;
0319: ArrayList newUserCol = null;
0320: ArrayList newCacheCol = null;
0321:
0322: while (collections.hasNext()) {
0323: cds = (CollectionDescriptor) collections.next();
0324: f = cds.getPersistentField();
0325: type = f.getType();
0326: isDependent = cds.getOtmDependent();
0327: if (onlyDependants && !isDependent) {
0328: continue;
0329: }
0330: userCol = f.get(userObject);
0331: if (userCol != null) {
0332: if ((userCol instanceof CollectionProxyDefaultImpl)
0333: && !((CollectionProxyDefaultImpl) userCol)
0334: .isLoaded()) {
0335: continue;
0336: }
0337:
0338: if (buildingObject) {
0339: newUserCol = new ArrayList();
0340: newCacheCol = new ArrayList();
0341: }
0342:
0343: if (Collection.class.isAssignableFrom(type)) {
0344: userColIterator = ((Collection) userCol).iterator();
0345: } else if (type.isArray()) {
0346: userColIterator = new ArrayIterator(userCol);
0347: } else {
0348: throw new OJBRuntimeException(
0349: userCol.getClass()
0350: + " can not be managed by OJB OTM, use Array or Collection instead !");
0351: }
0352:
0353: while (userColIterator.hasNext()) {
0354: relUserObj = userColIterator.next();
0355: relOid = new Identity(relUserObj, _pb);
0356: entry = (ContextEntry) _objects.get(relOid);
0357: if ((entry == null)
0358: || (entry.userObject != relUserObj)) {
0359: entry = insertInternal(relOid, relUserObj,
0360: lock, isDependent, null, stack);
0361: }
0362: if (buildingObject && (entry != null)) {
0363: newUserCol.add(entry.userObject);
0364: newCacheCol.add(entry.cacheObject);
0365: }
0366: }
0367: if (buildingObject) {
0368: setCollectionField(userObject, f, newUserCol);
0369: setCollectionField(cacheObject, f, newCacheCol);
0370: }
0371: }
0372: }
0373: }
0374:
0375: /**
0376: * @see org.apache.ojb.otm.EditingContext#remove(Identity)
0377: */
0378: public void remove(Identity oid) {
0379: _objects.remove(oid);
0380: _order.remove(oid);
0381: LockManager.getInstance().releaseLock(oid, _tx);
0382: }
0383:
0384: public void deletePersistent(Identity oid, Object userObject)
0385: throws LockingException {
0386: ContextEntry entry;
0387:
0388: entry = insertInternal(oid, userObject, LockType.WRITE_LOCK,
0389: true, null, new Stack());
0390: if (entry != null) {
0391: entry.state = entry.state.deletePersistent();
0392: }
0393: _order.remove(oid);
0394: _order.add(oid);
0395: }
0396:
0397: /**
0398: * @see org.apache.ojb.otm.EditingContext#lookup(Identity)
0399: */
0400: public Object lookup(Identity oid) {
0401: ContextEntry entry = (ContextEntry) _objects.get(oid);
0402: return (entry == null ? null : entry.userObject);
0403: }
0404:
0405: public boolean contains(Identity oid) {
0406: return lookup(oid) != null;
0407: }
0408:
0409: /**
0410: * @see org.apache.ojb.otm.EditingContext#lookupState(Identity)
0411: */
0412: public State lookupState(Identity oid) throws LockingException {
0413: State retval = null;
0414: ContextEntry entry = (ContextEntry) _objects.get(oid);
0415: if (entry != null) {
0416: /**
0417: * possibly return a clone so we don't allow people to tweak states.
0418: */
0419: retval = entry.state;
0420: }
0421: return retval;
0422: }
0423:
0424: /**
0425: * @see org.apache.ojb.otm.EditingContext#setState(Identity, State)
0426: */
0427: public void setState(Identity oid, State state) {
0428: ContextEntry entry = (ContextEntry) _objects.get(oid);
0429: entry.state = state;
0430: }
0431:
0432: public Collection getAllObjectsInContext() {
0433: return _objects.values();
0434: }
0435:
0436: //////////////////////////////////////////
0437: // MaterializationListener interface
0438: //////////////////////////////////////////
0439:
0440: public void beforeMaterialization(IndirectionHandler handler,
0441: Identity oid) {
0442: //noop
0443: }
0444:
0445: public void afterMaterialization(IndirectionHandler handler,
0446: Object cacheObject) {
0447: Identity oid = handler.getIdentity();
0448: ContextEntry entry = (ContextEntry) _objects.get(oid);
0449:
0450: if (entry == null) {
0451: return;
0452: }
0453:
0454: int lock = LockManager.getInstance().getLockHeld(oid, _tx);
0455: ObjectCopyStrategy copyStrategy = _tx.getKit().getCopyStrategy(
0456: oid);
0457: Object userObject = copyStrategy.copy(cacheObject, _pb);
0458: handler.setRealSubject(userObject);
0459: _original.put(oid, getFields(userObject, false, true));
0460:
0461: // replace the proxy object with the real one
0462: entry.userObject = userObject;
0463: entry.cacheObject = cacheObject;
0464: entry.handler.removeListener(this );
0465: entry.handler = null;
0466:
0467: // perform automatic lock for all reachable objects
0468: // if the inserted object is materialized
0469: try {
0470: lockReachableObjects(oid, userObject, cacheObject, lock,
0471: new Stack(), true);
0472: } catch (LockingException ex) {
0473: throw new LockingPassthruException(ex);
0474: }
0475: }
0476:
0477: //////////////////////////////////////////
0478: // Other operations
0479: //////////////////////////////////////////
0480:
0481: /**
0482: *
0483: * Commit this context into the persistent store.
0484: * The EditingContext is not usable after a commit.
0485: *
0486: */
0487: public void commit() throws TransactionAbortedException {
0488: checkpointInternal(true);
0489: releaseLocksAndClear();
0490: }
0491:
0492: private void releaseLocksAndClear() {
0493: releaseLocks();
0494: removeMaterializationListener();
0495: _objects.clear();
0496: _order.clear();
0497: _original.clear();
0498: if (_checkpointed != _original) {
0499: _checkpointed.clear();
0500: }
0501: }
0502:
0503: /**
0504: *
0505: * Writes all changes in this context into the persistent store.
0506: *
0507: */
0508: public void checkpoint() throws TransactionAbortedException {
0509: checkpointInternal(false);
0510: _checkpointed = new HashMap();
0511: for (Iterator iterator = _order.iterator(); iterator.hasNext();) {
0512: Identity oid = (Identity) iterator.next();
0513: ContextEntry entry = (ContextEntry) _objects.get(oid);
0514: if (entry.handler == null) {
0515: _checkpointed.put(oid, getFields(entry.userObject,
0516: false, true));
0517: }
0518: }
0519: }
0520:
0521: /**
0522: *
0523: * Writes all changes in this context into the persistent store.
0524: *
0525: */
0526: private void checkpointInternal(boolean isCommit)
0527: throws TransactionAbortedException {
0528: if (_order.size() == 0) {
0529: return;
0530: }
0531:
0532: removeCollectionProxyListeners();
0533:
0534: ConnectionManagerIF connMan = _pb.serviceConnectionManager();
0535: boolean saveBatchMode = connMan.isBatchMode();
0536: Swizzling swizzlingStrategy = _tx.getKit()
0537: .getSwizzlingStrategy();
0538: LockManager lockManager = LockManager.getInstance();
0539: Identity[] lockOrder = (Identity[]) _order
0540: .toArray(new Identity[_order.size()]);
0541: ObjectCache cache = _pb.serviceObjectCache();
0542: boolean isInsertVerified = _tx.getKit().isInsertVerified();
0543: ArrayList changedCollections = new ArrayList();
0544:
0545: // sort objects in the order of oid.hashCode to avoid deadlocks
0546: Arrays.sort(lockOrder, new Comparator() {
0547: public int compare(Object o1, Object o2) {
0548: return o1.hashCode() - o2.hashCode();
0549: }
0550:
0551: public boolean equals(Object obj) {
0552: return false;
0553: }
0554: });
0555:
0556: try {
0557: // mark dirty objects and lock them for write
0558: // also handle dependent objects and if there were inserted once,
0559: // repeat this process for their dependants ("cascade create")
0560: ArrayList newObjects = new ArrayList();
0561: int countNewObjects;
0562: do {
0563: newObjects.clear();
0564: countNewObjects = 0;
0565: for (int i = 0; i < lockOrder.length; i++) {
0566: Identity oid = lockOrder[i];
0567: ContextEntry entry = (ContextEntry) _objects
0568: .get(oid);
0569: State state = entry.state;
0570:
0571: if (entry.userObject == null) // invalidated
0572: {
0573: continue;
0574: }
0575:
0576: if (entry.handler == null) // materialized
0577: {
0578: if (!state.isDeleted()) {
0579: Object[][] origFields = (Object[][]) _checkpointed
0580: .get(oid);
0581: Object[][] newFields = getFields(
0582: entry.userObject, true, !isCommit);
0583:
0584: if (origFields == null) {
0585: entry.needsCacheSwizzle = true;
0586: newObjects
0587: .addAll(handleDependentReferences(
0588: oid, entry.userObject,
0589: null, newFields[0],
0590: newFields[2]));
0591: newObjects
0592: .addAll(handleDependentCollections(
0593: oid, entry.userObject,
0594: null, newFields[1],
0595: newFields[3]));
0596: } else {
0597: if (isModified(origFields[0],
0598: newFields[0])) {
0599: entry.state = state.markDirty();
0600: entry.needsCacheSwizzle = true;
0601: lockManager.ensureLock(oid, _tx,
0602: LockType.WRITE_LOCK, _pb);
0603: newObjects
0604: .addAll(handleDependentReferences(
0605: oid,
0606: entry.userObject,
0607: origFields[0],
0608: newFields[0],
0609: newFields[2]));
0610: }
0611:
0612: if (isModified(origFields[1],
0613: newFields[1])) {
0614: // there are modified collections,
0615: // so we need to lock the object and to swizzle it to cache
0616: entry.needsCacheSwizzle = true;
0617: lockManager.ensureLock(oid, _tx,
0618: LockType.WRITE_LOCK, _pb);
0619: newObjects
0620: .addAll(handleDependentCollections(
0621: oid,
0622: entry.userObject,
0623: origFields[1],
0624: newFields[1],
0625: newFields[3]));
0626: changedCollections.add(oid);
0627: }
0628: }
0629: }
0630: }
0631: }
0632: countNewObjects = newObjects.size();
0633: if (countNewObjects > 0) {
0634: // new objects are not locked, so we don't need to ensure the order
0635: lockOrder = (Identity[]) newObjects
0636: .toArray(new Identity[countNewObjects]);
0637: }
0638: } while (countNewObjects > 0);
0639:
0640: // Swizzle the context objects and the cache objects
0641: for (Iterator it = _order.iterator(); it.hasNext();) {
0642: Identity oid = (Identity) it.next();
0643: ContextEntry entry = (ContextEntry) _objects.get(oid);
0644:
0645: if (entry.needsCacheSwizzle) {
0646: entry.userObject = swizzlingStrategy
0647: .getRealTarget(entry.userObject);
0648: entry.cacheObject = swizzlingStrategy.swizzle(
0649: // we create the special ObjectCache implememntation
0650: // that returns cacheObject, not userObject
0651: entry.userObject, entry.cacheObject, _pb,
0652: new ObjectCache() {
0653: public Object lookup(Identity anOid) {
0654: ContextEntry ent = (ContextEntry) _objects
0655: .get(anOid);
0656: return (ent == null ? null
0657: : ent.cacheObject);
0658: }
0659:
0660: public boolean contains(Identity oid) {
0661: return lookup(oid) != null;
0662: }
0663:
0664: public void cache(Identity anOid,
0665: Object obj) {
0666: // do nothing
0667: }
0668:
0669: public boolean cacheIfNew(Identity oid,
0670: Object obj) {
0671: return false;
0672: }
0673:
0674: public void clear() {
0675: // do nothing
0676: }
0677:
0678: public void remove(Identity anOid) {
0679: // do nothing
0680: }
0681: });
0682: }
0683: }
0684:
0685: // Cascade delete for dependent objects
0686: int countCascadeDeleted;
0687: do {
0688: countCascadeDeleted = 0;
0689: // Use intermediate new ArrayList(_order) because _order
0690: // may be changed during cascade delete
0691: for (Iterator it = (new ArrayList(_order)).iterator(); it
0692: .hasNext();) {
0693: Identity oid = (Identity) it.next();
0694: ContextEntry entry = (ContextEntry) _objects
0695: .get(oid);
0696:
0697: if (entry.state.isDeleted()) {
0698: countCascadeDeleted += doCascadeDelete(oid,
0699: entry.userObject);
0700: }
0701: }
0702: } while (countCascadeDeleted > 0);
0703:
0704: // perform database operations
0705: connMan.setBatchMode(true);
0706: try {
0707: for (Iterator it = _order.iterator(); it.hasNext();) {
0708: Identity oid = (Identity) it.next();
0709: ContextEntry entry = (ContextEntry) _objects
0710: .get(oid);
0711: State state = entry.state;
0712:
0713: if (!state.needsInsert() && !state.needsUpdate()
0714: && !state.needsDelete()) {
0715: if (changedCollections.contains(oid)) {
0716: _pb.store(entry.cacheObject, state);
0717: }
0718: continue;
0719: }
0720:
0721: if (state.needsInsert()) {
0722: if (isInsertVerified) {
0723: // PB verifies object existence by default
0724: _pb.store(entry.cacheObject);
0725: } else {
0726: // PB migth already created the object by auto-update
0727: if (cache.lookup(oid) == null) {
0728: _pb.store(entry.cacheObject, state);
0729: }
0730: }
0731:
0732: } else if (state.needsUpdate()) {
0733: _pb.store(entry.cacheObject, state);
0734: } else if (state.needsDelete()) {
0735: _pb.delete(entry.cacheObject);
0736: }
0737: entry.state = state.commit();
0738: }
0739: connMan.executeBatch();
0740: } finally {
0741: connMan.setBatchMode(saveBatchMode);
0742: }
0743: } catch (Throwable ex) {
0744: ex.printStackTrace();
0745: throw new TransactionAbortedException(ex);
0746: }
0747: }
0748:
0749: /**
0750: *
0751: * Rollback all changes made during this transaction. The EditingContext is not usable after
0752: * a rollback.
0753: *
0754: */
0755: public void rollback() {
0756: for (Iterator iterator = _order.iterator(); iterator.hasNext();) {
0757: Identity oid = (Identity) iterator.next();
0758: ContextEntry entry = (ContextEntry) _objects.get(oid);
0759: entry.state = entry.state.rollback();
0760: Object[][] origFields = (Object[][]) _original.get(oid);
0761: if (origFields != null) {
0762: setFields(entry.userObject, origFields);
0763: setFields(entry.cacheObject, origFields);
0764: }
0765: }
0766: releaseLocksAndClear();
0767: }
0768:
0769: /**
0770: *
0771: * Rollback all changes made during this transaction to the given object.
0772: *
0773: */
0774: public void refresh(Identity oid, Object object) {
0775: ContextEntry entry = (ContextEntry) _objects.get(oid);
0776: Object[][] origFields = (Object[][]) _original.get(oid);
0777: if (origFields != null) {
0778: setFields(entry.userObject, origFields);
0779: if (object != entry.userObject) {
0780: setFields(object, origFields);
0781: }
0782: }
0783: entry.state = entry.state.refresh();
0784: }
0785:
0786: private void removeMaterializationListener() {
0787: for (Iterator it = _order.iterator(); it.hasNext();) {
0788: Identity oid = (Identity) it.next();
0789: ContextEntry entry = (ContextEntry) _objects.get(oid);
0790: if (entry.handler != null) {
0791: entry.handler.removeListener(this );
0792: }
0793: }
0794: }
0795:
0796: private void removeCollectionProxyListeners() {
0797: if (_colProxyListeners != null) {
0798: for (Iterator it = _colProxyListeners.keySet().iterator(); it
0799: .hasNext();) {
0800: CollectionProxyListener listener = (CollectionProxyListener) it
0801: .next();
0802: CollectionProxyDefaultImpl colProxy = (CollectionProxyDefaultImpl) _colProxyListeners
0803: .get(listener);
0804: colProxy.removeListener(listener);
0805: }
0806: _colProxyListeners.clear();
0807: }
0808: }
0809:
0810: private void releaseLocks() {
0811: LockManager lockManager = LockManager.getInstance();
0812:
0813: for (Iterator it = _objects.keySet().iterator(); it.hasNext();) {
0814: Identity oid = (Identity) it.next();
0815: lockManager.releaseLock(oid, _tx);
0816: }
0817: _tx.getKit().getLockMap().gc();
0818: }
0819:
0820: /**
0821: * This method compared simple field values:
0822: * there are some tricks...
0823: */
0824: private boolean isEqual(Object fld1, Object fld2) {
0825: if (fld1 == null || fld2 == null) {
0826: return (fld1 == fld2);
0827: } else if ((fld1 instanceof BigDecimal)
0828: && (fld2 instanceof BigDecimal)) {
0829: return (((BigDecimal) fld1).compareTo((BigDecimal) fld2) == 0);
0830: } else if ((fld1 instanceof Date) && (fld2 instanceof Date)) {
0831: return (((Date) fld1).getTime() == ((Date) fld2).getTime());
0832: } else {
0833: return fld1.equals(fld2);
0834: }
0835: }
0836:
0837: private boolean isModified(Object[] newFields, Object[] oldFields) {
0838: if (newFields.length != oldFields.length) {
0839: return true;
0840: }
0841:
0842: for (int i = 0; i < newFields.length; i++) {
0843: if (!isEqual(newFields[i], oldFields[i])) {
0844: return true;
0845: }
0846: }
0847:
0848: return false;
0849: }
0850:
0851: /**
0852: * @param addListeners Whether to add CollectionProxy listeners
0853: * @return four arrays of field values:
0854: * 1) The class, simple fields, identities of references
0855: * 2) collections of identities
0856: * 3) references (parallel to identities of references in 1)
0857: * 4) collections of objects (parallel to collections of identities in 2)
0858: * if "withObjects" parameter is "false", then returns nulls
0859: * in places of 3) and 4)
0860: */
0861: private Object[][] getFields(Object obj, boolean withObjects,
0862: boolean addListeners) {
0863: ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
0864: FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
0865: Collection refDescs = mif.getObjectReferenceDescriptors();
0866: Collection colDescs = mif.getCollectionDescriptors();
0867: int count = 0;
0868: Object[] fields = new Object[1 + fieldDescs.length
0869: + refDescs.size()];
0870: ArrayList[] collections = new ArrayList[colDescs.size()];
0871: Object[] references = null;
0872: ArrayList[] collectionsOfObjects = null;
0873: int lockForListeners = LockType.NO_LOCK;
0874:
0875: if (withObjects) {
0876: references = new Object[refDescs.size()];
0877: collectionsOfObjects = new ArrayList[colDescs.size()];
0878: }
0879:
0880: if (addListeners) {
0881: lockForListeners = LockManager.getInstance().getLockHeld(
0882: new Identity(obj, _pb), _tx);
0883: }
0884:
0885: fields[0] = obj.getClass(); // we must notice if the object class changes
0886: count++;
0887:
0888: for (int i = 0; i < fieldDescs.length; i++) {
0889: FieldDescriptor fd = fieldDescs[i];
0890: PersistentField f = fd.getPersistentField();
0891: fields[count] = f.get(obj);
0892: count++;
0893: }
0894:
0895: int countRefs = 0;
0896: for (Iterator it = refDescs.iterator(); it.hasNext(); count++, countRefs++) {
0897: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it
0898: .next();
0899: PersistentField f = rds.getPersistentField();
0900: Object relObj = f.get(obj);
0901: if (relObj != null) {
0902: fields[count] = new Identity(relObj, _pb);
0903: if (withObjects) {
0904: references[countRefs] = relObj;
0905: }
0906: }
0907: }
0908:
0909: count = 0;
0910: for (Iterator it = colDescs.iterator(); it.hasNext(); count++) {
0911: CollectionDescriptor cds = (CollectionDescriptor) it.next();
0912: PersistentField f = cds.getPersistentField();
0913: Class type = f.getType();
0914: Object col = f.get(obj);
0915:
0916: if ((col != null)
0917: && (col instanceof CollectionProxyDefaultImpl)
0918: && !((CollectionProxyDefaultImpl) col).isLoaded()) {
0919: if (addListeners) {
0920: OTMCollectionProxyListener listener = new OTMCollectionProxyListener(
0921: cds, collections, count, lockForListeners);
0922:
0923: ((CollectionProxyDefaultImpl) col)
0924: .addListener(listener);
0925: if (_colProxyListeners == null) {
0926: _colProxyListeners = new HashMap();
0927: }
0928: _colProxyListeners.put(listener, col);
0929: }
0930: continue;
0931: }
0932:
0933: if (col != null) {
0934: ArrayList list = new ArrayList();
0935: ArrayList listOfObjects = null;
0936: Iterator colIterator;
0937:
0938: collections[count] = list;
0939: if (withObjects) {
0940: listOfObjects = new ArrayList();
0941: collectionsOfObjects[count] = listOfObjects;
0942: }
0943:
0944: if (Collection.class.isAssignableFrom(type)) {
0945: colIterator = ((Collection) col).iterator();
0946: } else if (type.isArray()) {
0947: colIterator = new ArrayIterator(col);
0948: } else {
0949: continue;
0950: }
0951:
0952: while (colIterator.hasNext()) {
0953: Object relObj = colIterator.next();
0954: list.add(new Identity(relObj, _pb));
0955: if (withObjects) {
0956: listOfObjects.add(relObj);
0957: }
0958: }
0959: }
0960: }
0961:
0962: return new Object[][] { fields, collections, references,
0963: collectionsOfObjects };
0964: }
0965:
0966: private void setFields(Object obj, Object[][] fieldsAndCollections) {
0967: ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
0968: FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
0969: Collection refDescs = mif.getObjectReferenceDescriptors();
0970: Collection colDescs = mif.getCollectionDescriptors();
0971: Object[] fields = fieldsAndCollections[0];
0972: ArrayList[] collections = (ArrayList[]) fieldsAndCollections[1];
0973: int count = 0;
0974:
0975: if (!fields[0].equals(obj.getClass())) {
0976: System.err
0977: .println("Can't restore the object fields "
0978: + "since its class changed during transaction from "
0979: + fields[0] + " to " + obj.getClass());
0980: return;
0981: }
0982: count++;
0983:
0984: for (int i = 0; i < fieldDescs.length; i++) {
0985: FieldDescriptor fd = fieldDescs[i];
0986: PersistentField f = fd.getPersistentField();
0987: f.set(obj, fields[count]);
0988: count++;
0989: }
0990:
0991: for (Iterator it = refDescs.iterator(); it.hasNext(); count++) {
0992: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it
0993: .next();
0994: PersistentField f = rds.getPersistentField();
0995: Identity oid = (Identity) fields[count];
0996: Object relObj;
0997: if (oid == null) {
0998: relObj = null;
0999: } else {
1000: relObj = _pb.getObjectByIdentity(oid);
1001: }
1002: f.set(obj, relObj);
1003: }
1004:
1005: count = 0;
1006: for (Iterator it = colDescs.iterator(); it.hasNext(); count++) {
1007: CollectionDescriptor cds = (CollectionDescriptor) it.next();
1008: PersistentField f = cds.getPersistentField();
1009: ArrayList list = collections[count];
1010: ArrayList newCol;
1011:
1012: if (list == null) {
1013: f.set(obj, null);
1014: } else {
1015: newCol = new ArrayList();
1016: for (Iterator it2 = list.iterator(); it2.hasNext();) {
1017: Identity relOid = (Identity) it2.next();
1018: Object relObj = _pb.getObjectByIdentity(relOid);
1019:
1020: if (relObj != null) {
1021: newCol.add(relObj);
1022: }
1023: }
1024: setCollectionField(obj, f, newCol);
1025: }
1026: }
1027: }
1028:
1029: private void setCollectionField(Object obj, PersistentField f,
1030: List newCol) {
1031: Class type = f.getType();
1032:
1033: if (Collection.class.isAssignableFrom(type)) {
1034: Collection col = (Collection) f.get(obj);
1035:
1036: if (col == null) {
1037: if (type == List.class || type == Collection.class) {
1038: col = new ArrayList();
1039: } else if (type == Set.class) {
1040: col = new HashSet();
1041: } else {
1042: try {
1043: col = (Collection) type.newInstance();
1044: } catch (Throwable ex) {
1045: System.err
1046: .println("Cannot instantiate collection field: "
1047: + f);
1048: ex.printStackTrace();
1049: return;
1050: }
1051: }
1052: } else {
1053: if (col instanceof CollectionProxyDefaultImpl) {
1054: CollectionProxyDefaultImpl cp = (CollectionProxyDefaultImpl) col;
1055: if (col instanceof List) {
1056: col = new ListProxyDefaultImpl(_pb.getPBKey(),
1057: cp.getData().getClass(), null);
1058: } else if (col instanceof Set) {
1059: col = new SetProxyDefaultImpl(_pb.getPBKey(),
1060: cp.getData().getClass(), null);
1061: } else {
1062: col = new CollectionProxyDefaultImpl(_pb
1063: .getPBKey(), cp.getData().getClass(),
1064: null);
1065: }
1066: col.clear();
1067: } else {
1068: try {
1069: col = (Collection) col.getClass().newInstance();
1070: } catch (Exception ex) {
1071: System.err
1072: .println("Cannot instantiate collection field: "
1073: + f);
1074: ex.printStackTrace();
1075: return;
1076: }
1077: }
1078: }
1079: col.addAll(newCol);
1080: f.set(obj, col);
1081: } else if (type.isArray()) {
1082: int length = newCol.size();
1083: Object array = Array.newInstance(type.getComponentType(),
1084: length);
1085:
1086: for (int i = 0; i < length; i++) {
1087: Array.set(array, i, newCol.get(i));
1088: }
1089: f.set(obj, array);
1090: }
1091: }
1092:
1093: /**
1094: * Does the given class has bidirectional assiciation
1095: * with some other class?
1096: */
1097: private boolean hasBidirectionalAssociation(Class clazz) {
1098: ClassDescriptor cdesc;
1099: Collection refs;
1100: boolean hasBidirAssc;
1101:
1102: if (_withoutBidirAssc.contains(clazz)) {
1103: return false;
1104: }
1105:
1106: if (_withBidirAssc.contains(clazz)) {
1107: return true;
1108: }
1109:
1110: // first time we meet this class, let's look at metadata
1111: cdesc = _pb.getClassDescriptor(clazz);
1112: refs = cdesc.getObjectReferenceDescriptors();
1113: hasBidirAssc = false;
1114: REFS_CYCLE: for (Iterator it = refs.iterator(); it.hasNext();) {
1115: ObjectReferenceDescriptor ord;
1116: ClassDescriptor relCDesc;
1117: Collection relRefs;
1118:
1119: ord = (ObjectReferenceDescriptor) it.next();
1120: relCDesc = _pb.getClassDescriptor(ord.getItemClass());
1121: relRefs = relCDesc.getObjectReferenceDescriptors();
1122: for (Iterator relIt = relRefs.iterator(); relIt.hasNext();) {
1123: ObjectReferenceDescriptor relOrd;
1124:
1125: relOrd = (ObjectReferenceDescriptor) relIt.next();
1126: if (relOrd.getItemClass().equals(clazz)) {
1127: hasBidirAssc = true;
1128: break REFS_CYCLE;
1129: }
1130: }
1131: }
1132: if (hasBidirAssc) {
1133: _withBidirAssc.add(clazz);
1134: } else {
1135: _withoutBidirAssc.add(clazz);
1136: }
1137:
1138: return hasBidirAssc;
1139: }
1140:
1141: /**
1142: * @return number of deleted objects: 1 or 0 (if the object is already deleted)
1143: */
1144: private int markDelete(Identity oid, Identity mainOid,
1145: boolean isCollection) {
1146: ContextEntry entry = (ContextEntry) _objects.get(oid);
1147:
1148: if (entry == null) {
1149: throw new IllegalStateException(
1150: "markDelete failed: the dependent object " + oid
1151: + " is not in the editing context");
1152: }
1153:
1154: if (entry.state.isDeleted()) {
1155: return 0;
1156: } else {
1157: entry.state = entry.state.deletePersistent();
1158: if (mainOid != null) {
1159: int dependentIndex = _order.indexOf(oid);
1160: int mainIndex = _order.indexOf(mainOid);
1161:
1162: if (isCollection) // remove collection item before main obj
1163: {
1164: if (dependentIndex > mainIndex) {
1165: _order.remove(dependentIndex);
1166: _order.add(mainIndex, oid);
1167: }
1168: } else // remove reference after main obj
1169: {
1170: if (dependentIndex < mainIndex) {
1171: _order.remove(dependentIndex); // this causes mainIndex--
1172: _order.add(mainIndex, oid);
1173: }
1174: }
1175:
1176: }
1177: return 1;
1178: }
1179: }
1180:
1181: /**
1182: * Mark for creation all newly introduced dependent references.
1183: * Mark for deletion all nullified dependent references.
1184: * @return the list of created objects
1185: */
1186: private ArrayList handleDependentReferences(Identity oid,
1187: Object userObject, Object[] origFields, Object[] newFields,
1188: Object[] newRefs) throws LockingException {
1189: ClassDescriptor mif = _pb.getClassDescriptor(userObject
1190: .getClass());
1191: FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
1192: Collection refDescs = mif.getObjectReferenceDescriptors();
1193: int count = 1 + fieldDescs.length;
1194: ArrayList newObjects = new ArrayList();
1195: int countRefs = 0;
1196:
1197: for (Iterator it = refDescs.iterator(); it.hasNext(); count++, countRefs++) {
1198: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it
1199: .next();
1200: Identity origOid = (origFields == null ? null
1201: : (Identity) origFields[count]);
1202: Identity newOid = (Identity) newFields[count];
1203:
1204: if (rds.getOtmDependent()) {
1205: if ((origOid == null) && (newOid != null)) {
1206: ContextEntry entry = (ContextEntry) _objects
1207: .get(newOid);
1208:
1209: if (entry == null) {
1210: Object relObj = newRefs[countRefs];
1211: insertInternal(newOid, relObj,
1212: LockType.WRITE_LOCK, true, oid,
1213: new Stack());
1214: newObjects.add(newOid);
1215: }
1216: } else if ((origOid != null)
1217: && ((newOid == null) || !newOid.equals(origOid))) {
1218: markDelete(origOid, oid, false);
1219: }
1220: }
1221: }
1222:
1223: return newObjects;
1224: }
1225:
1226: /**
1227: * Mark for creation all objects that were included into dependent collections.
1228: * Mark for deletion all objects that were excluded from dependent collections.
1229: */
1230: private ArrayList handleDependentCollections(Identity oid,
1231: Object obj, Object[] origCollections,
1232: Object[] newCollections, Object[] newCollectionsOfObjects)
1233: throws LockingException {
1234: ClassDescriptor mif = _pb.getClassDescriptor(obj.getClass());
1235: Collection colDescs = mif.getCollectionDescriptors();
1236: ArrayList newObjects = new ArrayList();
1237: int count = 0;
1238:
1239: for (Iterator it = colDescs.iterator(); it.hasNext(); count++) {
1240: CollectionDescriptor cds = (CollectionDescriptor) it.next();
1241:
1242: if (cds.getOtmDependent()) {
1243: ArrayList origList = (origCollections == null ? null
1244: : (ArrayList) origCollections[count]);
1245: ArrayList newList = (ArrayList) newCollections[count];
1246:
1247: if (origList != null) {
1248: for (Iterator it2 = origList.iterator(); it2
1249: .hasNext();) {
1250: Identity origOid = (Identity) it2.next();
1251:
1252: if ((newList == null)
1253: || !newList.contains(origOid)) {
1254: markDelete(origOid, oid, true);
1255: }
1256: }
1257: }
1258:
1259: if (newList != null) {
1260: int countElem = 0;
1261: for (Iterator it2 = newList.iterator(); it2
1262: .hasNext(); countElem++) {
1263: Identity newOid = (Identity) it2.next();
1264:
1265: if ((origList == null)
1266: || !origList.contains(newOid)) {
1267: ContextEntry entry = (ContextEntry) _objects
1268: .get(newOid);
1269:
1270: if (entry == null) {
1271: ArrayList relCol = (ArrayList) newCollectionsOfObjects[count];
1272: Object relObj = relCol.get(countElem);
1273: insertInternal(newOid, relObj,
1274: LockType.WRITE_LOCK, true,
1275: null, new Stack());
1276: newObjects.add(newOid);
1277: }
1278: }
1279: }
1280: }
1281: }
1282: }
1283:
1284: return newObjects;
1285: }
1286:
1287: /**
1288: * Mark for deletion all dependent objects (via references and collections).
1289: * @return the number of deleted objects
1290: */
1291: private int doCascadeDelete(Identity oid, Object obj) {
1292: ClassDescriptor mif = _pb.getClassDescriptor(ProxyHelper
1293: .getRealClass(obj));
1294: Collection refDescs = mif.getObjectReferenceDescriptors();
1295: Collection colDescs = mif.getCollectionDescriptors();
1296: int countCascadeDeleted = 0;
1297:
1298: for (Iterator it = refDescs.iterator(); it.hasNext();) {
1299: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) it
1300: .next();
1301:
1302: if (rds.getOtmDependent()) {
1303: PersistentField f = rds.getPersistentField();
1304: Object relObj = f.get(obj);
1305:
1306: if (relObj != null) {
1307: countCascadeDeleted += markDelete(new Identity(
1308: relObj, _pb), oid, false);
1309: }
1310: }
1311: }
1312:
1313: for (Iterator it = colDescs.iterator(); it.hasNext();) {
1314: CollectionDescriptor cds = (CollectionDescriptor) it.next();
1315:
1316: if (cds.getOtmDependent()) {
1317: PersistentField f = cds.getPersistentField();
1318: Class type = f.getType();
1319: Object col = f.get(obj);
1320:
1321: if (col != null) {
1322: Iterator colIterator;
1323:
1324: if (Collection.class.isAssignableFrom(type)) {
1325: colIterator = ((Collection) col).iterator();
1326: } else if (type.isArray()) {
1327: colIterator = new ArrayIterator(col);
1328: } else {
1329: continue;
1330: }
1331:
1332: while (colIterator.hasNext()) {
1333:
1334: countCascadeDeleted += markDelete(new Identity(
1335: colIterator.next(), _pb), oid, true);
1336: }
1337: }
1338: }
1339: }
1340:
1341: return countCascadeDeleted;
1342: }
1343:
1344: /*
1345: * The rest of ObjectCache implementation for swizling
1346: * All methods except lookup() are never used by swizzling,
1347: * remove() appeared to already exist in this class
1348: * with the same signature as in ObjectCache interface,
1349: * other methods are unsupported.
1350: */
1351: public void cache(Identity oid, Object obj) {
1352: throw new UnsupportedOperationException();
1353: }
1354:
1355: public boolean cacheIfNew(Identity oid, Object obj) {
1356: // not implemented
1357: throw new UnsupportedOperationException("Not implemented");
1358: }
1359:
1360: public void clear() {
1361: throw new UnsupportedOperationException();
1362: }
1363:
1364: //////////////////////////////////////////
1365: // Inner classes
1366: //////////////////////////////////////////
1367:
1368: private static class ContextEntry {
1369: Object userObject;
1370: Object cacheObject;
1371: State state = State.PERSISTENT_CLEAN;
1372:
1373: /**
1374: * Handler the proxy object, null if the object is real
1375: */
1376: IndirectionHandler handler;
1377:
1378: /**
1379: * This flag is used during commit/checkpoint
1380: */
1381: boolean needsCacheSwizzle;
1382:
1383: ContextEntry(Object theUserObject) {
1384: userObject = theUserObject;
1385: if (userObject != null) {
1386: handler = ProxyHelper.getIndirectionHandler(userObject);
1387: if ((handler != null) && handler.alreadyMaterialized()) {
1388: userObject = handler.getRealSubject();
1389: handler = null;
1390: }
1391: }
1392: }
1393: }
1394:
1395: private class OTMCollectionProxyListener implements
1396: CollectionProxyListener {
1397: private final CollectionDescriptor _cds;
1398: private final ArrayList[] _collections;
1399: private final int _index;
1400: private final int _lock;
1401:
1402: OTMCollectionProxyListener(CollectionDescriptor cds,
1403: ArrayList[] collections, int index, int lock) {
1404: _cds = cds;
1405: _collections = collections;
1406: _index = index;
1407: _lock = lock;
1408: }
1409:
1410: public void beforeLoading(CollectionProxyDefaultImpl colProxy) {
1411: // do nothing
1412: }
1413:
1414: /**
1415: * The collection proxy contains PB cache objects. We have to replace it
1416: * with a collection of user objects.
1417: */
1418: public void afterLoading(CollectionProxyDefaultImpl colProxy) {
1419: ArrayList list = new ArrayList();
1420: ArrayList newUserCol = new ArrayList();
1421: LockManager lockManager = LockManager.getInstance();
1422: _collections[_index] = list;
1423:
1424: for (Iterator it = colProxy.iterator(); it.hasNext();) {
1425: Object relUserObj;
1426: Object relCacheObj = it.next();
1427: Identity relOid = new Identity(relCacheObj, _pb);
1428: ContextEntry entry;
1429:
1430: list.add(relOid);
1431: entry = (ContextEntry) _objects.get(relOid);
1432: if (entry != null) {
1433: relUserObj = entry.userObject;
1434: } else {
1435: ObjectCopyStrategy copyStrategy;
1436:
1437: copyStrategy = _tx.getKit().getCopyStrategy(relOid);
1438: relUserObj = copyStrategy.copy(relCacheObj, _pb);
1439: try {
1440: entry = insertInternal(relOid, relUserObj,
1441: _lock, _cds.getOtmDependent(), null,
1442: new Stack());
1443: if (entry != null) {
1444: relUserObj = entry.userObject;
1445: }
1446: } catch (LockingException ex) {
1447: throw new LockingPassthruException(ex);
1448: }
1449: }
1450: newUserCol.add(relUserObj);
1451: }
1452: colProxy.clear();
1453: colProxy.addAll(newUserCol);
1454: }
1455: }
1456: }
|