a b wrote:
> Hi all, insert seem to work. However, whenever I try
> to edit an entry I get an exception as follows, can
> anyone help me with this? thanks a lot
> 
> java.util.ConcurrentModificationException
>       at
> java.util.AbstractList$Itr.checkForComodification(AbstractList.java:445)
>       at
> java.util.AbstractList$Itr.next(AbstractList.java:418)
>       at
> org.apache.ojb.odmg.ObjectEnvelopeTable.commit(ObjectEnvelopeTable.java:120)
>       at
> org.apache.ojb.odmg.TransactionImpl.doCommit(TransactionImpl.java:285)
>       at
> org.apache.ojb.odmg.TransactionImpl.commit(TransactionImpl.java:469)
>       at
> org.apache.ojb.mytest.UCEditLogin.apply(UCEditProduct.java:60)
>       at
> org.apache.ojb.mytest.Application.run(Application.java:133)
>       at
> org.apache.ojb.mytest.Application.main(Application.java:89)
> [org.apache.ojb.odmg.TransactionImpl] WARN: Abort
> transaction was called on tx
> org.apache.ojb.odmg.TransactionImpl@2a15cd, associated
> PB was null
> null

Hi,

I had the same Error and I found out that the PersistenceBroker 
Implementation dont uses collections in a thrad safe way.
I wrote a little patch and it solved the problem in my case, but I can 
not garantee that it will help in yours.
Anyway it i'll attach my patch to this mail in case it is usefull.

Regards
- Cornelius Keller

package org.apache.ojb.broker.singlevm;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache ObjectRelationalBridge" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache ObjectRelationalBridge", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
import java.lang.reflect.Array;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import org.apache.ojb.broker.*;
import org.apache.ojb.broker.ta.PersistenceBrokerFactoryIF;
import org.apache.ojb.broker.ta.PersistenceBrokerFactoryFactory;
import org.apache.ojb.broker.accesslayer.*;
import org.apache.ojb.broker.cache.ObjectCache;
import org.apache.ojb.broker.cache.ObjectCacheFactory;
import org.apache.ojb.broker.metadata.*;
import org.apache.ojb.broker.platforms.Platform;
import org.apache.ojb.broker.platforms.PlatformFactory;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryByCriteria;
import org.apache.ojb.broker.query.QueryByExample;
import org.apache.ojb.broker.query.QueryBySQL;
import org.apache.ojb.broker.query.QueryFactory;
import org.apache.ojb.broker.server.PersistenceBrokerClient;
import org.apache.ojb.broker.server.ServerEntry;
import org.apache.ojb.broker.server.ServerPool;
import org.apache.ojb.broker.util.ArrayIterator;
import org.apache.ojb.broker.util.IdentityHashMap;
import org.apache.ojb.broker.util.ObjectModification;
import org.apache.ojb.broker.util.collections.ManageableArrayList;
import org.apache.ojb.broker.util.collections.ManageableVector;
import org.apache.ojb.broker.util.configuration.Configuration;
import org.apache.ojb.broker.util.configuration.ConfigurationException;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.broker.util.sequence.SequenceManager;
import org.apache.ojb.broker.util.sequence.SequenceManagerFactory;
import org.apache.commons.beanutils.BeanUtils;


import java.lang.reflect.Proxy;


/**
 * The PersistenceBrokerImpl is an implementation of the PersistenceBroker
 * Interface that specifies a persistence mechanism for Java objects.
 * This Concrete implementation provides an object relational mapping
 * and allows to store and retrieve arbitrary objects in/from relational
 * databases accessed by JDBC.
 *
 * @see test.org.apache.ojb.tutorial1.Application for a sample application
 * @author <a href="mailto:[EMAIL PROTECTED]";>Thomas Mahler<a>
 */
public class PersistenceBrokerImpl implements PersistenceBroker, PBState {
    private Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * m_DbAccess is used to do all Jdbc related work: connecting, executing...
     */
    
    protected JdbcAccess dbAccess;
    /**
     * m_SqlGen is a simple SQL Generator that produces all SQL Queries
     */
    
    private SqlGenerator sqlGenerator = SqlGenerator.getInstance();
    /**
     * m_ObjectCache caches object already loaded from db and protects the uniqueness
     * of objects. When an object is loaded from the db it is stored in the cache.
     * Any later lookups with the same pk values will return the cached object
     * instead of performing another db lookup.
     */
    
    private ObjectCache objectCache = ObjectCacheFactory.getObjectCache();
    /**
     * holds mapping information for all classes to be treated by PersistenceBroker
     */
    
    protected DescriptorRepository descriptorRepository = null;
    protected ConnectionManagerIF connectionManager = null;
    protected SequenceManager sequenceManager = null;
    protected StatementManagerIF statementManager = null;
    protected PersistenceBrokerFactoryIF pbf;
    
    private ServerPool pool = null;
    private boolean runningInServerMode;
    protected PBKey pbKey;
    
    /**
     * signs if this broker was closed
     */
    private boolean isClosed;
    
    /**
     * this flag tells if the Broker is currently processing a Transaction
     */
    protected boolean inTransaction = false;
    
    protected PersistenceBrokerImpl() {
    }
    
    /**
     *
     */
    public PersistenceBrokerImpl(PBKey key, PersistenceBrokerFactoryIF pbf) {
        this.descriptorRepository = DescriptorRepository.getInstance(key);
        this.pbf = pbf;
        this.pbKey = key;
        connectionManager = new ConnectionManager(this);
        dbAccess = new JdbcAccess(this);
        sequenceManager = SequenceManagerFactory.getSequenceManager(this);
        statementManager = new StatementManagerExt(this);
    }
    
    /**
     * @deprecated
     */
    public PersistenceBrokerImpl(DescriptorRepository repository) {
        descriptorRepository = repository;
        this.pbKey = repository.getPBkey();
        this.pbf = PersistenceBrokerFactoryFactory.instance();
        connectionManager = new ConnectionManager(this);
        dbAccess = new JdbcAccess(this);
        sequenceManager = SequenceManagerFactory.getSequenceManager(this);
        statementManager = new StatementManagerExt(this);
    }
    
    /**
     * Release all resources used by this
     * class - CAUTION: No further operations can be
     * done with this instance after calling this method.
     */
    public void releaseAllResources() {
        this.setClosed(true);
        this.inTransaction = false;
        this.descriptorRepository = null;
        this.pbKey = null;
        this.pbf = null;
        this.connectionManager = null;
        this.dbAccess = null;
        this.objectCache = null;
        this.pool = null;
        this.sequenceManager = null;
        this.sqlGenerator = null;
        this.statementManager = null;
    }
    
    public PBKey getPBKey() {
        return pbKey;
    }
    
    public void setPBKey(PBKey key) {
        this.pbKey = key;
    }
    
    public boolean isClosed() {
        return this.isClosed;
    }
    
    public void setClosed(boolean closed) {
        this.isClosed = closed;
    }
    
    /**
     * @see PersistenceBroker#close()
     */
    public boolean close() {
        if (logger.isDebugEnabled()) logger.debug("PB.close was called");
        this.connectionManager.releaseConnection();
        /*@todo while pbf.releaseInstance was public, we setClosed(true)
        in further versions this method should only called by the pool*/
        this.setClosed(true);
        try {
            this.pbf.releaseInstance(this);
        }
        catch (Throwable t) {
        }
        return true;
    }
    
    /**
     * Abort and close the transaction.
     * Calling abort abandons all persistent object modifications and releases the
     * associated locks.
     * If transaction is not in progress a TransactionNotInProgressException is thrown
     */
    public synchronized void abortTransaction()
    throws TransactionNotInProgressException {
        if (!inTransaction) {
            throw new TransactionNotInProgressException();
        }
        inTransaction = false;
        this.connectionManager.localRollback();
    }
    
