/*
* Copyright 2004 (C) TJDO.
* All rights reserved.
*
* This software is distributed under the terms of the TJDO License version 1.0.
* See the terms of the TJDO License in the documentation provided with this software.
*
* $Id: StateManagerImpl.java,v 1.19 2004/01/25 22:31:16 jackknifebarber Exp $
*/
package com.triactive.jdo.state;
import com.triactive.jdo.AbstractFieldManager;
import com.triactive.jdo.FieldManager;
import com.triactive.jdo.GenericFieldManager;
import com.triactive.jdo.PersistenceManager;
import com.triactive.jdo.SCO;
import com.triactive.jdo.model.ClassMetaData;
import com.triactive.jdo.model.FieldMetaData;
import com.triactive.jdo.sco.SCOProcessor;
import com.triactive.jdo.store.StoreManager;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import javax.jdo.InstanceCallbacks;
import javax.jdo.JDOFatalInternalException;
import javax.jdo.JDOFatalUserException;
import javax.jdo.JDOUnsupportedOptionException;
import javax.jdo.JDOUserException;
import javax.jdo.Transaction;
import javax.jdo.spi.JDOImplHelper;
import javax.jdo.spi.PersistenceCapable;
import javax.jdo.spi.StateManager;
import org.apache.log4j.Category;
/**
* This class implements the StateManager.
*
* @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
* @version $Revision: 1.19 $
*/
public class StateManagerImpl implements com.triactive.jdo.StateManager
{
private static final Category LOG = Category.getInstance(StateManagerImpl.class);
private static final JDOImplHelper helper;
private static final StateFieldManager hollowFieldManager = new StateFieldManager();
private PersistenceManager myPM;
private PersistenceCapable myPC;
private Transaction myTX;
private Object myID;
private LifeCycleState myLC;
private byte jdoFlags;
private PersistenceCapable savedImage = null;
private byte savedFlags;
private boolean[] savedLoadedFields = null;
private ClassMetaData cmd;
private StoreManager srm;
private int fieldCount;
private boolean inserting = false;
private boolean dirty = false;
private boolean deleting = false;
private boolean flushing = false;
private boolean changingState = false;
private boolean postLoadPending = false;
private boolean disconnecting = false;
private boolean[] dirtyFields;
private boolean[] loadedFields;
private boolean[] defaultFetchGroupFields;
private boolean[] secondClassMutableFields;
private int[] allFieldNumbers;
private int[] persistentFieldNumbers;
private int[] defaultFetchGroupFieldNumbers;
private int[] secondClassMutableFieldNumbers;
private FieldManager currFM = null;
static
{
try
{
helper = JDOImplHelper.getInstance();
}
catch (SecurityException e)
{
throw new JDOFatalUserException("Insufficient access granted to com.triactive.jdo.*", e);
}
}
/**
* Constructs a state manager to manage an existing persistence-capable
* instance that is not persistent.
* All mutable SCO fields are wrapped in suitable wrapper objects and the
* PC instance transitions to a TransientClean state.
*
* @param pm
* The persistence manager controlling this state manager.
* @param pc
* The instance to be managed.
*/
public StateManagerImpl(PersistenceManager pm, PersistenceCapable pc)
{
myPM = pm;
myTX = pm.currentTransaction();
srm = myPM.getStoreManager();
myPC = pc;
myID = null;
myLC = LifeCycleState.getLifeCycleState(LifeCycleState.T_CLEAN);
jdoFlags = PersistenceCapable.READ_OK;
cmd = ClassMetaData.forClass(pc.getClass());
initialize();
for (int i = 0; i < fieldCount; ++i)
loadedFields[i] = true;
myPC.jdoReplaceStateManager(this);
myPC.jdoReplaceFlags();
replaceSCOFields();
}
/**
* Constructs a state manager to manage a new, hollow instance having the
* given object ID.
* This constructor is used for creating new instances of existing
* persistent objects (i.e. via pm.getObjectById()).
*
* @param pm
* The persistence manager controlling this state manager.
* @param pcClass
* The class of the new instance to be created.
* @param id
* The JDO identity of the object.
*/
public StateManagerImpl(PersistenceManager pm, Class pcClass, Object id)
{
myPM = pm;
myTX = pm.currentTransaction();
srm = myPM.getStoreManager();
myID = id;
myLC = LifeCycleState.getLifeCycleState(LifeCycleState.HOLLOW);
jdoFlags = PersistenceCapable.LOAD_REQUIRED;
cmd = ClassMetaData.forClass(pcClass);
initialize();
myPC = helper.newInstance(pcClass, this);
}
private void initialize()
{
fieldCount = cmd.getInheritedFieldCount() + cmd.getFieldCount();
dirtyFields = new boolean[fieldCount];
loadedFields = (boolean[])cmd.getTransactionalFieldFlags().clone();
defaultFetchGroupFields = cmd.getDefaultFetchGroupFieldFlags();
secondClassMutableFields = cmd.getSecondClassMutableFieldFlags();
allFieldNumbers = cmd.getAllFieldNumbers();
persistentFieldNumbers = cmd.getPersistentFieldNumbers();
defaultFetchGroupFieldNumbers = cmd.getDefaultFetchGroupFieldNumbers();
secondClassMutableFieldNumbers = cmd.getSecondClassMutableFieldNumbers();
}
/**
* Passes all SCO field values through replaceFields(), which will wrap any
* unwrapped values with an appropriate SCO wrapper.
*/
void replaceSCOFields()
{
StateFieldManager scoFieldValues = new StateFieldManager();
provideFields(secondClassMutableFieldNumbers, scoFieldValues);
replaceFields(secondClassMutableFieldNumbers, scoFieldValues);
}
Object wrapSCOInstance(int field, Object value)
{
if (value == null || (value instanceof SCO && ((SCO)value).getOwner() == myPC))
return value;
else
{
FieldMetaData fmd = cmd.getFieldAbsolute(field);
Class fieldType = fmd.getType();
SCOProcessor scoProc = SCOProcessor.forFieldType(fieldType);
if (scoProc == null)
throw new JDOUserException("Class not supported as a second-class object: " + fieldType.getName());
return scoProc.newSCOInstance(myPC, fmd.getName(), value);
}
}
/**
* Calls unsetOwner() on all SCO fields.
*/
private void disownSCOFields()
{
provideFields(secondClassMutableFieldNumbers, new AbstractFieldManager()
{
public void storeObjectField(int fieldNumber, Object value)
{
if (value instanceof SCO)
((SCO)value).unsetOwner();
}
});
}
private boolean isDFGLoaded()
{
for (int i = 0; i < defaultFetchGroupFieldNumbers.length; ++i)
{
if (!loadedFields[defaultFetchGroupFieldNumbers[i]])
return false;
}
return true;
}
void enlistInTransaction()
{
myPM.enlistInTransaction(this);
if (jdoFlags == PersistenceCapable.LOAD_REQUIRED && isDFGLoaded())
{
/*
* A transactional object whose DFG fields are loaded does not need
* to contact us in order to read those fields.
*/
jdoFlags = PersistenceCapable.READ_OK;
myPC.jdoReplaceFlags();
}
}
void evictFromTransaction()
{
myPM.evictFromTransaction(this);
if (jdoFlags == PersistenceCapable.READ_OK && myLC.isPersistent())
{
/*
* A non-transactional object needs to contact us on any field read no
* matter what fields are loaded.
*/
jdoFlags = PersistenceCapable.LOAD_REQUIRED;
myPC.jdoReplaceFlags();
}
}
void saveFields()
{
savedFlags = jdoFlags;
savedLoadedFields = (boolean[])loadedFields.clone();
savedImage = myPC.jdoNewInstance(this);
savedImage.jdoCopyFields(myPC, allFieldNumbers);
for (int i = 0; i < secondClassMutableFieldNumbers.length; ++i)
{
int field = secondClassMutableFieldNumbers[i];
/*
* Note: SCO fields in the saved image are clones and therefore
* unowned. If/when they are later restored the individual life-
* cycle states decide whether or not they must be rewrapped to get
* reowned (if going Transient they don't, otherwise they do).
*/
Object value = provideField(savedImage, field);
if (value != null)
{
if (value instanceof SCO)
value = ((SCO)value).clone();
else
{
Class c = value.getClass();
try
{
Method m = c.getMethod("clone", null);
if (m != null)
value = m.invoke(value, null);
}
catch (Exception e)
{
throw new JDOFatalInternalException("SCO class not cloneable: " + c.getName());
}
}
replaceField(savedImage, field, value);
}
}
}
void restoreFields()
{
if (savedImage != null)
{
loadedFields = savedLoadedFields;
jdoFlags = savedFlags;
myPC.jdoReplaceFlags();
myPC.jdoCopyFields(savedImage, allFieldNumbers);
clearDirtyFlags();
discardSavedFields();
}
}
void discardSavedFields()
{
savedFlags = 0;
savedLoadedFields = null;
savedImage = null;
}
void clearPersistentFields()
{
try
{
if (myPC instanceof InstanceCallbacks)
((InstanceCallbacks)myPC).jdoPreClear();
}
finally
{
replaceFieldsInternal(persistentFieldNumbers, hollowFieldManager);
clearLoadedFlags();
clearDirtyFlags();
}
}
private void clearLoadedFlags()
{
jdoFlags = PersistenceCapable.LOAD_REQUIRED;
myPC.jdoReplaceFlags();
System.arraycopy(cmd.getTransactionalFieldFlags(), 0, loadedFields, 0, loadedFields.length);
}
private void clearDirtyFlags()
{
dirty = false;
clearFlags(dirtyFields);
}
/**
* Returns an array of integers containing the indices of all elements in
* <tt>flags</tt> that are set to <tt>state</tt>.
*/
private static int[] getFlagsSetTo(boolean[] flags, boolean state)
{
int[] temp = new int[flags.length];
int j = 0;
for (int i = 0; i < flags.length; i++)
{
if (flags[i] == state)
temp[j++] = i;
}
if (j != 0)
{
int[] fieldNumbers = new int[j];
System.arraycopy(temp, 0, fieldNumbers, 0, j);
return fieldNumbers;
}
else
return null;
}
/**
* Returns an array of integers containing the indices of all elements in
* <tt>flags</tt> whose index occurs in <tt>indices</tt> and whose value is
* <tt>state</tt>.
*/
private static int[] getFlagsSetTo(boolean[] flags, int[] indices, boolean state)
{
int[] temp = new int[indices.length];
int j = 0;
for (int i = 0; i < indices.length; i++)
{
if (flags[indices[i]] == state)
temp[j++] = indices[i];
}
if (j != 0)
{
int[] fieldNumbers = new int[j];
System.arraycopy(temp, 0, fieldNumbers, 0, j);
return fieldNumbers;
}
else
return null;
}
private static void clearFlags(boolean[] flags)
{
for (int i = 0; i < flags.length; i++)
flags[i] = false;
}
private boolean disconnectClone(PersistenceCapable pc)
{
if (pc != myPC)
{
if (LOG.isDebugEnabled())
LOG.debug("Disconnecting clone " + toJVMIDString(pc) + " from " + this);
/*
* Reset jdoFlags in the clone to PersistenceCapable.READ_WRITE_OK
* and clear its state manager.
*/
byte myJdoFlags = jdoFlags;
jdoFlags = PersistenceCapable.READ_WRITE_OK;
try
{
pc.jdoReplaceFlags();
}
finally
{
jdoFlags = myJdoFlags;
}
pc.jdoReplaceStateManager(null);
return true;
}
else
return false;
}
/**
* The StateManager uses this method to supply the value of jdoFlags to the
* associated PersistenceCapable instance.
*
* @param pc
* the calling PersistenceCapable instance
* @return
* the value of jdoFlags to be stored in the PersistenceCapable instance
*/
public byte replacingFlags(PersistenceCapable pc)
{
// If this is a clone, return READ_WRITE_OK.
if (pc != myPC)
return PersistenceCapable.READ_WRITE_OK;
else
return jdoFlags;
}
/**
* Return the PersistenceManager that owns this instance.
*
* @param pc
* the calling PersistenceCapable instance
* @return
* the PersistenceManager that owns this instance
*/
public javax.jdo.PersistenceManager getPersistenceManager(PersistenceCapable pc)
{
if (disconnectClone(pc))
return null;
else
{
this.myPM.hereIsStateManager(this, myPC);
return myPM;
}
}
private void postWriteField(int field)
{
/*
* If we've written a field in the middle of inserting or flushing it
* must be due to jdoPreStore(). If inserting, we haven't actually done
* the INSERT yet so we don't want to mark anything as dirty, which
* would make us want to do an UPDATE later.
*/
if (myLC.isPersistent() && !inserting)
{
dirty = true;
dirtyFields[field] = true;
loadedFields[field] = true;
/*
* If flushing, to avoid an infinite recursion we don't notify the
* PM or call flush().
*/
if (!flushing)
{
if (myTX.isActive())
myPM.markDirty(this);
else
flush();
}
}
}
/**
* Marks the given field dirty.
*/
public void makeDirty(int field)
{
transitionWriteField();
postWriteField(field);
}
/**
* Mark the associated PersistenceCapable field dirty.
*
* @param pc the calling PersistenceCapable instance
* @param fieldName the name of the field
*/
public void makeDirty(PersistenceCapable pc, String fieldName)
{
if (!disconnectClone(pc))
{
int fieldNumber = cmd.getAbsoluteFieldNumber(fieldName);
if (fieldNumber == -1)
throw new JDOUserException("No such field '" + fieldName + "' in class " + cmd.getPCClass().getName());
makeDirty(fieldNumber);
}
}
public Object getObjectId()
{
return myID;
}
public StoreManager getStoreManager()
{
return srm;
}
public PersistenceManager getPersistenceManager()
{
return myPM;
}
/**
* Return the object representing the JDO identity of the calling instance.
*
* According to the JDO specification, if the JDO identity is being changed
* in the current transaction, this method returns the JDO identify as of the
* beginning of the transaction.
*
* @param pc the calling PersistenceCapable instance
* @return the object representing the JDO identity of the calling instance
*/
public Object getObjectId(PersistenceCapable pc)
{
if (disconnectClone(pc))
return null;
else
return myID;
}
/**
* Replace the current value of jdoStateManager.
*
* <P>This method is called by the PersistenceCapable whenever
* jdoReplaceStateManager is called and there is already
* an owning StateManager. This is a security precaution
* to ensure that the owning StateManager is the only
* source of any change to its reference in the PersistenceCapable.</p>
*
* @return the new value for the jdoStateManager
* @param pc the calling PersistenceCapable instance
* @param sm the proposed new value for the jdoStateManager
*/
public StateManager replacingStateManager(PersistenceCapable pc, StateManager sm)
{
if (myLC == null)
throw new JDOFatalInternalException("Null LifeCycleState");
if (pc == myPC)
{
if (sm != null)
throw new JDOFatalInternalException("Attempted to replace with a different state manager");
if (!disconnecting)
throw new JDOFatalInternalException("Attempted to clear state manager from other than disconnect()");
if (LOG.isDebugEnabled())
LOG.debug("Clearing state manager for " + toJVMIDString(pc));
return null;
}
else if (pc == savedImage)
return null;
else
return sm;
}
/**
* Return the object representing the JDO identity
* of the calling instance. If the JDO identity is being changed in
* the current transaction, this method returns the current identity as
* changed in the transaction.
*
* @param pc the calling PersistenceCapable instance
* @return the object representing the JDO identity of the calling instance
*/
public Object getTransactionalObjectId(PersistenceCapable pc)
{
return getObjectId(pc);
}
/**
* Tests whether this object is dirty.
*
* Instances that have been modified, deleted, or newly
* made persistent in the current transaction return true.
*
* <P>Transient nontransactional instances return false (JDO spec), but the
* TriActive implementation does not currently support the transient
* transactional state.
*
* @see PersistenceCapable#jdoMakeDirty(String fieldName)
* @param pc the calling PersistenceCapable instance
* @return true if this instance has been modified in the current transaction.
*/
public boolean isDirty(PersistenceCapable pc)
{
if (disconnectClone(pc))
return false;
else
return myLC.isDirty();
}
/**
* Tests whether this object is transactional.
*
* Instances that respect transaction boundaries return true. These instances
* include transient instances made transactional as a result of being the
* target of a makeTransactional method call; newly made persistent or deleted
* persistent instances; persistent instances read in data store
* transactions; and persistent instances modified in optimistic transactions.
*
* <P>Transient nontransactional instances return false.
*
* @param pc the calling PersistenceCapable instance
* @return true if this instance is transactional.
*/
public boolean isTransactional(PersistenceCapable pc)
{
if (disconnectClone(pc))
return false;
else
return myLC.isTransactional();
}
/**
* Tests whether this object is persistent.
*
* Instances whose state is stored in the data store return true.
*
* <P>Transient instances return false.
*
* @see PersistenceManager#makePersistent(Object pc)
* @param pc the calling PersistenceCapable instance
* @return true if this instance is persistent.
*/
public boolean isPersistent(PersistenceCapable pc)
{
if (disconnectClone(pc))
return false;
else
return myLC.isPersistent();
}
/**
* Tests whether this object has been newly made persistent.
*
* Instances that have been made persistent in the current transaction
* return true.
*
* <P>Transient instances return false.
*
* @see PersistenceManager#makePersistent(Object pc)
* @param pc the calling PersistenceCapable instance
* @return true if this instance was made persistent
* in the current transaction.
*/
public boolean isNew(PersistenceCapable pc)
{
if (disconnectClone(pc))
return false;
else
return myLC.isNew();
}
/**
* Tests whether this object has been deleted.
*
* Instances that have been deleted in the current transaction return true.
*
* <P>Transient instances return false.
*
* @see PersistenceManager#deletePersistent(Object pc)
* @param pc the calling PersistenceCapable instance
* @return true if this instance was deleted
* in the current transaction.
*/
public boolean isDeleted(PersistenceCapable pc)
{
if (disconnectClone(pc))
return false;
else
return myLC.isDeleted();
}
/**
* Called whenever the default fetch group fields have all been loaded.
* Updates jdoFlags and calls jdoPostLoad() as appropriate.
* <p>
* If it's called in the midst of a life-cycle transition both actions will
* be deferred until the transition is complete.
* <em>This deferral is important</em>.
* Without it, we could enter user code (jdoPostLoad()) while still making
* a state transition, and that way lies madness.
* <p>
* As an example, consider a jdoPostLoad() that calls other enhanced methods
* that read fields (jdoPostLoad() itself is not enhanced). A P_NONTRANS
* object accessed within a transaction would produce the following infinite
* loop:
* <p>
* <blockquote><pre>
* isLoaded()
* transitionReadField()
* refreshLoadedFields()
* jdoPostLoad()
* isLoaded()
* ...
* </pre></blockquote>
* <p>
* because the transition from P_NONTRANS to P_CLEAN can never be completed.
*/
private void postLoad()
{
if (changingState)
postLoadPending = true;
else
{
/*
* A transactional object whose DFG fields are loaded does not need
* to contact us in order to read those fields, so we can safely set
* READ_OK.
*
* A non-transactional object needs to notify us on all field reads
* so that we can decide whether or not any transition should occur,
* so we leave the flags at LOAD_REQUIRED.
*/
if (jdoFlags == PersistenceCapable.LOAD_REQUIRED && myLC.isTransactional())
{
jdoFlags = PersistenceCapable.READ_OK;
myPC.jdoReplaceFlags();
}
if (myPC instanceof InstanceCallbacks)
((InstanceCallbacks)myPC).jdoPostLoad();
}
}
/**
* Validates that the instance exists in the data store.
* Called by pm.getObjectById() when validate == true.
*/
public void validate()
{
if (!myLC.isTransactional())
{
/*
* We're either Hollow or PNonTrans. If a TX is active and there
* are any DFG fields then a retrieve() does what we want, i.e. it
* validates existence and makes us PClean. Otherwise we do the
* minimum lookup operation and leave our state as it is.
*/
if (myTX.isActive() && defaultFetchGroupFieldNumbers.length != 0)
retrieve(true);
else
srm.lookup(this);
}
}
/**
* Offers the specified pre-fetched fields to the state manager.
* If the instance is in a state that can benefit from newly available field
* values, the fields are replaced in the instance and a state change occurs
* as though the instance itself had read a field.
* Called by pm.getObjectById() when given prefetched fields.
*/
public void offerPrefetchedFields(int[] fieldNumbers, FieldManager fieldManager)
{
if (!myLC.isTransactional())
{
/* We're either Hollow or PNonTrans. Accept the fields. */
transitionReadField();
boolean dfgWasAlreadyLoaded = isDFGLoaded();
replaceFields(fieldNumbers, fieldManager);
if (!dfgWasAlreadyLoaded && isDFGLoaded())
postLoad();
}
}
/**
* Fetchs from the database all fields in the default fetch group not
* already loaded.
* Called by, or immediately after, life-cycle transitions.
*/
void loadDFGFields()
{
int[] fieldNumbers = getFlagsSetTo(loadedFields, defaultFetchGroupFieldNumbers, false);
if (fieldNumbers != null)
{
srm.fetch(this, fieldNumbers);
postLoad();
}
}
/**
* Fetch a given field from the database. Do NOT call with a default fetch
* group field.
*
* @param fieldNumber the field number of the field to fetch.
*/
private void loadNonDFGField(int fieldNumber)
{
srm.fetch(this, new int[] { fieldNumber });
}
/**
* Fetchs from the database all fields not currently loaded.
* Called by life-cycle transitions.
*/
void loadUnloadedFields()
{
int[] fieldNumbers = getFlagsSetTo(loadedFields, false);
if (fieldNumbers != null)
{
boolean dfgWasAlreadyLoaded = isDFGLoaded();
srm.fetch(this, fieldNumbers);
/* If the DFG was not already loaded, it is now. */
if (!dfgWasAlreadyLoaded)
postLoad();
}
}
/**
* Refreshes from the database all fields currently loaded.
* Called by life-cycle transitions.
*/
void refreshLoadedFields()
{
int[] fieldNumbers = getFlagsSetTo(loadedFields, true);
if (fieldNumbers != null)
{
clearDirtyFlags();
clearLoadedFlags();
srm.fetch(this, fieldNumbers);
if (isDFGLoaded())
postLoad();
}
}
/**
* Guarantee that the serializable transactional and persistent fields
* are loaded into the instance. This method is called by the generated
* jdoPreSerialize method prior to serialization of the instance.
*
* @param pc the calling PersistenceCapable instance
*/
public void preSerialize(PersistenceCapable pc)
{
if (disconnectClone(pc))
return;
retrieve(false);
}
/**
* Return true if the field is cached in the calling instance.
* <P>In the TriActive implementation of this method, isLoaded() will always
* return true. If the field is not loaded, it will be loaded as a side effect
* of the call to this method. If it is in the default fetch group, the default
* fetch group, including this field, will be loaded.
*
* @param pc the calling PersistenceCapable instance
* @param field the absolute field number
* @return always returns true (this implementation)
*/
public boolean isLoaded(PersistenceCapable pc, int field)
{
if (disconnectClone(pc))
return true;
else
{
transitionReadField();
if (!loadedFields[field])
{
if (defaultFetchGroupFields[field])
loadDFGFields();
else
loadNonDFGField(field);
}
return true;
}
}
/**
* Reads the current value of the specified field.
* This has the same effect on the state of the instance as if user code
* attempted to read the field.
*
* @param field
* The field number to read.
* @return
* The value of the field. Primitives are boxed in appropriate
* java.lang wrapper classes.
*/
public Object getField(int field)
{
isLoaded(myPC, field);
return provideField(myPC, field);
}
/**
* Updates the current value of the specified field.
* This has the same effect on the state of the instance as if user code
* attempted to write the field.
*
* @param field
* The field number to write.
* @param currentValue
* The current value of the field. Used to determine whether or not
* the write is redundant. If necessary, the value can be obtained
* using {@link #getField}.
* @param newValue
* The new value of the field. Primitives must be boxed in appropriate
* java.lang wrapper classes.
*/
public void setField(int field, Object currentValue, Object newValue)
{
if (!loadedFields[field] || currentValue != newValue)
writeObjectField(field, currentValue, newValue);
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public boolean getBooleanField(PersistenceCapable pc, int field, boolean currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public byte getByteField(PersistenceCapable pc, int field, byte currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public char getCharField(PersistenceCapable pc, int field, char currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public double getDoubleField(PersistenceCapable pc, int field, double currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public float getFloatField(PersistenceCapable pc, int field, float currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public int getIntField(PersistenceCapable pc, int field, int currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public long getLongField(PersistenceCapable pc, int field, long currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public short getShortField(PersistenceCapable pc, int field, short currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public String getStringField(PersistenceCapable pc, int field, String currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* This method is called by the associated PersistenceCapable if the
* value for the specified field is not cached (i.e., StateManager.isLoaded()
* fails). In this implementation of the StateManager, isLoaded() has a
* side effect of loading unloaded information and will always return true.
* As such, this method should never be called.
*/
public Object getObjectField(PersistenceCapable pc, int field, Object currentValue)
{
throw new JDOUnsupportedOptionException("Method not supported");
}
/**
* Called by the various setXXXField() methods once it's established that
* the field really deserves to be written.
* Makes the state transition and replaces the field value in the PC
* instance.
*/
private void writeField(int field, Object newValue)
{
transitionWriteField();
replaceField(myPC, field, newValue);
postWriteField(field);
}
/**
* Called by setObjectField() once it's established that the field really
* deserves to be written.
* Makes the state transition and replaces the field value in the PC
* instance.
* Wraps SCO fields if needed.
*/
private void writeObjectField(int field, Object currentValue, Object newValue)
{
transitionWriteField();
if (secondClassMutableFields[field])
{
if (currentValue instanceof SCO)
((SCO)currentValue).unsetOwner();
newValue = wrapSCOInstance(field, newValue);
}
replaceField(myPC, field, newValue);
postWriteField(field);
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setBooleanField(PersistenceCapable pc, int field, boolean currentValue, boolean newValue)
{
if (pc != myPC)
{
replaceField(pc, field, newValue ? Boolean.TRUE : Boolean.FALSE);
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, newValue ? Boolean.TRUE : Boolean.FALSE);
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setByteField(PersistenceCapable pc, int field, byte currentValue, byte newValue)
{
if (pc != myPC)
{
replaceField(pc, field, new Byte(newValue));
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, new Byte(newValue));
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setCharField(PersistenceCapable pc, int field, char currentValue, char newValue)
{
if (pc != myPC)
{
replaceField(pc, field, new Character(newValue));
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, new Character(newValue));
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setDoubleField(PersistenceCapable pc, int field, double currentValue, double newValue)
{
if (pc != myPC)
{
replaceField(pc, field, new Double(newValue));
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, new Double(newValue));
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setFloatField(PersistenceCapable pc, int field, float currentValue, float newValue)
{
if (pc != myPC)
{
replaceField(pc, field, new Float(newValue));
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, new Float(newValue));
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setIntField(PersistenceCapable pc, int field, int currentValue, int newValue)
{
if (pc != myPC)
{
replaceField(pc, field, new Integer(newValue));
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, new Integer(newValue));
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setLongField(PersistenceCapable pc, int field, long currentValue, long newValue)
{
if (pc != myPC)
{
replaceField(pc, field, new Long(newValue));
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, new Long(newValue));
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setShortField(PersistenceCapable pc, int field, short currentValue, short newValue)
{
if (pc != myPC)
{
replaceField(pc, field, new Short(newValue));
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeField(field, new Short(newValue));
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setStringField(PersistenceCapable pc, int field, String currentValue, String newValue)
{
if (pc != myPC)
{
replaceField(pc, field, newValue);
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || !equals(currentValue, newValue))
writeField(field, newValue);
}
}
/**
* This method is called by the associated PersistenceCapable when the
* corresponding mutator method (setXXX()) is called on the PersistenceCapable.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
* @param newValue the new value for the field
*/
public void setObjectField(PersistenceCapable pc, int field, Object currentValue, Object newValue)
{
if (pc != myPC)
{
replaceField(pc, field, newValue);
disconnectClone(pc);
}
else
{
if (!loadedFields[field] || currentValue != newValue)
writeObjectField(field, currentValue, newValue);
}
}
/**
* Compares two objects for equality, where one or both of the object
* references may be null.
*
* @return <code>true</code> if the objects are both <code>null</code> or
* compare equal according to their equals() method,
* <code>false</code> otherwise.
*/
private static boolean equals(Object o1, Object o2)
{
return o1 == null ? (o2 == null) : o1.equals(o2);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedBooleanField(PersistenceCapable pc, int field, boolean currentValue)
{
currFM.storeBooleanField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedByteField(PersistenceCapable pc, int field, byte currentValue)
{
currFM.storeByteField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedCharField(PersistenceCapable pc, int field, char currentValue)
{
currFM.storeCharField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedDoubleField(PersistenceCapable pc, int field, double currentValue)
{
currFM.storeDoubleField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedFloatField(PersistenceCapable pc, int field, float currentValue)
{
currFM.storeFloatField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedIntField(PersistenceCapable pc, int field, int currentValue)
{
currFM.storeIntField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedLongField(PersistenceCapable pc, int field, long currentValue)
{
currFM.storeLongField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedShortField(PersistenceCapable pc, int field, short currentValue)
{
currFM.storeShortField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedStringField(PersistenceCapable pc, int field, String currentValue)
{
currFM.storeStringField(field, currentValue);
}
/**
* This method is called from the associated PersistenceCapable when its
* PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
* to provide the value of the specified field to the StateManager.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param currentValue the current value of the field
*/
public void providedObjectField(PersistenceCapable pc, int field, Object currentValue)
{
currFM.storeObjectField(field, currentValue);
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a boolean field.
*
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public boolean replacingBooleanField(PersistenceCapable pc, int field)
{
boolean value = currFM.fetchBooleanField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a byte field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public byte replacingByteField(PersistenceCapable obj, int field)
{
byte value = currFM.fetchByteField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a char field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public char replacingCharField(PersistenceCapable obj, int field)
{
char value = currFM.fetchCharField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a double field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public double replacingDoubleField(PersistenceCapable obj, int field)
{
double value = currFM.fetchDoubleField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a float field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public float replacingFloatField(PersistenceCapable obj, int field)
{
float value = currFM.fetchFloatField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a int field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public int replacingIntField(PersistenceCapable obj, int field)
{
int value = currFM.fetchIntField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a long field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public long replacingLongField(PersistenceCapable obj, int field)
{
long value = currFM.fetchLongField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a short field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public short replacingShortField(PersistenceCapable obj, int field)
{
short value = currFM.fetchShortField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of a String field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public String replacingStringField(PersistenceCapable obj, int field)
{
String value = currFM.fetchStringField(field);
loadedFields[field] = true;
return value;
}
/**
* This method is invoked by the PersistenceCapable object's
* jdoReplaceField() method to refresh the value of an Object field.
*
* @param obj the calling PersistenceCapable instance
* @param field the field number
* @return the new value for the field
*/
public Object replacingObjectField(PersistenceCapable obj, int field)
{
Object value = currFM.fetchObjectField(field);
loadedFields[field] = true;
return value;
}
public PersistenceCapable getObject()
{
return myPC;
}
private synchronized Object provideField(PersistenceCapable pc, int fieldNumber)
{
Object obj;
FieldManager prevFM = currFM;
currFM = new StateFieldManager();
try
{
pc.jdoProvideField(fieldNumber);
return currFM.fetchObjectField(fieldNumber);
}
finally
{
currFM = prevFM;
}
}
private synchronized void replaceField(PersistenceCapable pc, int fieldNumber, final Object value)
{
FieldManager prevFM = currFM;
currFM = new GenericFieldManager()
{
public Object fetchObjectField(int field)
{
return value;
}
public void storeObjectField(int field, Object value) {} // not possible
};
try
{
pc.jdoReplaceField(fieldNumber);
}
finally
{
currFM = prevFM;
}
}
/**
* Called from the StoreManager after StoreManager.update() is called to obtain
* updated values from the PersistenceCapable associated with this StateManager.
*
* @param fieldNumbers
* An array of field numbers to be updated by the Store
* @param fm
* The updated values are stored in this object. This object is only valid
* for the duration of this call.
*/
public synchronized void provideFields(int fieldNumbers[], FieldManager fm)
{
FieldManager prevFM = currFM;
currFM = fm;
try
{
myPC.jdoProvideFields(fieldNumbers);
}
finally
{
currFM = prevFM;
}
}
/**
* Called to refresh data in the PersistenceCapable object associated with
* this StateManager.
* <p>
* Unwrapped values for SCO fields are wrapped with an appropriate SCO
* wrapper object.
*
* @param fieldNumbers
* An array of field numbers to be refreshed.
* @param fm
* Provides the updated values. This object is only used for the
* duration of the call.
*/
public synchronized void replaceFields(int fieldNumbers[], FieldManager fm)
{
replaceFieldsInternal(fieldNumbers, new SCOWrapper(fm, this, secondClassMutableFields));
}
private synchronized void replaceFieldsInternal(int fieldNumbers[], FieldManager fm)
{
FieldManager prevFM = currFM;
currFM = fm;
try
{
myPC.jdoReplaceFields(fieldNumbers);
}
finally
{
currFM = prevFM;
}
}
private void preStateChange()
{
changingState = true;
}
private void postStateChange()
{
changingState = false;
if (postLoadPending)
{
postLoadPending = false;
postLoad();
}
}
/**
* Makes the instance persistent.
* If the object is already persistent, this method does nothing.
* Otherwise, a new object ID for the instance is obtained from the store
* manager and the object is inserted in the data store.
* <p>
* Any failure will leave the instance in its previous life-cycle state.
*
* @return
* <code>true</code> if the object was successfully made persistent,
* <code>false</code> if the object was already persistent.
*/
public boolean makePersistent()
{
if (myLC.isPersistent())
return false;
LifeCycleState oldLC = myLC;
preStateChange();
try { myLC = myLC.transitionMakePersistent(this, myTX); }
finally { postStateChange(); }
boolean succeeded = false;
try
{
myID = srm.newObjectID(myPC.getClass());
if (inserting)
throw new JDOFatalInternalException("makePersistent() called recursively");
inserting = true;
try
{
if (myPC instanceof InstanceCallbacks)
((InstanceCallbacks)myPC).jdoPreStore();
srm.insert(this);
}
finally
{
inserting = false;
}
succeeded = true;
}
finally
{
if (!succeeded)
{
preStateChange();
try { myLC = myLC.transitionRollbackState(this, oldLC); }
finally { postStateChange(); }
myID = null;
}
}
myPM.dataStoreModified();
return true;
}
private void transitionReadField()
{
preStateChange();
try { myLC = myLC.transitionReadField(this, myTX); }
finally { postStateChange(); }
}
private void transitionWriteField()
{
preStateChange();
try { myLC = myLC.transitionWriteField(this, myTX); }
finally { postStateChange(); }
}
public void makeTransactional()
{
preStateChange();
try { myLC = myLC.transitionMakeTransactional(this, myTX); }
finally { postStateChange(); }
}
public void makeNontransactional()
{
preStateChange();
try { myLC = myLC.transitionMakeNontransactional(this); }
finally { postStateChange(); }
}
public void makeTransient()
{
preStateChange();
try { myLC = myLC.transitionMakeTransient(this); }
finally { postStateChange(); }
}
public void evict()
{
preStateChange();
try { myLC = myLC.transitionEvict(this); }
finally { postStateChange(); }
}
public void refresh()
{
preStateChange();
try { myLC = myLC.transitionRefresh(this, myTX); }
finally { postStateChange(); }
}
public void retrieve(boolean DFGOnly)
{
preStateChange();
try { myLC = myLC.transitionRetrieve(this, myTX, DFGOnly); }
finally { postStateChange(); }
}
/**
* This method is invoked when a commit is performed in a Transaction
* involving the PersistenceCapable managed by this StateManager
*/
public void postCommit()
{
preStateChange();
try { myLC = myLC.transitionCommit(this, myTX); }
finally { postStateChange(); }
}
/**
* This method is invoked when a rollback is performed in a Transaction
* involving the PersistenceCapable managed by this StateManager.
*/
public void preRollback()
{
preStateChange();
try { myLC = myLC.transitionRollback(this, myTX); }
finally { postStateChange(); }
}
void preDelete()
{
if (myPC instanceof InstanceCallbacks)
((InstanceCallbacks)myPC).jdoPreDelete();
}
/**
* Deletes the instance.
* If the object is already deleted, this method does nothing.
* Otherwise, the object is removed from the data store.
* <p>
* Any failure will leave the instance in its previous life-cycle state.
*/
public void deletePersistent()
{
if (myLC.isDeleted())
return;
if (deleting)
throw new JDOFatalInternalException("deletePersistent() called recursively");
deleting = true;
try
{
LifeCycleState oldLC = myLC;
preStateChange();
try { myLC = myLC.transitionDeletePersistent(this, myTX); }
finally { postStateChange(); }
boolean succeeded = false;
try
{
srm.delete(this);
clearLoadedFlags();
succeeded = true;
}
finally
{
if (!succeeded)
{
preStateChange();
try { myLC = myLC.transitionRollbackState(this, oldLC); }
finally { postStateChange(); }
}
}
}
finally
{
deleting = false;
}
myPM.dataStoreModified();
}
/**
* Flushes any dirty fields to the data store.
*
* <p>Note that "dirty" in this case is not equated to being in the P_DIRTY
* state. The P_DIRTY state means that at least one field in the object has
* been written by the user during the current transaction, whereas for the
* purposes of this method, a field is "dirty" if it's been written by the
* user but not yet updated in the data store. The difference is, it's
* possible for an object's state to be P_DIRTY, yet have no "dirty" fields
* because flush() has been called at least once during the transaction.
*/
public void flush()
{
if (dirty)
{
if (flushing)
throw new JDOFatalInternalException("flush() re-entered");
flushing = true;
try
{
if (myPC instanceof InstanceCallbacks && !deleting)
((InstanceCallbacks)myPC).jdoPreStore();
int[] dirtyFieldNumbers = getFlagsSetTo(dirtyFields, true);
if (dirtyFieldNumbers == null)
throw new JDOFatalInternalException("dirty == true but no fields are marked dirty");
srm.update(this, dirtyFieldNumbers);
clearDirtyFlags();
}
finally
{
flushing = false;
}
myPM.dataStoreModified();
}
}
void disconnect()
{
if (LOG.isDebugEnabled())
LOG.debug("Disconnecting " + toJVMIDString(myPC) + " from " + this);
disownSCOFields();
myPM.removeStateManager(this);
jdoFlags = PersistenceCapable.READ_WRITE_OK;
myPC.jdoReplaceFlags();
disconnecting = true;
try
{
myPC.jdoReplaceStateManager(null);
}
finally
{
disconnecting = false;
}
discardSavedFields();
myPM = null;
myPC = null;
myID = null;
myLC = null;
cmd = null;
srm = null;
}
public String toString()
{
if (myPC == null)
return "StateManager@" + System.identityHashCode(this) + "(disconnected)";
else
{
String pcClassName = myPC.getClass().getName();
return "StateManager:" + pcClassName.substring(pcClassName.lastIndexOf('.') + 1)
+ '@' + System.identityHashCode(myPC) + '(' + myID + ')';
}
}
/* ------------------------ debugging helper methods -------------------- */
private static String toJVMIDString(Object obj)
{
if (obj == null)
return "null";
else
return obj.getClass().getName() + '@' + System.identityHashCode(obj);
}
private static String booleanArrayToString(boolean[] ba)
{
if (ba == null)
return "null";
StringBuffer sb = new StringBuffer("[");
for (int i = 0; i < ba.length; ++i)
sb.append(ba[i] ? 'Y' : 'N');
sb.append(']');
return sb.toString();
}
private static String intArrayToString(int[] ia)
{
if (ia == null)
return "null";
StringBuffer sb = new StringBuffer("[");
for (int i = 0; i < ia.length; ++i)
{
if (i > 0)
sb.append(',');
sb.append(ia[i]);
}
sb.append(']');
return sb.toString();
}
private static String jdoFlagsToString(byte flags)
{
switch (flags)
{
case PersistenceCapable.LOAD_REQUIRED:
return "LOAD_REQUIRED";
case PersistenceCapable.READ_OK:
return "READ_OK";
case PersistenceCapable.READ_WRITE_OK:
return "READ_WRITE_OK";
default:
return "???";
}
}
private static void dumpPC(PersistenceCapable pc, PrintWriter out)
{
out.println(toJVMIDString(pc));
if (pc == null)
return;
out.print("jdoStateManager = "); out.println(peekField(pc, "jdoStateManager"));
out.print("jdoFlags = ");
Object flagsObj = peekField(pc, "jdoFlags");
if (flagsObj instanceof Byte)
out.println(jdoFlagsToString(((Byte)flagsObj).byteValue()));
else
out.println(flagsObj);
Class c = pc.getClass();
do
{
String[] fieldNames = helper.getFieldNames(c);
for (int i = 0; i < fieldNames.length; ++i)
{
out.print(fieldNames[i]); out.print(" = "); out.println(peekField(pc, fieldNames[i]));
}
c = c.getSuperclass();
} while (c != null && PersistenceCapable.class.isAssignableFrom(c));
}
private static Object peekField(Object obj, String fieldName)
{
try
{
/*
* This doesn't work due to security problems but you get the idea.
* I'm trying to get field values directly without going through
* the provideField machinery.
*/
Object value = obj.getClass().getDeclaredField(fieldName).get(obj);
if (value instanceof PersistenceCapable)
return toJVMIDString(value);
else
return value;
}
catch (Exception e)
{
return e.toString();
}
}
public void dump(PrintWriter out)
{
out.print("myPM = "); out.println(myPM);
out.print("myTX = "); out.println(myTX);
out.print("myID = "); out.println(myID);
out.print("myLC = "); out.println(myLC);
out.print("cmd = "); out.println(cmd);
out.print("srm = "); out.println(srm);
out.print("fieldCount = "); out.println(fieldCount);
out.print("dirty = "); out.println(dirty);
out.print("flushing = "); out.println(flushing);
out.print("changingState = "); out.println(changingState);
out.print("postLoadPending = "); out.println(postLoadPending);
out.print("disconnecting = "); out.println(disconnecting);
out.print("dirtyFields = "); out.println(booleanArrayToString(dirtyFields));
out.print("defaultFetchGroupFields = "); out.println(booleanArrayToString(defaultFetchGroupFields));
out.print("secondClassMutableFields = "); out.println(booleanArrayToString(secondClassMutableFields));
out.print("allFieldNumbers = "); out.println(intArrayToString(allFieldNumbers));
out.print("persistentFieldNumbers = "); out.println(intArrayToString(persistentFieldNumbers));
out.print("defaultFetchGroupFieldNumbers = "); out.println(intArrayToString(defaultFetchGroupFieldNumbers));
out.print("secondClassMutableFieldNumbers = "); out.println(intArrayToString(secondClassMutableFieldNumbers));
out.println();
out.print("jdoFlags = "); out.println(jdoFlagsToString(jdoFlags));
out.print("loadedFields = "); out.println(booleanArrayToString(loadedFields));
out.print("myPC = "); dumpPC(myPC, out);
out.println();
out.print("savedFlags = "); out.println(jdoFlagsToString(savedFlags));
out.print("savedLoadedFields = "); out.println(booleanArrayToString(savedLoadedFields));
out.print("savedImage = "); dumpPC(savedImage, out);
}
}
|