Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/state/StateManagerImpl.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/state/StateManagerImpl.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/state/StateManagerImpl.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/state/StateManagerImpl.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,2471 @@ +/* + * 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 + * @version 1.0.1 + */ + +package org.apache.jdo.impl.state; + +import java.util.*; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.jdo.*; + +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 1.0.1 + */ +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; + + // 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); + initializePC(); + } + + /** 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(); + myPC = jdoImplHelper.newInstance (myPCClass, this); + srm.copyKeyFieldsFromObjectId(this, myPCClass); + markPKFieldsAsLoaded(); + + } else if (srm.hasActualPCClass(ioid)){ + initializePC(); + 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 initializePC() { + if (debugging()) + debug("initializePC"); // 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]); + } + } + + // + // ------------ 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(); + + myPC = jdoImplHelper.newInstance (myPCClass, this); + StoreManager srm = myPM.getStoreManager(); + srm.copyKeyFieldsFromObjectId(this, myPCClass); + markPKFieldsAsLoaded(); + } + } + + /** 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(); + } + + // + // 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. + + loadedFields.andNot(loadedFields); + markPKFieldsAsLoaded(); + } + + /** 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); + loadedFields.andNot(loadedFields); + } + + 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 + + boolean isTransientTransactional = + !myLC.isPersistent() && myLC.isTransactional(); + if (beforeImage == null && tx.isActive() && + (tx.getOptimistic() || tx.getRestoreValues() || + isTransientTransactional)) { + + 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", null); // NOI18N + if (m != null) { + clone = m.invoke(o, 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 + + boolean isTransientTransactional = + !myLC.isPersistent() && myLC.isTransactional(); + if (tx.getOptimistic() || tx.getRestoreValues() || + isTransientTransactional) { + + 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); + } + + /** + * 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(); + } + + + /** 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); + } + + // + // 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; + } + + /** + * 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(); + } +}