    /**
     * begin a transaction against the underlying RDBMS.
     * Calling <code>beginTransaction</code> multiple times,
     * without an intervening call to <code>commitTransaction</code> or 
<code>abortTransaction</code>,
     * causes the exception <code>TransactionInProgressException</code> to be thrown
     * on the second and subsequent calls.
     */
    public synchronized void beginTransaction()
    throws TransactionInProgressException, TransactionAbortedException {
        if (inTransaction) {
            throw new TransactionInProgressException();
        }
        this.connectionManager.localBegin();
        inTransaction = true;
    }
    
    /**
     * Commit and close the transaction.
     * Calling <code>commit</code> commits to the database all
     * UPDATE, INSERT and DELETE statements called within the transaction and
     * releases any locks held by the transaction.
     * If beginTransaction() has not been called before a
     * TransactionNotInProgressException exception is thrown.
     * If the transaction cannot be commited a TransactionAbortedException exception 
is thrown.
     */
    public synchronized void commitTransaction()
    throws TransactionNotInProgressException, TransactionAbortedException {
        if (!inTransaction) {
            throw new TransactionNotInProgressException();
        }
        inTransaction = false;
        this.connectionManager.localCommit();
    }
    
    
    public StatementManagerIF getStatementManager() {
        return statementManager;
    }
    
    public ConnectionManagerIF getConnectionManager() {
        return connectionManager;
    }
    
    /**
     * deletes Object obj's representation in the underlying RDBMS
     */
    public void delete(Object obj) throws PersistenceBrokerException {
        delete(obj, new HashMap());
    }
    
    /**
     * Internal delete will not recurse infinitely.
     * @param obj
     * @param markedForDelete
     * @throws PersistenceBrokerException
     */
    private void delete(Object obj, Map markedForDelete)
    throws PersistenceBrokerException {
        //logger.info("DELETING " + obj);
        
        // only delete if object is not null
        if (obj != null) {
            
            // if obj is a proxy we must delete its real subject !
            if (obj instanceof VirtualProxy) {
                VirtualProxy proxy = (VirtualProxy) obj;
                obj = proxy.getRealSubject();
            }
            // same for dynamic Proxies
            else if (Proxy.isProxyClass(obj.getClass())) {
                IndirectionHandler handler =
                (IndirectionHandler) Proxy.getInvocationHandler(obj);
                obj = handler.getRealSubject();
            }
            
            /**
             * MBAIRD
             * 1. if we are marked for delete already, avoid recursing on this object
             */
            if (markedForDelete.containsKey(obj)) {
                return;
            }
            /**
             * MBAIRD
             * 2. register object in markedForDelete map.
             */
            markedForDelete.put(obj, obj);
            
            // invoke callback on PersistenceBrokerAware instances
            if (obj instanceof PersistenceBrokerAware) {
                ((PersistenceBrokerAware) obj).beforeDelete();
            }
            ClassDescriptor cld =
            descriptorRepository.getDescriptorFor(obj.getClass());
            // 1. delete dependend collections
            deleteCollections(
            obj,
            cld.getCollectionDescriptors(),
            markedForDelete);
            // 2. delete object from directly mapped table
            Identity oid = new Identity(obj, this);
            dbAccess.executeDelete(cld, obj); // use obj not oid to delete, BRJ
            // 3. delete dependend upon objects last to avoid FK violations
            deleteReferences(
            obj,
            cld.getObjectReferenceDescriptors(),
            markedForDelete);
            // remove obj from the object cache:
            objectCache.remove(oid);
            
            // invoke callback on PersistenceBrokerAware instances
            if (obj instanceof PersistenceBrokerAware) {
                ((PersistenceBrokerAware) obj).afterDelete();
            }
        }
    }
    
    /**
     * Deletes references that <b>obj</b> points to.
     * All objects which we have a FK poiting to (Via ReferenceDescriptors) will be 
deleted if auto-delete is true <b>AND</b>
     * the member field containing the object reference if NOT null.
     *
     * @param obj Object which we will delete references for
     * @param vecRds vector of ObjectRederenceDescriptors
     * @throws PersistenceBrokerException if some goes wrong - please see the error 
message for details
     */
    private void deleteReferences(
    Object obj,
    Vector vecRds,
    Map markedForDelete)
    throws PersistenceBrokerException {
        // get all members of obj that are references and delete them
        synchronized (vecRds) {
            Iterator i = vecRds.iterator();
            while (i.hasNext()) {
                ObjectReferenceDescriptor rds =
                (ObjectReferenceDescriptor) i.next();
                if (rds.getCascadeDelete()) {
                    Object referencedObject = rds.getPersistentField().get(obj);
                    if (referencedObject != null) {
                        //logger.info("DELETING refence for "+obj);
                        //logger.info("DELETING refence 
"+rds.getItemClass()+"="+referencedObject);
                        
                        delete(referencedObject, markedForDelete);
                    }
                }
            }
        }
    }
    
