blob: acf1059a78c4b4069efb90e6d3daffe612ea8153 [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* StateManagerImpl.java
*
* Created on September 1, 2000, 2:29 PM
*/
package org.apache.jdo.impl.state;
import java.util.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.jdo.*;
import javax.jdo.spi.Detachable;
import javax.jdo.spi.JDOImplHelper;
import javax.jdo.spi.PersistenceCapable;
import javax.jdo.spi.StateManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jdo.impl.model.java.runtime.RuntimeJavaModelFactory;
import org.apache.jdo.model.jdo.JDOClass;
import org.apache.jdo.model.jdo.JDOField;
import org.apache.jdo.pm.PersistenceManagerInternal;
import org.apache.jdo.sco.SCO;
import org.apache.jdo.sco.SCOCollection;
import org.apache.jdo.sco.SCOMap;
import org.apache.jdo.state.FieldManager;
import org.apache.jdo.state.StateManagerInternal;
import org.apache.jdo.store.StoreManager;
import org.apache.jdo.util.I18NHelper;
/**
* This is the StoreManager independent implemetation of
* javax.jdo.spi.StateManager interface. Delegates state transition
* requests to LifeCycleState.
*
* @author Marina Vatkina
* @version 2.0
*/
class StateManagerImpl implements StateManagerInternal {
// Reference to the associated PersistenceManager as
// PersistenceManagerInternal
private PersistenceManagerInternal myPM = null;
// Current Transaction
private Transaction tx = null;
// Associated PersistenceCapable object
private PersistenceCapable myPC = null;
/** Detached state of Detachable instance.
*/
private Object[] detachedState = null;
// LifeCycle state to handle state transition requests.
private LifeCycleState myLC = null;
private byte jdoFlags = 0;
/** beforeImage represents state of an instance before
* any change or as of the call to makePersistent/deletePersistent
*/
private PersistenceCapable beforeImage = null;
/** flushedImage represents state of an instance as of the last flush
* to the datastore.
*/
private PersistenceCapable flushedImage = null;
/** Helper StateFieldManager instance for resetting fields in a Hollow
* instance at commit/rollback.
*/
private final static StateFieldManager hollowFieldManager =
new StateFieldManager();
/** Helper StateFieldManager instance for fetching Object fields values for
* reachability and SCO processing.
*/
private StateFieldManager objectFieldManager = new StateFieldManager();
/** Reference to JDO Model.
*/
private Object metaData = null;
private Class myPCClass = null;
// Flag that indicates processing inside Transaction.afterCompletion when
// fields are being reset to Java default values
private boolean inAfterCompletion = false;
// Flag that indicates state transition to Transient inside
// disconnect() method to allow setting jdoStateManager to null
// from call-back replacingStateManager()
private boolean transitionTransient = false;
// Flag that indicates that PersistenceManagerFactory supports option
// javax.jdo.option.ChangeApplicationIdentity.
private boolean allowedChangeApplicationIdentity = false;
// Representation of the available ("get") fields
private BitSet loadedFields = null;
// Representation of the changed ("set") fields
private BitSet dirtyFields = null;
// Representation of the fieldspresent in the beforeImage
private BitSet biFields = null;
// Helper array to keep a single field number.
private int[] fieldArr = new int[1];
// The objectId of the object associated with this StateManager.
private Object objectId;
// The transactional objectId of the object associated with this
// StateManager.
private Object txObjectId;
// Object contains dependency information specific to this
// instance of the StateManager
private Object dependency = null;
// Assists in moving data from this StateManager's object to/from the
// store. See provideFields(), replaceFields(), and the getXXXField()
// and providedXXXField() methods.
private FieldManager fieldManager;
// Reference to the instance requested to provide fields via fieldManager.
private Object expectedProvider;
// Class model information
private JDOClass jdoClass;
// Number of persistent fields
private int numFields = 0;
// JDOImplHelper instance
static final JDOImplHelper jdoImplHelper =
(JDOImplHelper) AccessController.doPrivileged (
// Need to have privileges to perform JDOImplHelper.getInstance().
new PrivilegedAction () {
public Object run () {
try {
return JDOImplHelper.getInstance();
}
catch (SecurityException e) {
throw new JDOFatalUserException (msg.msg(
"EXC_CannotGetJDOImplHelper"), e); // NOI18N
}
}
}
);
/** RuntimeJavaModelFactory. */
private static final RuntimeJavaModelFactory javaModelFactory =
(RuntimeJavaModelFactory) AccessController.doPrivileged(
new PrivilegedAction () {
public Object run () {
return RuntimeJavaModelFactory.getInstance();
}
}
);
// ReachabilityHandler instance
private final static ReachabilityHandler reachabilityHandler =
ReachabilityHandler.getInstance();
// SCOProcessor instance
private final static SCOProcessor scoProcessor =
SCOProcessor.getInstance();
// Helper string
private final static String ChangeApplicationIdentityOption =
"javax.jdo.option.ChangeApplicationIdentity"; // NOI18N
/**
* I18N message handler
*/
private final static I18NHelper msg =
I18NHelper.getInstance("org.apache.jdo.impl.state.Bundle"); // NOI18N
/**
* Logger instance
*/
private static final Log logger = LogFactory.getFactory().getInstance(
"org.apache.jdo.impl.state"); // NOI18N
/** Register this class with the JDOImplHelper as an authorized class
* for replaceStateManager.
*/
static {
AccessController.doPrivileged(
new PrivilegedAction () {
public Object run () {
JDOImplHelper.registerAuthorizedStateManagerClass(StateManagerImpl.class);
return null;
}
}
);
}
/** Constructs a new <code>StateManagerImpl</code> to process
* future makePersistent request.
* @param pc the reference to the associated PersistenceCapable instance
* @param pm the reference to the associated
* PersistenceManagerInternal instance
*/
StateManagerImpl(PersistenceCapable pc, PersistenceManagerInternal pm) {
if (debugging())
debug("constructor with PC: " + pc); // NOI18N
myPC = pc;
myPCClass = pc.getClass();
initializePM(pm);
initializePCInfo();
}
/** Constructs a new <code>StateManagerImpl</code> when requested
* from query processing.
* @param uoid the reference to the user object ID
* @param ioid the reference to the internal object ID
* @param pm the reference to the associated
* PersistenceManagerInternal instance
* @param clazz Class of the PersistenceCapable instance
*/
StateManagerImpl(Object uoid, Object ioid, PersistenceManagerInternal pm,
Class clazz) {
this.objectId = ioid;
this.txObjectId = this.objectId;
if (debugging())
debug("constructor with user OID: " + uoid); // NOI18N
initializePM(pm);
StoreManager srm = myPM.getStoreManager();
myPCClass = clazz;
if (uoid == null) { // Requested by the store.
initializePC(srm);
} else if (srm.hasActualPCClass(ioid)){
initializePCInfo();
myPC = jdoImplHelper.newInstance (myPCClass, this, uoid);
markPKFieldsAsLoaded();
} // else do nothing.
if (tx.isActive() && (tx.getOptimistic() == false)) {
myLC = LifeCycleState.getLifeCycleState(LifeCycleState.HOLLOW);
} else {
myLC = LifeCycleState.getLifeCycleState(LifeCycleState.P_NON_TX);
}
if (debugging())
debug("constructor " + myLC + " for: " + myPCClass); // NOI18N
registerNonTransactional();
}
/** Initialize PC Class information.
*/
private void initializePCInfo() {
if (debugging())
debug("initializePCInfo"); // NOI18N
jdoClass = javaModelFactory.getJavaType(myPCClass).getJDOClass();
// RESOLVE: remember JDOFields of FieldDescriptors.
numFields = jdoClass.getManagedFields().length;
loadedFields = new BitSet(numFields);
dirtyFields = new BitSet(numFields);
biFields = new BitSet(numFields);
}
/** Initialize PersistenceManager related information.
* @param pm the reference to the associated
* PersistenceManagerInternal instance
*/
private void initializePM(PersistenceManagerInternal pm) {
if (debugging())
debug("initializePM: " + pm); // NOI18N
myPM = pm;
tx = myPM.currentTransaction();
PersistenceManagerFactory pmf = myPM.getPersistenceManagerFactory();
allowedChangeApplicationIdentity = pmf.supportedOptions().contains(
ChangeApplicationIdentityOption);
}
/** Mark PK fields as loaded:
*/
private void markPKFieldsAsLoaded() {
int[] pkfields = jdoClass.getPrimaryKeyFieldNumbers();
for (int i = 0; i < pkfields.length; i++) {
if (debugging())
debug("markPKFieldsAsLoaded " + myPCClass.getName() + "." + // NOI18N
getFieldName(pkfields[i]));
loadedFields.set(pkfields[i]);
}
}
/** Clears the loaded field bits and marks only the key fields, if
* any, as loaded. Note: The key fields must always be marked as
* loaded for Application Identity.
*/
private void clearLoadedFields() {
loadedFields.andNot(loadedFields);
markPKFieldsAsLoaded();
}
//
// ------------ State transition methods ------------
//
/** Transitions LifeCycleState on afterCompletion.
* @param abort true if rollback
* @param retainValues the flag that indicates how to proceed on commit.
* @param restoreValues the flag that indicates how to proceed on rollback.
*/
public void afterCompletion(boolean abort, boolean retainValues,
boolean restoreValues) {
inAfterCompletion = true;
if (debugging())
debug("afterCompletion " + myLC); // NOI18N
if (abort)
myLC = myLC.transitionRollback(restoreValues, this);
else
myLC = myLC.transitionCommit(retainValues, this);
if (debugging())
debug("afterCompletion " + myLC); // NOI18N
inAfterCompletion = false;
}
/** Transition to Persistent-New
*/
public void makePersistent() {
LifeCycleState st = myLC;
if (debugging())
debug("makePersistent " + myLC); // NOI18N
if (myLC == null) {
// New request.
initializeSM(LifeCycleState.P_NEW);
} else {
// LifeCycle is already asigned.
myLC = myLC.transitionMakePersistent(this);
}
if (st != myLC) {
// It was not a no-op...
createAllBeforeImage();
// Replace java.util SCO instances with tracked SCOs.
replaceSCOFields();
processReachability(false);
setSCOOwner(true);
}
if (debugging())
debug("makePersistent " + myLC); // NOI18N
}
/** delete persistencecapable
*/
public void deletePersistent() {
if (debugging())
debug("deletePersistent " + myLC); // NOI18N
myLC = myLC.transitionDeletePersistent(this);
//deleteRelationships();
if (debugging())
debug("deletePersistent " + myLC); // NOI18N
}
/** Transition to Transactional
*/
public void makeTransactional() {
if (debugging())
debug("makeTransactional " + myLC); // NOI18N
if (myLC == null) {
// New request.
initializeSM(LifeCycleState.T_CLEAN);
markAllDirty();
createBeforeImage();
} else if (tx.isActive()); {
// LifeCycle is already asigned.
myLC = myLC.transitionMakeTransactional(this, tx);
}
if (debugging())
debug("makeTransactional " + myLC); // NOI18N
}
/** Transition to Nontransactional
*/
public void makeNontransactional() {
if (debugging())
debug("makeNontransactional " + myLC); // NOI18N
myLC = myLC.transitionMakeNontransactional(this, tx);
if (debugging())
debug("makeNontransactional " + myLC); // NOI18N
}
/** Transition to Transient
*/
public void makeTransient() {
if (debugging())
debug("makeTransient " + myLC); // NOI18N
myLC = myLC.transitionMakeTransient(this, tx);
if (debugging())
debug("makeTransient " + myLC); // NOI18N
}
/** Transition to Hollow
*/
public void evictInstance() {
if (debugging())
debug("evictInstance " + myLC); // NOI18N
myLC = myLC.transitionEvict(this, tx);
if (debugging())
debug("evictInstance " + myLC); // NOI18N
}
/** Transition to Clean
*/
public void refreshInstance() {
if (debugging())
debug("refreshInstance " + myLC); // NOI18N
myLC = myLC.transitionRefresh(this, tx);
if (debugging())
debug("refreshInstance " + myLC); // NOI18N
}
/** Transition on retrieve request. This fetches Hollow instance
* and transitions to the appropriate LifeCycle state.
*/
public void retrieve() {
if (debugging())
debug("retrieve " + myLC); // NOI18N
myLC = myLC.transitionRetrieve(this, tx);
if (debugging())
debug("retrieve " + myLC); // NOI18N
}
/**
* Transition the lifecycle state as if the instance is retrieved from the
* datastore, but use the specified field values instead of loading them
* from the datastore.
* @param fields Indicates which fields should be replaced in the PC.
* @param fieldManager FieldManager from which the field's value should be
* obtained.
*/
// Synchronized to avoid conflicts w.r.t. fieldManager.
public synchronized void replace(int[] fields, FieldManager fieldManager) {
if (debugging())
debug("replace " + myLC); // NOI18N
myLC = myLC.transitionReplace(this, tx, fields, fieldManager);
if (debugging())
debug("replace " + myLC); // NOI18N
}
/** Fetches or refreshes pc instance. Called by
* PersistenceManager.getObjectById with validate flag set to true.
*/
public void reload() {
if (debugging())
debug("reload " + myLC); // NOI18N
try {
myLC = myLC.transitionReload(this, tx);
} catch (JDOException e) {
// RESOLVE - disconnect or just deregister?
disconnect();
throw e;
}
if (debugging())
debug("reload " + myLC); // NOI18N
}
/**
* @see org.apache.jdo.state.StateManagerInternal#flush(StoreManager srm)
*/
public boolean flush(StoreManager srm) {
LifeCycleState st = myLC;
try {
myLC = myLC.flush(loadedFields, dirtyFields, srm, this);
} catch (JDOException e) {
myLC = st;
throw e;
}
return true;
}
/**
* @see org.apache.jdo.state.StateManagerInternal#handleReachability(
* boolean flag)
*/
public void handleReachability(boolean commit) {
if (myLC.isDeleted) {
// Don't do reachability for deleted instances.
return;
} else {
processReachability(commit);
if (!commit && myLC.isPersistent() && ! myLC.isAutoPersistent()) {
// Set owner only inside an active transaction and
// only for persistent instances.
setSCOOwner(true);
}
}
}
/**
* Replaces field values that are regular SCO instances with tracked SCOs.
* @see org.apache.jdo.state.StateManagerInternal#replaceSCOFields()
*/
public void replaceSCOFields() {
SCO sco = null;
Object o = null;
Class cls = null;
JDOField jdoField = null;
int[] nonpkfields = jdoClass.getPersistentNonPrimaryKeyFieldNumbers();
for (int i = 0; i < nonpkfields.length; i++) {
int field = nonpkfields[i];
jdoField = jdoClass.getField(field);
if (isSCOType(javaModelFactory.getJavaClass(jdoField.getType()))) {
o = fetchObjectField(field);
sco = scoProcessor.getSCOField(o, jdoField, myPM);
replaceSCO(sco, field);
} // else primitive - do nothing.
}
}
/**
* Unsets owner of tracked SCO field values and marks fields as not loaded.
*/
protected void unsetSCOFields() {
Object o = null;
JDOField jdoField = null;
int[] nonpkfields = jdoClass.getPersistentNonPrimaryKeyFieldNumbers();
int tmp[] = new int[nonpkfields.length];
int l = 0;
for (int i = 0; i < nonpkfields.length; i++) {
int field = nonpkfields[i];
jdoField = jdoClass.getField(field);
if (isSCOType(javaModelFactory.getJavaClass(jdoField.getType()))) {
o = fetchObjectField(field);
if (org.apache.jdo.sco.SCO.class.isInstance(o)) {
resetOwner((SCO)o, field, false);
loadedFields.clear(field);
tmp[l] = field;
l++;
}
} // else primitive - do nothing.
}
/* DO NOT SET TO NULL
// Now set those references to null.
int[] fields = new int[l];
System.arraycopy(tmp, 0, fields, 0, l);
this.fieldManager = hollowFieldManager; // Save for callback in giveXXXField
expectedProvider = myPC; // Save for verification.
myPC.jdoReplaceFields(fields);
expectedProvider = null; // No expected request.
*/
}
/**
* @see org.apache.jdo.state.StateManagerInternal#makeDirty (int field)
*/
public void makeDirty (int field) {
if (debugging())
debug("makeDirty " + field); // NOI18N
// Reload field if necessary:
if(myLC.isPersistent()) {
loadField(field);
}
if (debugging())
debug("makeDirty " + myLC); // NOI18N
myLC = myLC.transitionWriteField(this, tx);
if (debugging())
debug("makeDirty " + myLC); // NOI18N
updateBeforeImage(getFields(field));
dirtyFields.set(field);
}
/**
* Makes newly added instances to an SCO Collection or SCO Map
* auto-persistent.
*
* @see org.apache.jdo.state.StateManagerInternal#trackUpdates (int field,
* SCO sco)
*/
public void trackUpdates (int field, SCO sco) {
if (debugging())
debug("trackUpdates " + field); // NOI18N
scoProcessor.trackUpdates(this, field, sco);
}
/**
* @see org.apache.jdo.state.StateManagerInternal#getFieldName (int field)
*/
public String getFieldName (int field) {
return jdoClass.getField(field).getName();
}
/**
* @see org.apache.jdo.state.StateManagerInternal#getPCClass ()
*/
public Class getPCClass () {
return myPCClass;
}
/**
* @see org.apache.jdo.state.StateManagerInternal#setPCClass (Class pcClass)
*/
public void setPCClass (Class pcClass) {
if (myPC == null) {
// myPC will be null if its class cannot be resolved
// until fetch.
if (debugging())
debug("setPCClass " + myLC + " for: " + pcClass); // NOI18N
myPCClass = pcClass;
initializePC(myPM.getStoreManager());
}
}
/** Tests whether this StateManager represents a instance made persistent
* object.
*
* @see org.apache.jdo.state.StateManagerInternal#isNew ()
* @return <code>true</code> if this StateManager represents an
* instance made persistent in the current transaction.
*/
public boolean isNew() {
return myLC.isNew();
}
/**
* Returns <code>true</code>, if a before image must be created. The
* decision is based on the current lifecycle state plus other conditions
* e.g. transaction type, restore values flag, etc.
* @see org.apache.jdo.state.StateManagerInternal#isBeforeImageRequired ()
* @return <code>true</code> if a before image must be created.
*/
public boolean isBeforeImageRequired() {
boolean isTransientTransactional =
!myLC.isPersistent() && myLC.isTransactional();
return (tx.getOptimistic() || tx.getRestoreValues() ||
isTransientTransactional);
}
//
// LifeCycleState transition requests
//
/** Transition to Auto-Persistent-New (persistence-by-reachability)
*/
protected void makeAutoPersistent() {
LifeCycleState st = myLC;
if (debugging())
debug("makeAutoPersistent " + myLC); // NOI18N
if (myLC == null) {
initializeSM(LifeCycleState.AP_NEW);
} else {
myLC = myLC.transitionToAutoPersistent(this);
}
if (st != myLC) {
// It was not a no-op...
createAllBeforeImage();
handleReachability(false);
}
if (debugging())
debug("makeAutoPersistent " + myLC); // NOI18N
}
/** Processes Array of referenced objects for possible auto-persistence
* (persistence-by-reachability).
* @param o Array of referenced objects
*/
protected void makeAutoPersistent(Object[] o) {
if (o != null) {
for (int i = 0; i < o.length; i++) {
if (o[i] != null) {
reachabilityHandler.process(o[i], myPM, false);
} // else nothing to do.
}
}
}
/** Set owner on elements of an Array of SCO objects.
* @param o array of referenced objects.
* @param field the field number.
* @param set true if owner field should be set, false if unset.
*/
protected void resetOwner(Object[] o, int field, boolean set) {
if (o == null) {
// Nothing to do.
return;
}
for (int i = 0; i < o.length; i++) {
if (o[i] != null) {
resetOwner(o[i], field, set);
} // else nothing to do.
}
}
/** Set owner on elements of an Iterator of SCO objects.
* @param it Iterator over referenced objects.
* @param field the field number.
* @param set true if owner field should be set, false if unset.
*/
protected void resetOwner(Iterator it, int field, boolean set) {
if (it == null) {
// Nothing to do.
return;
}
while (it.hasNext()) {
Object o = it.next();
if (o != null) {
if (o instanceof Map.Entry) {
Map.Entry mapEntry = (Map.Entry)o;
resetOwner(mapEntry.getKey(), field, set);
resetOwner(mapEntry.getValue(), field, set);
} else {
resetOwner(o, field, set);
}
}
}
}
/** Restore fields from beforeImage on commit or rollback
* called by LifeCycle on commit or rollback transition.
*/
protected void restoreFields() {
//
// Only restore if there is a before image
//
if (beforeImage != null) {
myPC.jdoCopyFields(beforeImage, getFieldNums(loadedFields));
}
}
/** Clear fields on commit or rollback
* called by LifeCycle on commit or rollback transition.
*/
protected void clearFields() {
if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
((InstanceCallbacks)myPC).jdoPreClear();
}
// Unset owner on SCO fields.
setSCOOwner(false);
// CLEAR FIELDS: myPC.clearFields();
this.fieldManager = hollowFieldManager; // Save for callback in giveXXXField
expectedProvider = myPC; // Save for verification.
myPC.jdoReplaceFields(jdoClass.getPersistentNonPrimaryKeyFieldNumbers());
expectedProvider = null; // No expected request.
// Mark only the key fields as loaded for Application Identity
clearLoadedFields();
}
/** Disconnect StateManager and PC. Called by LifeCycle when
* transition to Transient.
*/
protected void disconnect() {
deregister();
if (myPC != null) {
// myPC will be null if its class cannot be resolved
// until fetch.
// Need to set jdoFlag to READ_WRITE_OK for transient instance.
jdoFlags = PersistenceCapable.READ_WRITE_OK;
myPC.jdoReplaceFlags();
transitionTransient = true;
myPC.jdoReplaceStateManager(null);
transitionTransient = false;
}
resetRef();
}
/** Reset all settings
*/
protected void reset() {
if (debugging())
debug("reset"); // NOI18N
beforeImage = null;
flushedImage = null;
this.txObjectId = this.objectId;
dirtyFields.andNot(dirtyFields);
biFields.andNot(biFields);
}
/** Refresh object inside of an active transaction as requested by
* the LifeCycle.
*/
protected void refresh() {
int[] fields = null;
if (myPC != null) {
// myPC will be null if its class cannot be resolved
// until fetch.
resetDirtyFields();
fields = getFieldNums(loadedFields);
// Mark only the key fields as loaded for Application Identity
clearLoadedFields();
}
fetch (myPM.getStoreManager(), fields);
}
/** Load all persistent fields as requested by the LifeCycle.
*/
protected void loadUnloaded() {
// Check if some fields were not loaded yet:
int[] fields = getUnloaded(jdoClass.getPersistentFieldNumbers(),
loadedFields);
if (fields != null && fields.length > 0) {
StoreManager srm = myPM.getStoreManager();
fetch (myPM.getStoreManager(), fields);
}
}
/**
* Adds this StateManager to all caches
*/
protected void registerTransactional() {
myPM.register(this, objectId, true, false);
}
/**
* Adds this StateManager to non-transactional caches
*/
protected void registerNonTransactional() {
myPM.register(this, objectId, false, false);
}
/**
* Removes this StateManager from all the caches
*/
protected void deregister() {
if (myLC.isPersistent()) {
myPM.deregister(objectId);
} else {
myPM.deregisterTransient(this);
}
}
// For some unknown reason, the @see for jdoPreDelete results in an error
// message from javadoc. We don't know why. We have tried to fix it,
// and have tried javadoc in JDK 1.3 and 1.4beta1, to no avail.
/**
* @see javax.jdo.InstanceCallbacks#jdoPreDelete()
*/
protected void preDelete() {
if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
((InstanceCallbacks)myPC).jdoPreDelete();
}
}
/** If this class implements InstanceCallbacks, call the jdoPostLoad
* method. This is done after the default fetch group values have been
* loaded from the store.
*/
protected void postLoad() {
if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
((InstanceCallbacks)myPC).jdoPostLoad();
}
}
/**
* Called by LifeCycleState when transition persistent instance to the
* corresponding flushed state.
*/
protected void markAsFlushed() {
//
// reset the current state to the point where we can accept more changes.
//
resetDirtyFields();
myPM.markAsFlushed(this);
}
/**
* Reset beforeImage on refresh or flush
*/
protected void unsetBeforeImage() {
if (debugging())
debug("unsetBeforeImage"); // NOI18N
beforeImage = null;
biFields.andNot(biFields);
}
/**
* Create a new beforeImage in an active optimistic transaction or
* an active datastore transaction with restoreValues flag set to true
* or for a transient-transactional instance.
*/
protected void createBeforeImage() {
if (debugging())
debug("createBeforeImage: new " + (beforeImage == null) + // NOI18N
" Tx is active: " + tx.isActive()); // NOI18N
if (beforeImage == null && tx.isActive() &&
isBeforeImageRequired()) {
beforeImage = jdoImplHelper.newInstance (myPCClass, this);
int[] fields = getFieldNums(loadedFields);
beforeImage.jdoCopyFields(myPC, fields);
replaceSCOWithClones(fields);
biFields.or(loadedFields);
}
}
/**
* Verifies that this class type is a supported SCO type.
* @param type Class type to check.
* @return true if this type is a supported SCO type.
*/
protected boolean isSCOType(Class type) {
return myPM.isSupportedSCOType(type);
}
//
// Private helper methods for state transitions
//
/**
* Replaces SCO instances with clones in the before image
* to preserve the state.
* @param fields array of field numbers to process.
*/
private void replaceSCOWithClones(int[] fields) {
for (int i = 0; i < fields.length; i++) {
int field = fields[i];
JDOField jdoField = jdoClass.getField(field);
Class type = javaModelFactory.getJavaClass(jdoField.getType());
if (isSCOType(type)) {
Object o = fetchObjectField(field);
if (o == null) {
// Nothing to do.
continue;
}
Object clone = null;
if (o instanceof SCO) {
clone = ((SCO)o).clone();
} else {
try {
java.lang.reflect.Method m =
o.getClass().getMethod("clone", (Class[]) null); // NOI18N
if (m != null) {
clone = m.invoke(o, (Object[]) null);
}
} catch (Exception e) {
throw new JDOFatalInternalException(
msg.msg("EXC_SCONotCloneable", o.getClass().getName())); //NOI18N
}
}
fieldManager = objectFieldManager;
fieldManager.storeObjectField(field, clone);
expectedProvider = beforeImage; // Save for verification.
beforeImage.jdoReplaceField(field) ; //getFields(field));
expectedProvider = null; // No expected request.
}
}
}
/** Returns current value from the Object type field.
* @param field the field number
* @return current value as Object.
*/
private Object fetchObjectField(int field) {
this.fieldManager = objectFieldManager;
expectedProvider = myPC; // Save for verification.
myPC.jdoProvideField(field);
expectedProvider = null; // No expected request.
return fieldManager.fetchObjectField(field);
}
/** Transition referenced fields to Persistent at commit
* (persistence-by-reachability)
* @param commit true if it is called during commit.
*/
private void processReachability(boolean commit) {
int[] relfields = jdoClass.getPersistentRelationshipFieldNumbers();
for (int i = 0; i < relfields.length; i++) {
Object o = fetchObjectField(relfields[i]);
if (o != null) {
reachabilityHandler.process(o, myPM, commit);
}
}
}
/** Set owner on referenced SCO objects.
* @param o referenced object.
* @param field the field number.
* @param set true if owner field should be set, false if unset.
*/
private void resetOwner(Object o, int field, boolean set) {
if (o == null) {
return;
} else if(org.apache.jdo.sco.SCO.class.isInstance(o)) {
resetOwner((SCO)o, field, set);
} else { // check for elements of Arrays.
Class c = o.getClass();
if (c.isArray() && !c.getComponentType().isPrimitive()) {
resetOwner((Object[])o, field, set);
} // else do nothing
}
}
/** Set owner on referenced SCO objects.
* @param sco referenced SCO object.
* @param field the field number.
* @param set true if owner field should be set, false if unset.
*/
private void resetOwner(SCO sco, int field, boolean set) {
if (set) {
sco.setOwner(this, field);
} else {
sco.unsetOwner(this, field);
}
// Verify elements of an SCO Collection:
if (org.apache.jdo.sco.SCOCollection.class.isInstance(sco)) {
SCOCollection scoCollection = (SCOCollection)sco;
if (set) {
resetOwner(scoCollection.iterator(), field, set);
} else {
// if collection is frozen, don't thaw it to unset owner.
resetOwner(scoCollection.eitherIterator(), field, set);
}
// Verify elements and keys of an SCO Map:
} else if (org.apache.jdo.sco.SCOMap.class.isInstance(sco)) {
SCOMap scoMap = (SCOMap)sco;
if (set) {
resetOwner(scoMap.entrySet().iterator(), field, set);
} else {
// if map is frozen, don't thaw it to unset owner.
resetOwner(scoMap.eitherIterator(), field, set);
}
} // else do nothing
}
/** Load field value if necessary
* @param field the field number
*/
private void loadField (int field) {
myPM.assertReadAllowed();
LifeCycleState st = myLC;
if (debugging())
debug("loadField " + myPCClass.getName() + "." // NOI18N
+ getFieldName(field) + " " + myLC); // NOI18N
try {
if (!loadedFields.get(field)) {
StoreManager srm = myPM.getStoreManager();
fetch (srm, getFields(field));
}
myLC = myLC.transitionReadField(this, tx);
} catch (JDOException e) {
if (!st.isTransactional() && myLC.isTransactional()) {
registerNonTransactional();
}
myLC = st;
throw e;
}
if (debugging())
debug("Done: loadField " + myPCClass.getName() + "." + // NOI18N
getFieldName(field) + " " + myLC); // NOI18N
}
/** Preparation steps for replacingXXXField operation
* @param pc the calling PersistenceCapable instance
* @param field the field number
*/
private void loadingField(Object pc, int field) {
if (myPC == pc && !inAfterCompletion) {
if (debugging())
debug("loadingField " + myPCClass.getName() + "." + // NOI18N
getFieldName(field));
loadedFields.set(field);
}
}
/** Preparation steps for setXXXField operation for non-Object type field.
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param fieldManager the FieldManager that handles double-dispatch
*/
private void prepareSetField(PersistenceCapable pc, int field,
FieldManager fieldManager) {
// Check if it is attempt to change PrimaryKey value:
boolean isPrimaryKey = jdoClass.getField(field).isPrimaryKey();
if (!allowedChangeApplicationIdentity && isPrimaryKey) {
throw new JDOUnsupportedOptionException(
ChangeApplicationIdentityOption);
}
prepareSetField1(pc, field);
prepareSetField2(field, fieldManager);
}
/** Preparation steps for setXXXField operation for Object type field.
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @param fieldManager the FieldManager that handles double-dispatch
* @param currentValue current value of the field.
* @param newValue the new value of the field.
*/
private void prepareSetField(PersistenceCapable pc, int field,
FieldManager fieldManager, Object currentValue, Object newValue) {
boolean present = loadedFields.get(field);
prepareSetField1(pc, field);
// now the value is populated, if it was not:
if (!present) {
currentValue = fetchObjectField(field);
}
boolean replace = (newValue != currentValue);
// Verify elementType of a new SCO before proceeding further:
if (replace) {
assertSCOElementType(newValue, field);
}
prepareSetField2(field, fieldManager);
// Everything is fine, adjust the ownership on SCO objects:
if (replace) {
if (currentValue != null &&
org.apache.jdo.sco.SCO.class.isInstance(currentValue)) {
// Unset owner on SCO instance that is not referenced any more.
resetOwner(currentValue, field, false);
}
if (newValue != null &&
org.apache.jdo.sco.SCO.class.isInstance(newValue)) {
// This is the first time we set it:
resetOwner(newValue, field, true);
}
}
}
/** Verification and load part of the preparation steps for
* setXXXField operation.
* @param pc the calling PersistenceCapable instance
* @param field the field number
*/
private void prepareSetField1(PersistenceCapable pc, int field) {
if (myPC != pc) {
// It is clone that we will disconnect. But before set the
// field value:
expectedProvider = pc; // Save for verification.
pc.jdoReplaceField(field) ; //getFields(field));
expectedProvider = null; // No expected request.
disconnectClone(pc);
}
// Reload field if necessary:
if(myLC.isPersistent()) {
loadField(field);
}
}
/** Transtion write access and replace value step for setXXXField operation.
* @param field the field number
* @param fieldManager the FieldManager that handles double-dispatch
*/
private void prepareSetField2(int field, FieldManager fieldManager) {
LifeCycleState st = myLC;
if (debugging())
debug("prepareSetField2 " + myLC); // NOI18N
myLC = myLC.transitionWriteField(this, tx);
if (debugging())
debug("prepareSetField2 " + myLC); // NOI18N
this.fieldManager = fieldManager; // Save for callback in giveXXXField
expectedProvider = myPC; // Save for verification.
myPC.jdoReplaceField(field) ; //getFields(field));
expectedProvider = null; // No expected request.
dirtyFields.set(field);
}
/**
* Update existing beforeImage in a transaction.
*/
private void updateBeforeImage(int[] fields) {
//Do NOT create beforeImage here - this is update only.
if (beforeImage != null) {
fields = getUnloaded(fields, biFields);
if (fields != null && fields.length > 0) {
if (debugging()) {
debug("updateBeforeImage ["); // NOI18N
for (int i = 0; i < fields.length; i++)
logger.debug (fields[i] + " "); // NOI18N
logger.debug("]"); // NOI18N
}
beforeImage.jdoCopyFields(myPC, fields);
replaceSCOWithClones(fields);
}
biFields.or(loadedFields);
}
}
/**
* Replace field value with tracked SCO.
* @param sco tracked SCO instance to be replaced.
* @param field the field number.
*/
private void replaceSCO(SCO sco, int field) {
this.fieldManager = objectFieldManager;
if (sco != null) {
resetOwner(sco, field, true);
// Replace the field
fieldManager.storeObjectField(field, sco);
expectedProvider = myPC; // Save for verification.
myPC.jdoReplaceField(field) ; //getFields(field));
expectedProvider = null; // No expected request.
}
}
/**
* Change owner of all SCO fields.
*
* @param set true if owner should be set, false if references
* to this SCO instance will be nullified and owner to be set to null.
*/
private void setSCOOwner(boolean set) {
Object o = null;
Class cls = null;
JDOField jdoField = null;
int[] nonpkfields = jdoClass.getPersistentNonPrimaryKeyFieldNumbers();
Throwable[] err = new Throwable[nonpkfields.length];
int l = 0;
for (int i = 0; i < nonpkfields.length; i++) {
int field = nonpkfields[i];
jdoField = jdoClass.getField(field);
if (isSCOType(javaModelFactory.getJavaClass(jdoField.getType()))) {
o = fetchObjectField(field);
try {
if (set) {
scoProcessor.assertSCOElementType(o, jdoField);
}
resetOwner(o, field, set);
} catch (Throwable e) {
err[l++] = e;
}
} // else not an Object field - do nothing
}
if (l > 0) {
Throwable[] t = new Throwable[l];
System.arraycopy(err, 0, t, 0, l);
throw new JDOUserException(msg.msg(
"EXC_FailedToProcessAll"), t); // NOI18N
}
}
/** Assert element type of an SCO Collection or key and value types
* of an SCO Map.
* @param o Object to be tested.
* @param field the corresponding field number.
* @throws JDOUserException if assertion fails.
*/
private void assertSCOElementType(Object o, int field) {
scoProcessor.assertSCOElementType(o, jdoClass.getField(field));
}
/** Returns external representation of the transactional object id
* that can be used by the client
*/
private Object getTransactionalObjectId () {
StoreManager srm = myPM.getStoreManager();
Object oid = null;
if (flushedImage != null) {
oid = srm.getExternalObjectId(txObjectId, flushedImage);
} else if (beforeImage != null) {
oid = srm.getExternalObjectId(txObjectId, beforeImage);
} else {
oid = srm.getExternalObjectId(txObjectId, myPC);
}
return oid;
}
/** Create beforeImage for all fields - called by transition from TRANSIENT
* to P_NEW
*/
private void createAllBeforeImage() {
markAllDirty();
if (debugging())
debug("createBeforeImage"); // NOI18N
if (isBeforeImageRequired()) {
beforeImage = jdoImplHelper.newInstance (myPCClass, this);
beforeImage.jdoCopyFields(myPC, getFieldNums(loadedFields));
biFields.or(loadedFields);
}
}
/** Mark all fields as loaded and dirty - called by transition from
* TRANSIENT to P_NEW and T_CLEAN
*/
private void markAllDirty() {
for (int i = 0; i < numFields; i++) {
loadedFields.set(i);
dirtyFields.set(i);
}
}
/** Initialize SM reference in PC and Oid
*/
private void initializeSM(int newState) {
myLC = LifeCycleState.getLifeCycleState(newState);
final StateManagerImpl thisSM = this;
try {
if (myLC.isPersistent()) {
StoreManager srm = myPM.getStoreManager();
objectId = srm.createObjectId(this, myPM);
myPM.register(this, objectId, true, true);
this.txObjectId = this.objectId;
} else {
myPM.registerTransient(this);
}
// Everything OK so far. Now we can set SM reference in PC
// It can be done only after myLC is set to deligate validation
// to the LC and objectId verified for uniqueness
AccessController.doPrivileged (
// Need to have privileges to perform jdoReplaceStateManager.
new PrivilegedAction () {
public Object run () {
try {
myPC.jdoReplaceStateManager(thisSM);
return null;
}
catch (SecurityException e) {
throw new JDOFatalUserException (msg.msg(
"EXC_CannotSetStateManager"), e); // NOI18N
}
}
}
);
} catch (SecurityException e) {
throw new JDOUserException(e.getMessage());
} catch (JDOException e1) {
if (myPM.getStateManager(objectId, myPCClass) == this) {
deregister();
}
resetRef();
throw e1;
}
}
/** Clear dirtyFields list on flush
*/
private void resetDirtyFields() {
// RESOLVE: flushed Oid
dirtyFields.andNot(dirtyFields);
}
/** Reset all references to null
*/
private void resetRef() {
if (debugging())
debug("resetRef"); // NOI18N
myPC = null;
myPM = null;
myLC = null;
jdoClass = null;
myPCClass = null;
beforeImage = null;
flushedImage = null;
this.txObjectId = null;
this.objectId = null;
}
/** Desconnects clone instance.
* @return true if it was clone.
*/
private boolean disconnectClone(PersistenceCapable pc) {
if (pc != myPC) {
// Replace jdoFlags. It will be set to
// PersistenceCapable.READ_WRITE_OK
// by replacingFlags().
pc.jdoReplaceFlags();
// Reset StateManager:
pc.jdoReplaceStateManager(null);
return true;
}
return false;
}
/** Verifies field provider
* @throws JDOUserException if provider is not the one expected.
*/
private boolean verifyProvider(PersistenceCapable pc) {
if (pc != expectedProvider) {
if (pc == myPC || pc == beforeImage || pc == flushedImage) {
throw new JDOFatalInternalException(
msg.msg("EXC_WrongProvider")); // NOI18N
}
disconnectClone(pc);
return false;
}
return true;
}
/** Fetches instance from the data store
*/
private void fetch(StoreManager srm) {
fetch(srm, null);
}
/** Fetches specific fields in the instance from the data store
*/
private void fetch(StoreManager srm, int[] fetchFields) {
srm.fetch(this, fetchFields);
}
/**
* Create a new PC instance with key fields copied from objectId
*/
private void initializePC(StoreManager srm) {
initializePCInfo();
if(srm.isMediationRequiredToCopyOid()) {
myPC = jdoImplHelper.newInstance (myPCClass, this);
srm.copyKeyFieldsFromObjectId(this, myPCClass);
} else {
myPC = jdoImplHelper.newInstance (myPCClass, this, objectId);
}
markPKFieldsAsLoaded();
}
/**
* Helper method to define the list of fields to be loaded
* together with this field
*/
private int[] getFields(int field) {
// RESOLVE: SRM.dosomething(field)
fieldArr[0] = field;
return fieldArr;
}
/**
* Helper method to convert not loaded bits to field numbers.
*/
private int[] getUnloaded(int[] newfields, BitSet set) {
int l = 0;
int tmp[] = new int[newfields.length];
for (int i = 0; i < newfields.length; i++) {
if (set.get(newfields[i]) == false) {
tmp[l] = newfields[i];
l++;
}
}
int[] fields = new int[l];
System.arraycopy(tmp, 0, fields, 0, l);
return fields;
}
/**
* Helper method to convert set bits in a BitSet to field numbers.
*/
private int[] getFieldNums(BitSet bs) {
int length = 0;
int tmp[] = new int[numFields];
for (int i = 0; i < numFields; i++) {
if (bs.get(i) == true) {
tmp[length] = i;
length++;
}
}
int[] fields = new int[length];
System.arraycopy(tmp, 0, fields, 0, length);
return fields;
}
//
// ------------ End methods for state transitions ------------
//
/** The owning StateManager uses this method to supply the
* value of the flags to the 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 (myPC != pc) {
// This is clone - set flags as in a transient instance:
return PersistenceCapable.READ_WRITE_OK;
}
return jdoFlags;
}
/** Replace the current value of jdoStateManager.
* 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 itself.
* @param pc the calling PersistenceCapable instance
* @return the new value for the jdoStateManager
*/
public StateManager replacingStateManager (PersistenceCapable pc,
StateManager sm) {
if (myLC == null) {
// This should never happen. LifeCycle is set the first
// thing on makePersistent and makeTransactional.
throw new JDOFatalInternalException(msg.msg(
"EXC_NullLifeCycle")); // NOI18N
}
// This is the same PC - not beforeImage or clone.
if (myPC == pc) {
// This call back should happen only when LifeCycle is
// transitioning to Transient
if (sm == null) { // transitioning to transient...
if (!transitionTransient) { // we are NOT transitioning to transient
throw new JDOFatalInternalException(msg.msg(
"EXC_NotTransitionTransient")); // NOI18N
}
return null; // OK.
} else if (sm == this) { // should not happen
throw new JDOFatalInternalException(msg.msg(
"EXC_SameStateManager")); // NOI18N
} else if (this.myPM == ((StateManagerImpl)sm).myPM) {
// same PM && sm != null && sm != this
// This is a race condition when makePersistent or
// makeTransactional is called on the same PC instance
// for the same PM. It has been already set to this SM
// - just disconnect the other one. Return this SM so
// it won't be replaced.
((StateManagerImpl)sm).disconnect();
return this;
} else { // another PM && sm != null && sm != this
// This is race condition when makePersistent or
// makeTransactional is called on the same PC instance
// for different PM
throw new JDOUserException(msg.msg(
"EXC_PersistentInAnotherPersistenceManager"));// NOI18N
}
} else if (pc != beforeImage) {
/* This indicates that the clone is calling the method.
* Either the clone is trying to become persistent or we told it
* to become transient.
*/
if (sm != null) {
// It is not becoming transient - force it:
((StateManagerImpl)sm).disconnect();
disconnectClone(pc);
}
return null;
}
return null;
}
/** Return the PersistenceManager that owns this instance.
* Called from internal methods. No validation performed.
* @return the PersistenceManager that owns this instance
*/
public PersistenceManagerInternal getPersistenceManager () {
if (debugging())
debug("getPersistenceManager " + myPM); // NOI18N
return myPM;
}
/** Return the PersistenceManager that owns this instance as
* PersistenceManager wrapper. If called by PersistenceManagerImpl, it
* will perform validation during this call.
* @param pc the calling PersistenceCapable instance
* @return the PersistenceManager that owns this instance
*/
public PersistenceManager getPersistenceManager (PersistenceCapable pc) {
if (disconnectClone(pc)) {
// This was clone. It should be transient now:
return null;
} else {
myPM.hereIsStateManager(this, myPC);
return myPM.getCurrentWrapper();
}
}
/** Mark the associated PersistenceCapable field dirty.
* <P> The StateManager will make a copy of the field
* so it can be restored if needed later, and then mark
* the field as modified in the current transaction.
* @param pc the calling PersistenceCapable instance
* @param fieldName the name of the field
*/
public void makeDirty (PersistenceCapable pc, String fieldName) {
if (debugging())
debug("makeDirty " + fieldName); // NOI18N
if (disconnectClone(pc)) {
// This was clone. It should be transient now:
return;
}
JDOField f = jdoClass.getManagedField(fieldName);
if (f != null) {
this.makeDirty(f.getFieldNumber());
} // else non-managed field - do nothing.
}
/** 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 identity 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)) {
// This was clone. It should be transient now:
return null;
}
return getExternalObjectId();
}
/** 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){
if (disconnectClone(pc)) {
// This was clone. It should be transient now:
return null;
}
return getTransactionalObjectId();
}
/** Return the object representing the version
* of the calling instance.
* @param pc the calling <code>PersistenceCapable</code> instance
* @return the object representing the version of the calling instance
* @since 2.0
*/
public Object getVersion (PersistenceCapable pc) {
throw new UnsupportedOperationException(
"Method getVersion(PersistenceCapable) not yet implemented");
}
/** Tests whether this object is dirty.
*
* Instances that have been modified, deleted, or newly
* made persistent in the current transaction return true.
*
*<P>Transient instances return false.
*<P>
* @see javax.jdo.spi.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) || myLC == null)
return false;
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 instances return false.
*<P>
* @param pc the calling PersistenceCapable instance
* @return true if this instance is transactional.
*/
public boolean isTransactional(PersistenceCapable pc) {
if (disconnectClone(pc) || myLC == null)
return false;
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.
*<P>
* @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) || myLC == null)
return false;
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.
*<P>
* @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) || myLC == null)
return false;
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.
*<P>
* @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) || myLC == null)
return false;
return myLC.isDeleted();
}
/** Guarantee that the serializable transactional and persistent fields
* are loaded into the instance. This method is called by the generated
* or user-written writeObject method prior to serialization of the
* instance.
* @param pc the calling PersistenceCapable instance
*/
public void preSerialize (PersistenceCapable pc) {
if (disconnectClone(pc)) {
// It was clone that we disconnected. Return as
// for any transient instance.
return ;
}
if (debugging())
debug("preSerialize " + myLC); // NOI18N
myLC = myLC.transitionReload(this, tx);
if (debugging())
debug("preSerialize " + myLC); // NOI18N
// Check if some fields were not loaded yet:
int[] fields = getUnloaded(
jdoClass.getPersistentSerializableFieldNumbers(), loadedFields);
// RESOLVE: how to skip java transient fields:
if (fields != null && fields.length > 0) {
StoreManager srm = myPM.getStoreManager();
fetch (myPM.getStoreManager(), fields);
}
}
/** This implementation of isLoaded will always return true.
* So the getXXXField methods do not ever need to be implemented.
* @param pc the calling PersistenceCapable instance
* @param field the field number
* @return true
*/
public boolean isLoaded (PersistenceCapable pc, int field) {
if (disconnectClone(pc)) {
// It was clone that we disconnected. Return true as
// for any transient instance.
return true;
}
loadField(field);
return loadedFields.get(field);
}
//
// getXXXField methods
//
// In the RI, these methods all throw a JDOFatalInternalException, since
// their implementation is not needed. See StateManagerImpl.isLoaded
//
/**
* @see javax.jdo.spi.StateManager#getBooleanField(
* PersistenceCapable pc, int field, boolean currentValue)
*/
public boolean getBooleanField(PersistenceCapable pc, int field,
boolean currentValue) {
notNeededByRI("getBooleanField"); // NOI18N
return false;
}
/**
* @see javax.jdo.spi.StateManager#getCharField(PersistenceCapable
* pc, int field, char currentValue)
*/
public char getCharField(PersistenceCapable pc, int field,
char currentValue) {
notNeededByRI("getCharField"); // NOI18N
return 'a';
}
/**
* @see javax.jdo.spi.StateManager#getByteField(
* PersistenceCapable pc, int field, byte currentValue)
*/
public byte getByteField(PersistenceCapable pc, int field,
byte currentValue) {
notNeededByRI("getByteField"); // NOI18N
return 0;
}
/**
* @see javax.jdo.spi.StateManager#getShortField(
* PersistenceCapable pc, int field, short currentValue)
*/
public short getShortField(PersistenceCapable pc, int field,
short currentValue) {
notNeededByRI("getShortField"); // NOI18N
return 0;
}
/**
* @see javax.jdo.spi.StateManager#getIntField(
* PersistenceCapable pc, int field, int currentValue)
*/
public int getIntField(PersistenceCapable pc, int field,
int currentValue) {
notNeededByRI("getIntField"); // NOI18N
return 0;
}
/**
* @see javax.jdo.spi.StateManager#getLongField(
* PersistenceCapable pc, int field, long currentValue)
*/
public long getLongField(PersistenceCapable pc, int field,
long currentValue) {
notNeededByRI("getLongField"); // NOI18N
return 0;
}
/**
* @see javax.jdo.spi.StateManager#getFloatField(
* PersistenceCapable pc, int field, float currentValue)
*/
public float getFloatField(PersistenceCapable pc, int field,
float currentValue) {
notNeededByRI("getFloatField"); // NOI18N
return 0.0F;
}
/**
* @see javax.jdo.spi.StateManager#getDoubleField(
* PersistenceCapable pc, int field, double currentValue)
*/
public double getDoubleField(PersistenceCapable pc, int field,
double currentValue) {
notNeededByRI("getDoubleField"); // NOI18N
return 0.0;
}
/**
* @see javax.jdo.spi.StateManager#getStringField(
* PersistenceCapable pc, int field, String currentValue)
*/
public String getStringField(PersistenceCapable pc, int field,
String currentValue) {
notNeededByRI("getStringField"); // NOI18N
return null;
}
/**
* @see javax.jdo.spi.StateManager#getObjectField(
* PersistenceCapable pc, int field, Object currentValue)
*/
public Object getObjectField(PersistenceCapable pc, int field,
Object currentValue) {
notNeededByRI("getObjectField"); // NOI18N
return null;
}
private void notNeededByRI(String s) {
throw new JDOFatalInternalException(msg.msg(
"EXC_NotNeededByRI", s)); // NOI18N
}
//
// setXXXField methods
//
/**
* @see javax.jdo.spi.StateManager#setBooleanField(
* PersistenceCapable pc, int field, boolean currentValue, boolean newValue)
*/
public void setBooleanField(PersistenceCapable pc, int field,
boolean currentValue, boolean newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeBooleanField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setCharField(
* PersistenceCapable pc, int field, char currentValue, char newValue)
*/
public void setCharField(PersistenceCapable pc, int field,
char currentValue, char newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeCharField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setByteField(
* PersistenceCapable pc, int field, byte currentValue, byte newValue)
*/
public void setByteField(PersistenceCapable pc, int field,
byte currentValue, byte newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeByteField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setShortField(
* PersistenceCapable pc, int field, short currentValue, short newValue)
*/
public void setShortField(PersistenceCapable pc, int field,
short currentValue, short newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeShortField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setIntField(
* PersistenceCapable pc, int field, int currentValue, int newValue)
*/
public void setIntField(PersistenceCapable pc, int field,
int currentValue, int newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeIntField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setLongField(
* PersistenceCapable pc, int field, long currentValue, long newValue)
*/
public void setLongField(PersistenceCapable pc, int field,
long currentValue, long newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeLongField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setFloatField(
* PersistenceCapable pc, int field, float currentValue, float newValue)
*/
public void setFloatField(PersistenceCapable pc, int field,
float currentValue, float newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeFloatField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setDoubleField(
* PersistenceCapable pc, int field, double currentValue, double newValue)
*/
public void setDoubleField(PersistenceCapable pc, int field,
double currentValue, double newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeDoubleField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setStringField(
* PersistenceCapable pc, int field, String currentValue, String newValue)
*/
public void setStringField(PersistenceCapable pc, int field,
String currentValue, String newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeStringField(field, newValue);
prepareSetField(pc, field, sfm);
}
/**
* @see javax.jdo.spi.StateManager#setObjectField(
* PersistenceCapable pc, int field, Object currentValue, Object newValue)
*/
public void setObjectField(PersistenceCapable pc, int field,
Object currentValue, Object newValue) {
StateFieldManager sfm = new StateFieldManager();
sfm.storeObjectField(field, newValue);
prepareSetField(pc, field, sfm, currentValue, newValue);
}
//
// providedXXXField methods
//
/**
* @see javax.jdo.spi.StateManager#providedBooleanField(
* PersistenceCapable pc, int field, boolean currentValue)
*/
public void providedBooleanField(PersistenceCapable pc, int field,
boolean currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeBooleanField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedCharField(
* PersistenceCapable pc, int field, char currentValue)
*/
public void providedCharField(PersistenceCapable pc, int field,
char currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeCharField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedByteField(
* PersistenceCapable pc, int field, byte currentValue)
*/
public void providedByteField(PersistenceCapable pc, int field,
byte currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeByteField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedShortField(
* PersistenceCapable pc, int field, short currentValue)
*/
public void providedShortField(PersistenceCapable pc, int field,
short currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeShortField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedIntField(
* PersistenceCapable pc, int field, int currentValue)
*/
public void providedIntField(PersistenceCapable pc, int field,
int currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeIntField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedLongField(
* PersistenceCapable pc, int field, long currentValue)
*/
public void providedLongField(PersistenceCapable pc, int field,
long currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeLongField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedFloatField(
* PersistenceCapable pc, int field, float currentValue)
*/
public void providedFloatField(PersistenceCapable pc, int field,
float currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeFloatField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedDoubleField(
* PersistenceCapable pc, int field, double currentValue)
*/
public void providedDoubleField(PersistenceCapable pc, int field,
double currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeDoubleField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedStringField(
* PersistenceCapable pc, int field, String currentValue)
*/
public void providedStringField(PersistenceCapable pc, int field,
String currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeStringField(field, currentValue);
}
}
/**
* @see javax.jdo.spi.StateManager#providedObjectField(
* PersistenceCapable pc, int field, Object currentValue)
*/
public void providedObjectField(PersistenceCapable pc, int field,
Object currentValue) {
if (verifyProvider(pc)) {
fieldManager.storeObjectField(field, currentValue);
}
}
//
// replacingXXXField methods
//
/**
* @see javax.jdo.spi.StateManager#replacingBooleanField(
* PersistenceCapable pc, int field)
*/
public boolean replacingBooleanField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchBooleanField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingCharField(
* PersistenceCapable pc, int field)
*/
public char replacingCharField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchCharField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingByteField(
* PersistenceCapable pc, int field)
*/
public byte replacingByteField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchByteField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingShortField(
* PersistenceCapable pc, int field)
*/
public short replacingShortField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchShortField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingIntField(
* PersistenceCapable pc, int field)
*/
public int replacingIntField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchIntField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingLongField(
* PersistenceCapable pc, int field)
*/
public long replacingLongField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchLongField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingFloatField(
* PersistenceCapable pc, int field)
*/
public float replacingFloatField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchFloatField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingDoubleField(
* PersistenceCapable pc, int field)
*/
public double replacingDoubleField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchDoubleField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingStringField(
* PersistenceCapable pc, int field)
*/
public String replacingStringField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchStringField(field);
}
/**
* @see javax.jdo.spi.StateManager#replacingObjectField(
* PersistenceCapable pc, int field)
*/
public Object replacingObjectField(PersistenceCapable pc, int field) {
loadingField(pc, field);
return fieldManager.fetchObjectField(field);
}
/** The replacing value of the object id in the calling instance.
* @param pc the calling <code>PersistenceCapable</code> instance
* @param oid the current value of the oid
* @return the new value for the oid
* @since 2.0
*/
public Object replacingObjectId (PersistenceCapable pc, Object oid) {
throw new UnsupportedOperationException(
"Method replacingObjectId(PersistenceCapable, Object) not yet implemented");
}
/** The replacing value of the version in the calling instance.
* @param pc the calling <code>PersistenceCapable</code> instance
* @param version the current value of the version
* @return the new value for the version
* @since 2.0
*/
public Object replacingVersion (PersistenceCapable pc, Object version) {
throw new UnsupportedOperationException(
"Method replacingVersion(PersistenceCapable, Object) not yet implemented");
}
/** The provided value of the loaded field list in the calling instance.
* @param pc the calling <code>PersistenceCapable</code> instance
* @param loaded the current value of the loaded field list
* @since 2.0
*/
public void providedLoadedFieldList (PersistenceCapable pc, BitSet loaded) {
throw new UnsupportedOperationException(
"Method providedLoadedFieldList(PersistenceCapable, BitSet) not yet implemented");
}
/** The replacing value of the loaded field list in the calling instance.
* @param pc the calling <code>PersistenceCapable</code> instance
* @param loaded the current value of the loaded field list
* @return the replacement value for the loaded field list
* @since 2.0
*/
public BitSet replacingLoadedFieldList (PersistenceCapable pc, BitSet loaded) {
throw new UnsupportedOperationException(
"Method replacingLoadedFieldList(PersistenceCapable, BitSet) not yet implemented");
}
/** The provided value of the modified field list in the calling instance.
* @param pc the calling <code>PersistenceCapable</code> instance
* @param modified the current value of the modified field list
* @since 2.0
*/
public void providedModifiedFieldList (PersistenceCapable pc, BitSet modified) {
throw new UnsupportedOperationException(
"Method providedModifiedFieldList(PersistenceCapable, BitSet) not yet implemented");
}
/** The replacing value of the modified field list in the calling instance.
* @param pc the calling <code>PersistenceCapable</code> instance
* @param modified the current value of the modified field list
* @return the replacement value for the modified field list
* @since 2.0
*/
public BitSet replacingModifiedFieldList (PersistenceCapable pc, BitSet modified) {
throw new UnsupportedOperationException(
"Method replacingModifiedFieldList(PersistenceCapable, BitSet) not yet implemented");
}
//
// Implemention of other StateManagerInternal methods
//
/**
* Returns true if current state is present in the datastore.
*/
public boolean isStored() {
return myLC.isStored();
}
/**
* Returns true if current state is flushed.
*/
public boolean isFlushed() {
return myLC.isFlushed();
}
/**
* @see org.apache.jdo.state.StateManagerInternal#setDependency(
* Object dependency)
*/
public Object setDependency(Object dependency) {
return this.dependency = dependency;
}
/**
* @see org.apache.jdo.state.StateManagerInternal#getDependency()
*/
public Object getDependency() {
return dependency;
}
/**
* @see org.apache.jdo.state.StateManagerInternal#getObject()
*/
public PersistenceCapable getObject() {
return myPC;
}
/**
* @see org.apache.jdo.state.StateManagerInternal#setObjectId(Object objectId)
*/
public void setObjectId(Object objectId) {
myPM.replaceObjectId(this.objectId, objectId);
this.objectId = objectId;
// RESOLVE: what will happen if we support PK updates?
this.txObjectId = objectId;
}
/** Return the object representing the JDO identity
* of the associated instance
* @return the object representing the JDO identity of the associated
* instance.
*/
public Object getInternalObjectId () {
if (null == objectId) {
if (debugging())
debug("getInternalObjectId"); // NOI18N
StoreManager srm = myPM.getStoreManager();
objectId = srm.createObjectId(this, myPM);
txObjectId = this.objectId;
}
return objectId;
}
/** Returns external representation of the object id that can be used
* by the client
*/
public Object getExternalObjectId () {
StoreManager srm = myPM.getStoreManager();
return srm.getExternalObjectId(objectId, myPC);
}
/**
* @see org.apache.jdo.state.StateManagerInternal#provideField(
* int fieldNumber,
* FieldManager fieldManager, boolean identifying)
*/
// Synchronized to avoid conflicts w.r.t. fieldManager.
public synchronized void provideField(int fieldNumber,
FieldManager fieldManager,
boolean identifying) {
this.fieldManager = fieldManager; // Save for callback in giveXXXField
if (identifying) {
if (flushedImage != null) {
expectedProvider = flushedImage; // Save for verification.
flushedImage.jdoProvideField(fieldNumber);
} else {
expectedProvider = beforeImage; // Save for verification.
beforeImage.jdoProvideField(fieldNumber);
}
} else {
expectedProvider = myPC; // Save for verification.
myPC.jdoProvideField(fieldNumber);
}
expectedProvider = null; // No expected request.
}
/**
* @see org.apache.jdo.state.StateManagerInternal#provideFields(int[] fields,
* FieldManager fieldManager, boolean identifying)
*/
// Synchronized to avoid conflicts w.r.t. fieldManager.
public synchronized void provideFields(int fields[],
FieldManager fieldManager,
boolean identifying) {
this.fieldManager = fieldManager; // Save for callback in giveXXXField
if (identifying) {
if (flushedImage != null) {
expectedProvider = flushedImage; // Save for verification.
flushedImage.jdoProvideFields(fields);
} else {
expectedProvider = beforeImage; // Save for verification.
beforeImage.jdoProvideFields(fields);
}
} else {
expectedProvider = myPC; // Save for verification.
myPC.jdoProvideFields(fields);
}
expectedProvider = null; // No expected request.
}
/**
* @see org.apache.jdo.state.StateManagerInternal#replaceFields(int[] fields,
* FieldManager fieldManager)
*/
// Synchronized to avoid conflicts w.r.t. fieldManager.
public synchronized void replaceFields(int[] fields,
FieldManager fieldManager) {
this.fieldManager = fieldManager; // Save for callback in giveXXXField
if (fields != null && fields.length > 0) {
expectedProvider = myPC; // Save for verification.
myPC.jdoReplaceFields(fields);
expectedProvider = null; // No expected request.
// Merge BeforeImage
updateBeforeImage(fields);
}
this.fieldManager = null;
}
/**
* Replace the detached state of the caller with the return value.
* This method is called only in response to this StateManager
* calling jdoReplaceDetachedState().
* <P>This method performs two functions:
* <ul><li>it gets the detached state from
* a Detachable instance when attaching it. In this case, after
* this call, the detachedState in the StateManager instance will
* have the value from the Detachable instance.
* </li><li>it sets the detached state in a Detachable instance
* prior to detaching (or serializing) it. In this case,
* the caller of the replaceDetachedState method must first set
* the detachedState in the StateManager instance to the value to be
* replaced in the Detachable instance.
* </li></ul>
*/
public Object[] replacingDetachedState(Detachable pc, Object[] detachedState) {
if (detachedState == null) {
// replace the null value in Detachable
return this.detachedState;
} else {
// replace this value with the value from Detachable
this.detachedState = detachedState;
return detachedState;
}
}
/**
* For replacing field values in a PC with one that is provided by
* the FieldManager. This method does not replace fields that are
* already loaded, even if their field number are included in the
* specified field number array.
* @param fields Indicates which fields should be replaced in the PC.
* @param fieldManager FieldManager from which the field values should
* be obtained.
*/
protected void replaceUnloadedFields(int[] fields,
FieldManager fieldManager) {
replaceFields(getUnloaded(fields, loadedFields), fieldManager);
}
/*
* @see org.apache.jdo.state.StateManagerInternal#preStore()
*/
public void preStore() {
if (myLC.isDeleted) {
// Don't call jdoPreStore for deleted instances.
return;
} else if (javax.jdo.InstanceCallbacks.class.isInstance(myPC)) {
((InstanceCallbacks)myPC).jdoPreStore();
}
}
/**
* Tracing method
* @param msg String to display
*/
private void debug(String msg) {
logger.debug("In StateManagerImpl " + msg); // NOI18N
}
/**
* Verifies if debugging is enabled.
* @return true if debugging is enabled.
*/
private boolean debugging() {
return logger.isDebugEnabled();
}
}