Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.kernel;
   20   
   21   import java.io.IOException;
   22   import java.io.NotSerializableException;
   23   import java.io.ObjectInputStream;
   24   import java.io.ObjectOutput;
   25   import java.io.ObjectOutputStream;
   26   import java.io.Serializable;
   27   import java.lang.reflect.Modifier;
   28   import java.util.ArrayList;
   29   import java.util.Arrays;
   30   import java.util.BitSet;
   31   import java.util.Calendar;
   32   import java.util.Comparator;
   33   import java.util.Date;
   34   import java.util.HashMap;
   35   import java.util.Iterator;
   36   import java.util.TimeZone;
   37   
   38   import org.apache.commons.lang.StringUtils;
   39   import org.apache.openjpa.conf.OpenJPAConfiguration;
   40   import org.apache.openjpa.enhance.DynamicPersistenceCapable;
   41   import org.apache.openjpa.enhance.FieldManager;
   42   import org.apache.openjpa.enhance.ManagedInstanceProvider;
   43   import org.apache.openjpa.enhance.PCRegistry;
   44   import org.apache.openjpa.enhance.PersistenceCapable;
   45   import org.apache.openjpa.enhance.RedefinitionHelper;
   46   import org.apache.openjpa.enhance.StateManager;
   47   import org.apache.openjpa.event.LifecycleEvent;
   48   import org.apache.openjpa.event.LifecycleEventManager;
   49   import org.apache.openjpa.lib.util.Localizer;
   50   import org.apache.openjpa.meta.ClassMetaData;
   51   import org.apache.openjpa.meta.FetchGroup;
   52   import org.apache.openjpa.meta.FieldMetaData;
   53   import org.apache.openjpa.meta.JavaTypes;
   54   import org.apache.openjpa.meta.UpdateStrategies;
   55   import org.apache.openjpa.meta.ValueMetaData;
   56   import org.apache.openjpa.meta.ValueStrategies;
   57   import org.apache.openjpa.util.ApplicationIds;
   58   import org.apache.openjpa.util.Exceptions;
   59   import org.apache.openjpa.util.ImplHelper;
   60   import org.apache.openjpa.util.InternalException;
   61   import org.apache.openjpa.util.InvalidStateException;
   62   import org.apache.openjpa.util.ObjectNotFoundException;
   63   import org.apache.openjpa.util.OpenJPAId;
   64   import org.apache.openjpa.util.ProxyManager;
   65   import org.apache.openjpa.util.RuntimeExceptionTranslator;
   66   import org.apache.openjpa.util.UserException;
   67   import serp.util.Numbers;
   68   
   69   /**
   70    * Implementation of the {@link OpenJPAStateManager} interface for use
   71    * with this runtime. Each state manager manages the state of a single
   72    * persistence capable instance. The state manager is also responsible for
   73    * all communications about the instance to the {@link StoreManager}.
   74    *  The state manager uses the State pattern in both its interaction with
   75    * the governed instance and its interaction with the broker.
   76    * In its interactions with the persistence capable instance, it uses the
   77    * {@link FieldManager} interface. Similarly, when interacting with the
   78    * broker, it uses the {@link PCState} singleton that represents
   79    * the current lifecycle state of the instance.
   80    *
   81    * @author Abe White
   82    */
   83   public class StateManagerImpl
   84       implements OpenJPAStateManager, Serializable {
   85   
   86       public static final int LOAD_FGS = 0;
   87       public static final int LOAD_ALL = 1;
   88       public static final int LOAD_SERIALIZE = 2;
   89   
   90       private static final int FLAG_SAVE = 2 << 0;
   91       private static final int FLAG_DEREF = 2 << 1;
   92       private static final int FLAG_LOADED = 2 << 2;
   93       private static final int FLAG_READ_LOCKED = 2 << 3;
   94       private static final int FLAG_WRITE_LOCKED = 2 << 4;
   95       private static final int FLAG_OID_ASSIGNED = 2 << 5;
   96       private static final int FLAG_LOADING = 2 << 6;
   97       private static final int FLAG_PRE_DELETING = 2 << 7;
   98       private static final int FLAG_FLUSHED = 2 << 8;
   99       private static final int FLAG_PRE_FLUSHED = 2 << 9;
  100       private static final int FLAG_FLUSHED_DIRTY = 2 << 10;
  101       private static final int FLAG_IMPL_CACHE = 2 << 11;
  102       private static final int FLAG_INVERSES = 2 << 12;
  103       private static final int FLAG_NO_UNPROXY = 2 << 13;
  104       private static final int FLAG_VERSION_CHECK = 2 << 14;
  105       private static final int FLAG_VERSION_UPDATE = 2 << 15;
  106       private static final int FLAG_DETACHING = 2 << 16;
  107   
  108       private static final Localizer _loc = Localizer.forPackage
  109           (StateManagerImpl.class);
  110   
  111       // information about the instance
  112       private transient PersistenceCapable _pc = null;
  113       private transient ClassMetaData _meta = null;
  114       private BitSet _loaded = null;
  115       private BitSet _dirty = null;
  116       private BitSet _flush = null;
  117       private int _flags = 0;
  118   
  119       // id is the state manager identity; oid is the persistent identity.  oid
  120       // may be null for embedded and transient-transactional objects or new
  121       // instances that haven't been assigned an oid.  id is reassigned to oid
  122       // on successful oid assignment (or flush completion if assignment is
  123       // during flush)
  124       private Object _id = null;
  125       private Object _oid = null;
  126   
  127       // the managing persistence manager and lifecycle state
  128       private transient BrokerImpl _broker; // this is serialized specially
  129       private PCState _state = PCState.TRANSIENT;
  130   
  131       // the current and last loaded version indicators, and the lock object
  132       private Object _version = null;
  133       private Object _loadVersion = null;
  134       private Object _lock = null;
  135       private int _readLockLevel = -1;
  136       private int _writeLockLevel = -1;
  137   
  138       // delegates when providing/replacing instance data
  139       private SingleFieldManager _single = null;
  140       private SaveFieldManager _saved = null;
  141       private FieldManager _fm = null;
  142   
  143       // impldata; field impldata and intermediate data share the same array
  144       private Object _impl = null;
  145       private Object[] _fieldImpl = null;
  146   
  147       // information about the owner of this instance, if it is embedded
  148       private StateManagerImpl _owner = null;
  149       private int _ownerIndex = -1;
  150   
  151       /**
  152        * Constructor; supply id, type metadata, and owning persistence manager.
  153        */
  154       protected StateManagerImpl(Object id, ClassMetaData meta, 
  155           BrokerImpl broker) {
  156           _id = id;
  157           _meta = meta;
  158           _broker = broker;
  159           _single = new SingleFieldManager(this, broker);
  160   
  161           if (_meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
  162               throw new UserException(_loc.get("meta-unknownid", _meta));
  163       }
  164   
  165       /**
  166        * Set the owning state and field if this is an embedded instance.
  167        */
  168       void setOwner(StateManagerImpl owner, ValueMetaData ownerMeta) {
  169           _owner = owner;
  170           _ownerIndex = ownerMeta.getFieldMetaData().getIndex();
  171       }
  172   
  173       /**
  174        * Whether this state manager is in the middle of a load.
  175        */
  176       boolean isLoading() {
  177           return (_flags & FLAG_LOADING) > 0;
  178       }
  179   
  180       /**
  181        * Whether this state manager is in the middle of a load initiated
  182        * by outside code; for any internal methods that cause loading, the
  183        * loading flag is set automatically.
  184        */
  185       void setLoading(boolean loading) {
  186           if (loading)
  187               _flags |= FLAG_LOADING;
  188           else
  189               _flags &= ~FLAG_LOADING;
  190       }
  191   
  192       /**
  193        * Set or reset the lifecycle state of the managed instance. If the
  194        * transactional state of the instance changes, it will be enlisted/
  195        * delisted from the current transaction as necessary. The given
  196        * state will be initialized after being set. If the given state
  197        * is the same as the current state, this method will have no effect.
  198        */
  199       private void setPCState(PCState state) {
  200           if (_state == state)
  201               return;
  202   
  203           lock();
  204           try {
  205               // notify the store manager that we're changing states; can veto
  206               _broker.getStoreManager().beforeStateChange(this, _state, state);
  207   
  208               // replace state
  209               boolean wasDeleted = _state.isDeleted();
  210               boolean wasDirty = _state.isDirty();
  211               boolean wasPending = _state.isPendingTransactional();
  212               _state = state;
  213   
  214               // enlist/delist from transaction
  215               if (_state.isTransactional()) {
  216                   _broker.addToTransaction(this);
  217                   if (_state.isDeleted() != wasDeleted)
  218                       _broker.setDirty(this, !wasDirty || isFlushed());
  219                   else if (_state.isDirty() && !wasDirty)
  220                       _broker.setDirty(this, true);
  221               } else if (!wasPending && _state.isPendingTransactional())
  222                   _broker.addToPendingTransaction(this);
  223               else if (wasPending && !_state.isPendingTransactional())
  224                   _broker.removeFromPendingTransaction(this);
  225               else
  226                   _broker.removeFromTransaction(this);
  227   
  228               // initialize
  229               _state.initialize(this);
  230               if (_state.isDeleted() && !wasDeleted)
  231                   fireLifecycleEvent(LifecycleEvent.AFTER_DELETE);
  232           } finally {
  233               unlock();
  234           }
  235       }
  236   
  237       //////////////////////////////////////
  238       // OpenJPAStateManager implementation
  239       //////////////////////////////////////
  240   
  241       public void initialize(Class cls, PCState state) {
  242           // check to see if our current object id instance is the
  243           // correct id type for the specified class; this is for cases
  244           // when we have an application id hierarchy and we had set the
  245           // metadata to a superclass id -- the subclass' id may be a
  246           // different class, so we need to reset it
  247           if (_meta.getDescribedType() != cls) {
  248               ClassMetaData sub = _meta.getRepository().getMetaData
  249                   (cls, _broker.getClassLoader(), true);
  250               if (_oid != null) {
  251                   if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
  252                       _oid = _broker.getStoreManager().copyDataStoreId(_oid,
  253                           sub);
  254                   else if (_meta.isOpenJPAIdentity())
  255                       _oid = ApplicationIds.copy(_oid, sub);
  256                   else if (sub.getObjectIdType() != _meta.getObjectIdType()) {
  257                       Object[] pkFields = ApplicationIds.toPKValues(_oid, _meta);
  258                       _oid = ApplicationIds.fromPKValues(pkFields, sub);
  259                   }
  260               }
  261               _meta = sub;
  262           }
  263   
  264           PersistenceCapable inst = PCRegistry.newInstance(cls, this, _oid, true);
  265           if (inst == null) {
  266               // the instance was null: check to see if the instance is
  267               // abstract (as can sometimes be the case when the
  268               // class discriminator strategy is not configured correctly)
  269               if (Modifier.isAbstract(cls.getModifiers()))
  270                   throw new UserException(_loc.get("instantiate-abstract",
  271                       cls.getName(), _oid));
  272               throw new InternalException();
  273           }
  274   
  275           initialize(inst, state);
  276       }
  277   
  278       /**
  279        * Initialize with the given instance and state.
  280        */
  281       protected void initialize(PersistenceCapable pc, PCState state) {
  282           if (pc == null)
  283               throw new UserException(_loc.get("init-null-pc", _meta));
  284           if (pc.pcGetStateManager() != null && pc.pcGetStateManager() != this)
  285               throw new UserException(_loc.get("init-sm-pc",
  286                   Exceptions.toString(pc))).setFailedObject(pc);
  287           pc.pcReplaceStateManager(this);
  288   
  289           FieldMetaData[] fmds = _meta.getFields();
  290           _loaded = new BitSet(fmds.length);
  291           _flush = new BitSet(fmds.length);
  292           _dirty = new BitSet(fmds.length);
  293   
  294           for (int i = 0; i < fmds.length; i++) {
  295               // mark primary key and non-persistent fields as loaded
  296               if (fmds[i].isPrimaryKey()
  297                   || fmds[i].getManagement() != fmds[i].MANAGE_PERSISTENT)
  298                   _loaded.set(i);
  299   
  300               // record whether there are any managed inverse fields
  301               if (_broker.getInverseManager() != null
  302                   && fmds[i].getInverseMetaDatas().length > 0)
  303                   _flags |= FLAG_INVERSES;
  304           }
  305   
  306           pc.pcSetDetachedState(null);
  307           _pc = pc;
  308   
  309           if (_oid instanceof OpenJPAId)
  310               ((OpenJPAId) _oid).setManagedInstanceType(_meta.getDescribedType());
  311   
  312           // initialize our state and add ourselves to the broker's cache
  313           setPCState(state);
  314           _broker.setStateManager(_id, this, BrokerImpl.STATUS_INIT);
  315           if (state == PCState.PNEW)
  316               fireLifecycleEvent(LifecycleEvent.AFTER_PERSIST);
  317   
  318           // if this is a non-tracking PC, add a hard ref to the appropriate data
  319           // sets and give it an opportunity to make a state snapshot.
  320           if (!isIntercepting()) {
  321               saveFields(true);
  322               if (!isNew())
  323                   RedefinitionHelper.assignLazyLoadProxies(this);
  324           }
  325       }
  326   
  327       /**
  328        * Whether or not data access in this instance is intercepted. This differs
  329        * from {@link ClassMetaData#isIntercepting()} in that it checks for
  330        * property access + subclassing in addition to the redefinition /
  331        * enhancement checks.
  332        *
  333        * @since 1.0.0
  334        */
  335       public boolean isIntercepting() {
  336           if (getMetaData().isIntercepting())
  337               return true;
  338           if (getMetaData().getAccessType() != ClassMetaData.ACCESS_FIELD
  339               && _pc instanceof DynamicPersistenceCapable)
  340               return true;
  341   
  342           return false;
  343       }
  344   
  345       /**
  346        * Fire the given lifecycle event to all listeners.
  347        */
  348       private boolean fireLifecycleEvent(int type) {
  349           return _broker.fireLifecycleEvent(getManagedInstance(), null,
  350               _meta, type);
  351       }
  352   
  353       public void load(FetchConfiguration fetch) {
  354           load(fetch, LOAD_FGS, null, null, false);
  355       }
  356   
  357       /**
  358        * Load the state of this instance based on the given fetch configuration
  359        * and load mode. Return true if any data was loaded, false otherwise.
  360        */
  361       protected boolean load(FetchConfiguration fetch, int loadMode,
  362           BitSet exclude, Object sdata, boolean forWrite) {
  363           if (!forWrite && (!isPersistent() || isNew() || isDeleted()))
  364               return false;
  365   
  366           // if any fields being loaded, do state transitions for read
  367           BitSet fields = getUnloadedInternal(fetch, loadMode, exclude);
  368           boolean active = _broker.isActive();
  369           if (!forWrite && fields != null)
  370               beforeRead(-1);
  371   
  372           // call load even if no fields are being loaded, because it takes
  373           // care of checking if the DFG is loaded, making sure version info
  374           // is loaded, etc
  375           int lockLevel = calculateLockLevel(active, forWrite, fetch);
  376           boolean ret = loadFields(fields, fetch, lockLevel, sdata);
  377           obtainLocks(active, forWrite, lockLevel, fetch, sdata);
  378           return ret;
  379       }
  380   
  381       public Object getManagedInstance() {
  382           if (_pc instanceof ManagedInstanceProvider)
  383               return ((ManagedInstanceProvider) _pc).getManagedInstance();
  384           else
  385               return _pc;
  386       }
  387   
  388       public PersistenceCapable getPersistenceCapable() {
  389           return _pc;
  390       }
  391   
  392       public ClassMetaData getMetaData() {
  393           return _meta;
  394       }
  395   
  396       public OpenJPAStateManager getOwner() {
  397           return _owner;
  398       }
  399   
  400       public int getOwnerIndex() {
  401           return _ownerIndex;
  402       }
  403   
  404       public boolean isEmbedded() {
  405           return _owner != null;
  406       }
  407   
  408       public boolean isFlushed() {
  409           return (_flags & FLAG_FLUSHED) > 0;
  410       }
  411   
  412       public boolean isFlushedDirty() {
  413           return (_flags & FLAG_FLUSHED_DIRTY) > 0;
  414       }
  415   
  416       public BitSet getLoaded() {
  417           return _loaded;
  418       }
  419   
  420       public BitSet getFlushed() {
  421           return _flush;
  422       }
  423   
  424       public BitSet getDirty() {
  425           return _dirty;
  426       }
  427   
  428       public BitSet getUnloaded(FetchConfiguration fetch) {
  429           // collect fields to load from data store based on fetch configuration
  430           BitSet fields = getUnloadedInternal(fetch, LOAD_FGS, null);
  431           return (fields == null) ? new BitSet(0) : fields;
  432       }
  433   
  434       /**
  435        * Internal version of {@link OpenJPAStateManager#getUnloaded} that avoids
  436        * creating an empty bit set by returning null when there are no unloaded
  437        * fields.
  438        */
  439       private BitSet getUnloadedInternal(FetchConfiguration fetch, int mode,
  440           BitSet exclude) {
  441           if (exclude == StoreContext.EXCLUDE_ALL)
  442               return null;
  443   
  444           BitSet fields = null;
  445           FieldMetaData[] fmds = _meta.getFields();
  446           boolean load;
  447           for (int i = 0; i < fmds.length; i++) {
  448               if (_loaded.get(i) || (exclude != null && exclude.get(i)))
  449                   continue;
  450   
  451               switch (mode) {
  452                   case LOAD_SERIALIZE:
  453                       load = !fmds[i].isTransient();
  454                       break;
  455                   case LOAD_FGS:
  456                       load = fetch == null || fetch.requiresFetch(fmds[i]) 
  457                           != FetchConfiguration.FETCH_NONE;
  458                       break;
  459                   default: // LOAD_ALL
  460                       load = true;
  461               }
  462   
  463               if (load) {
  464                   if (fields == null)
  465                       fields = new BitSet(fmds.length);
  466                   fields.set(i);
  467               }
  468           }
  469           return fields;
  470       }
  471   
  472       public StoreContext getContext() {
  473           return _broker;
  474       }
  475   
  476       /**
  477        * Managing broker.
  478        */
  479       BrokerImpl getBroker() {
  480           return _broker;
  481       }
  482   
  483       public Object getId() {
  484           return _id;
  485       }
  486   
  487       public Object getObjectId() {
  488           StateManagerImpl sm = this;
  489           while (sm.getOwner() != null)
  490               sm = (StateManagerImpl) sm.getOwner();
  491           return sm._oid;
  492       }
  493   
  494       public void setObjectId(Object oid) {
  495           _oid = oid;
  496           if (_pc != null && oid instanceof OpenJPAId)
  497               ((OpenJPAId) oid).setManagedInstanceType(_meta.getDescribedType());
  498       }
  499   
  500       public boolean assignObjectId(boolean flush) {
  501           lock();
  502           try {
  503               return assignObjectId(flush, false);
  504           } finally {
  505               unlock();
  506           }
  507       }
  508   
  509       /**
  510        * Ask store manager to assign our oid, optionally flushing and
  511        * optionally recaching on the new oid.
  512        */
  513       private boolean assignObjectId(boolean flush, boolean preFlushing) {
  514           if (_oid != null || isEmbedded() || !isPersistent())
  515               return true;
  516   
  517           if (_broker.getStoreManager().assignObjectId(this, preFlushing)) {
  518               if (!preFlushing)
  519                   assertObjectIdAssigned(true);
  520           } else if (flush)
  521               _broker.flush();
  522           else
  523               return false;
  524           return true;
  525       }
  526   
  527       /**
  528        * Make sure we were assigned an oid, and perform actions to make it
  529        * permanent.
  530        *
  531        * @param recache whether to recache ourself on the new oid
  532        */
  533       private void assertObjectIdAssigned(boolean recache) {
  534           if (!isNew() || isDeleted() || isProvisional() 
  535               || (_flags & FLAG_OID_ASSIGNED) != 0)
  536               return;
  537           if (_oid == null) {
  538               if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
  539                   throw new InternalException(Exceptions.toString
  540                       (getManagedInstance()));
  541               _oid = ApplicationIds.create(_pc, _meta);
  542           }
  543   
  544           Object orig = _id;
  545           _id = _oid;
  546           if (recache) {
  547               try {
  548                   _broker.setStateManager(orig, this,
  549                       BrokerImpl.STATUS_OID_ASSIGN);
  550               } catch (RuntimeException re) {
  551                   _id = orig;
  552                   _oid = null;
  553                   throw re;
  554               }
  555           }
  556           _flags |= FLAG_OID_ASSIGNED;
  557       }
  558   
  559       /**
  560        * Assign the proper generated value to the given field based on its
  561        * value-strategy.
  562        */
  563       private boolean assignField(int field, boolean preFlushing) {
  564           OpenJPAStateManager sm = this;
  565           while (sm.isEmbedded())
  566               sm = sm.getOwner();
  567           if (!sm.isNew() || sm.isFlushed() || sm.isDeleted())
  568               return false;
  569   
  570           // special-case oid fields, which require us to look inside the oid
  571           // object
  572           FieldMetaData fmd = _meta.getField(field);
  573           if (fmd.getDeclaredTypeCode() == JavaTypes.OID) {
  574               // try to shortcut if possible
  575               if (_oid != null || isEmbedded() || !isPersistent())
  576                   return true;
  577   
  578               // check embedded fields of oid for value strategy + default value
  579               FieldMetaData[] pks = fmd.getEmbeddedMetaData().getFields();
  580               OpenJPAStateManager oidsm = null;
  581               boolean assign = false;
  582               for (int i = 0; !assign && i < pks.length; i++) {
  583                   if (pks[i].getValueStrategy() == ValueStrategies.NONE)
  584                       continue;
  585                   if (oidsm == null)
  586                       oidsm = new ObjectIdStateManager(fetchObjectField(field),
  587                           this, fmd);
  588                   assign = oidsm.isDefaultValue(i);
  589               }
  590               return assign && assignObjectId(!preFlushing, preFlushing);
  591           }
  592   
  593           // Just return if there's no value generation strategy
  594           if (fmd.getValueStrategy() == ValueStrategies.NONE)
  595               return false;
  596           
  597           // Throw exception if field already has a value assigned.
  598           // @GeneratedValue overrides POJO initial values and setter methods
  599           if (!fmd.isValueGenerated() && !isDefaultValue(field))
  600               throw new InvalidStateException(_loc.get(
  601                   "existing-value-override-excep", fmd.getFullName(false)));
  602   
  603           // for primary key fields, assign the object id and recache so that
  604           // to the user, so it looks like the oid always matches the pk fields
  605           if (fmd.isPrimaryKey() && !isEmbedded())
  606               return assignObjectId(!preFlushing, preFlushing);
  607   
  608           // for other fields just assign the field or flush if needed
  609           if (_broker.getStoreManager().assignField(this, field, preFlushing)) {
  610               fmd.setValueGenerated(true);
  611               return true;
  612           }
  613           if (!preFlushing)
  614               _broker.flush();
  615           return !preFlushing;
  616       }
  617   
  618       public Object getLock() {
  619           return _lock;
  620       }
  621   
  622       public void setLock(Object lock) {
  623           _lock = lock;
  624       }
  625   
  626       public Object getVersion() {
  627           return _version;
  628       }
  629   
  630       public void setVersion(Object version) {
  631           _loadVersion = version;
  632           assignVersionField(version);
  633       }
  634   
  635       Object getLoadVersion() {
  636           return _loadVersion;
  637       }
  638   
  639       public void setNextVersion(Object version) {
  640           assignVersionField(version);
  641       }
  642   
  643       private void assignVersionField(Object version) {
  644           _version = version;
  645           FieldMetaData vfield = _meta.getVersionField();
  646           if (vfield != null)
  647               store(vfield.getIndex(), JavaTypes.convert(version,
  648                   vfield.getTypeCode()));
  649       }
  650   
  651       public PCState getPCState() {
  652           return _state;
  653       }
  654   
  655       public synchronized Object getImplData() {
  656           return _impl;
  657       }
  658   
  659       public synchronized Object setImplData(Object data, boolean cacheable) {
  660           Object old = _impl;
  661           _impl = data;
  662           if (cacheable && data != null)
  663               _flags |= FLAG_IMPL_CACHE;
  664           else
  665               _flags &= ~FLAG_IMPL_CACHE;
  666           return old;
  667       }
  668   
  669       public boolean isImplDataCacheable() {
  670           return (_flags & FLAG_IMPL_CACHE) != 0;
  671       }
  672   
  673       public Object getImplData(int field) {
  674           return getExtraFieldData(field, true);
  675       }
  676   
  677       public Object setImplData(int field, Object data) {
  678           return setExtraFieldData(field, data, true);
  679       }
  680   
  681       public synchronized boolean isImplDataCacheable(int field) {
  682           if (_fieldImpl == null || !_loaded.get(field))
  683               return false;
  684           if (_meta.getField(field).usesImplData() != null)
  685               return false;
  686           int idx = _meta.getExtraFieldDataIndex(field);
  687           return idx != -1 && _fieldImpl[idx] != null;
  688       }
  689   
  690       public Object getIntermediate(int field) {
  691           return getExtraFieldData(field, false);
  692       }
  693   
  694       public void setIntermediate(int field, Object data) {
  695           setExtraFieldData(field, data, false);
  696       }
  697   
  698       /**
  699        * Return the data from the proper index of the extra field data array.
  700        */
  701       private synchronized Object getExtraFieldData(int field, boolean isLoaded) {
  702           // only return the field data if the field is in the right loaded
  703           // state; otherwise we might return intermediate for impl data or
  704           // vice versa
  705           if (_fieldImpl == null || _loaded.get(field) != isLoaded)
  706               return null;
  707           int idx = _meta.getExtraFieldDataIndex(field);
  708           return (idx == -1) ? null : _fieldImpl[idx];
  709       }
  710   
  711       /**
  712        * Set the data from to proper index of the extra field data array.
  713        */
  714       private synchronized Object setExtraFieldData(int field, Object data,
  715           boolean loaded) {
  716           int idx = _meta.getExtraFieldDataIndex(field);
  717           if (idx == -1)
  718               throw new InternalException(String.valueOf(_meta.getField(field)));
  719   
  720           Object old = (_fieldImpl == null) ? null : _fieldImpl[idx];
  721           if (data != null) {
  722               // cannot set if field in wrong loaded state
  723               if (_loaded.get(field) != loaded)
  724                   throw new InternalException(String.valueOf(_meta.getField
  725                       (field)));
  726   
  727               // set data
  728               if (_fieldImpl == null)
  729                   _fieldImpl = new Object[_meta.getExtraFieldDataLength()];
  730               _fieldImpl[idx] = data;
  731           } else if (_fieldImpl != null && _loaded.get(field) == loaded)
  732               _fieldImpl[idx] = null;
  733           return old;
  734       }
  735   
  736       public Object fetch(int field) {
  737           Object val = fetchField(field, false);
  738           return _meta.getField(field).getExternalValue(val, _broker);
  739       }
  740   
  741       public Object fetchField(int field, boolean transitions) {
  742           FieldMetaData fmd = _meta.getField(field);
  743           if (fmd == null)
  744               throw new UserException(_loc.get("no-field",
  745                   String.valueOf(field), getManagedInstance().getClass())).
  746                   setFailedObject(getManagedInstance());
  747   
  748           // do normal state transitions
  749           if (!fmd.isPrimaryKey() && transitions)
  750               accessingField(field);
  751   
  752           switch (fmd.getDeclaredTypeCode()) {
  753               case JavaTypes.STRING:
  754                   return fetchStringField(field);
  755               case JavaTypes.OBJECT:
  756                   return fetchObjectField(field);
  757               case JavaTypes.BOOLEAN:
  758                   return (fetchBooleanField(field)) ? Boolean.TRUE
  759                       : Boolean.FALSE;
  760               case JavaTypes.BYTE:
  761                   return new Byte(fetchByteField(field));
  762               case JavaTypes.CHAR:
  763                   return new Character(fetchCharField(field));
  764               case JavaTypes.DOUBLE:
  765                   return new Double(fetchDoubleField(field));
  766               case JavaTypes.FLOAT:
  767                   return new Float(fetchFloatField(field));
  768               case JavaTypes.INT:
  769                   return Numbers.valueOf(fetchIntField(field));
  770               case JavaTypes.LONG:
  771                   return Numbers.valueOf(fetchLongField(field));
  772               case JavaTypes.SHORT:
  773                   return new Short(fetchShortField(field));
  774               default:
  775                   return fetchObjectField(field);
  776           }
  777       }
  778   
  779       public void store(int field, Object val) {
  780           val = _meta.getField(field).getFieldValue(val, _broker);
  781           storeField(field, val);
  782       }
  783   
  784       public void storeField(int field, Object val) {
  785           storeField(field, val, this);
  786       }
  787   
  788       /**
  789        * <p>Checks whether or not <code>_pc</code> is dirty. In the cases where
  790        * field tracking is not happening (see below), this method will do a
  791        * state comparison to find whether <code>_pc</code> is dirty, and will
  792        * update this instance with this information. In the cases where field
  793        * tracking is happening, this method is a no-op.</p>
  794        *
  795        * <p>Fields are tracked for all classes that are run through the OpenJPA
  796        * enhancer prior to or during deployment, and all classes (enhanced or
  797        * unenhanced) in a Java 6 environment or newer.</p>
  798        *
  799        * <p>In a Java 5 VM or older:
  800        * <br>- instances of unenhanced classes that use
  801        * property access and obey the property access limitations are tracked
  802        * when the instances are loaded from the database by OpenJPA, and are
  803        * not tracked when the instances are created by application code.
  804        * <br>- instances of unenhanced classes that use field access are
  805        * never tracked.</p>
  806        *
  807        * @since 1.0.0
  808        */
  809       public void dirtyCheck() {
  810           if (!needsDirtyCheck())
  811               return;
  812   
  813           SaveFieldManager saved = getSaveFieldManager();
  814           if (saved == null)
  815               throw new InternalException(_loc.get("no-saved-fields",
  816                   getMetaData().getDescribedType().getName()));
  817   
  818           FieldMetaData[] fmds = getMetaData().getFields();
  819           for (int i = 0; i < fmds.length; i++) {
  820               // pk and version fields cannot be mutated; don't mark them
  821               // as such. ##### validate?
  822               if (!fmds[i].isPrimaryKey() && !fmds[i].isVersion()
  823                   && _loaded.get(i)) {
  824                   if (!saved.isFieldEqual(i, fetch(i))) {
  825                       dirty(i);
  826                   }
  827               }
  828           }
  829       }
  830   
  831       private boolean needsDirtyCheck() {
  832           if (isIntercepting())
  833               return false;
  834           if (isDeleted())
  835               return false;
  836           if (isNew() && !isFlushed())
  837               return false;
  838           return true;
  839       }
  840   
  841       public Object fetchInitialField(int field) {
  842           FieldMetaData fmd = _meta.getField(field);
  843           if (_broker.getRestoreState() == RestoreState.RESTORE_NONE
  844               && ((_flags & FLAG_INVERSES) == 0
  845               || fmd.getInverseMetaDatas().length == 0))
  846               throw new InvalidStateException(_loc.get("restore-unset"));
  847   
  848           switch (fmd.getDeclaredTypeCode()) {
  849               case JavaTypes.DATE:
  850               case JavaTypes.CALENDAR:
  851               case JavaTypes.ARRAY:
  852               case JavaTypes.COLLECTION:
  853               case JavaTypes.MAP:
  854               case JavaTypes.OBJECT:
  855                   // if we're not saving mutable types, throw an exception
  856                   if (_broker.getRestoreState() != RestoreState.RESTORE_ALL
  857                       && ((_flags & FLAG_INVERSES) == 0
  858                       || fmd.getInverseMetaDatas().length == 0))
  859                       throw new InvalidStateException(_loc.get
  860                           ("mutable-restore-unset"));
  861           }
  862   
  863           lock();
  864           try {
  865               if (_saved == null || !_loaded.get(field) || !_dirty.get(field))
  866                   return fetchField(field, false);
  867   
  868               // if the field is dirty but we never loaded it, we can't restore it
  869               if (_saved.getUnloaded().get(field))
  870                   throw new InvalidStateException(_loc.get("initial-unloaded",
  871                       fmd));
  872   
  873               provideField(_saved.getState(), _single, field);
  874               return fetchField(_single, fmd);
  875           } finally {
  876               unlock();
  877           }
  878       }
  879   
  880       /**
  881        * Fetch the specified field from the specified field manager, wrapping it
  882        * in an object if it's a primitive. A field should be provided to the
  883        * field manager before this call is made.
  884        */
  885       private static Object fetchField(FieldManager fm, FieldMetaData fmd) {
  886           int field = fmd.getIndex();
  887           switch (fmd.getDeclaredTypeCode()) {
  888               case JavaTypes.BOOLEAN:
  889                   return (fm.fetchBooleanField(field)) ? Boolean.TRUE
  890                       : Boolean.FALSE;
  891               case JavaTypes.BYTE:
  892                   return new Byte(fm.fetchByteField(field));
  893               case JavaTypes.CHAR:
  894                   return new Character(fm.fetchCharField(field));
  895               case JavaTypes.DOUBLE:
  896                   return new Double(fm.fetchDoubleField(field));
  897               case JavaTypes.FLOAT:
  898                   return new Float(fm.fetchFloatField(field));
  899               case JavaTypes.INT:
  900                   return Numbers.valueOf(fm.fetchIntField(field));
  901               case JavaTypes.LONG:
  902                   return Numbers.valueOf(fm.fetchLongField(field));
  903               case JavaTypes.SHORT:
  904                   return new Short(fm.fetchShortField(field));
  905               case JavaTypes.STRING:
  906                   return fm.fetchStringField(field);
  907               default:
  908                   return fm.fetchObjectField(field);
  909           }
  910       }
  911   
  912       public void setRemote(int field, Object value) {
  913           lock();
  914           try {
  915               Boolean stat = dirty(field, Boolean.FALSE, false);
  916               storeField(field, value, _single);
  917               replaceField(_pc, _single, field);
  918               postDirty(stat);
  919           } finally {
  920               unlock();
  921           }
  922       }
  923   
  924       ////////////////////////
  925       // Lifecycle operations
  926       ////////////////////////
  927   
  928       /**
  929        * Notification that the object is about to be accessed.
  930        *
  931        * @param field the field number being read, or -1 if not a single
  932        * field read
  933        */
  934       void beforeRead(int field) {
  935           // allow unmediated reads of primary key fields
  936           if (field != -1 && _meta.getField(field).isPrimaryKey())
  937               return;
  938   
  939           if (_broker.isActive() && !_broker.isTransactionEnding()) {
  940               if (_broker.getOptimistic())
  941                   setPCState(_state.beforeOptimisticRead(this, field));
  942               else
  943                   setPCState(_state.beforeRead(this, field));
  944           } else if (_broker.getNontransactionalRead())
  945               setPCState(_state.beforeNontransactionalRead(this, field));
  946           else
  947               throw new InvalidStateException(_loc.get("non-trans-read")).
  948                   setFailedObject(getManagedInstance());
  949       }
  950   
  951       /**
  952        * Delegates to the current state.
  953        *
  954        * @see PCState#beforeFlush
  955        */
  956       void beforeFlush(int reason, OpCallbacks call) {
  957           _state.beforeFlush(this, reason == BrokerImpl.FLUSH_LOGICAL, call);
  958       }
  959   
  960       /**
  961        * Delegates to the current state.
  962        *
  963        * @see PCState#flush
  964        */
  965       void afterFlush(int reason) {
  966           // nothing happens when we flush non-persistent states
  967           if (!isPersistent())
  968               return;
  969   
  970           if (reason != BrokerImpl.FLUSH_ROLLBACK
  971               && reason != BrokerImpl.FLUSH_LOGICAL) {
  972               // analyze previous state for later
  973               boolean wasNew = isNew();
  974               boolean wasFlushed = isFlushed();
  975               boolean wasDeleted = isDeleted();
  976   
  977               // all dirty fields were flushed
  978               _flush.or(_dirty);
  979   
  980               // important to set flushed bit after calling _state.flush so
  981               // that the state can tell whether this is the first flush
  982               setPCState(_state.flush(this));
  983               _flags |= FLAG_FLUSHED;
  984               _flags &= ~FLAG_FLUSHED_DIRTY;
  985   
  986               _flags &= ~FLAG_VERSION_CHECK;
  987               _flags &= ~FLAG_VERSION_UPDATE;
  988   
  989               // if this was an inc flush during which we had our identity
  990               // assigned, tell the broker to cache us under our final oid
  991               if (reason == BrokerImpl.FLUSH_INC)
  992                   assertObjectIdAssigned(true);
  993   
  994               // if this object was stored with preFlush, do post-store callback
  995               if ((_flags & FLAG_PRE_FLUSHED) > 0)
  996                   fireLifecycleEvent(LifecycleEvent.AFTER_STORE);
  997   
  998               // do post-update as needed
  999               if (wasNew && !wasFlushed)
 1000                   fireLifecycleEvent(LifecycleEvent.AFTER_PERSIST_PERFORMED);
 1001               else if (wasDeleted)
 1002                   fireLifecycleEvent(LifecycleEvent.AFTER_DELETE_PERFORMED);
 1003               else 
 1004                   // updates and new-flushed with changes
 1005                   fireLifecycleEvent(LifecycleEvent.AFTER_UPDATE_PERFORMED);
 1006           } else if (reason == BrokerImpl.FLUSH_ROLLBACK) {
 1007               // revert to last loaded version and original oid
 1008               assignVersionField(_loadVersion);
 1009               if (isNew() && (_flags & FLAG_OID_ASSIGNED) == 0)
 1010                   _oid = null;
 1011           }
 1012           _flags &= ~FLAG_PRE_FLUSHED;
 1013       }
 1014   
 1015       /**
 1016        * Delegates to the current state after checking the value
 1017        * of the RetainState flag.
 1018        *
 1019        * @see PCState#commit
 1020        * @see PCState#commitRetain
 1021        */
 1022       void commit() {
 1023           // release locks before oid updated
 1024           releaseLocks();
 1025   
 1026           // update version and oid information
 1027           setVersion(_version);
 1028           _flags &= ~FLAG_FLUSHED;
 1029           _flags &= ~FLAG_FLUSHED_DIRTY;
 1030   
 1031           Object orig = _id;
 1032           assertObjectIdAssigned(false);
 1033   
 1034           boolean wasNew = isNew() && !isDeleted() && !isProvisional();
 1035           if (_broker.getRetainState())
 1036               setPCState(_state.commitRetain(this));
 1037           else
 1038               setPCState(_state.commit(this));
 1039   
 1040           // ask the broker to re-cache us if we were new previously
 1041           if (wasNew)
 1042               _broker.setStateManager(orig, this, BrokerImpl.STATUS_COMMIT_NEW);
 1043       }
 1044   
 1045       /**
 1046        * Delegates to the current state after checking the value
 1047        * of the RetainState flag.
 1048        *
 1049        * @see PCState#rollback
 1050        * @see PCState#rollbackRestore
 1051        */
 1052       void rollback() {
 1053           // release locks
 1054           releaseLocks();
 1055           _flags &= ~FLAG_FLUSHED;
 1056           _flags &= ~FLAG_FLUSHED_DIRTY;
 1057           afterFlush(BrokerImpl.FLUSH_ROLLBACK);
 1058   
 1059           if (_broker.getRestoreState() != RestoreState.RESTORE_NONE)
 1060               setPCState(_state.rollbackRestore(this));
 1061           else
 1062               setPCState(_state.rollback(this));
 1063       }
 1064   
 1065       /**
 1066        * Rollback state of the managed instance to the given savepoint.
 1067        */
 1068       void rollbackToSavepoint(SavepointFieldManager savepoint) {
 1069           _state = savepoint.getPCState();
 1070           BitSet loaded = savepoint.getLoaded();
 1071           for (int i = 0, len = loaded.length(); i < len; i++) {
 1072               if (loaded.get(i) && savepoint.restoreField(i)) {
 1073                   provideField(savepoint.getCopy(), savepoint, i);
 1074                   replaceField(_pc, savepoint, i);
 1075               }
 1076           }
 1077           _loaded = loaded;
 1078           _dirty = savepoint.getDirty();
 1079           _flush = savepoint.getFlushed();
 1080           _version = savepoint.getVersion();
 1081           _loadVersion = savepoint.getLoadVersion();
 1082       }
 1083   
 1084       /**
 1085        * Delegates to the current state.
 1086        *
 1087        * @see PCState#persist
 1088        * @see Broker#persist
 1089        */
 1090       void persist() {
 1091           setPCState(_state.persist(this));
 1092       }
 1093   
 1094       /**
 1095        * Delegates to the current state.
 1096        *
 1097        * @see PCState#delete
 1098        * @see Broker#delete
 1099        */
 1100       void delete() {
 1101           setPCState(_state.delete(this));
 1102       }
 1103   
 1104       /**
 1105        * Delegates to the current state.
 1106        *
 1107        * @see PCState#nontransactional
 1108        * @see Broker#nontransactional
 1109        */
 1110       void nontransactional() {
 1111           setPCState(_state.nontransactional(this));
 1112       }
 1113   
 1114       /**
 1115        * Delegates to the current state.
 1116        *
 1117        * @see PCState#transactional
 1118        * @see Broker#transactional
 1119        */
 1120       void transactional() {
 1121           setPCState(_state.transactional(this));
 1122       }
 1123   
 1124       /**
 1125        * Delegates to the current state.
 1126        *
 1127        * @see PCState#nonprovisional
 1128        */
 1129       void nonprovisional(boolean logical, OpCallbacks call) {
 1130           setPCState(_state.nonprovisional(this, logical, call));
 1131       }
 1132   
 1133       /**
 1134        * Delegates to the current state.
 1135        *
 1136        * @see PCState#release
 1137        * @see Broker#release
 1138        */
 1139       void release(boolean unproxy) {
 1140           release(unproxy, false);
 1141       }
 1142   
 1143       void release(boolean unproxy, boolean force) {
 1144           // optimization for detach-in-place special case when fields are
 1145           // already (un)proxied correctly
 1146           if (!unproxy)
 1147               _flags |= FLAG_NO_UNPROXY;
 1148           try {
 1149               if (force)
 1150                   setPCState(PCState.TRANSIENT);
 1151               else
 1152                   setPCState(_state.release(this));
 1153           } finally {
 1154               _flags &= ~FLAG_NO_UNPROXY;
 1155           }
 1156       }
 1157   
 1158       /**
 1159        * Delegates to the current state.
 1160        *
 1161        * @see PCState#evict
 1162        * @see Broker#evict
 1163        */
 1164       void evict() {
 1165           setPCState(_state.evict(this));
 1166       }
 1167   
 1168       /**
 1169        * Gather relations reachable from values using
 1170        * {@link ValueMetaData#CASCADE_IMMEDIATE}.
 1171        */
 1172       void gatherCascadeRefresh(OpCallbacks call) {
 1173           FieldMetaData[] fmds = _meta.getFields();
 1174           for (int i = 0; i < fmds.length; i++) {
 1175               if (!_loaded.get(i))
 1176                   continue;
 1177   
 1178               if (fmds[i].getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE
 1179                   || fmds[i].getKey().getCascadeRefresh()
 1180                   == ValueMetaData.CASCADE_IMMEDIATE
 1181                   || fmds[i].getElement().getCascadeRefresh()
 1182                   == ValueMetaData.CASCADE_IMMEDIATE) {
 1183                   _single.storeObjectField(i, fetchField(i, false));
 1184                   _single.gatherCascadeRefresh(call);
 1185                   _single.clear();
 1186               }
 1187           }
 1188       }
 1189   
 1190       public boolean beforeRefresh(boolean refreshAll) {
 1191           // note: all logic placed here rather than in the states for
 1192           // optimization; this method public b/c used by remote package
 1193   
 1194           // nothing to do for non persistent or new unflushed instances
 1195           if (!isPersistent() || (isNew() && !isFlushed()))
 1196               return false;
 1197   
 1198           lock();
 1199           try {
 1200               // if dirty need to clear fields
 1201               if (isDirty()) {
 1202                   clearFields();
 1203                   return true;
 1204               }
 1205   
 1206               // if some fields have been loaded but the instance is out of
 1207               // date or this is part of a refreshAll() and we don't want to
 1208               // take the extra hit to see if the instance is out of date, clear
 1209               if (_loaded.length() > 0 && (refreshAll || isEmbedded()
 1210                   || !syncVersion(null))) {
 1211                   Object version = _version;
 1212                   clearFields();
 1213   
 1214                   // if syncVersion just replaced the version, reset it
 1215                   if (!refreshAll && !isEmbedded())
 1216                       setVersion(version);
 1217                   return true;
 1218               }
 1219               return false;
 1220           } finally {
 1221               unlock();
 1222           }
 1223       }
 1224   
 1225       /**
 1226        * Perform state transitions after refresh. This method is only
 1227        * called if {@link #beforeRefresh} returns true.
 1228        */
 1229       void afterRefresh() {
 1230           lock();
 1231           try {
 1232               // transition to clean or nontransactional depending on trans status
 1233               if (!_broker.isActive())
 1234                   setPCState(_state.afterNontransactionalRefresh());
 1235               else if (_broker.getOptimistic())
 1236                   setPCState(_state.afterOptimisticRefresh());
 1237               else
 1238                   setPCState(_state.afterRefresh());
 1239           } finally {
 1240               unlock();
 1241           }
 1242       }
 1243   
 1244       /**
 1245        * Mark this object as a dereferenced dependent object.
 1246        */
 1247       void setDereferencedDependent(boolean deref, boolean notify) {
 1248           if (!deref && (_flags & FLAG_DEREF) > 0) {
 1249               if (notify)
 1250                   _broker.removeDereferencedDependent(this);
 1251               _flags &= ~FLAG_DEREF;
 1252           } else if (deref && (_flags & FLAG_DEREF) == 0) {
 1253               _flags |= FLAG_DEREF;
 1254               if (notify)
 1255                   _broker.addDereferencedDependent(this);
 1256           }
 1257       }
 1258   
 1259       ///////////
 1260       // Locking
 1261       ///////////
 1262   
 1263       /**
 1264        * Notification that we've been read-locked. Pass in the level at which
 1265        * we were locked and the level at which we should write lock ourselves
 1266        * on dirty.
 1267        */
 1268       void readLocked(int readLockLevel, int writeLockLevel) {
 1269           // make sure object is added to transaction so lock will get
 1270           // cleared on commit/rollback
 1271           if (readLockLevel != LockLevels.LOCK_NONE)
 1272               transactional();
 1273   
 1274           _readLockLevel = readLockLevel;
 1275           _writeLockLevel = writeLockLevel;
 1276           _flags |= FLAG_READ_LOCKED;
 1277           _flags &= ~FLAG_WRITE_LOCKED;
 1278       }
 1279   
 1280       /**
 1281        * Return the lock level to use when loading state.
 1282        */
 1283       private int calculateLockLevel(boolean active, boolean forWrite,
 1284           FetchConfiguration fetch) {
 1285           if (!active)
 1286               return LockLevels.LOCK_NONE;
 1287           if (fetch == null)
 1288               fetch = _broker.getFetchConfiguration();
 1289   
 1290           if (_readLockLevel == -1)
 1291               _readLockLevel = fetch.getReadLockLevel();
 1292           if (_writeLockLevel == -1)
 1293               _writeLockLevel = fetch.getWriteLockLevel();
 1294           return (forWrite) ? _writeLockLevel : _readLockLevel;
 1295       }
 1296   
 1297       /**
 1298        * Make sure we're locked at the given level.
 1299        */
 1300       private void obtainLocks(boolean active, boolean forWrite, int lockLevel,
 1301           FetchConfiguration fetch, Object sdata) {
 1302           if (!active)
 1303               return;
 1304   
 1305           // if we haven't been locked yet, lock now at the given level
 1306           int flag = (forWrite) ? FLAG_WRITE_LOCKED : FLAG_READ_LOCKED;
 1307           if ((_flags & flag) == 0) {
 1308               // make sure object is added to transaction so lock will get
 1309               // cleared on commit/rollback
 1310               if (lockLevel != LockLevels.LOCK_NONE)
 1311                   transactional();
 1312   
 1313               if (fetch == null)
 1314                   fetch = _broker.getFetchConfiguration();
 1315               _broker.getLockManager().lock(this, lockLevel,
 1316                   fetch.getLockTimeout(), sdata);
 1317               _flags |= FLAG_READ_LOCKED;
 1318               _flags |= flag;
 1319           }
 1320       }
 1321   
 1322       /**
 1323        * Release locks.
 1324        */
 1325       private void releaseLocks() {
 1326           if (_lock != null)
 1327               _broker.getLockManager().release(this);
 1328           _readLockLevel = -1;
 1329           _writeLockLevel = -1;
 1330           _flags &= ~FLAG_READ_LOCKED;
 1331           _flags &= ~FLAG_WRITE_LOCKED;
 1332       }
 1333   
 1334       ////////////////////////////////////////////
 1335       // Implementation of StateManager interface
 1336       ////////////////////////////////////////////
 1337   
 1338       /**
 1339        * @return whether or not unloaded fields should be closed.
 1340        */
 1341       public boolean serializing() {
 1342           // if the broker is in the midst of a serialization, then no special
 1343           // handling should be performed on the instance, and no subsequent
 1344           // load should happen
 1345           if (_broker.isSerializing())
 1346               return false;
 1347   
 1348           try {
 1349               if (_meta.isDetachable())
 1350                   return DetachManager.preSerialize(this);
 1351   
 1352               load(_broker.getFetchConfiguration(), LOAD_SERIALIZE, null, null, 
 1353                   false);
 1354               return false;
 1355           } catch (RuntimeException re) {
 1356               throw translate(re);
 1357           }
 1358       }
 1359   
 1360       public boolean writeDetached(ObjectOutput out)
 1361           throws IOException {
 1362           BitSet idxs = new BitSet(_meta.getFields().length);
 1363           lock();
 1364           try {
 1365               boolean detsm = DetachManager.writeDetachedState(this, out, idxs);
 1366               if (detsm)
 1367                   _flags |= FLAG_DETACHING;
 1368   
 1369               FieldMetaData[] fmds = _meta.getFields();
 1370               for (int i = 0; i < fmds.length; i++) {
 1371                   if (fmds[i].isTransient())
 1372                       continue;
 1373                   provideField(_pc, _single, i);
 1374                   _single.serialize(out, !idxs.get(i));
 1375                   _single.clear();
 1376               }
 1377               return true;
 1378           } catch (RuntimeException re) {
 1379               throw translate(re);
 1380           } finally {
 1381               _flags &= ~FLAG_DETACHING;
 1382               unlock();
 1383           }
 1384       }
 1385   
 1386       public void proxyDetachedDeserialized(int idx) {
 1387           // we don't serialize state manager impls
 1388           throw new InternalException();
 1389       }
 1390   
 1391       public boolean isTransactional() {
 1392           // special case for TCLEAN, which we want to appear non-trans to
 1393           // internal code, but which publicly should be transactional
 1394           return _state == PCState.TCLEAN || _state.isTransactional();
 1395       }
 1396   
 1397       public boolean isPendingTransactional() {
 1398           return _state.isPendingTransactional();
 1399       }
 1400   
 1401       public boolean isProvisional() {
 1402           return _state.isProvisional();
 1403       }
 1404   
 1405       public boolean isPersistent() {
 1406           return _state.isPersistent();
 1407       }
 1408   
 1409       public boolean isNew() {
 1410           return _state.isNew();
 1411       }
 1412   
 1413       public boolean isDeleted() {
 1414           return _state.isDeleted();
 1415       }
 1416   
 1417       public boolean isDirty() {
 1418           return _state.isDirty();
 1419       }
 1420   
 1421       public boolean isDetached() {
 1422           return (_flags & FLAG_DETACHING) != 0;
 1423       }
 1424   
 1425       public Object getGenericContext() {
 1426           return _broker;
 1427       }
 1428   
 1429       public Object fetchObjectId() {
 1430           try {
 1431               assignObjectId(true);
 1432               if (_oid == null || !_broker.getConfiguration().
 1433                   getCompatibilityInstance().getCopyObjectIds())
 1434                   return _oid;
 1435   
 1436               if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
 1437                   return _broker.getStoreManager().copyDataStoreId(_oid, _meta);
 1438               return ApplicationIds.copy(_oid, _meta);
 1439           } catch (RuntimeException re) {
 1440               throw translate(re);
 1441           }
 1442       }
 1443   
 1444       public Object getPCPrimaryKey(Object oid, int field) {
 1445           FieldMetaData fmd = _meta.getField(field);
 1446           Object pk = ApplicationIds.get(oid, fmd);
 1447           if (pk == null)
 1448               return null;
 1449   
 1450           ClassMetaData relmeta = fmd.getDeclaredTypeMetaData();
 1451           if (relmeta.getIdentityType() == ClassMetaData.ID_DATASTORE
 1452               && fmd.getObjectIdFieldTypeCode() == JavaTypes.LONG)
 1453               pk = _broker.getStoreManager().newDataStoreId(pk, relmeta);
 1454           else if (relmeta.getIdentityType() == ClassMetaData.ID_APPLICATION 
 1455               && fmd.getObjectIdFieldType() != relmeta.getObjectIdType())
 1456               pk = ApplicationIds.fromPKValues(new Object[] { pk }, relmeta);
 1457           return _broker.find(pk, false, null);
 1458       }
 1459   
 1460       public byte replaceFlags() {
 1461           // we always use load required so that we can detect when objects
 1462           // are touched for locking or making transactional
 1463           return PersistenceCapable.LOAD_REQUIRED;
 1464       }
 1465   
 1466       public StateManager replaceStateManager(StateManager sm) {
 1467           return sm;
 1468       }
 1469   
 1470       public void accessingField(int field) {
 1471           // possibly change state
 1472           try {
 1473               beforeRead(field);
 1474               beforeAccessField(field);
 1475           } catch (RuntimeException re) {
 1476               throw translate(re);
 1477           }
 1478       }
 1479   
 1480       /**
 1481        * Load the given field before access.
 1482        */
 1483       protected void beforeAccessField(int field) {
 1484           lock();
 1485           try {
 1486               boolean active = _broker.isActive();
 1487               int lockLevel = calculateLockLevel(active, false, null);
 1488               if (!_loaded.get(field))
 1489                   loadField(field, lockLevel, false, true);
 1490               else
 1491                   assignField(field, false);
 1492               obtainLocks(active, false, lockLevel, null, null);
 1493           } catch (RuntimeException re) {
 1494               throw translate(re);
 1495           } finally {
 1496               unlock();
 1497           }
 1498       }
 1499   
 1500       public void dirty(String field) {
 1501           FieldMetaData fmd = _meta.getField(field);
 1502           if (fmd == null)
 1503               throw translate(new UserException(_loc.get("no-field", field,
 1504                   ImplHelper.getManagedInstance(_pc).getClass()))
 1505                   .setFailedObject(getManagedInstance()));
 1506   
 1507           dirty(fmd.getIndex(), null, true);
 1508       }
 1509   
 1510       public void dirty(int field) {
 1511           dirty(field, null, true);
 1512       }
 1513   
 1514       /**
 1515        * Make the given field dirty.
 1516        *
 1517        * @param mutate if null, may be an SCO mutation; if true, is certainly
 1518        * a mutation (or at least treat as one)
 1519        * @return {@link Boolean#FALSE} if this instance was already dirty,
 1520        * <code>null</code> if it was dirty but not since flush, and
 1521        * {@link Boolean#TRUE} if it was not dirty
 1522        */
 1523       private Boolean dirty(int field, Boolean mutate, boolean loadFetchGroup) {
 1524           boolean locked = false;
 1525           boolean newFlush = false;
 1526           boolean clean = false;
 1527           try {
 1528               FieldMetaData fmd = _meta.getField(field);
 1529               if (!isNew() || isFlushed()) {
 1530                   if (fmd.getUpdateStrategy() == UpdateStrategies.RESTRICT)
 1531                       throw new InvalidStateException(_loc.get
 1532                           ("update-restrict", fmd));
 1533                   if (fmd.getUpdateStrategy() == UpdateStrategies.IGNORE)
 1534                       return Boolean.FALSE;
 1535               }
 1536   
 1537               if (isEmbedded()) {
 1538                   // notify owner of change
 1539                   _owner.dirty(_ownerIndex, Boolean.TRUE, loadFetchGroup);
 1540               }
 1541   
 1542               // is this a direct mutation of an sco field?
 1543               if (mutate == null) {
 1544                   switch (fmd.getDeclaredTypeCode()) {
 1545                       case JavaTypes.COLLECTION:
 1546                       case JavaTypes.MAP:
 1547                       case JavaTypes.ARRAY:
 1548                       case JavaTypes.DATE:
 1549                       case JavaTypes.CALENDAR:
 1550                       case JavaTypes.OBJECT:
 1551                           mutate = Boolean.TRUE;
 1552                           break;
 1553                       case JavaTypes.PC:
 1554                           mutate =
 1555                               (fmd.isEmbedded()) ? Boolean.TRUE : Boolean.FALSE;
 1556                           break;
 1557                       default:
 1558                           mutate = Boolean.FALSE; // not sco
 1559                   }
 1560               }
 1561   
 1562               // possibly change state
 1563               boolean active = _broker.isActive();
 1564               clean = !_state.isDirty(); // intentional direct access
 1565   
 1566               // fire event fast before state change.
 1567               if (clean)
 1568                   fireLifecycleEvent(LifecycleEvent.BEFORE_DIRTY);
 1569               if (active) {
 1570                   if (_broker.getOptimistic())
 1571                       setPCState(_state.beforeOptimisticWrite(this, field,
 1572                           mutate.booleanValue()));
 1573                   else
 1574                       setPCState(_state.beforeWrite(this, field,
 1575                           mutate.booleanValue()));
 1576               } else if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT) {
 1577                   if (isPersistent() && !_broker.getNontransactionalWrite())
 1578                       throw new InvalidStateException(_loc.get
 1579                           ("non-trans-write")).setFailedObject
 1580                           (getManagedInstance());
 1581   
 1582                   setPCState(_state.beforeNontransactionalWrite(this, field,
 1583                       mutate.booleanValue()));
 1584               }
 1585   
 1586               if ((_flags & FLAG_FLUSHED) != 0) {
 1587                   newFlush = (_flags & FLAG_FLUSHED_DIRTY) == 0;
 1588                   _flags |= FLAG_FLUSHED_DIRTY;
 1589               }
 1590   
 1591               lock();
 1592               locked = true;
 1593   
 1594               // note that the field is in need of flushing again, and tell the
 1595               // broker too
 1596               _flush.clear(field);
 1597               _broker.setDirty(this, newFlush && !clean);
 1598   
 1599               // save the field for rollback if needed
 1600               saveField(field);
 1601   
 1602               // dirty the field and mark loaded; load fetch group if needed
 1603               int lockLevel = calculateLockLevel(active, true, null);
 1604               if (!_dirty.get(field)) {
 1605                   setLoaded(field, true);
 1606                   _dirty.set(field);
 1607   
 1608                   // make sure the field's fetch group is loaded
 1609                   if (loadFetchGroup && isPersistent()
 1610                       && fmd.getManagement() == fmd.MANAGE_PERSISTENT)
 1611                       loadField(field, lockLevel, true, true);
 1612               }
 1613               obtainLocks(active, true, lockLevel, null, null);
 1614           } catch (RuntimeException re) {
 1615               throw translate(re);
 1616           } finally {
 1617               if (locked)
 1618                   unlock();
 1619           }
 1620   
 1621           if (clean)
 1622               return Boolean.TRUE;
 1623           if (newFlush) {
 1624               // this event can be fired later cause we're already dirty.
 1625               fireLifecycleEvent(LifecycleEvent.BEFORE_DIRTY_FLUSHED);
 1626               return null;
 1627           }
 1628           return Boolean.FALSE;
 1629       }
 1630   
 1631       /**
 1632        * Fire post-dirty events after field value changes.
 1633        *
 1634        * @param status return value from {@link #dirty(int, Boolean, boolean)}
 1635        */
 1636       private void postDirty(Boolean status) {
 1637           if (Boolean.TRUE.equals(status))
 1638               fireLifecycleEvent(LifecycleEvent.AFTER_DIRTY);
 1639           else if (status == null)
 1640               fireLifecycleEvent(LifecycleEvent.AFTER_DIRTY_FLUSHED);
 1641       }
 1642   
 1643       public void removed(int field, Object removed, boolean key) {
 1644           if (removed == null)
 1645               return;
 1646   
 1647           try {
 1648               // dereference dependent fields, delete embedded
 1649               FieldMetaData fmd = _meta.getField(field);
 1650               ValueMetaData vmd = (key) ? fmd.getKey() : fmd.getElement();
 1651               if (vmd.isEmbeddedPC())
 1652                   _single.delete(vmd, removed, null);
 1653               else if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO)
 1654                   _single.dereferenceDependent(removed);
 1655           } catch (RuntimeException re) {
 1656               throw translate(re);
 1657           }
 1658       }
 1659   
 1660       public Object newProxy(int field) {
 1661           FieldMetaData fmd = _meta.getField(field);
 1662           if (!fmd.isExternalized())
 1663               return newFieldProxy(field);
 1664   
 1665           switch (fmd.getTypeCode()) {
 1666               case JavaTypes.DATE:
 1667                   if (fmd.getDeclaredType() == java.sql.Date.class)
 1668                       return new java.sql.Date(System.currentTimeMillis());
 1669                   if (fmd.getDeclaredType() == java.sql.Timestamp.class)
 1670                       return new java.sql.Timestamp(System.currentTimeMillis());
 1671                   if (fmd.getDeclaredType() == java.sql.Time.class)
 1672                       return new java.sql.Time(System.currentTimeMillis());
 1673                   return new Date();
 1674               case JavaTypes.CALENDAR:
 1675                   return Calendar.getInstance();
 1676               case JavaTypes.COLLECTION:
 1677                   return new ArrayList();
 1678               case JavaTypes.MAP:
 1679                   return new HashMap();
 1680           }
 1681           return null;
 1682       }
 1683   
 1684       public Object newFieldProxy(int field) {
 1685           FieldMetaData fmd = _meta.getField(field);
 1686           ProxyManager mgr = _broker.getConfiguration().
 1687               getProxyManagerInstance();
 1688           Object init = fmd.getInitializer();
 1689   
 1690           switch (fmd.getDeclaredTypeCode()) {
 1691               case JavaTypes.DATE:
 1692                   return mgr.newDateProxy(fmd.getDeclaredType());
 1693               case JavaTypes.CALENDAR:
 1694                   return mgr.newCalendarProxy(fmd.getDeclaredType(),
 1695                       init instanceof TimeZone ? (TimeZone) init : null);
 1696               case JavaTypes.COLLECTION:
 1697                   return mgr.newCollectionProxy(fmd.getProxyType(),
 1698                       fmd.getElement().getDeclaredType(),
 1699                       init instanceof Comparator ? (Comparator) init : null);
 1700               case JavaTypes.MAP:
 1701                   return mgr.newMapProxy(fmd.getProxyType(),
 1702                       fmd.getKey().getDeclaredType(),
 1703                       fmd.getElement().getDeclaredType(),
 1704                       init instanceof Comparator ? (Comparator) init : null);
 1705           }
 1706           return null;
 1707       }
 1708   
 1709       public boolean isDefaultValue(int field) {
 1710           lock();
 1711           try {
 1712               _single.clear();
 1713               provideField(_pc, _single, field);
 1714               boolean ret = _single.isDefaultValue();
 1715               _single.clear();
 1716               return ret;
 1717           } finally {
 1718               unlock();
 1719           }
 1720       }
 1721   
 1722       /////////////////////////////////////////////////////////
 1723       // Record that the field is dirty (which might load DFG)
 1724       /////////////////////////////////////////////////////////
 1725   
 1726       public void settingBooleanField(PersistenceCapable pc, int field,
 1727           boolean curVal, boolean newVal, int set) {
 1728           if (set != SET_REMOTE) {
 1729               if (newVal == curVal && _loaded.get(field))
 1730                   return;
 1731               assertNoPrimaryKeyChange(field);
 1732           }
 1733   
 1734           lock();
 1735           try {
 1736               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1737               _single.storeBooleanField(field, newVal);
 1738               replaceField(pc, _single, field);
 1739               postDirty(stat);
 1740           } finally {
 1741               unlock();
 1742           }
 1743       }
 1744   
 1745       public void settingByteField(PersistenceCapable pc, int field,
 1746           byte curVal, byte newVal, int set) {
 1747           if (set != SET_REMOTE) {
 1748               if (newVal == curVal && _loaded.get(field))
 1749                   return;
 1750               assertNoPrimaryKeyChange(field);
 1751           }
 1752   
 1753           lock();
 1754           try {
 1755               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1756               _single.storeByteField(field, newVal);
 1757               replaceField(pc, _single, field);
 1758               postDirty(stat);
 1759           } finally {
 1760               unlock();
 1761           }
 1762       }
 1763   
 1764       public void settingCharField(PersistenceCapable pc, int field,
 1765           char curVal, char newVal, int set) {
 1766           if (set != SET_REMOTE) {
 1767               if (newVal == curVal && _loaded.get(field))
 1768                   return;
 1769               assertNoPrimaryKeyChange(field);
 1770           }
 1771   
 1772           lock();
 1773           try {
 1774               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1775               _single.storeCharField(field, newVal);
 1776               replaceField(pc, _single, field);
 1777               postDirty(stat);
 1778           } finally {
 1779               unlock();
 1780           }
 1781       }
 1782   
 1783       public void settingDoubleField(PersistenceCapable pc, int field,
 1784           double curVal, double newVal, int set) {
 1785           if (set != SET_REMOTE) {
 1786               if (newVal == curVal && _loaded.get(field))
 1787                   return;
 1788               assertNoPrimaryKeyChange(field);
 1789           }
 1790   
 1791           lock();
 1792           try {
 1793               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1794               _single.storeDoubleField(field, newVal);
 1795               replaceField(pc, _single, field);
 1796               postDirty(stat);
 1797           } finally {
 1798               unlock();
 1799           }
 1800       }
 1801   
 1802       public void settingFloatField(PersistenceCapable pc, int field,
 1803           float curVal, float newVal, int set) {
 1804           if (set != SET_REMOTE) {
 1805               if (newVal == curVal && _loaded.get(field))
 1806                   return;
 1807               assertNoPrimaryKeyChange(field);
 1808           }
 1809   
 1810           lock();
 1811           try {
 1812               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1813               _single.storeFloatField(field, newVal);
 1814               replaceField(pc, _single, field);
 1815               postDirty(stat);
 1816           } finally {
 1817               unlock();
 1818           }
 1819       }
 1820   
 1821       public void settingIntField(PersistenceCapable pc, int field,
 1822           int curVal, int newVal, int set) {
 1823           if (set != SET_REMOTE) {
 1824               if (newVal == curVal && _loaded.get(field))
 1825                   return;
 1826               assertNoPrimaryKeyChange(field);
 1827           }
 1828   
 1829           lock();
 1830           try {
 1831               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1832               _single.storeIntField(field, newVal);
 1833               replaceField(pc, _single, field);
 1834               postDirty(stat);
 1835           } finally {
 1836               unlock();
 1837           }
 1838       }
 1839   
 1840       public void settingLongField(PersistenceCapable pc, int field,
 1841           long curVal, long newVal, int set) {
 1842           if (set != SET_REMOTE) {
 1843               if (newVal == curVal && _loaded.get(field))
 1844                   return;
 1845               assertNoPrimaryKeyChange(field);
 1846           }
 1847   
 1848           lock();
 1849           try {
 1850               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1851               _single.storeLongField(field, newVal);
 1852               replaceField(pc, _single, field);
 1853               postDirty(stat);
 1854           } finally {
 1855               unlock();
 1856           }
 1857       }
 1858   
 1859       public void settingObjectField(PersistenceCapable pc, int field,
 1860           Object curVal, Object newVal, int set) {
 1861           if (set != SET_REMOTE) {
 1862               FieldMetaData fmd = _meta.getField(field);
 1863               if (_loaded.get(field)) {
 1864                   if (newVal == curVal)
 1865                       return;
 1866   
 1867                   // only compare new to old values if the comparison is going to
 1868                   // be cheap -- don't compare collections, maps, UDTs
 1869                   switch (fmd.getDeclaredTypeCode()) {
 1870                       case JavaTypes.ARRAY:
 1871                       case JavaTypes.COLLECTION:
 1872                       case JavaTypes.MAP:
 1873                       case JavaTypes.PC:
 1874                       case JavaTypes.PC_UNTYPED:
 1875                           break;
 1876                       default:
 1877                           if (newVal != null && newVal.equals(curVal))
 1878                               return;
 1879                   }
 1880               } else {
 1881                   // if this is a dependent unloaded field, make sure to load
 1882                   // it now
 1883                   if (fmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO
 1884                       || fmd.getKey().getCascadeDelete()
 1885                       == ValueMetaData.CASCADE_AUTO
 1886                       || fmd.getElement().getCascadeDelete()
 1887                       == ValueMetaData.CASCADE_AUTO)
 1888                       curVal = fetchObjectField(field);
 1889               }
 1890   
 1891               assertNoPrimaryKeyChange(field);
 1892               if (fmd.getDeclaredTypeCode() == JavaTypes.OID)
 1893                   assertNotManagedObjectId(newVal);
 1894           }
 1895   
 1896           lock();
 1897           try {
 1898               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1899               if (set != SET_REMOTE) {
 1900                   _single.storeObjectField(field, curVal);
 1901                   _single.unproxy();
 1902                   _single.dereferenceDependent();
 1903                   _single.clear();
 1904               }
 1905               _single.storeObjectField(field, newVal);
 1906               replaceField(pc, _single, field);
 1907               postDirty(stat);
 1908           } finally {
 1909               unlock();
 1910           }
 1911       }
 1912   
 1913       public void settingShortField(PersistenceCapable pc, int field,
 1914           short curVal, short newVal, int set) {
 1915           if (set != SET_REMOTE) {
 1916               if (newVal == curVal && _loaded.get(field))
 1917                   return;
 1918               assertNoPrimaryKeyChange(field);
 1919           }
 1920   
 1921           lock();
 1922           try {
 1923               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1924               _single.storeShortField(field, newVal);
 1925               replaceField(pc, _single, field);
 1926               postDirty(stat);
 1927           } finally {
 1928               unlock();
 1929           }
 1930       }
 1931   
 1932       public void settingStringField(PersistenceCapable pc, int field,
 1933           String curVal, String newVal, int set) {
 1934           if (set != SET_REMOTE) {
 1935               if (StringUtils.equals(newVal, curVal) && _loaded.get(field))
 1936                   return;
 1937               assertNoPrimaryKeyChange(field);
 1938           }
 1939   
 1940           lock();
 1941           try {
 1942               Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
 1943               _single.storeStringField(field, newVal);
 1944               replaceField(pc, _single, field);
 1945               postDirty(stat);
 1946           } finally {
 1947               unlock();
 1948           }
 1949       }
 1950   
 1951       /**
 1952        * Disallows changing primary key fields for instances.
 1953        */
 1954       private void assertNoPrimaryKeyChange(int field) {
 1955           if (_oid != null && _meta.getField(field).isPrimaryKey())
 1956               throw translate(new InvalidStateException(_loc.get
 1957                   ("change-identity")).setFailedObject(getManagedInstance()));
 1958       }
 1959   
 1960       /**
 1961        * Disallows setting an object id field to a managed instance.
 1962        */
 1963       void assertNotManagedObjectId(Object val) {
 1964           if (val != null
 1965               && (ImplHelper.toPersistenceCapable(val,
 1966                    getContext().getConfiguration())).pcGetGenericContext()!= null)
 1967               throw translate(new InvalidStateException(_loc.get
 1968                   ("managed-oid", Exceptions.toString(val),
 1969                       Exceptions.toString(getManagedInstance()))).
 1970                   setFailedObject(getManagedInstance()));
 1971       }
 1972   
 1973       ////////////////////////////
 1974       // Delegate to FieldManager
 1975       ////////////////////////////
 1976   
 1977       public void providedBooleanField(PersistenceCapable pc, int field,
 1978           boolean curVal) {
 1979           _fm.storeBooleanField(field, curVal);
 1980       }
 1981   
 1982       public void providedByteField(PersistenceCapable pc, int field,
 1983           byte curVal) {
 1984           _fm.storeByteField(field, curVal);
 1985       }
 1986   
 1987       public void providedCharField(PersistenceCapable pc, int field,
 1988           char curVal) {
 1989           _fm.storeCharField(field, curVal);
 1990       }
 1991   
 1992       public void providedDoubleField(PersistenceCapable pc, int field,
 1993           double curVal) {
 1994           _fm.storeDoubleField(field, curVal);
 1995       }
 1996   
 1997       public void providedFloatField(PersistenceCapable pc, int field,
 1998           float curVal) {
 1999           _fm.storeFloatField(field, curVal);
 2000       }
 2001   
 2002       public void providedIntField(PersistenceCapable pc, int field,
 2003           int curVal) {
 2004           _fm.storeIntField(field, curVal);
 2005       }
 2006   
 2007       public void providedLongField(PersistenceCapable pc, int field,
 2008           long curVal) {
 2009           _fm.storeLongField(field, curVal);
 2010       }
 2011   
 2012       public void providedObjectField(PersistenceCapable pc, int field,
 2013           Object curVal) {
 2014           _fm.storeObjectField(field, curVal);
 2015       }
 2016   
 2017       public void providedShortField(PersistenceCapable pc, int field,
 2018           short curVal) {
 2019           _fm.storeShortField(field, curVal);
 2020       }
 2021   
 2022       public void providedStringField(PersistenceCapable pc, int field,
 2023           String curVal) {
 2024           _fm.storeStringField(field, curVal);
 2025       }
 2026   
 2027       public boolean replaceBooleanField(PersistenceCapable pc, int field) {
 2028           return _fm.fetchBooleanField(field);
 2029       }
 2030   
 2031       public byte replaceByteField(PersistenceCapable pc, int field) {
 2032           return _fm.fetchByteField(field);
 2033       }
 2034   
 2035       public char replaceCharField(PersistenceCapable pc, int field) {
 2036           return _fm.fetchCharField(field);
 2037       }
 2038   
 2039       public double replaceDoubleField(PersistenceCapable pc, int field) {
 2040           return _fm.fetchDoubleField(field);
 2041       }
 2042   
 2043       public float replaceFloatField(PersistenceCapable pc, int field) {
 2044           return _fm.fetchFloatField(field);
 2045       }
 2046   
 2047       public int replaceIntField(PersistenceCapable pc, int field) {
 2048           return _fm.fetchIntField(field);
 2049       }
 2050   
 2051       public long replaceLongField(PersistenceCapable pc, int field) {
 2052           return _fm.fetchLongField(field);
 2053       }
 2054   
 2055       public Object replaceObjectField(PersistenceCapable pc, int field) {
 2056           return _fm.fetchObjectField(field);
 2057       }
 2058   
 2059       public short replaceShortField(PersistenceCapable pc, int field) {
 2060           return _fm.fetchShortField(field);
 2061       }
 2062   
 2063       public String replaceStringField(PersistenceCapable pc, int field) {
 2064           return _fm.fetchStringField(field);
 2065       }
 2066   
 2067       //////////////////////////////////
 2068       // Implementation of FieldManager
 2069       //////////////////////////////////
 2070   
 2071       public boolean fetchBoolean(int field) {
 2072           FieldMetaData fmd = _meta.getField(field);
 2073           if (!fmd.isExternalized())
 2074               return fetchBooleanField(field);
 2075   
 2076           Object val = fetchField(field, false);
 2077           return ((Boolean) fmd.getExternalValue(val, _broker)).booleanValue();
 2078       }
 2079   
 2080       public boolean fetchBooleanField(int field) {
 2081           lock();
 2082           try {
 2083               if (!_loaded.get(field))
 2084                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2085   
 2086               provideField(_pc, _single, field);
 2087               return _single.fetchBooleanField(field);
 2088           } finally {
 2089               unlock();
 2090           }
 2091       }
 2092   
 2093       public byte fetchByte(int field) {
 2094           FieldMetaData fmd = _meta.getField(field);
 2095           if (!fmd.isExternalized())
 2096               return fetchByteField(field);
 2097   
 2098           Object val = fetchField(field, false);
 2099           return ((Number) fmd.getExternalValue(val, _broker)).byteValue();
 2100       }
 2101   
 2102       public byte fetchByteField(int field) {
 2103           lock();
 2104           try {
 2105               if (!_loaded.get(field))
 2106                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2107   
 2108               provideField(_pc, _single, field);
 2109               return _single.fetchByteField(field);
 2110           } finally {
 2111               unlock();
 2112           }
 2113       }
 2114   
 2115       public char fetchChar(int field) {
 2116           FieldMetaData fmd = _meta.getField(field);
 2117           if (!fmd.isExternalized())
 2118               return fetchCharField(field);
 2119   
 2120           Object val = fetchField(field, false);
 2121           return ((Character) fmd.getExternalValue(val, _broker)).charValue();
 2122       }
 2123   
 2124       public char fetchCharField(int field) {
 2125           lock();
 2126           try {
 2127               if (!_loaded.get(field))
 2128                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2129   
 2130               provideField(_pc, _single, field);
 2131               return _single.fetchCharField(field);
 2132           } finally {
 2133               unlock();
 2134           }
 2135       }
 2136   
 2137       public double fetchDouble(int field) {
 2138           FieldMetaData fmd = _meta.getField(field);
 2139           if (!fmd.isExternalized())
 2140               return fetchDoubleField(field);
 2141   
 2142           Object val = fetchField(field, false);
 2143           return ((Number) fmd.getExternalValue(val, _broker)).doubleValue();
 2144       }
 2145   
 2146       public double fetchDoubleField(int field) {
 2147           lock();
 2148           try {
 2149               if (!_loaded.get(field))
 2150                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2151   
 2152               provideField(_pc, _single, field);
 2153               return _single.fetchDoubleField(field);
 2154           } finally {
 2155               unlock();
 2156           }
 2157       }
 2158   
 2159       public float fetchFloat(int field) {
 2160           FieldMetaData fmd = _meta.getField(field);
 2161           if (!fmd.isExternalized())
 2162               return fetchFloatField(field);
 2163   
 2164           Object val = fetchField(field, false);
 2165           return ((Number) fmd.getExternalValue(val, _broker)).floatValue();
 2166       }
 2167   
 2168       public float fetchFloatField(int field) {
 2169           lock();
 2170           try {
 2171               if (!_loaded.get(field))
 2172                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2173   
 2174               provideField(_pc, _single, field);
 2175               return _single.fetchFloatField(field);
 2176           } finally {
 2177               unlock();
 2178           }
 2179       }
 2180   
 2181       public int fetchInt(int field) {
 2182           FieldMetaData fmd = _meta.getField(field);
 2183           if (!fmd.isExternalized())
 2184               return fetchIntField(field);
 2185   
 2186           Object val = fetchField(field, false);
 2187           return ((Number) fmd.getExternalValue(val, _broker)).intValue();
 2188       }
 2189   
 2190       public int fetchIntField(int field) {
 2191           lock();
 2192           try {
 2193               if (!_loaded.get(field))
 2194                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2195   
 2196               provideField(_pc, _single, field);
 2197               return _single.fetchIntField(field);
 2198           } finally {
 2199               unlock();
 2200           }
 2201       }
 2202   
 2203       public long fetchLong(int field) {
 2204           FieldMetaData fmd = _meta.getField(field);
 2205           if (!fmd.isExternalized())
 2206               return fetchLongField(field);
 2207   
 2208           Object val = fetchField(field, false);
 2209           return ((Number) fmd.getExternalValue(val, _broker)).longValue();
 2210       }
 2211   
 2212       public long fetchLongField(int field) {
 2213           lock();
 2214           try {
 2215               if (!_loaded.get(field))
 2216                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2217   
 2218               provideField(_pc, _single, field);
 2219               return _single.fetchLongField(field);
 2220           } finally {
 2221               unlock();
 2222           }
 2223       }
 2224   
 2225       public Object fetchObject(int field) {
 2226           FieldMetaData fmd = _meta.getField(field);
 2227           if (!fmd.isExternalized())
 2228               return fetchObjectField(field);
 2229   
 2230           Object val = fetchField(field, false);
 2231           return fmd.getExternalValue(val, _broker);
 2232       }
 2233   
 2234       public Object fetchObjectField(int field) {
 2235           lock();
 2236           try {
 2237               if (!_loaded.get(field))
 2238                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2239   
 2240               provideField(_pc, _single, field);
 2241               return _single.fetchObjectField(field);
 2242           } finally {
 2243               unlock();
 2244           }
 2245       }
 2246   
 2247       public short fetchShort(int field) {
 2248           FieldMetaData fmd = _meta.getField(field);
 2249           if (!fmd.isExternalized())
 2250               return fetchShortField(field);
 2251   
 2252           Object val = fetchField(field, false);
 2253           return ((Number) fmd.getExternalValue(val, _broker)).shortValue();
 2254       }
 2255   
 2256       public short fetchShortField(int field) {
 2257           lock();
 2258           try {
 2259               if (!_loaded.get(field))
 2260                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2261   
 2262               provideField(_pc, _single, field);
 2263               return _single.fetchShortField(field);
 2264           } finally {
 2265               unlock();
 2266           }
 2267       }
 2268   
 2269       public String fetchString(int field) {
 2270           FieldMetaData fmd = _meta.getField(field);
 2271           if (!fmd.isExternalized())
 2272               return fetchStringField(field);
 2273   
 2274           Object val = fetchField(field, false);
 2275           return (String) fmd.getExternalValue(val, _broker);
 2276       }
 2277   
 2278       public String fetchStringField(int field) {
 2279           lock();
 2280           try {
 2281               if (!_loaded.get(field))
 2282                   loadField(field, LockLevels.LOCK_NONE, false, false);
 2283   
 2284               provideField(_pc, _single, field);
 2285               return _single.fetchStringField(field);
 2286           } finally {
 2287               unlock();
 2288           }
 2289       }
 2290   
 2291       public void storeBoolean(int field, boolean externalVal) {
 2292           FieldMetaData fmd = _meta.getField(field);
 2293           if (!fmd.isExternalized())
 2294               storeBooleanField(field, externalVal);
 2295           else {
 2296               Object val = (externalVal) ? Boolean.TRUE : Boolean.FALSE;
 2297               storeField(field, fmd.getFieldValue(val, _broker));
 2298           }
 2299       }
 2300   
 2301       public void storeBooleanField(int field, boolean curVal) {
 2302           lock();
 2303           try {
 2304               _single.storeBooleanField(field, curVal);
 2305               replaceField(_pc, _single, field);
 2306               setLoaded(field, true);
 2307               postLoad(field, null);
 2308           } finally {
 2309               unlock();
 2310           }
 2311       }
 2312   
 2313       public void storeByte(int field, byte externalVal) {
 2314           FieldMetaData fmd = _meta.getField(field);
 2315           if (!fmd.isExternalized())
 2316               storeByteField(field, externalVal);
 2317           else
 2318               storeField(field, fmd.getFieldValue(new Byte(externalVal),
 2319                   _broker));
 2320       }
 2321   
 2322       public void storeByteField(int field, byte curVal) {
 2323           lock();
 2324           try {
 2325               _single.storeByteField(field, curVal);
 2326               replaceField(_pc, _single, field);
 2327               setLoaded(field, true);
 2328               postLoad(field, null);
 2329           } finally {
 2330               unlock();
 2331           }
 2332       }
 2333   
 2334       public void storeChar(int field, char externalVal) {
 2335           FieldMetaData fmd = _meta.getField(field);
 2336           if (!fmd.isExternalized())
 2337               storeCharField(field, externalVal);
 2338           else
 2339               storeField(field, fmd.getFieldValue(new Character(externalVal),
 2340                   _broker));
 2341       }
 2342   
 2343       public void storeCharField(int field, char curVal) {
 2344           lock();
 2345           try {
 2346               _single.storeCharField(field, curVal);
 2347               replaceField(_pc, _single, field);
 2348               setLoaded(field, true);
 2349               postLoad(field, null);
 2350           } finally {
 2351               unlock();
 2352           }
 2353       }
 2354   
 2355       public void storeDouble(int field, double externalVal) {
 2356           FieldMetaData fmd = _meta.getField(field);
 2357           if (!fmd.isExternalized())
 2358               storeDoubleField(field, externalVal);
 2359           else
 2360               storeField(field, fmd.getFieldValue(new Double(externalVal),
 2361                   _broker));
 2362       }
 2363   
 2364       public void storeDoubleField(int field, double curVal) {
 2365           lock();
 2366           try {
 2367               _single.storeDoubleField(field, curVal);
 2368               replaceField(_pc, _single, field);
 2369               setLoaded(field, true);
 2370               postLoad(field, null);
 2371           } finally {
 2372               unlock();
 2373           }
 2374       }
 2375   
 2376       public void storeFloat(int field, float externalVal) {
 2377           FieldMetaData fmd = _meta.getField(field);
 2378           if (!fmd.isExternalized())
 2379               storeFloatField(field, externalVal);
 2380           else
 2381               storeField(field, fmd.getFieldValue(new Float(externalVal),
 2382                   _broker));
 2383       }
 2384   
 2385       public void storeFloatField(int field, float curVal) {
 2386           lock();
 2387           try {
 2388               _single.storeFloatField(field, curVal);
 2389               replaceField(_pc, _single, field);
 2390               setLoaded(field, true);
 2391               postLoad(field, null);
 2392           } finally {
 2393               unlock();
 2394           }
 2395       }
 2396   
 2397       public void storeInt(int field, int externalVal) {
 2398           FieldMetaData fmd = _meta.getField(field);
 2399           if (!fmd.isExternalized())
 2400               storeIntField(field, externalVal);
 2401           else
 2402               storeField(field, fmd.getFieldValue(Numbers.valueOf(externalVal),
 2403                   _broker));
 2404       }
 2405   
 2406       public void storeIntField(int field, int curVal) {
 2407           lock();
 2408           try {
 2409               _single.storeIntField(field, curVal);
 2410               replaceField(_pc, _single, field);
 2411               setLoaded(field, true);
 2412               postLoad(field, null);
 2413           } finally {
 2414               unlock();
 2415           }
 2416       }
 2417   
 2418       public void storeLong(int field, long externalVal) {
 2419           FieldMetaData fmd = _meta.getField(field);
 2420           if (!fmd.isExternalized())
 2421               storeLongField(field, externalVal);
 2422           else
 2423               storeField(field, fmd.getFieldValue(Numbers.valueOf(externalVal),
 2424                   _broker));
 2425       }
 2426   
 2427       public void storeLongField(int field, long curVal) {
 2428           lock();
 2429           try {
 2430               _single.storeLongField(field, curVal);
 2431               replaceField(_pc, _single, field);
 2432               setLoaded(field, true);
 2433               postLoad(field, null);
 2434           } finally {
 2435               unlock();
 2436           }
 2437       }
 2438   
 2439       public void storeObject(int field, Object externalVal) {
 2440           FieldMetaData fmd = _meta.getField(field);
 2441           externalVal = fmd.order(externalVal);
 2442           if (!fmd.isExternalized())
 2443               storeObjectField(field, externalVal);
 2444           else
 2445               storeField(field, fmd.getFieldValue(externalVal, _broker));
 2446       }
 2447   
 2448       public void storeObjectField(int field, Object curVal) {
 2449           lock();
 2450           try {
 2451               _single.storeObjectField(field, curVal);
 2452               _single.proxy(true, false);
 2453               replaceField(_pc, _single, field);
 2454               setLoaded(field, true);
 2455               postLoad(field, null);
 2456           } finally {
 2457               unlock();
 2458           }
 2459       }
 2460   
 2461       public void storeShort(int field, short externalVal) {
 2462           FieldMetaData fmd = _meta.getField(field);
 2463           if (!fmd.isExternalized())
 2464               storeShortField(field, externalVal);
 2465           else
 2466               storeField(field, fmd.getFieldValue(new Short(externalVal),
 2467                   _broker));
 2468       }
 2469   
 2470       public void storeShortField(int field, short curVal) {
 2471           lock();
 2472           try {
 2473               _single.storeShortField(field, curVal);
 2474               replaceField(_pc, _single, field);
 2475               setLoaded(field, true);
 2476               postLoad(field, null);
 2477           } finally {
 2478               unlock();
 2479           }
 2480       }
 2481   
 2482       public void storeString(int field, String externalVal) {
 2483           FieldMetaData fmd = _meta.getField(field);
 2484           if (!fmd.isExternalized())
 2485               storeStringField(field, externalVal);
 2486           else
 2487               storeField(field, fmd.getFieldValue(externalVal, _broker));
 2488       }
 2489   
 2490       public void storeStringField(int field, String curVal) {
 2491           lock();
 2492           try {
 2493               _single.storeStringField(field, curVal);
 2494               replaceField(_pc, _single, field);
 2495               setLoaded(field, true);
 2496               postLoad(field, null);
 2497           } finally {
 2498               unlock();
 2499           }
 2500       }
 2501   
 2502       /**
 2503        * Store the given field value into the given field manager.
 2504        */
 2505       private void storeField(int field, Object val, FieldManager fm) {
 2506           FieldMetaData fmd = _meta.getField(field);
 2507           if (fmd == null)
 2508               throw new UserException(_loc.get("no-field-index",
 2509                   String.valueOf(field), _meta.getDescribedType())).
 2510                   setFailedObject(getManagedInstance());
 2511   
 2512           switch (fmd.getDeclaredTypeCode()) {
 2513               case JavaTypes.BOOLEAN:
 2514                   boolean bool = val != null && ((Boolean) val).booleanValue();
 2515                   fm.storeBooleanField(field, bool);
 2516                   break;
 2517               case JavaTypes.BYTE:
 2518                   byte b = (val == null) ? 0 : ((Number) val).byteValue();
 2519                   fm.storeByteField(field, b);
 2520                   break;
 2521               case JavaTypes.CHAR:
 2522                   char c = (val == null) ? 0 : ((Character) val).charValue();
 2523                   fm.storeCharField(field, c);
 2524                   break;
 2525               case JavaTypes.DOUBLE:
 2526                   double d = (val == null) ? 0 : ((Number) val).doubleValue();
 2527                   fm.storeDoubleField(field, d);
 2528                   break;
 2529               case JavaTypes.FLOAT:
 2530                   float f = (val == null) ? 0 : ((Number) val).floatValue();
 2531                   fm.storeFloatField(field, f);
 2532                   break;
 2533               case JavaTypes.INT:
 2534                   int i = (val == null) ? 0 : ((Number) val).intValue();
 2535                   fm.storeIntField(field, i);
 2536                   break;
 2537               case JavaTypes.LONG:
 2538                   long l = (val == null) ? 0 : ((Number) val).longValue();
 2539                   fm.storeLongField(field, l);
 2540                   break;
 2541               case JavaTypes.SHORT:
 2542                   short s = (val == null) ? 0 : ((Number) val).shortValue();
 2543                   fm.storeShortField(field, s);
 2544                   break;
 2545               case JavaTypes.STRING:
 2546                   fm.storeStringField(field, (String) val);
 2547                   break;
 2548               default:
 2549                   fm.storeObjectField(field, val);
 2550           }
 2551       }
 2552   
 2553       /////////////
 2554       // Utilities
 2555       /////////////
 2556   
 2557       /**
 2558        * Erase the fact that this instance has been flushed.
 2559        */
 2560       void eraseFlush() {
 2561           _flags &= ~FLAG_FLUSHED;
 2562           _flags &= ~FLAG_FLUSHED_DIRTY;
 2563   
 2564           int fmds = _meta.getFields().length;
 2565           for (int i = 0; i < fmds; i++)
 2566               _flush.clear(i);
 2567       }
 2568   
 2569       /**
 2570        * Records that all instance fields are/are not loaded.
 2571        * Primary key and non-persistent fields are not affected.
 2572        */
 2573       void setLoaded(boolean val) {
 2574           FieldMetaData[] fmds = _meta.getFields();
 2575           for (int i = 0; i < fmds.length; i++) {
 2576               if (!fmds[i].isPrimaryKey()
 2577                   && fmds[i].getManagement() == fmds[i].MANAGE_PERSISTENT)
 2578                   setLoaded(i, val);
 2579           }
 2580           if (!val) {
 2581               _flags &= ~FLAG_LOADED;
 2582               setDirty(false);
 2583           } else
 2584               _flags |= FLAG_LOADED;
 2585       }
 2586   
 2587       /**
 2588        * Records that all instance fields are/are not dirty,
 2589        * and changes the flags of the instance accordingly.
 2590        */
 2591       void setDirty(boolean val) {
 2592           FieldMetaData[] fmds = _meta.getFields();
 2593           boolean update = !isNew() || isFlushed();
 2594           for (int i = 0; i < fmds.length; i++) {
 2595               if (val && (!update
 2596                   || fmds[i].getUpdateStrategy() != UpdateStrategies.IGNORE))
 2597                   _dirty.set(i);
 2598               else if (!val) {
 2599                   // we never consider clean fields flushed; this also takes
 2600                   // care of clearing the flushed fields on commit/rollback
 2601                   _flush.clear(i);
 2602                   _dirty.clear(i);
 2603               }
 2604           }
 2605   
 2606           if (val)
 2607               _flags |= FLAG_LOADED;
 2608       }
 2609   
 2610       /**
 2611        * Executes pre-clear callbacks, clears all managed fields, and calls the
 2612        * {@link #setLoaded} method with a value of false. Primary key fields
 2613        * are not cleared.
 2614        */
 2615       void clearFields() {
 2616           if (!isIntercepting())
 2617               return;
 2618   
 2619           fireLifecycleEvent(LifecycleEvent.BEFORE_CLEAR);
 2620   
 2621           // unproxy all fields
 2622           unproxyFields();
 2623   
 2624           lock();
 2625           try {
 2626               // clear non-pk fields
 2627               FieldMetaData[] fmds = _meta.getFields();
 2628               for (int i = 0; i < fmds.length; i++) {
 2629                   if (!fmds[i].isPrimaryKey() && fmds[i].getManagement()
 2630                       == FieldMetaData.MANAGE_PERSISTENT)
 2631                       replaceField(_pc, ClearFieldManager.getInstance(), i);
 2632               }
 2633   
 2634               // forget version info and impl data so we re-read next time
 2635               setLoaded(false);
 2636               _version = null;
 2637               _loadVersion = null;
 2638               if (_fieldImpl != null)
 2639                   Arrays.fill(_fieldImpl, null);
 2640           } finally {
 2641               unlock();
 2642           }
 2643   
 2644           fireLifecycleEvent(LifecycleEvent.AFTER_CLEAR);
 2645       }
 2646   
 2647       /**
 2648        * Record that we should save any fields that change from this point
 2649        * forward.
 2650        */
 2651       void saveFields(boolean immediate) {
 2652           if (_broker.getRestoreState() == RestoreState.RESTORE_NONE
 2653               && (_flags & FLAG_INVERSES) == 0)
 2654               return;
 2655   
 2656           _flags |= FLAG_SAVE;
 2657           if (immediate) {
 2658               for (int i = 0, len = _loaded.length(); i < len; i++)
 2659                   saveField(i);
 2660               _flags &= ~FLAG_SAVE;
 2661           }
 2662       }
 2663   
 2664       /**
 2665        * If the field isn't already saved, saves the currently loaded field
 2666        * state of the instance. The saved values can all be restored via
 2667        * {@link #restoreFields}.
 2668        */
 2669       private void saveField(int field) {
 2670           if ((_flags & FLAG_SAVE) == 0)
 2671               return;
 2672   
 2673           // if this is a managed inverse field, load it so we're sure to have
 2674           // the original value
 2675           if (!_loaded.get(field) && ((_flags & FLAG_INVERSES) != 0
 2676               && _meta.getField(field).getInverseMetaDatas().length > 0))
 2677               loadField(field, LockLevels.LOCK_NONE, false, false);
 2678   
 2679           // don't bother creating the save field manager if we're not going to
 2680           // save the old field value anyway
 2681           if (_saved == null) {
 2682               if (_loaded.get(field))
 2683                   _saved = new SaveFieldManager(this, null, _dirty);
 2684               else
 2685                   return;
 2686           }
 2687   
 2688           // copy the field to save field manager; if the field is not directly
 2689           // copyable, immediately provide and replace it via the save field
 2690           // manager, which will copy the mutable value to prevent by-ref mods
 2691           if (_saved.saveField(field)) {
 2692               provideField(_pc, _saved, field);
 2693               replaceField(_saved.getState(), _saved, field);
 2694           }
 2695       }
 2696   
 2697       /**
 2698        * Notification that the state will not need to be rolled back
 2699        * to that of the last call to {@link #saveFields}.
 2700        */
 2701       void clearSavedFields() {
 2702           if (isIntercepting()) {
 2703               _flags &= ~FLAG_SAVE;
 2704               _saved = null;
 2705           }
 2706       }
 2707   
 2708       public SaveFieldManager getSaveFieldManager() {
 2709           return _saved;
 2710       }
 2711   
 2712       /**
 2713        * Rollback the state of the instance to the saved state from the
 2714        * last call to {@link #saveFields}, or to default values if never saved.
 2715        */
 2716       void restoreFields() {
 2717           lock();
 2718           try {
 2719               if (_saved == null) {
 2720                   if ((_flags & FLAG_SAVE) == 0)
 2721                       clearFields();
 2722                   else // only unloaded fields were dirtied
 2723                       _loaded.andNot(_loaded);
 2724               }
 2725               // we direct state transitions based on our own getRestoreState
 2726               // method, but to decide whether to actually rollback field
 2727               // values, we consult the broker for the user's setting
 2728               else if (_broker.getRestoreState() != RestoreState.RESTORE_NONE) {
 2729                   // rollback all currently-loaded fields
 2730                   for (int i = 0, len = _loaded.length(); i < len; i++)
 2731                       if (_loaded.get(i) && _saved.restoreField(i))
 2732                           replaceField(_pc, _saved, i);
 2733   
 2734                   // rollback loaded set
 2735                   _loaded.andNot(_saved.getUnloaded());
 2736               }
 2737           }
 2738           finally {
 2739               unlock();
 2740           }
 2741       }
 2742   
 2743       /**
 2744        * Replaces all second class object fields with fresh proxied instances
 2745        * containing the same information as the originals.
 2746        */
 2747       void proxyFields(boolean reset, boolean replaceNull) {
 2748           // we only replace nulls if the runtime can't differentiate between
 2749           // null and empty containers.  we replace nulls in this case to
 2750           // maintain consistency whether values are being retained or not
 2751           if (replaceNull)
 2752               replaceNull = !_broker.getConfiguration().supportedOptions().
 2753                   contains(OpenJPAConfiguration.OPTION_NULL_CONTAINER);
 2754   
 2755           lock();
 2756           try {
 2757               for (int i = 0, len = _loaded.length(); i < len; i++) {
 2758                   if (_loaded.get(i)) {
 2759                       provideField(_pc, _single, i);
 2760                       if (_single.proxy(reset, replaceNull))
 2761                           replaceField(_pc, _single, i);
 2762                       else
 2763                           _single.clear();
 2764                   }
 2765               }
 2766           } finally {
 2767               unlock();
 2768           }
 2769       }
 2770   
 2771       /**
 2772        * Unproxy all fields.
 2773        */
 2774       void unproxyFields() {
 2775           if ((_flags & FLAG_NO_UNPROXY) != 0)
 2776               return;
 2777   
 2778           lock();
 2779           try {
 2780               for (int i = 0, len = _loaded.length(); i < len; i++) {
 2781                   provideField(_pc, _single, i);
 2782                   _single.unproxy();
 2783                   _single.releaseEmbedded();
 2784                   _single.clear();
 2785               }
 2786           }
 2787           finally {
 2788               unlock();
 2789           }
 2790       }
 2791   
 2792       /**
 2793        * Get ready for a flush. Persists all persistence-capable object fields,
 2794        * and checks for illegal null values. Also assigns oids and field values
 2795        * for all strategies that don't require flushing.
 2796        */
 2797       void preFlush(boolean logical, OpCallbacks call) {
 2798           if ((_flags & FLAG_PRE_FLUSHED) != 0)
 2799               return;
 2800   
 2801           if (isPersistent()) {
 2802               fireLifecycleEvent(LifecycleEvent.BEFORE_STORE);
 2803               // BEFORE_PERSIST is handled during Broker.persist and Broker.attach
 2804               if (isDeleted())
 2805                   fireLifecycleEvent(LifecycleEvent.BEFORE_DELETE);
 2806               else if (!(isNew() && !isFlushed()))
 2807                   fireLifecycleEvent(LifecycleEvent.BEFORE_UPDATE);
 2808               _flags |= FLAG_PRE_FLUSHED;
 2809           }
 2810   
 2811           lock();
 2812           try {
 2813               if (!logical)
 2814                   assignObjectId(false, true);
 2815               for (int i = 0, len = _meta.getFields().length; i < len; i++) {
 2816                   if ((logical || !assignField(i, true)) && !_flush.get(i)
 2817                       && _dirty.get(i)) {
 2818                       provideField(_pc, _single, i);
 2819                       if (_single.preFlush(logical, call))
 2820                           replaceField(_pc, _single, i);
 2821                       else
 2822                           _single.clear();
 2823                   }
 2824               }
 2825   
 2826               dirtyCheck();
 2827           } finally {
 2828               unlock();
 2829           }
 2830       }
 2831   
 2832       /**
 2833        * Make callbacks for deletion.
 2834        */
 2835       void preDelete() {
 2836           // set a flag while call pre delete callback so that user can't
 2837           // get into infinite recursion by calling delete(this)
 2838           // within his callback method
 2839           if ((_flags & FLAG_PRE_DELETING) == 0) {
 2840               _flags |= FLAG_PRE_DELETING;
 2841               try {
 2842                   fireLifecycleEvent(LifecycleEvent.BEFORE_DELETE);
 2843               } finally {
 2844                   _flags &= ~FLAG_PRE_DELETING;
 2845               }
 2846           }
 2847       }
 2848   
 2849       /**
 2850        * Cascade deletes and dereference dependent fields.
 2851        */
 2852       void cascadeDelete(OpCallbacks call) {
 2853           FieldMetaData[] fmds = _meta.getFields();
 2854           for (int i = 0; i < fmds.length; i++) {
 2855               if (fmds[i].getCascadeDelete() != ValueMetaData.CASCADE_NONE
 2856                   || fmds[i].getKey().getCascadeDelete()
 2857                   != ValueMetaData.CASCADE_NONE
 2858                   || fmds[i].getElement().getCascadeDelete()
 2859                   != ValueMetaData.CASCADE_NONE) {
 2860                   _single.storeObjectField(i, fetchField(i, false));
 2861                   _single.delete(call);
 2862                   _single.clear();
 2863               }
 2864           }
 2865       }
 2866   
 2867       /**
 2868        * Called after an instance is persisted by a user through the broker.
 2869        * Cascades the persist operation to fields marked
 2870        * {@link ValueMetaData#CASCADE_IMMEDIATE}.
 2871        */
 2872       void cascadePersist(OpCallbacks call) {
 2873           FieldMetaData[] fmds = _meta.getFields();
 2874           for (int i = 0; i < fmds.length; i++) {
 2875               if (!_loaded.get(i))
 2876                   continue;
 2877   
 2878               if (fmds[i].getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE
 2879                   || fmds[i].getKey().getCascadePersist()
 2880                   == ValueMetaData.CASCADE_IMMEDIATE
 2881                   || fmds[i].getElement().getCascadePersist()
 2882                   == ValueMetaData.CASCADE_IMMEDIATE) {
 2883                   _single.storeObjectField(i, fetchField(i, false));
 2884                   _single.persist(call);
 2885                   _single.clear();
 2886               }
 2887           }
 2888       }
 2889   
 2890       /**
 2891        * Load the given field set from the data store into the instance.
 2892        * Return true if any data is loaded, false otherwise.
 2893        */
 2894       boolean loadFields(BitSet fields, FetchConfiguration fetch, int lockLevel,
 2895           Object sdata) {
 2896           // can't load version field from store
 2897           if (fields != null) {
 2898               FieldMetaData vfield = _meta.getVersionField();
 2899               if (vfield != null)
 2900                   fields.clear(vfield.getIndex());
 2901           }
 2902   
 2903           boolean ret = false;
 2904           setLoading(true);
 2905           try {
 2906               // if any fields given, load them
 2907               int len = (fields == null) ? 0 : fields.length();
 2908               if (len > 0) {
 2909                   if (fetch == null)
 2910                       fetch = _broker.getFetchConfiguration();
 2911                   if (!_broker.getStoreManager().load(this, fields, fetch,
 2912                       lockLevel, sdata)) {
 2913                       throw new ObjectNotFoundException(_loc.get("del-instance",
 2914                           _meta.getDescribedType(), _oid)).
 2915                           setFailedObject(getManagedInstance());
 2916                   }
 2917                   ret = true;
 2918               }
 2919   
 2920               // make sure version information has been set; version info must
 2921               // always be set after the first state load or set (which is why
 2922               // we do this even if no fields were loaded -- could be that this
 2923               // method is being called after a field is set)... some instances
 2924               // might not have version info, in which case this gets called
 2925               // mutiple times; that should be ok too
 2926               if (_loadVersion == null) {
 2927                   syncVersion(sdata);
 2928                   ret = ret || _loadVersion != null;
 2929               }
 2930           }
 2931           finally {
 2932               setLoading(false);
 2933           }
 2934   
 2935           // see if the dfg is now loaded; do this regardless of whether we
 2936           // loaded any fields, cause may already have been loaded by
 2937           // StoreManager during initialization
 2938           postLoad(-1, fetch);
 2939           return ret;
 2940       }
 2941   
 2942       /**
 2943        * Load the given field's fetch group; the field itself may already be
 2944        * loaded if it is being set by the user.
 2945        */
 2946       protected void loadField(int field, int lockLevel, boolean forWrite,
 2947           boolean fgs) {
 2948           FetchConfiguration fetch = _broker.getFetchConfiguration();
 2949           FieldMetaData fmd = _meta.getField(field);
 2950           BitSet fields = null;
 2951   
 2952           // if this is a dfg field or we need to load our dfg, do so
 2953           if (fgs && (_flags & FLAG_LOADED) == 0)
 2954               fields = getUnloadedInternal(fetch, LOAD_FGS, null);
 2955           
 2956           // check for load fetch group
 2957           String lfg = fmd.getLoadFetchGroup();
 2958           boolean lfgAdded = false;
 2959           if (lfg != null) {  
 2960               FieldMetaData[] fmds = _meta.getFields();
 2961               for (int i = 0; i < fmds.length; i++) {
 2962                   if (!_loaded.get(i) && (i == field
 2963                       || fmds[i].isInFetchGroup(lfg))) {
 2964                       if (fields == null)
 2965                           fields = new BitSet(fmds.length);
 2966                       fields.set(i);
 2967                   }
 2968               }
 2969   
 2970               // relation field is loaded with the load-fetch-group
 2971               // but this addition must be reverted once the load is over
 2972               if (!fetch.hasFetchGroup(lfg)) {
 2973                   fetch.addFetchGroup(lfg);
 2974                   lfgAdded = true;
 2975               }
 2976           } else if (fmd.isInDefaultFetchGroup() && fields == null) {
 2977               // no load group but dfg: add dfg fields if we haven't already
 2978               fields = getUnloadedInternal(fetch, LOAD_FGS, null);
 2979           } else if (!_loaded.get(fmd.getIndex())) {
 2980               // no load group or dfg: load individual field
 2981               if (fields == null)
 2982                   fields = new BitSet();
 2983               fields.set(fmd.getIndex());
 2984           }
 2985   
 2986           // call this method even if there are no unloaded fields; loadFields
 2987           // takes care of things like loading version info and setting PC flags
 2988           try {
 2989               loadFields(fields, fetch, lockLevel, null);
 2990           } finally {
 2991               if (lfgAdded)
 2992                   fetch.removeFetchGroup(lfg);
 2993           }
 2994       }
 2995   
 2996       /**
 2997        * Helper method to provide the given field number to the given
 2998        * field manager.
 2999        */
 3000       void provideField(PersistenceCapable pc, FieldManager store, int field) {
 3001           FieldManager beforeFM = _fm;
 3002           _fm = store;
 3003           pc.pcProvideField(field);
 3004           // Retaining original FM because of the possibility of reentrant calls
 3005           _fm = beforeFM;
 3006       }
 3007   
 3008       /**
 3009        * Helper method to replace the given field number to the given
 3010        * field manager.
 3011        */
 3012       void replaceField(PersistenceCapable pc, FieldManager load, int field) {
 3013           FieldManager beforeFM = _fm;
 3014           _fm = load;
 3015           pc.pcReplaceField(field);
 3016           // Retaining original FM because of the possibility of reentrant calls
 3017           _fm = beforeFM;
 3018       }
 3019   
 3020       /**
 3021        * Mark the field as loaded or unloaded.
 3022        */
 3023       private void setLoaded(int field, boolean isLoaded) {
 3024           // don't continue if loaded state is already correct; otherwise we
 3025           // can end up clearing _fieldImpl when we shouldn't
 3026           if (_loaded.get(field) == isLoaded)
 3027               return;
 3028   
 3029           // if loading, clear intermediate data; if unloading, clear impl data
 3030           if (_fieldImpl != null) {
 3031               int idx = _meta.getExtraFieldDataIndex(field);
 3032               if (idx != -1)
 3033                   _fieldImpl[idx] = null;
 3034           }
 3035   
 3036           if (isLoaded)
 3037               _loaded.set(field);
 3038           else
 3039               _loaded.clear(field);
 3040       }
 3041   
 3042       /**
 3043        * Perform post-load steps, including the post load callback.
 3044        * We have to check the dfg after all field loads because it might be
 3045        * loaded in multiple steps when paging is involved; the initial load
 3046        * might exclude some fields which are then immediately loaded in a
 3047        * separate step before being returned to the user.
 3048        *
 3049        * @param field the field index that was loaded, or -1 to indicate
 3050        * that a group of possibly unknown fields was loaded
 3051        */
 3052       private void postLoad(int field, FetchConfiguration fetch) {
 3053           // no need for postLoad callback?
 3054           if ((_flags & FLAG_LOADED) != 0)
 3055               return;
 3056   
 3057           // in the middle of a group load, after which this method will be
 3058           // called again?
 3059           if (field != -1 && isLoading())
 3060               return;
 3061   
 3062           // no listeners?
 3063           LifecycleEventManager mgr = _broker.getLifecycleEventManager();
 3064           if (mgr == null || !mgr.hasLoadListeners(getManagedInstance(), _meta))
 3065               return;
 3066   
 3067           if (fetch == null)
 3068               fetch = _broker.getFetchConfiguration();
 3069           // is this field a post-load field?
 3070           if (field != -1) {
 3071               FieldMetaData fmd = _meta.getField(field);
 3072               if (fmd.isInDefaultFetchGroup() 
 3073                   && fetch.hasFetchGroup(FetchGroup.NAME_DEFAULT)
 3074                   && postLoad(FetchGroup.NAME_DEFAULT, fetch))
 3075                   return;
 3076               String[] fgs = fmd.getCustomFetchGroups();
 3077               for (int i = 0; i < fgs.length; i++)
 3078                   if (fetch.hasFetchGroup(fgs[i]) && postLoad(fgs[i], fetch))
 3079                       return;
 3080           } else {
 3081               for (Iterator itr = fetch.getFetchGroups().iterator(); 
 3082                   itr.hasNext();) {
 3083                   if (postLoad((String) itr.next(), fetch))
 3084                       return;
 3085               }
 3086           }
 3087       }
 3088   
 3089       /**
 3090        * Perform post-load actions if the given fetch group is a post-load group
 3091        * and is fully loaded.
 3092        */
 3093       private boolean postLoad(String fgName, FetchConfiguration fetch) {
 3094           FetchGroup fg = _meta.getFetchGroup(fgName);
 3095           if (fg == null || !fg.isPostLoad())
 3096               return false;
 3097   
 3098           FieldMetaData[] fmds = _meta.getFields();
 3099           for (int i = 0; i < fmds.length; i++)
 3100               if (!_loaded.get(i) && fmds[i].isInFetchGroup(fgName))
 3101                   return false;
 3102   
 3103           _flags |= FLAG_LOADED;
 3104           _broker.fireLifecycleEvent(getManagedInstance(), fetch, _meta, 
 3105           	LifecycleEvent.AFTER_LOAD);
 3106           return true;
 3107       }
 3108   
 3109       /**
 3110        * Synchronize our version object with the datastore.
 3111        */
 3112       private boolean syncVersion(Object sdata) {
 3113           return _broker.getStoreManager().syncVersion(this, sdata);
 3114       }
 3115   
 3116       /**
 3117        * Returns whether this instance needs a version check.
 3118        */
 3119       public boolean isVersionCheckRequired() {
 3120           // explicit flag for version check
 3121           if ((_flags & FLAG_VERSION_CHECK) != 0)
 3122               return true;
 3123   
 3124           if (!_broker.getOptimistic() && !_broker.getConfiguration().
 3125               getCompatibilityInstance().getNonOptimisticVersionCheck())
 3126               return false;
 3127           return _state.isVersionCheckRequired(this);
 3128       }
 3129   
 3130       /**
 3131        * Set whether this instance requires a version check on the next flush.
 3132        */
 3133       void setCheckVersion(boolean versionCheck) {
 3134           if (versionCheck)
 3135               _flags |= FLAG_VERSION_CHECK;
 3136           else
 3137               _flags &= ~FLAG_VERSION_CHECK;
 3138       }
 3139   
 3140       /**
 3141        * Returns whether this instance needs a version update.
 3142        */
 3143       public boolean isVersionUpdateRequired() {
 3144           return (_flags & FLAG_VERSION_UPDATE) > 0;
 3145       }
 3146   
 3147       /**
 3148        * Set whether this instance requires a version update on the next flush.
 3149        */
 3150       void setUpdateVersion(boolean versionUpdate) {
 3151           if (versionUpdate)
 3152               _flags |= FLAG_VERSION_UPDATE;
 3153           else
 3154               _flags &= ~FLAG_VERSION_UPDATE;
 3155       }
 3156   
 3157       /**
 3158        * Translate the given exception based on the broker's implicit behavior.
 3159        * Translation only occurs if the exception is initiated by a user action
 3160        * on an instance, and therefore will not be caught and translated by the
 3161        * broker.
 3162        */
 3163       protected RuntimeException translate(RuntimeException re) {
 3164           RuntimeExceptionTranslator trans = _broker.
 3165               getInstanceExceptionTranslator();
 3166           return (trans == null) ? re : trans.translate(re);
 3167       }
 3168   
 3169       /**
 3170        * Lock the state manager if the multithreaded option is set.
 3171        */
 3172       protected void lock() {
 3173           // use broker-level lock to avoid deadlock situations with the state
 3174           // manager lock and broker lock being obtained in different orders
 3175           _broker.lock();
 3176       }
 3177   
 3178       /**
 3179        * Unlock the state manager.
 3180        */
 3181   	protected void unlock ()
 3182   	{
 3183   		// use broker-level lock to avoid deadlock situations with the state 
 3184   		// manager lock and broker lock being obtained in different orders
 3185   		_broker.unlock ();
 3186   	}
 3187   
 3188       private void writeObject(ObjectOutputStream oos) throws IOException {
 3189           oos.writeObject(_broker);
 3190           oos.defaultWriteObject();
 3191           oos.writeObject(_meta.getDescribedType());
 3192           writePC(oos, _pc);
 3193       }
 3194   
 3195       /**
 3196        * Write <code>pc</code> to <code>oos</code>, handling internal-form
 3197        * serialization. <code>pc</code> must be of the same type that this
 3198        * state manager manages.
 3199        *
 3200        * @since 1.1.0
 3201        */
 3202       void writePC(ObjectOutputStream oos, PersistenceCapable pc)
 3203           throws IOException {
 3204           if (!Serializable.class.isAssignableFrom(_meta.getDescribedType()))
 3205               throw new NotSerializableException(
 3206                   _meta.getDescribedType().getName());
 3207   
 3208           oos.writeObject(pc);
 3209       }
 3210   
 3211       private void readObject(ObjectInputStream in)
 3212           throws IOException, ClassNotFoundException {
 3213           _broker = (BrokerImpl) in.readObject();
 3214           in.defaultReadObject();
 3215   
 3216           // we need to store the class before the pc instance so that we can
 3217           // create _meta before calling readPC(), which relies on _meta being
 3218           // non-null when reconstituting ReflectingPC instances. Sadly, this
 3219           // penalizes the serialization footprint of non-ReflectingPC SMs also.
 3220           Class managedType = (Class) in.readObject();
 3221           _meta = _broker.getConfiguration().getMetaDataRepositoryInstance()
 3222               .getMetaData(managedType, null, true);
 3223   
 3224           _pc = readPC(in);
 3225       }
 3226   
 3227       /**
 3228        * Converts the deserialized <code>o</code> to a {@link PersistenceCapable}
 3229        * instance appropriate for storing in <code>_pc</code>.
 3230        *
 3231        * @since 1.1.0
 3232        */
 3233       PersistenceCapable readPC(ObjectInputStream in)
 3234           throws ClassNotFoundException, IOException {
 3235           Object o = in.readObject();
 3236   
 3237           if (o == null)
 3238               return null;
 3239   
 3240           PersistenceCapable pc;
 3241           if (!(o instanceof PersistenceCapable))
 3242               pc = ImplHelper.toPersistenceCapable(o, this);
 3243           else
 3244               pc = (PersistenceCapable) o;
 3245   
 3246           pc.pcReplaceStateManager(this);
 3247           return pc;
 3248       }
 3249   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]