    /**
     * Deletes collections of objects poiting to <b>obj</b>.
     * All object which have a FK poiting to this object (Via CollectionDescriptors) 
will be deleted if auto-delete is true <b>AND</b>
     * the member field containing the object reference if NOT null.
     *
     * @param obj Object which we will delete collections for
     * @param vecRds vector of ObjectRederenceDescriptors
     * @throws PersistenceBrokerException if some goes wrong - please see the error 
message for details
     */
    private void deleteCollections(
    Object obj,
    Vector vecCds,
    Map markedForDelete)
    throws PersistenceBrokerException {
        // get all members of obj that are collections and
        // delete all their elements
        synchronized (vecCds) {
            Iterator i = vecCds.iterator();
            
            while (i.hasNext()) {
                CollectionDescriptor cds = (CollectionDescriptor) i.next();
                // if this is a m:n mapped table, remove entries from association table
                if (cds.isMtoNRelation()) {
                    deleteMtoNImplementor(cds, obj);
                }
                
            /*
             * if cascading delete is on, delete referenced items
             * NOTE : this only works if auto-retrieve is true for this particular 
collection
             */
                if (cds.getCascadeDelete()) {
                    try {
                        Object col = cds.getPersistentField().get(obj);
                        if (col != null) {
                            //logger.info("DELETING collection for "+obj);
                            //logger.info("DELETING collection 
"+cds.getItemClass()+"="+col);
                            synchronized (col) {
                                Iterator colIterator;
                                if (col instanceof ManageableCollection) {
                                    colIterator =
                                    ((ManageableCollection) col).ojbIterator();
                                }
                                else if (col instanceof Collection) {
                                    colIterator = ((Collection) col).iterator();
                                }
                                else if (col.getClass().isArray()) {
                                    colIterator = new ArrayIterator(col);
                                }
                                else {
                                    throw new RuntimeException(
                                    col.getClass()
                                    + " can not be managed by OJB, use Array, 
Collection or ManageableCollection instead !");
                                }
                                while (colIterator.hasNext()) {
                                    delete(colIterator.next(), markedForDelete);
                                }
                            }
                        }
                    }
                    catch (Exception ex) {
                        logger.error(ex);
                        throw new PersistenceBrokerException(ex);
                    }
                }
            }
        }
    }
    
    /**
     * Method declaration
     *
     *
     * @param obj
     *
     * @throws PersistenceBrokerException
     *
     *
     */
    public void store(Object obj) throws PersistenceBrokerException {
        store(obj, new HashMap());
    }
    
    private void store(Object obj, Map markedForStore) {
        //logger.info("STORING " + obj);
        // only do something if obj != null
        if (obj != null) {
            // ProxyObjects only have to be updated if their real subjects have been 
loaded
            if (obj instanceof VirtualProxy) {
                VirtualProxy proxy = (VirtualProxy) obj;
                if (proxy.alreadyMaterialized()) {
                    obj = proxy.getRealSubject();
                }
                else {
                    return;
                }
            }
            else if (Proxy.isProxyClass(obj.getClass())) {
                IndirectionHandler handler =
                (IndirectionHandler) Proxy.getInvocationHandler(obj);
                if (handler.alreadyMaterialized()) {
                    obj = handler.getRealSubject();
                }
                else {
                    return;
                }
            }
            ClassDescriptor cld =
            descriptorRepository.getDescriptorFor(obj.getClass());
            Identity oid = new Identity(obj,this);
            // check if update or insert is needed
            boolean doInsert = false;
            // lookup cache or db to see whether object needs insert or update
            if (dbAccess.materializeObject(cld, oid) == null) {
                doInsert = true;
                // now store it:
            }
            store(obj, doInsert, markedForStore);
        }
        // if Object == null do nothing
        else
            return;
    }
    
    /**
     * Store all object references that <b>obj</b> points to.
     * All objects which we have a FK pointing to (Via ReferenceDescriptors) will be 
stored if auto-update is true <b>AND</b>
     * the member field containing the object reference is NOT null.
     *
     * @param obj Object which we will store references for
     * @param vecRds - Vector with all ObjectReferenceDescriptors that should be stored
     */
    private void storeReferences(Object obj, Vector vecRds, Map markedForStore) {
        // get all members of obj that are references and store them
        synchronized (vecRds) {
            Iterator i = vecRds.iterator();
            
            while (i.hasNext()) {
                ObjectReferenceDescriptor rds =
                (ObjectReferenceDescriptor) i.next();
                Object ref = rds.getPersistentField().get(obj);
                if (rds.getCascadeStore()) {
                    //logger.info("STORING reference for "+obj);
                    //logger.info("STORING reference "+rds.getItemClass()+"="+ref);
                    
                    store(ref, markedForStore);
                }
            }
        }
    }
    
    private void assertFkAssignment(
    Object obj,
    Object ref,
    ObjectReferenceDescriptor rds) {
        /**
         * MBAIRD
         * we have 'disassociated' this object from the referenced object, the object 
representing the ord is now null,
         * so set the fk to null.
         *
         * Note: I can't believe this wasn't tested (as of June9/2002), attaching and 
removing objects seems to be a
         * pretty important piece of functionality.
         *
         */
        if (ref == null) {
            ClassDescriptor cld =
            descriptorRepository.getDescriptorFor(obj.getClass());
            FieldDescriptor f[] = rds.getForeignKeyFieldDescriptors(cld);
            if (f != null) {
                for (int i = 0; i < f.length; i++) {
                    f[i].getPersistentField().set(obj, null);
                }
            }
            
        }
        
        if ((!(obj instanceof VirtualProxy))
        && (!(obj instanceof Proxy))
        && (ref != null)
        && (!(ref instanceof VirtualProxy))
        && (!(ref instanceof Proxy))) {
            ClassDescriptor refCld =
            descriptorRepository.getDescriptorFor(ref.getClass());
            Object[] refPkValues = refCld.getKeyValues(ref);
            ClassDescriptor objCld =
            descriptorRepository.getDescriptorFor(obj.getClass());
            FieldDescriptor[] objFkFields =
            rds.getForeignKeyFieldDescriptors(objCld);
            if (objFkFields != null) {
                FieldDescriptor fld = null;
                for (int i = 0; i < objFkFields.length; i++) {
                    fld = objFkFields[i];
                    fld.getPersistentField().set(obj, refPkValues[i]);
                }
            }
        }
    }
    
    /**
     * Store collections of objects poiting to <b>obj</b>.
     * All object which have a FK poiting to this object (Via CollectionDescriptors) 
will be stored if auto-update is true <b>AND</b>
     * the member field containing the object reference if NOT null.
     *
     * @param obj Object which we will store collections for
     * @param vecRds vector of ObjectRederenceDescriptors
     * @throws PersistenceBrokerException if some goes wrong - please see the error 
message for details
     */
    private void storeCollections(
    Object obj,
    Vector vecCds,
    Map markedForStore)
    throws PersistenceBrokerException {
        // get all members of obj that are collections and store all their elements
        synchronized (vecCds) {
            Iterator i = vecCds.iterator();
            while (i.hasNext()) {
                CollectionDescriptor cds = (CollectionDescriptor) i.next();
                Object col = cds.getPersistentField().get(obj);
                
                if (cds.isMtoNRelation()) {
                /* LEANDRO
                 * Clear all MtoN implementors.
                 * All new MtoN implementors are going to be added bellow
                 */
                    //logger.info("STORE COLLECTIONS: deleting all MtoN implementors 
for field "+cds.getPersistentField().getName());
                    deleteMtoNImplementor(cds, obj);
                }
                
                if (col != null) {
                    // MBAIRD
                    // if the collection is a collectionproxy and it's not already 
loaded
                    // no need to store it.
                    if (col instanceof CollectionProxy && 
!((CollectionProxy)col).isLoaded()) {
                        continue;
                    }
                    
                    synchronized (col) {
                        Iterator colIterator;
                        if (col instanceof ManageableCollection) {
                            colIterator = ((ManageableCollection) col).ojbIterator();
                        }
                        else if (col instanceof Collection) {
                            colIterator = ((Collection) col).iterator();
                        }
                        else if (col.getClass().isArray()) {
                            colIterator = new ArrayIterator(col);
                        }
                        else {
                            throw new RuntimeException(
                            col.getClass()
                            + " can not be managed by OJB, use Array, Collection or 
ManageableCollection instead !");
                        }
                        while (colIterator.hasNext()) {
                            Object otherObj = colIterator.next();
                            // for m:n mapped collections store association 
implementing entries
                            if (cds.isMtoNRelation()) {
                                // 1. Store depended upon object first to avoid FK 
violation
                                storeCollectionObject(cds, otherObj, markedForStore);
                                // 2. Store indirection record
                                storeMtoNImplementor(cds, obj, otherObj);
                            }
                            // for 1:n mapped collection assert proper fk assignment
                            else {
                                assertFkAssignment(otherObj, obj, cds);
                                storeCollectionObject(cds, otherObj, markedForStore);
                            }
                        }
                    }
                }
            }
        }
    }
    
    /**
     * Store an object which is a part of a collection of another object (Via 
CollectionDescriptors)
     * <b>otherObject</b> will be stored if auto-update is true
     *
     * @param otherObject Object which is contained into other's object collection
     * @param cds CollectionDescriptor providing metadata about the collection in 
which <b>otherObject</b> is part of
     * @throws PersistenceBrokerException if some goes wrong - please see the error 
message for details
     */
    private void storeCollectionObject(
    CollectionDescriptor cds,
    Object otherObject,
    Map markedForStore) {
        // if cascade store: store associated object
        if (cds.getCascadeStore()) {
            //logger.info("STORING collection object 
"+cds.getItemClass()+"="+otherObject);
            
            store(otherObject, markedForStore);
        }
    }
    
    /**
     * This method is used to store values of a M:N association in a indirection table.
     *
     */
    private void storeMtoNImplementor(
    CollectionDescriptor cod,
    Object obj,
    Object otherObj) {
        ClassDescriptor cld =
        getDescriptorRepository().getDescriptorFor(obj.getClass());
        Object[] pkValues = cld.getKeyValues(obj);
        Object[] pkColumns = cod.getFksToThisClass();
        ClassDescriptor otherCld =
        getDescriptorRepository().getDescriptorFor(
        otherObj.getClass());
        Object[] otherPkValues = otherCld.getKeyValues(otherObj);
        Object[] otherPkColumns = cod.getFksToItemClass();
        Object[] values = new Object[pkValues.length + otherPkValues.length];
        System.arraycopy(pkValues, 0, values, 0, pkValues.length);
        System.arraycopy(
        otherPkValues,
        0,
        values,
        pkValues.length,
        otherPkValues.length);
        Object[] columns = new Object[pkColumns.length + otherPkColumns.length];
        System.arraycopy(pkColumns, 0, columns, 0, pkColumns.length);
        System.arraycopy(
        otherPkColumns,
        0,
        columns,
        pkColumns.length,
        otherPkColumns.length);
        String table = cod.getIndirectionTable();
        String insertStmt =
        sqlGenerator.getInsertStatement(table, columns, values);
        try {
            dbAccess.executeUpdateSQL(insertStmt, cld);
        }
        catch (PersistenceBrokerException e) {
            // attempts to store existing entries are ignored
        }
    }
    
    private void deleteMtoNImplementor(CollectionDescriptor cod, Object obj) {
        ClassDescriptor cld =
        getDescriptorRepository().getDescriptorFor(obj.getClass());
        Object[] pkValues = cld.getKeyValues(obj);
        Object[] pkColumns = cod.getFksToThisClass();
        String table = cod.getIndirectionTable();
        String deleteStmt =
        sqlGenerator.getDeleteStatement(table, pkColumns, pkValues);
        try {
            dbAccess.executeUpdateSQL(deleteStmt, cld);
        }
        catch (PersistenceBrokerException e) {
            logger.debug(
            "// attempts to del existing entries are ignored:"
            + e.getMessage(),
            e);
            // attempts to del existing entries are ignored
        }
    }
    
    /**
     * Retrieve all References
     *
     *
     * @param newObj
     * @param cld
     *
     */
    public void retrieveReferences(Object newObj, ClassDescriptor cld)
    throws PersistenceBrokerException {
        synchronized (cld.getObjectReferenceDescriptors()) {
            Iterator i = cld.getObjectReferenceDescriptors().iterator();
            ObjectReferenceDescriptor rds = null;
            while (i.hasNext()) {
                rds = (ObjectReferenceDescriptor) i.next();
                retrieveReference(newObj, cld, rds);
            }
        }
    }
    
    /**
     * Retrieve a single Reference.
     * This implementation retrieves a referenced object from the data backend if 
<b>cascade-retrieve</b> is true.
     *
     *
     * @param obj - object that will have it's field set with a referenced object.
     * @param cld
     * @param rds
     */
    private void retrieveReference(
    Object obj,
    ClassDescriptor cld,
    ObjectReferenceDescriptor rds) {
        PersistentField refField;
        Object refObj;
        if (rds.getCascadeRetrieve()) {
            refObj = getReferencedObject(obj, rds, cld);
            refField = rds.getPersistentField();
            //logger.info("RETRIEVING reference for "+obj);
            //logger.info("RETRIEVING reference field "+refField.getName()+"="+refObj);
            
            refField.set(obj, refObj);
        }
    }
    
    /**
     * Retrieve a single Collection on behalf of <b>obj</b>.
     * The Collection is retrieved only if <b>cascade.retrieve is true</b>
     *
     *
     * @param obj
     * @param cld
     * @param cds
     *
     */
    private void retrieveCollection(
    Object obj,
    ClassDescriptor cld,
    CollectionDescriptor cds) {
        if (cds.getCascadeRetrieve()) {
            // this collection type will be used:
            Class collectionClass = cds.getCollectionClass();
            PersistentField collectionField = cds.getPersistentField();
            Query fkQuery = null;
            
            if (cds.isMtoNRelation()) {
                fkQuery = getMtoNQuery(obj, cld, cds);
            }
            else {
                fkQuery = getForeignKeyQuery(obj, cld, cds);
            }
            
            // check if collection must be ordered
            if (cds.getOrderBy() != null) {
                fkQuery.getCriteria().addOrderBy(
                cds.getOrderBy(),
                cds.isAscending());
            }
            if (collectionClass == null) {
                Collection result = getCollectionByQuery(fkQuery, cds.isLazy());
                
                //logger.info("RETRIEVING collection for "+obj);
                //logger.info("RETRIEVING collection field 
"+collectionField.getName()+"="+result);
                
                // assign collection to objects attribute
                // if attribute has an array type build an array, else assign 
collection directly
                if (collectionField.getType().isArray()) {
                    int length = result.size();
                    Class itemtype =
                    collectionField.getType().getComponentType();
                    Object resultArray = Array.newInstance(itemtype, length);
                    for (int j = 0; j < length; j++) {
                        Array.set(resultArray, j, ((Vector) result).get(j));
                    }
                    collectionField.set(obj, resultArray);
                }
                else {
                    collectionField.set(obj, result);
                }
            }
            else {
                ManageableCollection result =
                getCollectionByQuery(
                collectionClass,
                fkQuery,
                cds.isLazy());
                // assign collection to objects attribute
                collectionField.set(obj, result);
            }
        }
    }
    
    /**
     * Retrieve all Collections
     *
     * @param newObj
     * @param cld
     *
     */
    public void retrieveCollections(Object newObj, ClassDescriptor cld)
    throws PersistenceBrokerException {
        synchronized (cld.getCollectionDescriptors()) {
            Iterator i = cld.getCollectionDescriptors().iterator();
            CollectionDescriptor cds;
            while (i.hasNext()) {
                cds = (CollectionDescriptor) i.next();
                retrieveCollection(newObj, cld, cds);
            }
        }
    }
    
    /**
     * Refresh Relationships
     *
     * @throws PersistenceBrokerException if there is a error refreshing collections 
or references
     * @param obj
     * @param cld
     */
    public void refreshRelationships(Object obj, ClassDescriptor cld) {
        Iterator iter;
        CollectionDescriptor cds;
        ObjectReferenceDescriptor rds;
        synchronized (cld.getCollectionDescriptors()) {
            iter = cld.getCollectionDescriptors().iterator();
            while (iter.hasNext()) {
                cds = (CollectionDescriptor) iter.next();
                if (cds.isRefresh()) {
                    retrieveCollection(obj, cld, cds);
                }
            }
        }
        //
        //      Refresh References
        //
        synchronized (cld.getObjectReferenceDescriptors()) {
            iter = cld.getObjectReferenceDescriptors().iterator();
            while (iter.hasNext()) {
                rds = (ObjectReferenceDescriptor) iter.next();
                if (rds.isRefresh()) {
                    retrieveReference(obj, cld, rds);
                }
            }
        }
    }
    
    /**
     * retrieves an Object reference.
     * <br>
     * If there is a Proxy-class is defined in the ReferenceDescriptor or
     * if the ReferenceDescriptor is lazy, a Proxy-object is returned.
     * <br>
     * If no Proxy-class is defined, a getObjectByIdentity(...) lookup is performed.
     * <br>
     * Null is returned if all foreign keys are null
     */
    private Object getReferencedObject(
    Object obj,
    ObjectReferenceDescriptor rds,
    ClassDescriptor cld) {
        Class referencedClass = rds.getItemClass();
        // ensure that top-level extents are used for Identities
        referencedClass = descriptorRepository.getExtentClass(referencedClass);
        Object[] pkVals = rds.getForeignKeyValues(obj, cld);
        Identity id;
        Class referencedProxy;
        boolean allPkNull = true;
        
        //
        // BRJ : check if we have non null pk values
        //
        for (int i = 0; i < pkVals.length; i++) {
            if (pkVals[i] != null) {
                allPkNull = false;
                break;
            }
        }
        //
        // BRJ : if all pk values are null there's no referenced object
        //
        if (allPkNull) {
            return null;
        }
        
        id = new Identity(referencedClass, pkVals);
        if (rds.isLazy()) {
            referencedProxy =
            getClassDescriptor(referencedClass).getDynamicProxyClass();
        }
        else {
            referencedProxy = rds.getItemProxyClass();
        }
        
        if (referencedProxy != null) {
            try {
                return VirtualProxy.createProxy(referencedProxy, id);
            }
            catch (Exception e) {
                logger.error("Error instantiating obj: " + e.getMessage(), e);
                throw new PersistenceBrokerException(e);
            }
        }
        else {
            return getObjectByIdentity(id);
        }
    }
    
    
    
    
    
    /**
     * retrieve a collection of itemClass Objects matching the Query query
     * @param collectionClass type the collection to be returned
     * @param itemClass Class of item in collection
     * @param query the query
     */
    private ManageableCollection getCollectionByQuery(
    Class collectionClass,
    Class itemClass,
    Query query)
    throws ClassNotPersistenceCapableException, PersistenceBrokerException {
        logger.debug(
        "getCollectionByQuery ("
        + collectionClass
        + ", "
        + itemClass
        + ", "
        + query
        + ")");
        
        ClassDescriptor cld = descriptorRepository.getDescriptorFor(itemClass);
        ManageableCollection result = null;
        try {
            result = (ManageableCollection) collectionClass.newInstance();
            // now iterate over all elements and add them to the new collection
            
            OJBIterator i = getIteratorFromQuery(query, cld);
            /**
             * if the query has specified a start at index, and an end at index move 
to the start at index.
             */
            int size = i.size();
            /**
             * set full size of query to pass back to client.
             */
            query.fullSize(size);
            int startAt = query.getStartAtIndex();
            int endAt = query.getEndAtIndex();
            if ((startAt > 1) && (startAt < size)) {
                i.absolute(startAt);
            }
            int numberOfObjectsToFetch = endAt - startAt;
            int retrievedCount = 0;
            boolean hasProjectionAttribute = true;
            /**
             * keep retrieving objects while retrievedCount is less than the 
retrievalCount, or the
             * retrievalCount is -1 meaning get all results.
             * be sure not to go over the actual size of the iterator.
             */
            while (i.hasNext()
            && ((endAt == Query.NO_END_AT_INDEX)
            || (retrievedCount < numberOfObjectsToFetch))) {
                Object candidate = i.next();
                /**
                 * MBAIRD
                 * candidate CAN be null in the case of materializing from an iterator 
based
                 * on a query for a class that is mapped to a table that has other 
classes
                 * mapped to that table as well, but aren't extents.
                 */
                if (candidate != null) {
                    if ((itemClass.isAssignableFrom(candidate.getClass()))
                    || (Proxy.isProxyClass(candidate.getClass()))
                    || (candidate instanceof VirtualProxy)) {
                        boolean added = false;
                        if ((query.getRequestedAttribute() != null)
                        && (hasProjectionAttribute)) {
                            /**
                             * someone requested a nested attribute, say from an OQL 
statement like:
                             * select x.y.z.pkField from com.someclass, so we need to 
walk the path and
                             * get the attribute as requested.
                             */
                            int indexOfDot =
                            query.getRequestedAttribute().indexOf(".");
                            if (indexOfDot != -1) {
                                String attr =
                                query.getRequestedAttribute().substring(
                                indexOfDot + 1);
                                try {
                                    /**
                                     * this will fail if the attribute requested is 
invalid.
                                     */
                                    Object projectedAttribute =
                                    BeanUtils.getNestedProperty(
                                    candidate,
                                    attr);
                                    /**
                                     * MBAIRD:
                                     * When we are getting projection attributes, we 
need to use a collections
                                     * class that doesn't try to get an identity or 
lock the object, since there
                                     * is a chance it is not persistence capable.
                                     */
                                    if (!descriptorRepository
                                    .hasDescriptorFor(
                                    projectedAttribute.getClass())
                                    && (!(result
                                    instanceof ManageableArrayList))) {
                                        result = new ManageableArrayList();
                                    }
                                    result.ojbAdd(projectedAttribute);
                                    retrievedCount++;
                                    added = true;
                                }
                                catch (Exception e) {
                                    /**
                                     * probably an invalid projection attribute, so 
let's just ignore it.
                                     */
                                    hasProjectionAttribute = false;
                                }
                            }
                            else
                                hasProjectionAttribute = false;
                        }
                        if (!added) {
                            if (result == null)
                                result =
                                (ManageableCollection) collectionClass
                                .newInstance();
                            result.ojbAdd(candidate);
                            retrievedCount++;
                        }
                    }
                }
            }
        }
        catch (InstantiationException ex) {
            logger.error(ex);
            throw new PersistenceBrokerException(ex);
        }
        catch (IllegalAccessException ex) {
            logger.error(ex);
            throw new PersistenceBrokerException(ex);
        }
        return result;
    }
    
    /**
     * retrieve a collection of itemClass Objects matching the Query query
     * @param itemClass Class of item in collection
     * @param query the query
     */
    private Collection getCollectionFromQuery(Class itemClass, Query query)
    throws ClassNotPersistenceCapableException {
        logger.debug("getCollectionByQuery " + itemClass + ", " + query);
        
        ClassDescriptor cld = descriptorRepository.getDescriptorFor(itemClass);
        Vector res = new Vector();
        Iterator i = getIteratorFromQuery(query, cld);
        while (i.hasNext()) {
            res.add(i.next());
        }
        return res;
    }
    
    /**
     *
     * retrieve a collection of type collectionClass matching the Query query
     *
     */
    public ManageableCollection getCollectionByQuery(
    Class collectionClass,
    Query query)
    throws PersistenceBrokerException {
        return getCollectionByQuery(collectionClass, query, false);
    }
    
    public ManageableCollection getCollectionByQuery(Class collectionClass, Query 
query, boolean lazy)
    throws PersistenceBrokerException {
        Class itemClass = query.getSearchClass();
        ClassDescriptor cld = descriptorRepository.getDescriptorFor(itemClass);
        ManageableCollection result = null;
        String lastUsedTable = cld.getFullTableName();
        
        // if class is NOT an interface we can start searching its directly mapped 
table
        if (!cld.isInterface()) {
            if (lazy) {
                return new CollectionProxy(collectionClass, query);
            }
            else {
                result = getCollectionByQuery(collectionClass, itemClass, query);
            }
        }
        
        // if class is an extent, we have to search through all extent classes too
        if (cld.isExtent()) {
            List tmpList = new ArrayList();
            Vector extentClasses = cld.getExtentClasses();
            for (int i = 0; i < extentClasses.size(); i++) {
                Class ec = (Class) extentClasses.get(i);
                cld = getClassDescriptor(ec);
                
                /**
                 * BRJ
                 * query the same table only once.
                 * queries multiple classes maping to one table
                 * will have the ojbConcreteClass criteria set
                 * ie: ojbConcreteClass IN ('A','B').
                 * executing the query for each extent would return
                 * too many objects
                 */
                if(cld.getFullTableName().equals(lastUsedTable)) {
                    continue;
                }
                
                lastUsedTable = cld.getFullTableName();
                
                /**
                 * BRJ
                 * use getIteratorFromQuery and filter candidates only once
                 * when using getCollectionByQuery(...,concreteItemClass,...)
                 * legal candidates are filtered
                 */
                //              ManageableCollection tmp = 
getCollectionByQuery(collectionClass, concreteItemClass, query);
                //              Iterator iter = tmp.ojbIterator();
                Iterator iter = getIteratorFromQuery(query, cld);
                
                while (iter.hasNext()) {
                    IndirectionHandler handler = null;
                    Class candidateClass = null;
                    Object candidate = iter.next();
                    
                    // if candidate is a proxy, get candidateClass from
                    // the proxies IndirectionHandler.
                    if (candidate instanceof VirtualProxy) {
                        handler = VirtualProxy.getIndirectionHandler((VirtualProxy) 
candidate);
                    }
                    else if (candidate instanceof Proxy) {
                        handler = (IndirectionHandler) 
Proxy.getInvocationHandler(candidate);
                    }
                    if (handler != null) {
                        candidateClass = handler.getIdentity().getObjectsClass();
                    }
                    // if candidate is no proxy use getClass()
                    else {
                        candidateClass = candidate.getClass();
                    }
                    // only add candidates with matching class
                    if (itemClass.isAssignableFrom(candidateClass)) {
                        tmpList.add(candidate);
                    }
                }
            }
            
            if (result == null) {
                try {
                    result = (ManageableCollection) collectionClass.newInstance();
                }
                catch (Exception e) {
                    logger.error("Error instantiating obj: " + e.getMessage(), e);
                    throw new PersistenceBrokerException(e);
                }
            }
            
            if (tmpList.size() > 0) {
                Iterator iter = tmpList.iterator();
                while (iter.hasNext()) {
                    result.ojbAdd(iter.next());
                }
            }
        }
        
        return result;
    }
    
    
    /**
     *
     * retrieve a collection of itemClass Objects matching the Query query
     *
     */
    public Collection getCollectionByQuery(Query query)
    throws PersistenceBrokerException {
        return getCollectionByQuery(query, false);
    }
    
    /**
     *
     * retrieve a collection of itemClass Objects matching the Query query
     *
     */
    public Collection getCollectionByQuery(Query query, boolean lazy)
    throws PersistenceBrokerException {
        // thma: the following cast is safe because:
        // 1. ManageableVector implements Collection (will be returned if lazy == 
false)
        // 2. CollectionProxy implements Collection (will be returned if lazy == true)
        return (Collection) getCollectionByQuery(
        ManageableVector.class,
        query,
        lazy);
    }
    
    /**
     * Method declaration
     *
     * @param oid
     * @return
     * @throws ClassNotPersistenceCapableException
     */
    private Object getDBObject(Identity oid)
    throws ClassNotPersistenceCapableException {
        Class c = oid.getObjectsRealClass();
        
        if (c == null) {
            c = oid.getObjectsClass();
        }
        
        ClassDescriptor cld = descriptorRepository.getDescriptorFor(c);
        Object newObj = null;
        
        // Class is NOT an Interface: it has a directly mapped table and we lookup 
this table first:
        if (!cld.isInterface()) {
            // 1. try to retrieve skalar fields from directly mapped table columns
            newObj = dbAccess.materializeObject(cld, oid);
            
            if (newObj != null && oid.getObjectsRealClass() == null) {
                oid.setObjectsRealClass(newObj.getClass());
            }
            
        }
        // if we did not find the object yet AND if the cld represents an Extent,
        // we can lookup all tables of the extent classes:
        if (newObj == null && cld.isExtent()) {
            Vector extentClasses = cld.getExtentClasses();
            for (int i = 0; i < extentClasses.size(); i++) {
                Class ec = (Class) extentClasses.get(i);
                cld = descriptorRepository.getDescriptorFor(ec);
                // 1a. try to retrieve skalar fields from extent table columns
                newObj = dbAccess.materializeObject(cld, oid);
                if (newObj != null) {
                    if (oid.getObjectsRealClass() == null) {
                        oid.setObjectsRealClass(newObj.getClass());
                    }
                    break; // found object in one of the extent classes
                }
            }
        }
        // loading references is useful only when the Object could be found in db:
        if (newObj != null) {
            /*
             * synchronize on newObj so the ODMG-layer can take a snapshot only of
             * fully cached (i.e. with all references + collections) objects
             */
            synchronized(newObj) {
                // cache object immediately , so that references
                // can be established from referenced Objects back to this Object
                objectCache.cache(oid, newObj);
                // 2. retrieve non-skalar fields that contain objects retrievable from 
other tables
                retrieveReferences(newObj, cld);
                // 3. retrieve collection fields from foreign-key related tables:
                retrieveCollections(newObj, cld);
            }
        }
        return newObj;
    }
    
    /**
     * returns an Iterator that iterates Objects of class c if calling the .next()
     * method. The Elements returned come from a SELECT ... WHERE Statement
     * that is defined by the Query query.
     * If itemProxy is null, no proxies are used.
     */
    public Iterator getIteratorByQuery(Query query)
    throws PersistenceBrokerException {
        Class itemClass = query.getSearchClass();
        ClassDescriptor cld = descriptorRepository.getDescriptorFor(itemClass);
        return getIteratorFromQuery(query, cld);
    }
    
    /**
     * Get an Iterator based on the Query
     *
     * @param query
     * @param cld the ClassDescriptor
     *
     * @return Iterator
     */
    private OJBIterator getIteratorFromQuery(Query query, ClassDescriptor cld)
    throws PersistenceBrokerException {
        logger.debug(
        "getIteratorFromQuery " + query.getSearchClass() + ", " + query);
        
        OJBIterator iter = null;
        if (query instanceof QueryBySQL) {
            iter =
            new SqlBasedRsIterator(
            cld,
            ((QueryBySQL) query).getSql(),
            this);
        }
        else {
            iter = new RsIterator(query, cld, this);
        }
        return iter;
    }
    
    /**
     *
     * retrieve an instance of Class c from the underlying persistence system
     *
     * by using a sample object for providing primary key information.
     *
     */
    private Object getObjectByExample(Object exampleObj)
    throws PersistenceBrokerException {
        Identity oid = new Identity(exampleObj);
        return getObjectByIdentity(oid);
    }
    
    /**
     * Method declaration
     *
     * @param id
     * @return
     * @throws PersistenceBrokerException
     */
    public Object getObjectByIdentity(Identity id)
    throws PersistenceBrokerException {
        logger.debug("getObjectByIdentity " + id);
        
        // check if object is present in ObjectCache:
        Object obj = objectCache.lookup(id);
        // only perform a db lookup if necessary (object not cached yet)
        if (obj == null) {
            obj = getDBObject(id);
        }
        else {
            ClassDescriptor cld =
            descriptorRepository.getDescriptorFor(obj.getClass());
            refreshRelationships(obj, cld);
        }
        
        // invoke callback on PersistenceBrokerAware instances
        if (obj instanceof PersistenceBrokerAware) {
            ((PersistenceBrokerAware) obj).afterLookup();
        }
        
        //logger.info("RETRIEVING object " + obj);
        return obj;
    }
    
    /**
     * retrieve an Object by query
     * I.e perform a SELECT ... FROM ... WHERE ...  in an RDBMS
     */
    public Object getObjectByQuery(Query query)
    throws PersistenceBrokerException {
        if (query instanceof QueryByExample) {
            Object obj = query.getExampleObject();
            if (obj instanceof Identity) {
                return getObjectByIdentity((Identity) obj);
            }
            else {
                return getObjectByExample(obj);
            }
        }
        else {
            Vector coll = (Vector) getCollectionByQuery(query);
            if ((coll == null) || (coll.size() == 0)) {
                return null;
            }
            else {
                return coll.get(0);
            }
        }
    }
    
    /**
     * returns an Enumeration of PrimaryKey Objects for objects of class DataClass.
     * The Elements returned come from a SELECT ... WHERE Statement
     * that is defined by the fields and their coresponding values of vecFields
     * and vecValues.
     * Useful for EJB Finder Methods...
     * @param primaryKeyClass the pk class for the searched objects
     * @param query the query
     */
    public Enumeration getPKEnumerationByQuery(
    Class primaryKeyClass,
    Query query)
    throws PersistenceBrokerException {
        logger.debug("getPKEnumerationByQuery " + query);
        
        ClassDescriptor cld =
        descriptorRepository.getDescriptorFor(query.getSearchClass());
        return new PkEnumeration(query, cld, primaryKeyClass, this);
    }
    
    /**
     * makes object obj persistent in the underlying persistence system.
     * E.G. by INSERT INTO ... or UPDATE ...  in an RDBMS.
     * The ObjectModification parameter can be used to determine whether INSERT or 
update is to be used.
     * This functionality is typically called from transaction managers, that
     * track which objects have to be stored.
     */
    public void store(Object obj, ObjectModification mod)
    throws PersistenceBrokerException {
        store(obj, mod, new HashMap());
    }
    
    private void store(Object obj, ObjectModification mod, Map markedForStore)
    throws PersistenceBrokerException {
        // this call ensures that all autoincremented primary key attributes are filled
        Identity oid = new Identity(obj,this);
        // select flag for insert / update selection by checking the ObjectModification
        if (mod.needsInsert()) {
            store(obj, true, markedForStore);
        }
        else if (mod.needsUpdate()) {
            store(obj, false, markedForStore);
        }
        else {
            PersistenceBrokerException ex =
            new PersistenceBrokerException("called store(), but ObjectModification 
tells: don't store...");
            logger.error(ex);
            throw ex;
        }
    }
    /**
     * makes object obj persistent in the underlying persistence system.
     * E.G. by INSERT INTO ... or UPDATE ...  in an RDBMS.
     * The ModificationState parameter can be used to generate optimized SQL code.
     * This functionality is typically called from transaction managers, that
     * track which objects have to be stored. Thus this store method does not
     * use update cascading to referenced objects.
     *
     */
    private void store(Object obj, boolean insert, Map markedForStore) {
        // invoke callback on PersistenceBrokerAware instances
        if (obj instanceof PersistenceBrokerAware) {
            ((PersistenceBrokerAware) obj).beforeStore();
        }
        
        //logger.info("STORING :" + obj + ", doInsert ? " + insert);
        
        // proxies have to be stored only when their real subjects have been 
materialized
        if (obj != null) {
            /**
             * MBAIRD
             * 1. if we are marked for delete already, avoid recursing on this object
             * do this AFTER we check if it's a proxy and it's materialized otherwise 
we will
             * materialize the proxy by putting it in the map.
             */
            if (markedForStore.containsKey(obj)) {
                Boolean insertUpdateMarker = (Boolean) markedForStore.get(obj);
                if (insertUpdateMarker.booleanValue() == insert) {
                    return;
                }
            }
            /**
             * MBAIRD
             * 2. register object in markedForDelete map with proper marker (insert 
flag)
             */
            markedForStore.put(obj, new Boolean(insert));
            
            ClassDescriptor cld =
            descriptorRepository.getDescriptorFor(obj.getClass());
            Identity oid = new Identity(obj,this);
            /**
             * only do the update/insert if the primary key is autoincrement, or is 
already a valid
             * value.
             */
            if (isPKautoIncrement(cld.getPkFields())
            || isPKvalid(oid.getPrimaryKeyValues())) {
                // 1. assign foreign key values so that they can be stored in step 2
                assignReferenceFKs(obj, cld.getObjectReferenceDescriptors());
                // 2. store references (1:1) associations to avoid FK violations
                storeReferences(
                obj,
                cld.getObjectReferenceDescriptors(),
                markedForStore);
                // 3. store primitive typed attributes (Or is THIS step 3 ?)
                // if obj not present in db use INSERT
                if (insert) {
                    dbAccess.executeInsert(cld, obj);
                }
                // else use UPDATE
                else {
                    dbAccess.executeUpdate(cld, obj);
                }
                // cache object for symmetry with getObjectByXXX()
                objectCache.cache(obj);
                // 4. store 1:n and m:n associations
                storeCollections(
                obj,
                cld.getCollectionDescriptors(),
                markedForStore);
            }
        }
        else
            return;
        
        // invoke callback on PersistenceBrokerAware instances
        if (obj instanceof PersistenceBrokerAware) {
            ((PersistenceBrokerAware) obj).afterStore();
        }
    }
    
    /**
     *  assigns all foreign key attributes of the Object obj.
     *  used during store(obj, boolean);
     */
    private void assignReferenceFKs(Object obj, Vector vecRds)
    throws PersistenceBrokerException {
        // get all members of obj that are references and assign FKs
        synchronized (vecRds) {
            Iterator i = vecRds.iterator();
            while (i.hasNext()) {
                ObjectReferenceDescriptor rds =
                (ObjectReferenceDescriptor) i.next();
                Object ref = rds.getPersistentField().get(obj);
                assertFkAssignment(obj, ref, rds);
            }
        }
    }
    
    /**
     * returns true if the broker is currently running a transaction.
     * @return boolean
     */
    public boolean isInTransaction() {
        return inTransaction;
    }
    
    /**
     * Get Foreign key query for 1:n
     * @return org.apache.ojb.broker.query.Query
     * @param obj
     * @param cld
     * @param cod
     */
    private Query getForeignKeyQuery(
    Object obj,
    ClassDescriptor cld,
    CollectionDescriptor cod) {
        Object[] values = cld.getKeyValues(obj);
        ClassDescriptor refCld = getClassDescriptor(cod.getItemClass());
        FieldDescriptor[] fields = cod.getForeignKeyFieldDescriptors(refCld);
        Criteria criteria = new Criteria();
        
        for (int i = 0; i < fields.length; i++) {
            FieldDescriptor fld = (FieldDescriptor) fields[i];
            criteria.addEqualTo(fld.getAttributeName(), values[i]);
        }
        
        Query result =
        QueryFactory.newQuery(refCld.getClassOfObject(), criteria);
        
        return result;
    }
    
    /**
     * Get Foreign key query for m:n <br>
     * supports UNIDIRECTIONAL m:n using QueryByMtoNCriteria
     * @return org.apache.ojb.broker.query.Query
     * @param obj the owner of the relationship
     * @param cld the ClassDescriptor for the owner
     * @param cod the CollectionDescriptor
     */
    private Query getMtoNQuery(
    Object obj,
    ClassDescriptor cld,
    CollectionDescriptor cod) {
        Object[] values = cld.getKeyValues(obj);
        Object[] thisClassFks = cod.getFksToThisClass();
        Object[] itemClassFks = cod.getFksToItemClass();
        ClassDescriptor refCld = getClassDescriptor(cod.getItemClass());
        Criteria criteria = new Criteria();
        Query query;
        
        for (int i = 0; i < thisClassFks.length; i++) {
            criteria.addEqualTo(
            cod.getIndirectionTable() + "." + thisClassFks[i],
            values[i]);
        }
        for (int i = 0; i < itemClassFks.length; i++) {
            criteria.addEqualToColumn(
            cod.getIndirectionTable() + "." + itemClassFks[i].toString(),
            refCld.getPkFields()[i].getAttributeName());
        }
        
        query =
        QueryFactory.newQuery(
        refCld.getClassOfObject(),
        cod.getIndirectionTable(),
        criteria);
        return query;
    }
    
    /**
     * Get Foreign key query for m:n <br>
     * uses inverse relationship, does NOT work with unidirectional m:n <br>
     * THIS METHOD IS NOT YET USED
     * @return org.apache.ojb.broker.query.Query
     * @param obj the owner of the relationship
     * @param cld the ClassDescriptor for the owner
     * @param cod the CollectionDescriptor
     * @throws PersistenceBrokerException if no inverse relationship defined
     */
    private Query getMtoNQuery_inverse(
    Object obj,
    ClassDescriptor cld,
    CollectionDescriptor cod) {
        Object[] values = cld.getKeyValues(obj);
        ClassDescriptor refCld = getClassDescriptor(cod.getItemClass());
        FieldDescriptor[] pks = cld.getPkFields();
        CollectionDescriptor inverseCod = null;
        CollectionDescriptor icod;
        Class itemClass = cld.getClassOfObject();
        Class baseClass = getExtentClass(itemClass);
        Criteria criteria = new Criteria();
        QueryByCriteria query;
        
        //
        // find the inverse relationship of m:n
        //
        Vector v = refCld.getCollectionDescriptors();
        for (int i = 0; i < v.size(); i++) {
            icod = (CollectionDescriptor) v.elementAt(i);
            if (icod.getItemClass() == baseClass) {
                inverseCod = icod;
                break;
            }
        }
        
        if (inverseCod == null) {
            throw new PersistenceBrokerException(
            "No inverse relationship found for :" + cod.getAttributeName());
        }
        
        //
        // build the criteria using inverse relationship
        //
        for (int i = 0; i < pks.length; i++) {
            criteria.addEqualTo(
            inverseCod.getAttributeName() + "." + pks[i].getAttributeName(),
            values[i]);
        }
        
        query = new QueryByCriteria(refCld.getClassOfObject(), criteria);
        if (baseClass != itemClass) {
            query.addPathClass(inverseCod.getAttributeName(), itemClass);
            // Set the hint
        }
        
        return query;
    }
    
    public int getUniqueId(Class extent, String attribute) {
        return sequenceManager.getUniqueId(extent, attribute);
    }
    
    /**
     * removes the objects obj from the brokers internal cache.
     */
    public void removeFromCache(Object obj) throws PersistenceBrokerException {
        objectCache.remove(obj);
    }
    
    /**
     * returns a unique String for class extent and field attribute.
     *
     * the returned String is unique accross all tables in the extent of class extent.
     *
     */
    public String getUniqueString(Class extent, String attribute)
    throws PersistenceBrokerException {
        return sequenceManager.getUniqueString(extent, attribute);
    }
    
    /**
     * returns a unique Object for class extent and field attribute.
     * the returned Object is unique accross all tables in the extent of class extent.
     */
    public Object getUniqueObject(Class extent, String attribute)
    throws PersistenceBrokerException {
        return sequenceManager.getUniqueObject(extent, attribute);
    }
    
    /**
     * returns a ClassDescriptor for the persistence capable class clazz.
     * throws a PersistenceBrokerException if clazz is not persistence capable,
     * i.e. if clazz is not defined in the DescriptorRepository.
     */
    public ClassDescriptor getClassDescriptor(Class clazz)
    throws PersistenceBrokerException {
        return descriptorRepository.getDescriptorFor(clazz);
    }
    
    public boolean hasClassDescriptor(Class clazz) {
        return descriptorRepository.hasDescriptorFor(clazz);
    }
    /**
     * clears the brokers internal cache.
     * removing is recursive. That is referenced Objects are also
     * removed from the cache, if the auto-retrieve flag is set
     * for obj.getClass() in the metadata repository.
     *
     */
    public void clearCache() throws PersistenceBrokerException {
        objectCache.clear();
    }
    
    /**
     * adds a new or replaces an existing ClassDescriptor to the DescriptorRepository.
     */
    public void setClassDescriptor(ClassDescriptor modifiedOrNewDescriptor)
    throws PersistenceBrokerException {
        descriptorRepository.put(
        modifiedOrNewDescriptor.getClassOfObject(),
        modifiedOrNewDescriptor);
    }
    
    /**
     * returns the Extent to which the class clazz belongs.
     * This may be a baseclass,an interface or clazz itself, if no Extent
     * is defined.
     * throws a PersistenceBrokerException if clazz is not persistence capable,
     * i.e. if clazz is not defined in the DescriptorRepository.
     * @param clazz the class to lookup the Extent for
     */
    public Class getExtentClass(Class clazz) throws PersistenceBrokerException {
        return descriptorRepository.getExtentClass(clazz);
    }
    
    /**
     *
     * removes the objects obj from the brokers internal cache and
     * inform other caches in OJB cluster about invalidation.
     */
    public void invalidate(Identity oid) throws PersistenceBrokerException {
        // if running in servermode inform other caches
        if (isRunningInServerMode()) {
            // call all other servers in OJB cluster to remove Object from theirs 
caches
            synchronized (getPool()) {
                Iterator iter = getPool().getAllEntries();
                while (iter.hasNext()) {
                    ServerEntry entry = (ServerEntry) iter.next();
                    PersistenceBrokerClient client =
                    new PersistenceBrokerClient(entry);
                    client.removeFromCache(oid);
                }
            }
        }
        // if running in singlevm mode simply remove from local cache
        else {
            removeFromCache(oid);
        }
    }
    private synchronized ServerPool getPool() {
        return pool;
    }
    
    /**
     * returns a unique long for class extent and field attribute.
     * the returned number is unique accross all tables in the extent of class extent.
     */
    public long getUniqueLong(Class extent, String attribute)
    throws PersistenceBrokerException {
        return sequenceManager.getUniqueLong(extent, attribute);
    }
    
    /*
     * @see PersistenceBroker#getCount(Query)
     */
    public int getCount(Query query) throws PersistenceBrokerException {
        int result = 0;
        Class itemClass = query.getSearchClass();
        ClassDescriptor cld = getClassDescriptor(itemClass);
        String lastUsedTable = cld.getFullTableName();
        
        // if class is NOT an interface we can count its directly mapped table
        if (!cld.isInterface()) {
            result = dbAccess.executeCount(query, cld);
        }
        
        // if class is an extent, we have to search through all extent classes too
        if (cld.isExtent()) {
            Vector extentClasses = cld.getExtentClasses();
            for (int i = 0; i < extentClasses.size(); i++) {
                itemClass = (Class) extentClasses.get(i);
                cld = getClassDescriptor(itemClass);
                Class concreteItemClass = cld.getClassOfObject();
                ClassDescriptor concreteCld = getClassDescriptor(concreteItemClass);
                
                /**
                 * BRJ
                 * query the same table only once.
                 * queries multiple classes maping to one table
                 * will have the ojbConcreteClass criteria set
                 * ie: ojbConcreteClass IN ('A','B').
                 * executing the query for each extent would return
                 * wrong result
                 */
                if(concreteCld.getFullTableName().equals(lastUsedTable)) {
                    continue;
                }
                
                lastUsedTable = concreteCld.getFullTableName();
                int extentCount = dbAccess.executeCount(query, concreteCld);
                result += extentCount;
            }
        }
        
        return result;
    }
    
    /*
     * @see PersistenceBroker#getReportQueryIteratorByQuery(Query)
     */
    public Iterator getReportQueryIteratorByQuery(Query query)
    throws PersistenceBrokerException {
        ClassDescriptor cld =
        descriptorRepository.getDescriptorFor(query.getSearchClass());
        if (query instanceof QueryBySQL) {
            String sql = ((QueryBySQL) query).getSql();
            return new SqlBasedReportQueryRsIterator(cld, sql, this);
        }
        else {
            return new ReportQueryRsIterator(query, cld, this);
        }
    }
    
    /*
     * @deprecated
     */
    public boolean open(String repositoryFileName, String user, String passwd) {
        PBKey key = new PBKey(repositoryFileName, user, passwd);
        DescriptorRepository rep = DescriptorRepository.getInstance(key);
        if (user != null) {
            rep.getDefaultJdbcConnection().setUserName(user);
        }
        if (passwd != null) {
            rep.getDefaultJdbcConnection().setPassWord(passwd);
        }
        this.descriptorRepository = rep;
        return true;
    }
    
    /*
     * @see Configurable#configure(Configuration)
     */
    public void configure(Configuration pConfig) throws ConfigurationException {
        PersistenceBrokerConfiguration config =
        (PersistenceBrokerConfiguration) pConfig;
        
        pool = new ServerPool(config.getServers());
        runningInServerMode = config.isRunningInServerMode();
    }
    
    /**
     * Gets the runningInServerMode value.
     * @return Returns a boolean
     */
    public boolean isRunningInServerMode() {
        return runningInServerMode;
    }
    
    /**
     * return true if any of the FieldDescriptors is autoIncrement.
     * also return true if NO FieldDescriptors available
     * @param fdesc the primary key FieldDescriptor[]
     */
    private boolean isPKautoIncrement(FieldDescriptor[] fdesc) {
        boolean retval = false;
        int fieldDescriptorSize = fdesc.length;
        
        /**
         * if the fdesc array is 0 length, we don't have a primary key valid 
identified for this table.
         * that is valid in a way, so set retval to true
         */
        if (fieldDescriptorSize == 0) {
            retval = true;
        }
        
        for (int i = 0; i < fieldDescriptorSize; i++) {
            /**
             * if any of the keys in the primary key FieldDescriptor array
             * are autoincrement we return that this is an autoincrement PK.
             * We check all fields, in the case of a composite key.
             */
            if (fdesc[i].isAutoIncrement()) {
                retval = true;
                break;
            }
        }
        
        return retval;
    }
    
    /**
     * only checks string type keys for validity, since the default of other types of 
keys is
     * unknown (ie int)
     * @param pkValues
     * @return true if valid
     */
    private boolean isPKvalid(Object[] pkValues) {
        int size = pkValues.length;
        
        for (int i = 0; i < size; i++) {
            Object pkValue = pkValues[i];
            // null as value of a primary key is not acceptable
            if (pkValue == null) {
                return false;
            }
            
            if (pkValue instanceof String) {
                // the toString() method on a String-object is maybe faster
                // than the downcast to String. Also use length() to test
                // if a String empty or not, this is faster than the comparing
                // a String-object with an empty string using the equals()-method.
                if (pkValue.toString().trim().length() == 0) {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    /*
     * @see ObjectContainer#query()
     */
    public org.odbms.Query query() {
        return new org.apache.ojb.soda.QueryImpl();
    }
    
    /**
     * @return DescriptorRepository
     */
    public DescriptorRepository getDescriptorRepository() {
        return descriptorRepository;
    }
    
}

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to