Coverage Report - org.apache.ojb.otm.core.ConcreteEditingContext
 
Classes in this File Line Coverage Branch Coverage Complexity
ConcreteEditingContext
N/A
N/A
5.553
ConcreteEditingContext$1
N/A
N/A
5.553
ConcreteEditingContext$2
N/A
N/A
5.553
ConcreteEditingContext$ContextEntry
N/A
N/A
5.553
ConcreteEditingContext$OTMCollectionProxyListener
N/A
N/A
5.553
 
 1  
 package org.apache.ojb.otm.core;
 2  
 
 3  
 /* Copyright 2003-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import java.lang.reflect.Array;
 19  
 import java.math.BigDecimal;
 20  
 import java.util.ArrayList;
 21  
 import java.util.Arrays;
 22  
 import java.util.Collection;
 23  
 import java.util.Comparator;
 24  
 import java.util.Iterator;
 25  
 import java.util.HashMap;
 26  
 import java.util.HashSet;
 27  
 import java.util.Date;
 28  
 import java.util.List;
 29  
 import java.util.Set;
 30  
 import java.util.Stack;
 31  
 
 32  
 import org.apache.commons.collections.iterators.ArrayIterator;
 33  
 import org.apache.ojb.broker.Identity;
 34  
 import org.apache.ojb.broker.OJBRuntimeException;
 35  
 import org.apache.ojb.broker.PBKey;
 36  
 import org.apache.ojb.broker.PersistenceBroker;
 37  
 import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
 38  
 import org.apache.ojb.broker.cache.ObjectCache;
 39  
 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
 40  
 import org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl;
 41  
 import org.apache.ojb.broker.core.proxy.SetProxyDefaultImpl;
 42  
 import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
 43  
 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
 44  
 import org.apache.ojb.broker.core.proxy.MaterializationListener;
 45  
 import org.apache.ojb.broker.core.proxy.ProxyHelper;
 46  
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 47  
 import org.apache.ojb.broker.metadata.CollectionDescriptor;
 48  
 import org.apache.ojb.broker.metadata.FieldDescriptor;
 49  
 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
 50  
 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
 51  
 
 52  
 import org.apache.ojb.otm.EditingContext;
 53  
 import org.apache.ojb.otm.OTMKit;
 54  
 import org.apache.ojb.otm.copy.ObjectCopyStrategy;
 55  
 import org.apache.ojb.otm.lock.LockManager;
 56  
 import org.apache.ojb.otm.lock.LockType;
 57  
 import org.apache.ojb.otm.lock.LockingException;
 58  
 import org.apache.ojb.otm.states.State;
 59  
 import org.apache.ojb.otm.swizzle.Swizzling;
 60  
 
 61  
 /**
 62  
  *
 63  
  * Concrete implementation of EditingContext.
 64  
  *
 65  
  * @author  <a href="mailto:rraghuram@hotmail.com">Raghu Rajah</a>
 66  
  * @see     org.apache.ojb.otm.EditingContext
 67  
  *
 68  
  */
 69  
 public class ConcreteEditingContext
 70  
         implements EditingContext, MaterializationListener, ObjectCache
 71  
 {
 72  
     // for hasBidirectionalAssociation method
 73  
     // Maps PBKeys to the sets of classes with/without
 74  
     // bidirectional associations
 75  
     private static HashMap _withBidirAsscMap = new HashMap();
 76  
     private static HashMap _withoutBidirAsscMap = new HashMap();
 77  
 
 78  
     private HashSet _withBidirAssc;
 79  
     private HashSet _withoutBidirAssc;
 80  
 
 81  
     private HashMap _objects;
 82  
     private ArrayList _order;
 83  
     private Transaction _tx;
 84  
     private PersistenceBroker _pb;
 85  
     private HashMap _original;
 86  
     private HashMap _checkpointed;
 87  
     private HashMap _colProxyListeners;
 88  
 
 89  
     //////////////////////////////////////////
 90  
     // Constructor
 91  
     //////////////////////////////////////////
 92  
 
 93  
     public ConcreteEditingContext(Transaction tx, PersistenceBroker pb)
 94  
     {
 95  
         PBKey pbkey;
 96  
 
 97  
         _tx = tx;
 98  
         _pb = pb;
 99  
         _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  
 }