All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.kernel.StateManagerImpl Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */
package org.apache.openjpa.kernel;

import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TimeZone;

import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.enhance.DynamicPersistenceCapable;
import org.apache.openjpa.enhance.FieldManager;
import org.apache.openjpa.enhance.ManagedInstanceProvider;
import org.apache.openjpa.enhance.PCRegistry;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.RedefinitionHelper;
import org.apache.openjpa.enhance.StateManager;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.event.LifecycleEventManager;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FetchGroup;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.UpdateStrategies;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.Exceptions;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.InvalidStateException;
import org.apache.openjpa.util.ObjectNotFoundException;
import org.apache.openjpa.util.OpenJPAId;
import org.apache.openjpa.util.ProxyManager;
import org.apache.openjpa.util.RuntimeExceptionTranslator;
import org.apache.openjpa.util.UserException;
import serp.util.Numbers;

/**
 * Implementation of the {@link OpenJPAStateManager} interface for use
 * with this runtime. Each state manager manages the state of a single
 * persistence capable instance. The state manager is also responsible for
 * all communications about the instance to the {@link StoreManager}.
 *  The state manager uses the State pattern in both its interaction with
 * the governed instance and its interaction with the broker.
 * In its interactions with the persistence capable instance, it uses the
 * {@link FieldManager} interface. Similarly, when interacting with the
 * broker, it uses the {@link PCState} singleton that represents
 * the current lifecycle state of the instance.
 *
 * @author Abe White
 */
