alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (SQLStateManager.java)

This example Glassfish source code file (SQLStateManager.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

arraylist, collection, fielddesc, foreignfielddesc, foreignfielddesc, jdbc, localfielddesc, noi18n, noi18n, object, object, reflection, sco, scocollection, sql, sqlstatemanager, sqlstatemanager, util

The Glassfish SQLStateManager.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * SQLStateManager.java
 *
 * Created on March 3, 2000
 */

package com.sun.jdo.spi.persistence.support.sqlstore;

import com.sun.jdo.api.persistence.support.*;
import com.sun.jdo.spi.persistence.support.sqlstore.ejb.EJBHelper;
import com.sun.jdo.spi.persistence.support.sqlstore.model.*;
import com.sun.jdo.spi.persistence.support.sqlstore.query.jqlc.QueryValueFetcher;
import com.sun.jdo.spi.persistence.support.sqlstore.sco.SqlTimestamp;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.UpdateObjectDescImpl;
import com.sun.jdo.spi.persistence.support.sqlstore.state.LifeCycleState;
import com.sun.jdo.spi.persistence.support.sqlstore.state.PersistentNonTransactional;
import com.sun.jdo.spi.persistence.support.sqlstore.state.PersistentClean;
import com.sun.jdo.spi.persistence.support.sqlstore.state.Hollow;
import com.sun.jdo.spi.persistence.utility.NullSemaphore;
import com.sun.jdo.spi.persistence.utility.Semaphore;
import com.sun.jdo.spi.persistence.utility.SemaphoreImpl;
import com.sun.jdo.spi.persistence.utility.logging.Logger;
import org.glassfish.persistence.common.I18NHelper;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.*;


/**
 *
 */
public class SQLStateManager implements Cloneable, StateManager, TestStateManager {

    private static final int PRESENCE_MASK = 0;

    private static final int SET_MASK = 1;

    private static final int MAX_MASKS = 2;

    private BitSet fieldMasks;

    /** Array of Object. */
    public ArrayList hiddenValues;

    private ClassDesc persistenceConfig;

    private PersistenceManager persistenceManager;

    private PersistenceStore store;

    private SQLStateManager beforeImage;

    private Object persistentObject;

    private Object objectId;

    private LifeCycleState state;

    /** This flag is used to disable updates due to dependency management. */
    private static final short ST_UPDATE_DISABLED = 0x1;

    private static final short ST_REGISTERED = 0x2;

    private static final short ST_VISITED = 0x4;

    private static final short ST_PREPARED_PHASE_II = 0x8;

    private static final short ST_FIELD_TRACKING_INPROGRESS = 0x10;

    private static final short ST_DELETE_INPROGRESS = 0x20;

    private static final short ST_VALIDATION_FAILED = 0x40;

    private short stateFlags;

    // This instance is a replacement for a deleted instance with the same
    // ObjectId.
    private boolean isReplacementInstance = false;

    // This instance needs to be registered with the global (weak) cache at
    // rollback if it transitions to persistent (HOLLOW or P_NONTX) state.
    private boolean needsRegisterAtRollback = false;

    // This instance needs to be to be verified at the time it is removed
    // from the global (weak) cache at rollback if it transitions to transient state.
    private boolean needsVerifyAtDeregister = false;

    // This flag is initially set to false and changed to true when the first
    // operation (e.g. makePersistent, loadForRead, or getObjectById) succeeds.
    private boolean valid = false;

    /** Stores the updates to the associated object. */
    private UpdateObjectDescImpl updateDesc;

    /** Contains state managers depending on this object. */
    private HashSet updatedForeignReferences;

    /** Counts the foreign state managers this state manager depends on. */
    private int referenceCount;

    /** Serializes access to this StateManager. */
    private final Semaphore lock;

    /** The logger. */
    private static Logger logger = LogHelperStateManager.getLogger();

    /** I18N message handler. */
    private final static ResourceBundle messages = I18NHelper.loadBundle(
            SQLStateManager.class);

    /** Name of the USE_BATCH property. */
    public static final String USE_BATCH_PROPERTY =
        "com.sun.jdo.spi.persistence.support.sqlstore.USE_BATCH"; // NOI18N

    /**
     * Property to swich on/off batching. Note, the default is true, meaning we
     * try to do batching if the property is not specified.
     */
    private static final boolean USE_BATCH = Boolean.valueOf(
        System.getProperty(USE_BATCH_PROPERTY, "true")).booleanValue(); // NOI18N

    /**
     * Construct a new SQLStateManager so that it locks or does not lock as
     * per whether or not it is used in a managed environment.
     */
    public SQLStateManager(PersistenceStore store, ClassDesc persistenceConfig) {
        this.store = store;
        this.persistenceConfig = persistenceConfig;

        if (EJBHelper.isManaged()) {
            this.lock = new NullSemaphore("SQLStateManager");  // NOI18N
        } else {
            this.lock = new SemaphoreImpl("SQLStateManager");  // NOI18N
        }
    }

    public synchronized void initialize(boolean persistentInDB) {
        boolean xactActive = persistenceManager.isActiveTransaction();
        boolean optimistic = persistenceManager.isOptimisticTransaction();
        boolean nontransactionalRead = persistenceManager.isNontransactionalRead();
        LifeCycleState oldstate = state;

        if (state == null) {
            if (persistentInDB == false) {
                // Hollow object aquired by PM.getObjectByOid() does not require
                // to be persistent in DB
                state = LifeCycleState.getLifeCycleState(LifeCycleState.HOLLOW);
                persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
            } else {
                if (xactActive && !optimistic) {
                    state = LifeCycleState.getLifeCycleState(LifeCycleState.P_CLEAN);
                    persistenceManager.setFlags(persistentObject, READ_OK);
                } else {
                    state = LifeCycleState.getLifeCycleState(LifeCycleState.P_NON_TX);
                    persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
                }
                valid = true;
            }
        } else if (state.needMerge()) {
            state = state.transitionReadField(optimistic, nontransactionalRead, xactActive);

            // If we are in a state that requires the instance to be reloaded
            // we need to set the jdoFlags to LOAD_REQUIRED to enable field mediation.
            if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
                persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
            } else {
                if (persistenceManager.getFlags(persistentObject) == LOAD_REQUIRED) {
                    persistenceManager.setFlags(persistentObject, READ_OK);
                }
            }
        }

        registerInstance(false, null, oldstate);
    }

    private void registerInstance(boolean throwDuplicateException,
        ArrayList newlyRegisteredSMs, LifeCycleState oldstate) {

        if ((stateFlags & ST_REGISTERED) == 0 || // not registered or
            (oldstate != state &&  // state changed from clean to dirty or transactional type.
                (oldstate == null || oldstate.isDirty() != state.isDirty() ||
                    oldstate.isTransactional() != state.isTransactional()))) {

            persistenceManager.registerInstance(this, getObjectId(), throwDuplicateException, false);
            stateFlags |= ST_REGISTERED;
            if (newlyRegisteredSMs != null) {
                if (!newlyRegisteredSMs.contains(this))
                    newlyRegisteredSMs.add(this);
            }
        }
    }

    public void setPersistenceManager(com.sun.jdo.api.persistence.support.PersistenceManager pm) {
        this.persistenceManager = (PersistenceManager) pm;
    }

    public void setPersistent(Object pc) {
        this.persistentObject = pc;
    }

    public PersistenceStore getStore() {
        return store;
    }

    public Object getPersistent() {
        return persistentObject;
    }

    public PersistenceConfig getPersistenceConfig() {
        return persistenceConfig;
    }

    private UpdateObjectDescImpl getUpdateDesc() {
        if (updateDesc == null) {
            updateDesc = (UpdateObjectDescImpl) store.getUpdateObjectDesc(
                    persistenceConfig.getPersistenceCapableClass());
        }

        if (updateDesc.getConcurrency() == null) {
            boolean optimistic = persistenceManager.isOptimisticTransaction();
            updateDesc.setConcurrency(persistenceConfig.getConcurrency(optimistic));
        }

        return updateDesc;
    }

    private void unsetMaskBit(int index, int mask) {
        if (fieldMasks == null) {
            newFieldMasks();
        } else {
            if (index >= 0) {
                fieldMasks.clear(index + mask * persistenceConfig.maxFields);
            } else {
                fieldMasks.clear(-(index + 1) + persistenceConfig.maxVisibleFields +
                        mask * persistenceConfig.maxFields);
            }
        }
    }

    private void clearMask(int mask) {
        if (fieldMasks != null) {
            fieldMasks.clear(mask * persistenceConfig.maxFields,
                    (mask+1) * persistenceConfig.maxFields);
        }
    }

    private void setVisibleMaskBits(int mask) {
        if (fieldMasks == null) {
            newFieldMasks();
        }

        int offset = mask * persistenceConfig.maxFields;
        fieldMasks.set(offset, offset + persistenceConfig.maxVisibleFields);
    }

    private BitSet getVisibleMaskBits(int mask) {
        if (fieldMasks == null) {
            newFieldMasks();
        }

        int offset = mask * persistenceConfig.maxFields;
        return fieldMasks.get(offset, offset + persistenceConfig.maxVisibleFields);
    }

    private void newFieldMasks() {
        this.fieldMasks = new BitSet(MAX_MASKS * persistenceConfig.maxFields);
    }

    public void setPresenceMaskBit(int index) {
        if (fieldMasks == null) {
            newFieldMasks();
        }

        if (index >= 0) {
            fieldMasks.set(index + PRESENCE_MASK * persistenceConfig.maxFields);
        } else {
            fieldMasks.set(-(index + 1) + persistenceConfig.maxVisibleFields +
                    PRESENCE_MASK * persistenceConfig.maxFields);
        }
    }

    private void setSetMaskBit(int index) {
        if (fieldMasks == null) {
            newFieldMasks();
        }

        if (index >= 0) {
            fieldMasks.set(index + SET_MASK * persistenceConfig.maxFields);
        } else {
            fieldMasks.set(-(index + 1) + persistenceConfig.maxVisibleFields +
                    SET_MASK * persistenceConfig.maxFields);
        }
    }

    public boolean getPresenceMaskBit(int index) {
        if (fieldMasks == null) {
            newFieldMasks();
        }

        if (index >= 0) {
            return fieldMasks.get(index + PRESENCE_MASK * persistenceConfig.maxFields);
        } else {
            return fieldMasks.get(-(index + 1) + persistenceConfig.maxVisibleFields +
                    PRESENCE_MASK * persistenceConfig.maxFields);
        }
    }

    public boolean getSetMaskBit(int index) {
        if (fieldMasks == null) {
            newFieldMasks();
        }

        if (index >= 0) {
            return fieldMasks.get(index + SET_MASK * persistenceConfig.maxFields);
        } else {
            return fieldMasks.get(-(index + 1) + persistenceConfig.maxVisibleFields +
                    SET_MASK * persistenceConfig.maxFields);
        }
    }

    public Object getHiddenValue(int index) {
        // This method expects index to be negative for hidden fields.
        if (index >= 0) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.poshiddenindex", "" + index)); // NOI18N
        }

        int realIndex = -(index + 1);

        if ((hiddenValues != null) && (realIndex < hiddenValues.size())) {
            return hiddenValues.get(realIndex);
        }

        return null;
    }

    public void setHiddenValue(int index, Object value) {
        // This method expects index to be negative for hidden fields.
        if (index >= 0) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.poshiddenindex", "" + index)); // NOI18N
        }

        int realIndex = -(index + 1);

        if (hiddenValues == null) {
            hiddenValues = new ArrayList();
        }

        for (int i = hiddenValues.size(); i <= realIndex; i++) {
            hiddenValues.add(null);
        }

        hiddenValues.set(realIndex, value);
    }

    public synchronized void replaceObjectField(String fieldName, Object o) {
        boolean debug = logger.isLoggable();

        if (debug) {
            Object[] items = new Object[] {fieldName, o.getClass().getName()};
            logger.fine("sqlstore.sqlstatemanager.replaceobjectfield", items); // NOI18N
        }

        FieldDesc fieldDesc = persistenceConfig.getField(fieldName);
        Object oldo = prepareSetField(fieldDesc, o);

        if ((oldo instanceof SCO) && oldo != o) {
            if (debug)
                logger.fine("sqlstore.sqlstatemanager.replaceobjectfield.unsetsco"); // NOI18N
            ((SCO) oldo).unsetOwner();
        }
    }

    public synchronized void makeDirty(String fieldName) {
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.makedirty", fieldName); // NOI18N
        }

        FieldDesc fieldDesc = persistenceConfig.getField(fieldName);

        // Save current value: if it is SCO object we need to replace it with the
        // new value instead of new instance
        Object oldo = fieldDesc.getValue(this);

        prepareUpdateField(fieldDesc, null);

        // Now adjust SCO instance
        Object newo = fieldDesc.getValue(this);

        if ((newo instanceof SCO) && oldo != newo) {
            if (oldo instanceof SCOCollection) {
                if (debug) {
                    logger.fine("sqlstore.sqlstatemanager.makedirty.fixscocollection"); // NOI18N
                }

                ((SCOCollection) oldo).clearInternal();
                ((SCOCollection) oldo).addAllInternal((Collection) newo);
            }

            else if (oldo instanceof SCODate) {
                if (debug) {
                    logger.fine("sqlstore.sqlstatemanager.makedirty.fixscodate"); // NOI18N
                }

                long l = ((java.util.Date) newo).getTime();
                // Adjust nanoseconds if necessary:
                int n = 0;

                if (newo instanceof Timestamp) {
                    n = ((Timestamp) newo).getNanos();
                } else {
                    n = (int) ((l % 1000) * 1000000);
                }

                if (oldo instanceof SqlTimestamp) {
                    ((SCODate) oldo).setTimeInternal(l);
                    ((SqlTimestamp) oldo).setNanosInternal(n);

                } else if (newo instanceof Timestamp) {
                    ((SCODate) oldo).setTimeInternal(l + (n / 1000000));
                } else {
                    ((SCODate) oldo).setTimeInternal(l);
                }
            }

            updateTrackedFields(fieldDesc, oldo, null);
            fieldDesc.setValue(this, oldo);

            // disconnect temp SCO instance
            if (newo instanceof SCO)
                ((SCO) newo).unsetOwner();
        }
    }

    /**
     * This method is central to record changes to SCOCollections.
     */
    public void applyUpdates(String fieldName, SCOCollection c) {
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.applyupdates", fieldName); // NOI18N
        }

        FieldDesc fieldDesc = persistenceConfig.getField(fieldName);
        if (fieldDesc instanceof ForeignFieldDesc) {
            ArrayList removed = new ArrayList(c.getRemoved());
            ArrayList added = new ArrayList(c.getAdded());

            // We reset the collection to clear the added and removed list before calling
            // processCollectionUpdates() which can throw an exception.
            c.reset();
            processCollectionUpdates((ForeignFieldDesc) fieldDesc, removed, added, null, true, false);
        }
        // else it is an ERROR?

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.applyupdates.exit"); // NOI18N
        }
    }

    public void makePresent(String fieldName, Object value) {
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.makepresent", fieldName); // NOI18N
        }

        FieldDesc fieldDesc = persistenceConfig.getField(fieldName);
        fieldDesc.setValue(this, value);
        setPresenceMaskBit(fieldDesc.absoluteID);
    }

    public void setObjectId(Object objectId) {
        // RESOLVE: do we allow to replace existing?
        this.objectId = objectId;
    }

    public Object getObjectId() {
        // Note: PM.getObjectId() makes copy of the actual object id.
        if (objectId == null) {
            Class oidClass = persistenceConfig.getOidClass();
            Object oid = null;

            try {
                oid = oidClass.newInstance();
            } catch (Exception e) {
                throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                        "core.statemanager.cantnewoid", oidClass.getName()), e); // NOI18N
            }

            Field keyFields[] = persistenceConfig.getKeyFields();
            String keyFieldNames[] = persistenceConfig.getKeyFieldNames();
            for (int i = 0; i < keyFields.length; i++) {
                Field keyField = keyFields[i];
                try {
                    FieldDesc fd = persistenceConfig.getField(keyFieldNames[i]);

                    if (fd != null) {
                        keyField.set(oid, fd.getValue(this));
                    }

                } catch (IllegalAccessException e) {
                    throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                            "core.statemanager.cantsetkeyfield", keyField.getName()), e); // NOI18N
                }
            }
            objectId = oid;
        }

        return objectId;
    }

    private void makeAutoPersistent(Object pc) {
        persistenceManager.makePersistent(pc);
        SQLStateManager sm = (SQLStateManager) persistenceManager.getStateManager(pc);

        sm.state = LifeCycleState.getLifeCycleState(LifeCycleState.AP_NEW);
    }

    /**
     * Prepares the associated object to be stored in the datastore.
     * This method is called by PersistenceManager.makePersistent().
     * Thread synchronization is done in the persistence manager.
     */
    public void makePersistent(PersistenceManager pm, Object pc) {

        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.makepersistence", // NOI18N
            persistenceConfig.getPersistenceCapableClass().getName());
        }

        // If the instance is autopersistent, we simply transition it to persistent_new.
        if (state != null) {
            if (state.isAutoPersistent()) {
                state = state.transitionMakePersistent();
            }
            return;
        }

        this.persistenceManager = pm;
        this.persistentObject = pc;

        // Mark all the visible fields as present to prevent navigation and
        // to allow us to create a before image that contains all the fields.
        setVisibleMaskBits(PRESENCE_MASK);
        getBeforeImage();

        state = LifeCycleState.getLifeCycleState(LifeCycleState.P_NEW);

        try {
            registerInstance(true, null, null);
        } catch (JDOException e) {
            this.release();

            throw e;
        }

        // We set the statemanager for the pc now so the instance is considered
        // persistent. We need to do this in order for persistent-by-reachability to
        // work properly in the case of self-referencing relationship.
        pm.setStateManager(pc, this);
        valid = true;

        // Now that the state manager has been set in the pc, we need to
        // synchronize it so other threads can't modify this instance while
        // we perform the persistence-by-reachability algorithm.
        try {
            getLock();

            // Make sure all the fields have been marked dirty.
            Object obj = null;
            ArrayList fields = persistenceConfig.fields;
            for (int i = 0; i < fields.size(); i++) {
                FieldDesc f = (FieldDesc) fields.get(i);

                // In case of makePersistent, we skip all secondary tracked fields
                // and use the primary to propagate changes. In addition, we take
                // the policy that a tracked relationship field takes precedence
                // over its primitive counterpart. In other words, we skip all
                // primitive fields that also tracks relationship fields.
                if ((f.sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) > 0) {
                    continue;
                }

                obj = f.getValue(this);

                if (f instanceof ForeignFieldDesc) {
                    ForeignFieldDesc ff = (ForeignFieldDesc) f;
                    ArrayList trackedFields = null;

                    if (debug) {
                        logger.fine("sqlstore.sqlstatemanager.processforeign", ff.getName()); // NOI18N
                    }

                    if ((ff.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) > 0) {
                        trackedFields = ff.getTrackedFields();
                        Object theValue = obj;

                        for (int j = 0; j < trackedFields.size(); j++) {
                            FieldDesc tf = (FieldDesc) trackedFields.get(j);
                            Object value = tf.getValue(this);

                            if ((theValue != null) && (value != null) && (theValue != value)) {
                                if (needsVerifyAtDeregister) {
                                    persistenceManager.deregisterInstance(getObjectId(), this);
                                    needsVerifyAtDeregister = false;
                                } else {
                                    persistenceManager.deregisterInstance(getObjectId());
                                }
                                this.release();
                                throw new JDOUserException(I18NHelper.getMessage(messages,
                                        "core.statemanager.conflictingvalues", ff.getName(), tf.getName())); // NOI18N
                            } else if ((theValue == null) && (value != null)) {
                                theValue = value;
                            }
                        }

                        if (theValue != obj) {
                            obj = theValue;
                            ff.setValue(this, obj);
                        }
                    }

                    if (obj != null) {
                        if (obj instanceof Collection) {
                            if (((Collection) obj).size() > 0) {
                                ArrayList removed = null;
                                ArrayList added = new ArrayList((Collection) obj);
                                processCollectionUpdates(ff, removed, added, null, true, false);
                            }
                        } else {
                            // null out this field to pretend we are setting this field for the first time
                            ff.setValue(this, null);

                            updateObjectField(ff, obj, true, false);

                            // now restore the value
                            ff.setValue(this, obj);
                        }
                    } else {
                        // For a null managed collection relationship field, we replace it
                        // with an empty SCOCollection
                        if ((ff.getInverseRelationshipField() != null) && (ff.cardinalityUPB > 1)) {
                            replaceCollection(ff, null);
                        }
                    }

                    updateTrackedFields(ff, ff.getValue(this), null);
                } else {
                    // We ignore primitive fields that also tracks relationship field
                    if ((f.sqlProperties & FieldDesc.PROP_TRACK_RELATIONSHIP_FIELD) > 0) {
                        ArrayList trackedFields = f.getTrackedFields();
                        boolean found = false;

                        for (int j = trackedFields.size() - 1; j >= 0; j--) {
                            FieldDesc tf = (FieldDesc) trackedFields.get(j);

                            if (tf instanceof ForeignFieldDesc) {
                                if (tf.getValue(this) != null) {
                                    found = true;
                                    break;
                                }
                            } else {
                                break;
                            }
                        }

                        if (!found) {
                            //f.setValue(this, null);
                            updateTrackedFields(f, obj, null);
                            //f.setValue(this, obj);
                        }
                    } else {
                        updateTrackedFields(f, obj, null);
                    }

                    if ((f.sqlProperties & FieldDesc.PROP_RECORD_ON_UPDATE) > 0) {
                        getUpdateDesc().recordUpdatedField((LocalFieldDesc) f);
                    }
                }

                if (debug) {
                    logger.fine("sqlstore.sqlstatemanager.makedirtyfield", f.getName()); // NOI18N
                }

                setSetMaskBit(f.absoluteID);
            }
        } finally {
            releaseLock();
        }
    }

    /**
     * Prepares the associated object for delete. This method is
     * called by PersistenceManager.deletePersistent(). After
     * nullifying the relationship fields, the instance transitions to
     * deleted state.
     */
    public void deletePersistent() {
        if (logger.isLoggable()) {
             logger.fine("sqlstore.sqlstatemanager.deletepersistence", // NOI18N
             persistenceConfig.getPersistenceCapableClass().getName());

        }

        // Why try try?  The difference is in whether you try and then
        // acquire/get, or acquire/get and then try.
        // Prior to having acquireFieldUpdateLock, this code synchronized on
        // a field, following that with the try for {get,release}Lock.  That
        // pattern of usage calls for that order.
        // {acquire,release}FieldUpdateLock calls for the other order.  We
        // are close to FCS, so this strange situation persists for now
        // (i.e., it ain't broken).

        persistenceManager.acquireFieldUpdateLock();
        try {
            try {
                getLock();

                if (state.isDeleted()) {
                    return;
                }

                deleteRelationships();

                LifeCycleState oldstate = state;
                state = state.transitionDeletePersistent();
                persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
                registerInstance(false, null, oldstate);
            } finally {
                releaseLock();
            }
        } finally {
            persistenceManager.releaseFieldUpdateLock();
        }
    }

    /**
     * Prepares the current instance for delete by nullifying all
     * relationships. The deletion is propagated to relationship
     * fields marked for cascade delete.
     */
    private void deleteRelationships() {
        ArrayList foreignFields = persistenceConfig.foreignFields;
        int size = foreignFields.size();
        stateFlags |= ST_DELETE_INPROGRESS;

        for (int i = 0; i < size; i++) {
            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
            ForeignFieldDesc irf = ff.getInverseRelationshipField();

            // Skip this field if it is secondary.
            if ((ff.sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) > 0) {
                continue;
            }

            // Skip this field if it is not managed nor marked for cascade delete.
            if ((ff.deleteAction != ForeignFieldDesc.ACT_CASCADE) && (irf == null)) {
                continue;
            }

            prepareUpdateField(ff, null);

            if (ff.cardinalityUPB > 1) {
                Collection c = (Collection) ff.getValue(this);

                if (c != null) {
                    ArrayList removed = new ArrayList(c);

                    // For managed relationship or cascade delete, we need to call
                    // processCollectionUpdates() to set up the dependency. In case of
                    // managed relationship, the inverse relationship field should also
                    // be set to null.
                    processCollectionUpdates(ff, removed, null, null, true, false);

                    if (c instanceof SCOCollection) {
                        ((SCOCollection) c).clearInternal();
                    } else {
                        c.clear();
                    }

                    if (ff.deleteAction == ForeignFieldDesc.ACT_CASCADE) {
                        Iterator iter = removed.iterator();

                        while (iter.hasNext()) {
                            Object obj = iter.next();

                            if (obj != null) {
                                SQLStateManager sm = (SQLStateManager)
                                        persistenceManager.getStateManager(obj);

                                // Ignore if this sm is in the process of being cascade
                                // deleted. This is to prevent infinite recursive in case
                                // of self-referencing relationship.
                                if ((sm != null) && !sm.isDeleted() &&
                                        ((sm.stateFlags & ST_DELETE_INPROGRESS) == 0)) {
                                    try {
                                        persistenceManager.deletePersistent(obj);
                                    } catch (Throwable e) {
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                Object obj = ff.getValue(this);

                if (obj != null) {
                    updateObjectField(ff, null, true, false);
                    ff.setValue(this, null);

                    if (ff.deleteAction == ForeignFieldDesc.ACT_CASCADE) {
                        SQLStateManager sm = (SQLStateManager)
                                persistenceManager.getStateManager(obj);

                        // Ignore if this sm is in the process of being cascade
                        // deleted. This is to prevent infinite recursive in case
                        // of self-referencing relationships.
                        if ((sm != null) && !sm.isDeleted() &&
                                ((sm.stateFlags & ST_DELETE_INPROGRESS) == 0)) {
                            try {
                                persistenceManager.deletePersistent(obj);
                            } catch (Throwable e) {
                            }
                        }
                    }
                }
            }
        }

        stateFlags &= ~ST_DELETE_INPROGRESS;
    }

    /**
     * Stores the associated object in the datastore. This method is
     * called by {@link PersistenceManager#beforeCompletion} on
     * flush/commit.  The specified state manager argument is used to
     * determine whether the actual instance should be flushed
     * immediately or whether batch update is possible.
     *
     * @param next Next state manager in the transaction cache.
     */
    public void updatePersistent(StateManager next) {
        boolean debug = logger.isLoggable();

        if ((stateFlags & ST_UPDATE_DISABLED) > 0) {
            if (debug) {
                Object[] items = new Object[] {persistenceConfig.getPersistenceCapableClass().getName(),
                                               persistentObject};
                logger.fine("sqlstore.sqlstatemanager.updatepersistent.skipped", items); // NOI18N
            }
            return;
        }

        try {
            if (debug) {
                 logger.fine("sqlstore.sqlstatemanager.updatepersistent", // NOI18N
                 persistenceConfig.getPersistenceCapableClass().getName());
            }

            ArrayList actions = new ArrayList();

            // Get a list of actions to perform.
            getUpdateActions(actions);

            if (actions.size() == 1 && useBatch()) {
                // Batch update only if actions consists of a single action
                UpdateObjectDesc updateDesc = (UpdateObjectDesc)actions.get(0);
                boolean immediateFlush = requiresImmediateFlush((SQLStateManager)next);

                if (debug && immediateFlush) {
                    Object[] items = new Object[] {getPersistent(), (next != null) ? next.getPersistent() : null};
                    logger.fine("sqlstore.sqlstatemanager.updatepersistent.immediateflush", items); // NOI18N
                }

                store.executeBatch(persistenceManager, updateDesc, immediateFlush);
            } else if (actions.size() > 0) {
                store.execute(persistenceManager, actions);
            }

            incrementVersion(actions);

            if (debug) {
                logger.fine("sqlstore.sqlstatemanager.updatepersistent.exit"); // NOI18N
            }
        } catch (JDOException e) {
            e.addFailedObject(persistentObject);
            throw e;
        } catch (Exception e) {
            logger.throwing("sqlstore.SQLStateManager", "updatePersistent", e); // NOI18N
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.generic.unknownexception"), e); // NOI18N
        }
    }

    /**
     * Increments the version for all state managers in
     * <code>actions registered for version consistency.
     *
     * @param actions List of updated state managers.
     */
    static private void incrementVersion(List actions) {

        for (Iterator iter = actions.iterator(); iter.hasNext(); ) {
            ((UpdateObjectDescImpl) iter.next()).incrementVersion();
        }
    }

    /**
     * Increments the version fields for this state manager. Instances
     * mapped to multiple tables have got a version field for each table.
     */
    public void incrementVersion() {
        LocalFieldDesc [] versionFields = persistenceConfig.getVersionFields();

        for (int i = 0; i < versionFields.length; i++) {
            versionFields[i].incrementValue(this);
        }
    }

    /**
     * @inheritDoc StateManager#hasVersionConsistency
     */
    public boolean hasVersionConsistency() {
        return persistenceConfig.hasVersionConsistency();
    }

    /**
     * @inheritDoc StateManager#verifyPersistent
     */
    public boolean verifyPersistent() {
        assert persistenceConfig.hasVersionConsistency();
        boolean verified = true;

        if (state instanceof PersistentClean) {
            RetrieveDesc verificationRD = persistenceConfig.getRetrieveDescForVerificationQuery(store);
            LocalFieldDesc[] keyFields = persistenceConfig.getKeyFieldDescs();
            LocalFieldDesc[] versionFields = persistenceConfig.getVersionFields();

            // Please make sure that the order of parameter values is same as
            // order of parameters as defined by ClassDesc#getRetrieveDescForVerificationQuery()
            Object [] parameters = new Object[keyFields.length + versionFields.length];
            copyValues(parameters, keyFields, 0);
            copyValues(parameters, versionFields, keyFields.length);

            // verificationRD requires parameters for pk field and version fields
            Boolean result = (Boolean) store.
                    retrieve(persistenceManager, verificationRD, new QueryValueFetcher(parameters));
            verified = result.booleanValue();
        }
        return verified;
    }

    /**
     * Returns true if batch update might be used to store the changes
     * of this state manager.
     *
     * TODO: Because batched statements on Oracle don't return a
     * valid success indicator, batching is disabled for Version
     * Consistency.
     */
    private boolean useBatch() {
        boolean result = false;

        if (USE_BATCH) {
            switch(state.getUpdateAction()) {

            case ActionDesc.LOG_CREATE:
                result = !getUpdateDesc().hasChangedRelationships() &&
                         !getUpdateDesc().hasModifiedLobField();
                break;
            case ActionDesc.LOG_DESTROY:
            case ActionDesc.LOG_UPDATE:
                // Do not try to batch in optimitic tx for now. We need to
                // check for parallel updates, so the WHERE clause checks
                // the values from the beforeImage. We need a different SQL
                // statements for the null vs. non null case.
                result = !persistenceManager.isOptimisticTransaction() &&
                         !persistenceConfig.hasModifiedCheckAtCommitConsistency() &&
                         !getUpdateDesc().hasChangedRelationships() &&
                         !getUpdateDesc().hasModifiedLobField() &&
                         !hasVersionConsistency();
                break;
            default:
                result = false;
                break;
            }
        }

        return result;
    }

    /**
     * @inheritDoc StateManager#setVerificationFailed
     */
    public void setVerificationFailed() {
        if (hasVersionConsistency()) {
            stateFlags |= ST_VALIDATION_FAILED;
        }
    }

    /**
     * @inheritDoc StateManager#getFailed
     */
    public boolean isVerificationFailed() {
        return (stateFlags & ST_VALIDATION_FAILED) > 0;
    }

    /**
     * This method checks whether this StateManager instance needs to be
     * flushed immediately during beforeCompletion. A return of <code>false
     * means the store manager is allowed to combine flushing of these two
     * instance in a single database roundtrip (e.g. by using batched updates).
     *
     * @param next Next state manager to be flushed.
     */
    private boolean requiresImmediateFlush(SQLStateManager next) {
        // There is no next SM =>
        // flush this sm immediately
        if (next == null)
            return true;

        // The next StateManager has a different pc class =>
        // flush this sm immediately
        if (persistenceConfig != next.persistenceConfig)
            return true;

        // The next StateManager represents a different update operation
        // INSERT/UPDATE/DELETE => flush this sm immediately
        if (state.getUpdateAction() != next.state.getUpdateAction())
            return true;

        // If the next's flush is disabled, flush this sm
        if ((next.stateFlags & ST_UPDATE_DISABLED) > 0)
            return true;

        // If next sm does not use batch update flush this sm
        if (!next.useBatch())
            return true;

        // For updates, we need to check if the next sm updates the
        // same fields. If not, flush this sm
        if (getUpdateDesc().getUpdateAction() == ActionDesc.LOG_UPDATE &&
                !compareUpdatedFields(next)) {
            return true;
        }

        // If the next stateManager has got (foreign reference) dependencies,
        // it is not considered for batching at all
        if (next.updatedForeignReferences != null) {
            return true;
        }

        // Now we can make use of batching w/o flushing
        return false;
    }

    private boolean compareUpdatedFields(SQLStateManager next) {
        BitSet updFields = getVisibleMaskBits(SET_MASK);
        BitSet nextUpdFields = (next != null) ? next.getVisibleMaskBits(SET_MASK) : null;

        return updFields.equals(nextUpdFields);
    }

    public void refreshPersistent() {
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.refreshpersistent", // NOI18N
            persistenceConfig.getPersistenceCapableClass().getName());
        }

        // Only refresh if the state allows it.
        if (state.isRefreshable()) {
            LifeCycleState oldstate = state;
            state = state.transitionRefreshPersistent();
            reload(null);
            registerInstance(false, null, oldstate);
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.refreshpersistent.exit"); // NOI18N
        }
    }

    /**
     * Reloads the instance by delegating actual work, state transition, and
     * instance registration to {@link #reload(FieldDesc) reload(FieldDesc)}
     * With <code>null as an argument. Called by
     * {@link PersistenceManager#getObjectById(Object, boolean)
     * PersistenceManager.getObjectById(Object, boolean)} with validate
     * flag set to <code>true
     */
    public void reload() {
        boolean debug = logger.isLoggable(Logger.FINER);

        if (debug) {
            logger.finer("sqlstore.sqlstatemanager.unconditionalreload", // NOI18N
            persistenceConfig.getPersistenceCapableClass().getName());
        }

        persistenceManager.acquireShareLock();

        try {
            getLock();

            reload(null);

        } finally {
            persistenceManager.releaseShareLock();
            releaseLock();

            if (debug) {
                logger.finer("sqlstore.sqlstatemanager.unconditionalreload.exit"); // NOI18N
            }
        }
    }

    /**
     * Reloads this SM from the state in the datastore, getting data for the
     * given field.
     * @param additionalField Field to be loaded.
     */
    private void reload(FieldDesc additionalField) {
        boolean debug = logger.isLoggable();

        if (debug) {
            String fieldName =
                (additionalField != null) ? additionalField.getName() : null;
            logger.fine("sqlstore.sqlstatemanager.reload", // NOI18N
            persistenceConfig.getPersistenceCapableClass().getName(), fieldName);
        }

        // Clear the fields PresenceMask so all the currently present
        // fields will be replaced.
        clearMask(PRESENCE_MASK);

        // Need to mark the key fields as present
        markKeyFieldsPresent();

        clearMask(SET_MASK);

        LifeCycleState oldState = state;
        state = state.transitionReload(persistenceManager.isActiveTransaction());

        if (!retrieveFromVersionConsistencyCache(additionalField)) {
            // Retrieve the instance from the data store, if this class
            // is not version consistent, or not found in the cache.

            try {
                retrieve(additionalField);
            } catch (JDOException e) {
                // Reset the state if the instance couldn't be found.
                state = oldState;
                throw e;
            }
        }
        registerInstance(false, null, oldState);

        if (persistenceManager.getFlags(persistentObject) == LOAD_REQUIRED) {
            persistenceManager.setFlags(persistentObject, READ_OK);
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.reload.exit"); // NOI18N
        }
    }

    /**
     * Initialize this SM from the version consistency cache. If this
     * SM is in the cache and the additional field is not populated,
     * the field is retrieved from the store.
     * @param additionalField Field to be loaded.
     */
    private boolean retrieveFromVersionConsistencyCache(FieldDesc additionalField) {
        boolean rc =
            persistenceManager.initializeFromVersionConsistencyCache(this);

        if (rc) {

            // make sure additionalField is available
            if (additionalField != null
                    && !getPresenceMaskBit(additionalField.absoluteID)) {

                realizeField(additionalField);
            }
        }
        return rc;
    }

    /**
     * PersistenceManager calls this method to prepare a persistent
     * object for update. This is required for foreign fields only
     * as they could reference "regular" JDK Collections vs. SCO
     * Collections. Such process has the side-effect of causing more
     * objects to be registered with the transaction cache.
     */
    public void prepareToUpdatePhaseI() {
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph1", // NOI18N
            persistenceConfig.getPersistenceCapableClass().getName());
        }

        int action = state.getUpdateAction();

        if (action == ActionDesc.LOG_NOOP || action == ActionDesc.LOG_DESTROY) {
            // Nothing extra to do
            return;
        }

        // Initialize UpdateDesc.
        getUpdateDesc();

        ArrayList newlyRegisteredSMs = new ArrayList();
        ArrayList foreignFields = persistenceConfig.foreignFields;
        int size = foreignFields.size();

        for (int i = 0; i < size; i++) {
            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);

            if ((ff.sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) > 0) {
                continue;
            }

            if ((ff.cardinalityUPB > 1) && (getSetMaskBit(ff.absoluteID) == true)) {
                Collection v = (Collection) ff.getValue(this);

                if ((v != null) && (!(v instanceof SCO) || (((SCO) v).getOwner() == null)) &&
                        (v.size() > 0)) {
                    ArrayList removed = null;
                    ArrayList added = new ArrayList(v);

                    processCollectionUpdates(ff, removed, added, newlyRegisteredSMs, true, false);
                }
            }
        }

        // The newRegisteredSMs should contain a list of all the state managers that
        // are registered as the result of processCollectionUpdates.
        for (int i = 0; i < newlyRegisteredSMs.size(); i++) {
            SQLStateManager sm = (SQLStateManager) newlyRegisteredSMs.get(i);

            sm.prepareToUpdatePhaseI();
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph1.exit"); // NOI18N
        }
    }

    /**
     * This is the second phase of the commit processing. It populates phase3sms with all
     * the autopersistent instances that are no longer reachable from a persistent instance.
     *
     * @param phase3sms List containing autopersistent instances that are no longer reachable
     *  from a persistent instance.
     */
    public void prepareToUpdatePhaseII(HashSet phase3sms) {
        boolean debug = logger.isLoggable();

        if (debug) {
             logger.fine("sqlstore.sqlstatemanager.preparetoupdateph2", // NOI18N
             persistenceConfig.getPersistenceCapableClass().getName());
        }

        // If this instance is autopersistent, we transition it into a pending state and
        // add it to phase3sms collection. Any instance in phase3sms collection may be removed
        // later if it becomes persistent.
        if (state.isAutoPersistent()) {
            state = state.transitionMakePending();
            phase3sms.add(this);
            return;
        }

        if ((stateFlags & ST_PREPARED_PHASE_II) > 0) {
            return;
        }

        stateFlags |= ST_PREPARED_PHASE_II;

        if ((!state.isNew() && !state.isDirty()) || state.isDeleted()) {
            return;
        }

        ArrayList foreignFields = persistenceConfig.foreignFields;
        int size = foreignFields.size();

        // Walk the object graph starting from this instance and transition all
        // autopersistent instances to persistent and remove it from phase3sms.
        for (int i = 0; i < size; i++) {
            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);

            if (ff.cardinalityUPB <= 1) {
                if (getPresenceMaskBit(ff.absoluteID)) {
                    Object v = ff.getValue(this);

                    if (v != null) {
                        transitionPersistent(v, phase3sms);
                    }
                }
            } else {
                Collection c = getCollectionValue(ff);

                if (c != null) {
                    Iterator iter = c.iterator();

                    while (iter.hasNext()) {
                        Object v = iter.next();

                        transitionPersistent(v, phase3sms);
                    }
                }
            }
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph2.exit"); // NOI18N
        }
    }

    /**
     * This is the third phase of commit processing. It sets up the delete dependencies among
     * all the autopersistent instances that have been flushed to the database.
     */
    public void prepareToUpdatePhaseIII() {
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph3", // NOI18N
            persistenceConfig.getPersistenceCapableClass().getName());
        }

        if (!state.isPersistentInDataStore()) {
            // This object will not be written to the store. But we need to
            // make sure, that scheduled jointable entries aren't written either.
            // See UpdateQueryPlan#processJoinTables().
            if (updateDesc != null) {
                updateDesc.clearUpdatedJoinTableRelationships();
            }

            // Finished for this instance.
            return;
        }

        ArrayList foreignFields = persistenceConfig.foreignFields;
        int size = foreignFields.size();

        // Sets up dependencies between this instance and all its relationship fields
        // that are autopersistent.
        for (int i = 0; i < size; i++) {
            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);

            if (ff.cardinalityUPB <= 1) {
                if (getPresenceMaskBit(ff.absoluteID)) {
                    Object v = ff.getValue(this);

                    if (v != null) {
                        updateObjectField(ff, null, false, false);
                    }
                }
            } else {
                Collection c = getCollectionValue(ff);

                if (c != null) {
                    if (c.size() > 0) {
                        ArrayList removed = new ArrayList(c);
                        ArrayList added = null;

                        processCollectionUpdates(ff, removed, added, null, false, false);
                    }
                }
            }
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph3.exit"); // NOI18N
        }
    }

    /**
     * Transitions the instance <code>pc to persistent state if it's
     * autopersistent, and removes its state manager from the list
     * <code>phase3sms of unreachable autopersistent
     * instances. The recursive call to <code>prepareToUpdatePhaseII
     * removes the transitive closure of all state managers reachable from
     * <code>pc from phase3sms. This method has got no
     * effects on transient instances.
     *
     * @param pc Instance becoming persistent and removed from <code>phase3sms.
     * @param phase3sms List containing so far unreachable autopersistent instances.
     */
    private void transitionPersistent(Object pc, HashSet phase3sms) {
        SQLStateManager sm = (SQLStateManager) persistenceManager.getStateManager(pc);

        // Need to check if the associated state manager is null, if
        // called with an object from a collection relationship field. If
        // the collection is not a SCO collection, it is possible that it
        // contains transient instances. No need to check for object
        // relationship fields.
        if (sm != null && sm.state.isAutoPersistent()) {
            sm.state = sm.state.transitionMakePersistent();
            phase3sms.remove(sm);
            sm.prepareToUpdatePhaseII(phase3sms);
        }
    }

    /**
     * Returns the value of the collection relationship field
     * <code>ff.  For deferred SCOCollections, only the
     * objects added in the current transaction are returned.
     * This method may only be called for Collection fields!
     *
     * @param ff Collection relationship field.
     * @return The value of the collection relationship field
     * <code>ff. For deferred SCOCollections, only the
     * objects added in the current transaction are returned.
     */
    private Collection getCollectionValue(ForeignFieldDesc ff) {
        Collection c = null;
        if (ff.cardinalityUPB > 1) {
            c = (Collection) ff.getValue(this);
            if (c != null && c instanceof SCOCollection) {
                SCOCollection sco = (SCOCollection) c;
                if (sco.isDeferred()) {
                    c = sco.getAdded();
                }
            }
        }
        return c;
    }

    private void getUpdateActions(ArrayList actions) {
        if ((stateFlags & ST_VISITED) > 0) {
            return;
        }

        int action = state.getUpdateAction();

        if ((action == ActionDesc.LOG_NOOP) && (updateDesc == null)) {
            return;
        }

        // Initialize updateDesc.
        getUpdateDesc();

        updateDesc.setObjectInfo(getBeforeImage(), this, action);

        if ((action == ActionDesc.LOG_DESTROY) || (action == ActionDesc.LOG_CREATE) ||
                updateDesc.hasUpdatedFields() || updateDesc.hasUpdatedJoinTableRelationships()) {
            actions.add(updateDesc);
        }

        stateFlags |= ST_VISITED;

        if (updatedForeignReferences != null) {
            Iterator iter = updatedForeignReferences.iterator();

            while (iter.hasNext()) {
                SQLStateManager sm = ((UpdatedForeignReference) iter.next()).getStateManager();

                if (sm.referenceCount == 1) {
                    sm.getUpdateActions(actions);
                }

                sm.referenceCount--;
            }
        }
    }

    public void release() {
        if (null != persistenceManager) {

            // The persistenceManager can be null, for example if this
            // instance is used in the VersionConsistency cache.
            persistenceManager.setStateManager(persistentObject, null);
        }

        persistentObject = null;
        objectId = null;
        persistenceManager = null;
        beforeImage = null;
        hiddenValues = null;
        updatedForeignReferences = null;
        updateDesc = null;
        persistenceConfig = null;
        store = null;
        valid = false;
    }

    private void reset(boolean retainValues, boolean wasNew, boolean keepState) {
        boolean debug = logger.isLoggable();

        if (debug) {
             Object[] items = new Object[] {Boolean.valueOf(retainValues),
                                            Boolean.valueOf(wasNew), Boolean.valueOf(keepState)};
             logger.fine("sqlstore.sqlstatemanager.reset", items); // NOI18N

        }

        if (state == null) {
            // make the instance transient.

            if (!keepState) {
                persistenceManager.clearFields(persistentObject);
            }

            // Need to set jdoFlag to READ_WRITE_OK for transient instance.
            persistenceManager.setFlags(persistentObject, READ_WRITE_OK);

            if (needsVerifyAtDeregister) {
                persistenceManager.deregisterInstance(getObjectId(), this);
            } else {
                persistenceManager.deregisterInstance(getObjectId());
            }
            this.release();
        } else {
            // Reset the state manager for the next transaction.
            stateFlags = 0;
            beforeImage = null;
            updatedForeignReferences = null;
            referenceCount = 0;

            if (updateDesc != null) {
                updateDesc.reset();
            }

            // We retain the field values if retainValues is true or the state
            // is persistentNontransactional
            if (retainValues || (state instanceof PersistentNonTransactional)) {
                FieldDesc f = null;
                ArrayList fields = persistenceConfig.fields;
                for (int i = 0; i < fields.size(); i++) {
                    f = (FieldDesc) fields.get(i);
                    Object v = f.getValue(this);

                    // For new objects mark null references as not set if the field is
                    // not managed. This is to allow the field to be reloaded in the next
                    // transaction because a relationship may exist in the database.
                    if (wasNew && (f instanceof ForeignFieldDesc) &&
                            (v == null) && (((ForeignFieldDesc) f).getInverseRelationshipField() == null)) {
                        if (debug)
                            logger.fine("sqlstore.sqlstatemanager.unsetmask", f.getName()); // NOI18N
                        unsetMaskBit(f.absoluteID, PRESENCE_MASK);
                    }

                    // Replace java.util Collection and Date objects
                    // with SCO instances:
                    if ((v instanceof Collection) && !(v instanceof SCOCollection)
                            && !keepState) {
                        if (debug)
                            logger.fine("sqlstore.sqlstatemanager.resettingcollection"); // NOI18N

                        replaceCollection((ForeignFieldDesc) f, (Collection) v);

                        if (debug) {
                            logger.fine("sqlstore.sqlstatemanager.newtype", (f.getValue(this)).getClass()); // NOI18N
                        }
                    } else if (v instanceof SCOCollection) {
                        ((SCOCollection) v).reset();
                    }

                    // TO FIX!!!: this already replaces Date with SCO Date
                    else if ((v instanceof java.util.Date) && !(v instanceof SCODate)
                            && !keepState) {
                        if (debug)
                            logger.fine("sqlstore.sqlstatemanager.resettingdate"); // NOI18N

                        v = f.convertValue(v, this);
                        f.setValue(this, v);
                        if (debug) {
                            logger.fine("sqlstore.sqlstatemanager.newtype", (f.getValue(this)).getClass()); // NOI18N
                        }
                    }
                }

                // We need to set the jdoFlags to LOAD_REQUIRED instead of READ_OK in order to
                // have the StateManager intermediate access to this instance. The reason is
                // if the next transaction is pessimistic, the StateManager needs to reload
                // the instance. Note that the StateManager will not reload the instance
                // if the transaction is optimistic and the instance is p_nontransactional.
                persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
            } else {
                clearMask(PRESENCE_MASK);
                persistenceManager.clearFields(persistentObject);

                // Need to mark the key fields as present
                markKeyFieldsPresent();

                persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
            }

            clearMask(SET_MASK);
            isReplacementInstance = false;
            needsRegisterAtRollback = false;
            needsVerifyAtDeregister = false;
        }
    }

    /**
     * @return true if persistentObject has been flushed to db
     */
    public boolean isProcessed() {
        return (referenceCount == 0);
    }

    public void flushed() {
        // reset the current state to the point where we can accept more updates.
        state = state.transitionFlushed();

        clearMask(SET_MASK);

        stateFlags &= ~ST_VISITED;
        stateFlags &= ~ST_UPDATE_DISABLED;
        stateFlags &= ~ST_REGISTERED;

        // need to set the jdoFlags to LOAD_REQUIRED to allow updated to be tracked.
        persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);

        if (updatedForeignReferences != null) {
            updatedForeignReferences.clear();
        }

        if (updateDesc != null) {
            updateDesc.reset();
        }
    }

    public void commit(boolean retainValues) {
        boolean wasNew = (state.isNew() && !state.isDeleted());
        state = state.transitionCommit(retainValues);
        reset(retainValues, wasNew, false);
    }

    public void rollback(boolean retainValues) {
        boolean wasNew = (state.isNew() && !state.isDeleted());
        boolean needsRestore = state.needsRestoreOnRollback(retainValues);
        state = state.transitionRollback(retainValues);
        boolean keepState = needsRestore;

        // Only restore if there is a before image and the flag needsRestore is true.
        if ((beforeImage != null) && (needsRestore == true)) {
            copyFields(beforeImage, true, false);

            // Keep the fields from being reset in reset()
            keepState = true;
        }

        if (needsRegisterAtRollback && !isReplacementInstance) {
            persistenceManager.registerInstance(this, getObjectId());
        }
        reset(retainValues, wasNew, keepState);
    }

    private void markKeyFieldsPresent() {
        ArrayList keyFields = persistenceConfig.getPrimaryTable().getKey().getFields();

        for (int i = 0; i < keyFields.size(); i++) {
            LocalFieldDesc fd = (LocalFieldDesc) keyFields.get(i);

            if (fd != null) {
                setPresenceMaskBit(fd.absoluteID);
            }
        }
    }

    public void prepareGetField(int fieldID) {
        FieldDesc fieldDesc = persistenceConfig.getField(fieldID);

        prepareGetField(fieldDesc, false, true);
    }

    private void prepareGetField(FieldDesc fieldDesc) {
        prepareGetField(fieldDesc, true, false);
    }

    /**
     * Loads the field described by <code>fieldDesc. If the field is not
     * present in the instance, it will be loaded from the data store by calling
     * {@link #realizeField(FieldDesc)}. Depending on its lifecycle state,
     * the instance is registered in the transaction cache and the field
     * is marked as read.
     *
     * @param fieldDesc Field descriptor for the field to be loaded.
     */
    private void prepareGetField(FieldDesc fieldDesc, boolean internal, boolean acquireShareLock) {
        boolean debug = logger.isLoggable(Logger.FINEST);

        if (debug) {
            logger.finest("sqlstore.sqlstatemanager.preparegetfield", fieldDesc.getName()); // NOI18N
        }

        if (acquireShareLock) {
            persistenceManager.acquireShareLock();
        }

        try {
            getLock();

            boolean xactActive = persistenceManager.isActiveTransaction();
            boolean optimistic = persistenceManager.isOptimisticTransaction();
            boolean nontransactionalRead = persistenceManager.isNontransactionalRead();

            if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
                reload(fieldDesc);
            }

            LifeCycleState oldstate = state;
            state = state.transitionReadField(optimistic, nontransactionalRead, xactActive);

            registerInstance(false, null, oldstate);

            // Only allow dynamic navigation if we are not in the state that allows it.
            if (state.isNavigable() || !internal) {
                if (getPresenceMaskBit(fieldDesc.absoluteID) == false) {
                    realizeField(fieldDesc);
                }
            }
        } catch (JDOException e) {
            throw e;
        } catch (Exception e) {
            logger.log(Logger.FINE,"sqlstore.exception.log", e);
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.getfieldfailed"), e); // NOI18N
        } finally {
            if (acquireShareLock) {
                persistenceManager.releaseShareLock();
            }
            releaseLock();

            if (debug) {
                logger.finest("sqlstore.sqlstatemanager.preparegetfield.exit"); // NOI18N
            }
        }
    }

    /**
     * Retrieves a field specified by <code>fieldDesc from the data
     * store. If the field is part of a group then all unfetched fields
     * in the group are retrieved.  realizeField is part of dynamic
     * navigation. The field is marked as present.
     *
     * @param fieldDesc The field descriptor of the field to be retrieved.<p>
     * Note: The <code>fieldDesc parameter must not be null.
     */
    private void realizeField(FieldDesc fieldDesc) {
        assert fieldDesc != null;

        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.realizefield", fieldDesc.getName()); // NOI18N
        }

        if (!persistenceConfig.isNavigable()) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.notnavigable", // NOI18N
                    fieldDesc.getName(), persistentObject.getClass().getName()));
        }

        boolean fieldRealized = false;

        if (fieldDesc instanceof ForeignFieldDesc) {
            ForeignFieldDesc ff = (ForeignFieldDesc) fieldDesc;

            // We can do an enhancement if we are only getting a single
            // relationship field. Check if the field can be retrieved on
            // it's own and the relationship is not mapped to a join table.
            // The field can be retrieved on it's own if it's is in
            // an independent fetch group or not in a fetch group at all.
            // Independent fetch groups have group ids < FieldDesc.GROUP_NONE.
            if (ff.fetchGroup <= FieldDesc.GROUP_NONE
                    && persistenceConfig.getFetchGroup(ff.fetchGroup).size() <= 1
                 && !ff.useJoinTable()) {

                fieldRealized = realizeForeignField(ff);
            }
        }

        if (!fieldRealized) {
            retrieve(fieldDesc);
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.realizefield.exit"); // NOI18N
        }
    }

    /**
     * For foreign fields we want to take advantage of knowing the
     * relationship key and only selecting the foreign rather than the
     * primary with the foreign attached.  Most of the work is in
     * figuring out whether we can do that.
     *
     * @param foreignField The relationship field to be retrieved.
     *  Following is true for this field.
     * <ul>
     *  <li>It is part of an independent fetch group with only one
     *  field in the fetch group or not part of any fetch group.</li>
     *  <li>It is not mapped to a join table. 
     * </ul>
     * Note: The <code>foreignField parameter must not be null.
     *
     * @return True, if relationship field has been retrieved, false otherwise.
     */
    private boolean realizeForeignField(ForeignFieldDesc foreignField) {
        assert foreignField != null;

        boolean isPresent = false;
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.realizeforeignfield", // NOI18N
                    foreignField.getName());
        }

        // Check and see if all the values we need are present.
        for (int i = 0; i < foreignField.localFields.size(); i++) {
            LocalFieldDesc lf = (LocalFieldDesc) foreignField.localFields.get(i);
            isPresent = getPresenceMaskBit(lf.absoluteID);

            if (!isPresent) {
                break;
            }
        }

        if (isPresent) {
            // All the values we need are present.  Wow.  Now we'll have to
            // format a more specialized request and attach the object(s)
            // we get back to our managed object.
            populateForeignField(foreignField);
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.realizeforeignfield.exit", // NOI18N
                    Boolean.valueOf(isPresent));
        }

        return isPresent;
    }

    /**
     * Retrieves the relationship.
     *
     * @param foreignField The relationship field to be retrieved.
     *  Following is true for this field.
     * <ul>
     *  <li>It is part of an independent fetch group with only one
     *  field in the fetch group or not part of any fetch group.</li>
     *  <li>It is not mapped to a join table. 
     * </ul>
     * Note: The <code>foreignField must not be null.
     */
    private void populateForeignField(ForeignFieldDesc foreignField) {
        assert foreignField != null;

        boolean foundInstance = false;

        if (foreignField.hasForeignKey() && foreignField.isMappedToPk()) {
            // Cache lookup, returns an object.
            Object pc = getObjectById(foreignField, null, null, true);

            if (foundInstance = (pc != null)) {
                foreignField.setValue(this, pc);
            }
        }

        if (!foundInstance) {
            // Query lookup, returns a collection.
            Collection result = retrieveForeign(foreignField);
            attachQueryResult(foreignField, result);
        }

        // Or in PRESENT bit for this field in the properties mask
        setPresenceMaskBit(foreignField.absoluteID);
    }

    /**
     * Attaches the retrieved object(s) <code>queryResult to the
     * relationship field <code>foreignField of the instance
     * managed by this state manager.
     *
     * @param queryResult Retrieved value for the relationship field
     * <code>foreignField.
     */
    private void attachQueryResult(ForeignFieldDesc foreignField,
                                   Collection queryResult) {
        assert foreignField != null;

        // Attach the object(s) we got back to our managed object.
        // There are three cases:
        //		1) The foreign field contains a collection
        //		2) The foreign object doesn't exist (NIL)
        //		3) The foreign field is a reference to a single object
        if (foreignField.getComponentType() != null) {
            // Instantiate and populate a dynamic array, namely Collection.
            // NOTE: queryResult is null, if we didn't execute the retrieval!
            replaceCollection(foreignField, queryResult);
        } else if (queryResult == null || queryResult.size() == 0) {
            foreignField.setValue(this, null);
        } else {
            if (queryResult.size() > 1) {
                throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                        "core.persistencestore.toomanyobjforcard1", // NOI18N
                        persistenceConfig.getName(),
                        foreignField.getName(), "" + queryResult.size())); // NOI18N
            }
            Object v = queryResult.iterator().next();
            foreignField.setValue(this, v);
        }
    }

    /**
     * Retrieves the relationship based on the values of the
     * relationship columns on the local side. Note: This method
     * assumes that the relationship key fields are loaded.
     *
     * RESOLVE:
     * The relationship might be mapped to an Unique Key.
     * Do databases constrain the Unique Key columns to be non null?
     *
     * @param foreignField The relationship field to be retrieved.
     *  Following is true for this field.
     * <ul>
     *  <li>It is part of an independent fetch group with only one
     *  field in the fetch group or not part of any fetch group.</li>
     *  <li>It is not mapped to a join table. 
     * </ul>
     * Note: The <code>foreignField must not be null.
     *
     * @return Collection returned by the query. Null, if the relationship
     *  is not set or the passed relationship field is null.
     * @see #retrieve
     */
    private Collection retrieveForeign(ForeignFieldDesc foreignField) {
        assert foreignField != null;

        Collection result = null;
        boolean debug = logger.isLoggable();

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.retrieveforeign", // NOI18N
                    foreignField.getName());
        }

        Object[] values = new Object[foreignField.localFields.size()];
        boolean isValidForeignKey = true;

        for (int i = 0; i < foreignField.localFields.size(); i++) {
            FieldDesc flf = (FieldDesc) foreignField.localFields.get(i);

            if (!getPresenceMaskBit(i)) {
                // throw exception
            }

            if (getSetMaskBit(flf.absoluteID)) {
                // This is one reason why we must have a before image
                // on relationship changes!
                values[i] = flf.getValue(beforeImage);
            } else {
                values[i] = flf.getValue(this);
            }

            // Make sure we have a valid query key.
            if (values[i] == null) {
                // The relationship must be null. No need to query!
                isValidForeignKey = false;
            }
        }

        if (isValidForeignKey) {
            // Getting a new generated RD or a cached one.
            RetrieveDesc fdesc =
                    persistenceConfig.getRetrieveDescForFKQuery(foreignField, store);
            result = (Collection) store.retrieve(
                    persistenceManager, fdesc, new QueryValueFetcher(values));
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.retrieveforeign.exit"); // NOI18N
        }

        return result;
    }

    /**
     * The retrieve method gets a retrieve descriptor to retrieve the
     * desired field and adds constraints necessary to limit the
     * retrieval set to the source object runs the retrieve
     * descriptor against the store, the source object is connected to,
     * and then merges the results back into the source object.
     *
     * @param additionalField The additional field to be retrieved.<p>
     * Note: The <code>additionalField might be null if we just
     * want to reload the instance.
     * @see #retrieveForeign
     */
    private void retrieve(FieldDesc additionalField) {
        boolean debug = logger.isLoggable();

        if (debug) {
            String fieldName = (additionalField != null) ? additionalField.getName() : null;
            logger.fine("sqlstore.sqlstatemanager.retrieve", fieldName); // NOI18N
        }

        LocalFieldDesc[] keyFields = persistenceConfig.getKeyFieldDescs();
        Object [] values = new Object[keyFields.length];
        copyValues(values, keyFields, 0);

        // Getting a new generated RD or a cached one.
        RetrieveDesc rd = persistenceConfig.getRetrieveDescForPKQuery(additionalField, store);
        Collection result = (Collection) store.
                retrieve(persistenceManager, rd, new QueryValueFetcher(values));

        if (result.size() > 1) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.toomanyrows", // NOI18N
                    persistenceConfig.getPersistenceCapableClass().getName()));
        } else if (result.size() < 1 || result.iterator().next() != persistentObject) {

            // If there are no instances fetched, or the fetched instances is not the one
            // we asked for, it means that it is not found and we throw an exception
            throw new JDOObjectNotFoundException(I18NHelper.getMessage(messages,
                    "core.statemanager.objectnotfound"), // NOI18N
                    new Object[]{persistentObject});
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.retrieve.exit"); // NOI18N
        }
    }

    /**
     * Copies the value of <code>fields into the array
     * <code>values. The values are copied into the
     * array starting at index <code>startIndex.
     *
     * @param values Array taking the values of <code>fields.
     * @param fields Array of LocalFieldDesc.
     * @param startIndex Starting index into <code>values.
     */
    private void copyValues(Object[] values, LocalFieldDesc[] fields, int startIndex) {

        // The values array should be long enough to hold all the fields.
        assert values.length - startIndex >= fields.length;

        for (int i = 0; i < fields.length; i++) {
            LocalFieldDesc field = fields[i];
            values[i + startIndex] = field.getValue(this);

            // The field is not expected to be null.
            assert values[i + startIndex] != null;
        }
    }

    public SQLStateManager getBeforeImage() {

        // Do not try to get before image if not required
        if (beforeImage == null && isBeforeImageRequired()) {
            boolean debug = logger.isLoggable();

            if (debug) {
                logger.fine("sqlstore.sqlstatemanager.getbeforeimage", // NOI18N
							persistenceConfig.getPersistenceCapableClass().getName());
            }

            try {
                getLock();

                // Make a copy of the persistentObject.
                beforeImage = copyPersistent();
            } finally {
                releaseLock();
            }

            if (debug) {
                logger.fine("sqlstore.sqlstatemanager.getbeforeimage.exit"); // NOI18N
            }
        }

        return beforeImage;
    }

    public boolean isBeforeImageRequired() {

        com.sun.jdo.api.persistence.support.Transaction t = persistenceManager.currentTransaction();
        // NOTE: We need to create a before image on relationship changes for two reasons:
        // (1) Relationship management assumes relationship fields to be loaded.
        // See prepareUpdateField.
        // (2) The before image is used in realizeField.
        boolean isBeforeImageRequired =
                persistenceManager.isOptimisticTransaction() ||
                getUpdateDesc().hasChangedRelationships() ||
                t.getRetainValues() || t.getRestoreValues() ||
                persistenceConfig.hasModifiedCheckAtCommitConsistency();
       if (logger.isLoggable(Logger.FINER)) {
            logger.finer("sqlstore.sqlstatemanager.isbeforeimagerequired", // NOI18N
                    Boolean.valueOf(isBeforeImageRequired));
       }
       return isBeforeImageRequired;
    }

    private SQLStateManager copyPersistent() {
        PersistenceManager pm = (PersistenceManager) getPersistenceManagerInternal();
        SQLStateManager newStateManager = (SQLStateManager) clone();

        // Associate state manager with a new pc instance.
        pm.newInstance(newStateManager);

        newStateManager.copyFields(this, true, true);

        return newStateManager;
    }

    /**
     * @inheritDoc StateManager#copyFields(StateManager source)
     * Does not copy relationship fields.  Other fields are cloned while
     * copying as per {@link #cloneObjectMaybe(Object source)}.
     * @throws IllegalArgumentException if source is null, is
     * not <code>instanceof SQLStateManager, or is not managing the
     * same type of persistent instance as this StateManager.
     */
    public void copyFields(StateManager source) {
        if (!(source instanceof SQLStateManager)) {
            String className =
                (source != null) ? source.getClass().getName() : null;
            throw new IllegalArgumentException(className);
        }

        SQLStateManager sqlSource = (SQLStateManager) source;

        if (persistenceConfig != sqlSource.getPersistenceConfig()) {
            Class thisPCClass =
                persistenceConfig.getPersistenceCapableClass();
            Class sourcePCClass =
                sqlSource.getPersistenceConfig().getPersistenceCapableClass();
            throw new IllegalArgumentException(
                I18NHelper.getMessage(
                    messages,
                    "core.statemanager.copyFields.mismatch", // NOI18N
                    thisPCClass.getName(),
                    sourcePCClass.getName()));
        }

        copyFields(sqlSource, false, true);
    }

    /**
     * @inheritDoc  StateManager#copyFields(StateManager source).
     * @param copyRelationships if true, then relationship fields are copied,
     * otherwise they are not copied.
     * @param clone if true, then the fields are cloned while copying,
     * otherwise both the <code>source and this StateManager reference
     * the same field value.
     */
    private void copyFields(SQLStateManager source,
                            boolean copyRelationships,
                            boolean clone) {
        ArrayList fields = null;

        // Reset the field masks, as the instance will
        // be populated with the state from the source.
        clearMask(PRESENCE_MASK);
        clearMask(SET_MASK);

        for (int i = 0; i < 2; i++) {
            if (i == 0) {
                fields = persistenceConfig.fields;
            } else {
                fields = persistenceConfig.hiddenFields;
            }

            for (int j = 0; (fields != null) && (j < fields.size()); j++) {
                FieldDesc f = (FieldDesc) fields.get(j);

                if (!copyRelationships && f.isRelationshipField()) {
                    continue;
                }

                if (source.getPresenceMaskBit(f.absoluteID)) {
                    Object value = f.getValue(source);
                    f.setValue(this, (clone) ? cloneObjectMaybe(value) : value);
                    setPresenceMaskBit(f.absoluteID);
                }
            }
        }
    }

    private Object cloneObjectMaybe(Object source) {
        // RESOLVE: need to clone SCOCollection

        if (source != null) {
            // RESOLVE: Should we clone byte[]???
            if ((source instanceof SCO)) {
                return ((SCO)source).cloneInternal();

            } else if (!(source instanceof Number) &&
                    !(source instanceof String) &&
                    !(source instanceof Character) &&
                    !(source instanceof Boolean) &&
                    // RESOLVE: #javax.ejb package# !(source instanceof javax.ejb.EJBObject) &&
                    !(source instanceof com.sun.jdo.api.persistence.support.PersistenceCapable) &&
                    !(source instanceof byte[])) {
                try {
                    Class type = source.getClass();

                    if (!type.isArray()) {
                        Method m = type.getMethod("clone", (Class []) null); // NOI18N

                        if (m != null) {
                            return m.invoke(source, (Object []) null);
                        }
                    } else {
                        Object srcArray[] = (Object[]) source;
                        Object dstArray[] = (Object[])
                                java.lang.reflect.Array.newInstance(type.getComponentType(), srcArray.length);

                        for (int i = 0; i < srcArray.length; i++) {
                            dstArray[i] = srcArray[i];
                        }
                        return dstArray;
                    }
                } catch (NoSuchMethodException e) {
                    if (logger.isLoggable()) {
                        Object[] items = new Object[] {e, source.getClass().getName()};
                        logger.fine("sqlstore.sqlstatemanager.nosuchmethodexcep.clone", items); // NOI18N
                    }
                } catch (InvocationTargetException e) {
                } catch (IllegalAccessException e) {
                }
            }
        }

        return source;
    }

    /**
     * Prepares the field described by <code>fieldDesc for update.
     * This method is central to record changes to fields. The state
     * transitions to dirty. The instance is registered in the
     * transaction cache. If the field is set for the first time,
     * the before image is prepared and the field is marked as modified.
     * Updated local fields are added to the column list to be updated.
     *
     * @param fieldDesc Updated field.
     * @param newlyRegisteredSMs
     *   State managers of autopersistent objects will be added to this list.
     * @see #prepareUpdateFieldSpecial
     */
    private void prepareUpdateField(FieldDesc fieldDesc, ArrayList newlyRegisteredSMs) {

        // No updates for key fields. As this method is called by
        // loadForUpdate for _all_ DFG fields (to prepare a hollow
        // instance for update), we can't throw an exception here
        // like in prepareUpdateFieldSpecial.
        if (fieldDesc.isKeyField()) {
            return;
        }

        getUpdateDesc().markRelationshipChange(fieldDesc);

        boolean debug = logger.isLoggable();

        if (debug) {
             Object[] items = new Object[] {fieldDesc.getName(),state};
             logger.fine("sqlstore.sqlstatemanager.prepareupdatefield", items); // NOI18N
        }

        boolean optimistic = persistenceManager.isOptimisticTransaction();
        boolean xactActive = persistenceManager.isActiveTransaction();
        boolean nontransactionalRead = persistenceManager.isNontransactionalRead();

        if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
            reload(fieldDesc);
        }

        // State transition.
        // The state transition prevends field updates on deleted instances
        // and must always be executed. See PersistentDeleted.transitionWriteField().
        LifeCycleState oldstate = state;
        state = state.transitionWriteField(xactActive);
        registerInstance(false, newlyRegisteredSMs, oldstate);

        if (state == oldstate && getSetMaskBit(fieldDesc.absoluteID) &&
                getPresenceMaskBit(fieldDesc.absoluteID)) {
            // The presence mask bit is NEVER set for deferred collections,
            // because deferred collections MUST always be reloaded.
            return;
        }

        // We don't reload the field for a newly created instance.
        if (state.isBeforeImageUpdatable()) {

            // Reload the field, as relationship management
            // assumes that relationship fields are always present.
            // See updateObjectField() or processCollectionupdates().
            if (!getPresenceMaskBit(fieldDesc.absoluteID)) {
                prepareGetField(fieldDesc);
            }

            updateBeforeImage(fieldDesc, null);
        }

        recordUpdatedField(fieldDesc);

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.prepareupdatefield.exit"); // NOI18N
        }
    }

    /**
     * Initializes the <code>beforeImage and registers the before image
     * value if not null.
     *
     * @param fieldDesc Updated field.
     * @param value BeforeImage value, null if called from
     * {@link #prepareUpdateField}. If the value is null it will be retrieved
     * from the instance.
     * @see #prepareUpdateFieldSpecial
     */
    private void updateBeforeImage(FieldDesc fieldDesc, Object value) {

        getBeforeImage();

        if (beforeImage != null
                && !beforeImage.getPresenceMaskBit(fieldDesc.absoluteID)
                && (fieldDesc.sqlProperties & FieldDesc.PROP_LOG_ON_UPDATE) > 0) {

            if (value == null) {
                value = fieldDesc.getValue(this);
            }

            if (value != null) {
                if (logger.isLoggable(Logger.FINEST)) {
                    Object[] items = new Object[] {fieldDesc, value};
                    logger.finest("sqlstore.sqlstatemanager.updatebeforeimage", items); // NOI18N
                }

                fieldDesc.setValue(beforeImage, cloneObjectMaybe(value));
                beforeImage.setPresenceMaskBit(fieldDesc.absoluteID);
            }
        }
    }

    /**
     * Marks the field <code>fieldDesc as set and schedules local fields
     * to be written to the data store.
     *
     * @param fieldDesc Updated field.
     */
    private void recordUpdatedField(FieldDesc fieldDesc) {
        boolean debug = logger.isLoggable(Logger.FINEST);

        if (!fieldDesc.isRelationshipField() &&
                (fieldDesc.sqlProperties & FieldDesc.PROP_RECORD_ON_UPDATE) > 0) {
            if (debug) {
                logger.finest("sqlstore.sqlstatemanager.recordingfield", fieldDesc); // NOI18N
            }
            getUpdateDesc().recordUpdatedField((LocalFieldDesc) fieldDesc);
        }

        if (debug) {
            logger.finest("sqlstore.sqlstatemanager.makedirtyfield", fieldDesc); // NOI18N
        }
        setSetMaskBit(fieldDesc.absoluteID);
    }

    /**
     * This method adds the dependency between this StateManager
     * and the other.
     *
     * @param sm Second state manager.
     * @see StateManager#addDependency(StateManager sm)
     */
    public void addDependency(StateManager sm) {

        if (logger.isLoggable()) {
            Object[] items = new Object[] {this, sm};
            logger.fine("sqlstore.sqlstatemanager.adddependency", items); // NOI18N
        }

        // The simple solution is to call addUpdatedForeignReference()
        // internally. It might try to reregister both instances again,
        // but the caller should clean up the cache. e.g. the
        // PersistenceManager MUST replace the deleted StateManager
        // with the new instance in the weak cache AFTER this call.

        SQLStateManager other = (SQLStateManager)sm;
        if (!state.isNew() || !state.isDeleted()) {
            // Do not need to add a dependency to a new-deleted instance as
            // its flush is a no-op any way.

            // First parameter == null marks a non removable dependency.
            this.addUpdatedForeignReference(null, other);
        } else if ((other.stateFlags & ST_REGISTERED) == 0) {

            // If we did not add a dependency, we still need to register the other
            // instance if it is not yet registered.
            persistenceManager.registerInstance(other, other.getObjectId(), false, true);
            other.stateFlags |= ST_REGISTERED;
        }
    }

    /**
     * Resolves the dependencies for the instances waiting for this state manager.
     * Dependencies are registered instantly during the course of the transaction.
     * For this reason, the introduced dependencies must be checked, if they are
     * still valid at commit/flush. E.g. remove dependencies introduced on
     * relationship removal are only valid, if the removed instance is deleted.
     * <p>
     * This method checks the dependencies for all instances waiting for the
     * current state manager to be flushed to the store.
     */
    public void resolveDependencies() {
        if (logger.isLoggable()) {
            logger.fine("sqlstore.sqlstatemanager.resolvedependencies", this.getPersistent()); // NOI18N
        }

        if (updatedForeignReferences != null) {
            Iterator iter = updatedForeignReferences.iterator();

            while (iter.hasNext()) {
                final UpdatedForeignReference ufr = (UpdatedForeignReference) iter.next();
                final ForeignFieldDesc fieldDesc = ufr.getFieldDesc();
                final SQLStateManager foreignSM = ufr.getStateManager();

                if (resolveDependency(fieldDesc, foreignSM)) {
                    foreignSM.removeDependency();
                    iter.remove();
                }
            }
        }
    }

    /**
     * Tries to resolve the dependency between <code>this
     * and <code>foreignSM introduced on the update of
     * relationship field <code>fieldDesc. There are three
     * kinds of dependencies:
     * <ul>
     * <li>Create dependency, see {@link #registerRemoveDependency}
     * <li>Remove dependency, see {@link #registerCreateDependency}
     * <li>Update dependency, see {@link #manageDependencyForObjectField}
     * </ul>
     * The current implementation does not attempt to resolve
     * <em>Update dependencies.
     *
     * @param fieldDesc Relationship field.
     * @param foreignSM Foreign state manager.
     * @return True, if the dependency between <code>this
     *   and <code>foreignSM can be safely removed.
     */
    private boolean resolveDependency(ForeignFieldDesc fieldDesc,
                                      SQLStateManager foreignSM) {
        boolean removeDependency = false;
        Object pc = foreignSM.getPersistent();

        if (!state.isPersistentInDataStore()) {
            // Check for a create dependency.
            // The create dependency is valid, if the instance is being created.
            if (state.getUpdateAction() != ActionDesc.LOG_CREATE) {
                // The instance is not persistent and will not be created.
                removeDependency = true;
            } else if (!checkRelationship(this, fieldDesc, pc)) {
                // No relationship between the two instances.
                removeDependency = true;
            }
        } else {
            // Not removable dependencies are marked by fieldDesc == null.
            // See delete/create with the same id dependency in addDependency
            // or update dependencies in manageDependencyForObjectField.
            // RESOLVE: Are update dependencies removable?
            if (fieldDesc != null) {
                // Check for a remove dependency.
                // The remove dependency is valid, if the formerly referred
                // instance is being removed.
                if (foreignSM.state.getUpdateAction() != ActionDesc.LOG_DESTROY) {
                    // The formerly referred instance will not be removed.
                    removeDependency = true;
                } else if (fieldDesc.cardinalityUPB <= 1) {
                    // Don't check collection relationships, as collection
                    // fields might not be populated in the before image.
                    // RESOLVE: Collection relationships shouldn't be
                    // checked for remove dependencies anyway.
                    if (!checkRelationship(beforeImage, fieldDesc, pc)) {
                        // No previous relationship between the two instances.
                        removeDependency = true;
                    }
                }
            }
        }

        if (removeDependency && logger.isLoggable()) {
            Object[] items = new Object[] {this.getPersistent(), fieldDesc.getName(), pc};
            logger.fine("sqlstore.sqlstatemanager.resolvedependency", items); // NOI18N
        }
        return removeDependency;
    }

    /**
     * Decrements the reference count and marks this instance
     * as updateable, if the reference count is zero.
     */
    private void removeDependency() {
        if (--referenceCount == 0) {
            stateFlags &= ~ST_UPDATE_DISABLED;
        }
    }

    /**
     * Checks, if there is a relationship between the persistence
     * capable instance managed by state manager <code>sm and
     * <code>pc on field fieldDesc. If
     * <code>sm represents a before image, the relationship was
     * reset in the current transaction.
     *
     * @param sm Either the before- or after image of the current state manager.
     * @param fieldDesc Relationship field.
     * @param pc Persistence capable instance checked for relationship.
     * @return True, if the persistence capable instances are (were)
     * related on the given field.
     */
    static private boolean checkRelationship(SQLStateManager sm,
                                             ForeignFieldDesc fieldDesc,
                                             Object pc) {
        boolean related = false;

        if (fieldDesc != null && sm != null
                && sm.getPresenceMaskBit(fieldDesc.absoluteID)) {

            if (fieldDesc.cardinalityUPB > 1) {
                // Checking directly for contains doesn't work for deferred SCOCollections.
                Collection c = sm.getCollectionValue(fieldDesc);

                // Resulting collection can't be null because the presence mask is set.
                related = c.contains(pc);
            } else {
                related = fieldDesc.getValue(sm) == pc;
            }
        }
        return related;
    }

    /**
     * Nullify the relationship in the data store before the _possible_ removal
     * of removedSM.  To maintain referentional integrity constraints in the
     * database, the relationship to the removed instance has to be nullified,
     * before the removed object might be deleted. Since we use immediate
     * dependency management, we don't know, if the removed object is deleted
     * later on. The data store relationship has to be erased before the
     * removal. Immediate dependency management determines dependencies
     * immediatly when the relationship is set. In contrast the deferred
     * approach waits until the objects are flushed to the store.  Set the
     * dependency only if both instances are already persistent.
     *
     * @param fieldDesc Updated relationship field.
     * @param removedSM State manager removed from the relationship.
     * @see #nullifyForeignKey(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc, boolean)
     * @see #removeJoinTableEntry(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc)
     */
    private void registerRemoveDependency(ForeignFieldDesc fieldDesc, SQLStateManager removedSM) {
        if (this.state.isPersistentInDataStore() &&
                removedSM.state.isPersistentInDataStore()) {

            this.addUpdatedForeignReference(fieldDesc, removedSM);
        }
    }

    /**
     * The referred object has to be written to the store before the
     * relationship can be set.  To ensure referentional integrity
     * constraints in the database, the added object has to be written
     * to the store, before the relationship can be set. The same
     * dependency applies for relationships mapped to a jointable.
     *
     * @param inverseFieldDesc Updated inverse relationship field.
     * WE must pass the inverse field, because the dependency is
     * registered on the added state manager. See
     * {@link #registerRemoveDependency}.
     * @param addedSM State manager added to the relationship.
     * @see #setForeignKey(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc)
     * @see #addJoinTableEntry(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc)
     */
    private void registerCreateDependency(ForeignFieldDesc inverseFieldDesc, SQLStateManager addedSM) {
        if (!addedSM.state.isPersistentInDataStore()) {
            addedSM.addUpdatedForeignReference(inverseFieldDesc, this);
        }
    }

    /**
     * Adds a dependency for state manager <code>sm. State
     * manager <code>sm must wait for this to be
     * flushed to the store before it can be written itself. State
     * manager <code>sm is added to the list of objects
     * depending on <code>this and will be notified when
     * <code>this is written to the store. Store updates to
     * <code>sm are disabled until then. This dependency is
     * established to maintain referential integrity conditions in the
     * data store.
     *
     * @param fieldDesc Updated relationship field.
     * @param sm Foreign state manager depending on <code>this.
     */
    private void addUpdatedForeignReference(ForeignFieldDesc fieldDesc, SQLStateManager sm) {

        // Avoid self-dependency.
        if (sm == this) {
            return;
        }

        // IMPORTANT: The following check assumes that this StateManager needs to be
        // registered only if it has no updatedForeignReferences. Check if this causes
        // a problem after this instance has been flushed, and updatedForeignReferences
        // is now an empty collection.
        if (updatedForeignReferences == null) {
            updatedForeignReferences = new HashSet();

            // Register this instance disregarding it's LifeCycle state.
            // Otherwise, its state may never be reset after the transaction commits.
            if ((stateFlags & ST_REGISTERED) == 0) {
                persistenceManager.registerInstance(this, getObjectId(), false, true);
                stateFlags |= ST_REGISTERED;
            }
        }

        if (updatedForeignReferences.add(new UpdatedForeignReference(fieldDesc, sm))) {
            sm.stateFlags |= ST_UPDATE_DISABLED;
            sm.referenceCount++;

            if (logger.isLoggable() ) {
                String fieldName = (fieldDesc != null) ? fieldDesc.getName() : null;
                Object[] items = new Object[] {this.persistentObject, fieldName,
                                               sm.persistentObject, new Integer(sm.referenceCount)};
                logger.fine("sqlstore.sqlstatemanager.addupdate", items); // NOI18N
            }

            // Register this instance disregarding it's LifeCycle state.
            // Otherwise, its state may never be reset after the transaction commits.
            if ((sm.stateFlags & ST_REGISTERED) == 0) {
                persistenceManager.registerInstance(sm, sm.getObjectId(), false, true);
                sm.stateFlags |= ST_REGISTERED;
            }
        }
    }

    /**
     * Removes the dependency from state manager <code>sm on
     * <code>this. State manager sm does not need to
     * wait for <code>this to be flushed to the store. The dependency
     * was established to maintain referential integrity conditions in the
     * data store.
     *
     * @param fieldDesc Updated relationship field.
     * @param sm Foreign state manager removed from the dependency.
     */
    private void removeUpdatedForeignReference(ForeignFieldDesc fieldDesc, SQLStateManager sm) {
        if ((updatedForeignReferences == null) ||
                (updatedForeignReferences.size() == 0)) {
            return;
        }

        if (updatedForeignReferences.remove(new UpdatedForeignReference(fieldDesc, sm))) {
            sm.referenceCount--;

            if (logger.isLoggable()) {
                String fieldName = (fieldDesc != null) ? fieldDesc.getName() : null;
                Object[] items = new Object[] {this.persistentObject, fieldName,
                                               sm.persistentObject, new Integer(sm.referenceCount)};
                logger.fine("sqlstore.sqlstatemanager.removeupdate", items); // NOI18N
            }

            if (sm.referenceCount == 0) {
                sm.stateFlags &= ~ST_UPDATE_DISABLED;
            }
        }
    }

    /**
     * Handles relationship updates for the object side of a one-to-many or both
     * sides of a one-to-one relationship. This method processes i.e. the
     * Employee side of the Employee-Department, or both sides
     * Employee-Insurance relationship. Nullifies the relation on the instance
     * removed from the relationship. The relation is set on the added instance
     * <code>value. Data store updates are scheduled. Updates the inverse
     * relationship side if <code>updateInverseRelationshipField == true
     * and the relationship is mapped as bi-directional by the user.
     *
     * @param fieldDesc Updated relationship field.
     * @param addedObject Relationship object to be set.
     * @param updateInverseRelationshipField
     *   True, if we need to update the inverse relationship side.
     * @param managedRelationshipInProgress True during relationship management.
     * @return Always true.
     * @exception JDOUserException
     *    Thrown if the added object <code>addedObject has already been deleted.
     */
    private boolean updateObjectField(ForeignFieldDesc fieldDesc,
                                      Object addedObject,
                                      boolean updateInverseRelationshipField,
                                      boolean managedRelationshipInProgress) {

        boolean debug = logger.isLoggable();

        if (debug) {
            Object[] items = new Object[] {fieldDesc.getName(),fieldDesc.getComponentType()};
            logger.fine("sqlstore.sqlstatemanager.updateobjfield", items); // NOI18N
        }

        Object removedObject = fieldDesc.getValue(this);

        // Don't do anything if the before and after value are the same.
        if (addedObject != removedObject) {

            SQLStateManager addedSM = getAddedSM(addedObject, null);
            SQLStateManager removedSM = getRemovedSM(removedObject);
            SQLStateManager addedInverseFieldSM = null;

            // If the new value is already deleted, we throw an exception.
            if (addedSM != null && addedSM.isDeleted()) {
                JDOUserException ex = new JDOUserException(I18NHelper.getMessage(messages,
                        "jdo.lifecycle.deleted.accessField")); // NOI18N
                ex.addFailedObject(addedObject);
                throw ex;
            }

            ForeignFieldDesc inverseFieldDesc = fieldDesc.getInverseRelationshipField();

            updateRelationshipInDataStore(fieldDesc, addedSM, removedSM,
                    inverseFieldDesc, managedRelationshipInProgress);

            if (updateInverseRelationshipField && inverseFieldDesc != null) {
                addedInverseFieldSM = manageRelationshipForObjectField(inverseFieldDesc, addedSM, removedSM,
                        managedRelationshipInProgress);
            }

            manageDependencyForObjectField(fieldDesc, addedSM, removedSM, addedInverseFieldSM);
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.updateobjfield.exit"); // NOI18N
        }

        return true;
    }

    /**
     * Updates the relationship in the data store. Updates the (hidden) local fields
     * corresponding to the foreign key columns if the relationship is mapped to a
     * foreign key. Jointable entries are scheduled for creation/removal if the
     * relationship is mapped to a jointable.
     *
     * @param fieldDesc Updated relationship field.
     * @param addedSM State manager of the added object.
     * @param removedSM State manager of the removed object.
     * @param inverseFieldDesc Inverse relationship field.
     * @param managedRelationshipInProgress
     *   True during relationship management. We don't want to update the
     *   relationship fields twice during relationship management.
     */
    private void updateRelationshipInDataStore(ForeignFieldDesc fieldDesc,
                                               SQLStateManager addedSM,
                                               SQLStateManager removedSM,
                                               ForeignFieldDesc inverseFieldDesc,
                                               boolean managedRelationshipInProgress) {

        if (!fieldDesc.useJoinTable()) {
            processForeignKeys(fieldDesc, addedSM, removedSM, inverseFieldDesc,
                    managedRelationshipInProgress);
        } else {
            processJoinTableEntries(fieldDesc, addedSM, removedSM, inverseFieldDesc,
                    managedRelationshipInProgress);
        }
    }

    /**
     * Updates the (hidden) local fields corresponding to the foreign key columns
     * if the relationship is mapped to a foreign key. The updates are written
     * to the store when the instance is flushed. For relationships mapped to
     * foreign keys, we always update the side with the foreign key.
     * The collection side never has the foreign key. Data store updates for
     * added objects are not processed twice during relationship management.
     *
     * Data store dependencies for the update operations are established.
     *
     * @param fieldDesc Updated relationship field.
     * @param addedSM State manager of the added object.
     * @param removedSM State manager of the removed object.
     * @param inverseFieldDesc Inverse relationship field.
     * @param managedRelationshipInProgress True during relationship management.
     */
    private void processForeignKeys(ForeignFieldDesc fieldDesc,
                                    SQLStateManager addedSM,
                                    SQLStateManager removedSM,
                                    ForeignFieldDesc inverseFieldDesc,
                                    boolean managedRelationshipInProgress) {

        // If the fieldDesc property has the REF_INTEGRITY_UPDATES unset, it means we
        // need to update the other side of the relationship naming the foreign key fields.
        boolean updateOtherSide = (fieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) == 0;

        if (updateOtherSide) {
            // Null out the foreign key on the removed object.
            if (removedSM != null) {
                removedSM.nullifyForeignKey(inverseFieldDesc, this, fieldDesc, false);
            }
            // Set the foreign key on the added object.
            // Don't set the fk twice during relationship management.
            if (addedSM != null && !managedRelationshipInProgress) {
                addedSM.setForeignKey(inverseFieldDesc, this, fieldDesc);
            }
        } else {
            // Null out the foreign key to the removed object.
            if (removedSM != null) {
                // Don't overwrite the foreign key, if both removedSM and
                // addedSM != null. See runtime test rel12 for an example.
                nullifyForeignKey(fieldDesc, removedSM, inverseFieldDesc, addedSM != null);
            }
            // Set the foreign key to the added object.
            // Don't set the fk twice during relationship management.
            if (addedSM != null && !managedRelationshipInProgress) {
                // See above!
                setForeignKey(fieldDesc, addedSM, inverseFieldDesc);
            }
        }
    }

    /**
     * Schedules jointable entries for relationships mapped to jointables.
     * The actual creation/removal of the jointable entry is deferred until
     * flush. Data store updates for added objects are not processed twice
     * during relationship management.
     *
     * Data store dependencies for the update operations are established.
     *
     * @param fieldDesc Updated relationship field.
     * @param addedSM State manager of the added object.
     * @param removedSM State manager of the removed object.
     * @param inverseFieldDesc Inverse relationship field.
     * @param managedRelationshipInProgress True during relationship management.
     */
    private void processJoinTableEntries(ForeignFieldDesc fieldDesc,
                                         SQLStateManager addedSM,
                                         SQLStateManager removedSM,
                                         ForeignFieldDesc inverseFieldDesc,
                                         boolean managedRelationshipInProgress) {

        // If the fieldDesc property has the REF_INTEGRITY_UPDATES unset,
        // it means we need to update the other side of the relationship.
        boolean updateOtherSide = (fieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) == 0;

        if (updateOtherSide) {
            // Schedule the removal of the jointable entry to removedSM.
            if (removedSM != null) {
                removedSM.removeJoinTableEntry(inverseFieldDesc, this, fieldDesc);
            }

            // Schedule the jointable entry to the added object.
            // Don't schedule the jointable entry twice during relationship management.
            if (addedSM != null && !managedRelationshipInProgress) {
                addedSM.addJoinTableEntry(inverseFieldDesc, this, fieldDesc);
            }
        } else {
            // Schedule the removal of the jointable entry to removedSM.
            if (removedSM != null) {
                // Contrary to foreign key relationships, we always have
                // to remove the previous jointable entry.
                removeJoinTableEntry(fieldDesc, removedSM, inverseFieldDesc);
            }

            // Schedule the jointable entry to the added object.
            // Don't schedule the jointable entry twice during relationship management.
            if (addedSM != null && !managedRelationshipInProgress) {
                addJoinTableEntry(fieldDesc, addedSM, inverseFieldDesc);
            }
        }
    }

    /**
     * Updates the (inverse) relationship field for <code>addedSM
     * and <code>removedSM.
     *
     * @param inverseFieldDesc Inverse relationship field.
     * @param addedSM State manager of the added object.
     * @param removedSM State manager of the removed object.
     * @param managedRelationshipInProgress True during relationship management.
     * @return State manager managing the previous value of addedSM's
     *   relationship field.
     */
    private SQLStateManager manageRelationshipForObjectField(ForeignFieldDesc inverseFieldDesc,
                                                             SQLStateManager addedSM,
                                                             SQLStateManager removedSM,
                                                             boolean managedRelationshipInProgress) {

        Object addedInverseFieldValue = null;
        SQLStateManager addedInverseFieldSM = null;

        if (removedSM != null) {
            removedSM.removeRelationship(inverseFieldDesc, this);
        }

        if (addedSM != null && !managedRelationshipInProgress) {
            addedInverseFieldValue = addedSM.addRelationship(inverseFieldDesc, this);

            if (addedInverseFieldValue != null) {
                addedInverseFieldSM = (SQLStateManager)
                        persistenceManager.getStateManager(addedInverseFieldValue);
            }
        }

        return addedInverseFieldSM;
    }

    /**
     * Dependency management for database operations on the one-to-one
     * relationships. Establishes a dependency to nullify the foreign key on the
     * removed instance before the added instance's foreign key can be set. If
     * the relationship is mapped to a jointable, remove the jointable entry to
     * the removed instance before the jointable entry on the added can be
     * added. This update dependency is valid only for one-to-one relationships,
     * because only one-to-one relationships can be enforced by an unique
     * index on the foreign key. On the other hand, there's never a state
     * manager removed from the relationship for one-to-many relationships.
     *
     * We would like to do this in updateRelationshipInDataStore,
     * but we don't know the added instance's previous value in case of
     * updateOtherSide == false there. Here, we can handle this situation
     * at one place.
     *
     * @param fieldDesc Updated relationship field.
     * @param addedSM Added state manager.
     * @param removedSM Removed state manager.
     * @param addedInverseFieldSM
     *  State manager removed from the relationship,
     *  if the foreign key is on the local side.
     */
    private void manageDependencyForObjectField(ForeignFieldDesc fieldDesc,
                                                SQLStateManager addedSM,
                                                SQLStateManager removedSM,
                                                SQLStateManager addedInverseFieldSM) {

        // If the fieldDesc property has the REF_INTEGRITY_UPDATES unset,
        // it means data store updates are scheduled on the other side of the relationship.
        boolean updateOtherSide = (fieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) == 0;

        // Nullify the foreign key on the removed object before
        // the added object's foreign key can be set.
        if (updateOtherSide && removedSM != null && addedSM != null) {

            // Add the dependency only if both objects involved
            // in the relationship being removed are already persistent.
            if (removedSM.state.isPersistentInDataStore()
                    && this.state.isPersistentInDataStore()) {

                // First parameter == null marks a non removable dependency.
                // RESOLVE: Pass inverseFieldDesc here.
                removedSM.addUpdatedForeignReference(null, addedSM);
            }
        }

        // If the foreign key is on this side, addedInverseFieldSM
        // corresponds to the removedSM and this is the addedSM!
        if (!updateOtherSide && addedInverseFieldSM != null) {

            // Add the dependency only if both objects involved
            // in the relationship being removed are already persistent.
            if (addedInverseFieldSM.state.isPersistentInDataStore()
                    && addedSM.state.isPersistentInDataStore()) {

                // First parameter == null marks a non removable dependency.
                // RESOLVE: Pass inverseFieldDesc here.
                addedInverseFieldSM.addUpdatedForeignReference(null, this);
            }
        }
    }

    /**
     * Returns the added object's state manager. Transient objects become
     * "autopersistent" on the association to an already persistent instance and
     * are associated with a new state manager.
     *
     * If <code>newlyRegisteredSMs is not null, the newly created state manager
     * is added to the list. This list is only non-null for the treatment of deferred
     * collection fields, which is done before the actual flush to the data store.
     * Autopersistence management for all other cases is handled sufficiently
     * in the makeAutoPersistent call.
     *
     * @param addedObject Object added to a relationship.
     * @param newlyRegisteredSMs
     *   The state managers of autopersistent objects will be added to this list.
     * @return State manager for the added object. The statemanager will
     *   be not null for all persistence capable objects != null.
     */
    private SQLStateManager getAddedSM(Object addedObject, ArrayList newlyRegisteredSMs) {
        SQLStateManager addedSM = null;

        if (addedObject != null) {
            // Persistence by reachablity.
            if ((addedSM = (SQLStateManager) persistenceManager.getStateManager(addedObject)) == null) {
                makeAutoPersistent(addedObject);
                addedSM = (SQLStateManager) persistenceManager.getStateManager(addedObject);

                // Add the newly created state manager to the newlyRegisteredSMs
                // list so we can do further processing on it.
                if (newlyRegisteredSMs != null && !newlyRegisteredSMs.contains(addedSM)) {
                    newlyRegisteredSMs.add(addedSM);
                }
            }
        }
        return addedSM;
    }

    /**
     * Returns the removed object's state manager.
     *
     * @param removedObject Object removed to a relationship.
     * @return State manager for the removed object. The state manager might
     *   be null even for objects != null (in case of JDK collections).
     * @see #removeCollectionRelationship
     */
    private SQLStateManager getRemovedSM(Object removedObject) {
        SQLStateManager removedSM = null;

        if (removedObject != null) {
            removedSM = (SQLStateManager) persistenceManager.getStateManager(removedObject);
        }
        return removedSM;
    }

    /**
     * Updates the relationship for the collection side of a one-to-many or
     * many-to-many relationship. Objects in <code>removedList are
     * removed from the relation. The relation is set on all objects in
     * <code>addedList. In case of user updates, relationship management
     * on "this" relationship side is done by the user, i.e.  by
     * d.getEmployees().add(newEmp) for a Department d. This method is never
     * called during relationship management.
     *
     * @param fieldDesc Updated relationship field.
     * @param removedList List of objects to be removed from the relationship.
     * @param addedList List of objects to be added to the relationship.
     * @param newlyRegisteredSMs
     *   List taking newly registered SMs for objects becoming autopersistent.
     * @param updateInverseRelationshipField
     *   True, if we need to update the inverse relationship field.
     * @param managedRelationshipInProgress
     *   True during relationship management. NOTE: This parameter is
     *   always false, as the method is never called during relationship management.
     * @exception JDOUserException  Thrown on failures in <code>afterList handling.
     * @see #prepareSetField(FieldDesc,Object,boolean)
     */
    private void processCollectionUpdates(ForeignFieldDesc fieldDesc,
                                          ArrayList removedList,
                                          ArrayList addedList,
                                          ArrayList newlyRegisteredSMs,
                                          boolean updateInverseRelationshipField,
                                          boolean managedRelationshipInProgress) {

        boolean debug = logger.isLoggable();
        ForeignFieldDesc inverseFieldDesc = fieldDesc.getInverseRelationshipField();

        // RESOLVE: What if
        // * inverseFieldDesc is null?
        // * fieldDesc.cardinalityUPB == 1

        if (debug) {
            Object[] items = new Object[] {removedList,addedList};
            logger.fine("sqlstore.sqlstatemanager.processcollectionupdate", items); // NOI18N
        }

        // removedList contains the list of objects removed.
        if (removedList != null) {
            removeCollectionRelationship(fieldDesc, removedList, inverseFieldDesc,
                    updateInverseRelationshipField, managedRelationshipInProgress);
        }

        // addedList contains the objects added.
        if (addedList != null) {
            addCollectionRelationship(fieldDesc, addedList, inverseFieldDesc,
                    newlyRegisteredSMs,
                    updateInverseRelationshipField, managedRelationshipInProgress);
        }

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.processcollectionupdate.exit"); // NOI18N
        }
    }

    /**
     * Nullifies the relationship for the objects removed from a collection relationship.
     *
     * @param fieldDesc Updated relationship field.
     * @param removedList List of objects to be removed from the relationship.
     * @param inverseFieldDesc Inverse relationship field.
     * @param updateInverseRelationshipField
     *   True, if we need to update the inverse relationship side.
     * @param managedRelationshipInProgress
     *   True during relationship management. NOTE: This parameter is always
     *   false, as the method is never called during relationship management.
     * @see #processCollectionUpdates
     */
    private void removeCollectionRelationship(ForeignFieldDesc fieldDesc,
                                              ArrayList removedList,
                                              ForeignFieldDesc inverseFieldDesc,
                                              boolean updateInverseRelationshipField,
                                              boolean managedRelationshipInProgress) {

        for (int i = 0; i < removedList.size(); i++) {
            SQLStateManager removedSM = getRemovedSM(removedList.get(i));

            // removedSM == null can happen if the collection is non-SCO and contains
            // transient instances which don't become persistent until commit.
            if (removedSM != null) {

                // The collection side never has the foreign key, i.e.
                // it's never processed during relationship management,
                // because data store updates are already done.
                if (!managedRelationshipInProgress) {
                    updateRelationshipInDataStore(fieldDesc, null, removedSM, inverseFieldDesc, false);

                    // Relationship management
                    if (updateInverseRelationshipField && inverseFieldDesc != null) {
                        removedSM.removeRelationship(inverseFieldDesc, this);
                    }
                }
            }
        }
    }

    /**
     * Nullifies the (hidden) local fields corresponding to the foreign key columns
     * for relationship field <code>fieldDesc. Usually the foreign key
     * columns are not mapped explicitly by the user. For this reason the runtime
     * creates hidden fields representing the foreign key columns internally. We
     * determine the local fields by iterating appropriate field list of either
     * <code>fieldDesc or inverseFieldDesc.
     *
     * For dependency management, the removal of the foreign key has
     * to be nullified before the _possible_ removal of removedSM. The
     * same dependency applies to jointable relationships, see {@link
     * #registerRemoveDependency}.
     *
     * @param fieldDesc Updated relationship field.
     * @param removedSM State manager of the removed object.
     * @param inverseFieldDesc Inverse relationship field.
     * @param setDependencyOnly Only set the dependency between <code>this
     *   and <code>removedSM.
     */
    private void nullifyForeignKey(ForeignFieldDesc fieldDesc,
                                   SQLStateManager removedSM,
                                   ForeignFieldDesc inverseFieldDesc,
                                   boolean setDependencyOnly) {

        if (!isDeleted() && !setDependencyOnly) {
            // fieldDesc can be null for one-directional relationships. We are
            // only interested in the LocalFieldDescs for the foreign key columns,
            // which can also be retrieved from inverseFieldDesc.
            if (fieldDesc != null) {
                for (int i = 0; i < fieldDesc.localFields.size(); i++) {
                    LocalFieldDesc la = (LocalFieldDesc) fieldDesc.localFields.get(i);

                    nullifyForeignKey(fieldDesc, la);
                }
            } else {
                for (int i = 0; i < inverseFieldDesc.foreignFields.size(); i++) {
                    LocalFieldDesc la = (LocalFieldDesc) inverseFieldDesc.foreignFields.get(i);

                    nullifyForeignKey(fieldDesc, la);
                 }
            }
        }

        // Nullify the foreign key before the _possible_ removal of removedSM.
        registerRemoveDependency(fieldDesc, removedSM);
    }

    /**
     * Actually nullifies the local field <code>la corresponding to a
     * foreign key column for the relationship update.
     * Fields tracking the foreign key field <code>la are updated.
     *
     * @param fieldDesc Updated relationship field.
     * @param la Local field corresponding to a foreign key column.
     * @exception JDOUserException Is thrown, if the field to be updated
     *   is a primary key field. We don't allow pk updates.
     */
    private void nullifyForeignKey(ForeignFieldDesc fieldDesc, LocalFieldDesc la) {

        if (!getSetMaskBit(la.absoluteID)) {
            prepareUpdateField(la, null);
        }

        JDOUserException pkUpdateEx = null;

        if (la.isKeyField()) {
            try {
                assertPKUpdate(la, null);
            } catch (JDOUserException e) {
                // If the relationship being set to null
                // and the parent instance is being deleted, we will ignore
                // the exception thrown from assertPKUpdate(). The reason is
                // that we are really just trying to set up the dependency
                // and not modify the relationship itself. If we do not
                // ignore this exception, there will be no way to delete
                // the parent instance at all because we don't support
                // modifying primary key.

                if (((stateFlags & ST_DELETE_INPROGRESS) == 0)) {
                    throw e;
                }

                pkUpdateEx = e;
            }
        }

        if (pkUpdateEx == null) {
            // As la tracks fieldDesc, fieldDesc is ignored in updateTrackedFields.
            updateTrackedFields(la, null, fieldDesc);
            la.setValue(this, null);
        }
    }

    /**
     * Schedules the removal of the jointable entry between this and the
     * foreign state manager. A scheduled creation of the jointable entry
     * between these two objects is simply removed. The removal is
     * scheduled on the local side.
     *
     * For dependency management, the removal of the jointable entry has
     * to precede the _possible_ removal of removedSM. The same dependency applies
     * to foreign key relationships, see {@link #registerRemoveDependency}.
     *
     * RESOLVE: What happens, if a field descriptor is null, e.g. for one
     * way relationships, as the descriptors are taken as keys during scheduling?
     *
     * @param fieldDesc Updated relationship field. This field is mapped to a jointable.
     * @param removedSM State manager of the removed object.
     * @param inverseFieldDesc Inverse relationship field.
     * @see #prepareToUpdatePhaseIII
     */
    private void removeJoinTableEntry(ForeignFieldDesc fieldDesc,
                                      SQLStateManager removedSM,
                                      ForeignFieldDesc inverseFieldDesc) {

        // Cleanup dependencies.  We need to cleanup dependencies for
        // flushed autopersistent instances that aren't reachable
        // before commit. See runtime test Autopersistence.TestCase12.
        // The cleanup must be done for removals only, because the
        // only operations executed in prepareToUpdatePhaseIII are
        // removals.
        if (removedSM.state.isAutoPersistent() || this.state.isAutoPersistent()) {
            removedSM.removeUpdatedForeignReference(inverseFieldDesc, this);
            this.removeUpdatedForeignReference(fieldDesc, removedSM);
        }

        // Remove scheduled creation on this side.
        if (fieldDesc != null && getUpdateDesc().removeUpdatedJoinTableRelationship(
                fieldDesc, removedSM, ActionDesc.LOG_CREATE) == false) {

            // Remove scheduled creation on the other side.
            if (inverseFieldDesc == null || removedSM.getUpdateDesc().removeUpdatedJoinTableRelationship(
                    inverseFieldDesc, this, ActionDesc.LOG_CREATE) == false) {

                // Schedule removal on this side.
                // The field descriptor taken as key must not be null, see above!
                getUpdateDesc().recordUpdatedJoinTableRelationship(
                        fieldDesc, this, removedSM, ActionDesc.LOG_DESTROY);

                // Remove the jointable entry before the _possible_ removal of removedSM.
                registerRemoveDependency(fieldDesc, removedSM);
            }
        } else if (fieldDesc == null) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.cantschedulejointable", // NOI18N
                    this.getPersistenceConfig().getPersistenceCapableClass().getName(),
                    removedSM.getPersistenceConfig().getPersistenceCapableClass().getName()));
        }
    }

    /**
     * Updates the relationship field <code>fieldDesc between this
     * and <code>removedSM.
     *
     * @param fieldDesc Updated relationship field. The field must be != null.
     * @param removedSM State manager of the removed object.
     */
    private void removeRelationship(ForeignFieldDesc fieldDesc, SQLStateManager removedSM) {

        boolean isCollection = (fieldDesc.cardinalityUPB > 1);

        if (!isCollection) {
            prepareUpdateFieldSpecial(fieldDesc, removedSM.persistentObject, false);
            updateTrackedFields(fieldDesc, null, null);
            fieldDesc.setValue(this, null);
        } else {
            try {
                prepareUpdateFieldSpecial(fieldDesc, null, true);
                SCOCollection c = (SCOCollection) fieldDesc.getValue(this);
                c.removeInternal(removedSM.persistentObject);
                updateTrackedFields(fieldDesc, c, null);
            } catch (ClassCastException e) {
                // ignore
            }
        }
    }

    /**
     * Sets the relationship for the objects added to a collection relationship.
     *
     * @param fieldDesc Updated relationship field.
     * @param addedList List of objects to be added to the relationship.
     * @param inverseFieldDesc Inverse relationship field.
     * @param newlyRegisteredSMs
     *   State managers for autopersistent objects will be added to this list.
     * @param updateInverseRelationshipField
     *   True, if we need to update the inverse relationship side.
     * @param managedRelationshipInProgress
     *   True during relationship management. NOTE: This parameter is always
     *   false, as the method is never called during relationship management.
     * @exception JDOUserException  Thrown if objects in <code>addedList have been deleted.
     * @see #processCollectionUpdates
     */
    private void addCollectionRelationship(ForeignFieldDesc fieldDesc,
                                           ArrayList addedList,
                                           ForeignFieldDesc inverseFieldDesc,
                                           ArrayList newlyRegisteredSMs,
                                           boolean updateInverseRelationshipField,
                                           boolean managedRelationshipInProgress) {

        JDOUserException ex = null;

        for (int i = 0; i < addedList.size(); i++) {
            Object addedObject = addedList.get(i);
            SQLStateManager addedSM = getAddedSM(addedObject, newlyRegisteredSMs);

            // addedSM == null can happen if the collection is non-SCO and contains
            // transient instances which don't become persistent until commit.
            if (addedSM != null) {

                if (addedSM.isDeleted()) {
                    // For managed relationships, if the addedObject is deleted, we need
                    // to throw an exception at the end and the exception should include
                    // the deleted objects in its failedObjectArray.
                    if (inverseFieldDesc != null) {
                        if (ex == null) {
                            ex = new JDOUserException(I18NHelper.getMessage(messages,
                                    "jdo.lifecycle.deleted.accessField")); // NOI18N
                        }

                        ex.addFailedObject(addedObject);
                    }
                    continue;
                }

                // The collection side never has the foreign key, i.e.
                // it's never processed during relationship management,
                // because data store updates are already done at that time.
                if (!managedRelationshipInProgress) {
                    updateRelationshipInDataStore(fieldDesc, addedSM, null, inverseFieldDesc, false);

                    // Relationship management
                    if (updateInverseRelationshipField && inverseFieldDesc != null) {
                        addedSM.addRelationship(inverseFieldDesc, this);
                    }
                }
            }
        }

        if (ex != null) {
            throw ex;
        }
    }

    /**
     * Sets the foreign key corresponding to the relationship field
     * <code>fieldDesc. Usually the foreign key columns are not mapped
     * explicitly by the user. For this reason the runtime creates hidden fields
     * representing the foreign key columns internally. We determine the local
     * fields by iterating appropriate field list of either
     * <code>fieldDesc or inverseFieldDesc.
     *
     * To ensure referentional integrity constraints in the database,
     * the added object has to be written to the store, before the
     * foreign key can be set. The same dependency applies to relationships
     * mapped to jointables, see {@link #addJoinTableEntry}.
     *
     * @param fieldDesc Updated relationship field.
     * @param addedSM State manager of the added object.
     * @param inverseFieldDesc Inverse relationship field.
     */
    private void setForeignKey(ForeignFieldDesc fieldDesc,
                               SQLStateManager addedSM,
                               ForeignFieldDesc inverseFieldDesc) {

        if (!isDeleted()) {
            // fieldDesc can be null for one-directional relationships. We are
            // only interested in the LocalFieldDescs for the foreign key columns,
            // which can also be retrieved from inverseFieldDesc.
            if (fieldDesc != null) {
                for (int i = 0; i < fieldDesc.localFields.size(); i++) {
                    LocalFieldDesc la = (LocalFieldDesc) fieldDesc.localFields.get(i);
                    LocalFieldDesc fa = (LocalFieldDesc) fieldDesc.foreignFields.get(i);

                    setForeignKey(fieldDesc, la, fa.getValue(addedSM));
                }
            } else {
                for (int i = 0; i < inverseFieldDesc.foreignFields.size(); i++) {
                    LocalFieldDesc la = (LocalFieldDesc) inverseFieldDesc.foreignFields.get(i);
                    LocalFieldDesc fa = (LocalFieldDesc) inverseFieldDesc.localFields.get(i);

                    setForeignKey(fieldDesc, la, fa.getValue(addedSM));
                }
            }
        }

        // The referred object has to be written to the store before the foreign key can be set.
        registerCreateDependency(inverseFieldDesc, addedSM);
    }

    /**
     * Actually sets the local field <code>la corresponding to a foreign
     * key column to the new value for the relationship update.  The new value
     * is taken from <code>fa, which is typically a primary key field on
     * the other relationship side.  Fields tracking the foreign key field
     * <code>la are updated.
     *
     * @param fieldDesc Updated relationship field.
     * @param la Local field corresponding to a foreign key column.
     * @param faValue Value of the local field corresponding to the primary
     *  key column on the other relationship side.
     * @exception JDOUserException Is thrown, if the field to be updated
     *   is a primary key field. We don't allow pk updates.
     */
    private void setForeignKey(ForeignFieldDesc fieldDesc,
                               LocalFieldDesc la,
                               Object faValue) {

        if (!getSetMaskBit(la.absoluteID)) {
            prepareUpdateField(la, null);
        }

        if (la.isKeyField()) {
            assertPKUpdate(la, faValue);
        }

        updateTrackedFields(la, faValue, fieldDesc);
        la.setValue(this, faValue);
    }

    /**
     * Schedules the creation of a jointable entry between this and the added
     * state manager. A scheduled removal of the jointable entry between these
     * two is simply removed. The creation is scheduled on the local side.
     *
     * For dependency management, the side creating the jointable entry has
     * to wait for the other to become persistent. The same dependency applies
     * to foreign key relationships, see {@link #setForeignKey}.
     *
     * RESOLVE: What happens, if a field descriptor is null, e.g. for one
     * way relationships, as the descriptors are taken as keys during scheduling?
     *
     * @param fieldDesc Updated relationship field. This field is mapped to a jointable.
     * @param addedSM State manager of the added object.
     * @param inverseFieldDesc Inverse relationship field.
     */
    private void addJoinTableEntry(ForeignFieldDesc fieldDesc,
                                   SQLStateManager addedSM,
                                   ForeignFieldDesc inverseFieldDesc) {

        // Cleanup dependencies.
        // Note: The following lines break deadlock detection for circular dependencies.
        //this.removeUpdatedForeignReference(addedSM);
        //addedSM.removeUpdatedForeignReference(this);

        // Remove scheduled removal on this side.
        if (fieldDesc != null && getUpdateDesc().removeUpdatedJoinTableRelationship(
                fieldDesc, addedSM, ActionDesc.LOG_DESTROY) == false) {

            // Remove scheduled removal on the other side.
            if (inverseFieldDesc == null || addedSM.getUpdateDesc().removeUpdatedJoinTableRelationship(
                    inverseFieldDesc, this, ActionDesc.LOG_DESTROY) == false) {

                // Schedule creation on this side.
                // The field descriptor taken as key must not be null, see above!
                getUpdateDesc().recordUpdatedJoinTableRelationship(
                        fieldDesc, this, addedSM, ActionDesc.LOG_CREATE);

                // The side creating the jointable entry has to wait for the other to become persistent.
                registerCreateDependency(inverseFieldDesc, addedSM);
            }
        } else if (fieldDesc == null) {
            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                    "core.statemanager.cantschedulejointable", // NOI18N
                    this.getPersistenceConfig().getPersistenceCapableClass().getName(),
                    addedSM.getPersistenceConfig().getPersistenceCapableClass().getName()));
        }
    }

    /**
     * Updates the relationship field <code>fieldDesc between this
     * and the state manager of the added object <code>addedSM.
     *
     * @param fieldDesc Updated relationship field. The field must be != null.
     * @param addedSM State manager of the added object.
     * @return Field <code>fieldDesc's previous value.
     */
    private Object addRelationship(ForeignFieldDesc fieldDesc,
                                   SQLStateManager addedSM) {

        Object previousValue = null;
        boolean isCollection = (fieldDesc.cardinalityUPB > 1);

        if (!isCollection) {
            previousValue = prepareSetField(fieldDesc, addedSM.persistentObject, true);
        } else {
            try {
                prepareUpdateFieldSpecial(fieldDesc, null, true);
                SCOCollection c = (SCOCollection) fieldDesc.getValue(this);

                // Note: c might be null during relationship management for a
                // self relationship, as in the Employee-Manager relation.
                // See runtime test AutoPersistence.TestCase31 for an example.
                if (c == null) {
                    replaceCollection(fieldDesc, null);
                    c = (SCOCollection) fieldDesc.getValue(this);
                }

                c.addInternal(addedSM.persistentObject);
                updateTrackedFields(fieldDesc, c, null);
            } catch (ClassCastException e) {
                // ignore
            }
        }

        return previousValue;
    }

    /**
     * This is a special version of prepareUpdateField that does not do navigation
     * if a field is not loaded. We don't need to reload the field, because the
     * before image value is given as parameter! This method is mostly called
     * during relatioship management, as the before image value is already know
     * in this case.
     * <p>
     * The <code>createDeferredCollection parameter should be true
     * only if <code>fieldDesc corresponds to a collection field. Note:
     * <ul>
     * <li>createDeferredCollection == false
     *   ==> <code>beforeImageValue must be non-null.
     * <li>createDeferredCollection == true
     *   ==> <code>beforeImageValue is null.
     * </ul>.
     *
     * @param fieldDesc The field to be prepared.
     * @param beforeImageValue The before image value.
     * @param createDeferredCollection
     *   Indicates whether to create a deferred SCOCollection. Deferred collections
     *   are created during relationship management if the inverse field is not
     *   loaded.
     * @see #prepareUpdateField
     */
    private synchronized void prepareUpdateFieldSpecial(FieldDesc fieldDesc,
                                                        Object beforeImageValue,
                                                        boolean createDeferredCollection) {
        if (fieldDesc.isKeyField()) {
            throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
                    "core.statemanager.nopkupdate")); // NOI18N
        }

        getUpdateDesc().markRelationshipChange(fieldDesc);

        boolean debug = logger.isLoggable();

        if (debug) {
            Object[] items = new Object[] {fieldDesc.getName(),state};
            logger.fine("sqlstore.sqlstatemanager.prepareupdatefieldspl", items); // NOI18N
        }

        boolean optimistic = persistenceManager.isOptimisticTransaction();
        boolean xactActive = persistenceManager.isActiveTransaction();
        boolean nontransactionalRead = persistenceManager.isNontransactionalRead();

        if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
            if (!optimistic) {
                persistenceManager.clearFields(this.persistentObject);
            }

            reload(null);
        }

        LifeCycleState oldstate = state;
        state = state.transitionWriteField(xactActive);
        registerInstance(false, null, oldstate);

        if (getSetMaskBit(fieldDesc.absoluteID)) {
            // Note: The set mask is set for all fields on make persistent.
            return;
        }

        if (!getPresenceMaskBit(fieldDesc.absoluteID)) {
            if (!createDeferredCollection) {
                if (!(beforeImageValue instanceof SCOCollection) ||
                        !((SCOCollection) beforeImageValue).isDeferred()) {

                    updateBeforeImage(fieldDesc, beforeImageValue);

                    // Set the presence mask for a non deferred collection.
                    setPresenceMaskBit(fieldDesc.absoluteID);
                }
            } else {
                // Deferred collection handling.
                if (!(fieldDesc instanceof ForeignFieldDesc) ||
                        (((ForeignFieldDesc) fieldDesc).cardinalityUPB <= 1)) {
                    //should throw an exception
                    return;
                }

                Object value = fieldDesc.getValue(this);

                if (value == null) {
                    // If the collection field is null, we need to create a
                    // deferred SCOCollection.
                    SCOCollection c = (SCOCollection) persistenceManager.newCollectionInstanceInternal(
                            fieldDesc.getType(),
                            persistentObject,
                            fieldDesc.getName(),
                            fieldDesc.getComponentType(),
                            false,
                            10);
                    c.markDeferred();
                    fieldDesc.setValue(this, c);
                }
                // NOTE: We don't set the presence mask bit for deferred collections,
                // because deferred collections MUST be reloaded on the first read access!
            }
        }

        recordUpdatedField(fieldDesc);

        if (debug) {
            logger.fine("sqlstore.sqlstatemanager.prepareupdatefieldspl.exit"); // NOI18N
        }
    }

    /**
     * Updates the values for fields tracking field
     * <code>fieldDesc.  Must be called before the new value
     * for field <code>fieldDesc is actually set.

* * If called when setting the local fields mapped to the * relationship on relationship updates, the relationship field * tracked by <code>fieldDesc must be ignored when * propagating the changes.<p> * * For overlapping pk/fk situations or if a fk column is * explicitly mapped to a visible field, the update of the local * field triggers the update of the relationship field tracking * the local field. * * @param fieldDesc Field whose tracked fields we wish to update. * @param value New value for the field. * @param fieldToIgnore Field to be ignored when propagating * changes. This is the relationship field tracked by field * <code>fieldDesc if fieldDesc is a * <b>hidden local field. */ private void updateTrackedFields(FieldDesc fieldDesc, Object value, ForeignFieldDesc fieldToIgnore) { ArrayList trackedFields = fieldDesc.getTrackedFields(); if (trackedFields == null) { return; } boolean debug = logger.isLoggable(Logger.FINEST); if (debug) { Object[] items = new Object[] {fieldDesc.getName(), value, ((fieldToIgnore != null) ? fieldToIgnore.getName() : null)}; logger.finest("sqlstore.sqlstatemanager.updatetrackedfields", items); // NOI18N } Object currentValue = fieldDesc.getValue(this); int size = trackedFields.size(); ArrayList fieldsToIgnore = ((fieldToIgnore != null) ? fieldToIgnore.getTrackedFields() : null); if (fieldDesc instanceof ForeignFieldDesc) { // For tracked relationship fields, we simply set the new value. for (int i = 0; i < size; i++) { ForeignFieldDesc tf = (ForeignFieldDesc) trackedFields.get(i); prepareUpdateFieldSpecial(tf, currentValue, false); tf.setValue(this, value); } } else { Object previousValues[] = new Object[size]; LocalFieldDesc primaryTrackedField = null; Object primaryTrackedFieldValue = null; if ((fieldDesc.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) > 0) { primaryTrackedField = (LocalFieldDesc) fieldDesc; primaryTrackedFieldValue = value; } for (int i = 0; i < size; i++) { FieldDesc tf = (FieldDesc) trackedFields.get(i); if (tf instanceof LocalFieldDesc) { Object convertedValue = null; Object convertedCurrentValue = null; // RESOLVE: SCODate is problematic because convertValue unsets // the owner. The SCO to be used for restoring is broken. try { convertedValue = tf.convertValue(value, this); convertedCurrentValue = tf.convertValue(currentValue, this); } catch (JDOUserException e) { // We got a conversion error. We need to revert all // the tracked fields to their previous values. // NOTE: We don't have to revert relationship fields // because they come after all the primitive fields. for (int j = 0; j < i; j++) { tf = (FieldDesc) trackedFields.get(j); tf.setValue(this, previousValues[j]); } throw e; } if ((tf.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) > 0) { primaryTrackedField = (LocalFieldDesc) tf; primaryTrackedFieldValue = convertedValue; } prepareUpdateFieldSpecial(tf, convertedCurrentValue, false); // save the previous values for rollback previousValues[i] = tf.getValue(this); tf.setValue(this, convertedValue); } else { // We bypass fieldToIgnore and its trackedFields if (((stateFlags & ST_FIELD_TRACKING_INPROGRESS) > 0) || (tf == fieldToIgnore) || ((fieldsToIgnore != null) && fieldsToIgnore.contains(tf))) { continue; } ForeignFieldDesc ftf = (ForeignFieldDesc) tf; Object pc = null; if (primaryTrackedFieldValue != null) { pc = getObjectById(ftf, primaryTrackedField, primaryTrackedFieldValue, false); } stateFlags |= ST_FIELD_TRACKING_INPROGRESS; prepareSetField(ftf, pc); stateFlags &= ~ST_FIELD_TRACKING_INPROGRESS; } } } if (debug) { logger.finest("sqlstore.sqlstatemanager.updatetrackedfields.exit"); // NOI18N } } /** * Looks up the object associated to this state manager on * relationship <code>ff field in the persistence manager cache. * The method first constructs the related instance's object id by * calling {@link ForeignFieldDesc#createObjectId}. Then asks the * persistence manager to retrieve the object associated to this * id from it's caches. If the referred object is not found, the * instance returned by {@link PersistenceManager#getObjectById(Object)} * is Hollow. Hollow instances are ignored for navigation. * * @param ff Relationship to be retrieved. The relationship must have * an object ("to one side") value. * @param updatedField Updated local field mapped to this relationship. * @param value <code>updatedField's new value. * @param forNavigation If true, the lookup is executed for navigation. * @return Object found in the cache. Null, if the object wasn't found. */ private Object getObjectById(ForeignFieldDesc ff, LocalFieldDesc updatedField, Object value, boolean forNavigation) { assert ff.cardinalityUPB <=1; // If called for navigation updatedField and value should be null. assert forNavigation ? updatedField == null && value == null : true; Object rc = null; Object oid = ff.createObjectId(this, updatedField, value); if (oid != null) { rc = persistenceManager.getObjectById(oid); LifeCycleState rcState = ((SQLStateManager) ((PersistenceCapable) rc).jdoGetStateManager()).state; if (forNavigation && (rcState instanceof Hollow)) { rc = null; } } return rc; } /** * Sets field <code>fieldDesc to value. * The update of relationship fields is triggered by either calling * {@link #updateCollectionField} or {@link #updateObjectField}, * depending on the field's cardinality. * * @param fieldDesc Field to be updated. * @param value New value. * @param managedRelationshipInProgress * True during relationship management. * @return Field <code>fieldDesc's previous value. */ private Object doUpdateField(FieldDesc fieldDesc, Object value, boolean managedRelationshipInProgress) { prepareUpdateField(fieldDesc, null); if (fieldDesc instanceof ForeignFieldDesc) { ForeignFieldDesc ff = (ForeignFieldDesc) fieldDesc; if (ff.cardinalityUPB > 1) { updateCollectionField(ff, (Collection) value, managedRelationshipInProgress); } else { updateObjectField(ff, value, true, managedRelationshipInProgress); } } updateTrackedFields(fieldDesc, value, null); Object currentValue = fieldDesc.getValue(this); fieldDesc.setValue(this, value); return currentValue; } private Object prepareSetField(int fieldID, Object value) { FieldDesc fieldDesc = persistenceConfig.getField(fieldID); return prepareSetField(fieldDesc, value, false, true); } private Object prepareSetField(FieldDesc fieldDesc, Object value) { return prepareSetField(fieldDesc, value, false, false); } /** * Internal method setting the new value <code>value for field * <code>fieldDesc during relationship management. Only called on * relationship additions for object fields. This method is never called * for collection fields. Relationship management for collection fields * is done by deferred collections in {@link #addRelationship}. * Deferred collections are implemented in <code>SCOCollection. * Relationship management on relationship removal is done in * {@link #removeRelationship} for both object and collection fields. * * @param fieldDesc Field to be updated. * @param value New value. * @param managedRelationshipInProgress Always true. * @return Field <code>fieldDesc's previous value. * @see com.sun.jdo.spi.persistence.support.sqlstore.SCOCollection */ private Object prepareSetField(FieldDesc fieldDesc, Object value, boolean managedRelationshipInProgress) { return prepareSetField(fieldDesc, value, managedRelationshipInProgress, false); } /** * Sets field <code>fieldDesc by calling * {@link SQLStateManager#doUpdateField(FieldDesc, Object, boolean)}. * * @param fieldDesc Field to be updated. * @param value New value. * @param managedRelationshipInProgress * True during relationship management. * @param acquireShareLock Acquire a shared lock during the update. * @return Field <code>fieldDesc's previous value. */ private Object prepareSetField(FieldDesc fieldDesc, Object value, boolean managedRelationshipInProgress, boolean acquireShareLock) { boolean debug = logger.isLoggable(Logger.FINEST); if (debug) { logger.finest("sqlstore.sqlstatemanager.preparesetfield", fieldDesc.getName()); // NOI18N } if (acquireShareLock) { persistenceManager.acquireShareLock(); } try { getLock(); if ((fieldDesc.sqlProperties & FieldDesc.PROP_READ_ONLY) > 0) { throw new JDOUserException(I18NHelper.getMessage(messages, "core.statemanager.readonly", fieldDesc.getName(), // NOI18N persistentObject.getClass().getName())); } // We need to lock fieldUpdateLock if there is a chance that // relationship field values might be affected. This is the case if // fieldDesc is a relationship field or it tracks other fields. if ((fieldDesc.getTrackedFields() != null) || (fieldDesc instanceof ForeignFieldDesc)) { persistenceManager.acquireFieldUpdateLock(); try { return doUpdateField(fieldDesc, value, managedRelationshipInProgress); } finally { persistenceManager.releaseFieldUpdateLock(); } } else { return doUpdateField(fieldDesc, value, managedRelationshipInProgress); } } catch (JDOException e) { throw e; } catch (Exception e) { logger.log(Logger.FINE,"sqlstore.exception.log", e); throw new JDOFatalInternalException(I18NHelper.getMessage(messages, "core.statemanager.setfieldfailed"), e); // NOI18N } finally { if (acquireShareLock) { persistenceManager.releaseShareLock(); } releaseLock(); if (debug) { logger.finest("sqlstore.sqlstatemanager.preparesetfield.exit"); // NOI18N } } } /** * Sets the new value for the collection field by calling * {@link #processCollectionUpdates}. * * @param fieldDesc Field descriptor of the field to be set. * @param value New value. * @param managedRelationshipInProgress * True during relationship management. */ private void updateCollectionField(ForeignFieldDesc fieldDesc, Collection value, boolean managedRelationshipInProgress) { boolean debug = logger.isLoggable(Logger.FINEST); if (debug) { Object[] items = new Object[] {value,((value == null)? "NO" : value.getClass().getName())}; // NOI18N logger.finest("sqlstore.sqlstatemanager.processforeignfield", items); // NOI18N } Object currVal = fieldDesc.getValue(this); // Do nothing if the current value is identical to the new value. if (currVal != value) { Object owner = null; ArrayList added = null; ArrayList removed = null; // Verify SCO owner and fieldName if any if (value != null && value instanceof SCOCollection) { SCOCollection sco = (SCOCollection) value; owner = sco.getOwner(); if (owner == null) { sco.setOwner(persistentObject, fieldDesc.getName(), fieldDesc.getComponentType()); } else if (owner != persistentObject || !fieldDesc.getName().equals(sco.getFieldName())) { throw new JDOUserException(I18NHelper.getMessage( messages, "core.statemanager.anotherowner"), // NOI18N new Object[]{owner, sco.getFieldName()}); } // SCO should not behave as a JDK collection, // but become owned and tracked at setXXX operation. added = new ArrayList(value); } Object befrVal = fieldDesc.getValue(beforeImage); if (currVal != null) { if (debug) logger.finest("sqlstore.sqlstatemanager.processforeignfield.remove"); // NOI18N // This is a setXXX (i.e. replace) operation, we need to // "remove" elements from the current SCOCollection and mark it as not used if (((Collection) currVal).size() > 0) { removed = new ArrayList((Collection) currVal); } if (currVal instanceof SCOCollection) { if (debug) logger.finest("sqlstore.sqlstatemanager.processforeignfield.reset"); // NOI18N // SCOCollection: mark it as not used ((SCO) currVal).unsetOwner(); } } else if (getSetMaskBit(fieldDesc.absoluteID) == false && befrVal != null) // && value instanceof SCOCollection && owner != null) { if (debug) logger.finest("sqlstore.sqlstatemanager.processforeignfield.remove_from_bi"); // NOI18N // Replace with SCOCollection: mark beforeImage as removed if (((Collection) befrVal).size() > 0) { removed = new ArrayList((Collection) befrVal); } } processCollectionUpdates(fieldDesc, removed, added, null, true, managedRelationshipInProgress); } } public Object clone() { SQLStateManager clone = new SQLStateManager(store, persistenceConfig); clone.persistenceManager = persistenceManager; return clone; } private void assertNotPK(int fieldNumber) { if (persistenceConfig.isPKField(fieldNumber)) throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages, "core.statemanager.nopkupdate")); // NOI18N } private void assertPKUpdate(FieldDesc f, Object value) { Object currentValue = f.getValue(this); boolean throwException = false; // We only throw an exception if the new value is actually different from // the current value. if ((value != null) && (currentValue != null)) { if (value.toString().compareTo(currentValue.toString()) != 0) { throwException = true; } } else if (value != currentValue) { throwException = true; } if (throwException) { throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages, "core.statemanager.nopkupdate")); // NOI18N } } /** * ... */ public com.sun.jdo.api.persistence.support.PersistenceManager getPersistenceManagerInternal() { return persistenceManager; } /** * ... */ public com.sun.jdo.api.persistence.support.PersistenceManager getPersistenceManager() { return (persistenceManager == null)? null : persistenceManager.getCurrentWrapper(); } /** * ... */ // !!! olsen: changed to return byte instead of void (->PC.jdoSetFlags()) public byte setFlags(byte flags) { // RESOLVE: Need to verify that the flags are valid with the current // state of the state manager. return flags; } /** * Triggers the state transition for READ and registers the * instance in the transaction cache. */ public void loadForRead() { boolean debug = logger.isLoggable(Logger.FINER); if (debug) { logger.finer("sqlstore.sqlstatemanager.loadforread"); // NOI18N } persistenceManager.acquireShareLock(); try { getLock(); byte oldFlags = persistenceManager.getFlags(persistentObject); // If the jdoFlag is either READ_OK or READ_WRITE_OK, that means another // thread might have already call loadForRead on this instance. if (oldFlags != LOAD_REQUIRED) { return; } try { boolean xactActive = persistenceManager.isActiveTransaction(); boolean optimistic = persistenceManager.isOptimisticTransaction(); boolean nontransactionalRead = persistenceManager.isNontransactionalRead(); if (state.needsReload(optimistic, nontransactionalRead, xactActive)) { reload(null); } LifeCycleState oldstate = state; state = state.transitionReadField(optimistic, nontransactionalRead, xactActive); persistenceManager.setFlags(persistentObject, READ_OK); registerInstance(false, null, oldstate); } catch (JDOException e) { // restore the jdoFlags. persistenceManager.setFlags(persistentObject, oldFlags); throw e; } } finally { persistenceManager.releaseShareLock(); releaseLock(); if (debug) { logger.finer("sqlstore.sqlstatemanager.loadforread.exit"); // NOI18N } } } /** * Triggers the state transition for WRITE and registers the instance * in the transaction cache. Prepares all DFG fields for update. */ public void loadForUpdate() { boolean debug = logger.isLoggable(Logger.FINER); if (debug) { logger.finer("sqlstore.sqlstatemanager.loadforupdate"); // NOI18N } persistenceManager.acquireShareLock(); try { getLock(); byte oldFlags = persistenceManager.getFlags(persistentObject); // If the jdoFlags is already set to READ_WRITE_OK, it means that anther // thread has called loadForUpdate on this instance. if (oldFlags == READ_WRITE_OK) { return; } persistenceManager.setFlags(persistentObject, READ_WRITE_OK); ArrayList fields = persistenceConfig.fields; try { // Mark all the fields in the dfg dirty. for (int i = 0; i < fields.size(); i++) { FieldDesc f = (FieldDesc) fields.get(i); if (f.fetchGroup == FieldDesc.GROUP_DEFAULT) { //prepareSetField(f, null); prepareUpdateField(f, null); } } } catch (JDOException e) { // restore the jdoFlags. persistenceManager.setFlags(persistentObject, oldFlags); throw e; } } finally { persistenceManager.releaseShareLock(); releaseLock(); if (debug) { logger.finer("sqlstore.sqlstatemanager.loadforupdate.exit"); // NOI18N } } } /** * This method serves two purposes: * 1. If the field value is null or contains a non-SCOCollection instance, it * creates a new SCOCollection and populates with elements in c. * 2. If the field value is a SCOCollection instance, then if it is deferred, * it calls applyDeferredUpdates on the collection passing in c. Otherwise, * it clears the collection and repopulates with elements in c. */ public synchronized void replaceCollection(ForeignFieldDesc ff, Collection c) { Collection collection = (Collection) ff.getValue(this); SCOCollection scoCollection = null; if ((collection == null) || !(collection instanceof SCO)) { scoCollection = (SCOCollection) persistenceManager.newCollectionInstanceInternal( ff.getType(), persistentObject, ff.getName(), ff.getComponentType(), false, ((c != null) ? c.size() : 0)); ff.setValue(this, scoCollection); scoCollection.addAllInternal(c); } else { scoCollection = (SCOCollection) collection; if (scoCollection.isDeferred()) { scoCollection.applyDeferredUpdates(c); // We need to mark all the tracked fields as present. ArrayList trackedFields = ff.getTrackedFields(); if (trackedFields != null) { for (int i = 0; i < trackedFields.size(); i++) { ForeignFieldDesc tf = (ForeignFieldDesc) trackedFields.get(i); setPresenceMaskBit(tf.absoluteID); } } } else { scoCollection.clearInternal(); scoCollection.addAllInternal(c); } } // Should not use old collection as SCO if any if (c != null && c instanceof SCO) { ((SCO) c).unsetOwner(); } } /** * For test purposes */ protected LifeCycleState getCurrentState() { return state; } // Status interrogation methods // For each one of these methods, there is a corresponding version // of it prefixed with jdo on the PersistenceCapable class. These // methods are used to query the state o an instance. For example, // when jdoIsReadReady is called on the PersistenceCapable // instance, the generated <code>jdoIsReadReady will delegate the // status interrogation to the <code>StateManager by call // <code>isReadReady(). /** * ... */ public boolean isDirty() { if (state != null) { return state.isDirty(); } return false; } /** * ... */ public boolean isTransactional() { if (state != null) { return state.isTransactional(); } return false; } /** * ... */ public boolean isNew() { if (state != null) { return state.isNew(); } return false; } /** * ... */ public boolean isDeleted() { if (state != null) { return state.isDeleted(); } return false; } /** * ... */ public boolean isPersistent() { if (state != null) { return state.isPersistent(); } return false; } /** * @inheritDoc */ public boolean needsRegisterWithVersionConsistencyCache() { boolean rc = hasVersionConsistency(); if (rc && state != null) { rc = state.isPersistent() && state.isTransactional() && !state.isNew() && !state.isDirty() && !state.isDeleted(); } return rc; } /** * @inheritDoc */ public boolean needsUpdateInVersionConsistencyCache() { boolean rc = hasVersionConsistency(); if (rc && state != null) { rc = (state.isDirty() || state.isNew() || persistenceConfig.hasLocalNonDFGFields()) && !state.isDeleted(); } return rc; } // Setter methods // These are methods for accessing the persistent field values // from the <code>StateManager. The setter methods can also // serve as the hook for keeping track of changes made to the // <code>StateManager. public boolean setBooleanField(int fieldNumber, boolean value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Boolean(value)); return value; } public boolean[] setBooleanArrayField(int fieldNumber, boolean[] value) { prepareSetField(fieldNumber, null); return value; } public byte setByteField(int fieldNumber, byte value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Byte(value)); return value; } public byte[] setByteArrayField(int fieldNumber, byte[] value) { prepareSetField(fieldNumber, null); return value; } public short setShortField(int fieldNumber, short value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Short(value)); return value; } public short[] setShortArrayField(int fieldNumber, short[] value) { prepareSetField(fieldNumber, null); return value; } public int setIntField(int fieldNumber, int value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Integer(value)); return value; } public int[] setIntArrayField(int fieldNumber, int[] value) { prepareSetField(fieldNumber, null); return value; } public long setLongField(int fieldNumber, long value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Long(value)); return value; } public long[] setLongArrayField(int fieldNumber, long[] value) { prepareSetField(fieldNumber, null); return value; } public char setCharField(int fieldNumber, char value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Character(value)); return value; } public char setCharArrayField(int fieldNumber, char value) { prepareSetField(fieldNumber, null); return value; } public float setFloatField(int fieldNumber, float value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Float(value)); return value; } public float[] setFloatArrayField(int fieldNumber, float[] value) { prepareSetField(fieldNumber, null); return value; } public double setDoubleField(int fieldNumber, double value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, new Double(value)); return value; } public double[] setDoubleArrayField(int fieldNumber, double[] value) { prepareSetField(fieldNumber, null); return value; } public String setStringField(int fieldNumber, String value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, value); return value; } public String[] setStringArrayField(int fieldNumber, String[] value) { prepareSetField(fieldNumber, null); return value; } /** * This method sets object fields, e.g. relationship fields. */ public Object setObjectField(int fieldNumber, Object value) { assertNotPK(fieldNumber); prepareSetField(fieldNumber, value); return value; } public Object[] setObjectArrayField(int fieldNumber, Object[] value) { prepareSetField(fieldNumber, value); return value; } public boolean testIsLoaded(int fieldNumber) { return getPresenceMaskBit(fieldNumber); } public boolean testIsLoaded(String fieldName) { FieldDesc f = persistenceConfig.getField(fieldName); return testIsLoaded(f.absoluteID); } public boolean testIsAutoPersistent() { return state.isAutoPersistent(); } /** * Marks this instance needs to require registering with the global (weak) cache * at rollback if it transitions to persistent state. * Used for replacing a deleted instance with the newly persistent with * the same object id. */ public void markNotRegistered() { needsRegisterAtRollback = true; } /** * Marks this instance as needs to be verified at the time it is removed from the * global (weak) cache at rollback if it transitions to transient state. */ public void markVerifyAtDeregister() { needsVerifyAtDeregister = true; } /** * Marks this instance as a replacement for a deleted instance with the same * ObjectId. */ public void markReplacement() { isReplacementInstance = true; } /** * Lock this instance. */ // For consistency's sake, this should be changed to acquireLock. public void getLock() { lock.acquire(); } /** * Release lock. */ public void releaseLock() { lock.release(); } /** * Return value for valid flag. */ public boolean isValid() { return valid; } /** * Mark this StateManager as valid. Called before returning from * getObjectById. */ public void setValid() { try { getLock(); valid = true; } finally { releaseLock(); } } /** * This class stores a database dependency between the current and * a foreign state manager. To resolve dependencies before * commit/flush, remember the relationship field intorducing this * dependency. */ private class UpdatedForeignReference { ForeignFieldDesc fieldDesc; SQLStateManager sm; private UpdatedForeignReference(ForeignFieldDesc fieldDesc, SQLStateManager sm) { this.fieldDesc = fieldDesc; this.sm = sm; } private ForeignFieldDesc getFieldDesc() { return fieldDesc; } private SQLStateManager getStateManager() { return sm; } public boolean equals(Object obj) { if (obj != null && this.getClass().equals(obj.getClass())) { final UpdatedForeignReference other = (UpdatedForeignReference) obj; return (this.fieldDesc == other.fieldDesc && this.sm == other.sm); } return (false); } public int hashCode() { int hashCode = sm.hashCode(); if (fieldDesc != null) { hashCode += fieldDesc.hashCode(); } return hashCode; } } }

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish SQLStateManager.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.