Coverage Report - org.apache.ojb.broker.core.PersistenceBrokerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PersistenceBrokerImpl
N/A
N/A
3.488
 
 1  
 package org.apache.ojb.broker.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.Constructor;
 19  
 import java.util.Collection;
 20  
 import java.util.Collections;
 21  
 import java.util.Enumeration;
 22  
 import java.util.HashSet;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 import java.util.Set;
 26  
 
 27  
 import org.apache.commons.lang.ObjectUtils;
 28  
 import org.apache.ojb.broker.Identity;
 29  
 import org.apache.ojb.broker.IdentityFactory;
 30  
 import org.apache.ojb.broker.ManageableCollection;
 31  
 import org.apache.ojb.broker.MtoNImplementor;
 32  
 import org.apache.ojb.broker.OptimisticLockException;
 33  
 import org.apache.ojb.broker.PBKey;
 34  
 import org.apache.ojb.broker.PBState;
 35  
 import org.apache.ojb.broker.PersistenceBrokerException;
 36  
 import org.apache.ojb.broker.TransactionAbortedException;
 37  
 import org.apache.ojb.broker.TransactionInProgressException;
 38  
 import org.apache.ojb.broker.TransactionNotInProgressException;
 39  
 import org.apache.ojb.broker.accesslayer.ChainingIterator;
 40  
 import org.apache.ojb.broker.accesslayer.ConnectionManagerFactory;
 41  
 import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
 42  
 import org.apache.ojb.broker.accesslayer.JdbcAccess;
 43  
 import org.apache.ojb.broker.accesslayer.JdbcAccessFactory;
 44  
 import org.apache.ojb.broker.accesslayer.OJBIterator;
 45  
 import org.apache.ojb.broker.accesslayer.PagingIterator;
 46  
 import org.apache.ojb.broker.accesslayer.PkEnumeration;
 47  
 import org.apache.ojb.broker.accesslayer.RelationshipPrefetcherFactory;
 48  
 import org.apache.ojb.broker.accesslayer.StatementManagerFactory;
 49  
 import org.apache.ojb.broker.accesslayer.StatementManagerIF;
 50  
 import org.apache.ojb.broker.accesslayer.sql.SqlGenerator;
 51  
 import org.apache.ojb.broker.accesslayer.sql.SqlGeneratorFactory;
 52  
 import org.apache.ojb.broker.cache.MaterializationCache;
 53  
 import org.apache.ojb.broker.cache.ObjectCache;
 54  
 import org.apache.ojb.broker.cache.ObjectCacheFactory;
 55  
 import org.apache.ojb.broker.cache.ObjectCacheInternal;
 56  
 import org.apache.ojb.broker.core.proxy.AbstractProxyFactory;
 57  
 import org.apache.ojb.broker.core.proxy.CollectionProxy;
 58  
 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
 59  
 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
 60  
 import org.apache.ojb.broker.core.proxy.ProxyFactory;
 61  
 import org.apache.ojb.broker.core.proxy.VirtualProxy;
 62  
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 63  
 import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
 64  
 import org.apache.ojb.broker.metadata.CollectionDescriptor;
 65  
 import org.apache.ojb.broker.metadata.DescriptorRepository;
 66  
 import org.apache.ojb.broker.metadata.FieldDescriptor;
 67  
 import org.apache.ojb.broker.metadata.MetadataManager;
 68  
 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
 69  
 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
 70  
 import org.apache.ojb.broker.query.Query;
 71  
 import org.apache.ojb.broker.query.QueryByIdentity;
 72  
 import org.apache.ojb.broker.query.QueryBySQL;
 73  
 import org.apache.ojb.broker.util.BrokerHelper;
 74  
 import org.apache.ojb.broker.util.IdentityArrayList;
 75  
 import org.apache.ojb.broker.util.ObjectModification;
 76  
 import org.apache.ojb.broker.util.logging.Logger;
 77  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 78  
 import org.apache.ojb.broker.util.sequence.SequenceManager;
 79  
 import org.apache.ojb.broker.util.sequence.SequenceManagerFactory;
 80  
 
 81  
 /**
 82  
  * The PersistenceBrokerImpl is an implementation of the PersistenceBroker
 83  
  * Interface that specifies a persistence mechanism for Java objects.
 84  
  * This Concrete implementation provides an object relational mapping
 85  
  * and allows to store and retrieve arbitrary objects in/from relational
 86  
  * databases accessed by JDBC.
 87  
  *
 88  
  * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
 89  
  * @author <a href="mailto:leandro@ibnetwork.com.br">Leandro Rodrigo Saad Cruz<a>
 90  
  * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
 91  
  * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
 92  
  *
 93  
  * @version $Id: PersistenceBrokerImpl.java,v 1.4 2008-07-08 15:53:48 sfheise Exp $
 94  
  */
 95  
 public class PersistenceBrokerImpl extends PersistenceBrokerAbstractImpl implements PBState
 96  
 {
 97  
     private Logger logger = LoggerFactory.getLogger(PersistenceBrokerImpl.class);
 98  
 
 99  
     protected PersistenceBrokerFactoryIF pbf;
 100  
     protected BrokerHelper brokerHelper;
 101  
     protected MtoNBroker mtoNBroker;
 102  
     protected QueryReferenceBroker referencesBroker;
 103  
 
 104  
     /**
 105  
      * signs if this broker was closed
 106  
      */
 107  
     private boolean isClosed;
 108  
     /**
 109  
      * Reflects the transaction status of this instance.
 110  
      */
 111  
     private boolean inTransaction;
 112  
     /**
 113  
      * Flag indicate that this PB instance is handled
 114  
      * in a managed environment.
 115  
      */
 116  
     private boolean managed;
 117  
     private MaterializationCache objectCache;
 118  
     /**
 119  
      * m_DbAccess is used to do all Jdbc related work: connecting, executing...
 120  
      */
 121  
     private JdbcAccess dbAccess;
 122  
     /**
 123  
      * holds mapping information for all classes to be treated by PersistenceBroker
 124  
      */
 125  
     private DescriptorRepository descriptorRepository = null;
 126  
     private ConnectionManagerIF connectionManager = null;
 127  
     private SequenceManager sequenceManager = null;
 128  
     private StatementManagerIF statementManager = null;
 129  
     private SqlGenerator sqlGenerator;
 130  
     private IdentityFactory identityFactory;
 131  
     private RelationshipPrefetcherFactory relationshipPrefetcherFactory;
 132  
     private ProxyFactory proxyFactory;
 133  
     private PBKey pbKey;
 134  
 
 135  
     /**
 136  
      * List of objects being stored now, allows to avoid infinite
 137  
      * recursion storeCollections -> storeReferences -> storeCollections...
 138  
      *
 139  
      */
 140  
     /*
 141  
     we use an object identity based List to compare objects to prevent problems
 142  
     with user implemented equals/hashCode methods of persistence capable objects
 143  
     (e.g. objects are equals but PK fields not)
 144  
     */
 145  
     private List nowStoring = new IdentityArrayList();
 146  
 
 147  
     /**
 148  
      * Lists for object registration during delete operations.
 149  
      * We reuse these list to avoid excessive object creation.
 150  
      * @see #clearRegistrationLists
 151  
      */
 152  
     /*
 153  
     arminw: list was cleared before delete method end. Internal we only
 154  
     call doDelete(...) method. Same procedure as 'nowStoring'
 155  
 
 156  
     we use an object identity based List to compare objects to prevent problems
 157  
     with user implemented equals/hashCode methods of persistence capable objects
 158  
     (e.g. objects are equals but PK fields not)
 159  
     */
 160  
     private List markedForDelete = new IdentityArrayList();
 161  
 
 162  
     /**
 163  
      * The set of identities of all deleted objects during current transaction
 164  
      */
 165  
     /*
 166  
     olegnitz: this is the only way I know that solves the following problem
 167  
     of batch mode: if one does store() after delete() for the same OID,
 168  
     the broker checks whether the given OID exists in database to decide
 169  
     which action to do: INSERT or UPDATE. If the preceding DELETE statement is
 170  
     still in batch (not executed yet), then the OID exists in database so
 171  
     the broker does UPDATE. Due the the following set of deleted OIDs
 172  
     the broker will know that it should do INSERT.
 173  
     */
 174  
     private Set deletedDuringTransaction = new HashSet();
 175  
 
 176  
     /**
 177  
      * Constructor used by {@link PersistenceBrokerFactoryIF} implementation.
 178  
      */
 179  
     public PersistenceBrokerImpl(PBKey key, PersistenceBrokerFactoryIF pbf)
 180  
     {
 181  
         refresh();
 182  
         if(key == null) throw new PersistenceBrokerException("Could not instantiate broker with PBKey 'null'");
 183  
         this.pbf = pbf;
 184  
         this.pbKey = key;
 185  
         /*
 186  
         be careful when changing initializing order
 187  
         */
 188  
         brokerHelper = new BrokerHelper(this);
 189  
         connectionManager = ConnectionManagerFactory.getInstance().createConnectionManager(this);
 190  
         /*
 191  
         TODO: find better solution
 192  
         MaterializationCache is a interim solution help to solve the problem of not full
 193  
         materialized object reads by concurrent threads and will be replaced when
 194  
         the new real two-level cache was introduced
 195  
         */
 196  
         objectCache = ObjectCacheFactory.getInstance().createObjectCache(this);
 197  
         sequenceManager = SequenceManagerFactory.getSequenceManager(this);
 198  
         dbAccess = JdbcAccessFactory.getInstance().createJdbcAccess(this);
 199  
         statementManager = StatementManagerFactory.getInstance().createStatementManager(this);
 200  
         sqlGenerator = SqlGeneratorFactory.getInstance().createSqlGenerator(
 201  
                         connectionManager.getSupportedPlatform());
 202  
         mtoNBroker = new MtoNBroker(this);
 203  
         referencesBroker = new QueryReferenceBroker(this);
 204  
         identityFactory = new IdentityFactoryImpl(this);
 205  
         relationshipPrefetcherFactory = new RelationshipPrefetcherFactory(this);
 206  
         proxyFactory = AbstractProxyFactory.getProxyFactory();
 207  
     }
 208  
 
 209  
     public MaterializationCache getInternalCache()
 210  
     {
 211  
         return objectCache;
 212  
     }
 213  
 
 214  
     public IdentityFactory serviceIdentity()
 215  
     {
 216  
         return this.identityFactory;
 217  
     }
 218  
 
 219  
     public SqlGenerator serviceSqlGenerator()
 220  
     {
 221  
         return this.sqlGenerator;
 222  
     }
 223  
 
 224  
     public StatementManagerIF serviceStatementManager()
 225  
     {
 226  
         return statementManager;
 227  
     }
 228  
 
 229  
     public JdbcAccess serviceJdbcAccess()
 230  
     {
 231  
         return dbAccess;
 232  
     }
 233  
 
 234  
     public ConnectionManagerIF serviceConnectionManager()
 235  
     {
 236  
         return connectionManager;
 237  
     }
 238  
 
 239  
     public SequenceManager serviceSequenceManager()
 240  
     {
 241  
         return this.sequenceManager;
 242  
     }
 243  
 
 244  
     public BrokerHelper serviceBrokerHelper()
 245  
     {
 246  
         return this.brokerHelper;
 247  
     }
 248  
 
 249  
     public ObjectCache serviceObjectCache()
 250  
     {
 251  
         return this.objectCache;
 252  
     }
 253  
 
 254  
     public QueryReferenceBroker getReferenceBroker()
 255  
     {
 256  
         return this.referencesBroker;
 257  
     }
 258  
 
 259  
     public RelationshipPrefetcherFactory getRelationshipPrefetcherFactory()
 260  
     {
 261  
         return relationshipPrefetcherFactory;
 262  
     }
 263  
 
 264  
     public boolean isClosed()
 265  
     {
 266  
         return this.isClosed;
 267  
     }
 268  
 
 269  
     public void setClosed(boolean closed)
 270  
     {
 271  
         // When lookup the PB instance from pool method setClosed(false)
 272  
         // was called before returning instance from pool, in this case
 273  
         // OJB have to refresh the instance.
 274  
         if(!closed)
 275  
         {
 276  
             refresh();
 277  
         }
 278  
         this.isClosed = closed;
 279  
     }
 280  
 
 281  
     /**
 282  
      * If <em>true</em> this instance is handled by a managed
 283  
      * environment - registered within a JTA transaction.
 284  
      */
 285  
     public boolean isManaged()
 286  
     {
 287  
         return managed;
 288  
     }
 289  
 
 290  
     /**
 291  
      * Set <em>true</em> if this instance is registered within a
 292  
      * JTA transaction. On {@link #close()} call this flag was reset
 293  
      * to <em>false</em> automatic.
 294  
      */
 295  
     public void setManaged(boolean managed)
 296  
     {
 297  
         this.managed = managed;
 298  
     }
 299  
 
 300  
     /**
 301  
      * Lookup the current {@link DescriptorRepository} for
 302  
      * this class. This method is responsible to keep this
 303  
      * PB instance in sync with {@link MetadataManager}.
 304  
      */
 305  
     public void refresh()
 306  
     {
 307  
         // guarantee that refreshed use initial status
 308  
         setInTransaction(false);
 309  
         this.descriptorRepository = MetadataManager.getInstance().getRepository();
 310  
     }
 311  
 
 312  
     /**
 313  
      * Release all resources used by this
 314  
      * class - CAUTION: No further operations can be
 315  
      * done with this instance after calling this method.
 316  
      */
 317  
     public void destroy()
 318  
     {
 319  
         removeAllListeners();
 320  
         if (connectionManager != null)
 321  
         {
 322  
             if(connectionManager.isInLocalTransaction())
 323  
             {
 324  
                 connectionManager.localRollback();
 325  
             }
 326  
             connectionManager.releaseConnection();
 327  
         }
 328  
         this.setClosed(true);
 329  
 
 330  
         this.descriptorRepository = null;
 331  
         this.pbKey = null;
 332  
         this.pbf = null;
 333  
         this.connectionManager = null;
 334  
         this.dbAccess = null;
 335  
         this.objectCache = null;
 336  
         this.sequenceManager = null;
 337  
         this.sqlGenerator = null;
 338  
         this.statementManager = null;
 339  
     }
 340  
 
 341  
     public PBKey getPBKey()
 342  
     {
 343  
         return pbKey;
 344  
     }
 345  
 
 346  
     public void setPBKey(PBKey key)
 347  
     {
 348  
         this.pbKey = key;
 349  
     }
 350  
 
 351  
     /**
 352  
      * @see org.apache.ojb.broker.PersistenceBroker#close()
 353  
      */
 354  
     public boolean close()
 355  
     {
 356  
         /**
 357  
          * MBAIRD: if we call close on a broker that is in a transaction,
 358  
          * we should just abort whatever it's doing.
 359  
          */
 360  
         if (isInTransaction())
 361  
         {
 362  
             logger.error("Broker is still in PB-transaction, do automatic abort before close!");
 363  
             abortTransaction();
 364  
         }
 365  
         if (logger.isDebugEnabled())
 366  
         {
 367  
             logger.debug("PB.close was called: " + this);
 368  
         }
 369  
         try
 370  
         {
 371  
             fireBrokerEvent(BEFORE_CLOSE_EVENT);
 372  
             clearRegistrationLists();
 373  
             referencesBroker.removePrefetchingListeners();
 374  
             if (connectionManager != null)
 375  
             {
 376  
                 connectionManager.releaseConnection();
 377  
                 /*
 378  
                 arminw:
 379  
                 set batch mode explicit to 'false'. Using
 380  
 
 381  
                 connectionManager.setBatchMode(
 382  
                         connectionManager.getConnectionDescriptor().getBatchMode());
 383  
 
 384  
                 cause many unexpected junit failures/errors when running
 385  
                 test suite with batch-mode 'true' setting.
 386  
                 */
 387  
                 connectionManager.setBatchMode(false);
 388  
             }
 389  
         }
 390  
         finally
 391  
         {
 392  
             // reset flag indicating use in managed environment
 393  
             setManaged(false);
 394  
             // free current used DescriptorRepository reference
 395  
             descriptorRepository = null;
 396  
             removeAllListeners();
 397  
             this.setClosed(true);
 398  
         }
 399  
         return true;
 400  
     }
 401  
 
 402  
     /**
 403  
      * Abort and close the transaction.
 404  
      * Calling abort abandons all persistent object modifications and releases the
 405  
      * associated locks.
 406  
      * If transaction is not in progress a TransactionNotInProgressException is thrown
 407  
      */
 408  
     public synchronized void abortTransaction() throws TransactionNotInProgressException
 409  
     {
 410  
         if(isInTransaction())
 411  
         {
 412  
             fireBrokerEvent(BEFORE_ROLLBACK_EVENT);
 413  
             setInTransaction(false);
 414  
             clearRegistrationLists();
 415  
             referencesBroker.removePrefetchingListeners();
 416  
             /*
 417  
             arminw:
 418  
             check if we in local tx, before do local rollback
 419  
             Necessary, because ConnectionManager may do a rollback by itself
 420  
             or in managed environments the used connection is already be closed
 421  
             */
 422  
             if(connectionManager.isInLocalTransaction()) this.connectionManager.localRollback();
 423  
             fireBrokerEvent(AFTER_ROLLBACK_EVENT);
 424  
         }
 425  
     }
 426  
 
 427  
     /**
 428  
      * begin a transaction against the underlying RDBMS.
 429  
      * Calling <code>beginTransaction</code> multiple times,
 430  
      * without an intervening call to <code>commitTransaction</code> or <code>abortTransaction</code>,
 431  
      * causes the exception <code>TransactionInProgressException</code> to be thrown
 432  
      * on the second and subsequent calls.
 433  
      */
 434  
     public synchronized void beginTransaction() throws TransactionInProgressException, TransactionAbortedException
 435  
     {
 436  
         if (isInTransaction())
 437  
         {
 438  
             throw new TransactionInProgressException("PersistenceBroker is already in transaction");
 439  
         }
 440  
         fireBrokerEvent(BEFORE_BEGIN_EVENT);
 441  
         setInTransaction(true);
 442  
         this.connectionManager.localBegin();
 443  
         fireBrokerEvent(AFTER_BEGIN_EVENT);
 444  
     }
 445  
 
 446  
     /**
 447  
      * Commit and close the transaction.
 448  
      * Calling <code>commit</code> commits to the database all
 449  
      * UPDATE, INSERT and DELETE statements called within the transaction and
 450  
      * releases any locks held by the transaction.
 451  
      * If beginTransaction() has not been called before a
 452  
      * TransactionNotInProgressException exception is thrown.
 453  
      * If the transaction cannot be commited a TransactionAbortedException exception is thrown.
 454  
      */
 455  
     public synchronized void commitTransaction() throws TransactionNotInProgressException, TransactionAbortedException
 456  
     {
 457  
         if (!isInTransaction())
 458  
         {
 459  
             throw new TransactionNotInProgressException("PersistenceBroker is NOT in transaction, can't commit");
 460  
         }
 461  
         fireBrokerEvent(BEFORE_COMMIT_EVENT);
 462  
         setInTransaction(false);
 463  
         clearRegistrationLists();
 464  
         referencesBroker.removePrefetchingListeners();
 465  
         /*
 466  
         arminw:
 467  
         In managed environments it should be possible to close a used connection before
 468  
         the tx was commited, thus it will be possible that the PB instance is in PB-tx, but
 469  
         the connection is already closed. To avoid problems check if CM is in local tx before
 470  
         do the CM.commit call
 471  
         */
 472  
         if(connectionManager.isInLocalTransaction())
 473  
         {
 474  
             this.connectionManager.localCommit();
 475  
         }
 476  
         fireBrokerEvent(AFTER_COMMIT_EVENT);
 477  
     }
 478  
 
 479  
     /**
 480  
      * Deletes the concrete representation of the specified object in the underlying
 481  
      * persistence system. This method is intended for use in top-level api or
 482  
      * by internal calls.
 483  
      *
 484  
      * @param obj The object to delete.
 485  
      * @param ignoreReferences With this flag the automatic deletion/unlinking
 486  
      * of references can be suppressed (independent of the used auto-delete setting in metadata),
 487  
      * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
 488  
      * these kind of reference (descriptor) will always be performed. If <em>true</em>
 489  
      * all "normal" referenced objects will be ignored, only the specified object is handled.
 490  
      * @throws PersistenceBrokerException
 491  
      */
 492  
     public void delete(Object obj, boolean ignoreReferences) throws PersistenceBrokerException
 493  
     {
 494  
         if(isTxCheck() && !isInTransaction())
 495  
         {
 496  
             if(logger.isEnabledFor(Logger.ERROR))
 497  
             {
 498  
                 String msg = "No running PB-tx found. Please, only delete objects in context of a PB-transaction" +
 499  
                     " to avoid side-effects - e.g. when rollback of complex objects.";
 500  
                 try
 501  
                 {
 502  
                     throw new Exception("** Delete object without active PersistenceBroker transaction **");
 503  
                 }
 504  
                 catch(Exception e)
 505  
                 {
 506  
                     logger.error(msg, e);
 507  
                 }
 508  
             }
 509  
         }
 510  
         try
 511  
         {
 512  
             doDelete(obj, ignoreReferences);
 513  
         }
 514  
         finally
 515  
         {
 516  
             markedForDelete.clear();
 517  
         }
 518  
     }
 519  
 
 520  
     /**
 521  
      * @see org.apache.ojb.broker.PersistenceBroker#delete
 522  
      */
 523  
     public void delete(Object obj) throws PersistenceBrokerException
 524  
     {
 525  
         delete(obj, false);
 526  
     }
 527  
 
 528  
     /**
 529  
      * do delete given object. Should be used by all intern classes to delete
 530  
      * objects.
 531  
      */
 532  
     private void doDelete(Object obj, boolean ignoreReferences) throws PersistenceBrokerException
 533  
     {
 534  
         //logger.info("DELETING " + obj);
 535  
         // object is not null
 536  
         if (obj != null)
 537  
         {
 538  
             obj = getProxyFactory().getRealObject(obj);
 539  
             /**
 540  
              * Kuali Foundation modification -- 8/24/2007
 541  
              */
 542  
             if ( obj == null ) return;
 543  
             /**
 544  
              * End of Kuali Foundation modification
 545  
              */
 546  
             /**
 547  
              * MBAIRD
 548  
              * 1. if we are marked for delete already, avoid recursing on this object
 549  
              *
 550  
              * arminw:
 551  
              * use object instead Identity object in markedForDelete List,
 552  
              * because using objects we get a better performance. I can't find
 553  
              * side-effects in doing so.
 554  
              */
 555  
             if (markedForDelete.contains(obj))
 556  
             {
 557  
                 return;
 558  
             }
 559  
             
 560  
             ClassDescriptor cld = getClassDescriptor(obj.getClass());
 561  
             //BRJ: check for valid pk
 562  
             if (!serviceBrokerHelper().assertValidPkForDelete(cld, obj))
 563  
             {
 564  
                 String msg = "Cannot delete object without valid PKs. " + obj;
 565  
                 logger.error(msg);
 566  
                 return;
 567  
             }
 568  
             
 569  
             /**
 570  
              * MBAIRD
 571  
              * 2. register object in markedForDelete map.
 572  
              */
 573  
             markedForDelete.add(obj);
 574  
             Identity oid = serviceIdentity().buildIdentity(cld, obj);
 575  
 
 576  
             // Invoke events on PersistenceBrokerAware instances and listeners
 577  
             BEFORE_DELETE_EVENT.setTarget(obj);
 578  
             fireBrokerEvent(BEFORE_DELETE_EVENT);
 579  
             BEFORE_DELETE_EVENT.setTarget(null);
 580  
 
 581  
             // now perform deletion
 582  
             performDeletion(cld, obj, oid, ignoreReferences);
 583  
                     
 584  
             // Invoke events on PersistenceBrokerAware instances and listeners
 585  
             AFTER_DELETE_EVENT.setTarget(obj);
 586  
             fireBrokerEvent(AFTER_DELETE_EVENT);
 587  
             AFTER_DELETE_EVENT.setTarget(null);
 588  
                             
 589  
             // let the connection manager to execute batch
 590  
             connectionManager.executeBatchIfNecessary();
 591  
         }
 592  
     }
 593  
                     
 594  
     /**
 595  
      * This method perform the delete of the specified object
 596  
      * based on the {@link org.apache.ojb.broker.metadata.ClassDescriptor}.
 597  
      */
 598  
     private void performDeletion(final ClassDescriptor cld, final Object obj, final Identity oid, final boolean ignoreReferences) throws PersistenceBrokerException
 599  
     {
 600  
             // 1. delete dependend collections
 601  
             if (!ignoreReferences  && cld.getCollectionDescriptors().size() > 0)
 602  
             {
 603  
                 deleteCollections(obj, cld.getCollectionDescriptors());
 604  
             }
 605  
             // 2. delete object from directly mapped table
 606  
             try
 607  
             {
 608  
                 dbAccess.executeDelete(cld, obj); // use obj not oid to delete, BRJ
 609  
             }
 610  
             catch(OptimisticLockException e)
 611  
             {
 612  
                 // ensure that the outdated object be removed from cache
 613  
                 objectCache.remove(oid);
 614  
                 throw e;
 615  
             }
 616  
 
 617  
             // 3. Add OID to the set of deleted objects
 618  
             deletedDuringTransaction.add(oid);
 619  
 
 620  
             // 4. delete dependend upon objects last to avoid FK violations
 621  
             if (cld.getObjectReferenceDescriptors().size() > 0)
 622  
             {
 623  
                 deleteReferences(cld, obj, oid, ignoreReferences);
 624  
             }
 625  
             // remove obj from the object cache:
 626  
             objectCache.remove(oid);
 627  
     }
 628  
 
 629  
     /**
 630  
      * Extent aware Delete by Query
 631  
      * @param query
 632  
      * @param cld
 633  
      * @throws PersistenceBrokerException
 634  
      */
 635  
     private void deleteByQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
 636  
     {
 637  
         if (logger.isDebugEnabled())
 638  
         {
 639  
             logger.debug("deleteByQuery " + cld.getClassNameOfObject() + ", " + query);
 640  
         }
 641  
 
 642  
         if (query instanceof QueryBySQL)
 643  
         {
 644  
             String sql = ((QueryBySQL) query).getSql();
 645  
             this.dbAccess.executeUpdateSQL(sql, cld);
 646  
         }
 647  
         else
 648  
         {
 649  
             // if query is Identity based transform it to a criteria based query first
 650  
             if (query instanceof QueryByIdentity)
 651  
             {
 652  
                 QueryByIdentity qbi = (QueryByIdentity) query;
 653  
                 Object oid = qbi.getExampleObject();
 654  
                 // make sure it's an Identity
 655  
                 if (!(oid instanceof Identity))
 656  
                 {
 657  
                     oid = serviceIdentity().buildIdentity(oid);
 658  
                 }
 659  
                 query = referencesBroker.getPKQuery((Identity) oid);
 660  
             }
 661  
 
 662  
             if (!cld.isInterface())
 663  
             {
 664  
                 this.dbAccess.executeDelete(query, cld);
 665  
             }
 666  
 
 667  
             // if class is an extent, we have to delete all extent classes too
 668  
             String lastUsedTable = cld.getFullTableName();
 669  
             if (cld.isExtent())
 670  
             {
 671  
                 Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
 672  
 
 673  
                 while (extents.hasNext())
 674  
                 {
 675  
                     ClassDescriptor extCld = (ClassDescriptor) extents.next();
 676  
 
 677  
                     // read same table only once
 678  
                     if (!extCld.getFullTableName().equals(lastUsedTable))
 679  
                     {
 680  
                         lastUsedTable = extCld.getFullTableName();
 681  
                         this.dbAccess.executeDelete(query, extCld);
 682  
                     }
 683  
                 }
 684  
             }
 685  
 
 686  
         }
 687  
     }
 688  
 
 689  
     /**
 690  
      * @see org.apache.ojb.broker.PersistenceBroker#deleteByQuery(Query)
 691  
      */
 692  
     public void deleteByQuery(Query query) throws PersistenceBrokerException
 693  
     {
 694  
         ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
 695  
         deleteByQuery(query, cld);
 696  
     }
 697  
 
 698  
     /**
 699  
      * Deletes references that <b>obj</b> points to.
 700  
      * All objects which we have a FK poiting to (Via ReferenceDescriptors)
 701  
      * will be deleted if auto-delete is true <b>AND</b>
 702  
      * the member field containing the object reference is NOT null.
 703  
      *
 704  
      * @param obj Object which we will delete references for
 705  
      * @param listRds list of ObjectRederenceDescriptors
 706  
      * @param ignoreReferences With this flag the automatic deletion/unlinking
 707  
      * of references can be suppressed (independent of the used auto-delete setting in metadata),
 708  
      * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
 709  
      * these kind of reference (descriptor) will always be performed.
 710  
      * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
 711  
      */
 712  
     private void deleteReferences(ClassDescriptor cld, Object obj, Identity oid, boolean ignoreReferences) throws PersistenceBrokerException
 713  
     {
 714  
             List listRds = cld.getObjectReferenceDescriptors();
 715  
         // get all members of obj that are references and delete them
 716  
         Iterator i = listRds.iterator();
 717  
         while (i.hasNext())
 718  
         {
 719  
             ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
 720  
             if ((!ignoreReferences && rds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)
 721  
                     || rds.isSuperReferenceDescriptor())
 722  
             {
 723  
                 Object referencedObject = rds.getPersistentField().get(obj);
 724  
                 if (referencedObject != null)
 725  
                 {
 726  
                         if(rds.isSuperReferenceDescriptor())
 727  
                         {
 728  
                                 ClassDescriptor base = cld.getSuperClassDescriptor();
 729  
                                 /*
 730  
                                     arminw: If "table-per-subclass" inheritance is used we have to
 731  
                                     guarantee that all super-class table entries are deleted too.
 732  
                                     Thus we have to perform the recursive deletion of all super-class
 733  
                                     table entries.
 734  
                                  */
 735  
                                 performDeletion(base, referencedObject, oid, ignoreReferences);
 736  
                         }
 737  
                         else
 738  
                         {
 739  
                                 doDelete(referencedObject, ignoreReferences);
 740  
                         }
 741  
                 }
 742  
             }
 743  
         }
 744  
     }
 745  
 
 746  
     /**
 747  
      * Deletes collections of objects poiting to <b>obj</b>.
 748  
      * All object which have a FK poiting to this object (Via CollectionDescriptors)
 749  
      * will be deleted if auto-delete is true <b>AND</b>
 750  
      * the member field containing the object reference if NOT null.
 751  
      *
 752  
      * @param obj Object which we will delete collections for
 753  
      * @param listCds list of ObjectReferenceDescriptors
 754  
      * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
 755  
      */
 756  
     private void deleteCollections(Object obj, List listCds) throws PersistenceBrokerException
 757  
     {
 758  
         // get all members of obj that are collections and delete all their elements
 759  
         Iterator i = listCds.iterator();
 760  
 
 761  
         while (i.hasNext())
 762  
         {
 763  
             CollectionDescriptor cds = (CollectionDescriptor) i.next();
 764  
             if(cds.getCascadingDelete() != ObjectReferenceDescriptor.CASCADE_NONE)
 765  
             {
 766  
                 if(cds.isMtoNRelation())
 767  
                 {
 768  
                     // if this is a m:n mapped table, remove entries from indirection table
 769  
                     mtoNBroker.deleteMtoNImplementor(cds, obj);
 770  
                 }
 771  
                 /*
 772  
                 if cascading delete is on, delete all referenced objects.
 773  
                 NOTE: User has to take care to populate all referenced objects before delete
 774  
                 the main object to avoid referential constraint violation
 775  
                  */
 776  
                 if (cds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)
 777  
                 {
 778  
                     Object col = cds.getPersistentField().get(obj);
 779  
                     if (col != null)
 780  
                     {
 781  
                         Iterator colIterator = BrokerHelper.getCollectionIterator(col);
 782  
                         while (colIterator.hasNext())
 783  
                         {
 784  
                             doDelete(colIterator.next(), false);
 785  
                         }
 786  
                     }
 787  
                 }
 788  
             }
 789  
         }
 790  
     }
 791  
 
 792  
     /**
 793  
      * Store an Object.
 794  
      * @see org.apache.ojb.broker.PersistenceBroker#store(Object)
 795  
      */
 796  
     public void store(Object obj) throws PersistenceBrokerException
 797  
     {
 798  
         obj = extractObjectToStore(obj);
 799  
         // only do something if obj != null
 800  
         if(obj == null) return;
 801  
 
 802  
         ClassDescriptor cld = getClassDescriptor(obj.getClass());
 803  
         /*
 804  
         if one of the PK fields was null, we assume the objects
 805  
         was new and needs insert
 806  
         */
 807  
         boolean insert = serviceBrokerHelper().hasNullPKField(cld, obj);
 808  
         Identity oid = serviceIdentity().buildIdentity(cld, obj);
 809  
         /*
 810  
         if PK values are set, lookup cache or db to see whether object
 811  
         needs insert or update
 812  
         */
 813  
         if (!insert)
 814  
         {
 815  
             insert = objectCache.lookup(oid) == null
 816  
                 && !serviceBrokerHelper().doesExist(cld, oid, obj);
 817  
         }
 818  
         store(obj, oid, cld, insert);
 819  
     }
 820  
 
 821  
     /**
 822  
      * Check if the given object is <code>null</code> or an unmaterialized proxy object - in
 823  
      * both cases <code>null</code> will be returned, else the given object itself or the
 824  
      * materialized proxy object will be returned.
 825  
      */
 826  
     private Object extractObjectToStore(Object obj)
 827  
     {
 828  
         Object result = obj;
 829  
         // only do something if obj != null
 830  
         if(result != null)
 831  
         {
 832  
             // ProxyObjects only have to be updated if their real
 833  
             // subjects have been loaded
 834  
             result = getProxyFactory().getRealObjectIfMaterialized(obj);
 835  
             // null for unmaterialized Proxy
 836  
             if (result == null)
 837  
             {
 838  
                 if(logger.isDebugEnabled())
 839  
                     logger.debug("No materialized object could be found -> nothing to store," +
 840  
                             " object was " + ObjectUtils.identityToString(obj));
 841  
             }
 842  
         }
 843  
         return result;
 844  
     }
 845  
 
 846  
     /**
 847  
      * Method which start the real store work (insert or update)
 848  
      * and is intended for use by top-level api or internal calls.
 849  
      *
 850  
      * @param obj The object to store.
 851  
      * @param oid The {@link Identity} of the object to store.
 852  
      * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
 853  
      * @param insert If <em>true</em> an insert operation will be performed, else update operation.
 854  
      * @param ignoreReferences With this flag the automatic storing/linking
 855  
      * of references can be suppressed (independent of the used auto-update setting in metadata),
 856  
      * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
 857  
      * these kind of reference (descriptor) will always be performed. If <em>true</em>
 858  
      * all "normal" referenced objects will be ignored, only the specified object is handled.
 859  
      */
 860  
     public void store(Object obj, Identity oid, ClassDescriptor cld,  boolean insert, boolean ignoreReferences)
 861  
     {
 862  
         if(obj == null || nowStoring.contains(obj))
 863  
         {
 864  
             return;
 865  
         }
 866  
 
 867  
         /*
 868  
         if the object has been deleted during this transaction,
 869  
         then we must insert it
 870  
         */
 871  
         //System.out.println("## insert: " +insert + " / deleted: " + deletedDuringTransaction);
 872  
         if (!insert)
 873  
         {
 874  
             insert = deletedDuringTransaction.contains(oid);
 875  
         }
 876  
 
 877  
         //************************************************
 878  
         // now store it:
 879  
         if(isTxCheck() && !isInTransaction())
 880  
         {
 881  
             if(logger.isEnabledFor(Logger.ERROR))
 882  
             {
 883  
                 try
 884  
                 {
 885  
                     throw new Exception("** Try to store object without active PersistenceBroker transaction **");
 886  
                 }
 887  
                 catch(Exception e)
 888  
                 {
 889  
                     logger.error("No running tx found, please only store in context of an PB-transaction" +
 890  
                     ", to avoid side-effects - e.g. when rollback of complex objects", e);
 891  
                 }
 892  
             }
 893  
         }
 894  
         // Invoke events on PersistenceBrokerAware instances and listeners
 895  
         if (insert)
 896  
         {
 897  
             BEFORE_STORE_EVENT.setTarget(obj);
 898  
             fireBrokerEvent(BEFORE_STORE_EVENT);
 899  
             BEFORE_STORE_EVENT.setTarget(null);
 900  
         }
 901  
         else
 902  
         {
 903  
             BEFORE_UPDATE_EVENT.setTarget(obj);
 904  
             fireBrokerEvent(BEFORE_UPDATE_EVENT);
 905  
             BEFORE_UPDATE_EVENT.setTarget(null);
 906  
         }
 907  
 
 908  
         try
 909  
         {
 910  
             nowStoring.add(obj);
 911  
             storeToDb(obj, cld, oid, insert, ignoreReferences);
 912  
         }
 913  
         finally
 914  
         {
 915  
             // to optimize calls to DB don't remove already stored objects
 916  
             nowStoring.remove(obj);
 917  
         }
 918  
 
 919  
 
 920  
         // Invoke events on PersistenceBrokerAware instances and listeners
 921  
         if (insert)
 922  
         {
 923  
             AFTER_STORE_EVENT.setTarget(obj);
 924  
             fireBrokerEvent(AFTER_STORE_EVENT);
 925  
             AFTER_STORE_EVENT.setTarget(null);
 926  
         }
 927  
         else
 928  
         {
 929  
             AFTER_UPDATE_EVENT.setTarget(obj);
 930  
             fireBrokerEvent(AFTER_UPDATE_EVENT);
 931  
             AFTER_UPDATE_EVENT.setTarget(null);
 932  
         }
 933  
         // end of store operation
 934  
         //************************************************
 935  
 
 936  
         // if the object was stored, remove it from deleted set
 937  
         if(deletedDuringTransaction.size() > 0) deletedDuringTransaction.remove(oid);
 938  
 
 939  
         // let the connection manager to execute batch
 940  
         connectionManager.executeBatchIfNecessary();
 941  
     }
 942  
 
 943  
     /**
 944  
      * Internal used method which start the real store work.
 945  
      */
 946  
     protected void store(Object obj, Identity oid, ClassDescriptor cld,  boolean insert)
 947  
     {
 948  
         store(obj, oid, cld, insert, false);
 949  
     }
 950  
 
 951  
     /**
 952  
      * Store all object references that <b>obj</b> points to.
 953  
      * All objects which we have a FK pointing to (Via ReferenceDescriptors) will be
 954  
      * stored if auto-update is true <b>AND</b> the member field containing the object
 955  
      * reference is NOT null.
 956  
      * With flag <em>ignoreReferences</em> the storing/linking
 957  
      * of references can be suppressed (independent of the used auto-update setting),
 958  
      * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
 959  
      * these kind of reference (descriptor) will always be performed.
 960  
      *
 961  
      * @param obj Object which we will store references for
 962  
      */
 963  
     private void storeReferences(Object obj, ClassDescriptor cld, boolean insert, boolean ignoreReferences)
 964  
     {
 965  
         // get all members of obj that are references and store them
 966  
         Collection listRds = cld.getObjectReferenceDescriptors();
 967  
         // return if nothing to do
 968  
         if(listRds == null || listRds.size() == 0)
 969  
         {
 970  
             return;
 971  
         }
 972  
         Iterator i = listRds.iterator();
 973  
         while (i.hasNext())
 974  
         {
 975  
             ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
 976  
             /*
 977  
             arminw: the super-references (used for table per subclass inheritance) must
 978  
             be performed in any case. The "normal" 1:1 references can be ignored when
 979  
             flag "ignoreReferences" is set
 980  
             */
 981  
             if((!ignoreReferences && rds.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE)
 982  
                     || rds.isSuperReferenceDescriptor())
 983  
             {
 984  
                 storeAndLinkOneToOne(false, obj, cld, rds, insert);
 985  
             }
 986  
         }
 987  
     }
 988  
 
 989  
     /**
 990  
      * Store/Link 1:1 reference.
 991  
      *
 992  
      * @param obj real object the reference starts
 993  
      * @param rds {@link ObjectReferenceDescriptor} of the real object
 994  
      * @param insert flag for insert operation
 995  
      */
 996  
     private void storeAndLinkOneToOne(boolean onlyLink, Object obj, ClassDescriptor cld,
 997  
                                       ObjectReferenceDescriptor rds, boolean insert)
 998  
     {
 999  
         Object ref = rds.getPersistentField().get(obj);
 1000  
         if (!onlyLink && rds.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
 1001  
         {
 1002  
             if(rds.isSuperReferenceDescriptor())
 1003  
             {
 1004  
                 ClassDescriptor superCld = rds.getClassDescriptor().getSuperClassDescriptor();
 1005  
                 Identity oid = serviceIdentity().buildIdentity(superCld, ref);
 1006  
                 storeToDb(ref, superCld, oid, insert);
 1007  
             }
 1008  
             else store(ref);
 1009  
         }
 1010  
     
 1011  
         /**
 1012  
          * Kuali Foundation modification -- 1/10/2008
 1013  
          */
 1014  
         ref = getProxyFactory().getRealObject(ref);
 1015  
         /**
 1016  
          * End of Kuali Foundation modification
 1017  
          */
 1018  
         link(obj, cld, rds, ref, insert);
 1019  
     }
 1020  
 
 1021  
     /**
 1022  
      * Store/Link collections of objects poiting to <b>obj</b>.
 1023  
      * More info please see comments in source.
 1024  
      *
 1025  
      * @param obj real object which we will store collections for
 1026  
      * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
 1027  
      */
 1028  
     private void storeCollections(Object obj, ClassDescriptor cld, boolean insert) throws PersistenceBrokerException
 1029  
     {
 1030  
         // get all members of obj that are collections and store all their elements
 1031  
         Collection listCods = cld.getCollectionDescriptors();
 1032  
         // return if nothing to do
 1033  
         if (listCods.size() == 0)
 1034  
         {
 1035  
             return;
 1036  
         }
 1037  
         Iterator i = listCods.iterator();
 1038  
         while (i.hasNext())
 1039  
         {
 1040  
             CollectionDescriptor cod = (CollectionDescriptor) i.next();
 1041  
 
 1042  
             // if CASCADE_NONE was set, do nothing with referenced objects
 1043  
             if (cod.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE)
 1044  
             {
 1045  
                 Object referencedObjects = cod.getPersistentField().get(obj);
 1046  
                 if (cod.isMtoNRelation())
 1047  
                 {
 1048  
                     storeAndLinkMtoN(false, obj, cod, referencedObjects, insert);
 1049  
                 }
 1050  
                 else
 1051  
                 {
 1052  
                     storeAndLinkOneToMany(false, obj, cod, referencedObjects, insert);
 1053  
                 }
 1054  
 
 1055  
                 // BRJ: only when auto-update = object (CASCADE_OBJECT)
 1056  
                 //
 1057  
                 if ((cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
 1058  
                         && (referencedObjects instanceof ManageableCollection))
 1059  
                 {
 1060  
                     ((ManageableCollection) referencedObjects).afterStore(this);
 1061  
                 }
 1062  
             }
 1063  
         }
 1064  
     }
 1065  
 
 1066  
     /**
 1067  
      * Store/Link m:n collection references.
 1068  
      *
 1069  
      * @param obj real object the reference starts
 1070  
      * @param cod {@link CollectionDescriptor} of the real object
 1071  
      * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
 1072  
      * @param insert flag for insert operation
 1073  
      */
 1074  
     private void storeAndLinkMtoN(boolean onlyLink, Object obj, CollectionDescriptor cod,
 1075  
                                   Object referencedObjects, boolean insert)
 1076  
     {
 1077  
         /*
 1078  
         - if the collection is a collectionproxy and it's not already loaded
 1079  
         no need to perform an update on the referenced objects
 1080  
         - on insert we link and insert the referenced objects, because the proxy
 1081  
         collection maybe "inherited" from the object before the PK was replaced
 1082  
         */
 1083  
         if(insert || !(referencedObjects instanceof CollectionProxy
 1084  
                         && !((CollectionProxy) referencedObjects).isLoaded()))
 1085  
         {
 1086  
             // if referenced objects are null, assign empty list
 1087  
             if(referencedObjects == null)
 1088  
             {
 1089  
                 referencedObjects = Collections.EMPTY_LIST;
 1090  
             }
 1091  
             /*
 1092  
             NOTE: Take care of referenced objects, they could be of type Collection or
 1093  
             an Array or of type ManageableCollection, thus it is not guaranteed that we
 1094  
             can cast to Collection!!!
 1095  
 
 1096  
             if we store an object with m:n reference and no references could be
 1097  
             found, we remove all entires of given object in indirection table
 1098  
             */
 1099  
             Iterator referencedObjectsIterator;
 1100  
 
 1101  
             if(!onlyLink && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
 1102  
             {
 1103  
                 referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
 1104  
                 while (referencedObjectsIterator.hasNext())
 1105  
                 {
 1106  
                     store(referencedObjectsIterator.next());
 1107  
                 }
 1108  
             }
 1109  
 
 1110  
             Collection existingMtoNKeys;
 1111  
             if(!insert)
 1112  
             {
 1113  
                 existingMtoNKeys = mtoNBroker.getMtoNImplementor(cod, obj);
 1114  
                 // we can't reuse iterator
 1115  
                 referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
 1116  
                 // remove all entries in indirection table which not be part of referenced objects
 1117  
                 mtoNBroker.deleteMtoNImplementor(cod, obj, referencedObjectsIterator, existingMtoNKeys);
 1118  
             }
 1119  
             else
 1120  
             {
 1121  
                 existingMtoNKeys = Collections.EMPTY_LIST;
 1122  
             }
 1123  
             // we can't reuse iterator
 1124  
             referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
 1125  
             while (referencedObjectsIterator.hasNext())
 1126  
             {
 1127  
                 Object refObj = referencedObjectsIterator.next();
 1128  
                 // Now store indirection record
 1129  
                 // BRJ: this could cause integrity problems because
 1130  
                 // obj may not be stored depending on auto-update
 1131  
                 mtoNBroker.storeMtoNImplementor(cod, obj, refObj, existingMtoNKeys);
 1132  
             }
 1133  
         }
 1134  
     }
 1135  
 
 1136  
     /**
 1137  
      * Store/Link 1:n collection references.
 1138  
      *
 1139  
      * @param obj real object the reference starts
 1140  
      * @param linkOnly if true the referenced objects will only be linked (FK set, no reference store).
 1141  
      * Reference store setting in descriptor will be ignored in this case
 1142  
      * @param cod {@link CollectionDescriptor} of the real object
 1143  
      * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
 1144  
      * @param insert flag for insert operation
 1145  
      */
 1146  
     private void storeAndLinkOneToMany(boolean linkOnly, Object obj, CollectionDescriptor cod,
 1147  
                                        Object referencedObjects, boolean insert)
 1148  
     {
 1149  
         if(referencedObjects == null)
 1150  
         {
 1151  
             return;
 1152  
         }
 1153  
         /*
 1154  
         Only make sense to perform (link or/and store) real referenced objects
 1155  
         or materialized collection proxy objects, because on unmaterialized collection
 1156  
         nothing has changed.
 1157  
 
 1158  
         - if the collection is a collectionproxy and it's not already loaded
 1159  
         no need to perform an update on the referenced objects
 1160  
         - on insert we link and insert the referenced objects, because the proxy
 1161  
         collection maybe "inherited" from the object before the PK was replaced
 1162  
         */
 1163  
         if(insert || !(referencedObjects instanceof CollectionProxyDefaultImpl
 1164  
                         && !((CollectionProxyDefaultImpl) referencedObjects).isLoaded()))
 1165  
         {
 1166  
             Iterator it = BrokerHelper.getCollectionIterator(referencedObjects);
 1167  
             Object refObj;
 1168  
             while(it.hasNext())
 1169  
             {
 1170  
                 refObj = it.next();
 1171  
                 /*
 1172  
                 TODO: Check this!
 1173  
                 arminw:
 1174  
                 When it's necessary to 'link' (set the FK) the 1:n reference objects?
 1175  
                 1. set FK in refObj if it is materialized
 1176  
                 2. if the referenced object is a proxy AND the main object needs insert
 1177  
                 we have to materialize the real object, because the user may move a collection
 1178  
                 of proxy objects from object A to new object B. In this case we have to replace the
 1179  
                 FK in the proxy object with new key of object B.
 1180  
                 */
 1181  
                 if(insert || getProxyFactory().isMaterialized(refObj))
 1182  
                 {
 1183  
                     ClassDescriptor refCld = getClassDescriptor(getProxyFactory().getRealClass(refObj));
 1184  
                     // get the real object before linking
 1185  
                     refObj = getProxyFactory().getRealObject(refObj);
 1186  
                     link(refObj, refCld, cod, obj, insert);
 1187  
                     // if enabled cascade store and not only link, store the refObj
 1188  
                     if(!linkOnly && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
 1189  
                     {
 1190  
                         store(refObj);
 1191  
                     }
 1192  
                 }
 1193  
             }
 1194  
         }
 1195  
     }
 1196  
 
 1197  
     /**
 1198  
      * Assign FK value to target object by reading PK values of referenced object.
 1199  
      *
 1200  
      * @param targetObject real (non-proxy) target object
 1201  
      * @param cld {@link ClassDescriptor} of the real target object
 1202  
      * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
 1203  
      * associated with the real object.
 1204  
      * @param referencedObject referenced object or proxy
 1205  
      * @param insert Show if "linking" is done while insert or update.
 1206  
      */
 1207  
     public void link(Object targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds, Object referencedObject, boolean insert)
 1208  
     {
 1209  
         // MBAIRD: we have 'disassociated' this object from the referenced object,
 1210  
         // the object represented by the reference descriptor is now null, so set
 1211  
         // the fk in the target object to null.
 1212  
         // arminw: if an insert was done and ref object was null, we should allow
 1213  
         // to pass FK fields of main object (maybe only the FK fields are set)
 1214  
         if (referencedObject == null)
 1215  
         {
 1216  
             /*
 1217  
             arminw:
 1218  
             if update we set FK fields to 'null', because reference was disassociated
 1219  
             We do nothing on insert, maybe only the FK fields of main object (without
 1220  
             materialization of the reference object) are set by the user
 1221  
             */
 1222  
             if(!insert)
 1223  
             {
 1224  
                 unlinkFK(targetObject, cld, rds);
 1225  
             }
 1226  
         }
 1227  
         else
 1228  
         {
 1229  
             setFKField(targetObject, cld, rds, referencedObject);
 1230  
         }
 1231  
     }
 1232  
 
 1233  
     /**
 1234  
      * Unkink FK fields of target object.
 1235  
      *
 1236  
      * @param targetObject real (non-proxy) target object
 1237  
      * @param cld {@link ClassDescriptor} of the real target object
 1238  
      * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
 1239  
      * associated with the real object.
 1240  
      */
 1241  
     public void unlinkFK(Object targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds)
 1242  
     {
 1243  
         setFKField(targetObject, cld, rds, null);
 1244  
     }
 1245  
 
 1246  
     /**
 1247  
      * Set the FK value on the target object, extracted from the referenced object. If the referenced object was
 1248  
      * <i>null</i> the FK values were set to <i>null</i>, expect when the FK field was declared as PK.
 1249  
      *
 1250  
      * @param targetObject real (non-proxy) target object
 1251  
      * @param cld {@link ClassDescriptor} of the real target object
 1252  
      * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
 1253  
      * @param referencedObject The referenced object or <i>null</i>
 1254  
      */
 1255  
     private void setFKField(Object targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds, Object referencedObject)
 1256  
     {
 1257  
         ValueContainer[] refPkValues;
 1258  
         FieldDescriptor fld;
 1259  
         FieldDescriptor[] objFkFields = rds.getForeignKeyFieldDescriptors(cld);
 1260  
         if (objFkFields == null)
 1261  
         {
 1262  
             throw new PersistenceBrokerException("No foreign key fields defined for class '"+cld.getClassNameOfObject()+"'");
 1263  
         }
 1264  
         if(referencedObject == null)
 1265  
         {
 1266  
             refPkValues = null;
 1267  
         }
 1268  
         else
 1269  
         {
 1270  
             Class refClass = proxyFactory.getRealClass(referencedObject);
 1271  
             ClassDescriptor refCld = getClassDescriptor(refClass);
 1272  
             refPkValues = brokerHelper.getKeyValues(refCld, referencedObject, false);
 1273  
         }
 1274  
         for (int i = 0; i < objFkFields.length; i++)
 1275  
         {
 1276  
             fld = objFkFields[i];
 1277  
             /*
 1278  
             arminw:
 1279  
             we set the FK value when the extracted PK fields from the referenced object are not null at all
 1280  
             or if null, the FK field was not a PK field of target object too.
 1281  
             Should be ok, because the values of the extracted PK field values should never be null and never
 1282  
             change, so it doesn't matter if the target field is a PK too.
 1283  
             */
 1284  
             if(refPkValues != null || !fld.isPrimaryKey())
 1285  
             {
 1286  
                 fld.getPersistentField().set(targetObject, refPkValues != null ? refPkValues[i].getValue(): null);
 1287  
             }
 1288  
         }
 1289  
     }
 1290  
 
 1291  
     /**
 1292  
      * Assign FK value of main object with PK values of the reference object.
 1293  
      *
 1294  
      * @param obj real object with reference (proxy) object (or real object with set FK values on insert)
 1295  
      * @param cld {@link ClassDescriptor} of the real object
 1296  
      * @param rds An {@link ObjectReferenceDescriptor} of real object.
 1297  
      * @param insert Show if "linking" is done while insert or update.
 1298  
      */
 1299  
     public void linkOneToOne(Object obj, ClassDescriptor cld, ObjectReferenceDescriptor rds, boolean insert)
 1300  
     {
 1301  
         storeAndLinkOneToOne(true, obj, cld, rds, true);
 1302  
     }
 1303  
 
 1304  
     /**
 1305  
      * Assign FK value to all n-side objects referenced by given object.
 1306  
      *
 1307  
      * @param obj real object with 1:n reference
 1308  
      * @param cod {@link CollectionDescriptor} of referenced 1:n objects
 1309  
      * @param insert flag signal insert operation, false signals update operation
 1310  
      */
 1311  
     public void linkOneToMany(Object obj, CollectionDescriptor cod, boolean insert)
 1312  
     {
 1313  
         Object referencedObjects = cod.getPersistentField().get(obj);
 1314  
         storeAndLinkOneToMany(true, obj, cod,referencedObjects, insert);
 1315  
     }
 1316  
 
 1317  
     /**
 1318  
      * Assign FK values and store entries in indirection table
 1319  
      * for all objects referenced by given object.
 1320  
      *
 1321  
      * @param obj real object with 1:n reference
 1322  
      * @param cod {@link CollectionDescriptor} of referenced 1:n objects
 1323  
      * @param insert flag signal insert operation, false signals update operation
 1324  
      */
 1325  
     public void linkMtoN(Object obj, CollectionDescriptor cod, boolean insert)
 1326  
     {
 1327  
         Object referencedObjects = cod.getPersistentField().get(obj);
 1328  
         storeAndLinkMtoN(true, obj, cod, referencedObjects, insert);
 1329  
     }
 1330  
 
 1331  
     public void unlinkXtoN(Object obj, CollectionDescriptor col)
 1332  
     {
 1333  
         if(col.isMtoNRelation())
 1334  
         {
 1335  
             // if this is a m:n mapped table, remove entries from indirection table
 1336  
             mtoNBroker.deleteMtoNImplementor(col, obj);
 1337  
         }
 1338  
         else
 1339  
         {
 1340  
             Object collectionObject = col.getPersistentField().get(obj);
 1341  
             if (collectionObject != null)
 1342  
             {
 1343  
                 Iterator colIterator = BrokerHelper.getCollectionIterator(collectionObject);
 1344  
                 ClassDescriptor cld = null;
 1345  
                 while (colIterator.hasNext())
 1346  
                 {
 1347  
                     Object target = colIterator.next();
 1348  
                     if(cld == null) cld = getClassDescriptor(getProxyFactory().getRealClass(target));
 1349  
                     unlinkFK(target, cld, col);
 1350  
                 }
 1351  
             }
 1352  
         }
 1353  
     }
 1354  
 
 1355  
     /**
 1356  
      * Retrieve all References (also Collection-attributes) of a given instance.
 1357  
      * Loading is forced, even if the collection- and reference-descriptors differ.
 1358  
      * @param pInstance the persistent instance to work with
 1359  
      */
 1360  
     public void retrieveAllReferences(Object pInstance) throws PersistenceBrokerException
 1361  
     {
 1362  
         if (logger.isDebugEnabled())
 1363  
         {
 1364  
                 logger.debug("Manually retrieving all references for object " + serviceIdentity().buildIdentity(pInstance));
 1365  
         }
 1366  
         ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
 1367  
         getInternalCache().enableMaterializationCache();
 1368  
         // to avoid problems with circular references, locally cache the current object instance
 1369  
         Identity oid = serviceIdentity().buildIdentity(pInstance);
 1370  
 //        boolean needLocalRemove = false;
 1371  
         if(getInternalCache().doLocalLookup(oid) == null)
 1372  
         {
 1373  
             getInternalCache().doInternalCache(oid, pInstance, MaterializationCache.TYPE_TEMP);
 1374  
 //            needLocalRemove = true;
 1375  
         }
 1376  
         try
 1377  
         {
 1378  
             referencesBroker.retrieveReferences(pInstance, cld, true);
 1379  
             referencesBroker.retrieveCollections(pInstance, cld, true);
 1380  
 // arminw: should no longer needed since we use TYPE_TEMP for this kind of objects
 1381  
 //            // do locally remove the object to avoid problems with object state detection (insert/update),
 1382  
 //            // because objects found in the cache detected as 'old' means 'update'
 1383  
 //            if(needLocalRemove) getInternalCache().doLocalRemove(oid);
 1384  
             getInternalCache().disableMaterializationCache();
 1385  
         }
 1386  
         catch(RuntimeException e)
 1387  
         {
 1388  
             getInternalCache().doLocalClear();
 1389  
             throw e;
 1390  
         }
 1391  
     }
 1392  
 
 1393  
     /**
 1394  
      * retrieve a single reference- or collection attribute
 1395  
      * of a persistent instance.
 1396  
      * @param pInstance the persistent instance
 1397  
      * @param pAttributeName the name of the Attribute to load
 1398  
      */
 1399  
     public void retrieveReference(Object pInstance, String pAttributeName) throws PersistenceBrokerException
 1400  
     {
 1401  
         if (logger.isDebugEnabled())
 1402  
         {
 1403  
                 logger.debug("Retrieving reference named ["+pAttributeName+"] on object of type ["+
 1404  
                             pInstance.getClass().getName()+"]");
 1405  
         }
 1406  
         ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
 1407  
         CollectionDescriptor cod = cld.getCollectionDescriptorByName(pAttributeName);
 1408  
         getInternalCache().enableMaterializationCache();
 1409  
         // to avoid problems with circular references, locally cache the current object instance
 1410  
         Identity oid = serviceIdentity().buildIdentity(pInstance);
 1411  
         boolean needLocalRemove = false;
 1412  
         if(getInternalCache().doLocalLookup(oid) == null)
 1413  
         {
 1414  
             getInternalCache().doInternalCache(oid, pInstance, MaterializationCache.TYPE_TEMP);
 1415  
             needLocalRemove = true;
 1416  
         }
 1417  
         try
 1418  
         {
 1419  
             if (cod != null)
 1420  
             {
 1421  
                 referencesBroker.retrieveCollection(pInstance, cld, cod, true);
 1422  
             }
 1423  
             else
 1424  
             {
 1425  
                 ObjectReferenceDescriptor ord = cld.getObjectReferenceDescriptorByName(pAttributeName);
 1426  
                 if (ord != null)
 1427  
                 {
 1428  
                     referencesBroker.retrieveReference(pInstance, cld, ord, true);
 1429  
                 }
 1430  
                 else
 1431  
                 {
 1432  
                     throw new PersistenceBrokerException("did not find attribute " + pAttributeName +
 1433  
                             " for class " + pInstance.getClass().getName());
 1434  
                 }
 1435  
             }
 1436  
             // do locally remove the object to avoid problems with object state detection (insert/update),
 1437  
             // because objects found in the cache detected as 'old' means 'update'
 1438  
             if(needLocalRemove) getInternalCache().doLocalRemove(oid);
 1439  
             getInternalCache().disableMaterializationCache();
 1440  
         }
 1441  
         catch(RuntimeException e)
 1442  
         {
 1443  
             getInternalCache().doLocalClear();
 1444  
             throw e;
 1445  
         }
 1446  
     }
 1447  
 
 1448  
     /**
 1449  
      * Check if the references of the specified object have enabled
 1450  
      * the <em>refresh</em> attribute and refresh the reference if set <em>true</em>.
 1451  
      *
 1452  
      * @throws PersistenceBrokerException if there is a error refreshing collections or references
 1453  
      * @param obj The object to check.
 1454  
      * @param oid The {@link Identity} of the object.
 1455  
      * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
 1456  
      */
 1457  
     public void checkRefreshRelationships(Object obj, Identity oid, ClassDescriptor cld)
 1458  
     {
 1459  
         Iterator iter;
 1460  
         CollectionDescriptor cds;
 1461  
         ObjectReferenceDescriptor rds;
 1462  
         // to avoid problems with circular references, locally cache the current object instance
 1463  
         Object tmp = getInternalCache().doLocalLookup(oid);
 1464  
         if(tmp != null && getInternalCache().isEnabledMaterialisationCache())
 1465  
         {
 1466  
             /*
 1467  
             arminw: This should fix OJB-29, infinite loops on bidirectional 1:1 relations with
 1468  
             refresh attribute 'true' for both references. OJB now assume that the object is already
 1469  
             refreshed when it's cached in the materialisation cache
 1470  
             */
 1471  
             return;
 1472  
         }
 1473  
         try
 1474  
         {
 1475  
             getInternalCache().enableMaterializationCache();
 1476  
             if(tmp == null)
 1477  
             {
 1478  
                 getInternalCache().doInternalCache(oid, obj, MaterializationCache.TYPE_TEMP);
 1479  
             }
 1480  
             if(logger.isDebugEnabled()) logger.debug("Refresh relationships for " + oid);
 1481  
             iter = cld.getCollectionDescriptors().iterator();
 1482  
             while (iter.hasNext())
 1483  
             {
 1484  
                 cds = (CollectionDescriptor) iter.next();
 1485  
                 if (cds.isRefresh())
 1486  
                 {
 1487  
                     referencesBroker.retrieveCollection(obj, cld, cds, false);
 1488  
                 }
 1489  
             }
 1490  
             iter = cld.getObjectReferenceDescriptors().iterator();
 1491  
             while (iter.hasNext())
 1492  
             {
 1493  
                 rds = (ObjectReferenceDescriptor) iter.next();
 1494  
                 if (rds.isRefresh())
 1495  
                 {
 1496  
                     referencesBroker.retrieveReference(obj, cld, rds, false);
 1497  
                 }
 1498  
             }
 1499  
             getInternalCache().disableMaterializationCache();
 1500  
         }
 1501  
         catch(RuntimeException e)
 1502  
         {
 1503  
             getInternalCache().doLocalClear();
 1504  
             throw e;
 1505  
         }
 1506  
     }
 1507  
 
 1508  
     /**
 1509  
      * retrieve a collection of type collectionClass matching the Query query
 1510  
      *
 1511  
      * @see org.apache.ojb.broker.PersistenceBroker#getCollectionByQuery(Class, Query)
 1512  
      */
 1513  
     public ManageableCollection getCollectionByQuery(Class collectionClass, Query query)
 1514  
             throws PersistenceBrokerException
 1515  
     {
 1516  
         return referencesBroker.getCollectionByQuery(collectionClass, query, false);
 1517  
     }
 1518  
 
 1519  
     /**
 1520  
      * retrieve a collection of itemClass Objects matching the Query query
 1521  
      */
 1522  
     public Collection getCollectionByQuery(Query query) throws PersistenceBrokerException
 1523  
     {
 1524  
         return referencesBroker.getCollectionByQuery(query, false);
 1525  
     }
 1526  
 
 1527  
     /**
 1528  
      * Retrieve an plain object (without populated references) by it's identity
 1529  
      * from the database
 1530  
      *
 1531  
      * @param cld the real {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object to refresh
 1532  
      * @param oid the {@link org.apache.ojb.broker.Identity} of the object
 1533  
      * @return A new plain object read from the database or <em>null</em> if not found
 1534  
      * @throws ClassNotPersistenceCapableException
 1535  
      */
 1536  
     private Object getPlainDBObject(ClassDescriptor cld, Identity oid) throws ClassNotPersistenceCapableException
 1537  
     {
 1538  
         Object newObj = null;
 1539  
 
 1540  
         // Class is NOT an Interface: it has a directly mapped table and we lookup this table first:
 1541  
         if (!cld.isInterface())
 1542  
         {
 1543  
             // 1. try to retrieve skalar fields from directly mapped table columns
 1544  
             newObj = dbAccess.materializeObject(cld, oid);
 1545  
         }
 1546  
 
 1547  
         // if we did not find the object yet AND if the cld represents an Extent,
 1548  
         // we can lookup all tables of the extent classes:
 1549  
         if (newObj == null && cld.isExtent())
 1550  
         {
 1551  
             Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
 1552  
 
 1553  
             while (extents.hasNext())
 1554  
             {
 1555  
                 ClassDescriptor extCld = (ClassDescriptor) extents.next();
 1556  
 
 1557  
                 newObj = dbAccess.materializeObject(extCld, oid);
 1558  
                 if (newObj != null)
 1559  
                 {
 1560  
                     break;
 1561  
                 }
 1562  
             }
 1563  
         }
 1564  
         return newObj;
 1565  
     }
 1566  
     
 1567  
     
 1568  
     /**
 1569  
      * Retrieve an full materialized (dependent on the metadata settings)
 1570  
      * object by it's identity from the database, as well as caching the
 1571  
      * object
 1572  
      *
 1573  
      * @param oid The {@link org.apache.ojb.broker.Identity} of the object to for
 1574  
      * @return A new object read from the database or <em>null</em> if not found
 1575  
      * @throws ClassNotPersistenceCapableException
 1576  
      */
 1577  
     private Object getDBObject(Identity oid) throws ClassNotPersistenceCapableException
 1578  
     {
 1579  
         Class c = oid.getObjectsRealClass();
 1580  
 
 1581  
         if (c == null)
 1582  
         {
 1583  
             logger.info("Real class for used Identity object is 'null', use top-level class instead");
 1584  
             c = oid.getObjectsTopLevelClass();
 1585  
         }
 1586  
 
 1587  
         ClassDescriptor cld = getClassDescriptor(c);
 1588  
         Object newObj = getPlainDBObject(cld, oid);
 1589  
 
 1590  
         // loading references is useful only when the Object could be found in db:
 1591  
         if (newObj != null)
 1592  
         {
 1593  
             if (oid.getObjectsRealClass() == null)
 1594  
             {
 1595  
                 oid.setObjectsRealClass(newObj.getClass());
 1596  
             }
 1597  
 
 1598  
             /*
 1599  
              * synchronize on newObj so the ODMG-layer can take a snapshot only of
 1600  
              * fully cached (i.e. with all references + collections) objects
 1601  
              */
 1602  
             synchronized (newObj)
 1603  
             {
 1604  
                 objectCache.enableMaterializationCache();
 1605  
                 try
 1606  
                 {
 1607  
                     // cache object immediately , so that references
 1608  
                     // can be established from referenced Objects back to this Object
 1609  
                     objectCache.doInternalCache(oid, newObj, ObjectCacheInternal.TYPE_NEW_MATERIALIZED);
 1610  
 
 1611  
                     /*
 1612  
                      * Chris Lewington: can cause problems with multiple objects
 1613  
                      * mapped to one table, as follows:
 1614  
                      *
 1615  
                      * if the class searched on does not match the retrieved
 1616  
                      * class, eg a search on an OID retrieves a row but it could
 1617  
                      * be a different class (OJB gets all column values),
 1618  
                      * then trying to resolve references will fail as the object
 1619  
                      * will not match the Class Descriptor.
 1620  
                      *
 1621  
                      * To be safe, get the descriptor of the retrieved object
 1622  
                      * BEFORE resolving refs
 1623  
                      */
 1624  
                     ClassDescriptor newObjCld = getClassDescriptor(newObj.getClass());
 1625  
                     // don't force loading of references:
 1626  
                     final boolean unforced = false;
 1627  
 
 1628  
                     // 2. retrieve non-skalar fields that contain objects retrievable from other tables
 1629  
                     referencesBroker.retrieveReferences(newObj, newObjCld, unforced);
 1630  
                     // 3. retrieve collection fields from foreign-key related tables:
 1631  
                     referencesBroker.retrieveCollections(newObj, newObjCld, unforced);
 1632  
                     objectCache.disableMaterializationCache();
 1633  
                 }
 1634  
                 catch(RuntimeException e)
 1635  
                 {
 1636  
                     objectCache.doLocalClear();
 1637  
                     throw e;
 1638  
                 }
 1639  
             }
 1640  
         }
 1641  
 
 1642  
         return newObj;
 1643  
     }
 1644  
 
 1645  
     /**
 1646  
      * returns an Iterator that iterates Objects of class c if calling the .next()
 1647  
      * method. The Elements returned come from a SELECT ... WHERE Statement
 1648  
      * that is defined by the Query query.
 1649  
      * If itemProxy is null, no proxies are used.
 1650  
      */
 1651  
     public Iterator getIteratorByQuery(Query query) throws PersistenceBrokerException
 1652  
     {
 1653  
         Class itemClass = query.getSearchClass();
 1654  
         ClassDescriptor cld = getClassDescriptor(itemClass);
 1655  
         return getIteratorFromQuery(query, cld);
 1656  
     }
 1657  
 
 1658  
     /**
 1659  
      * Get an extent aware Iterator based on the Query
 1660  
      *
 1661  
      * @param query
 1662  
      * @param cld the ClassDescriptor
 1663  
      * @return OJBIterator
 1664  
      */
 1665  
     protected OJBIterator getIteratorFromQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
 1666  
     {
 1667  
         RsIteratorFactory factory = RsIteratorFactoryImpl.getInstance();
 1668  
         OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
 1669  
 
 1670  
         if (query.usePaging())
 1671  
         {
 1672  
             result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex());
 1673  
         }
 1674  
         return result;
 1675  
     }
 1676  
 
 1677  
     public Object getObjectByIdentity(Identity id) throws PersistenceBrokerException
 1678  
     {
 1679  
         objectCache.enableMaterializationCache();
 1680  
         Object result = null;
 1681  
         try
 1682  
         {
 1683  
             result = doGetObjectByIdentity(id);
 1684  
             objectCache.disableMaterializationCache();
 1685  
         }
 1686  
         catch(RuntimeException e)
 1687  
         {
 1688  
             // catch runtime exc. to guarantee clearing of internal buffer on failure
 1689  
             objectCache.doLocalClear();
 1690  
             throw e;
 1691  
         }
 1692  
         return result;
 1693  
     }
 1694  
 
 1695  
     /**
 1696  
      * Internal used method to retrieve object based on Identity.
 1697  
      *
 1698  
      * @param id
 1699  
      * @return
 1700  
      * @throws PersistenceBrokerException
 1701  
      */
 1702  
     public Object doGetObjectByIdentity(Identity id) throws PersistenceBrokerException
 1703  
     {
 1704  
         if (logger.isDebugEnabled()) logger.debug("getObjectByIdentity " + id);
 1705  
 
 1706  
         // check if object is present in ObjectCache:
 1707  
         Object obj = objectCache.lookup(id);
 1708  
         // only perform a db lookup if necessary (object not cached yet)
 1709  
         if (obj == null)
 1710  
         {
 1711  
             obj = getDBObject(id);
 1712  
         }
 1713  
         else
 1714  
         {
 1715  
             ClassDescriptor cld = getClassDescriptor(obj.getClass());
 1716  
             // if specified in the ClassDescriptor the instance must be refreshed
 1717  
             if (cld.isAlwaysRefresh())
 1718  
             {
 1719  
                 refreshInstance(obj, id, cld);
 1720  
             }
 1721  
             // now refresh all references
 1722  
             checkRefreshRelationships(obj, id, cld);
 1723  
         }
 1724  
 
 1725  
         // Invoke events on PersistenceBrokerAware instances and listeners
 1726  
         AFTER_LOOKUP_EVENT.setTarget(obj);
 1727  
         fireBrokerEvent(AFTER_LOOKUP_EVENT);
 1728  
         AFTER_LOOKUP_EVENT.setTarget(null);
 1729  
 
 1730  
         //logger.info("RETRIEVING object " + obj);
 1731  
         return obj;
 1732  
     }
 1733  
 
 1734  
     /**
 1735  
      * refresh all primitive typed attributes of a cached instance
 1736  
      * with the current values from the database.
 1737  
      * refreshing of reference and collection attributes is not done
 1738  
      * here.
 1739  
      * @param cachedInstance the cached instance to be refreshed
 1740  
      * @param oid the Identity of the cached instance
 1741  
      * @param cld the ClassDescriptor of cachedInstance
 1742  
      */
 1743  
     private void refreshInstance(Object cachedInstance, Identity oid, ClassDescriptor cld)
 1744  
     {
 1745  
         // read in fresh copy from the db, but do not cache it
 1746  
         Object freshInstance = getPlainDBObject(cld, oid);
 1747  
 
 1748  
         // update all primitive typed attributes
 1749  
         FieldDescriptor[] fields = cld.getFieldDescriptions();
 1750  
         FieldDescriptor fmd;
 1751  
         PersistentField fld;
 1752  
         for (int i = 0; i < fields.length; i++)
 1753  
         {
 1754  
             fmd = fields[i];
 1755  
             fld = fmd.getPersistentField();
 1756  
             fld.set(cachedInstance, fld.get(freshInstance));
 1757  
         }
 1758  
     }
 1759  
 
 1760  
     /**
 1761  
      * retrieve an Object by query
 1762  
      * I.e perform a SELECT ... FROM ... WHERE ...  in an RDBMS
 1763  
      */
 1764  
     public Object getObjectByQuery(Query query) throws PersistenceBrokerException
 1765  
     {
 1766  
         Object result = null;
 1767  
         if (query instanceof QueryByIdentity)
 1768  
         {
 1769  
             // example obj may be an entity or an Identity
 1770  
             Object obj = query.getExampleObject();
 1771  
             if (obj instanceof Identity)
 1772  
             {
 1773  
                 Identity oid = (Identity) obj;
 1774  
                 result = getObjectByIdentity(oid);
 1775  
             }
 1776  
             else
 1777  
             {
 1778  
                 // TODO: This workaround doesn't allow 'null' for PK fields
 1779  
                 if (!serviceBrokerHelper().hasNullPKField(getClassDescriptor(obj.getClass()), obj))
 1780  
                 {
 1781  
                     Identity oid = serviceIdentity().buildIdentity(obj);
 1782  
                     result = getObjectByIdentity(oid);
 1783  
                 }
 1784  
             }
 1785  
         }
 1786  
         else
 1787  
         {
 1788  
             Class itemClass = query.getSearchClass();
 1789  
             ClassDescriptor cld = getClassDescriptor(itemClass);
 1790  
             /*
 1791  
             use OJB intern Iterator, thus we are able to close used
 1792  
             resources instantly
 1793  
             */
 1794  
             OJBIterator it = getIteratorFromQuery(query, cld);
 1795  
             /*
 1796  
             arminw:
 1797  
             patch by Andre Clute, instead of taking the first found result
 1798  
             try to get the first found none null result.
 1799  
             He wrote:
 1800  
             I have a situation where an item with a certain criteria is in my
 1801  
             database twice -- once deleted, and then a non-deleted version of it.
 1802  
             When I do a PB.getObjectByQuery(), the RsIterator get's both results
 1803  
             from the database, but the first row is the deleted row, so my RowReader
 1804  
             filters it out, and do not get the right result.
 1805  
             */
 1806  
             try
 1807  
             {
 1808  
                 while (result==null && it.hasNext())
 1809  
                 {
 1810  
                     result = it.next();
 1811  
                 }
 1812  
             } // make sure that we close the used resources
 1813  
             finally
 1814  
             {
 1815  
                 if(it != null) it.releaseDbResources();
 1816  
             }
 1817  
         }
 1818  
         return result;
 1819  
     }
 1820  
 
 1821  
     /**
 1822  
      * returns an Enumeration of PrimaryKey Objects for objects of class DataClass.
 1823  
      * The Elements returned come from a SELECT ... WHERE Statement
 1824  
      * that is defined by the fields and their coresponding values of listFields
 1825  
      * and listValues.
 1826  
      * Useful for EJB Finder Methods...
 1827  
      * @param primaryKeyClass the pk class for the searched objects
 1828  
      * @param query the query
 1829  
      */
 1830  
     public Enumeration getPKEnumerationByQuery(Class primaryKeyClass, Query query) throws PersistenceBrokerException
 1831  
     {
 1832  
         if (logger.isDebugEnabled()) logger.debug("getPKEnumerationByQuery " + query);
 1833  
 
 1834  
         query.setFetchSize(1);
 1835  
         ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
 1836  
         return new PkEnumeration(query, cld, primaryKeyClass, this);
 1837  
     }
 1838  
 
 1839  
     /**
 1840  
      * Makes object obj persistent in the underlying persistence system.
 1841  
      * E.G. by INSERT INTO ... or UPDATE ...  in an RDBMS.
 1842  
      * The ObjectModification parameter can be used to determine whether INSERT or update is to be used.
 1843  
      * This functionality is typically called from transaction managers, that
 1844  
      * track which objects have to be stored. If the object is an unmaterialized
 1845  
      * proxy the method return immediately.
 1846  
      */
 1847  
     public void store(Object obj, ObjectModification mod) throws PersistenceBrokerException
 1848  
     {
 1849  
         obj = extractObjectToStore(obj);
 1850  
         // null for unmaterialized Proxy
 1851  
         if (obj == null)
 1852  
         {
 1853  
             return;
 1854  
         }
 1855  
 
 1856  
         ClassDescriptor cld = getClassDescriptor(obj.getClass());
 1857  
         // this call ensures that all autoincremented primary key attributes are filled
 1858  
         Identity oid = serviceIdentity().buildIdentity(cld, obj);
 1859  
         // select flag for insert / update selection by checking the ObjectModification
 1860  
         if (mod.needsInsert())
 1861  
         {
 1862  
             store(obj, oid, cld, true);
 1863  
         }
 1864  
         else if (mod.needsUpdate())
 1865  
         {
 1866  
             store(obj, oid, cld, false);
 1867  
         }
 1868  
         /*
 1869  
         arminw
 1870  
         TODO: Why we need this behaviour? What about 1:1 relations?
 1871  
         */
 1872  
         else
 1873  
         {
 1874  
             // just store 1:n and m:n associations
 1875  
             storeCollections(obj, cld, mod.needsInsert());
 1876  
         }
 1877  
     }
 1878  
 
 1879  
     /**
 1880  
      * I pulled this out of internal store so that when doing multiple table
 1881  
      * inheritance, i can recurse this function.
 1882  
      *
 1883  
      * @param obj
 1884  
      * @param cld
 1885  
      * @param oid   BRJ: what is it good for ???
 1886  
      * @param insert
 1887  
      * @param ignoreReferences
 1888  
      */
 1889  
     private void storeToDb(Object obj, ClassDescriptor cld, Identity oid, boolean insert, boolean ignoreReferences)
 1890  
     {
 1891  
         // 1. link and store 1:1 references
 1892  
         storeReferences(obj, cld, insert, ignoreReferences);
 1893  
 
 1894  
         Object[] pkValues = oid.getPrimaryKeyValues();
 1895  
         if (!serviceBrokerHelper().assertValidPksForStore(cld.getPkFields(), pkValues))
 1896  
         {
 1897  
             // BRJ: fk values may be part of pk, but the are not known during
 1898  
             // creation of Identity. so we have to get them here
 1899  
             pkValues = serviceBrokerHelper().getKeyValues(cld, obj);
 1900  
             if (!serviceBrokerHelper().assertValidPksForStore(cld.getPkFields(), pkValues))
 1901  
             {
 1902  
                 String append = insert ? " on insert" : " on update" ;
 1903  
                 throw new PersistenceBrokerException("assertValidPkFields failed for Object of type: " + cld.getClassNameOfObject() + append);
 1904  
             }
 1905  
         }
 1906  
 
 1907  
         // get super class cld then store it with the object
 1908  
         /*
 1909  
         now for multiple table inheritance
 1910  
         1. store super classes, topmost parent first
 1911  
         2. go down through heirarchy until current class
 1912  
         3. todo: store to full extent?
 1913  
 
 1914  
 // arminw: TODO: The extend-attribute feature dosn't work, should we remove this stuff?
 1915  
         This if-clause will go up the inheritance heirarchy to store all the super classes.
 1916  
         The id for the top most super class will be the id for all the subclasses too
 1917  
          */
 1918  
         if(cld.getSuperClass() != null)
 1919  
         {
 1920  
 
 1921  
             ClassDescriptor superCld = getDescriptorRepository().getDescriptorFor(cld.getSuperClass());
 1922  
             storeToDb(obj, superCld, oid, insert);
 1923  
             // arminw: why this?? I comment out this section
 1924  
             // storeCollections(obj, cld.getCollectionDescriptors(), insert);
 1925  
         }
 1926  
 
 1927  
         // 2. store primitive typed attributes (Or is THIS step 3 ?)
 1928  
         // if obj not present in db use INSERT
 1929  
         if (insert)
 1930  
         {
 1931  
             dbAccess.executeInsert(cld, obj);
 1932  
             if(oid.isTransient())
 1933  
             {
 1934  
                 // Create a new Identity based on the current set of primary key values.
 1935  
                 oid = serviceIdentity().buildIdentity(cld, obj);
 1936  
             }
 1937  
         }
 1938  
         // else use UPDATE
 1939  
         else
 1940  
         {
 1941  
             try
 1942  
             {
 1943  
                 dbAccess.executeUpdate(cld, obj);
 1944  
             }
 1945  
             catch(OptimisticLockException e)
 1946  
             {
 1947  
                 // ensure that the outdated object be removed from cache
 1948  
                 objectCache.remove(oid);
 1949  
                 throw e;
 1950  
             }
 1951  
         }
 1952  
         // cache object for symmetry with getObjectByXXX()
 1953  
         // Add the object to the cache.
 1954  
         objectCache.doInternalCache(oid, obj, ObjectCacheInternal.TYPE_WRITE);
 1955  
         // 3. store 1:n and m:n associations
 1956  
         if(!ignoreReferences) storeCollections(obj, cld, insert);
 1957  
     }
 1958  
 
 1959  
     /**
 1960  
      * I pulled this out of internal store so that when doing multiple table
 1961  
      * inheritance, i can recurse this function.
 1962  
      *
 1963  
      * @param obj
 1964  
      * @param cld
 1965  
      * @param oid   BRJ: what is it good for ???
 1966  
      * @param insert
 1967  
      */
 1968  
     private void storeToDb(Object obj, ClassDescriptor cld, Identity oid, boolean insert)
 1969  
     {
 1970  
         storeToDb(obj, cld, oid, insert, false);
 1971  
     }
 1972  
 
 1973  
     /**
 1974  
      * returns true if the broker is currently running a transaction.
 1975  
      * @return boolean
 1976  
      */
 1977  
     public boolean isInTransaction()
 1978  
     {
 1979  
         // return this.connectionManager.isInLocalTransaction();
 1980  
         return inTransaction;
 1981  
     }
 1982  
 
 1983  
     public void setInTransaction(boolean inTransaction)
 1984  
     {
 1985  
         this.inTransaction = inTransaction;
 1986  
     }
 1987  
 
 1988  
     /**
 1989  
      * @see org.apache.ojb.broker.PersistenceBroker#removeFromCache
 1990  
      */
 1991  
     public void removeFromCache(Object objectOrIdentity) throws PersistenceBrokerException
 1992  
     {
 1993  
         Identity identity;
 1994  
         if (objectOrIdentity instanceof Identity)
 1995  
         {
 1996  
             identity = (Identity)objectOrIdentity;
 1997  
         }
 1998  
         else
 1999  
         {
 2000  
             identity = serviceIdentity().buildIdentity(objectOrIdentity);
 2001  
         }
 2002  
         objectCache.remove(identity);
 2003  
     }
 2004  
 
 2005  
     /**
 2006  
      * returns a ClassDescriptor for the persistence capable class clazz.
 2007  
      * throws a PersistenceBrokerException if clazz is not persistence capable,
 2008  
      * i.e. if clazz is not defined in the DescriptorRepository.
 2009  
      */
 2010  
     public ClassDescriptor getClassDescriptor(Class clazz) throws PersistenceBrokerException
 2011  
     {
 2012  
         return descriptorRepository.getDescriptorFor(clazz);
 2013  
     }
 2014  
 
 2015  
     public boolean hasClassDescriptor(Class clazz)
 2016  
     {
 2017  
         return descriptorRepository.hasDescriptorFor(clazz);
 2018  
     }
 2019  
 
 2020  
     /**
 2021  
      * clears the brokers internal cache.
 2022  
      * removing is recursive. That is referenced Objects are also
 2023  
      * removed from the cache, if the auto-retrieve flag is set
 2024  
      * for obj.getClass() in the metadata repository.
 2025  
      *
 2026  
      */
 2027  
     public void clearCache() throws PersistenceBrokerException
 2028  
     {
 2029  
         objectCache.clear();
 2030  
     }
 2031  
 
 2032  
     /**
 2033  
      * @see org.apache.ojb.broker.PersistenceBroker#getTopLevelClass
 2034  
      */
 2035  
     public Class getTopLevelClass(Class clazz) throws PersistenceBrokerException
 2036  
     {
 2037  
         try
 2038  
         {
 2039  
             return descriptorRepository.getTopLevelClass(clazz);
 2040  
         }
 2041  
         catch (ClassNotPersistenceCapableException e)
 2042  
         {
 2043  
             throw new PersistenceBrokerException(e);
 2044  
         }
 2045  
     }
 2046  
 
 2047  
     /**
 2048  
      * @see org.apache.ojb.broker.PersistenceBroker#getCount(Query)
 2049  
      */
 2050  
     public int getCount(Query query) throws PersistenceBrokerException
 2051  
     {
 2052  
         Query countQuery = serviceBrokerHelper().getCountQuery(query);
 2053  
         Iterator iter;
 2054  
         int result = 0;
 2055  
 
 2056  
         if (logger.isDebugEnabled()) logger.debug("getCount " + countQuery.getSearchClass() + ", " + countQuery);
 2057  
 
 2058  
         iter = getReportQueryIteratorByQuery(countQuery);
 2059  
         try
 2060  
         {
 2061  
             while (iter.hasNext())
 2062  
             {
 2063  
                 Object[] row = (Object[]) iter.next();
 2064  
                 result += ((Number) row[0]).intValue();
 2065  
             }
 2066  
         }
 2067  
         finally
 2068  
         {
 2069  
             if (iter instanceof OJBIterator)
 2070  
             {
 2071  
                 ((OJBIterator) iter).releaseDbResources();
 2072  
             }
 2073  
         }
 2074  
 
 2075  
         return result;
 2076  
     }
 2077  
 
 2078  
     /**
 2079  
      * Get an Iterator based on the ReportQuery
 2080  
      *
 2081  
      * @param query
 2082  
      * @return Iterator
 2083  
      */
 2084  
     public Iterator getReportQueryIteratorByQuery(Query query) throws PersistenceBrokerException
 2085  
     {
 2086  
         ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
 2087  
         return getReportQueryIteratorFromQuery(query, cld);
 2088  
     }
 2089  
 
 2090  
     /**
 2091  
      * Get an extent aware RsIterator based on the Query
 2092  
      *
 2093  
      * @param query
 2094  
      * @param cld
 2095  
      * @param factory the Factory for the RsIterator
 2096  
      * @return OJBIterator
 2097  
      */
 2098  
     private OJBIterator getRsIteratorFromQuery(Query query, ClassDescriptor cld, RsIteratorFactory factory)
 2099  
         throws PersistenceBrokerException
 2100  
     {
 2101  
         query.setFetchSize(1);
 2102  
         if (query instanceof QueryBySQL)
 2103  
         {
 2104  
             if(logger.isDebugEnabled()) logger.debug("Creating SQL-RsIterator for class ["+cld.getClassNameOfObject()+"]");
 2105  
             return factory.createRsIterator((QueryBySQL) query, cld, this);
 2106  
         }
 2107  
 
 2108  
         if (!cld.isExtent() || !query.getWithExtents())
 2109  
         {
 2110  
             // no extents just use the plain vanilla RsIterator
 2111  
             if(logger.isDebugEnabled()) logger.debug("Creating RsIterator for class ["+cld.getClassNameOfObject()+"]");
 2112  
 
 2113  
             return factory.createRsIterator(query, cld, this);
 2114  
         }
 2115  
 
 2116  
         if(logger.isDebugEnabled()) logger.debug("Creating ChainingIterator for class ["+cld.getClassNameOfObject()+"]");
 2117  
 
 2118  
         ChainingIterator chainingIter = new ChainingIterator();
 2119  
 
 2120  
         // BRJ: add base class iterator
 2121  
         if (!cld.isInterface())
 2122  
         {
 2123  
             if(logger.isDebugEnabled()) logger.debug("Adding RsIterator for class ["+cld.getClassNameOfObject()+"] to ChainingIterator");
 2124  
 
 2125  
             chainingIter.addIterator(factory.createRsIterator(query, cld, this));
 2126  
         }
 2127  
 
 2128  
         Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
 2129  
         while (extents.hasNext())
 2130  
         {
 2131  
             ClassDescriptor extCld = (ClassDescriptor) extents.next();
 2132  
 
 2133  
             // read same table only once
 2134  
             if (chainingIter.containsIteratorForTable(extCld.getFullTableName()))
 2135  
             {
 2136  
                 if(logger.isDebugEnabled()) logger.debug("Skipping class ["+extCld.getClassNameOfObject()+"]");
 2137  
             }
 2138  
             else
 2139  
             {
 2140  
                 if(logger.isDebugEnabled()) logger.debug("Adding RsIterator of class ["+extCld.getClassNameOfObject()+"] to ChainingIterator");
 2141  
 
 2142  
                 // add the iterator to the chaining iterator.
 2143  
                 chainingIter.addIterator(factory.createRsIterator(query, extCld, this));
 2144  
             }
 2145  
         }
 2146  
 
 2147  
         return chainingIter;
 2148  
     }
 2149  
 
 2150  
     /**
 2151  
      * Get an extent aware Iterator based on the ReportQuery
 2152  
      *
 2153  
      * @param query
 2154  
      * @param cld
 2155  
      * @return OJBIterator
 2156  
      */
 2157  
     private OJBIterator getReportQueryIteratorFromQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
 2158  
     {
 2159  
         RsIteratorFactory factory = ReportRsIteratorFactoryImpl.getInstance();
 2160  
         OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
 2161  
 
 2162  
         if (query.usePaging())
 2163  
         {
 2164  
             result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex());
 2165  
         }
 2166  
 
 2167  
         return result;
 2168  
     }
 2169  
 
 2170  
     /**
 2171  
      * @see org.odbms.ObjectContainer#query()
 2172  
      */
 2173  
     public org.odbms.Query query()
 2174  
     {
 2175  
         return new org.apache.ojb.soda.QueryImpl(this);
 2176  
     }
 2177  
 
 2178  
     /**
 2179  
      * @return DescriptorRepository
 2180  
      */
 2181  
     public DescriptorRepository getDescriptorRepository()
 2182  
     {
 2183  
         return descriptorRepository;
 2184  
     }
 2185  
 
 2186  
     protected void finalize()
 2187  
     {
 2188  
         if (!isClosed)
 2189  
         {
 2190  
             close();
 2191  
         }
 2192  
     }
 2193  
 
 2194  
     /**
 2195  
      * clean up the maps for reuse by the next transaction.
 2196  
      */
 2197  
     private void clearRegistrationLists()
 2198  
     {
 2199  
         nowStoring.clear();
 2200  
         objectCache.doLocalClear();
 2201  
         deletedDuringTransaction.clear();
 2202  
         /*
 2203  
         arminw:
 2204  
         for better performance I don't register MtoNBroker as listner,
 2205  
         so use this method to reset on commit/rollback
 2206  
         */
 2207  
         mtoNBroker.reset();
 2208  
     }
 2209  
     
 2210  
 
 2211  
 
 2212  
     /**
 2213  
      * @see org.apache.ojb.broker.PersistenceBroker#deleteMtoNImplementor
 2214  
      */
 2215  
     public void deleteMtoNImplementor(MtoNImplementor m2nImpl) throws PersistenceBrokerException
 2216  
     {
 2217  
         mtoNBroker.deleteMtoNImplementor(m2nImpl);
 2218  
     }
 2219  
 
 2220  
     /**
 2221  
      * @see org.apache.ojb.broker.PersistenceBroker#addMtoNImplementor
 2222  
      */
 2223  
     public void addMtoNImplementor(MtoNImplementor m2n) throws PersistenceBrokerException
 2224  
     {
 2225  
                 mtoNBroker.storeMtoNImplementor(m2n);
 2226  
     }
 2227  
 
 2228  
     public ProxyFactory getProxyFactory() {
 2229  
         return proxyFactory;
 2230  
     }
 2231  
     
 2232  
     /**
 2233  
      * Creates a proxy instance.
 2234  
      * 
 2235  
      * @param baseClassForProxy  The base class that the Proxy should extend. For dynamic Proxies, the method of 
 2236  
      *                           generation is dependent on the ProxyFactory implementation.
 2237  
      * @param realSubjectsIdentity The identity of the subject
 2238  
      * @return An instance of the proxy subclass
 2239  
      * @throws PersistenceBrokerException If there is an error creating the proxy object
 2240  
      */
 2241  
     public Object createProxy(Class baseClassForProxy, Identity realSubjectsIdentity)
 2242  
     {
 2243  
         try
 2244  
         {
 2245  
             // the invocation handler manages all delegation stuff
 2246  
             IndirectionHandler handler     = getProxyFactory().createIndirectionHandler(pbKey, realSubjectsIdentity);
 2247  
 
 2248  
             // the proxy simply provides the interface of the real subject
 2249  
             if (VirtualProxy.class.isAssignableFrom(baseClassForProxy))
 2250  
             {
 2251  
                 Constructor constructor = baseClassForProxy.getDeclaredConstructor(new Class[]{ IndirectionHandler.class });
 2252  
                 return constructor.newInstance(new Object[]{ handler });
 2253  
             }
 2254  
             else
 2255  
             {                
 2256  
                 return getProxyFactory().createProxy(baseClassForProxy,handler);
 2257  
             }
 2258  
 
 2259  
             
 2260  
         }
 2261  
         catch (Exception ex)
 2262  
         {
 2263  
             throw new PersistenceBrokerException("Unable to create proxy using class:"+baseClassForProxy.getName(), ex);
 2264  
         }
 2265  
     }
 2266  
     
 2267  
     
 2268  
 }