KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > core > PersistenceBrokerImpl


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 JavaDoc;
19 import java.util.Collection JavaDoc;
20 import java.util.Collections JavaDoc;
21 import java.util.Enumeration JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Set JavaDoc;
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.83.2.30 2005/12/13 18:21:23 arminw 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 JavaDoc 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 JavaDoc 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 JavaDoc deletedDuringTransaction = new HashSet JavaDoc();
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 JavaDoc obj, boolean ignoreReferences) throws PersistenceBrokerException
493     {
494         if(isTxCheck() && !isInTransaction())
495         {
496             if(logger.isEnabledFor(Logger.ERROR))
497             {
498                 String JavaDoc 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 JavaDoc("** Delete object without active PersistenceBroker transaction **");
503                 }
504                 catch(Exception JavaDoc 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 JavaDoc 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 JavaDoc 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              * MBAIRD
541              * 1. if we are marked for delete already, avoid recursing on this object
542              *
543              * arminw:
544              * use object instead Identity object in markedForDelete List,
545              * because using objects we get a better performance. I can't find
546              * side-effects in doing so.
547              */

548             if (markedForDelete.contains(obj))
549             {
550                 return;
551             }
552             
553             ClassDescriptor cld = getClassDescriptor(obj.getClass());
554             //BRJ: check for valid pk
555
if (!serviceBrokerHelper().assertValidPkForDelete(cld, obj))
556             {
557                 String JavaDoc msg = "Cannot delete object without valid PKs. " + obj;
558                 logger.error(msg);
559                 return;
560             }
561             
562             /**
563              * MBAIRD
564              * 2. register object in markedForDelete map.
565              */

566             markedForDelete.add(obj);
567             Identity oid = serviceIdentity().buildIdentity(cld, obj);
568
569             // Invoke events on PersistenceBrokerAware instances and listeners
570
BEFORE_DELETE_EVENT.setTarget(obj);
571             fireBrokerEvent(BEFORE_DELETE_EVENT);
572             BEFORE_DELETE_EVENT.setTarget(null);
573
574             // 1. delete dependend collections
575
if (!ignoreReferences && cld.getCollectionDescriptors().size() > 0)
576             {
577                 deleteCollections(obj, cld.getCollectionDescriptors());
578             }
579             // 2. delete object from directly mapped table
580
try
581             {
582                 dbAccess.executeDelete(cld, obj); // use obj not oid to delete, BRJ
583
}
584             catch(OptimisticLockException e)
585             {
586                 // ensure that the outdated object be removed from cache
587
objectCache.remove(oid);
588                 throw e;
589             }
590
591             // 3. Add OID to the set of deleted objects
592
deletedDuringTransaction.add(oid);
593
594             // 4. delete dependend upon objects last to avoid FK violations
595
if (cld.getObjectReferenceDescriptors().size() > 0)
596             {
597                 deleteReferences(obj, cld.getObjectReferenceDescriptors(), ignoreReferences);
598             }
599             // remove obj from the object cache:
600
objectCache.remove(oid);
601
602             // Invoke events on PersistenceBrokerAware instances and listeners
603
AFTER_DELETE_EVENT.setTarget(obj);
604             fireBrokerEvent(AFTER_DELETE_EVENT);
605             AFTER_DELETE_EVENT.setTarget(null);
606
607             // let the connection manager to execute batch
608
connectionManager.executeBatchIfNecessary();
609         }
610     }
611
612     /**
613      * Extent aware Delete by Query
614      * @param query
615      * @param cld
616      * @throws PersistenceBrokerException
617      */

618     private void deleteByQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
619     {
620         if (logger.isDebugEnabled())
621         {
622             logger.debug("deleteByQuery " + cld.getClassNameOfObject() + ", " + query);
623         }
624
625         if (query instanceof QueryBySQL)
626         {
627             String JavaDoc sql = ((QueryBySQL) query).getSql();
628             this.dbAccess.executeUpdateSQL(sql, cld);
629         }
630         else
631         {
632             // if query is Identity based transform it to a criteria based query first
633
if (query instanceof QueryByIdentity)
634             {
635                 QueryByIdentity qbi = (QueryByIdentity) query;
636                 Object JavaDoc oid = qbi.getExampleObject();
637                 // make sure it's an Identity
638
if (!(oid instanceof Identity))
639                 {
640                     oid = serviceIdentity().buildIdentity(oid);
641                 }
642                 query = referencesBroker.getPKQuery((Identity) oid);
643             }
644
645             if (!cld.isInterface())
646             {
647                 this.dbAccess.executeDelete(query, cld);
648             }
649
650             // if class is an extent, we have to delete all extent classes too
651
String JavaDoc lastUsedTable = cld.getFullTableName();
652             if (cld.isExtent())
653             {
654                 Iterator JavaDoc extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
655
656                 while (extents.hasNext())
657                 {
658                     ClassDescriptor extCld = (ClassDescriptor) extents.next();
659
660                     // read same table only once
661
if (!extCld.getFullTableName().equals(lastUsedTable))
662                     {
663                         lastUsedTable = extCld.getFullTableName();
664                         this.dbAccess.executeDelete(query, extCld);
665                     }
666                 }
667             }
668
669         }
670     }
671
672     /**
673      * @see org.apache.ojb.broker.PersistenceBroker#deleteByQuery(Query)
674      */

675     public void deleteByQuery(Query query) throws PersistenceBrokerException
676     {
677         ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
678         deleteByQuery(query, cld);
679     }
680
681     /**
682      * Deletes references that <b>obj</b> points to.
683      * All objects which we have a FK poiting to (Via ReferenceDescriptors)
684      * will be deleted if auto-delete is true <b>AND</b>
685      * the member field containing the object reference is NOT null.
686      *
687      * @param obj Object which we will delete references for
688      * @param listRds list of ObjectRederenceDescriptors
689      * @param ignoreReferences With this flag the automatic deletion/unlinking
690      * of references can be suppressed (independent of the used auto-delete setting in metadata),
691      * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
692      * these kind of reference (descriptor) will always be performed.
693      * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
694      */

695     private void deleteReferences(Object JavaDoc obj, List JavaDoc listRds, boolean ignoreReferences) throws PersistenceBrokerException
696     {
697         // get all members of obj that are references and delete them
698
Iterator JavaDoc i = listRds.iterator();
699         while (i.hasNext())
700         {
701             ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
702             if ((!ignoreReferences && rds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)
703                     || rds.isSuperReferenceDescriptor())
704             {
705                 Object JavaDoc referencedObject = rds.getPersistentField().get(obj);
706                 if (referencedObject != null)
707                 {
708                     doDelete(referencedObject, ignoreReferences);
709                 }
710             }
711         }
712     }
713
714     /**
715      * Deletes collections of objects poiting to <b>obj</b>.
716      * All object which have a FK poiting to this object (Via CollectionDescriptors)
717      * will be deleted if auto-delete is true <b>AND</b>
718      * the member field containing the object reference if NOT null.
719      *
720      * @param obj Object which we will delete collections for
721      * @param listCds list of ObjectReferenceDescriptors
722      * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
723      */

724     private void deleteCollections(Object JavaDoc obj, List JavaDoc listCds) throws PersistenceBrokerException
725     {
726         // get all members of obj that are collections and delete all their elements
727
Iterator JavaDoc i = listCds.iterator();
728
729         while (i.hasNext())
730         {
731             CollectionDescriptor cds = (CollectionDescriptor) i.next();
732             if(cds.getCascadingDelete() != ObjectReferenceDescriptor.CASCADE_NONE)
733             {
734                 if(cds.isMtoNRelation())
735                 {
736                     // if this is a m:n mapped table, remove entries from indirection table
737
mtoNBroker.deleteMtoNImplementor(cds, obj);
738                 }
739                 /*
740                 if cascading delete is on, delete all referenced objects.
741                 NOTE: User has to take care to populate all referenced objects before delete
742                 the main object to avoid referential constraint violation
743                  */

744                 if (cds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)
745                 {
746                     Object JavaDoc col = cds.getPersistentField().get(obj);
747                     if (col != null)
748                     {
749                         Iterator JavaDoc colIterator = BrokerHelper.getCollectionIterator(col);
750                         while (colIterator.hasNext())
751                         {
752                             doDelete(colIterator.next(), false);
753                         }
754                     }
755                 }
756             }
757         }
758     }
759
760     /**
761      * Store an Object.
762      * @see org.apache.ojb.broker.PersistenceBroker#store(Object)
763      */

764     public void store(Object JavaDoc obj) throws PersistenceBrokerException
765     {
766         obj = extractObjectToStore(obj);
767         // only do something if obj != null
768
if(obj == null) return;
769
770         ClassDescriptor cld = getClassDescriptor(obj.getClass());
771         /*
772         if one of the PK fields was null, we assume the objects
773         was new and needs insert
774         */

775         boolean insert = serviceBrokerHelper().hasNullPKField(cld, obj);
776         Identity oid = serviceIdentity().buildIdentity(cld, obj);
777         /*
778         if PK values are set, lookup cache or db to see whether object
779         needs insert or update
780         */

781         if (!insert)
782         {
783             insert = objectCache.lookup(oid) == null
784                 && !serviceBrokerHelper().doesExist(cld, oid, obj);
785         }
786         store(obj, oid, cld, insert);
787     }
788
789     /**
790      * Check if the given object is <code>null</code> or an unmaterialized proxy object - in
791      * both cases <code>null</code> will be returned, else the given object itself or the
792      * materialized proxy object will be returned.
793      */

794     private Object JavaDoc extractObjectToStore(Object JavaDoc obj)
795     {
796         Object JavaDoc result = obj;
797         // only do something if obj != null
798
if(result != null)
799         {
800             // ProxyObjects only have to be updated if their real
801
// subjects have been loaded
802
result = getProxyFactory().getRealObjectIfMaterialized(obj);
803             // null for unmaterialized Proxy
804
if (result == null)
805             {
806                 if(logger.isDebugEnabled())
807                     logger.debug("No materialized object could be found -> nothing to store," +
808                             " object was " + ObjectUtils.identityToString(obj));
809             }
810         }
811         return result;
812     }
813
814     /**
815      * Method which start the real store work (insert or update)
816      * and is intended for use by top-level api or internal calls.
817      *
818      * @param obj The object to store.
819      * @param oid The {@link Identity} of the object to store.
820      * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
821      * @param insert If <em>true</em> an insert operation will be performed, else update operation.
822      * @param ignoreReferences With this flag the automatic storing/linking
823      * of references can be suppressed (independent of the used auto-update setting in metadata),
824      * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
825      * these kind of reference (descriptor) will always be performed. If <em>true</em>
826      * all "normal" referenced objects will be ignored, only the specified object is handled.
827      */

828     public void store(Object JavaDoc obj, Identity oid, ClassDescriptor cld, boolean insert, boolean ignoreReferences)
829     {
830         if(obj == null || nowStoring.contains(obj))
831         {
832             return;
833         }
834
835         /*
836         if the object has been deleted during this transaction,
837         then we must insert it
838         */

839         //System.out.println("## insert: " +insert + " / deleted: " + deletedDuringTransaction);
840
if (!insert)
841         {
842             insert = deletedDuringTransaction.contains(oid);
843         }
844
845         //************************************************
846
// now store it:
847
if(isTxCheck() && !isInTransaction())
848         {
849             if(logger.isEnabledFor(Logger.ERROR))
850             {
851                 try
852                 {
853                     throw new Exception JavaDoc("** Try to store object without active PersistenceBroker transaction **");
854                 }
855                 catch(Exception JavaDoc e)
856                 {
857                     logger.error("No running tx found, please only store in context of an PB-transaction" +
858                     ", to avoid side-effects - e.g. when rollback of complex objects", e);
859                 }
860             }
861         }
862         // Invoke events on PersistenceBrokerAware instances and listeners
863
if (insert)
864         {
865             BEFORE_STORE_EVENT.setTarget(obj);
866             fireBrokerEvent(BEFORE_STORE_EVENT);
867             BEFORE_STORE_EVENT.setTarget(null);
868         }
869         else
870         {
871             BEFORE_UPDATE_EVENT.setTarget(obj);
872             fireBrokerEvent(BEFORE_UPDATE_EVENT);
873             BEFORE_UPDATE_EVENT.setTarget(null);
874         }
875
876         try
877         {
878             nowStoring.add(obj);
879             storeToDb(obj, cld, oid, insert, ignoreReferences);
880         }
881         finally
882         {
883             // to optimize calls to DB don't remove already stored objects
884
nowStoring.remove(obj);
885         }
886
887
888         // Invoke events on PersistenceBrokerAware instances and listeners
889
if (insert)
890         {
891             AFTER_STORE_EVENT.setTarget(obj);
892             fireBrokerEvent(AFTER_STORE_EVENT);
893             AFTER_STORE_EVENT.setTarget(null);
894         }
895         else
896         {
897             AFTER_UPDATE_EVENT.setTarget(obj);
898             fireBrokerEvent(AFTER_UPDATE_EVENT);
899             AFTER_UPDATE_EVENT.setTarget(null);
900         }
901         // end of store operation
902
//************************************************
903

904         // if the object was stored, remove it from deleted set
905
if(deletedDuringTransaction.size() > 0) deletedDuringTransaction.remove(oid);
906
907         // let the connection manager to execute batch
908
connectionManager.executeBatchIfNecessary();
909     }
910
911     /**
912      * Internal used method which start the real store work.
913      */

914     protected void store(Object JavaDoc obj, Identity oid, ClassDescriptor cld, boolean insert)
915     {
916         store(obj, oid, cld, insert, false);
917     }
918
919     /**
920      * Store all object references that <b>obj</b> points to.
921      * All objects which we have a FK pointing to (Via ReferenceDescriptors) will be
922      * stored if auto-update is true <b>AND</b> the member field containing the object
923      * reference is NOT null.
924      * With flag <em>ignoreReferences</em> the storing/linking
925      * of references can be suppressed (independent of the used auto-update setting),
926      * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
927      * these kind of reference (descriptor) will always be performed.
928      *
929      * @param obj Object which we will store references for
930      */

931     private void storeReferences(Object JavaDoc obj, ClassDescriptor cld, boolean insert, boolean ignoreReferences)
932     {
933         // get all members of obj that are references and store them
934
Collection JavaDoc listRds = cld.getObjectReferenceDescriptors();
935         // return if nothing to do
936
if(listRds == null || listRds.size() == 0)
937         {
938             return;
939         }
940         Iterator JavaDoc i = listRds.iterator();
941         while (i.hasNext())
942         {
943             ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
944             /*
945             arminw: the super-references (used for table per subclass inheritance) must
946             be performed in any case. The "normal" 1:1 references can be ignored when
947             flag "ignoreReferences" is set
948             */

949             if((!ignoreReferences && rds.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE)
950                     || rds.isSuperReferenceDescriptor())
951             {
952                 storeAndLinkOneToOne(false, obj, cld, rds, insert);
953             }
954         }
955     }
956
957     /**
958      * Store/Link 1:1 reference.
959      *
960      * @param obj real object the reference starts
961      * @param rds {@link ObjectReferenceDescriptor} of the real object
962      * @param insert flag for insert operation
963      */

964     private void storeAndLinkOneToOne(boolean onlyLink, Object JavaDoc obj, ClassDescriptor cld,
965                                       ObjectReferenceDescriptor rds, boolean insert)
966     {
967         Object JavaDoc ref = rds.getPersistentField().get(obj);
968         if (!onlyLink && rds.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
969         {
970             if(rds.isSuperReferenceDescriptor())
971             {
972                 ClassDescriptor superCld = rds.getClassDescriptor().getSuperClassDescriptor();
973                 Identity oid = serviceIdentity().buildIdentity(superCld, ref);
974                 storeToDb(ref, superCld, oid, insert);
975             }
976             else store(ref);
977         }
978         link(obj, cld, rds, ref, insert);
979     }
980
981     /**
982      * Store/Link collections of objects poiting to <b>obj</b>.
983      * More info please see comments in source.
984      *
985      * @param obj real object which we will store collections for
986      * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
987      */

988     private void storeCollections(Object JavaDoc obj, ClassDescriptor cld, boolean insert) throws PersistenceBrokerException
989     {
990         // get all members of obj that are collections and store all their elements
991
Collection JavaDoc listCods = cld.getCollectionDescriptors();
992         // return if nothing to do
993
if (listCods.size() == 0)
994         {
995             return;
996         }
997         Iterator JavaDoc i = listCods.iterator();
998         while (i.hasNext())
999         {
1000            CollectionDescriptor cod = (CollectionDescriptor) i.next();
1001
1002            // if CASCADE_NONE was set, do nothing with referenced objects
1003
if (cod.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE)
1004            {
1005                Object JavaDoc referencedObjects = cod.getPersistentField().get(obj);
1006                if (cod.isMtoNRelation())
1007                {
1008                    storeAndLinkMtoN(false, obj, cod, referencedObjects, insert);
1009                }
1010                else
1011                {
1012                    storeAndLinkOneToMany(false, obj, cod, referencedObjects, insert);
1013                }
1014
1015                // BRJ: only when auto-update = object (CASCADE_OBJECT)
1016
//
1017
if ((cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
1018                        && (referencedObjects instanceof ManageableCollection))
1019                {
1020                    ((ManageableCollection) referencedObjects).afterStore(this);
1021                }
1022            }
1023        }
1024    }
1025
1026    /**
1027     * Store/Link m:n collection references.
1028     *
1029     * @param obj real object the reference starts
1030     * @param cod {@link CollectionDescriptor} of the real object
1031     * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
1032     * @param insert flag for insert operation
1033     */

1034    private void storeAndLinkMtoN(boolean onlyLink, Object JavaDoc obj, CollectionDescriptor cod,
1035                                  Object JavaDoc referencedObjects, boolean insert)
1036    {
1037        /*
1038        - if the collection is a collectionproxy and it's not already loaded
1039        no need to perform an update on the referenced objects
1040        - on insert we link and insert the referenced objects, because the proxy
1041        collection maybe "inherited" from the object before the PK was replaced
1042        */

1043        if(insert || !(referencedObjects instanceof CollectionProxy
1044                        && !((CollectionProxy) referencedObjects).isLoaded()))
1045        {
1046            // if referenced objects are null, assign empty list
1047
if(referencedObjects == null)
1048            {
1049                referencedObjects = Collections.EMPTY_LIST;
1050            }
1051            /*
1052            NOTE: Take care of referenced objects, they could be of type Collection or
1053            an Array or of type ManageableCollection, thus it is not guaranteed that we
1054            can cast to Collection!!!
1055
1056            if we store an object with m:n reference and no references could be
1057            found, we remove all entires of given object in indirection table
1058            */

1059            Iterator JavaDoc referencedObjectsIterator;
1060
1061            if(!onlyLink && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
1062            {
1063                referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
1064                while (referencedObjectsIterator.hasNext())
1065                {
1066                    store(referencedObjectsIterator.next());
1067                }
1068            }
1069
1070            Collection JavaDoc existingMtoNKeys;
1071            if(!insert)
1072            {
1073                existingMtoNKeys = mtoNBroker.getMtoNImplementor(cod, obj);
1074                // we can't reuse iterator
1075
referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
1076                // remove all entries in indirection table which not be part of referenced objects
1077
mtoNBroker.deleteMtoNImplementor(cod, obj, referencedObjectsIterator, existingMtoNKeys);
1078            }
1079            else
1080            {
1081                existingMtoNKeys = Collections.EMPTY_LIST;
1082            }
1083            // we can't reuse iterator
1084
referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
1085            while (referencedObjectsIterator.hasNext())
1086            {
1087                Object JavaDoc refObj = referencedObjectsIterator.next();
1088                // Now store indirection record
1089
// BRJ: this could cause integrity problems because
1090
// obj may not be stored depending on auto-update
1091
mtoNBroker.storeMtoNImplementor(cod, obj, refObj, existingMtoNKeys);
1092            }
1093        }
1094    }
1095
1096    /**
1097     * Store/Link 1:n collection references.
1098     *
1099     * @param obj real object the reference starts
1100     * @param linkOnly if true the referenced objects will only be linked (FK set, no reference store).
1101     * Reference store setting in descriptor will be ignored in this case
1102     * @param cod {@link CollectionDescriptor} of the real object
1103     * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
1104     * @param insert flag for insert operation
1105     */

1106    private void storeAndLinkOneToMany(boolean linkOnly, Object JavaDoc obj, CollectionDescriptor cod,
1107                                       Object JavaDoc referencedObjects, boolean insert)
1108    {
1109        if(referencedObjects == null)
1110        {
1111            return;
1112        }
1113        /*
1114        Only make sense to perform (link or/and store) real referenced objects
1115        or materialized collection proxy objects, because on unmaterialized collection
1116        nothing has changed.
1117
1118        - if the collection is a collectionproxy and it's not already loaded
1119        no need to perform an update on the referenced objects
1120        - on insert we link and insert the referenced objects, because the proxy
1121        collection maybe "inherited" from the object before the PK was replaced
1122        */

1123        if(insert || !(referencedObjects instanceof CollectionProxyDefaultImpl
1124                        && !((CollectionProxyDefaultImpl) referencedObjects).isLoaded()))
1125        {
1126            Iterator JavaDoc it = BrokerHelper.getCollectionIterator(referencedObjects);
1127            Object JavaDoc refObj;
1128            while(it.hasNext())
1129            {
1130                refObj = it.next();
1131                /*
1132                TODO: Check this!
1133                arminw:
1134                When it's necessary to 'link' (set the FK) the 1:n reference objects?
1135                1. set FK in refObj if it is materialized
1136                2. if the referenced object is a proxy AND the main object needs insert
1137                we have to materialize the real object, because the user may move a collection
1138                of proxy objects from object A to new object B. In this case we have to replace the
1139                FK in the proxy object with new key of object B.
1140                */

1141                if(insert || getProxyFactory().isMaterialized(refObj))
1142                {
1143                    ClassDescriptor refCld = getClassDescriptor(getProxyFactory().getRealClass(refObj));
1144                    // get the real object before linking
1145
refObj = getProxyFactory().getRealObject(refObj);
1146                    link(refObj, refCld, cod, obj, insert);
1147                    // if enabled cascade store and not only link, store the refObj
1148
if(!linkOnly && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
1149                    {
1150                        store(refObj);
1151                    }
1152                }
1153            }
1154        }
1155    }
1156
1157    /**
1158     * Assign FK value to target object by reading PK values of referenced object.
1159     *
1160     * @param targetObject real (non-proxy) target object
1161     * @param cld {@link ClassDescriptor} of the real target object
1162     * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1163     * associated with the real object.
1164     * @param referencedObject referenced object or proxy
1165     * @param insert Show if "linking" is done while insert or update.
1166     */

1167    public void link(Object JavaDoc targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds, Object JavaDoc referencedObject, boolean insert)
1168    {
1169        // MBAIRD: we have 'disassociated' this object from the referenced object,
1170
// the object represented by the reference descriptor is now null, so set
1171
// the fk in the target object to null.
1172
// arminw: if an insert was done and ref object was null, we should allow
1173
// to pass FK fields of main object (maybe only the FK fields are set)
1174
if (referencedObject == null)
1175        {
1176            /*
1177            arminw:
1178            if update we set FK fields to 'null', because reference was disassociated
1179            We do nothing on insert, maybe only the FK fields of main object (without
1180            materialization of the reference object) are set by the user
1181            */

1182            if(!insert)
1183            {
1184                unlinkFK(targetObject, cld, rds);
1185            }
1186        }
1187        else
1188        {
1189            setFKField(targetObject, cld, rds, referencedObject);
1190        }
1191    }
1192
1193    /**
1194     * Unkink FK fields of target object.
1195     *
1196     * @param targetObject real (non-proxy) target object
1197     * @param cld {@link ClassDescriptor} of the real target object
1198     * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1199     * associated with the real object.
1200     */

1201    public void unlinkFK(Object JavaDoc targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds)
1202    {
1203        setFKField(targetObject, cld, rds, null);
1204    }
1205
1206    /**
1207     * Set the FK value on the target object, extracted from the referenced object. If the referenced object was
1208     * <i>null</i> the FK values were set to <i>null</i>, expect when the FK field was declared as PK.
1209     *
1210     * @param targetObject real (non-proxy) target object
1211     * @param cld {@link ClassDescriptor} of the real target object
1212     * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1213     * @param referencedObject The referenced object or <i>null</i>
1214     */

1215    private void setFKField(Object JavaDoc targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds, Object JavaDoc referencedObject)
1216    {
1217        ValueContainer[] refPkValues;
1218        FieldDescriptor fld;
1219        FieldDescriptor[] objFkFields = rds.getForeignKeyFieldDescriptors(cld);
1220        if (objFkFields == null)
1221        {
1222            throw new PersistenceBrokerException("No foreign key fields defined for class '"+cld.getClassNameOfObject()+"'");
1223        }
1224        if(referencedObject == null)
1225        {
1226            refPkValues = null;
1227        }
1228        else
1229        {
1230            Class JavaDoc refClass = proxyFactory.getRealClass(referencedObject);
1231            ClassDescriptor refCld = getClassDescriptor(refClass);
1232            refPkValues = brokerHelper.getKeyValues(refCld, referencedObject, false);
1233        }
1234        for (int i = 0; i < objFkFields.length; i++)
1235        {
1236            fld = objFkFields[i];
1237            /*
1238            arminw:
1239            we set the FK value when the extracted PK fields from the referenced object are not null at all
1240            or if null, the FK field was not a PK field of target object too.
1241            Should be ok, because the values of the extracted PK field values should never be null and never
1242            change, so it doesn't matter if the target field is a PK too.
1243            */

1244            if(refPkValues != null || !fld.isPrimaryKey())
1245            {
1246                fld.getPersistentField().set(targetObject, refPkValues != null ? refPkValues[i].getValue(): null);
1247            }
1248        }
1249    }
1250
1251    /**
1252     * Assign FK value of main object with PK values of the reference object.
1253     *
1254     * @param obj real object with reference (proxy) object (or real object with set FK values on insert)
1255     * @param cld {@link ClassDescriptor} of the real object
1256     * @param rds An {@link ObjectReferenceDescriptor} of real object.
1257     * @param insert Show if "linking" is done while insert or update.
1258     */

1259    public void linkOneToOne(Object JavaDoc obj, ClassDescriptor cld, ObjectReferenceDescriptor rds, boolean insert)
1260    {
1261        storeAndLinkOneToOne(true, obj, cld, rds, true);
1262    }
1263
1264    /**
1265     * Assign FK value to all n-side objects referenced by given object.
1266     *
1267     * @param obj real object with 1:n reference
1268     * @param cod {@link CollectionDescriptor} of referenced 1:n objects
1269     * @param insert flag signal insert operation, false signals update operation
1270     */

1271    public void linkOneToMany(Object JavaDoc obj, CollectionDescriptor cod, boolean insert)
1272    {
1273        Object JavaDoc referencedObjects = cod.getPersistentField().get(obj);
1274        storeAndLinkOneToMany(true, obj, cod,referencedObjects, insert);
1275    }
1276
1277    /**
1278     * Assign FK values and store entries in indirection table
1279     * for all objects referenced by given object.
1280     *
1281     * @param obj real object with 1:n reference
1282     * @param cod {@link CollectionDescriptor} of referenced 1:n objects
1283     * @param insert flag signal insert operation, false signals update operation
1284     */

1285    public void linkMtoN(Object JavaDoc obj, CollectionDescriptor cod, boolean insert)
1286    {
1287        Object JavaDoc referencedObjects = cod.getPersistentField().get(obj);
1288        storeAndLinkMtoN(true, obj, cod, referencedObjects, insert);
1289    }
1290
1291    public void unlinkXtoN(Object JavaDoc obj, CollectionDescriptor col)
1292    {
1293        if(col.isMtoNRelation())
1294        {
1295            // if this is a m:n mapped table, remove entries from indirection table
1296
mtoNBroker.deleteMtoNImplementor(col, obj);
1297        }
1298        else
1299        {
1300            Object JavaDoc collectionObject = col.getPersistentField().get(obj);
1301            if (collectionObject != null)
1302            {
1303                Iterator JavaDoc colIterator = BrokerHelper.getCollectionIterator(collectionObject);
1304                ClassDescriptor cld = null;
1305                while (colIterator.hasNext())
1306                {
1307                    Object JavaDoc target = colIterator.next();
1308                    if(cld == null) cld = getClassDescriptor(getProxyFactory().getRealClass(target));
1309                    unlinkFK(target, cld, col);
1310                }
1311            }
1312        }
1313    }
1314
1315    /**
1316     * Retrieve all References (also Collection-attributes) of a given instance.
1317     * Loading is forced, even if the collection- and reference-descriptors differ.
1318     * @param pInstance the persistent instance to work with
1319     */

1320    public void retrieveAllReferences(Object JavaDoc pInstance) throws PersistenceBrokerException
1321    {
1322        if (logger.isDebugEnabled())
1323        {
1324            logger.debug("Manually retrieving all references for object " + serviceIdentity().buildIdentity(pInstance));
1325        }
1326        ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
1327        getInternalCache().enableMaterializationCache();
1328        // to avoid problems with circular references, locally cache the current object instance
1329
Identity oid = serviceIdentity().buildIdentity(pInstance);
1330// boolean needLocalRemove = false;
1331
if(getInternalCache().doLocalLookup(oid) == null)
1332        {
1333            getInternalCache().doInternalCache(oid, pInstance, MaterializationCache.TYPE_TEMP);
1334// needLocalRemove = true;
1335
}
1336        try
1337        {
1338            referencesBroker.retrieveReferences(pInstance, cld, true);
1339            referencesBroker.retrieveCollections(pInstance, cld, true);
1340// arminw: should no longer needed since we use TYPE_TEMP for this kind of objects
1341
// // do locally remove the object to avoid problems with object state detection (insert/update),
1342
// // because objects found in the cache detected as 'old' means 'update'
1343
// if(needLocalRemove) getInternalCache().doLocalRemove(oid);
1344
getInternalCache().disableMaterializationCache();
1345        }
1346        catch(RuntimeException JavaDoc e)
1347        {
1348            getInternalCache().doLocalClear();
1349            throw e;
1350        }
1351    }
1352
1353    /**
1354     * retrieve a single reference- or collection attribute
1355     * of a persistent instance.
1356     * @param pInstance the persistent instance
1357     * @param pAttributeName the name of the Attribute to load
1358     */

1359    public void retrieveReference(Object JavaDoc pInstance, String JavaDoc pAttributeName) throws PersistenceBrokerException
1360    {
1361        if (logger.isDebugEnabled())
1362        {
1363            logger.debug("Retrieving reference named ["+pAttributeName+"] on object of type ["+
1364                        pInstance.getClass().getName()+"]");
1365        }
1366        ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
1367        CollectionDescriptor cod = cld.getCollectionDescriptorByName(pAttributeName);
1368        getInternalCache().enableMaterializationCache();
1369        // to avoid problems with circular references, locally cache the current object instance
1370
Identity oid = serviceIdentity().buildIdentity(pInstance);
1371        boolean needLocalRemove = false;
1372        if(getInternalCache().doLocalLookup(oid) == null)
1373        {
1374            getInternalCache().doInternalCache(oid, pInstance, MaterializationCache.TYPE_TEMP);
1375            needLocalRemove = true;
1376        }
1377        try
1378        {
1379            if (cod != null)
1380            {
1381                referencesBroker.retrieveCollection(pInstance, cld, cod, true);
1382            }
1383            else
1384            {
1385                ObjectReferenceDescriptor ord = cld.getObjectReferenceDescriptorByName(pAttributeName);
1386                if (ord != null)
1387                {
1388                    referencesBroker.retrieveReference(pInstance, cld, ord, true);
1389                }
1390                else
1391                {
1392                    throw new PersistenceBrokerException("did not find attribute " + pAttributeName +
1393                            " for class " + pInstance.getClass().getName());
1394                }
1395            }
1396            // do locally remove the object to avoid problems with object state detection (insert/update),
1397
// because objects found in the cache detected as 'old' means 'update'
1398
if(needLocalRemove) getInternalCache().doLocalRemove(oid);
1399            getInternalCache().disableMaterializationCache();
1400        }
1401        catch(RuntimeException JavaDoc e)
1402        {
1403            getInternalCache().doLocalClear();
1404            throw e;
1405        }
1406    }
1407
1408    /**
1409     * Check if the references of the specified object have enabled
1410     * the <em>refresh</em> attribute and refresh the reference if set <em>true</em>.
1411     *
1412     * @throws PersistenceBrokerException if there is a error refreshing collections or references
1413     * @param obj The object to check.
1414     * @param oid The {@link Identity} of the object.
1415     * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
1416     */

1417    public void checkRefreshRelationships(Object JavaDoc obj, Identity oid, ClassDescriptor cld)
1418    {
1419        Iterator JavaDoc iter;
1420        CollectionDescriptor cds;
1421        ObjectReferenceDescriptor rds;
1422        // to avoid problems with circular references, locally cache the current object instance
1423
Object JavaDoc tmp = getInternalCache().doLocalLookup(oid);
1424        if(tmp != null && getInternalCache().isEnabledMaterialisationCache())
1425        {
1426            /*
1427            arminw: This should fix OJB-29, infinite loops on bidirectional 1:1 relations with
1428            refresh attribute 'true' for both references. OJB now assume that the object is already
1429            refreshed when it's cached in the materialisation cache
1430            */

1431            return;
1432        }
1433        try
1434        {
1435            getInternalCache().enableMaterializationCache();
1436            if(tmp == null)
1437            {
1438                getInternalCache().doInternalCache(oid, obj, MaterializationCache.TYPE_TEMP);
1439            }
1440            if(logger.isDebugEnabled()) logger.debug("Refresh relationships for " + oid);
1441            iter = cld.getCollectionDescriptors().iterator();
1442            while (iter.hasNext())
1443            {
1444                cds = (CollectionDescriptor) iter.next();
1445                if (cds.isRefresh())
1446                {
1447                    referencesBroker.retrieveCollection(obj, cld, cds, false);
1448                }
1449            }
1450            iter = cld.getObjectReferenceDescriptors().iterator();
1451            while (iter.hasNext())
1452            {
1453                rds = (ObjectReferenceDescriptor) iter.next();
1454                if (rds.isRefresh())
1455                {
1456                    referencesBroker.retrieveReference(obj, cld, rds, false);
1457                }
1458            }
1459            getInternalCache().disableMaterializationCache();
1460        }
1461        catch(RuntimeException JavaDoc e)
1462        {
1463            getInternalCache().doLocalClear();
1464            throw e;
1465        }
1466    }
1467
1468    /**
1469     * retrieve a collection of type collectionClass matching the Query query
1470     *
1471     * @see org.apache.ojb.broker.PersistenceBroker#getCollectionByQuery(Class, Query)
1472     */

1473    public ManageableCollection getCollectionByQuery(Class JavaDoc collectionClass, Query query)
1474            throws PersistenceBrokerException
1475    {
1476        return referencesBroker.getCollectionByQuery(collectionClass, query, false);
1477    }
1478
1479    /**
1480     * retrieve a collection of itemClass Objects matching the Query query
1481     */

1482    public Collection JavaDoc getCollectionByQuery(Query query) throws PersistenceBrokerException
1483    {
1484        return referencesBroker.getCollectionByQuery(query, false);
1485    }
1486
1487    /**
1488     * Retrieve an plain object (without populated references) by it's identity
1489     * from the database
1490     *
1491     * @param cld the real {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object to refresh
1492     * @param oid the {@link org.apache.ojb.broker.Identity} of the object
1493     * @return A new plain object read from the database or <em>null</em> if not found
1494     * @throws ClassNotPersistenceCapableException
1495     */

1496    private Object JavaDoc getPlainDBObject(ClassDescriptor cld, Identity oid) throws ClassNotPersistenceCapableException
1497    {
1498        Object JavaDoc newObj = null;
1499
1500        // Class is NOT an Interface: it has a directly mapped table and we lookup this table first:
1501
if (!cld.isInterface())
1502        {
1503            // 1. try to retrieve skalar fields from directly mapped table columns
1504
newObj = dbAccess.materializeObject(cld, oid);
1505        }
1506
1507        // if we did not find the object yet AND if the cld represents an Extent,
1508
// we can lookup all tables of the extent classes:
1509
if (newObj == null && cld.isExtent())
1510        {
1511            Iterator JavaDoc extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
1512
1513            while (extents.hasNext())
1514            {
1515                ClassDescriptor extCld = (ClassDescriptor) extents.next();
1516
1517                newObj = dbAccess.materializeObject(extCld, oid);
1518                if (newObj != null)
1519                {
1520                    break;
1521                }
1522            }
1523        }
1524        return newObj;
1525    }
1526    
1527    
1528    /**
1529     * Retrieve an full materialized (dependent on the metadata settings)
1530     * object by it's identity from the database, as well as caching the
1531     * object
1532     *
1533     * @param oid The {@link org.apache.ojb.broker.Identity} of the object to for
1534     * @return A new object read from the database or <em>null</em> if not found
1535     * @throws ClassNotPersistenceCapableException
1536     */

1537    private Object JavaDoc getDBObject(Identity oid) throws ClassNotPersistenceCapableException
1538    {
1539        Class JavaDoc c = oid.getObjectsRealClass();
1540
1541        if (c == null)
1542        {
1543            logger.info("Real class for used Identity object is 'null', use top-level class instead");
1544            c = oid.getObjectsTopLevelClass();
1545        }
1546
1547        ClassDescriptor cld = getClassDescriptor(c);
1548        Object JavaDoc newObj = getPlainDBObject(cld, oid);
1549
1550        // loading references is useful only when the Object could be found in db:
1551
if (newObj != null)
1552        {
1553            if (oid.getObjectsRealClass() == null)
1554            {
1555                oid.setObjectsRealClass(newObj.getClass());
1556            }
1557
1558            /*
1559             * synchronize on newObj so the ODMG-layer can take a snapshot only of
1560             * fully cached (i.e. with all references + collections) objects
1561             */

1562            synchronized (newObj)
1563            {
1564                objectCache.enableMaterializationCache();
1565                try
1566                {
1567                    // cache object immediately , so that references
1568
// can be established from referenced Objects back to this Object
1569
objectCache.doInternalCache(oid, newObj, ObjectCacheInternal.TYPE_NEW_MATERIALIZED);
1570
1571                    /*
1572                     * Chris Lewington: can cause problems with multiple objects
1573                     * mapped to one table, as follows:
1574                     *
1575                     * if the class searched on does not match the retrieved
1576                     * class, eg a search on an OID retrieves a row but it could
1577                     * be a different class (OJB gets all column values),
1578                     * then trying to resolve references will fail as the object
1579                     * will not match the Class Descriptor.
1580                     *
1581                     * To be safe, get the descriptor of the retrieved object
1582                     * BEFORE resolving refs
1583                     */

1584                    ClassDescriptor newObjCld = getClassDescriptor(newObj.getClass());
1585                    // don't force loading of references:
1586
final boolean unforced = false;
1587
1588                    // 2. retrieve non-skalar fields that contain objects retrievable from other tables
1589
referencesBroker.retrieveReferences(newObj, newObjCld, unforced);
1590                    // 3. retrieve collection fields from foreign-key related tables:
1591
referencesBroker.retrieveCollections(newObj, newObjCld, unforced);
1592                    objectCache.disableMaterializationCache();
1593                }
1594                catch(RuntimeException JavaDoc e)
1595                {
1596                    objectCache.doLocalClear();
1597                    throw e;
1598                }
1599            }
1600        }
1601
1602        return newObj;
1603    }
1604
1605    /**
1606     * returns an Iterator that iterates Objects of class c if calling the .next()
1607     * method. The Elements returned come from a SELECT ... WHERE Statement
1608     * that is defined by the Query query.
1609     * If itemProxy is null, no proxies are used.
1610     */

1611    public Iterator JavaDoc getIteratorByQuery(Query query) throws PersistenceBrokerException
1612    {
1613        Class JavaDoc itemClass = query.getSearchClass();
1614        ClassDescriptor cld = getClassDescriptor(itemClass);
1615        return getIteratorFromQuery(query, cld);
1616    }
1617
1618    /**
1619     * Get an extent aware Iterator based on the Query
1620     *
1621     * @param query
1622     * @param cld the ClassDescriptor
1623     * @return OJBIterator
1624     */

1625    protected OJBIterator getIteratorFromQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
1626    {
1627        RsIteratorFactory factory = RsIteratorFactoryImpl.getInstance();
1628        OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
1629
1630        if (query.usePaging())
1631        {
1632            result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex());
1633        }
1634        return result;
1635    }
1636
1637    public Object JavaDoc getObjectByIdentity(Identity id) throws PersistenceBrokerException
1638    {
1639        objectCache.enableMaterializationCache();
1640        Object JavaDoc result = null;
1641        try
1642        {
1643            result = doGetObjectByIdentity(id);
1644            objectCache.disableMaterializationCache();
1645        }
1646        catch(RuntimeException JavaDoc e)
1647        {
1648            // catch runtime exc. to guarantee clearing of internal buffer on failure
1649
objectCache.doLocalClear();
1650            throw e;
1651        }
1652        return result;
1653    }
1654
1655    /**
1656     * Internal used method to retrieve object based on Identity.
1657     *
1658     * @param id
1659     * @return
1660     * @throws PersistenceBrokerException
1661     */

1662    public Object JavaDoc doGetObjectByIdentity(Identity id) throws PersistenceBrokerException
1663    {
1664        if (logger.isDebugEnabled()) logger.debug("getObjectByIdentity " + id);
1665
1666        // check if object is present in ObjectCache:
1667
Object JavaDoc obj = objectCache.lookup(id);
1668        // only perform a db lookup if necessary (object not cached yet)
1669
if (obj == null)
1670        {
1671            obj = getDBObject(id);
1672        }
1673        else
1674        {
1675            ClassDescriptor cld = getClassDescriptor(obj.getClass());
1676            // if specified in the ClassDescriptor the instance must be refreshed
1677
if (cld.isAlwaysRefresh())
1678            {
1679                refreshInstance(obj, id, cld);
1680            }
1681            // now refresh all references
1682
checkRefreshRelationships(obj, id, cld);
1683        }
1684
1685        // Invoke events on PersistenceBrokerAware instances and listeners
1686
AFTER_LOOKUP_EVENT.setTarget(obj);
1687        fireBrokerEvent(AFTER_LOOKUP_EVENT);
1688        AFTER_LOOKUP_EVENT.setTarget(null);
1689
1690        //logger.info("RETRIEVING object " + obj);
1691
return obj;
1692    }
1693
1694    /**
1695     * refresh all primitive typed attributes of a cached instance
1696     * with the current values from the database.
1697     * refreshing of reference and collection attributes is not done
1698     * here.
1699     * @param cachedInstance the cached instance to be refreshed
1700     * @param oid the Identity of the cached instance
1701     * @param cld the ClassDescriptor of cachedInstance
1702     */

1703    private void refreshInstance(Object JavaDoc cachedInstance, Identity oid, ClassDescriptor cld)
1704    {
1705        // read in fresh copy from the db, but do not cache it
1706
Object JavaDoc freshInstance = getPlainDBObject(cld, oid);
1707
1708        // update all primitive typed attributes
1709
FieldDescriptor[] fields = cld.getFieldDescriptions();
1710        FieldDescriptor fmd;
1711        PersistentField fld;
1712        for (int i = 0; i < fields.length; i++)
1713        {
1714            fmd = fields[i];
1715            fld = fmd.getPersistentField();
1716            fld.set(cachedInstance, fld.get(freshInstance));
1717        }
1718    }
1719
1720    /**
1721     * retrieve an Object by query
1722     * I.e perform a SELECT ... FROM ... WHERE ... in an RDBMS
1723     */

1724    public Object JavaDoc getObjectByQuery(Query query) throws PersistenceBrokerException
1725    {
1726        Object JavaDoc result = null;
1727        if (query instanceof QueryByIdentity)
1728        {
1729            // example obj may be an entity or an Identity
1730
Object JavaDoc obj = query.getExampleObject();
1731            if (obj instanceof Identity)
1732            {
1733                Identity oid = (Identity) obj;
1734                result = getObjectByIdentity(oid);
1735            }
1736            else
1737            {
1738                // TODO: This workaround doesn't allow 'null' for PK fields
1739
if (!serviceBrokerHelper().hasNullPKField(getClassDescriptor(obj.getClass()), obj))
1740                {
1741                    Identity oid = serviceIdentity().buildIdentity(obj);
1742                    result = getObjectByIdentity(oid);
1743                }
1744            }
1745        }
1746        else
1747        {
1748            Class JavaDoc itemClass = query.getSearchClass();
1749            ClassDescriptor cld = getClassDescriptor(itemClass);
1750            /*
1751            use OJB intern Iterator, thus we are able to close used
1752            resources instantly
1753            */

1754            OJBIterator it = getIteratorFromQuery(query, cld);
1755            /*
1756            arminw:
1757            patch by Andre Clute, instead of taking the first found result
1758            try to get the first found none null result.
1759            He wrote:
1760            I have a situation where an item with a certain criteria is in my
1761            database twice -- once deleted, and then a non-deleted version of it.
1762            When I do a PB.getObjectByQuery(), the RsIterator get's both results
1763            from the database, but the first row is the deleted row, so my RowReader
1764            filters it out, and do not get the right result.
1765            */

1766            try
1767            {
1768                while (result==null && it.hasNext())
1769                {
1770                    result = it.next();
1771                }
1772            } // make sure that we close the used resources
1773
finally
1774            {
1775                if(it != null) it.releaseDbResources();
1776            }
1777        }
1778        return result;
1779    }
1780
1781    /**
1782     * returns an Enumeration of PrimaryKey Objects for objects of class DataClass.
1783     * The Elements returned come from a SELECT ... WHERE Statement
1784     * that is defined by the fields and their coresponding values of listFields
1785     * and listValues.
1786     * Useful for EJB Finder Methods...
1787     * @param primaryKeyClass the pk class for the searched objects
1788     * @param query the query
1789     */

1790    public Enumeration JavaDoc getPKEnumerationByQuery(Class JavaDoc primaryKeyClass, Query query) throws PersistenceBrokerException
1791    {
1792        if (logger.isDebugEnabled()) logger.debug("getPKEnumerationByQuery " + query);
1793
1794        query.setFetchSize(1);
1795        ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
1796        return new PkEnumeration(query, cld, primaryKeyClass, this);
1797    }
1798
1799    /**
1800     * Makes object obj persistent in the underlying persistence system.
1801     * E.G. by INSERT INTO ... or UPDATE ... in an RDBMS.
1802     * The ObjectModification parameter can be used to determine whether INSERT or update is to be used.
1803     * This functionality is typically called from transaction managers, that
1804     * track which objects have to be stored. If the object is an unmaterialized
1805     * proxy the method return immediately.
1806     */

1807    public void store(Object JavaDoc obj, ObjectModification mod) throws PersistenceBrokerException
1808    {
1809        obj = extractObjectToStore(obj);
1810        // null for unmaterialized Proxy
1811
if (obj == null)
1812        {
1813            return;
1814        }
1815
1816        ClassDescriptor cld = getClassDescriptor(obj.getClass());
1817        // this call ensures that all autoincremented primary key attributes are filled
1818
Identity oid = serviceIdentity().buildIdentity(cld, obj);
1819        // select flag for insert / update selection by checking the ObjectModification
1820
if (mod.needsInsert())
1821        {
1822            store(obj, oid, cld, true);
1823        }
1824        else if (mod.needsUpdate())
1825        {
1826            store(obj, oid, cld, false);
1827        }
1828        /*
1829        arminw
1830        TODO: Why we need this behaviour? What about 1:1 relations?
1831        */

1832        else
1833        {
1834            // just store 1:n and m:n associations
1835
storeCollections(obj, cld, mod.needsInsert());
1836        }
1837    }
1838
1839    /**
1840     * I pulled this out of internal store so that when doing multiple table
1841     * inheritance, i can recurse this function.
1842     *
1843     * @param obj
1844     * @param cld
1845     * @param oid BRJ: what is it good for ???
1846     * @param insert
1847     * @param ignoreReferences
1848     */

1849    private void storeToDb(Object JavaDoc obj, ClassDescriptor cld, Identity oid, boolean insert, boolean ignoreReferences)
1850    {
1851        // 1. link and store 1:1 references
1852
storeReferences(obj, cld, insert, ignoreReferences);
1853
1854        Object JavaDoc[] pkValues = oid.getPrimaryKeyValues();
1855        if (!serviceBrokerHelper().assertValidPksForStore(cld.getPkFields(), pkValues))
1856        {
1857            // BRJ: fk values may be part of pk, but the are not known during
1858
// creation of Identity. so we have to get them here
1859
pkValues = serviceBrokerHelper().getKeyValues(cld, obj);
1860            if (!serviceBrokerHelper().assertValidPksForStore(cld.getPkFields(), pkValues))
1861            {
1862                String JavaDoc append = insert ? " on insert" : " on update" ;
1863                throw new PersistenceBrokerException("assertValidPkFields failed for Object of type: " + cld.getClassNameOfObject() + append);
1864            }
1865        }
1866
1867        // get super class cld then store it with the object
1868
/*
1869        now for multiple table inheritance
1870        1. store super classes, topmost parent first
1871        2. go down through heirarchy until current class
1872        3. todo: store to full extent?
1873
1874// arminw: TODO: The extend-attribute feature dosn't work, should we remove this stuff?
1875        This if-clause will go up the inheritance heirarchy to store all the super classes.
1876        The id for the top most super class will be the id for all the subclasses too
1877         */

1878        if(cld.getSuperClass() != null)
1879        {
1880
1881            ClassDescriptor superCld = getDescriptorRepository().getDescriptorFor(cld.getSuperClass());
1882            storeToDb(obj, superCld, oid, insert);
1883            // arminw: why this?? I comment out this section
1884
// storeCollections(obj, cld.getCollectionDescriptors(), insert);
1885
}
1886
1887        // 2. store primitive typed attributes (Or is THIS step 3 ?)
1888
// if obj not present in db use INSERT
1889
if (insert)
1890        {
1891            dbAccess.executeInsert(cld, obj);
1892            if(oid.isTransient())
1893            {
1894                // Create a new Identity based on the current set of primary key values.
1895
oid = serviceIdentity().buildIdentity(cld, obj);
1896            }
1897        }
1898        // else use UPDATE
1899
else
1900        {
1901            try
1902            {
1903                dbAccess.executeUpdate(cld, obj);
1904            }
1905            catch(OptimisticLockException e)
1906            {
1907                // ensure that the outdated object be removed from cache
1908
objectCache.remove(oid);
1909                throw e;
1910            }
1911        }
1912        // cache object for symmetry with getObjectByXXX()
1913
// Add the object to the cache.
1914
objectCache.doInternalCache(oid, obj, ObjectCacheInternal.TYPE_WRITE);
1915        // 3. store 1:n and m:n associations
1916
if(!ignoreReferences) storeCollections(obj, cld, insert);
1917    }
1918
1919    /**
1920     * I pulled this out of internal store so that when doing multiple table
1921     * inheritance, i can recurse this function.
1922     *
1923     * @param obj
1924     * @param cld
1925     * @param oid BRJ: what is it good for ???
1926     * @param insert
1927     */

1928    private void storeToDb(Object JavaDoc obj, ClassDescriptor cld, Identity oid, boolean insert)
1929    {
1930        storeToDb(obj, cld, oid, insert, false);
1931    }
1932
1933    /**
1934     * returns true if the broker is currently running a transaction.
1935     * @return boolean
1936     */

1937    public boolean isInTransaction()
1938    {
1939        // return this.connectionManager.isInLocalTransaction();
1940
return inTransaction;
1941    }
1942
1943    public void setInTransaction(boolean inTransaction)
1944    {
1945        this.inTransaction = inTransaction;
1946    }
1947
1948    /**
1949     * @see org.apache.ojb.broker.PersistenceBroker#removeFromCache
1950     */

1951    public void removeFromCache(Object JavaDoc objectOrIdentity) throws PersistenceBrokerException
1952    {
1953        Identity identity;
1954        if (objectOrIdentity instanceof Identity)
1955        {
1956            identity = (Identity)objectOrIdentity;
1957        }
1958        else
1959        {
1960            identity = serviceIdentity().buildIdentity(objectOrIdentity);
1961        }
1962        objectCache.remove(identity);
1963    }
1964
1965    /**
1966     * returns a ClassDescriptor for the persistence capable class clazz.
1967     * throws a PersistenceBrokerException if clazz is not persistence capable,
1968     * i.e. if clazz is not defined in the DescriptorRepository.
1969     */

1970    public ClassDescriptor getClassDescriptor(Class JavaDoc clazz) throws PersistenceBrokerException
1971    {
1972        return descriptorRepository.getDescriptorFor(clazz);
1973    }
1974
1975    public boolean hasClassDescriptor(Class JavaDoc clazz)
1976    {
1977        return descriptorRepository.hasDescriptorFor(clazz);
1978    }
1979
1980    /**
1981     * clears the brokers internal cache.
1982     * removing is recursive. That is referenced Objects are also
1983     * removed from the cache, if the auto-retrieve flag is set
1984     * for obj.getClass() in the metadata repository.
1985     *
1986     */

1987    public void clearCache() throws PersistenceBrokerException
1988    {
1989        objectCache.clear();
1990    }
1991
1992    /**
1993     * @see org.apache.ojb.broker.PersistenceBroker#getTopLevelClass
1994     */

1995    public Class JavaDoc getTopLevelClass(Class JavaDoc clazz) throws PersistenceBrokerException
1996    {
1997        try
1998        {
1999            return descriptorRepository.getTopLevelClass(clazz);
2000        }
2001        catch (ClassNotPersistenceCapableException e)
2002        {
2003            throw new PersistenceBrokerException(e);
2004        }
2005    }
2006
2007    /**
2008     * @see org.apache.ojb.broker.PersistenceBroker#getCount(Query)
2009     */

2010    public int getCount(Query query) throws PersistenceBrokerException
2011    {
2012        Query countQuery = serviceBrokerHelper().getCountQuery(query);
2013        Iterator JavaDoc iter;
2014        int result = 0;
2015
2016        if (logger.isDebugEnabled()) logger.debug("getCount " + countQuery.getSearchClass() + ", " + countQuery);
2017
2018        iter = getReportQueryIteratorByQuery(countQuery);
2019        try
2020        {
2021            while (iter.hasNext())
2022            {
2023                Object JavaDoc[] row = (Object JavaDoc[]) iter.next();
2024                result += ((Number JavaDoc) row[0]).intValue();
2025            }
2026        }
2027        finally
2028        {
2029            if (iter instanceof OJBIterator)
2030            {
2031                ((OJBIterator) iter).releaseDbResources();
2032            }
2033        }
2034
2035        return result;
2036    }
2037
2038    /**
2039     * Get an Iterator based on the ReportQuery
2040     *
2041     * @param query
2042     * @return Iterator
2043     */

2044    public Iterator JavaDoc getReportQueryIteratorByQuery(Query query) throws PersistenceBrokerException
2045    {
2046        ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
2047        return getReportQueryIteratorFromQuery(query, cld);
2048    }
2049
2050    /**
2051     * Get an extent aware RsIterator based on the Query
2052     *
2053     * @param query
2054     * @param cld
2055     * @param factory the Factory for the RsIterator
2056     * @return OJBIterator
2057     */

2058    private OJBIterator getRsIteratorFromQuery(Query query, ClassDescriptor cld, RsIteratorFactory factory)
2059        throws PersistenceBrokerException
2060    {
2061        query.setFetchSize(1);
2062        if (query instanceof QueryBySQL)
2063        {
2064            if(logger.isDebugEnabled()) logger.debug("Creating SQL-RsIterator for class ["+cld.getClassNameOfObject()+"]");
2065            return factory.createRsIterator((QueryBySQL) query, cld, this);
2066        }
2067
2068        if (!cld.isExtent() || !query.getWithExtents())
2069        {
2070            // no extents just use the plain vanilla RsIterator
2071
if(logger.isDebugEnabled()) logger.debug("Creating RsIterator for class ["+cld.getClassNameOfObject()+"]");
2072
2073            return factory.createRsIterator(query, cld, this);
2074        }
2075
2076        if(logger.isDebugEnabled()) logger.debug("Creating ChainingIterator for class ["+cld.getClassNameOfObject()+"]");
2077
2078        ChainingIterator chainingIter = new ChainingIterator();
2079
2080        // BRJ: add base class iterator
2081
if (!cld.isInterface())
2082        {
2083            if(logger.isDebugEnabled()) logger.debug("Adding RsIterator for class ["+cld.getClassNameOfObject()+"] to ChainingIterator");
2084
2085            chainingIter.addIterator(factory.createRsIterator(query, cld, this));
2086        }
2087
2088        Iterator JavaDoc extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
2089        while (extents.hasNext())
2090        {
2091            ClassDescriptor extCld = (ClassDescriptor) extents.next();
2092
2093            // read same table only once
2094
if (chainingIter.containsIteratorForTable(extCld.getFullTableName()))
2095            {
2096                if(logger.isDebugEnabled()) logger.debug("Skipping class ["+extCld.getClassNameOfObject()+"]");
2097            }
2098            else
2099            {
2100                if(logger.isDebugEnabled()) logger.debug("Adding RsIterator of class ["+extCld.getClassNameOfObject()+"] to ChainingIterator");
2101
2102                // add the iterator to the chaining iterator.
2103
chainingIter.addIterator(factory.createRsIterator(query, extCld, this));
2104            }
2105        }
2106
2107        return chainingIter;
2108    }
2109
2110    /**
2111     * Get an extent aware Iterator based on the ReportQuery
2112     *
2113     * @param query
2114     * @param cld
2115     * @return OJBIterator
2116     */

2117    private OJBIterator getReportQueryIteratorFromQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
2118    {
2119        RsIteratorFactory factory = ReportRsIteratorFactoryImpl.getInstance();
2120        OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
2121
2122        if (query.usePaging())
2123        {
2124            result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex());
2125        }
2126
2127        return result;
2128    }
2129
2130    /**
2131     * @see org.odbms.ObjectContainer#query()
2132     */

2133    public org.odbms.Query query()
2134    {
2135        return new org.apache.ojb.soda.QueryImpl(this);
2136    }
2137
2138    /**
2139     * @return DescriptorRepository
2140     */

2141    public DescriptorRepository getDescriptorRepository()
2142    {
2143        return descriptorRepository;
2144    }
2145
2146    protected void finalize()
2147    {
2148        if (!isClosed)
2149        {
2150            close();
2151        }
2152    }
2153
2154    /**
2155     * clean up the maps for reuse by the next transaction.
2156     */

2157    private void clearRegistrationLists()
2158    {
2159        nowStoring.clear();
2160        objectCache.doLocalClear();
2161        deletedDuringTransaction.clear();
2162        /*
2163        arminw:
2164        for better performance I don't register MtoNBroker as listner,
2165        so use this method to reset on commit/rollback
2166        */

2167        mtoNBroker.reset();
2168    }
2169    
2170
2171
2172    /**
2173     * @see org.apache.ojb.broker.PersistenceBroker#deleteMtoNImplementor
2174     */

2175    public void deleteMtoNImplementor(MtoNImplementor m2nImpl) throws PersistenceBrokerException
2176    {
2177        mtoNBroker.deleteMtoNImplementor(m2nImpl);
2178    }
2179
2180    /**
2181     * @see org.apache.ojb.broker.PersistenceBroker#addMtoNImplementor
2182     */

2183    public void addMtoNImplementor(MtoNImplementor m2n) throws PersistenceBrokerException
2184    {
2185        mtoNBroker.storeMtoNImplementor(m2n);
2186    }
2187
2188    public ProxyFactory getProxyFactory() {
2189        return proxyFactory;
2190    }
2191    
2192    /**
2193     * Creates a proxy instance.
2194     *
2195     * @param baseClassForProxy The base class that the Proxy should extend. For dynamic Proxies, the method of
2196     * generation is dependent on the ProxyFactory implementation.
2197     * @param realSubjectsIdentity The identity of the subject
2198     * @return An instance of the proxy subclass
2199     * @throws PersistenceBrokerException If there is an error creating the proxy object
2200     */

2201    public Object JavaDoc createProxy(Class JavaDoc baseClassForProxy, Identity realSubjectsIdentity)
2202    {
2203        try
2204        {
2205            // the invocation handler manages all delegation stuff
2206
IndirectionHandler handler = getProxyFactory().createIndirectionHandler(pbKey, realSubjectsIdentity);
2207
2208            // the proxy simply provides the interface of the real subject
2209
if (VirtualProxy.class.isAssignableFrom(baseClassForProxy))
2210            {
2211                Constructor JavaDoc constructor = baseClassForProxy.getDeclaredConstructor(new Class JavaDoc[]{ IndirectionHandler.class });
2212                return constructor.newInstance(new Object JavaDoc[]{ handler });
2213            }
2214            else
2215            {
2216                return getProxyFactory().createProxy(baseClassForProxy,handler);
2217            }
2218
2219            
2220        }
2221        catch (Exception JavaDoc ex)
2222        {
2223            throw new PersistenceBrokerException("Unable to create proxy using class:"+baseClassForProxy.getName(), ex);
2224        }
2225    }
2226    
2227    
2228}
2229
Popular Tags