public class StateManagerImpl
    implements OpenJPAStateManager, Serializable {

    public static final int LOAD_FGS = 0;
    public static final int LOAD_ALL = 1;
    public static final int LOAD_SERIALIZE = 2;

    private static final int FLAG_SAVE = 2 << 0;
    private static final int FLAG_DEREF = 2 << 1;
    private static final int FLAG_LOADED = 2 << 2;
    private static final int FLAG_READ_LOCKED = 2 << 3;
    private static final int FLAG_WRITE_LOCKED = 2 << 4;
    private static final int FLAG_OID_ASSIGNED = 2 << 5;
    private static final int FLAG_LOADING = 2 << 6;
    private static final int FLAG_PRE_DELETING = 2 << 7;
    private static final int FLAG_FLUSHED = 2 << 8;
    private static final int FLAG_PRE_FLUSHED = 2 << 9;
    private static final int FLAG_FLUSHED_DIRTY = 2 << 10;
    private static final int FLAG_IMPL_CACHE = 2 << 11;
    private static final int FLAG_INVERSES = 2 << 12;
    private static final int FLAG_NO_UNPROXY = 2 << 13;
    private static final int FLAG_VERSION_CHECK = 2 << 14;
    private static final int FLAG_VERSION_UPDATE = 2 << 15;
    private static final int FLAG_DETACHING = 2 << 16;

    private static final Localizer _loc = Localizer.forPackage
        (StateManagerImpl.class);

    // information about the instance
    private transient PersistenceCapable _pc = null;
    private transient ClassMetaData _meta = null;
    private BitSet _loaded = null;
    private BitSet _dirty = null;
    private BitSet _flush = null;
    private int _flags = 0;

    // id is the state manager identity; oid is the persistent identity.  oid
    // may be null for embedded and transient-transactional objects or new
    // instances that haven't been assigned an oid.  id is reassigned to oid
    // on successful oid assignment (or flush completion if assignment is
    // during flush)
    private Object _id = null;
    private Object _oid = null;

    // the managing persistence manager and lifecycle state
    private transient BrokerImpl _broker; // this is serialized specially
    private PCState _state = PCState.TRANSIENT;

    // the current and last loaded version indicators, and the lock object
    private Object _version = null;
    private Object _loadVersion = null;
    private Object _lock = null;
    private int _readLockLevel = -1;
    private int _writeLockLevel = -1;

    // delegates when providing/replacing instance data
    private SingleFieldManager _single = null;
    private SaveFieldManager _saved = null;
    private FieldManager _fm = null;

    // impldata; field impldata and intermediate data share the same array
    private Object _impl = null;
    private Object[] _fieldImpl = null;

    // information about the owner of this instance, if it is embedded
    private StateManagerImpl _owner = null;
    private int _ownerIndex = -1;

    /**
     * Constructor; supply id, type metadata, and owning persistence manager.
     */
    protected StateManagerImpl(Object id, ClassMetaData meta, 
        BrokerImpl broker) {
        _id = id;
        _meta = meta;
        _broker = broker;
        _single = new SingleFieldManager(this, broker);

        if (_meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
            throw new UserException(_loc.get("meta-unknownid", _meta));
    }

    /**
     * Set the owning state and field if this is an embedded instance.
     */
    void setOwner(StateManagerImpl owner, ValueMetaData ownerMeta) {
        _owner = owner;
        _ownerIndex = ownerMeta.getFieldMetaData().getIndex();
    }

    /**
     * Whether this state manager is in the middle of a load.
     */
    boolean isLoading() {
        return (_flags & FLAG_LOADING) > 0;
    }

    /**
     * Whether this state manager is in the middle of a load initiated
     * by outside code; for any internal methods that cause loading, the
     * loading flag is set automatically.
     */
    void setLoading(boolean loading) {
        if (loading)
            _flags |= FLAG_LOADING;
        else
            _flags &= ~FLAG_LOADING;
    }

    /**
     * Set or reset the lifecycle state of the managed instance. If the
     * transactional state of the instance changes, it will be enlisted/
     * delisted from the current transaction as necessary. The given
     * state will be initialized after being set. If the given state
     * is the same as the current state, this method will have no effect.
     */
    private void setPCState(PCState state) {
        if (_state == state)
            return;

        lock();
        try {
            // notify the store manager that we're changing states; can veto
            _broker.getStoreManager().beforeStateChange(this, _state, state);

            // replace state
            boolean wasDeleted = _state.isDeleted();
            boolean wasDirty = _state.isDirty();
            boolean wasPending = _state.isPendingTransactional();
            _state = state;

            // enlist/delist from transaction
            if (_state.isTransactional()) {
                _broker.addToTransaction(this);
                if (_state.isDeleted() != wasDeleted)
                    _broker.setDirty(this, !wasDirty || isFlushed());
                else if (_state.isDirty() && !wasDirty)
                    _broker.setDirty(this, true);
            } else if (!wasPending && _state.isPendingTransactional())
                _broker.addToPendingTransaction(this);
            else if (wasPending && !_state.isPendingTransactional())
                _broker.removeFromPendingTransaction(this);
            else
                _broker.removeFromTransaction(this);

            // initialize
            _state.initialize(this);
            if (_state.isDeleted() && !wasDeleted)
                fireLifecycleEvent(LifecycleEvent.AFTER_DELETE);
        } finally {
            unlock();
        }
    }

    //////////////////////////////////////
    // OpenJPAStateManager implementation
    //////////////////////////////////////

    public void initialize(Class cls, PCState state) {
        // check to see if our current object id instance is the
        // correct id type for the specified class; this is for cases
        // when we have an application id hierarchy and we had set the
        // metadata to a superclass id -- the subclass' id may be a
        // different class, so we need to reset it
        if (_meta.getDescribedType() != cls) {
            ClassMetaData sub = _meta.getRepository().getMetaData
                (cls, _broker.getClassLoader(), true);
            if (_oid != null) {
                if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
                    _oid = _broker.getStoreManager().copyDataStoreId(_oid,
                        sub);
                else if (_meta.isOpenJPAIdentity())
                    _oid = ApplicationIds.copy(_oid, sub);
                else if (sub.getObjectIdType() != _meta.getObjectIdType()) {
                    Object[] pkFields = ApplicationIds.toPKValues(_oid, _meta);
                    _oid = ApplicationIds.fromPKValues(pkFields, sub);
                }
            }
            _meta = sub;
        }

        PersistenceCapable inst = PCRegistry.newInstance(cls, this, _oid, true);
        if (inst == null) {
            // the instance was null: check to see if the instance is
            // abstract (as can sometimes be the case when the
            // class discriminator strategy is not configured correctly)
            if (Modifier.isAbstract(cls.getModifiers()))
                throw new UserException(_loc.get("instantiate-abstract",
                    cls.getName(), _oid));
            throw new InternalException();
        }

        initialize(inst, state);
    }

    /**
     * Initialize with the given instance and state.
     */
    protected void initialize(PersistenceCapable pc, PCState state) {
        if (pc == null)
            throw new UserException(_loc.get("init-null-pc", _meta));
        if (pc.pcGetStateManager() != null && pc.pcGetStateManager() != this)
            throw new UserException(_loc.get("init-sm-pc",
                Exceptions.toString(pc))).setFailedObject(pc);
        pc.pcReplaceStateManager(this);

        FieldMetaData[] fmds = _meta.getFields();
        _loaded = new BitSet(fmds.length);
        _flush = new BitSet(fmds.length);
        _dirty = new BitSet(fmds.length);

        for (int i = 0; i < fmds.length; i++) {
            // mark primary key and non-persistent fields as loaded
            if (fmds[i].isPrimaryKey()
                || fmds[i].getManagement() != fmds[i].MANAGE_PERSISTENT)
                _loaded.set(i);

            // record whether there are any managed inverse fields
            if (_broker.getInverseManager() != null
                && fmds[i].getInverseMetaDatas().length > 0)
                _flags |= FLAG_INVERSES;
        }

        pc.pcSetDetachedState(null);
        _pc = pc;

        if (_oid instanceof OpenJPAId)
            ((OpenJPAId) _oid).setManagedInstanceType(_meta.getDescribedType());

        // initialize our state and add ourselves to the broker's cache
        setPCState(state);
        if ( _oid == null ||  
            _broker.getStateManagerImplById(_oid, false) == null) {
        	_broker.setStateManager(_id, this, BrokerImpl.STATUS_INIT);
        }
        if (state == PCState.PNEW)
            fireLifecycleEvent(LifecycleEvent.AFTER_PERSIST);

        // if this is a non-tracking PC, add a hard ref to the appropriate data
        // sets and give it an opportunity to make a state snapshot.
        if (!isIntercepting()) {
            saveFields(true);
            if (!isNew())
                RedefinitionHelper.assignLazyLoadProxies(this);
        }
    }

    /**
     * Whether or not data access in this instance is intercepted. This differs
     * from {@link ClassMetaData#isIntercepting()} in that it checks for
     * property access + subclassing in addition to the redefinition /
     * enhancement checks.
     *
     * @since 1.0.0
     */
    public boolean isIntercepting() {
        if (getMetaData().isIntercepting())
            return true;
        if (getMetaData().getAccessType() != ClassMetaData.ACCESS_FIELD
            && _pc instanceof DynamicPersistenceCapable)
            return true;

        return false;
    }

    /**
     * Fire the given lifecycle event to all listeners.
     */
    private boolean fireLifecycleEvent(int type) {
        return _broker.fireLifecycleEvent(getManagedInstance(), null,
            _meta, type);
    }

    public void load(FetchConfiguration fetch) {
        load(fetch, LOAD_FGS, null, null, false);
    }

    /**
     * Load the state of this instance based on the given fetch configuration
     * and load mode. Return true if any data was loaded, false otherwise.
     */
    protected boolean load(FetchConfiguration fetch, int loadMode,
        BitSet exclude, Object sdata, boolean forWrite) {
        if (!forWrite && (!isPersistent() || isNew() || isDeleted()))
            return false;

        // if any fields being loaded, do state transitions for read
        BitSet fields = getUnloadedInternal(fetch, loadMode, exclude);
        boolean active = _broker.isActive();
        if (!forWrite && fields != null)
            beforeRead(-1);

        // call load even if no fields are being loaded, because it takes
        // care of checking if the DFG is loaded, making sure version info
        // is loaded, etc
        int lockLevel = calculateLockLevel(active, forWrite, fetch);
        boolean ret = loadFields(fields, fetch, lockLevel, sdata);
        obtainLocks(active, forWrite, lockLevel, fetch, sdata);
        return ret;
    }

    public Object getManagedInstance() {
        if (_pc instanceof ManagedInstanceProvider)
            return ((ManagedInstanceProvider) _pc).getManagedInstance();
        else
            return _pc;
    }

    public PersistenceCapable getPersistenceCapable() {
        return _pc;
    }

    public ClassMetaData getMetaData() {
        return _meta;
    }

    public OpenJPAStateManager getOwner() {
        return _owner;
    }

    public int getOwnerIndex() {
        return _ownerIndex;
    }

    public boolean isEmbedded() {
        return _owner != null;
    }

    public boolean isFlushed() {
        return (_flags & FLAG_FLUSHED) > 0;
    }

    public boolean isFlushedDirty() {
        return (_flags & FLAG_FLUSHED_DIRTY) > 0;
    }

    public BitSet getLoaded() {
        return _loaded;
    }

    public BitSet getFlushed() {
        return _flush;
    }

    public BitSet getDirty() {
        return _dirty;
    }

    public BitSet getUnloaded(FetchConfiguration fetch) {
        // collect fields to load from data store based on fetch configuration
        BitSet fields = getUnloadedInternal(fetch, LOAD_FGS, null);
        return (fields == null) ? new BitSet(0) : fields;
    }

    /**
     * Internal version of {@link OpenJPAStateManager#getUnloaded} that avoids
     * creating an empty bit set by returning null when there are no unloaded
     * fields.
     */
    private BitSet getUnloadedInternal(FetchConfiguration fetch, int mode,
        BitSet exclude) {
        if (exclude == StoreContext.EXCLUDE_ALL)
            return null;

        BitSet fields = null;
        FieldMetaData[] fmds = _meta.getFields();
        boolean load;
        for (int i = 0; i < fmds.length; i++) {
            if (_loaded.get(i) || (exclude != null && exclude.get(i)))
                continue;

            switch (mode) {
                case LOAD_SERIALIZE:
                    load = !fmds[i].isTransient();
                    break;
                case LOAD_FGS:
                    load = fetch == null || fetch.requiresFetch(fmds[i]) 
                        != FetchConfiguration.FETCH_NONE;
                    break;
                default: // LOAD_ALL
                    load = true;
            }

            if (load) {
                if (fields == null)
                    fields = new BitSet(fmds.length);
                fields.set(i);
            }
        }
        return fields;
    }

    public StoreContext getContext() {
        return _broker;
    }

    /**
     * Managing broker.
     */
    BrokerImpl getBroker() {
        return _broker;
    }

    public Object getId() {
        return _id;
    }

    public Object getObjectId() {
        StateManagerImpl sm = this;
        while (sm.getOwner() != null)
            sm = (StateManagerImpl) sm.getOwner();
        return sm._oid;
    }

    public void setObjectId(Object oid) {
        _oid = oid;
        if (_pc != null && oid instanceof OpenJPAId)
            ((OpenJPAId) oid).setManagedInstanceType(_meta.getDescribedType());
    }

    public boolean assignObjectId(boolean flush) {
        lock();
        try {
            return assignObjectId(flush, false);
        } finally {
            unlock();
        }
    }

    /**
     * Ask store manager to assign our oid, optionally flushing and
     * optionally recaching on the new oid.
     */
    boolean assignObjectId(boolean flush, boolean preFlushing) {
        if (_oid != null || isEmbedded() || !isPersistent())
            return true;

        if (_broker.getStoreManager().assignObjectId(this, preFlushing)) {
            if (!preFlushing)
                assertObjectIdAssigned(true);
        } else if (flush)
            _broker.flush();
        else
            return false;
        return true;
    }

    /**
     * Make sure we were assigned an oid, and perform actions to make it
     * permanent.
     *
     * @param recache whether to recache ourself on the new oid
     */
    private void assertObjectIdAssigned(boolean recache) {
        if (!isNew() || isDeleted() || isProvisional() 
            || (_flags & FLAG_OID_ASSIGNED) != 0)
            return;
        if (_oid == null) {
            if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
                throw new InternalException(Exceptions.toString
                    (getManagedInstance()));
            _oid = ApplicationIds.create(_pc, _meta);
        }

        Object orig = _id;
        _id = _oid;
        if (recache) {
            try {
                _broker.setStateManager(orig, this,
                    BrokerImpl.STATUS_OID_ASSIGN);
            } catch (RuntimeException re) {
                _id = orig;
                _oid = null;
                throw re;
            }
        }
        _flags |= FLAG_OID_ASSIGNED;
    }

    /**
     * Assign the proper generated value to the given field based on its
     * value-strategy.
     */
    private boolean assignField(int field, boolean preFlushing) {
        OpenJPAStateManager sm = this;
        while (sm.isEmbedded())
            sm = sm.getOwner();
        if (!sm.isNew() || sm.isFlushed() || sm.isDeleted())
            return false;

        // special-case oid fields, which require us to look inside the oid
        // object
        FieldMetaData fmd = _meta.getField(field);
        if (fmd.getDeclaredTypeCode() == JavaTypes.OID) {
            // try to shortcut if possible
            if (_oid != null || isEmbedded() || !isPersistent())
                return true;

            // check embedded fields of oid for value strategy + default value
            FieldMetaData[] pks = fmd.getEmbeddedMetaData().getFields();
            OpenJPAStateManager oidsm = null;
            boolean assign = false;
            for (int i = 0; !assign && i < pks.length; i++) {
                if (pks[i].getValueStrategy() == ValueStrategies.NONE)
                    continue;
                if (oidsm == null)
                    oidsm = new ObjectIdStateManager(fetchObjectField(field),
                        this, fmd);
                assign = oidsm.isDefaultValue(i);
            }
            return assign && assignObjectId(!preFlushing, preFlushing);
        }

        // Just return if there's no value generation strategy
        if (fmd.getValueStrategy() == ValueStrategies.NONE)
            return false;
        
        // Throw exception if field already has a value assigned.
        // @GeneratedValue overrides POJO initial values and setter methods
        if (!fmd.isValueGenerated() && !isDefaultValue(field))
            throw new InvalidStateException(_loc.get(
                "existing-value-override-excep", fmd.getFullName(false)));

        // for primary key fields, assign the object id and recache so that
        // to the user, so it looks like the oid always matches the pk fields
        if (fmd.isPrimaryKey() && !isEmbedded())
            return assignObjectId(!preFlushing, preFlushing);

        // for other fields just assign the field or flush if needed
        if (_broker.getStoreManager().assignField(this, field, preFlushing)) {
            fmd.setValueGenerated(true);
            return true;
        }
        if (!preFlushing)
            _broker.flush();
        return !preFlushing;
    }

    public Object getLock() {
        return _lock;
    }

    public void setLock(Object lock) {
        _lock = lock;
    }

    public Object getVersion() {
        return _version;
    }

    public void setVersion(Object version) {
        _loadVersion = version;
        assignVersionField(version);
    }

    Object getLoadVersion() {
        return _loadVersion;
    }

    public void setNextVersion(Object version) {
        assignVersionField(version);
    }

    private void assignVersionField(Object version) {
        _version = version;
        FieldMetaData vfield = _meta.getVersionField();
        if (vfield != null)
            store(vfield.getIndex(), JavaTypes.convert(version,
                vfield.getTypeCode()));
    }

    public PCState getPCState() {
        return _state;
    }

    public synchronized Object getImplData() {
        return _impl;
    }

    public synchronized Object setImplData(Object data, boolean cacheable) {
        Object old = _impl;
        _impl = data;
        if (cacheable && data != null)
            _flags |= FLAG_IMPL_CACHE;
        else
            _flags &= ~FLAG_IMPL_CACHE;
        return old;
    }

    public boolean isImplDataCacheable() {
        return (_flags & FLAG_IMPL_CACHE) != 0;
    }

    public Object getImplData(int field) {
        return getExtraFieldData(field, true);
    }

    public Object setImplData(int field, Object data) {
        return setExtraFieldData(field, data, true);
    }

    public synchronized boolean isImplDataCacheable(int field) {
        if (_fieldImpl == null || !_loaded.get(field))
            return false;
        if (_meta.getField(field).usesImplData() != null)
            return false;
        int idx = _meta.getExtraFieldDataIndex(field);
        return idx != -1 && _fieldImpl[idx] != null;
    }

    public Object getIntermediate(int field) {
        return getExtraFieldData(field, false);
    }

    public void setIntermediate(int field, Object data) {
        setExtraFieldData(field, data, false);
    }

    /**
     * Return the data from the proper index of the extra field data array.
     */
    private synchronized Object getExtraFieldData(int field, boolean isLoaded) {
        // only return the field data if the field is in the right loaded
        // state; otherwise we might return intermediate for impl data or
        // vice versa
        if (_fieldImpl == null || _loaded.get(field) != isLoaded)
            return null;
        int idx = _meta.getExtraFieldDataIndex(field);
        return (idx == -1) ? null : _fieldImpl[idx];
    }

    /**
     * Set the data from to proper index of the extra field data array.
     */
    private synchronized Object setExtraFieldData(int field, Object data,
        boolean loaded) {
        int idx = _meta.getExtraFieldDataIndex(field);
        if (idx == -1)
            throw new InternalException(String.valueOf(_meta.getField(field)));

        Object old = (_fieldImpl == null) ? null : _fieldImpl[idx];
        if (data != null) {
            // cannot set if field in wrong loaded state
            if (_loaded.get(field) != loaded)
                throw new InternalException(String.valueOf(_meta.getField
                    (field)));

            // set data
            if (_fieldImpl == null)
                _fieldImpl = new Object[_meta.getExtraFieldDataLength()];
            _fieldImpl[idx] = data;
        } else if (_fieldImpl != null && _loaded.get(field) == loaded)
            _fieldImpl[idx] = null;
        return old;
    }

    public Object fetch(int field) {
        Object val = fetchField(field, false);
        return _meta.getField(field).getExternalValue(val, _broker);
    }

    public Object fetchField(int field, boolean transitions) {
        FieldMetaData fmd = _meta.getField(field);
        if (fmd == null)
            throw new UserException(_loc.get("no-field",
                String.valueOf(field), getManagedInstance().getClass())).
                setFailedObject(getManagedInstance());

        // do normal state transitions
        if (!fmd.isPrimaryKey() && transitions)
            accessingField(field);

        switch (fmd.getDeclaredTypeCode()) {
            case JavaTypes.STRING:
                return fetchStringField(field);
            case JavaTypes.OBJECT:
                return fetchObjectField(field);
            case JavaTypes.BOOLEAN:
                return (fetchBooleanField(field)) ? Boolean.TRUE
                    : Boolean.FALSE;
            case JavaTypes.BYTE:
                return new Byte(fetchByteField(field));
            case JavaTypes.CHAR:
                return new Character(fetchCharField(field));
            case JavaTypes.DOUBLE:
                return new Double(fetchDoubleField(field));
            case JavaTypes.FLOAT:
                return new Float(fetchFloatField(field));
            case JavaTypes.INT:
                return Numbers.valueOf(fetchIntField(field));
            case JavaTypes.LONG:
                return Numbers.valueOf(fetchLongField(field));
            case JavaTypes.SHORT:
                return new Short(fetchShortField(field));
            default:
                return fetchObjectField(field);
        }
    }

    public void store(int field, Object val) {
        val = _meta.getField(field).getFieldValue(val, _broker);
        storeField(field, val);
    }

    public void storeField(int field, Object val) {
        storeField(field, val, this);
    }

    /**
     * 

Checks whether or not _pc is dirty. In the cases where * field tracking is not happening (see below), this method will do a * state comparison to find whether _pc is dirty, and will * update this instance with this information. In the cases where field * tracking is happening, this method is a no-op.

* *

Fields are tracked for all classes that are run through the OpenJPA * enhancer prior to or during deployment, and all classes (enhanced or * unenhanced) in a Java 6 environment or newer.

* *

In a Java 5 VM or older: *
- instances of unenhanced classes that use * property access and obey the property access limitations are tracked * when the instances are loaded from the database by OpenJPA, and are * not tracked when the instances are created by application code. *
- instances of unenhanced classes that use field access are * never tracked.

* * @since 1.0.0 */ public void dirtyCheck() { if (!needsDirtyCheck()) return; SaveFieldManager saved = getSaveFieldManager(); if (saved == null) throw new InternalException(_loc.get("no-saved-fields", getMetaData().getDescribedType().getName())); FieldMetaData[] fmds = getMetaData().getFields(); for (int i = 0; i < fmds.length; i++) { // pk and version fields cannot be mutated; don't mark them // as such. ##### validate? if (!fmds[i].isPrimaryKey() && !fmds[i].isVersion() && _loaded.get(i)) { if (!saved.isFieldEqual(i, fetch(i))) { dirty(i); } } } } private boolean needsDirtyCheck() { if (isIntercepting()) return false; if (isDeleted()) return false; if (isNew() && !isFlushed()) return false; return true; } public Object fetchInitialField(int field) { FieldMetaData fmd = _meta.getField(field); if (_broker.getRestoreState() == RestoreState.RESTORE_NONE && ((_flags & FLAG_INVERSES) == 0 || fmd.getInverseMetaDatas().length == 0)) throw new InvalidStateException(_loc.get("restore-unset")); switch (fmd.getDeclaredTypeCode()) { case JavaTypes.DATE: case JavaTypes.CALENDAR: case JavaTypes.ARRAY: case JavaTypes.COLLECTION: case JavaTypes.MAP: case JavaTypes.OBJECT: // if we're not saving mutable types, throw an exception if (_broker.getRestoreState() != RestoreState.RESTORE_ALL && ((_flags & FLAG_INVERSES) == 0 || fmd.getInverseMetaDatas().length == 0)) throw new InvalidStateException(_loc.get ("mutable-restore-unset")); } lock(); try { if (_saved == null || !_loaded.get(field) || !_dirty.get(field)) return fetchField(field, false); // if the field is dirty but we never loaded it, we can't restore it if (_saved.getUnloaded().get(field)) throw new InvalidStateException(_loc.get("initial-unloaded", fmd)); provideField(_saved.getState(), _single, field); return fetchField(_single, fmd); } finally { unlock(); } } /** * Fetch the specified field from the specified field manager, wrapping it * in an object if it's a primitive. A field should be provided to the * field manager before this call is made. */ private static Object fetchField(FieldManager fm, FieldMetaData fmd) { int field = fmd.getIndex(); switch (fmd.getDeclaredTypeCode()) { case JavaTypes.BOOLEAN: return (fm.fetchBooleanField(field)) ? Boolean.TRUE : Boolean.FALSE; case JavaTypes.BYTE: return new Byte(fm.fetchByteField(field)); case JavaTypes.CHAR: return new Character(fm.fetchCharField(field)); case JavaTypes.DOUBLE: return new Double(fm.fetchDoubleField(field)); case JavaTypes.FLOAT: return new Float(fm.fetchFloatField(field)); case JavaTypes.INT: return Numbers.valueOf(fm.fetchIntField(field)); case JavaTypes.LONG: return Numbers.valueOf(fm.fetchLongField(field)); case JavaTypes.SHORT: return new Short(fm.fetchShortField(field)); case JavaTypes.STRING: return fm.fetchStringField(field); default: return fm.fetchObjectField(field); } } public void setRemote(int field, Object value) { lock(); try { Boolean stat = dirty(field, Boolean.FALSE, false); storeField(field, value, _single); replaceField(_pc, _single, field); postDirty(stat); } finally { unlock(); } } //////////////////////// // Lifecycle operations //////////////////////// /** * Notification that the object is about to be accessed. * * @param field the field number being read, or -1 if not a single * field read */ void beforeRead(int field) { // allow unmediated reads of primary key fields if (field != -1 && _meta.getField(field).isPrimaryKey()) return; if (_broker.isActive() && !_broker.isTransactionEnding()) { if (_broker.getOptimistic()) setPCState(_state.beforeOptimisticRead(this, field)); else setPCState(_state.beforeRead(this, field)); } else if (_broker.getNontransactionalRead()) setPCState(_state.beforeNontransactionalRead(this, field)); else throw new InvalidStateException(_loc.get("non-trans-read")). setFailedObject(getManagedInstance()); } /** * Delegates to the current state. * * @see PCState#beforeFlush */ void beforeFlush(int reason, OpCallbacks call) { _state.beforeFlush(this, reason == BrokerImpl.FLUSH_LOGICAL, call); } /** * Delegates to the current state. * * @see PCState#flush */ void afterFlush(int reason) { // nothing happens when we flush non-persistent states if (!isPersistent()) return; if (reason != BrokerImpl.FLUSH_ROLLBACK && reason != BrokerImpl.FLUSH_LOGICAL) { // analyze previous state for later boolean wasNew = isNew(); boolean wasFlushed = isFlushed(); boolean wasDeleted = isDeleted(); // all dirty fields were flushed _flush.or(_dirty); // important to set flushed bit after calling _state.flush so // that the state can tell whether this is the first flush setPCState(_state.flush(this)); _flags |= FLAG_FLUSHED; _flags &= ~FLAG_FLUSHED_DIRTY; _flags &= ~FLAG_VERSION_CHECK; _flags &= ~FLAG_VERSION_UPDATE; // if this was an inc flush during which we had our identity // assigned, tell the broker to cache us under our final oid if (reason == BrokerImpl.FLUSH_INC) assertObjectIdAssigned(true); // if this object was stored with preFlush, do post-store callback if ((_flags & FLAG_PRE_FLUSHED) > 0) fireLifecycleEvent(LifecycleEvent.AFTER_STORE); // do post-update as needed if (wasNew && !wasFlushed) fireLifecycleEvent(LifecycleEvent.AFTER_PERSIST_PERFORMED); else if (wasDeleted) fireLifecycleEvent(LifecycleEvent.AFTER_DELETE_PERFORMED); else // updates and new-flushed with changes fireLifecycleEvent(LifecycleEvent.AFTER_UPDATE_PERFORMED); } else if (reason == BrokerImpl.FLUSH_ROLLBACK) { // revert to last loaded version and original oid assignVersionField(_loadVersion); if (isNew() && (_flags & FLAG_OID_ASSIGNED) == 0) _oid = null; } _flags &= ~FLAG_PRE_FLUSHED; } /** * Delegates to the current state after checking the value * of the RetainState flag. * * @see PCState#commit * @see PCState#commitRetain */ void commit() { // release locks before oid updated releaseLocks(); // update version and oid information setVersion(_version); _flags &= ~FLAG_FLUSHED; _flags &= ~FLAG_FLUSHED_DIRTY; Object orig = _id; assertObjectIdAssigned(false); boolean wasNew = isNew() && !isDeleted() && !isProvisional(); if (_broker.getRetainState()) setPCState(_state.commitRetain(this)); else setPCState(_state.commit(this)); // ask the broker to re-cache us if we were new previously if (wasNew) _broker.setStateManager(orig, this, BrokerImpl.STATUS_COMMIT_NEW); } /** * Delegates to the current state after checking the value * of the RetainState flag. * * @see PCState#rollback * @see PCState#rollbackRestore */ void rollback() { // release locks releaseLocks(); _flags &= ~FLAG_FLUSHED; _flags &= ~FLAG_FLUSHED_DIRTY; afterFlush(BrokerImpl.FLUSH_ROLLBACK); if (_broker.getRestoreState() != RestoreState.RESTORE_NONE) setPCState(_state.rollbackRestore(this)); else setPCState(_state.rollback(this)); } /** * Rollback state of the managed instance to the given savepoint. */ void rollbackToSavepoint(SavepointFieldManager savepoint) { _state = savepoint.getPCState(); BitSet loaded = savepoint.getLoaded(); for (int i = 0, len = loaded.length(); i < len; i++) { if (loaded.get(i) && savepoint.restoreField(i)) { provideField(savepoint.getCopy(), savepoint, i); replaceField(_pc, savepoint, i); } } _loaded = loaded; _dirty = savepoint.getDirty(); _flush = savepoint.getFlushed(); _version = savepoint.getVersion(); _loadVersion = savepoint.getLoadVersion(); } /** * Delegates to the current state. * * @see PCState#persist * @see Broker#persist */ void persist() { setPCState(_state.persist(this)); } /** * Delegates to the current state. * * @see PCState#delete * @see Broker#delete */ void delete() { setPCState(_state.delete(this)); } /** * Delegates to the current state. * * @see PCState#nontransactional * @see Broker#nontransactional */ void nontransactional() { setPCState(_state.nontransactional(this)); } /** * Delegates to the current state. * * @see PCState#transactional * @see Broker#transactional */ void transactional() { setPCState(_state.transactional(this)); } /** * Delegates to the current state. * * @see PCState#nonprovisional */ void nonprovisional(boolean logical, OpCallbacks call) { setPCState(_state.nonprovisional(this, logical, call)); } /** * Delegates to the current state. * * @see PCState#release * @see Broker#release */ void release(boolean unproxy) { release(unproxy, false); } void release(boolean unproxy, boolean force) { // optimization for detach-in-place special case when fields are // already (un)proxied correctly if (!unproxy) _flags |= FLAG_NO_UNPROXY; try { if (force) setPCState(PCState.TRANSIENT); else setPCState(_state.release(this)); } finally { _flags &= ~FLAG_NO_UNPROXY; } } /** * Delegates to the current state. * * @see PCState#evict * @see Broker#evict */ void evict() { setPCState(_state.evict(this)); } /** * Gather relations reachable from values using * {@link ValueMetaData#CASCADE_IMMEDIATE}. */ void gatherCascadeRefresh(OpCallbacks call) { FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) { if (!_loaded.get(i)) continue; if (fmds[i].getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE || fmds[i].getKey().getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE || fmds[i].getElement().getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE) { _single.storeObjectField(i, fetchField(i, false)); _single.gatherCascadeRefresh(call); _single.clear(); } } } public boolean beforeRefresh(boolean refreshAll) { // note: all logic placed here rather than in the states for // optimization; this method public b/c used by remote package // nothing to do for non persistent or new unflushed instances if (!isPersistent() || (isNew() && !isFlushed())) return false; lock(); try { // if dirty need to clear fields if (isDirty()) { clearFields(); return true; } // if some fields have been loaded but the instance is out of // date or this is part of a refreshAll() and we don't want to // take the extra hit to see if the instance is out of date, clear if (_loaded.length() > 0 && (refreshAll || isEmbedded() || !syncVersion(null))) { Object version = _version; clearFields(); // if syncVersion just replaced the version, reset it if (!refreshAll && !isEmbedded()) setVersion(version); return true; } return false; } finally { unlock(); } } /** * Perform state transitions after refresh. This method is only * called if {@link #beforeRefresh} returns true. */ void afterRefresh() { lock(); try { // transition to clean or nontransactional depending on trans status if (!_broker.isActive()) setPCState(_state.afterNontransactionalRefresh()); else if (_broker.getOptimistic()) setPCState(_state.afterOptimisticRefresh()); else setPCState(_state.afterRefresh()); } finally { unlock(); } } /** * Mark this object as a dereferenced dependent object. */ void setDereferencedDependent(boolean deref, boolean notify) { if (!deref && (_flags & FLAG_DEREF) > 0) { if (notify) _broker.removeDereferencedDependent(this); _flags &= ~FLAG_DEREF; } else if (deref && (_flags & FLAG_DEREF) == 0) { _flags |= FLAG_DEREF; if (notify) _broker.addDereferencedDependent(this); } } /////////// // Locking /////////// /** * Notification that we've been read-locked. Pass in the level at which * we were locked and the level at which we should write lock ourselves * on dirty. */ void readLocked(int readLockLevel, int writeLockLevel) { // make sure object is added to transaction so lock will get // cleared on commit/rollback if (readLockLevel != LockLevels.LOCK_NONE) transactional(); _readLockLevel = readLockLevel; _writeLockLevel = writeLockLevel; _flags |= FLAG_READ_LOCKED; _flags &= ~FLAG_WRITE_LOCKED; } /** * Return the lock level to use when loading state. */ private int calculateLockLevel(boolean active, boolean forWrite, FetchConfiguration fetch) { if (!active) return LockLevels.LOCK_NONE; if (fetch == null) fetch = _broker.getFetchConfiguration(); if (_readLockLevel == -1) _readLockLevel = fetch.getReadLockLevel(); if (_writeLockLevel == -1) _writeLockLevel = fetch.getWriteLockLevel(); return (forWrite) ? _writeLockLevel : _readLockLevel; } /** * Make sure we're locked at the given level. */ private void obtainLocks(boolean active, boolean forWrite, int lockLevel, FetchConfiguration fetch, Object sdata) { if (!active) return; // if we haven't been locked yet, lock now at the given level int flag = (forWrite) ? FLAG_WRITE_LOCKED : FLAG_READ_LOCKED; if ((_flags & flag) == 0) { // make sure object is added to transaction so lock will get // cleared on commit/rollback if (lockLevel != LockLevels.LOCK_NONE) transactional(); if (fetch == null) fetch = _broker.getFetchConfiguration(); _broker.getLockManager().lock(this, lockLevel, fetch.getLockTimeout(), sdata); _flags |= FLAG_READ_LOCKED; _flags |= flag; } } /** * Release locks. */ private void releaseLocks() { if (_lock != null) _broker.getLockManager().release(this); _readLockLevel = -1; _writeLockLevel = -1; _flags &= ~FLAG_READ_LOCKED; _flags &= ~FLAG_WRITE_LOCKED; } //////////////////////////////////////////// // Implementation of StateManager interface //////////////////////////////////////////// /** * @return whether or not unloaded fields should be closed. */ public boolean serializing() { // if the broker is in the midst of a serialization, then no special // handling should be performed on the instance, and no subsequent // load should happen if (_broker.isSerializing()) return false; try { if (_meta.isDetachable()) return DetachManager.preSerialize(this); load(_broker.getFetchConfiguration(), LOAD_SERIALIZE, null, null, false); return false; } catch (RuntimeException re) { throw translate(re); } } public boolean writeDetached(ObjectOutput out) throws IOException { BitSet idxs = new BitSet(_meta.getFields().length); lock(); try { boolean detsm = DetachManager.writeDetachedState(this, out, idxs); if (detsm) _flags |= FLAG_DETACHING; FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) { if (fmds[i].isTransient()) continue; provideField(_pc, _single, i); _single.serialize(out, !idxs.get(i)); _single.clear(); } return true; } catch (RuntimeException re) { throw translate(re); } finally { _flags &= ~FLAG_DETACHING; unlock(); } } public void proxyDetachedDeserialized(int idx) { // we don't serialize state manager impls throw new InternalException(); } public boolean isTransactional() { // special case for TCLEAN, which we want to appear non-trans to // internal code, but which publicly should be transactional return _state == PCState.TCLEAN || _state.isTransactional(); } public boolean isPendingTransactional() { return _state.isPendingTransactional(); } public boolean isProvisional() { return _state.isProvisional(); } public boolean isPersistent() { return _state.isPersistent(); } public boolean isNew() { return _state.isNew(); } public boolean isDeleted() { return _state.isDeleted(); } public boolean isDirty() { return _state.isDirty(); } public boolean isDetached() { return (_flags & FLAG_DETACHING) != 0; } public Object getGenericContext() { return _broker; } public Object fetchObjectId() { try { assignObjectId(true); if (_oid == null || !_broker.getConfiguration(). getCompatibilityInstance().getCopyObjectIds()) return _oid; if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE) return _broker.getStoreManager().copyDataStoreId(_oid, _meta); return ApplicationIds.copy(_oid, _meta); } catch (RuntimeException re) { throw translate(re); } } public Object getPCPrimaryKey(Object oid, int field) { FieldMetaData fmd = _meta.getField(field); Object pk = ApplicationIds.get(oid, fmd); if (pk == null) return null; ClassMetaData relmeta = fmd.getDeclaredTypeMetaData(); pk = ApplicationIds.wrap(relmeta, pk); if (relmeta.getIdentityType() == ClassMetaData.ID_DATASTORE && fmd.getObjectIdFieldTypeCode() == JavaTypes.LONG) pk = _broker.getStoreManager().newDataStoreId(pk, relmeta); else if (relmeta.getIdentityType() == ClassMetaData.ID_APPLICATION && fmd.getObjectIdFieldType() != relmeta.getObjectIdType()) pk = ApplicationIds.fromPKValues(new Object[] { pk }, relmeta); return _broker.find(pk, false, null); } public byte replaceFlags() { // we always use load required so that we can detect when objects // are touched for locking or making transactional return PersistenceCapable.LOAD_REQUIRED; } public StateManager replaceStateManager(StateManager sm) { return sm; } public void accessingField(int field) { // possibly change state try { beforeRead(field); beforeAccessField(field); } catch (RuntimeException re) { throw translate(re); } } /** * Load the given field before access. */ protected void beforeAccessField(int field) { lock(); try { boolean active = _broker.isActive(); int lockLevel = calculateLockLevel(active, false, null); if (!_loaded.get(field)) loadField(field, lockLevel, false, true); else assignField(field, false); obtainLocks(active, false, lockLevel, null, null); } catch (RuntimeException re) { throw translate(re); } finally { unlock(); } } public void dirty(String field) { FieldMetaData fmd = _meta.getField(field); if (fmd == null) throw translate(new UserException(_loc.get("no-field", field, ImplHelper.getManagedInstance(_pc).getClass())) .setFailedObject(getManagedInstance())); dirty(fmd.getIndex(), null, true); } public void dirty(int field) { dirty(field, null, true); } /** * Make the given field dirty. * * @param mutate if null, may be an SCO mutation; if true, is certainly * a mutation (or at least treat as one) * @return {@link Boolean#FALSE} if this instance was already dirty, * null if it was dirty but not since flush, and * {@link Boolean#TRUE} if it was not dirty */ private Boolean dirty(int field, Boolean mutate, boolean loadFetchGroup) { boolean locked = false; boolean newFlush = false; boolean clean = false; try { FieldMetaData fmd = _meta.getField(field); if (!isNew() || isFlushed()) { if (fmd.getUpdateStrategy() == UpdateStrategies.RESTRICT) throw new InvalidStateException(_loc.get ("update-restrict", fmd)); if (fmd.getUpdateStrategy() == UpdateStrategies.IGNORE) return Boolean.FALSE; } if (isEmbedded()) { // notify owner of change _owner.dirty(_ownerIndex, Boolean.TRUE, loadFetchGroup); } // is this a direct mutation of an sco field? if (mutate == null) { switch (fmd.getDeclaredTypeCode()) { case JavaTypes.COLLECTION: case JavaTypes.MAP: case JavaTypes.ARRAY: case JavaTypes.DATE: case JavaTypes.CALENDAR: case JavaTypes.OBJECT: mutate = Boolean.TRUE; break; case JavaTypes.PC: mutate = (fmd.isEmbedded()) ? Boolean.TRUE : Boolean.FALSE; break; default: mutate = Boolean.FALSE; // not sco } } // possibly change state boolean active = _broker.isActive(); clean = !_state.isDirty(); // intentional direct access // fire event fast before state change. if (clean) fireLifecycleEvent(LifecycleEvent.BEFORE_DIRTY); if (active) { if (_broker.getOptimistic()) setPCState(_state.beforeOptimisticWrite(this, field, mutate.booleanValue())); else setPCState(_state.beforeWrite(this, field, mutate.booleanValue())); } else if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT) { if (isPersistent() && !_broker.getNontransactionalWrite()) throw new InvalidStateException(_loc.get ("non-trans-write")).setFailedObject (getManagedInstance()); setPCState(_state.beforeNontransactionalWrite(this, field, mutate.booleanValue())); } if ((_flags & FLAG_FLUSHED) != 0) { newFlush = (_flags & FLAG_FLUSHED_DIRTY) == 0; _flags |= FLAG_FLUSHED_DIRTY; } lock(); locked = true; // note that the field is in need of flushing again, and tell the // broker too _flush.clear(field); _broker.setDirty(this, newFlush && !clean); // save the field for rollback if needed saveField(field); // dirty the field and mark loaded; load fetch group if needed int lockLevel = calculateLockLevel(active, true, null); if (!_dirty.get(field)) { setLoaded(field, true); _dirty.set(field); // make sure the field's fetch group is loaded if (loadFetchGroup && isPersistent() && fmd.getManagement() == fmd.MANAGE_PERSISTENT) loadField(field, lockLevel, true, true); } obtainLocks(active, true, lockLevel, null, null); } catch (RuntimeException re) { throw translate(re); } finally { if (locked) unlock(); } if (clean) return Boolean.TRUE; if (newFlush) { // this event can be fired later cause we're already dirty. fireLifecycleEvent(LifecycleEvent.BEFORE_DIRTY_FLUSHED); return null; } return Boolean.FALSE; } /** * Fire post-dirty events after field value changes. * * @param status return value from {@link #dirty(int, Boolean, boolean)} */ private void postDirty(Boolean status) { if (Boolean.TRUE.equals(status)) fireLifecycleEvent(LifecycleEvent.AFTER_DIRTY); else if (status == null) fireLifecycleEvent(LifecycleEvent.AFTER_DIRTY_FLUSHED); } public void removed(int field, Object removed, boolean key) { if (removed == null) return; try { // dereference dependent fields, delete embedded FieldMetaData fmd = _meta.getField(field); ValueMetaData vmd = (key) ? fmd.getKey() : fmd.getElement(); if (vmd.isEmbeddedPC()) _single.delete(vmd, removed, null); else if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO) _single.dereferenceDependent(removed); } catch (RuntimeException re) { throw translate(re); } } public Object newProxy(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return newFieldProxy(field); switch (fmd.getTypeCode()) { case JavaTypes.DATE: if (fmd.getDeclaredType() == java.sql.Date.class) return new java.sql.Date(System.currentTimeMillis()); if (fmd.getDeclaredType() == java.sql.Timestamp.class) return new java.sql.Timestamp(System.currentTimeMillis()); if (fmd.getDeclaredType() == java.sql.Time.class) return new java.sql.Time(System.currentTimeMillis()); return new Date(); case JavaTypes.CALENDAR: return Calendar.getInstance(); case JavaTypes.COLLECTION: return new ArrayList(); case JavaTypes.MAP: return new HashMap(); } return null; } public Object newFieldProxy(int field) { FieldMetaData fmd = _meta.getField(field); ProxyManager mgr = _broker.getConfiguration(). getProxyManagerInstance(); Object init = fmd.getInitializer(); switch (fmd.getDeclaredTypeCode()) { case JavaTypes.DATE: return mgr.newDateProxy(fmd.getDeclaredType()); case JavaTypes.CALENDAR: return mgr.newCalendarProxy(fmd.getDeclaredType(), init instanceof TimeZone ? (TimeZone) init : null); case JavaTypes.COLLECTION: return mgr.newCollectionProxy(fmd.getProxyType(), fmd.getElement().getDeclaredType(), init instanceof Comparator ? (Comparator) init : null); case JavaTypes.MAP: return mgr.newMapProxy(fmd.getProxyType(), fmd.getKey().getDeclaredType(), fmd.getElement().getDeclaredType(), init instanceof Comparator ? (Comparator) init : null); } return null; } public boolean isDefaultValue(int field) { lock(); try { _single.clear(); provideField(_pc, _single, field); boolean ret = _single.isDefaultValue(); _single.clear(); return ret; } finally { unlock(); } } ///////////////////////////////////////////////////////// // Record that the field is dirty (which might load DFG) ///////////////////////////////////////////////////////// public void settingBooleanField(PersistenceCapable pc, int field, boolean curVal, boolean newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeBooleanField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingByteField(PersistenceCapable pc, int field, byte curVal, byte newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeByteField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingCharField(PersistenceCapable pc, int field, char curVal, char newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeCharField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingDoubleField(PersistenceCapable pc, int field, double curVal, double newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeDoubleField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingFloatField(PersistenceCapable pc, int field, float curVal, float newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeFloatField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingIntField(PersistenceCapable pc, int field, int curVal, int newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeIntField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingLongField(PersistenceCapable pc, int field, long curVal, long newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeLongField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingObjectField(PersistenceCapable pc, int field, Object curVal, Object newVal, int set) { if (set != SET_REMOTE) { FieldMetaData fmd = _meta.getField(field); if (_loaded.get(field)) { if (newVal == curVal) return; // only compare new to old values if the comparison is going to // be cheap -- don't compare collections, maps, UDTs switch (fmd.getDeclaredTypeCode()) { case JavaTypes.ARRAY: case JavaTypes.COLLECTION: case JavaTypes.MAP: case JavaTypes.PC_UNTYPED: break; case JavaTypes.PC: if (_meta.getField(field).isPrimaryKey()) { // this field is a derived identity //if (newVal != null && newVal.equals(curVal)) // return; //else { if (curVal != null && newVal != null && curVal instanceof PersistenceCapable && newVal instanceof PersistenceCapable) { PersistenceCapable curPc = (PersistenceCapable) curVal; PersistenceCapable newPc = (PersistenceCapable) newVal; if (curPc.pcFetchObjectId().equals(newPc.pcFetchObjectId())) return; } //} } else break; default: if (newVal != null && newVal.equals(curVal)) return; } } else { // if this is a dependent unloaded field, make sure to load // it now if (fmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO || fmd.getKey().getCascadeDelete() == ValueMetaData.CASCADE_AUTO || fmd.getElement().getCascadeDelete() == ValueMetaData.CASCADE_AUTO) curVal = fetchObjectField(field); } assertNoPrimaryKeyChange(field); if (fmd.getDeclaredTypeCode() == JavaTypes.OID) assertNotManagedObjectId(newVal); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); if (set != SET_REMOTE) { _single.storeObjectField(field, curVal); _single.unproxy(); _single.dereferenceDependent(); _single.clear(); } _single.storeObjectField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingShortField(PersistenceCapable pc, int field, short curVal, short newVal, int set) { if (set != SET_REMOTE) { if (newVal == curVal && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeShortField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } public void settingStringField(PersistenceCapable pc, int field, String curVal, String newVal, int set) { if (set != SET_REMOTE) { if (StringUtils.equals(newVal, curVal) && _loaded.get(field)) return; assertNoPrimaryKeyChange(field); } lock(); try { Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER); _single.storeStringField(field, newVal); replaceField(pc, _single, field); postDirty(stat); } finally { unlock(); } } /** * Disallows changing primary key fields for instances. */ private void assertNoPrimaryKeyChange(int field) { if (_oid != null && _meta.getField(field).isPrimaryKey()) throw translate(new InvalidStateException(_loc.get ("change-identity")).setFailedObject(getManagedInstance())); } /** * Disallows setting an object id field to a managed instance. */ void assertNotManagedObjectId(Object val) { if (val != null && (ImplHelper.toPersistenceCapable(val, getContext().getConfiguration())).pcGetGenericContext()!= null) throw translate(new InvalidStateException(_loc.get ("managed-oid", Exceptions.toString(val), Exceptions.toString(getManagedInstance()))). setFailedObject(getManagedInstance())); } //////////////////////////// // Delegate to FieldManager //////////////////////////// public void providedBooleanField(PersistenceCapable pc, int field, boolean curVal) { _fm.storeBooleanField(field, curVal); } public void providedByteField(PersistenceCapable pc, int field, byte curVal) { _fm.storeByteField(field, curVal); } public void providedCharField(PersistenceCapable pc, int field, char curVal) { _fm.storeCharField(field, curVal); } public void providedDoubleField(PersistenceCapable pc, int field, double curVal) { _fm.storeDoubleField(field, curVal); } public void providedFloatField(PersistenceCapable pc, int field, float curVal) { _fm.storeFloatField(field, curVal); } public void providedIntField(PersistenceCapable pc, int field, int curVal) { _fm.storeIntField(field, curVal); } public void providedLongField(PersistenceCapable pc, int field, long curVal) { _fm.storeLongField(field, curVal); } public void providedObjectField(PersistenceCapable pc, int field, Object curVal) { _fm.storeObjectField(field, curVal); } public void providedShortField(PersistenceCapable pc, int field, short curVal) { _fm.storeShortField(field, curVal); } public void providedStringField(PersistenceCapable pc, int field, String curVal) { _fm.storeStringField(field, curVal); } public boolean replaceBooleanField(PersistenceCapable pc, int field) { return _fm.fetchBooleanField(field); } public byte replaceByteField(PersistenceCapable pc, int field) { return _fm.fetchByteField(field); } public char replaceCharField(PersistenceCapable pc, int field) { return _fm.fetchCharField(field); } public double replaceDoubleField(PersistenceCapable pc, int field) { return _fm.fetchDoubleField(field); } public float replaceFloatField(PersistenceCapable pc, int field) { return _fm.fetchFloatField(field); } public int replaceIntField(PersistenceCapable pc, int field) { return _fm.fetchIntField(field); } public long replaceLongField(PersistenceCapable pc, int field) { return _fm.fetchLongField(field); } public Object replaceObjectField(PersistenceCapable pc, int field) { return _fm.fetchObjectField(field); } public short replaceShortField(PersistenceCapable pc, int field) { return _fm.fetchShortField(field); } public String replaceStringField(PersistenceCapable pc, int field) { return _fm.fetchStringField(field); } ////////////////////////////////// // Implementation of FieldManager ////////////////////////////////// public boolean fetchBoolean(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchBooleanField(field); Object val = fetchField(field, false); return ((Boolean) fmd.getExternalValue(val, _broker)).booleanValue(); } public boolean fetchBooleanField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchBooleanField(field); } finally { unlock(); } } public byte fetchByte(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchByteField(field); Object val = fetchField(field, false); return ((Number) fmd.getExternalValue(val, _broker)).byteValue(); } public byte fetchByteField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchByteField(field); } finally { unlock(); } } public char fetchChar(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchCharField(field); Object val = fetchField(field, false); return ((Character) fmd.getExternalValue(val, _broker)).charValue(); } public char fetchCharField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchCharField(field); } finally { unlock(); } } public double fetchDouble(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchDoubleField(field); Object val = fetchField(field, false); return ((Number) fmd.getExternalValue(val, _broker)).doubleValue(); } public double fetchDoubleField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchDoubleField(field); } finally { unlock(); } } public float fetchFloat(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchFloatField(field); Object val = fetchField(field, false); return ((Number) fmd.getExternalValue(val, _broker)).floatValue(); } public float fetchFloatField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchFloatField(field); } finally { unlock(); } } public int fetchInt(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchIntField(field); Object val = fetchField(field, false); return ((Number) fmd.getExternalValue(val, _broker)).intValue(); } public int fetchIntField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchIntField(field); } finally { unlock(); } } public long fetchLong(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchLongField(field); Object val = fetchField(field, false); return ((Number) fmd.getExternalValue(val, _broker)).longValue(); } public long fetchLongField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchLongField(field); } finally { unlock(); } } public Object fetchObject(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchObjectField(field); Object val = fetchField(field, false); return fmd.getExternalValue(val, _broker); } public Object fetchObjectField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchObjectField(field); } finally { unlock(); } } public short fetchShort(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchShortField(field); Object val = fetchField(field, false); return ((Number) fmd.getExternalValue(val, _broker)).shortValue(); } public short fetchShortField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchShortField(field); } finally { unlock(); } } public String fetchString(int field) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) return fetchStringField(field); Object val = fetchField(field, false); return (String) fmd.getExternalValue(val, _broker); } public String fetchStringField(int field) { lock(); try { if (!_loaded.get(field)) loadField(field, LockLevels.LOCK_NONE, false, false); provideField(_pc, _single, field); return _single.fetchStringField(field); } finally { unlock(); } } public void storeBoolean(int field, boolean externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeBooleanField(field, externalVal); else { Object val = (externalVal) ? Boolean.TRUE : Boolean.FALSE; storeField(field, fmd.getFieldValue(val, _broker)); } } public void storeBooleanField(int field, boolean curVal) { lock(); try { _single.storeBooleanField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeByte(int field, byte externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeByteField(field, externalVal); else storeField(field, fmd.getFieldValue(new Byte(externalVal), _broker)); } public void storeByteField(int field, byte curVal) { lock(); try { _single.storeByteField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeChar(int field, char externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeCharField(field, externalVal); else storeField(field, fmd.getFieldValue(new Character(externalVal), _broker)); } public void storeCharField(int field, char curVal) { lock(); try { _single.storeCharField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeDouble(int field, double externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeDoubleField(field, externalVal); else storeField(field, fmd.getFieldValue(new Double(externalVal), _broker)); } public void storeDoubleField(int field, double curVal) { lock(); try { _single.storeDoubleField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeFloat(int field, float externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeFloatField(field, externalVal); else storeField(field, fmd.getFieldValue(new Float(externalVal), _broker)); } public void storeFloatField(int field, float curVal) { lock(); try { _single.storeFloatField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeInt(int field, int externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeIntField(field, externalVal); else storeField(field, fmd.getFieldValue(Numbers.valueOf(externalVal), _broker)); } public void storeIntField(int field, int curVal) { lock(); try { _single.storeIntField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeLong(int field, long externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeLongField(field, externalVal); else storeField(field, fmd.getFieldValue(Numbers.valueOf(externalVal), _broker)); } public void storeLongField(int field, long curVal) { lock(); try { _single.storeLongField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeObject(int field, Object externalVal) { FieldMetaData fmd = _meta.getField(field); externalVal = fmd.order(externalVal); if (!fmd.isExternalized()) storeObjectField(field, externalVal); else storeField(field, fmd.getFieldValue(externalVal, _broker)); } public void storeObjectField(int field, Object curVal) { lock(); try { _single.storeObjectField(field, curVal); _single.proxy(true, false); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeShort(int field, short externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeShortField(field, externalVal); else storeField(field, fmd.getFieldValue(new Short(externalVal), _broker)); } public void storeShortField(int field, short curVal) { lock(); try { _single.storeShortField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } public void storeString(int field, String externalVal) { FieldMetaData fmd = _meta.getField(field); if (!fmd.isExternalized()) storeStringField(field, externalVal); else storeField(field, fmd.getFieldValue(externalVal, _broker)); } public void storeStringField(int field, String curVal) { lock(); try { _single.storeStringField(field, curVal); replaceField(_pc, _single, field); setLoaded(field, true); postLoad(field, null); } finally { unlock(); } } /** * Store the given field value into the given field manager. */ private void storeField(int field, Object val, FieldManager fm) { FieldMetaData fmd = _meta.getField(field); if (fmd == null) throw new UserException(_loc.get("no-field-index", String.valueOf(field), _meta.getDescribedType())). setFailedObject(getManagedInstance()); switch (fmd.getDeclaredTypeCode()) { case JavaTypes.BOOLEAN: boolean bool = val != null && ((Boolean) val).booleanValue(); fm.storeBooleanField(field, bool); break; case JavaTypes.BYTE: byte b = (val == null) ? 0 : ((Number) val).byteValue(); fm.storeByteField(field, b); break; case JavaTypes.CHAR: char c = (val == null) ? 0 : ((Character) val).charValue(); fm.storeCharField(field, c); break; case JavaTypes.DOUBLE: double d = (val == null) ? 0 : ((Number) val).doubleValue(); fm.storeDoubleField(field, d); break; case JavaTypes.FLOAT: float f = (val == null) ? 0 : ((Number) val).floatValue(); fm.storeFloatField(field, f); break; case JavaTypes.INT: int i = (val == null) ? 0 : ((Number) val).intValue(); fm.storeIntField(field, i); break; case JavaTypes.LONG: long l = (val == null) ? 0 : ((Number) val).longValue(); fm.storeLongField(field, l); break; case JavaTypes.SHORT: short s = (val == null) ? 0 : ((Number) val).shortValue(); fm.storeShortField(field, s); break; case JavaTypes.STRING: fm.storeStringField(field, (String) val); break; default: fm.storeObjectField(field, val); } } ///////////// // Utilities ///////////// /** * Erase the fact that this instance has been flushed. */ void eraseFlush() { _flags &= ~FLAG_FLUSHED; _flags &= ~FLAG_FLUSHED_DIRTY; int fmds = _meta.getFields().length; for (int i = 0; i < fmds; i++) _flush.clear(i); } /** * Records that all instance fields are/are not loaded. * Primary key and non-persistent fields are not affected. */ void setLoaded(boolean val) { FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) { if (!fmds[i].isPrimaryKey() && fmds[i].getManagement() == fmds[i].MANAGE_PERSISTENT) setLoaded(i, val); } if (!val) { _flags &= ~FLAG_LOADED; setDirty(false); } else _flags |= FLAG_LOADED; } /** * Records that all instance fields are/are not dirty, * and changes the flags of the instance accordingly. */ void setDirty(boolean val) { FieldMetaData[] fmds = _meta.getFields(); boolean update = !isNew() || isFlushed(); for (int i = 0; i < fmds.length; i++) { if (val && (!update || fmds[i].getUpdateStrategy() != UpdateStrategies.IGNORE)) _dirty.set(i); else if (!val) { // we never consider clean fields flushed; this also takes // care of clearing the flushed fields on commit/rollback _flush.clear(i); _dirty.clear(i); } } if (val) _flags |= FLAG_LOADED; } /** * Executes pre-clear callbacks, clears all managed fields, and calls the * {@link #setLoaded} method with a value of false. Primary key fields * are not cleared. */ void clearFields() { if (!isIntercepting()) return; fireLifecycleEvent(LifecycleEvent.BEFORE_CLEAR); // unproxy all fields unproxyFields(); lock(); try { // clear non-pk fields FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) { if (!fmds[i].isPrimaryKey() && fmds[i].getManagement() == FieldMetaData.MANAGE_PERSISTENT) replaceField(_pc, ClearFieldManager.getInstance(), i); } // forget version info and impl data so we re-read next time setLoaded(false); _version = null; _loadVersion = null; if (_fieldImpl != null) Arrays.fill(_fieldImpl, null); } finally { unlock(); } fireLifecycleEvent(LifecycleEvent.AFTER_CLEAR); } /** * Record that we should save any fields that change from this point * forward. */ void saveFields(boolean immediate) { if (_broker.getRestoreState() == RestoreState.RESTORE_NONE && (_flags & FLAG_INVERSES) == 0) return; _flags |= FLAG_SAVE; if (immediate) { for (int i = 0, len = _loaded.length(); i < len; i++) saveField(i); _flags &= ~FLAG_SAVE; // OPENJPA-659 // record a saved field manager even if no field is currently loaded // as existence of a SaveFieldManager is critical for a dirty check if (_saved == null) _saved = new SaveFieldManager(this, getPersistenceCapable(), _dirty); } } /** * If the field isn't already saved, saves the currently loaded field * state of the instance. The saved values can all be restored via * {@link #restoreFields}. */ private void saveField(int field) { if ((_flags & FLAG_SAVE) == 0) return; // if this is a managed inverse field, load it so we're sure to have // the original value if (!_loaded.get(field) && ((_flags & FLAG_INVERSES) != 0 && _meta.getField(field).getInverseMetaDatas().length > 0)) loadField(field, LockLevels.LOCK_NONE, false, false); // don't bother creating the save field manager if we're not going to // save the old field value anyway if (_saved == null) { if (_loaded.get(field)) _saved = new SaveFieldManager(this, null, _dirty); else return; } // copy the field to save field manager; if the field is not directly // copyable, immediately provide and replace it via the save field // manager, which will copy the mutable value to prevent by-ref mods if (_saved.saveField(field)) { provideField(_pc, _saved, field); replaceField(_saved.getState(), _saved, field); } } /** * Notification that the state will not need to be rolled back * to that of the last call to {@link #saveFields}. */ void clearSavedFields() { if (isIntercepting()) { _flags &= ~FLAG_SAVE; _saved = null; } } public SaveFieldManager getSaveFieldManager() { return _saved; } /** * Rollback the state of the instance to the saved state from the * last call to {@link #saveFields}, or to default values if never saved. */ void restoreFields() { lock(); try { if (_saved == null) { if ((_flags & FLAG_SAVE) == 0) clearFields(); else // only unloaded fields were dirtied _loaded.andNot(_loaded); } // we direct state transitions based on our own getRestoreState // method, but to decide whether to actually rollback field // values, we consult the broker for the user's setting else if (_broker.getRestoreState() != RestoreState.RESTORE_NONE) { // rollback all currently-loaded fields for (int i = 0, len = _loaded.length(); i < len; i++) if (_loaded.get(i) && _saved.restoreField(i)) replaceField(_pc, _saved, i); // rollback loaded set _loaded.andNot(_saved.getUnloaded()); } } finally { unlock(); } } /** * Replaces all second class object fields with fresh proxied instances * containing the same information as the originals. */ void proxyFields(boolean reset, boolean replaceNull) { // we only replace nulls if the runtime can't differentiate between // null and empty containers. we replace nulls in this case to // maintain consistency whether values are being retained or not if (replaceNull) replaceNull = !_broker.getConfiguration().supportedOptions(). contains(OpenJPAConfiguration.OPTION_NULL_CONTAINER); lock(); try { for (int i = 0, len = _loaded.length(); i < len; i++) { if (_loaded.get(i)) { provideField(_pc, _single, i); if (_single.proxy(reset, replaceNull)) replaceField(_pc, _single, i); else _single.clear(); } } } finally { unlock(); } } /** * Unproxy all fields. */ void unproxyFields() { if ((_flags & FLAG_NO_UNPROXY) != 0) return; lock(); try { for (int i = 0, len = _loaded.length(); i < len; i++) { provideField(_pc, _single, i); _single.unproxy(); _single.releaseEmbedded(); _single.clear(); } } finally { unlock(); } } /** * Get ready for a flush. Persists all persistence-capable object fields, * and checks for illegal null values. Also assigns oids and field values * for all strategies that don't require flushing. */ void preFlush(boolean logical, OpCallbacks call) { if ((_flags & FLAG_PRE_FLUSHED) != 0) return; if (isPersistent()) { fireLifecycleEvent(LifecycleEvent.BEFORE_STORE); // BEFORE_PERSIST is handled during Broker.persist and Broker.attach if (isDeleted()) fireLifecycleEvent(LifecycleEvent.BEFORE_DELETE); else if (!(isNew() && !isFlushed())) fireLifecycleEvent(LifecycleEvent.BEFORE_UPDATE); _flags |= FLAG_PRE_FLUSHED; } lock(); try { if (!logical) assignObjectId(false, true); for (int i = 0, len = _meta.getFields().length; i < len; i++) { if ((logical || !assignField(i, true)) && !_flush.get(i) && _dirty.get(i)) { provideField(_pc, _single, i); if (_single.preFlush(logical, call)) replaceField(_pc, _single, i); else _single.clear(); } } dirtyCheck(); } finally { unlock(); } } /** * Make callbacks for deletion. */ void preDelete() { // set a flag while call pre delete callback so that user can't // get into infinite recursion by calling delete(this) // within his callback method if ((_flags & FLAG_PRE_DELETING) == 0) { _flags |= FLAG_PRE_DELETING; try { fireLifecycleEvent(LifecycleEvent.BEFORE_DELETE); } finally { _flags &= ~FLAG_PRE_DELETING; } } } /** * Cascade deletes and dereference dependent fields. */ void cascadeDelete(OpCallbacks call) { FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) { if (fmds[i].getCascadeDelete() != ValueMetaData.CASCADE_NONE || fmds[i].getKey().getCascadeDelete() != ValueMetaData.CASCADE_NONE || fmds[i].getElement().getCascadeDelete() != ValueMetaData.CASCADE_NONE) { _single.storeObjectField(i, fetchField(i, false)); _single.delete(call); _single.clear(); } } } /** * Called after an instance is persisted by a user through the broker. * Cascades the persist operation to fields marked * {@link ValueMetaData#CASCADE_IMMEDIATE}. */ void cascadePersist(OpCallbacks call) { FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) { if (!_loaded.get(i)) continue; if (fmds[i].getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE || fmds[i].getKey().getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE || fmds[i].getElement().getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE) { _single.storeObjectField(i, fetchField(i, false)); _single.persist(call); _single.clear(); } } } /** * Load the given field set from the data store into the instance. * Return true if any data is loaded, false otherwise. */ boolean loadFields(BitSet fields, FetchConfiguration fetch, int lockLevel, Object sdata) { // can't load version field from store if (fields != null) { FieldMetaData vfield = _meta.getVersionField(); if (vfield != null) fields.clear(vfield.getIndex()); } boolean ret = false; setLoading(true); try { // if any fields given, load them int len = (fields == null) ? 0 : fields.length(); if (len > 0) { if (fetch == null) fetch = _broker.getFetchConfiguration(); if (!_broker.getStoreManager().load(this, fields, fetch, lockLevel, sdata)) { throw new ObjectNotFoundException(_loc.get("del-instance", _meta.getDescribedType(), _oid)). setFailedObject(getManagedInstance()); } ret = true; } // make sure version information has been set; version info must // always be set after the first state load or set (which is why // we do this even if no fields were loaded -- could be that this // method is being called after a field is set)... some instances // might not have version info, in which case this gets called // mutiple times; that should be ok too if (_loadVersion == null) { syncVersion(sdata); ret = ret || _loadVersion != null; } } finally { setLoading(false); } // see if the dfg is now loaded; do this regardless of whether we // loaded any fields, cause may already have been loaded by // StoreManager during initialization postLoad(-1, fetch); return ret; } /** * Load the given field's fetch group; the field itself may already be * loaded if it is being set by the user. */ protected void loadField(int field, int lockLevel, boolean forWrite, boolean fgs) { FetchConfiguration fetch = _broker.getFetchConfiguration(); FieldMetaData fmd = _meta.getField(field); BitSet fields = null; // if this is a dfg field or we need to load our dfg, do so if (fgs && (_flags & FLAG_LOADED) == 0) fields = getUnloadedInternal(fetch, LOAD_FGS, null); // check for load fetch group String lfg = fmd.getLoadFetchGroup(); boolean lfgAdded = false; if (lfg != null) { FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) { if (!_loaded.get(i) && (i == field || fmds[i].isInFetchGroup(lfg))) { if (fields == null) fields = new BitSet(fmds.length); fields.set(i); } } // relation field is loaded with the load-fetch-group // but this addition must be reverted once the load is over if (!fetch.hasFetchGroup(lfg)) { fetch.addFetchGroup(lfg); lfgAdded = true; } } else if (fmd.isInDefaultFetchGroup() && fields == null) { // no load group but dfg: add dfg fields if we haven't already fields = getUnloadedInternal(fetch, LOAD_FGS, null); } else if (!_loaded.get(fmd.getIndex())) { // no load group or dfg: load individual field if (fields == null) fields = new BitSet(); fields.set(fmd.getIndex()); } // call this method even if there are no unloaded fields; loadFields // takes care of things like loading version info and setting PC flags try { loadFields(fields, fetch, lockLevel, null); } finally { if (lfgAdded) fetch.removeFetchGroup(lfg); } } /** * Helper method to provide the given field number to the given * field manager. */ void provideField(PersistenceCapable pc, FieldManager store, int field) { FieldManager beforeFM = _fm; _fm = store; pc.pcProvideField(field); // Retaining original FM because of the possibility of reentrant calls _fm = beforeFM; } /** * Helper method to replace the given field number to the given * field manager. */ void replaceField(PersistenceCapable pc, FieldManager load, int field) { FieldManager beforeFM = _fm; _fm = load; pc.pcReplaceField(field); // Retaining original FM because of the possibility of reentrant calls _fm = beforeFM; } /** * Mark the field as loaded or unloaded. */ private void setLoaded(int field, boolean isLoaded) { // don't continue if loaded state is already correct; otherwise we // can end up clearing _fieldImpl when we shouldn't if (_loaded.get(field) == isLoaded) return; // if loading, clear intermediate data; if unloading, clear impl data if (_fieldImpl != null) { int idx = _meta.getExtraFieldDataIndex(field); if (idx != -1) _fieldImpl[idx] = null; } if (isLoaded) _loaded.set(field); else _loaded.clear(field); } /** * Perform post-load steps, including the post load callback. * We have to check the dfg after all field loads because it might be * loaded in multiple steps when paging is involved; the initial load * might exclude some fields which are then immediately loaded in a * separate step before being returned to the user. * * @param field the field index that was loaded, or -1 to indicate * that a group of possibly unknown fields was loaded */ private void postLoad(int field, FetchConfiguration fetch) { // no need for postLoad callback? if ((_flags & FLAG_LOADED) != 0) return; // in the middle of a group load, after which this method will be // called again? if (field != -1 && isLoading()) return; // no listeners? LifecycleEventManager mgr = _broker.getLifecycleEventManager(); if (mgr == null || !mgr.hasLoadListeners(getManagedInstance(), _meta)) return; if (fetch == null) fetch = _broker.getFetchConfiguration(); // is this field a post-load field? if (field != -1) { FieldMetaData fmd = _meta.getField(field); if (fmd.isInDefaultFetchGroup() && fetch.hasFetchGroup(FetchGroup.NAME_DEFAULT) && postLoad(FetchGroup.NAME_DEFAULT, fetch)) return; String[] fgs = fmd.getCustomFetchGroups(); for (int i = 0; i < fgs.length; i++) if (fetch.hasFetchGroup(fgs[i]) && postLoad(fgs[i], fetch)) return; } else { for (Iterator itr = fetch.getFetchGroups().iterator(); itr.hasNext();) { if (postLoad((String) itr.next(), fetch)) return; } } } /** * Perform post-load actions if the given fetch group is a post-load group * and is fully loaded. */ private boolean postLoad(String fgName, FetchConfiguration fetch) { FetchGroup fg = _meta.getFetchGroup(fgName); if (fg == null || !fg.isPostLoad()) return false; FieldMetaData[] fmds = _meta.getFields(); for (int i = 0; i < fmds.length; i++) if (!_loaded.get(i) && fmds[i].isInFetchGroup(fgName)) return false; _flags |= FLAG_LOADED; _broker.fireLifecycleEvent(getManagedInstance(), fetch, _meta, LifecycleEvent.AFTER_LOAD); return true; } /** * Synchronize our version object with the datastore. */ private boolean syncVersion(Object sdata) { return _broker.getStoreManager().syncVersion(this, sdata); } /** * Returns whether this instance needs a version check. */ public boolean isVersionCheckRequired() { // explicit flag for version check if ((_flags & FLAG_VERSION_CHECK) != 0) return true; if (!_broker.getOptimistic() && !_broker.getConfiguration(). getCompatibilityInstance().getNonOptimisticVersionCheck()) return false; return _state.isVersionCheckRequired(this); } /** * Set whether this instance requires a version check on the next flush. */ void setCheckVersion(boolean versionCheck) { if (versionCheck) _flags |= FLAG_VERSION_CHECK; else _flags &= ~FLAG_VERSION_CHECK; } /** * Returns whether this instance needs a version update. */ public boolean isVersionUpdateRequired() { return (_flags & FLAG_VERSION_UPDATE) > 0; } /** * Set whether this instance requires a version update on the next flush. */ void setUpdateVersion(boolean versionUpdate) { if (versionUpdate) _flags |= FLAG_VERSION_UPDATE; else _flags &= ~FLAG_VERSION_UPDATE; } /** * Translate the given exception based on the broker's implicit behavior. * Translation only occurs if the exception is initiated by a user action * on an instance, and therefore will not be caught and translated by the * broker. */ protected RuntimeException translate(RuntimeException re) { RuntimeExceptionTranslator trans = _broker. getInstanceExceptionTranslator(); return (trans == null) ? re : trans.translate(re); } /** * Lock the state manager if the multithreaded option is set. */ protected void lock() { // use broker-level lock to avoid deadlock situations with the state // manager lock and broker lock being obtained in different orders _broker.lock(); } /** * Unlock the state manager. */ protected void unlock () { // use broker-level lock to avoid deadlock situations with the state // manager lock and broker lock being obtained in different orders _broker.unlock (); } private void writeObject(ObjectOutputStream oos) throws IOException { oos.writeObject(_broker); oos.defaultWriteObject(); oos.writeObject(_meta.getDescribedType()); writePC(oos, _pc); } /** * Write pc to oos, handling internal-form * serialization. pc must be of the same type that this * state manager manages. * * @since 1.1.0 */ void writePC(ObjectOutputStream oos, PersistenceCapable pc) throws IOException { if (!Serializable.class.isAssignableFrom(_meta.getDescribedType())) throw new NotSerializableException( _meta.getDescribedType().getName()); oos.writeObject(pc); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { _broker = (BrokerImpl) in.readObject(); in.defaultReadObject(); // we need to store the class before the pc instance so that we can // create _meta before calling readPC(), which relies on _meta being // non-null when reconstituting ReflectingPC instances. Sadly, this // penalizes the serialization footprint of non-ReflectingPC SMs also. Class managedType = (Class) in.readObject(); _meta = _broker.getConfiguration().getMetaDataRepositoryInstance() .getMetaData(managedType, null, true); _pc = readPC(in); } /** * Converts the deserialized o to a {@link PersistenceCapable} * instance appropriate for storing in _pc. * * @since 1.1.0 */ PersistenceCapable readPC(ObjectInputStream in) throws ClassNotFoundException, IOException { Object o = in.readObject(); if (o == null) return null; PersistenceCapable pc; if (!(o instanceof PersistenceCapable)) pc = ImplHelper.toPersistenceCapable(o, this); else pc = (PersistenceCapable) o; pc.pcReplaceStateManager(this); return pc; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy