0001: /**********************************************************************
0002: Copyright (c) 2002 Kelly Grizzle and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015:
0016: Contributors:
0017: 2003 Erik Bengtson - removed exist() operation
0018: 2003 Andy Jefferson - added localiser
0019: 2003 Erik Bengtson - added new constructor for App ID
0020: 2003 Erik Bengtson - fixed loadDefaultFetchGroup to call jdoPostLoad
0021: 2003 Erik Bengtson - fixed evict to call jdoPreClear
0022: 2004 Andy Jefferson - converted to use Logger
0023: 2004 Andy Jefferson - reordered methods to put in categories, split String utilities across to StringUtils.
0024: 2004 Andy Jefferson - added Lifecycle Listener callbacks
0025: 2004 Andy Jefferson - removed JDK 1.4 methods so that we support 1.3 also
0026: 2005 Martin Taal - Contrib of detach() method for "detachOnClose" functionality.
0027: 2007 Xuan Baldauf - Contrib of initialiseForHollowPreConstructed()
0028: 2007 Xuan Baldauf - Contrib of internalXXX() methods for fields
0029: 2007 Xuan Baldauf - remove the fields "jdoLoadedFields" and "jdoModifiedFields".
0030: 2007 Xuan Baldauf - remove the fields "retrievingDetachedState" and "resettingDetachedState".
0031: 2007 Xuan Baldauf - remove the field "updatingEmbeddedFieldsWithOwner"
0032: ...
0033: **********************************************************************/package org.jpox.state;
0034:
0035: import java.io.PrintWriter;
0036: import java.security.AccessController;
0037: import java.security.PrivilegedAction;
0038: import java.util.ArrayList;
0039: import java.util.BitSet;
0040: import java.util.Collection;
0041: import java.util.HashMap;
0042: import java.util.Iterator;
0043: import java.util.List;
0044: import java.util.Map;
0045: import java.util.Set;
0046:
0047: import javax.jdo.JDOFatalInternalException;
0048: import javax.jdo.JDOFatalUserException;
0049: import javax.jdo.JDOUserException;
0050: import javax.jdo.PersistenceManager;
0051: import javax.jdo.spi.Detachable;
0052: import javax.jdo.spi.JDOImplHelper;
0053: import javax.jdo.spi.PersistenceCapable;
0054: import javax.jdo.spi.StateManager;
0055:
0056: import org.jpox.ClassLoaderResolver;
0057: import org.jpox.FetchPlan;
0058: import org.jpox.ObjectManager;
0059: import org.jpox.FetchPlan.FetchPlanForClass;
0060: import org.jpox.cache.CachedPC;
0061: import org.jpox.exceptions.ClassNotResolvedException;
0062: import org.jpox.exceptions.JPOXException;
0063: import org.jpox.exceptions.JPOXObjectNotFoundException;
0064: import org.jpox.exceptions.JPOXUserException;
0065: import org.jpox.identity.OID;
0066: import org.jpox.identity.OIDFactory;
0067: import org.jpox.jdo.JDOAdapter;
0068: import org.jpox.jdo.JPOXJDOHelper;
0069: import org.jpox.metadata.AbstractClassMetaData;
0070: import org.jpox.metadata.AbstractMemberMetaData;
0071: import org.jpox.metadata.IdentityStrategy;
0072: import org.jpox.metadata.IdentityType;
0073: import org.jpox.metadata.Relation;
0074: import org.jpox.sco.SCO;
0075: import org.jpox.sco.SCOCollection;
0076: import org.jpox.sco.SCOContainer;
0077: import org.jpox.sco.SCOMap;
0078: import org.jpox.sco.SCOUtils;
0079: import org.jpox.sco.UnsetOwners;
0080: import org.jpox.store.DatastoreClass;
0081: import org.jpox.store.FieldValues;
0082: import org.jpox.store.exceptions.NotYetFlushedException;
0083: import org.jpox.store.fieldmanager.AttachFieldManager;
0084: import org.jpox.store.fieldmanager.DeleteFieldManager;
0085: import org.jpox.store.fieldmanager.DetachFieldManager;
0086: import org.jpox.store.fieldmanager.FieldManager;
0087: import org.jpox.store.fieldmanager.LoadFieldManager;
0088: import org.jpox.store.fieldmanager.MakeTransientFieldManager;
0089: import org.jpox.store.fieldmanager.NullifyRelationFieldManager;
0090: import org.jpox.store.fieldmanager.PersistFieldManager;
0091: import org.jpox.store.fieldmanager.ReachabilityFieldManager;
0092: import org.jpox.store.fieldmanager.SingleTypeFieldManager;
0093: import org.jpox.store.fieldmanager.SingleValueFieldManager;
0094: import org.jpox.store.fieldmanager.AbstractFetchFieldManager.EndOfFetchPlanGraphException;
0095: import org.jpox.store.mapping.JavaTypeMapping;
0096: import org.jpox.util.JPOXLogger;
0097: import org.jpox.util.StringUtils;
0098: import org.jpox.util.TypeConversionHelper;
0099:
0100: /**
0101: * Implementation of the StateManager.
0102: * Implemented here as one StateManager per Object so adds on functionality particular
0103: * to each object. All PersistenceCapable objects will have a StateManager when they
0104: * have had communication with the PersistenceManager. They will typically always have
0105: * an identity also. The exception to that is for embedded/serialised objects.
0106: *
0107: * <H3>Embedded/Serialised Objects</H3>
0108: * An object that is being embedded/serialised in an owning object will NOT have an identity
0109: * unless the object is subject to a makePersistent() call also. When an object
0110: * is embedded/serialised and a field is changed, the field will NOT be marked as dirty (unless
0111: * it is also an object in its own right with an identity). When a field is changed
0112: * any owning objects are updated so that they can update their tables accordingly.
0113: *
0114: * <H3>Performance and Memory</H3>
0115: * StateManagers are very performance-critical, because for each PersistentCapable object made persistent,
0116: * there will be one StateManager instance, adding up to the total memory footprint of that object.
0117: * In heap profiling analysis, JDOStateManagerImpls showed to consume bytes 169 per StateManager by itself
0118: * and about 500 bytes per StateManager when taking PC-individual child-object (like the OID) referred
0119: * by the StateManager into account. With small Java objects this can mean a substantial memory overhead and
0120: * for applications using such small objects can be critical. For this reason the StateManager should always
0121: * be minimal in memory consumption.
0122: *
0123: * @version $Revision: 1.190 $
0124: */
0125: public final class JDOStateManagerImpl extends AbstractStateManager
0126: implements StateManager {
0127: private static final JDOImplHelper HELPER;
0128:
0129: private static final SingleTypeFieldManager HOLLOWFIELDMANAGER = new SingleTypeFieldManager();
0130:
0131: /** Flag for {@link #miscFlags} whether we are retrieving detached state from the detached object. */
0132: private static final byte MISC_RETRIEVING_DETACHED_STATE = (byte) (1 << 7);
0133:
0134: /** Flag for {@link #miscFlags} whether we are resetting the detached state. */
0135: private static final byte MISC_RESETTING_DETACHED_STATE = (byte) (1 << 6);
0136:
0137: /** Flag whether this SM is updating the ownership of its embedded/serialised field(s). */
0138: private static final byte MISC_UPDATING_EMBEDDING_FIELDS_WITH_OWNER = (byte) (1 << 5);
0139:
0140: /** Bit-packed flags that have been separate booleans (effectively separate bytes) before. */
0141: private byte miscFlags;
0142:
0143: /** Flags for DFG state stored with the object. */
0144: private byte jdoDfgFlags;
0145:
0146: /** Image of the PersistenceCapable instance when the instance is enlisted in the transaction. */
0147: private PersistenceCapable savedImage = null;
0148:
0149: /** Flags of the PersistenceCapable instance when the instance is enlisted in the transaction. */
0150: private byte savedFlags;
0151:
0152: /** Loaded fields of the PersistenceCapable instance when the instance is enlisted in the transaction. */
0153: private boolean[] savedLoadedFields = null;
0154:
0155: /** if the PersistenceCapable instance is new and was flushed to the datastore. */
0156: private boolean flushedNew = false;
0157:
0158: /** state for transitions of activities. */
0159: private ActivityState activity = ActivityState.NONE;
0160:
0161: private boolean changingState = false;
0162:
0163: private boolean postLoadPending = false;
0164:
0165: private boolean flushing = false;
0166:
0167: private boolean disconnecting = false;
0168:
0169: /** Whether this object is currently part of the detach process. */
0170: private boolean detaching = false;
0171:
0172: /** Whether this object is currently part of the attach process. */
0173: private boolean attaching = false;
0174:
0175: /** Whether this object is currently part of the makeTransient process. */
0176: private boolean makingTransient = false;
0177:
0178: /** Referenced PC object whilst attaching/detaching, for the other object in the process (if any). */
0179: private PersistenceCapable referencedPC = null;
0180:
0181: /** List of StateManagers that we must notify when we have completed inserting our record. */
0182: private List insertionNotifyList = null;
0183:
0184: /** Fields of this object that we must update when notified of the insertion of the related objects. */
0185: private Map fieldsToBeUpdatedAfterObjectInsertion = null;
0186:
0187: /** List of owners when embedded. */
0188: private List embeddedOwners = null;
0189:
0190: /** Manager of relationships for the managed object. Will be null if no fields yet to be managed. */
0191: private RelationshipManager relationManager;
0192:
0193: /**
0194: * Map of external field values (added to our object table where we dont have relations to them - unidirectional).
0195: * This will include FK and order mappings for 1-N uni.
0196: */
0197: private HashMap externalFieldValuesByMapping = null;
0198:
0199: static {
0200: HELPER = (JDOImplHelper) AccessController
0201: .doPrivileged(new PrivilegedAction() {
0202: public Object run() {
0203: try {
0204: return JDOImplHelper.getInstance();
0205: } catch (SecurityException e) {
0206: throw new JDOFatalUserException(LOCALISER
0207: .msg("026000"), e);
0208: }
0209: }
0210: });
0211: }
0212:
0213: /**
0214: * Basic constructor. Delegates to the superclass.
0215: * @param om The ObjectManager
0216: * @param cmd the metadata for the class.
0217: */
0218: public JDOStateManagerImpl(ObjectManager om,
0219: AbstractClassMetaData cmd) {
0220: super (om, cmd);
0221: }
0222:
0223: /**
0224: * Initialises a state manager to manage a hollow instance having the given object ID and the given
0225: * (optional) field values. This constructor is used for creating new instances of existing persistent
0226: * objects, and consequently shouldnt be used when the StoreManager controls the creation of such objects
0227: * (such as in an ODBMS).
0228: * @param id the JDO identity of the object.
0229: * @param fv the initial field values of the object (optional)
0230: * @param pcClass Class of the object that this will manage the state for
0231: */
0232: public void initialiseForHollow(Object id, FieldValues fv,
0233: Class pcClass) {
0234: myID = id;
0235: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
0236: LifeCycleState.HOLLOW);
0237: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
0238: if (id instanceof OID || id == null) {
0239: // Create new PC
0240: myPC = HELPER.newInstance(pcClass, this );
0241: } else {
0242: // Create new PC, and copy the key class to fields
0243: myPC = HELPER.newInstance(pcClass, this , myID);
0244: }
0245:
0246: if (fv != null) {
0247: loadFieldValues(fv);
0248: }
0249: }
0250:
0251: /**
0252: * Initialises a state manager to manage a HOLLOW / P_CLEAN instance having the given FieldValues.
0253: * This constructor is used for creating new instances of existing persistent objects using application
0254: * identity, and consequently shouldnt be used when the StoreManager controls the creation of such objects
0255: * (such as in an ODBMS).
0256: * @param fv the initial field values of the object.
0257: * @param pcClass Class of the object that this will manage the state for
0258: */
0259: public void initialiseForHollowAppId(FieldValues fv, Class pcClass) {
0260: if (cmd.getIdentityType() != IdentityType.APPLICATION) {
0261: throw new JPOXUserException(
0262: "This constructor is only for objects using application identity.")
0263: .setFatal();
0264: }
0265:
0266: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
0267: LifeCycleState.HOLLOW);
0268: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
0269: myPC = HELPER.newInstance(pcClass, this ); // Create new PC
0270: if (myPC == null) {
0271: if (!HELPER.getRegisteredClasses().contains(pcClass)) {
0272: // probably never will get here, as JDOImplHelper.newInstance() internally already throws
0273: // JDOFatalUserException when class is not registered
0274: throw new JPOXUserException(LOCALISER.msg("026018",
0275: pcClass.getName())).setFatal();
0276: } else {
0277: // Provide advisory information since we can't create an instance of this class, so maybe they
0278: // have an error in their data ?
0279: throw new JPOXUserException(LOCALISER.msg("026019",
0280: pcClass.getName())).setFatal();
0281: }
0282: }
0283:
0284: loadFieldValues(fv); // as a minimum the PK fields are loaded here
0285:
0286: // Create the ID now that we have the PK fields loaded
0287: myID = myPC.jdoNewObjectIdInstance();
0288: if (!cmd.usesSingleFieldIdentityClass()) {
0289: myPC.jdoCopyKeyFieldsToObjectId(myID);
0290: }
0291: }
0292:
0293: /**
0294: * Initialises a state manager to manage the given hollow instance having the given object ID.
0295: * Unlike the {@link #initialiseForHollow} method, this method does not create a new instance and instead
0296: * takes a pre-constructed instance.
0297: * @param id the identity of the object.
0298: * @param pc the object to be managed.
0299: */
0300: public void initialiseForHollowPreConstructed(Object id, Object pc) {
0301: myID = id;
0302: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
0303: LifeCycleState.HOLLOW);
0304: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
0305: myPC = (PersistenceCapable) pc;
0306:
0307: replaceStateManager(this ); // Assign this StateManager to the PC
0308: myPC.jdoReplaceFlags();
0309:
0310: // TODO Add to the cache
0311: }
0312:
0313: /**
0314: * Initialises a state manager to manage the passed persistent instance having the given object ID.
0315: * Used where we have retrieved a PC object from a datastore directly (not field-by-field), for example on
0316: * an object datastore. This initialiser will not add StateManagers to all related PCs. This must be done by
0317: * any calling process. This simply adds the StateManager to the specified object and records the id, setting
0318: * all fields of the object as loaded.
0319: * @param id the identity of the object.
0320: * @param pc The object to be managed
0321: */
0322: public void initialiseForPersistentClean(Object id, Object pc) {
0323: myID = id;
0324: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
0325: LifeCycleState.P_CLEAN);
0326: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
0327: myPC = (PersistenceCapable) pc;
0328:
0329: replaceStateManager(this ); // Assign this StateManager to the PC
0330: myPC.jdoReplaceFlags();
0331:
0332: // Mark all fields as loaded
0333: for (int i = 0; i < loadedFields.length; ++i) {
0334: loadedFields[i] = true;
0335: }
0336:
0337: // Add the object to the cache
0338: myOM.putObjectIntoCache(this , true, true);
0339: }
0340:
0341: /**
0342: * Initialises a state manager to manage a PersistenceCapable instance that will be EMBEDDED/SERIALISED
0343: * into another PersistenceCapable object. The instance will not be assigned an identity in the process
0344: * since it is a SCO.
0345: * @param pc The PersistenceCapable to manage (see copyPc also)
0346: * @param copyPc Whether the SM should manage a copy of the passed PC or that one
0347: */
0348: public void initialiseForEmbedded(Object pc, boolean copyPc) {
0349: pcObjectType = EMBEDDED_PC; // Default to an embedded PC object
0350: myID = null; // It is embedded at this point so dont need an ID since we're not persisting it
0351: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
0352: LifeCycleState.P_NEW);
0353: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
0354:
0355: myPC = (PersistenceCapable) pc;
0356: replaceStateManager(this ); // Set SM for embedded PC to be this
0357: if (copyPc) {
0358: // Create a new PC with the same field values
0359: PersistenceCapable pcCopy = myPC.jdoNewInstance(this );
0360: pcCopy.jdoCopyFields(myPC, getAllFieldNumbers());
0361:
0362: // Swap the managed PC to be the copy and not the input
0363: pcCopy.jdoReplaceStateManager(this );
0364: myPC = pcCopy;
0365: disconnectClone((PersistenceCapable) pc);
0366: }
0367:
0368: // Mark all fields as loaded since we are using the passed PersistenceCapable
0369: for (int i = 0; i < loadedFields.length; i++) {
0370: loadedFields[i] = true;
0371: }
0372: }
0373:
0374: /**
0375: * Initialises a state manager to manage a transient instance that is becoming newly persistent.
0376: * A new object ID for the instance is obtained from the store manager and the object is inserted
0377: * in the data store.
0378: * <p>This constructor is used for assigning state managers to existing
0379: * instances that are transitioning to a persistent state.
0380: * @param pc the instance being make persistent.
0381: * @param preInsertChanges Any changes to make before inserting
0382: */
0383: public void initialiseForPersistentNew(Object pc,
0384: FieldValues preInsertChanges) {
0385: myPC = (PersistenceCapable) pc;
0386: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
0387: LifeCycleState.P_NEW);
0388: jdoDfgFlags = PersistenceCapable.READ_OK;
0389: for (int i = 0; i < loadedFields.length; ++i) {
0390: loadedFields[i] = true;
0391: }
0392:
0393: replaceStateManager(this ); // Assign this StateManager to the PC
0394: myPC.jdoReplaceFlags();
0395:
0396: saveFields();
0397:
0398: // Populate all fields that have "value-strategy" and are not datastore populated
0399: populateStrategyFields();
0400:
0401: if (preInsertChanges != null) {
0402: // Apply any pre-insert field updates
0403: preInsertChanges.fetchFields(this );
0404: }
0405:
0406: if (cmd.getIdentityType() == IdentityType.APPLICATION) {
0407: //load key fields from Application Id instance to PC instance
0408:
0409: //if a primary key field is of type PersistenceCapable, it must first be persistent
0410: for (int fieldNumber = 0; fieldNumber < getAllFieldNumbers().length; fieldNumber++) {
0411: AbstractMemberMetaData fmd = cmd
0412: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
0413: if (fmd.isPrimaryKey()) {
0414: if (myOM.getMetaDataManager()
0415: .getMetaDataForClass(
0416: fmd.getType(),
0417: getObjectManager()
0418: .getClassLoaderResolver()) != null) {
0419: synchronized (currFMmonitor) {
0420: FieldManager prevFM = currFM;
0421: try {
0422: currFM = new SingleValueFieldManager();
0423: myPC.jdoProvideField(fieldNumber);
0424: PersistenceCapable pkFieldPC = (PersistenceCapable) ((SingleValueFieldManager) currFM)
0425: .fetchObjectField(fieldNumber);
0426: if (pkFieldPC == null) {
0427: throw new JPOXUserException(
0428: LOCALISER
0429: .msg(
0430: "026016",
0431: fmd
0432: .getFullFieldName()));
0433: }
0434: if (!myOM.getApiAdapter().isPersistent(
0435: pkFieldPC)) {
0436: // Can cause the insert of our object being managed by this SM via flush() when bidir relation
0437: myOM.persistObjectInternal(
0438: pkFieldPC, null, null, -1,
0439: org.jpox.StateManager.PC);
0440: }
0441: } finally {
0442: currFM = prevFM;
0443: }
0444: }
0445: }
0446: }
0447: }
0448: }
0449:
0450: /* Set the identity
0451: * This must come after the above block, because in identifying relationships
0452: * the PK FK associations must be persisted before, otherwise we
0453: * don't have an id assigned to the PK FK associations
0454: */
0455: setIdentity();
0456:
0457: if (this .getObjectManager().getTransaction().isActive()) {
0458: myOM.enlistInTransaction(this );
0459: }
0460:
0461: // Now in PERSISTENT_NEW so call any callbacks/listeners
0462: getCallbackHandler().postCreate(myPC);
0463:
0464: if (myOM.getOMFContext().getPersistenceConfiguration()
0465: .getManageRelationships()) {
0466: // Managed Relations : register non-null bidir fields for later processing
0467: ClassLoaderResolver clr = myOM.getClassLoaderResolver();
0468: int[] relationPositions = cmd
0469: .getRelationMemberPositions(myOM
0470: .getClassLoaderResolver());
0471: if (relationPositions != null) {
0472: for (int i = 0; i < relationPositions.length; i++) {
0473: AbstractMemberMetaData mmd = cmd
0474: .getMetaDataForManagedMemberAtAbsolutePosition(relationPositions[i]);
0475: int relationType = mmd.getRelationType(clr);
0476: if (relationType == Relation.ONE_TO_ONE_BI
0477: || relationType == Relation.MANY_TO_ONE_BI
0478: || relationType == Relation.ONE_TO_MANY_BI
0479: || relationType == Relation.MANY_TO_MANY_BI) {
0480: Object value = provideField(relationPositions[i]);
0481: if (value != null) {
0482: if (relationManager == null) {
0483: relationManager = new RelationshipManager(
0484: this );
0485: }
0486: // Store the field with value of null so it gets checked
0487: relationManager.relationChange(
0488: relationPositions[i], null, null);
0489: }
0490: }
0491: }
0492: }
0493: }
0494: }
0495:
0496: /**
0497: * Initialises a state manager to manage a Transactional Transient instance.
0498: * A new object ID for the instance is obtained from the store manager and the object is inserted in the data store.
0499: * <p>
0500: * This constructor is used for assigning state managers to Transient
0501: * instances that are transitioning to a transient clean state.
0502: * @param pc the instance being make persistent.
0503: */
0504: public void initialiseForTransactionalTransient(Object pc) {
0505: myPC = (PersistenceCapable) pc;
0506: myLC = null;
0507: jdoDfgFlags = PersistenceCapable.READ_OK;
0508: for (int i = 0; i < loadedFields.length; ++i) {
0509: loadedFields[i] = true;
0510: }
0511: myPC.jdoReplaceFlags();
0512:
0513: // Populate all fields that have "value-strategy" and are not datastore populated
0514: populateStrategyFields();
0515:
0516: // Set the identity
0517: setIdentity();
0518:
0519: // for non transactional read, tx might be not active
0520: // TODO add verification if is non transactional read = true
0521: if (myOM.getTransaction().isActive()) {
0522: myOM.enlistInTransaction(this );
0523: }
0524: }
0525:
0526: /**
0527: * Initialises the StateManager to manage a PersistenceCapable object in detached state.
0528: * @param pc the detach object.
0529: * @param id the identity of the object.
0530: * @param version the detached version
0531: * @since 1.1
0532: */
0533: public void initialiseForDetached(Object pc, Object id,
0534: Object version) {
0535: this .myID = id;
0536: this .myPC = (PersistenceCapable) pc;
0537: setVersion(version);
0538:
0539: // This lifecycle state is not always correct. It is certainly "detached"
0540: // but we dont know if it is CLEAN or DIRTY. We need this setting here since all objects
0541: // have a lifecycle state and other methods e.g isPersistent() depend on it.
0542: this .myLC = myOM.getOMFContext().getApiAdapter()
0543: .getLifeCycleState(LifeCycleState.DETACHED_CLEAN);
0544:
0545: this .myPC.jdoReplaceFlags();
0546: myPC.jdoReplaceStateManager(this );
0547: }
0548:
0549: /**
0550: * Initialises the StateManager to manage a PersistenceCapable object that is not persistent but is
0551: * about to be deleted.
0552: * @param pc the object to delete
0553: * @since 1.2
0554: */
0555: public void initialiseForPNewToBeDeleted(Object pc) {
0556: this .myID = null;
0557: this .myPC = (PersistenceCapable) pc;
0558: this .myLC = myOM.getOMFContext().getApiAdapter()
0559: .getLifeCycleState(LifeCycleState.P_NEW);
0560: for (int i = 0; i < loadedFields.length; ++i) // Mark all fields as loaded
0561: {
0562: loadedFields[i] = true;
0563: }
0564: myPC.jdoReplaceStateManager(this );
0565: }
0566:
0567: /**
0568: * Initialise to create a StateManager for a PersistenceCapable object, assigning the specified id to the object.
0569: * This is used when getting objects out of the L2 Cache, where they have no StateManager assigned, and returning
0570: * them as associated with a particular PM.
0571: * @param pc The PersistenceCapable object
0572: * @param id Id to assign to the PersistenceCapable object
0573: * @param loaded The list of loaded fields (when put in the cache)
0574: * @param pcClass Class of the object that this will manage the state for
0575: */
0576: public void initialiseForCachedPC(Object pc, Object id,
0577: boolean loaded[], Class pcClass) {
0578: // Create a new copy of the input object type, performing the majority of the initialisation
0579: initialiseForHollow(id, null, pcClass);
0580:
0581: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
0582: LifeCycleState.P_CLEAN);
0583: jdoDfgFlags = PersistenceCapable.READ_OK;
0584:
0585: // Synchronise the L2 cached object while we grab its fields.
0586: synchronized (pc) {
0587: ((PersistenceCapable) pc).jdoReplaceStateManager(this );
0588:
0589: int[] fieldsToLoad = getFlagsSetTo(loaded,
0590: getAllFieldNumbers(), true);
0591: if (fieldsToLoad != null) {
0592: // Copy the fields from the input object to our copy that will be returned to the user
0593: myPC.jdoCopyFields(pc, fieldsToLoad);
0594: }
0595:
0596: // Reinstate the original loadedFields list
0597: for (int i = 0; i < loadedFields.length; i++) {
0598: loadedFields[i] = loaded[i];
0599: }
0600:
0601: // Mark all relationships as not yet loaded since we can't maintain those whilst cached.
0602: for (int i = 0; i < cmd
0603: .getPersistenceCapableMemberPositions().length; i++) {
0604: loadedFields[cmd.getPersistenceCapableMemberPositions()[i]] = false;
0605: }
0606:
0607: int[] secondClassMutableFieldNumbers = getSecondClassMutableFieldNumbers();
0608: for (int i = 0; i < secondClassMutableFieldNumbers.length; i++) {
0609: loadedFields[secondClassMutableFieldNumbers[i]] = false;
0610: }
0611:
0612: // Disconnect the input object
0613: disconnectClone((PersistenceCapable) pc);
0614: }
0615:
0616: if (isFetchPlanLoaded()) {
0617: // Should we call postLoad when getting the object out of the L2 cache ? Seems incorrect IMHO
0618: postLoad();
0619: }
0620: }
0621:
0622: /**
0623: * Look to the database to determine which class this object is. This parameter is a hint. Set false, if it's
0624: * already determined the correct pcClass for this pc "object" in a certain
0625: * level in the hierarchy. Set to true and it will look to the database.
0626: * @param fv the initial field values of the object.
0627: */
0628: public void checkInheritance(FieldValues fv) {
0629: // Inheritance case, check the level of the instance
0630: ClassLoaderResolver clr = myOM.getClassLoaderResolver();
0631: String className = getStoreManager().getClassNameForObjectID(
0632: myID, clr, myOM);
0633: if (className == null) {
0634: // className is null when id class exists, and object has been validated and doesn't exist.
0635: throw new JPOXObjectNotFoundException(LOCALISER
0636: .msg("026013"), myID);
0637: } else if (!cmd.getFullClassName().equals(className)) {
0638: Class pcClass;
0639: try {
0640: //load the class and make sure the class is initialized
0641: pcClass = getObjectManager().getClassLoaderResolver()
0642: .classForName(className,
0643: myID.getClass().getClassLoader(), true);
0644: cmd = myOM.getMetaDataManager().getMetaDataForClass(
0645: pcClass,
0646: getObjectManager().getClassLoaderResolver());
0647: } catch (ClassNotResolvedException e) {
0648: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("026014",
0649: myID));
0650: throw new JPOXUserException(LOCALISER.msg("026014",
0651: myID), e);
0652: }
0653: if (cmd == null) {
0654: throw new JPOXUserException(LOCALISER.msg("026012",
0655: pcClass)).setFatal();
0656: }
0657: if (cmd.getIdentityType() != IdentityType.APPLICATION) {
0658: throw new JPOXUserException(
0659: "This method should only be used for objects using application identity.")
0660: .setFatal();
0661: }
0662: myFP = myOM.getFetchPlan().manageFetchPlanForClass(cmd);
0663:
0664: initialiseFieldInformation();
0665:
0666: // Create new PC at right inheritance level
0667: myPC = HELPER.newInstance(pcClass, this );
0668: if (myPC == null) {
0669: throw new JPOXUserException(LOCALISER.msg("026018", cmd
0670: .getFullClassName())).setFatal();
0671: }
0672:
0673: // Note that this will mean the fields are loaded twice (loaded earlier in this method)
0674: // and also that postLoad will be called twice
0675: loadFieldValues(fv);
0676:
0677: // Create the id for the new PC
0678: myID = myPC.jdoNewObjectIdInstance();
0679: if (!cmd.usesSingleFieldIdentityClass()) {
0680: myPC.jdoCopyKeyFieldsToObjectId(myID);
0681: }
0682: }
0683: }
0684:
0685: /**
0686: * Convenience method to populate all fields in the PC object that have "value-strategy" specified
0687: * and that aren't datastore attributed. This applies not just to PK fields (where it is most
0688: * useful to use value-strategy) but also to any other field. Fields are populated only if they are null
0689: * This is called once on a PC object, when makePersistent is called.
0690: */
0691: private void populateStrategyFields() {
0692: int totalFieldCount = cmd.getNoOfInheritedManagedMembers()
0693: + cmd.getNoOfManagedMembers();
0694: DatastoreClass table = null;
0695: if (!cmd.isEmbeddedOnly()
0696: && getStoreManager().usesDatastoreClass()) {
0697: table = getStoreManager().getDatastoreClass(
0698: cmd.getFullClassName(),
0699: myOM.getClassLoaderResolver());
0700: }
0701:
0702: for (int fieldNumber = 0; fieldNumber < totalFieldCount; fieldNumber++) {
0703: AbstractMemberMetaData fmd = cmd
0704: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
0705: IdentityStrategy strategy = fmd.getValueStrategy();
0706:
0707: // Check for the strategy, and if it is a datastore attributed strategy
0708: if (strategy != null
0709: && !getStoreManager()
0710: .isStrategyDatastoreAttributed(strategy,
0711: false)) {
0712: // Assign the strategy value where required.
0713: // Default JDO2 behaviour is to always provide a strategy value when it is marked as using a strategy
0714: boolean applyStrategy = true;
0715: if (!fmd.getType().isPrimitive()
0716: && strategy != null
0717: && fmd.hasExtension("strategy-when-notnull")
0718: && fmd.getValueForExtension(
0719: "strategy-when-notnull")
0720: .equalsIgnoreCase("false")
0721: && this .provideField(fieldNumber) != null) {
0722: // JPOX allows an extension to only provide a value-strategy value where the field is null at persistence.
0723: applyStrategy = false;
0724: }
0725:
0726: if (applyStrategy) {
0727: // Apply a strategy value for this field
0728: DatastoreClass fieldTable = null;
0729: if (getStoreManager().usesDatastoreClass()) {
0730: fieldTable = table
0731: .getBaseDatastoreClassWithField(fmd);
0732: }
0733: Object obj = getStoreManager().getStrategyValue(
0734: myOM, fieldTable, cmd, fieldNumber);
0735: this .replaceField(fieldNumber, obj, true);
0736: }
0737: }
0738: }
0739: }
0740:
0741: /**
0742: * Convenience method to load the passed field values.
0743: * Loads the fields using any required fetch plan and calls jdoPostLoad() as appropriate.
0744: * @param fv Field Values to load (including any fetch plan to use when loading)
0745: */
0746: public void loadFieldValues(FieldValues fv) {
0747: // Fetch the required fields using any defined fetch plan
0748: FetchPlan.FetchPlanForClass origFetchPlan = myFP;
0749: FetchPlan loadFetchPlan = fv.getFetchPlanForLoading();
0750: if (loadFetchPlan != null) {
0751: myFP = loadFetchPlan.manageFetchPlanForClass(cmd);
0752: }
0753:
0754: boolean callPostLoad = myFP
0755: .isToCallPostLoadFetchPlan(this .loadedFields);
0756: if (loadedFields.length == 0) {
0757: // Class has no fields so since we are loading from scratch just call postLoad
0758: callPostLoad = true;
0759: }
0760:
0761: fv.fetchFields(this );
0762:
0763: // TODO This only applies to JDO (call postLoad even after we have the fields loaded)
0764: // We should have a JPAStateManagerImpl that has this difference.
0765: if (callPostLoad
0766: && isFetchPlanLoaded()
0767: && myOM.getOMFContext().getApiAdapter() instanceof JDOAdapter) {
0768: postLoad();
0769: }
0770:
0771: // Reinstate the original (PM) fetch plan
0772: myFP = origFetchPlan;
0773: }
0774:
0775: /**
0776: * Utility to set the identity for the PersistenceCapable object.
0777: * Will only create the id instance if it is not attributed (in some way) by the datastore.
0778: * So, for example, autoassign cases will not gain their identity here. In such cases the identity
0779: * is only created when they are inserted into the datastore.
0780: */
0781: private void setIdentity() {
0782: if (cmd.getIdentityType() == IdentityType.DATASTORE) {
0783: if (cmd.getIdentityMetaData() == null
0784: || !getStoreManager()
0785: .isStrategyDatastoreAttributed(
0786: cmd.getIdentityMetaData()
0787: .getValueStrategy(), true)) {
0788: myID = getStoreManager().newObjectID(myOM,
0789: cmd.getFullClassName(), myPC);
0790: }
0791: } else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
0792: boolean isObjectIDDatastoreAttributed = false;
0793: int totalFieldCount = cmd.getNoOfInheritedManagedMembers()
0794: + cmd.getNoOfManagedMembers();
0795: for (int fieldNumber = 0; fieldNumber < totalFieldCount; fieldNumber++) {
0796: AbstractMemberMetaData fmd = cmd
0797: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
0798: if (fmd.isPrimaryKey()) {
0799: if (getStoreManager()
0800: .isStrategyDatastoreAttributed(
0801: fmd.getValueStrategy(), false)) {
0802: isObjectIDDatastoreAttributed = true;
0803: break;
0804: } else if (cmd.usesSingleFieldIdentityClass()) {
0805: if (this .provideField(fieldNumber) == null) {
0806: // SingleFieldIdentity field has not had its value set (by user, or by value-strategy)
0807: throw new JPOXUserException(LOCALISER.msg(
0808: "026017", cmd.getFullClassName(),
0809: fmd.getName())).setFatal();
0810: }
0811: }
0812: }
0813: }
0814:
0815: if (!isObjectIDDatastoreAttributed) {
0816: // Not generating the identity in the datastore so set it now
0817: myID = getStoreManager().newObjectID(myOM,
0818: cmd.getFullClassName(), myPC);
0819: }
0820: }
0821:
0822: if (myInternalID != myID && myID != null) {
0823: // Update the id with the PM if it is changing
0824: myOM.replaceObjectId(myPC, myInternalID, myID);
0825: }
0826: }
0827:
0828: /**
0829: * Accessor for a L2-Cacheable form of this PersistenceCapable object.
0830: * This currently marks all non-relation fields as cacheable, and hence all relations will not be
0831: * stored in the L2 cached object. See CORE-3215.
0832: * @return The L2 cacheable object
0833: */
0834: public CachedPC getL2CacheableObject() {
0835: PersistenceCapable pcCopy = myPC.jdoNewInstance(this , myPC
0836: .jdoGetObjectId());
0837:
0838: // Make a copy of the field values from the original object - basic fields + PC fields + SCO fields (omit SCO containers)
0839: int[] allFieldNumbers = getAllFieldNumbers();
0840: boolean[] l2loadedFields = new boolean[allFieldNumbers.length];
0841:
0842: // Pass 1 to find the number of cacheable fields TODO Embody this in AbstractMemberMetaData
0843: ClassLoaderResolver clr = getObjectManager()
0844: .getClassLoaderResolver();
0845: int numCacheableFields = 0;
0846: for (int i = 0; i < allFieldNumbers.length; i++) {
0847: AbstractMemberMetaData fmd = cmd
0848: .getMetaDataForManagedMemberAtAbsolutePosition(i);
0849: if (fmd.getRelationType(clr) == Relation.NONE) {
0850: numCacheableFields++;
0851: }
0852: }
0853: int[] cacheableFieldNumbers = new int[numCacheableFields];
0854:
0855: // Pass 2 to set up the loaded fields info for all cacheable fields
0856: int cacheableNum = 0;
0857: for (int i = 0; i < allFieldNumbers.length; i++) {
0858: boolean cacheable = true;
0859: AbstractMemberMetaData fmd = cmd
0860: .getMetaDataForManagedMemberAtAbsolutePosition(i);
0861: if (fmd.getRelationType(clr) != Relation.NONE) {
0862: cacheable = false;
0863: }
0864: if (cacheable) {
0865: cacheableFieldNumbers[cacheableNum++] = allFieldNumbers[i];
0866: l2loadedFields[i] = (cacheable && loadedFields[i]);
0867: } else {
0868: l2loadedFields[i] = false;
0869: }
0870: }
0871: if (cacheableFieldNumbers != null
0872: && cacheableFieldNumbers.length > 0) {
0873: pcCopy.jdoCopyFields(myPC, cacheableFieldNumbers);
0874: }
0875:
0876: // Reset jdoFlags in the copy to PersistenceCapable.READ_WRITE_OK and clear its state manager.
0877: pcCopy.jdoReplaceFlags();
0878: pcCopy.jdoReplaceStateManager(null);
0879:
0880: return new CachedPC(pcCopy, l2loadedFields);
0881: }
0882:
0883: /**
0884: * Method that replaces the PC managed by this StateManager to be the supplied object.
0885: * This happens when we want to get an object for an id and create a Hollow object, and then validate
0886: * against the datastore. This validation can pull in a new object graph from the datastore (e.g for DB4O)
0887: * @param pc The PersistenceCapable to use
0888: */
0889: public void replaceManagedPC(PersistenceCapable pc) {
0890: if (pc == null) {
0891: return;
0892: }
0893:
0894: // Swap the StateManager on the objects
0895: pc.jdoReplaceStateManager(this );
0896: myPC.jdoReplaceStateManager(null);
0897:
0898: // Swap our object
0899: myPC = pc;
0900:
0901: // Put it in the cache in case the previous object was stored
0902: myOM.putObjectIntoCache(this , true, true);
0903: }
0904:
0905: /**
0906: * Convenience method to update our object with the field values from the passed object.
0907: * Objects need to be of the same type, and the other object should not have a StateManager.
0908: * @param pc The object that we should copy fields from
0909: */
0910: public void copyFieldsFromObject(PersistenceCapable pc,
0911: int[] fieldNumbers) {
0912: if (pc == null) {
0913: return;
0914: }
0915: if (!pc.getClass().getName().equals(myPC.getClass().getName())) {
0916: return;
0917: }
0918:
0919: // Assign the new object to this StateManager temporarily so that we can copy its fields
0920: pc.jdoReplaceStateManager(this );
0921: myPC.jdoCopyFields(pc, fieldNumbers);
0922:
0923: // Remove the StateManager from the other object
0924: pc.jdoReplaceStateManager(null);
0925:
0926: // Set the loaded flags now that we have copied
0927: for (int i = 0; i < fieldNumbers.length; i++) {
0928: loadedFields[fieldNumbers[i]] = true;
0929: }
0930: }
0931:
0932: /**
0933: * Utility to update our object to use a different state manager.
0934: * @param sm The new state manager.
0935: **/
0936: private void replaceStateManager(StateManager sm) {
0937: try {
0938: myPC.jdoReplaceStateManager(sm);
0939: } catch (SecurityException e) {
0940: throw new JDOFatalUserException(LOCALISER.msg("026000"), e);
0941: }
0942: }
0943:
0944: /**
0945: * Method to enlist the managed object in the current transaction.
0946: */
0947: public void enlistInTransaction() {
0948: if (!getObjectManager().getTransaction().isActive()) {
0949: return;
0950: }
0951: myOM.enlistInTransaction(this );
0952:
0953: if (jdoDfgFlags == PersistenceCapable.LOAD_REQUIRED
0954: && isDefaultFetchGroupLoaded()) {
0955: // All DFG fields loaded and object is transactional so it doesnt need to contact us for those fields
0956: // Note that this is the DFG and NOT the current FetchPlan since in the enhancement of classes
0957: // all DFG fields are set to check jdoFlags before relaying back to the StateManager
0958: jdoDfgFlags = PersistenceCapable.READ_OK;
0959: myPC.jdoReplaceFlags();
0960: }
0961: }
0962:
0963: /**
0964: * Method to evict the managed object from the current transaction.
0965: */
0966: public void evictFromTransaction() {
0967: myOM.evictFromTransaction(this );
0968:
0969: /*
0970: * A non-transactional object needs to contact us on any field read no
0971: * matter what fields are loaded.
0972: */
0973: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
0974: myPC.jdoReplaceFlags();
0975: }
0976:
0977: /**
0978: * Method to save all fields of the object.
0979: */
0980: public void saveFields() {
0981: savedImage = myPC.jdoNewInstance(this );
0982: savedImage.jdoCopyFields(myPC, getAllFieldNumbers());
0983: savedFlags = jdoDfgFlags;
0984: savedLoadedFields = (boolean[]) loadedFields.clone();
0985: }
0986:
0987: /**
0988: * Method to restore all fields of the object.
0989: */
0990: public void restoreFields() {
0991: if (savedImage != null) {
0992: loadedFields = savedLoadedFields;
0993: jdoDfgFlags = savedFlags;
0994: myPC.jdoReplaceFlags();
0995: myPC.jdoCopyFields(savedImage, getAllFieldNumbers());
0996:
0997: clearDirtyFlags();
0998: clearSavedFields();
0999: }
1000: }
1001:
1002: /**
1003: * Method to clear all fields of the object.
1004: */
1005: public void clearFields() {
1006: try {
1007: getCallbackHandler().preClear(myPC);
1008: } finally {
1009: clearFieldsByNumbers(getAllFieldNumbers());
1010: clearDirtyFlags();
1011:
1012: getStoreManager().notifyObjectIsOutdated(this ); // For datastores that manage the object reference
1013: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
1014: myPC.jdoReplaceFlags();
1015:
1016: getCallbackHandler().postClear(myPC);
1017: }
1018: }
1019:
1020: /**
1021: * Method to clear all fields that are not part of the primary key of the object.
1022: */
1023: public void clearNonPrimaryKeyFields() {
1024: try {
1025: getCallbackHandler().preClear(myPC);
1026: } finally {
1027: clearFieldsByNumbers(getNonPrimaryKeyFieldNumbers());
1028:
1029: clearDirtyFlags(getNonPrimaryKeyFieldNumbers());
1030:
1031: getStoreManager().notifyObjectIsOutdated(this ); // For datastores that manage the object reference
1032: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
1033: myPC.jdoReplaceFlags();
1034:
1035: getCallbackHandler().postClear(myPC);
1036: }
1037: }
1038:
1039: private void clearFieldsByNumbers(int[] fieldNumbers) {
1040: replaceFields(fieldNumbers, HOLLOWFIELDMANAGER);
1041: for (int i = 0; i < fieldNumbers.length; i++) {
1042: loadedFields[fieldNumbers[i]] = false;
1043: dirtyFields[fieldNumbers[i]] = false;
1044: }
1045: }
1046:
1047: /**
1048: * Method to clear all saved fields on the object.
1049: */
1050: public void clearSavedFields() {
1051: savedImage = null;
1052: savedFlags = 0;
1053: savedLoadedFields = null;
1054: }
1055:
1056: /**
1057: * Method to clear all loaded flags on the object.
1058: * Note that the contract of this method implies, especially for object database backends, that the memory form
1059: * of the object is outdated.
1060: * Thus, for features like implicit saving of dirty object subgraphs should be switched off for this PC, even if the
1061: * object actually looks like being dirty (because it is being changed to null values).
1062: */
1063: public void clearLoadedFlags() {
1064: getStoreManager().notifyObjectIsOutdated(this );
1065:
1066: jdoDfgFlags = PersistenceCapable.LOAD_REQUIRED;
1067: myPC.jdoReplaceFlags();
1068: clearFlags(loadedFields);
1069: }
1070:
1071: /**
1072: * Marks the given field dirty.
1073: * @param field The no of field to mark as dirty.
1074: */
1075: public void makeDirty(int field) {
1076: if (activity != ActivityState.DELETING) {
1077: // Mark dirty unless in the process of being deleted
1078: boolean wasDirty = preWriteField(field);
1079: postWriteField(wasDirty);
1080: }
1081: }
1082:
1083: /**
1084: * Mark the associated PersistenceCapable field dirty.
1085: *
1086: * @param pc the calling PersistenceCapable instance
1087: * @param fieldName the name of the field
1088: */
1089: public void makeDirty(PersistenceCapable pc, String fieldName) {
1090: if (!disconnectClone(pc)) {
1091: int fieldNumber = cmd
1092: .getAbsolutePositionOfMember(fieldName);
1093: if (fieldNumber == -1) {
1094: throw new JDOUserException(LOCALISER.msg("026002",
1095: fieldName, cmd.getFullClassName()));
1096: }
1097:
1098: makeDirty(fieldNumber);
1099: }
1100: }
1101:
1102: // -------------------------- Accessor Methods -----------------------------
1103:
1104: /**
1105: * Accessor for the PersistenceManager that owns this instance.
1106: * @param pc The PersistenceCapable instance
1107: * @return The PersistenceManager that owns this instance
1108: */
1109: public javax.jdo.PersistenceManager getPersistenceManager(
1110: PersistenceCapable pc) {
1111: //in identifying relationships, jdoCopyKeyFieldsFromId will call
1112: //this method, and at this moment, myPC in statemanager is null
1113: // Currently AbstractPersistenceManager.java putObjectInCache prevents any identifying relation object being put in L2
1114:
1115: //if not identifying relationship, do the default check of disconnectClone:
1116: //"this.disconnectClone(pc)"
1117: if (myPC != null && this .disconnectClone(pc)) {
1118: return null;
1119: } else if (myOM == null) {
1120: return null;
1121: } else {
1122: myOM.hereIsStateManager(this , myPC);
1123: return (PersistenceManager) myOM.getOwner();
1124: }
1125: }
1126:
1127: /**
1128: * Return the object representing the JDO identity of the calling instance.
1129: *
1130: * According to the JDO specification, if the JDO identity is being changed
1131: * in the current transaction, this method returns the JDO identify as of
1132: * the beginning of the transaction.
1133: *
1134: * @param pc the calling PersistenceCapable instance
1135: * @return the object representing the JDO identity of the calling instance
1136: */
1137: public Object getObjectId(PersistenceCapable pc) {
1138: if (disconnectClone(pc)) {
1139: return null;
1140: } else {
1141: return getExternalObjectId(pc);
1142: }
1143: }
1144:
1145: /**
1146: * If the id is obtained after inserting the object into the database, set
1147: * new a new id for persistent classes (for example, increment).
1148: * @param id the id received from the datastore
1149: */
1150: public void setPostStoreNewObjectId(Object id) {
1151: if (cmd.getIdentityType() == IdentityType.DATASTORE) {
1152: if (id instanceof OID) {
1153: // Provided an OID direct
1154: this .myID = id;
1155: } else {
1156: // OID "key" value provided
1157: myID = OIDFactory.getInstance(myOM, cmd
1158: .getFullClassName(), id);
1159: }
1160: } else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
1161: try {
1162: myID = null;
1163:
1164: int fieldCount = getHighestFieldNumber();
1165: for (int fieldNumber = 0; fieldNumber < fieldCount; fieldNumber++) {
1166: AbstractMemberMetaData fmd = cmd
1167: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
1168: if (fmd.isPrimaryKey()
1169: && getStoreManager()
1170: .isStrategyDatastoreAttributed(
1171: fmd.getValueStrategy(),
1172: false)) {
1173: //replace the value of the id, but before convert the value to the field type if needed
1174: replaceField(fieldNumber, TypeConversionHelper
1175: .convertTo(id, fmd.getType()), true);
1176: }
1177: }
1178: } catch (Exception e) {
1179: JPOXLogger.PERSISTENCE.error(e);
1180: } finally {
1181: myID = myOM.getApiAdapter()
1182: .getNewApplicationIdentityObjectId(getObject(),
1183: cmd);
1184: }
1185: }
1186:
1187: if (myInternalID != myID && myID != null) {
1188: // Update the id with the PM if it is changing
1189: myOM.replaceObjectId(myPC, myInternalID, myID);
1190: }
1191: }
1192:
1193: /**
1194: * Return an object id that the user can use.
1195: * @param obj the PersistenceCapable object
1196: * @return the object id
1197: */
1198: public Object getExternalObjectId(Object obj) {
1199: if (cmd.getIdentityType() == IdentityType.DATASTORE) {
1200: if (!flushing) {
1201: // Flush any datastore changes so that myID is set by the time we return
1202: if (!flushedNew
1203: && activity != ActivityState.INSERTING
1204: && activity != ActivityState.INSERTING_CALLBACKS
1205: && myLC.stateType() == LifeCycleState.P_NEW) {
1206: if (getStoreManager()
1207: .isStrategyDatastoreAttributed(
1208: cmd.getIdentityMetaData()
1209: .getValueStrategy(), true)) {
1210: flush();
1211: }
1212: }
1213: }
1214: } else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
1215: // Note that we always create a new application identity since it is mutable and we can't allow
1216: // the user to change it. The only drawback of this is that we *must* have the relevant fields
1217: // set when this method is called, so that the identity can be generated.
1218: if (!flushing) {
1219: // Flush any datastore changes so that we have all necessary fields populated
1220: // only if the datastore generates the field numbers
1221: if (!flushedNew
1222: && activity != ActivityState.INSERTING
1223: && activity != ActivityState.INSERTING_CALLBACKS
1224: && myLC.stateType() == LifeCycleState.P_NEW) {
1225: int[] pkFieldNumbers = cmd.getPKMemberPositions();
1226: for (int i = 0; i < pkFieldNumbers.length; i++) {
1227: AbstractMemberMetaData fmd = cmd
1228: .getMetaDataForManagedMemberAtAbsolutePosition(i);
1229: if (getStoreManager()
1230: .isStrategyDatastoreAttributed(
1231: fmd.getValueStrategy(), false)) {
1232: flush();
1233: break;
1234: }
1235: }
1236: }
1237: }
1238:
1239: if (cmd.usesSingleFieldIdentityClass()) {
1240: //SingleFieldIdentity classes are immutable.
1241: //Note, the instances of SingleFieldIdentity can be changed by the user using reflection,
1242: //but this is not allowed by the JDO spec
1243: return myID;
1244: }
1245: return myOM.getApiAdapter()
1246: .getNewApplicationIdentityObjectId(myPC, cmd);
1247: }
1248:
1249: return myID;
1250: }
1251:
1252: /**
1253: * Replace the current value of jdoStateManager.
1254: *
1255: * <P>This method is called by the PersistenceCapable whenever
1256: * jdoReplaceStateManager is called and there is already
1257: * an owning StateManager. This is a security precaution
1258: * to ensure that the owning StateManager is the only
1259: * source of any change to its reference in the PersistenceCapable.</p>
1260: *
1261: * @return the new value for the jdoStateManager
1262: * @param pc the calling PersistenceCapable instance
1263: * @param sm the proposed new value for the jdoStateManager
1264: */
1265: public StateManager replacingStateManager(PersistenceCapable pc,
1266: StateManager sm) {
1267: if (myLC == null) {
1268: throw new JDOFatalInternalException("Null LifeCycleState");
1269: }
1270:
1271: if (myLC.stateType() == LifeCycleState.DETACHED_CLEAN) {
1272: return sm;
1273: }
1274:
1275: if (pc == myPC) {
1276: //TODO check if we are really in transition to a transient instance
1277: if (sm == null) {
1278: return null;
1279: }
1280: if (sm == this ) {
1281: return this ;
1282: }
1283:
1284: if (this .myOM == ((AbstractStateManager) sm).myOM) {
1285: // This is a race condition when makePersistent or
1286: // makeTransactional is called on the same PC instance for the
1287: // same PM. It has been already set to this SM - just
1288: // disconnect the other one. Return this SM so it won't be
1289: // replaced.
1290: ((JDOStateManagerImpl) sm).disconnect();
1291: return this ;
1292: }
1293:
1294: if (sm != null) {
1295: throw new JDOUserException(LOCALISER.msg("026003"));
1296: }
1297: if (!disconnecting) {
1298: throw new JDOUserException(LOCALISER.msg("026004"));
1299: }
1300:
1301: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1302: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("026005",
1303: StringUtils.toJVMIDString(pc)));
1304: }
1305:
1306: return null;
1307: } else if (pc == savedImage) {
1308: return null;
1309: } else {
1310: return sm;
1311: }
1312: }
1313:
1314: /**
1315: * Return the object representing the JDO identity
1316: * of the calling instance. If the JDO identity is being changed in
1317: * the current transaction, this method returns the current identity as
1318: * changed in the transaction.
1319: *
1320: * @param pc the calling PersistenceCapable instance
1321: * @return the object representing the JDO identity of the calling instance
1322: */
1323: public Object getTransactionalObjectId(PersistenceCapable pc) {
1324: return getObjectId(pc);
1325: }
1326:
1327: // --------------------------- Load Field Methods --------------------------
1328:
1329: /**
1330: * Fetchs from the database all SCO fields that are not containers that aren't already loaded.
1331: */
1332: private void loadSCONonContainerFields() {
1333: int[] noncontainerFieldNumbers = cmd
1334: .getSCONonContainerMemberPositions();
1335: int[] fieldNumbers = getFlagsSetTo(loadedFields,
1336: noncontainerFieldNumbers, false);
1337: if (fieldNumbers != null && fieldNumbers.length > 0) {
1338: getStoreManager().fetchObject(this , fieldNumbers);
1339:
1340: // We currently dont call postLoad here since this is only called as part of attaching an object
1341: // and consequently we just read to get the current (attached) values. Could add a flag on input to allow postLoad
1342:
1343: // Update the L2 cache to have this object (more loaded fields)
1344: myOM.putObjectIntoCache(this , false, true);
1345: }
1346: }
1347:
1348: /**
1349: * Fetch the specified fields from the database.
1350: * @param fieldNumbers the numbers of the field(s) to fetch.
1351: */
1352: protected void loadSpecifiedFields(int[] fieldNumbers) {
1353: if (myOM.getApiAdapter().isDetached(myPC)) {
1354: // Nothing to do since we're detached
1355: return;
1356: }
1357:
1358: if (isEmbedded()) {
1359: // Should never happen since embedded will always retrieve all fields in one go.
1360: } else {
1361: getStoreManager().fetchObject(this , fieldNumbers);
1362: }
1363:
1364: // Update the L2 cache to have this object (more loaded fields)
1365: myOM.putObjectIntoCache(this , false, true);
1366: }
1367:
1368: /**
1369: * Convenience method to load the specified field if not loaded.
1370: * @param fieldNumber Absolute field number
1371: */
1372: public void loadField(int fieldNumber) {
1373: if (loadedFields[fieldNumber]) {
1374: // Already loaded
1375: return;
1376: }
1377: loadSpecifiedFields(new int[] { fieldNumber });
1378: }
1379:
1380: /**
1381: * Fetch from the database all fields that are not currently loaded regardless of whether
1382: * they are in the current fetch group or not. Called by lifecycle transitions.
1383: * @since 1.1
1384: */
1385: public void loadUnloadedFields() {
1386: int[] fieldNumbers = getFlagsSetTo(loadedFields,
1387: getAllFieldNumbers(), false);
1388: if (fieldNumbers != null && fieldNumbers.length > 0) {
1389: boolean callPostLoad = myFP
1390: .isToCallPostLoadFetchPlan(this .loadedFields);
1391: getStoreManager().fetchObject(this , fieldNumbers);
1392:
1393: int[] secondClassMutableFieldNumbers = getSecondClassMutableFieldNumbers();
1394:
1395: // Make sure all SCO lazy-loaded fields have been loaded
1396: for (int i = 0; i < secondClassMutableFieldNumbers.length; i++) {
1397: SingleValueFieldManager sfv = new SingleValueFieldManager();
1398: provideFields(
1399: new int[] { secondClassMutableFieldNumbers[i] },
1400: sfv);
1401: Object value = sfv.fetchObjectField(i);
1402: if (value instanceof SCOContainer) {
1403: ((SCOContainer) value).load();
1404: }
1405: }
1406:
1407: if (callPostLoad) {
1408: postLoad();
1409: }
1410:
1411: // Update the L2 cache to have this object (more loaded fields)
1412: myOM.putObjectIntoCache(this , false, true);
1413: }
1414: }
1415:
1416: boolean loadingFieldsInFetchPlan = false;
1417:
1418: /**
1419: * Method to load all unloaded fields in the FetchPlan.
1420: * Recurses through the FetchPlan objects and loads fields of sub-objects where needed.
1421: * Used as a precursor to detaching objects at commit since fields can't be loaded during
1422: * the postCommit phase when the detach actually happens.
1423: * @param state The FetchPlan state
1424: */
1425: public void loadFieldsInFetchPlan(FetchPlanState state) {
1426: if (loadingFieldsInFetchPlan) {
1427: // Already in the process of loading fields in this class so skip
1428: return;
1429: }
1430:
1431: // Load unloaded FetchPlan fields of this object
1432: loadingFieldsInFetchPlan = true;
1433: loadUnloadedFieldsInFetchPlan();
1434:
1435: // Recurse through all fields and do the same
1436: int[] fieldNumbers = getFlagsSetTo(loadedFields,
1437: getAllFieldNumbers(), true);
1438: if (fieldNumbers != null && fieldNumbers.length > 0) {
1439: // TODO Fix this to just access the fields of the FieldManager yet this actually does a replaceField
1440: replaceFields(fieldNumbers, new LoadFieldManager(this ,
1441: getSecondClassMutableFields(), myFP, state));
1442: }
1443:
1444: loadingFieldsInFetchPlan = false;
1445: }
1446:
1447: /**
1448: * Fetchs from the database all fields that are not currently loaded and that are in the current
1449: * fetch group. Called by lifecycle transitions.
1450: * @since 1.1
1451: */
1452: public void loadUnloadedFieldsInFetchPlan() {
1453: int[] fieldNumbers = getFlagsSetTo(loadedFields, myFP
1454: .getFieldsInActualFetchPlan(), false);
1455: if (fieldNumbers != null && fieldNumbers.length > 0) {
1456: boolean callPostLoad = myFP
1457: .isToCallPostLoadFetchPlan(this .loadedFields);
1458: getStoreManager().fetchObject(this , fieldNumbers);
1459: if (callPostLoad) {
1460: postLoad();
1461: }
1462:
1463: // Update the L2 cache to have this object (more loaded fields)
1464: myOM.putObjectIntoCache(this , false, true);
1465: }
1466: }
1467:
1468: /**
1469: * Fetchs from the database all fields in the actual fetch plan.
1470: * Called by life-cycle transitions.
1471: * @since 1.1
1472: */
1473: public void loadUnloadedFieldsOfClassInFetchPlan(FetchPlan fetchPlan) {
1474: FetchPlanForClass fpc = fetchPlan
1475: .manageFetchPlanForClass(this .cmd);
1476: int[] fieldNumbers = getFlagsSetTo(loadedFields, fpc
1477: .getFieldsInActualFetchPlan(), false);
1478: if (fieldNumbers != null && fieldNumbers.length > 0) {
1479: boolean callPostLoad = fpc
1480: .isToCallPostLoadFetchPlan(this .loadedFields);
1481: getStoreManager().fetchObject(this , fieldNumbers);
1482: if (callPostLoad) {
1483: postLoad();
1484: }
1485:
1486: // Update the L2 cache to have this object (more loaded fields)
1487: myOM.putObjectIntoCache(this , false, true);
1488: }
1489: }
1490:
1491: /**
1492: * Convenience method to unload a field/property.
1493: * @param fieldName Name of the field/property
1494: * @throws JPOXUserException if the object managed by this StateManager is embedded
1495: */
1496: public void unloadField(String fieldName) {
1497: if (pcObjectType == PC) {
1498: // Mark as not loaded
1499: AbstractMemberMetaData mmd = getClassMetaData()
1500: .getMetaDataForMember(fieldName);
1501: loadedFields[mmd.getAbsoluteFieldNumber()] = false;
1502: } else {
1503: throw new JPOXUserException(
1504: "Cannot unload field/property of embedded object");
1505: }
1506: }
1507:
1508: /**
1509: * Refreshes from the database all fields in fetch plan.
1510: * Called by life-cycle transitions when the object undergoes a "transitionRefresh".
1511: */
1512: public void refreshFieldsInFetchPlan() {
1513: int[] fieldNumbers = myFP.getFieldsInActualFetchPlan();
1514: if (fieldNumbers != null && fieldNumbers.length > 0) {
1515: clearDirtyFlags(fieldNumbers);
1516: clearFlags(loadedFields, fieldNumbers);
1517:
1518: boolean callPostLoad = myFP
1519: .isToCallPostLoadFetchPlan(this .loadedFields);
1520:
1521: // Refresh the fetch plan fields in this object
1522: setTransactionalVersion(null); // Make sure that the version is reset upon fetch
1523: getStoreManager().fetchObject(this , fieldNumbers);
1524:
1525: if (cmd.hasRelations(myOM.getClassLoaderResolver())) {
1526: // Check for cascade refreshes to related objects
1527: for (int i = 0; i < fieldNumbers.length; i++) {
1528: AbstractMemberMetaData fmd = cmd
1529: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]);
1530: int relationType = fmd.getRelationType(myOM
1531: .getClassLoaderResolver());
1532: if (relationType != Relation.NONE
1533: && fmd.isCascadeRefresh()) {
1534: // Need to refresh the related field object(s)
1535: Object value = provideField(fieldNumbers[i]);
1536: if (value != null) {
1537: if (value instanceof Collection) {
1538: // Refresh any PC elements in the collection
1539: // TODO This should replace the SCO wrapper with a new one, or reload the wrapper
1540: SCOUtils
1541: .refreshFetchPlanFieldsForCollection(
1542: this ,
1543: ((Collection) value)
1544: .toArray());
1545: } else if (value instanceof Map) {
1546: // Refresh any PC keys/values in the map
1547: // TODO This should replace the SCO wrapper with a new one, or reload the wrapper
1548: SCOUtils.refreshFetchPlanFieldsForMap(
1549: this , ((Map) value).entrySet());
1550: } else if (value instanceof PersistenceCapable) {
1551: // Refresh any PC fields
1552: myOM.refreshObject(value);
1553: }
1554: }
1555: }
1556: }
1557: }
1558:
1559: if (callPostLoad) {
1560: postLoad();
1561: }
1562:
1563: getCallbackHandler().postRefresh(myPC);
1564:
1565: // Update the L2 cache to have this object (more loaded fields)
1566: myOM.putObjectIntoCache(this , false, true);
1567: }
1568: }
1569:
1570: /**
1571: * Refreshes from the database all fields currently loaded.
1572: * Called by life-cycle transitions when making transactional or reading fields.
1573: */
1574: public void refreshLoadedFields() {
1575: int[] fieldNumbers = getFlagsSetTo(loadedFields, myFP
1576: .getFieldsInActualFetchPlan(), true);
1577:
1578: if (fieldNumbers != null && fieldNumbers.length > 0) {
1579: clearDirtyFlags();
1580: clearFlags(loadedFields);
1581:
1582: boolean callPostLoad = myFP
1583: .isToCallPostLoadFetchPlan(this .loadedFields);
1584: getStoreManager().fetchObject(this , fieldNumbers);
1585: if (callPostLoad) {
1586: postLoad();
1587: }
1588:
1589: // Update the L2 cache to have this object (more loaded fields)
1590: myOM.putObjectIntoCache(this , false, true);
1591: }
1592: }
1593:
1594: /**
1595: * Method that will unload all fields that are not in the FetchPlan.
1596: * This is typically for use when the instance is being refreshed.
1597: * @since 1.2
1598: */
1599: public void unloadNonFetchPlanFields() {
1600: int[] fpFieldNumbers = myFP.getFieldsInActualFetchPlan();
1601: int[] nonfpFieldNumbers = null;
1602: if (fpFieldNumbers == null || fpFieldNumbers.length == 0) {
1603: nonfpFieldNumbers = getAllFieldNumbers();
1604: } else {
1605: int fieldCount = getHighestFieldNumber();
1606: if (fieldCount == fpFieldNumbers.length) {
1607: // No fields that arent in FetchPlan
1608: return;
1609: }
1610:
1611: nonfpFieldNumbers = new int[fieldCount
1612: - fpFieldNumbers.length];
1613: int currentFPFieldIndex = 0;
1614: int j = 0;
1615: for (int i = 0; i < fieldCount; i++) {
1616: if (currentFPFieldIndex >= fpFieldNumbers.length) {
1617: // Past end of FetchPlan fields
1618: nonfpFieldNumbers[j++] = i;
1619: } else {
1620: if (fpFieldNumbers[currentFPFieldIndex] == i) {
1621: // FetchPlan field so move to next
1622: currentFPFieldIndex++;
1623: } else {
1624: nonfpFieldNumbers[j++] = i;
1625: }
1626: }
1627: }
1628: }
1629:
1630: // Mark all non-FetchPlan fields as unloaded
1631: for (int i = 0; i < nonfpFieldNumbers.length; i++) {
1632: loadedFields[nonfpFieldNumbers[i]] = false;
1633: }
1634: }
1635:
1636: /**
1637: * Convenience method to load a field from the datastore.
1638: * Used in attaching fields and checking their old values (so we dont
1639: * want any postLoad method being called).
1640: * TODO Merge this with one of the loadXXXFields methods.
1641: * @param fieldNumber The field number.
1642: */
1643: public void loadFieldFromDatastore(int fieldNumber) {
1644: getStoreManager().fetchObject(this , new int[] { fieldNumber });
1645: }
1646:
1647: /**
1648: * Return true if the field is cached in the calling instance.
1649: * <P>
1650: * In the JPOX implementation of this method, isLoaded() will always
1651: * return true. If the field is not loaded, it will be loaded as a side
1652: * effect of the call to this method. If it is in the default fetch group,
1653: * the default fetch group, including this field, will be loaded.
1654: *
1655: * @param pc the calling PersistenceCapable instance
1656: * @param field the absolute field number
1657: * @return always returns true (this implementation)
1658: */
1659: public boolean isLoaded(PersistenceCapable pc, int field) {
1660: try {
1661: if (disconnectClone(pc)) {
1662: return true;
1663: } else {
1664: boolean checkRead = true;
1665: boolean beingDeleted = false;
1666: if ((myLC.isDeleted && myOM.isFlushing())
1667: || activity == ActivityState.DELETING) {
1668: // Bypass "read-field" check when deleting, or when marked for deletion and flushing
1669: checkRead = false;
1670: beingDeleted = true;
1671: }
1672: if (checkRead) {
1673: transitionReadField(loadedFields[field]);
1674: }
1675:
1676: if (!loadedFields[field]) {
1677: // Field not loaded, so load it
1678: if (pcObjectType != PC) {
1679: // Embedded object so we assume that all was loaded before (when it was read)
1680: return true;
1681: }
1682:
1683: if (!beingDeleted
1684: && myFP.isFieldInActualFetchPlan(field)) {
1685: // Load rest of FetchPlan if this is part of it (and not in the process of deletion)
1686: loadUnloadedFieldsInFetchPlan();
1687: } else {
1688: // Just load this field
1689: loadSpecifiedFields(new int[] { field });
1690: }
1691: }
1692:
1693: return true;
1694: }
1695: } catch (JPOXException jpe) {
1696: JPOXLogger.PERSISTENCE.warn(
1697: "Exception thrown by StateManager.isLoaded", jpe);
1698: // Convert into a JDOException since this is called from a user update of a field
1699: throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
1700: }
1701: }
1702:
1703: /**
1704: * Returns whether all fields are loaded.
1705: * @return Returns true if all fields are loaded.
1706: */
1707: public boolean getAllFieldsLoaded() {
1708: for (int i = 0; i < loadedFields.length; i++) {
1709: if (!loadedFields[i]) {
1710: return false;
1711: }
1712: }
1713: return true;
1714: }
1715:
1716: /**
1717: * Returns whether the object being managed is dirty.
1718: * @return whether at least one field is dirty by checking the dirty flag.
1719: */
1720: public boolean isDirty() {
1721: return dirty;
1722: }
1723:
1724: /**
1725: * Creates a copy of the {@link #dirtyFields} bitmap.
1726: * @return a copy of the {@link #dirtyFields} bitmap.
1727: */
1728: public boolean[] getDirtyFields() {
1729: boolean[] copy = new boolean[dirtyFields.length];
1730: System.arraycopy(dirtyFields, 0, copy, 0, dirtyFields.length);
1731: return copy;
1732: }
1733:
1734: // ---------------------- Field Accessor/Mutator Methods -------------------
1735:
1736: /**
1737: * Called by the various setXXXField() methods once it's established that
1738: * the field really deserves to be written.
1739: * Makes the state transition and replaces the field value in the PC
1740: * instance.
1741: */
1742: private void writeField(int field, Object newValue) {
1743: replaceField(field, newValue, true);
1744: }
1745:
1746: /**
1747: * This method is called by the associated PersistenceCapable when the
1748: * corresponding mutator method (setXXX()) is called on the
1749: * PersistenceCapable.
1750: *
1751: * @param pc the calling PersistenceCapable instance
1752: * @param field the field number
1753: * @param currentValue the current value of the field
1754: * @param newValue the new value for the field
1755: */
1756: public void setBooleanField(PersistenceCapable pc, int field,
1757: boolean currentValue, boolean newValue) {
1758: if (pc != myPC) {
1759: replaceField(pc, field, newValue ? Boolean.TRUE
1760: : Boolean.FALSE, true);
1761: disconnectClone(pc);
1762: } else if (myLC != null) {
1763: if (!loadedFields[field] || currentValue != newValue) {
1764: boolean wasDirty = preWriteField(field);
1765: writeField(field, newValue ? Boolean.TRUE
1766: : Boolean.FALSE);
1767: postWriteField(wasDirty);
1768: }
1769: } else {
1770: replaceField(field,
1771: newValue ? Boolean.TRUE : Boolean.FALSE, true);
1772: }
1773: }
1774:
1775: /**
1776: * This method is called by the associated PersistenceCapable when the
1777: * corresponding mutator method (setXXX()) is called on the
1778: * PersistenceCapable.
1779: *
1780: * @param pc the calling PersistenceCapable instance
1781: * @param field the field number
1782: * @param currentValue the current value of the field
1783: * @param newValue the new value for the field
1784: */
1785: public void setByteField(PersistenceCapable pc, int field,
1786: byte currentValue, byte newValue) {
1787: if (pc != myPC) {
1788: replaceField(pc, field, new Byte(newValue), true);
1789: disconnectClone(pc);
1790: } else if (myLC != null) {
1791: if (!loadedFields[field] || currentValue != newValue) {
1792: boolean wasDirty = preWriteField(field);
1793: writeField(field, new Byte(newValue));
1794: postWriteField(wasDirty);
1795: }
1796: } else {
1797: replaceField(field, new Byte(newValue), true);
1798: }
1799: }
1800:
1801: /**
1802: * This method is called by the associated PersistenceCapable when the
1803: * corresponding mutator method (setXXX()) is called on the
1804: * PersistenceCapable.
1805: *
1806: * @param pc the calling PersistenceCapable instance
1807: * @param field the field number
1808: * @param currentValue the current value of the field
1809: * @param newValue the new value for the field
1810: */
1811: public void setCharField(PersistenceCapable pc, int field,
1812: char currentValue, char newValue) {
1813: if (pc != myPC) {
1814: replaceField(pc, field, new Character(newValue), true);
1815: disconnectClone(pc);
1816: } else if (myLC != null) {
1817: if (!loadedFields[field] || currentValue != newValue) {
1818: boolean wasDirty = preWriteField(field);
1819: writeField(field, new Character(newValue));
1820: postWriteField(wasDirty);
1821: }
1822: } else {
1823: replaceField(field, new Character(newValue), true);
1824: }
1825: }
1826:
1827: /**
1828: * This method is called by the associated PersistenceCapable when the
1829: * corresponding mutator method (setXXX()) is called on the
1830: * PersistenceCapable.
1831: *
1832: * @param pc the calling PersistenceCapable instance
1833: * @param field the field number
1834: * @param currentValue the current value of the field
1835: * @param newValue the new value for the field
1836: */
1837: public void setDoubleField(PersistenceCapable pc, int field,
1838: double currentValue, double newValue) {
1839: if (pc != myPC) {
1840: replaceField(pc, field, new Double(newValue), true);
1841: disconnectClone(pc);
1842: } else if (myLC != null) {
1843: if (!loadedFields[field] || currentValue != newValue) {
1844: boolean wasDirty = preWriteField(field);
1845: writeField(field, new Double(newValue));
1846: postWriteField(wasDirty);
1847: }
1848: } else {
1849: replaceField(field, new Double(newValue), true);
1850: }
1851: }
1852:
1853: /**
1854: * This method is called by the associated PersistenceCapable when the
1855: * corresponding mutator method (setXXX()) is called on the
1856: * PersistenceCapable.
1857: *
1858: * @param pc the calling PersistenceCapable instance
1859: * @param field the field number
1860: * @param currentValue the current value of the field
1861: * @param newValue the new value for the field
1862: */
1863: public void setFloatField(PersistenceCapable pc, int field,
1864: float currentValue, float newValue) {
1865: if (pc != myPC) {
1866: replaceField(pc, field, new Float(newValue), true);
1867: disconnectClone(pc);
1868: } else if (myLC != null) {
1869: if (!loadedFields[field] || currentValue != newValue) {
1870: boolean wasDirty = preWriteField(field);
1871: writeField(field, new Float(newValue));
1872: postWriteField(wasDirty);
1873: }
1874: } else {
1875: replaceField(field, new Float(newValue), true);
1876: }
1877: }
1878:
1879: /**
1880: * This method is called by the associated PersistenceCapable when the
1881: * corresponding mutator method (setXXX()) is called on the
1882: * PersistenceCapable.
1883: *
1884: * @param pc the calling PersistenceCapable instance
1885: * @param field the field number
1886: * @param currentValue the current value of the field
1887: * @param newValue the new value for the field
1888: */
1889: public void setIntField(PersistenceCapable pc, int field,
1890: int currentValue, int newValue) {
1891: if (pc != myPC) {
1892: replaceField(pc, field, new Integer(newValue), true);
1893: disconnectClone(pc);
1894: } else if (myLC != null) {
1895: if (!loadedFields[field] || currentValue != newValue) {
1896: boolean wasDirty = preWriteField(field);
1897: writeField(field, new Integer(newValue));
1898: postWriteField(wasDirty);
1899: }
1900: } else {
1901: replaceField(field, new Integer(newValue), true);
1902: }
1903: }
1904:
1905: /**
1906: * This method is called by the associated PersistenceCapable when the
1907: * corresponding mutator method (setXXX()) is called on the
1908: * PersistenceCapable.
1909: *
1910: * @param pc the calling PersistenceCapable instance
1911: * @param field the field number
1912: * @param currentValue the current value of the field
1913: * @param newValue the new value for the field
1914: */
1915: public void setLongField(PersistenceCapable pc, int field,
1916: long currentValue, long newValue) {
1917: if (pc != myPC) {
1918: replaceField(pc, field, new Long(newValue), true);
1919: disconnectClone(pc);
1920: } else if (myLC != null) {
1921: if (!loadedFields[field] || currentValue != newValue) {
1922: boolean wasDirty = preWriteField(field);
1923: writeField(field, new Long(newValue));
1924: postWriteField(wasDirty);
1925: }
1926: } else {
1927: replaceField(field, new Long(newValue), true);
1928: }
1929: }
1930:
1931: /**
1932: * This method is called by the associated PersistenceCapable when the
1933: * corresponding mutator method (setXXX()) is called on the
1934: * PersistenceCapable.
1935: *
1936: * @param pc the calling PersistenceCapable instance
1937: * @param field the field number
1938: * @param currentValue the current value of the field
1939: * @param newValue the new value for the field
1940: */
1941: public void setShortField(PersistenceCapable pc, int field,
1942: short currentValue, short newValue) {
1943: if (pc != myPC) {
1944: replaceField(pc, field, new Short(newValue), true);
1945: disconnectClone(pc);
1946: } else if (myLC != null) {
1947: if (!loadedFields[field] || currentValue != newValue) {
1948: boolean wasDirty = preWriteField(field);
1949: writeField(field, new Short(newValue));
1950: postWriteField(wasDirty);
1951: }
1952: } else {
1953: replaceField(field, new Short(newValue), true);
1954: }
1955: }
1956:
1957: /**
1958: * This method is called by the associated PersistenceCapable when the
1959: * corresponding mutator method (setXXX()) is called on the
1960: * PersistenceCapable.
1961: *
1962: * @param pc the calling PersistenceCapable instance
1963: * @param field the field number
1964: * @param currentValue the current value of the field
1965: * @param newValue the new value for the field
1966: */
1967: public void setStringField(PersistenceCapable pc, int field,
1968: String currentValue, String newValue) {
1969: if (pc != myPC) {
1970: replaceField(pc, field, newValue, true);
1971: disconnectClone(pc);
1972: } else if (myLC != null) {
1973: if (!loadedFields[field] || !equals(currentValue, newValue)) {
1974: boolean wasDirty = preWriteField(field);
1975: writeField(field, newValue);
1976: postWriteField(wasDirty);
1977: }
1978: } else {
1979: replaceField(field, newValue, true);
1980: }
1981: }
1982:
1983: /**
1984: * This method is called by the associated PersistenceCapable when the
1985: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1986: * @param pc the calling PersistenceCapable instance
1987: * @param field the field number
1988: * @param currentValue the current value of the field
1989: * @param newValue the new value for the field
1990: */
1991: public void setObjectField(PersistenceCapable pc, int field,
1992: Object currentValue, Object newValue) {
1993: if (currentValue != null && currentValue != newValue
1994: && currentValue instanceof PersistenceCapable) {
1995: // Where the object is embedded, remove the owner from its old value since it is no longer managed by this StateManager
1996: JDOStateManagerImpl currentSM = (JDOStateManagerImpl) myOM
1997: .findStateManager(currentValue);
1998: if (currentSM != null && currentSM.isEmbedded()) {
1999: currentSM.removeEmbeddedOwner(this , field);
2000: }
2001: }
2002:
2003: if (pc != myPC) {
2004: // Clone
2005: replaceField(pc, field, newValue, true);
2006: disconnectClone(pc);
2007: } else if (myLC != null) {
2008: boolean loadedOldValue = false;
2009: Object oldValue = currentValue;
2010: AbstractMemberMetaData fmd = cmd
2011: .getMetaDataForManagedMemberAtAbsolutePosition(field);
2012: ClassLoaderResolver clr = myOM.getClassLoaderResolver();
2013: int relationType = fmd.getRelationType(clr);
2014: if (!loadedFields[field] && currentValue == null) {
2015: // Updating value of a field that isnt currently loaded
2016: if (myOM.getOMFContext().getPersistenceConfiguration()
2017: .getManageRelationships()
2018: && (relationType == Relation.ONE_TO_ONE_BI || relationType == Relation.MANY_TO_ONE_BI)) {
2019: // Managed relation field, so load old value
2020: loadField(field);
2021: loadedOldValue = true;
2022: oldValue = provideField(field);
2023: }
2024: if (relationType != Relation.NONE && fmd.isDependent()
2025: && newValue == null) {
2026: // Field being nulled and is dependent so load the existing value so it can be deleted
2027: loadField(field);
2028: loadedOldValue = true;
2029: oldValue = provideField(field);
2030: }
2031: // TODO When field has relation consider loading it always for managed relations
2032: }
2033:
2034: // Check equality of old and new values
2035: boolean equal = false;
2036: if (oldValue == null && newValue == null) {
2037: equal = true;
2038: } else if (oldValue != null && newValue != null) {
2039: if (oldValue instanceof PersistenceCapable) {
2040: // PC object field so compare object equality
2041: // See JDO2 [5.4] "The JDO implementation must not use the application's hashCode and equals methods
2042: // from the persistence-capable classes except as needed to implement the Collections Framework"
2043: if (oldValue == newValue) {
2044: equal = true;
2045: }
2046: } else {
2047: // Non-PC object field so compare using equals()
2048: if (oldValue.equals(newValue)) {
2049: equal = true;
2050: }
2051: }
2052: }
2053:
2054: // Update the field
2055: if (!loadedFields[field] || !equal || fmd.hasArray()) {
2056: // Either field isn't loaded, or has changed, or is an array.
2057: // We include arrays here since we have no way of knowing if the array element has changed
2058: // except if the user sets the array field. See JDO2 [6.3] that the application should
2059: // replace the value with its current value.
2060: boolean wasDirty = preWriteField(field);
2061:
2062: if (oldValue instanceof SCO) {
2063: if (oldValue instanceof SCOContainer) {
2064: // Make sure container values are loaded
2065: ((SCOContainer) oldValue).load();
2066: }
2067: ((SCO) oldValue).unsetOwner();
2068: }
2069: if (newValue instanceof SCO) {
2070: SCO sco = (SCO) newValue;
2071: Object owner = sco.getOwner();
2072: if (owner != null) {
2073: throw new JDOUserException(LOCALISER.msg(
2074: "026007", sco.getFieldName(), owner));
2075: }
2076: }
2077:
2078: writeField(field, newValue);
2079: postWriteField(wasDirty);
2080: } else if (loadedOldValue) {
2081: // We've updated the value with the old value (when retrieving it above), so put the new value back again
2082: boolean wasDirty = preWriteField(field);
2083: writeField(field, newValue);
2084: postWriteField(wasDirty);
2085: }
2086:
2087: if (!equal
2088: && myOM.getOMFContext()
2089: .getPersistenceConfiguration()
2090: .getManageRelationships()) {
2091: // Managed Relations : register updated bidir fields for later processing
2092: if (relationType == Relation.ONE_TO_ONE_BI
2093: || relationType == Relation.MANY_TO_ONE_BI
2094: || relationType == Relation.ONE_TO_MANY_BI
2095: || relationType == Relation.MANY_TO_MANY_BI) {
2096: // Managed Relationships - add the field to be managed so we can analyse its value at flush
2097: if (relationManager == null) {
2098: relationManager = new RelationshipManager(this );
2099: }
2100: relationManager.relationChange(field, oldValue,
2101: newValue);
2102: }
2103: }
2104:
2105: if (fmd.isDependent() && oldValue != null
2106: && newValue == null
2107: && oldValue instanceof PersistenceCapable) {
2108: // PC field being nulled, so delete previous PC value
2109: // TODO Avoid this flush since it will screw up optimistic txn handling
2110: flush(); // Make sure that any null reference is set first (to avoid any FK constraint failures)
2111:
2112: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("026026",
2113: oldValue, fmd.getFullFieldName()));
2114: myOM.deleteObjectInternal(oldValue);
2115: }
2116: } else {
2117: replaceField(field, newValue, true);
2118: }
2119: }
2120:
2121: /**
2122: * Accessor for the relationship manager, and create if not existing and we are managing relations.
2123: * @return The Relationship manager
2124: */
2125: public RelationshipManager getRelationshipManager() {
2126: if (relationManager == null
2127: && myOM.getOMFContext().getPersistenceConfiguration()
2128: .getManageRelationships()
2129: && !myOM.isManagingRelations()) {
2130: relationManager = new RelationshipManager(this );
2131: }
2132: return relationManager;
2133: }
2134:
2135: /**
2136: * Method to check all updated managed relations in this object.
2137: */
2138: public void checkManagedRelations() {
2139: if (myLC == null || myLC.isDeleted()) {
2140: // Has been deleted so ignore all relationship changes
2141: return;
2142: }
2143: if (relationManager == null) {
2144: return;
2145: }
2146: relationManager.checkConsistency();
2147: }
2148:
2149: /**
2150: * Method to process all updated managed relations in this object.
2151: */
2152: public void processManagedRelations() {
2153: if (myLC == null || myLC.isDeleted()) {
2154: // Has been deleted so ignore all relationship changes
2155: return;
2156: }
2157: if (relationManager == null) {
2158: return;
2159: }
2160: relationManager.process();
2161: }
2162:
2163: /**
2164: * Method to clear all initial values for bidirectional fields involved in "managed relationships".
2165: */
2166: public void clearManagedRelations() {
2167: if (relationManager != null) {
2168: relationManager.clearFields();
2169: relationManager = null;
2170: }
2171: }
2172:
2173: /**
2174: * Convenience method to change the value of a field that is assumed loaded.
2175: * Will mark the object/field as dirty if it isnt previously. If the object is deleted then does nothing.
2176: * Only for use in management of relations.
2177: * @param fieldNumber Number of field
2178: * @param newValue The new value
2179: */
2180: public void replaceFieldValue(int fieldNumber, Object newValue) {
2181: if (myLC.isDeleted()) {
2182: // Object is deleted so do nothing
2183: return;
2184: }
2185: boolean currentWasDirty = preWriteField(fieldNumber);
2186: writeField(fieldNumber, newValue);
2187: postWriteField(currentWasDirty);
2188: }
2189:
2190: /**
2191: * The StateManager uses this method to supply the value of jdoFlags to the
2192: * associated PersistenceCapable instance.
2193: * @param pc the calling PersistenceCapable instance
2194: * @return the value of jdoFlags to be stored in the PersistenceCapable instance
2195: */
2196: public byte replacingFlags(PersistenceCapable pc) {
2197: // If this is a clone, return READ_WRITE_OK.
2198: if (pc != myPC) {
2199: return PersistenceCapable.READ_WRITE_OK;
2200: } else {
2201: return jdoDfgFlags;
2202: }
2203: }
2204:
2205: /**
2206: * Method to return the current value of a particular field.
2207: * @param fieldNumber Number of field
2208: * @return The value of the field
2209: */
2210: public Object provideField(int fieldNumber) {
2211: return provideField(myPC, fieldNumber);
2212: }
2213:
2214: /**
2215: * Method to change the value of a particular field.
2216: * @param fieldNumber Number of field
2217: * @param value New value
2218: * @param makeDirty Whether to make the field dirty when replacing it
2219: */
2220: public void replaceField(int fieldNumber, Object value,
2221: boolean makeDirty) {
2222: replaceField(myPC, fieldNumber, value, makeDirty);
2223: }
2224:
2225: /**
2226: * Method to retrieve the value of a field from the PC object.
2227: * Assumes that it is loaded.
2228: * @param pc The PC object
2229: * @param fieldNumber Number of field
2230: * @return The value of the field
2231: */
2232: private Object provideField(PersistenceCapable pc, int fieldNumber) {
2233: Object obj;
2234: synchronized (currFMmonitor) {
2235: FieldManager prevFM = currFM;
2236: currFM = new SingleValueFieldManager();
2237: try {
2238: pc.jdoProvideField(fieldNumber);
2239: obj = currFM.fetchObjectField(fieldNumber);
2240: } finally {
2241: currFM = prevFM;
2242: }
2243: }
2244:
2245: return obj;
2246: }
2247:
2248: /**
2249: * Method to change the value of a field in the PC object.
2250: * @param pc The PC object
2251: * @param fieldNumber Number of field
2252: * @param value The new value of the field
2253: * @param makeDirty Whether to make the field dirty while replacing its value (in embedded owners)
2254: */
2255: private void replaceField(PersistenceCapable pc, int fieldNumber,
2256: Object value, boolean makeDirty) {
2257: if (embeddedOwners != null) {
2258: // Notify any owners that embed this object that it has just changed
2259: // We do this before we actually change the object so we can compare with the old value
2260: Iterator ownerIter = embeddedOwners.iterator();
2261: while (ownerIter.hasNext()) {
2262: EmbeddedOwnerRelation owner = (EmbeddedOwnerRelation) ownerIter
2263: .next();
2264: JDOStateManagerImpl ownerSM = (JDOStateManagerImpl) owner.sm;
2265:
2266: if (ownerSM == null || ownerSM.cmd == null) {
2267: //for some reason these are null... raised when running JPA TCK
2268: continue;
2269: }
2270: AbstractMemberMetaData ownerFmd = ownerSM.cmd
2271: .getMetaDataForManagedMemberAtAbsolutePosition(owner.fieldNumber);
2272: if (ownerFmd.getCollection() != null) {
2273: // PC Object embedded in collection
2274: Object ownerField = ownerSM
2275: .provideField(owner.fieldNumber);
2276: if (ownerField instanceof SCOCollection) {
2277: ((SCOCollection) ownerField)
2278: .updateEmbeddedElement(myPC,
2279: fieldNumber, value);
2280: }
2281: } else if (ownerFmd.getMap() != null) {
2282: // PC Object embedded in map
2283: Object ownerField = ownerSM
2284: .provideField(owner.fieldNumber);
2285: if (ownerField instanceof SCOMap) {
2286: if (pcObjectType == EMBEDDED_MAP_KEY_PC) {
2287: ((SCOMap) ownerField).updateEmbeddedKey(
2288: myPC, fieldNumber, value);
2289: }
2290: if (pcObjectType == EMBEDDED_MAP_VALUE_PC) {
2291: ((SCOMap) ownerField).updateEmbeddedValue(
2292: myPC, fieldNumber, value);
2293: }
2294: }
2295: } else {
2296: // PC Object embedded in PC object
2297: if ((ownerSM.miscFlags & MISC_UPDATING_EMBEDDING_FIELDS_WITH_OWNER) == 0) {
2298: // Update the owner when one of our fields have changed, EXCEPT when they have just
2299: // notified us of our owner field!
2300: if (ownerSM.isEmbedded()) {
2301: // Owner is embedded so just update its field
2302: ownerSM.replaceField(owner.fieldNumber, pc,
2303: makeDirty);
2304: } else {
2305: if (makeDirty) {
2306: // Owner is not embedded so mark the field as dirty too
2307: boolean wasDirty = ownerSM
2308: .preWriteField(owner.fieldNumber);
2309: ownerSM.replaceField(owner.fieldNumber,
2310: pc, true);
2311: ownerSM.postWriteField(wasDirty);
2312: } else {
2313: ownerSM.replaceField(owner.fieldNumber,
2314: pc, false);
2315: }
2316: }
2317: }
2318: }
2319: }
2320: }
2321:
2322: // Update the field in our PC object
2323: synchronized (currFMmonitor) {
2324: FieldManager prevFM = currFM;
2325: currFM = new SingleValueFieldManager();
2326:
2327: try {
2328: currFM.storeObjectField(fieldNumber, value);
2329: pc.jdoReplaceField(fieldNumber);
2330: } finally {
2331: currFM = prevFM;
2332: }
2333: }
2334: }
2335:
2336: /**
2337: * Called from the StoreManager after StoreManager.update() is called to
2338: * obtain updated values from the PersistenceCapable associated with this
2339: * StateManager.
2340: *
2341: * @param fieldNumbers An array of field numbers to be updated by the Store
2342: * @param fm The updated values are stored in this object. This object is only valid
2343: * for the duration of this call.
2344: */
2345: public void provideFields(int fieldNumbers[], FieldManager fm) {
2346: synchronized (currFMmonitor) {
2347: FieldManager prevFM = currFM;
2348: currFM = fm;
2349:
2350: try {
2351: // This will respond by calling this.providedXXXFields() with the value of the field
2352: myPC.jdoProvideFields(fieldNumbers);
2353: } finally {
2354: currFM = prevFM;
2355: }
2356: }
2357: }
2358:
2359: /**
2360: * Called from the StoreManager to refresh data in the PersistenceCapable
2361: * object associated with this StateManager.
2362: * @param fieldNumbers An array of field numbers to be refreshed by the Store
2363: * @param fm The updated values are stored in this object. This object is only valid
2364: * for the duration of this call.
2365: * @param replaceWhenDirty Whether to replace the fields when they are dirty here
2366: */
2367: public void replaceFields(int fieldNumbers[], FieldManager fm,
2368: boolean replaceWhenDirty) {
2369: synchronized (currFMmonitor) {
2370: FieldManager prevFM = currFM;
2371: currFM = fm;
2372:
2373: try {
2374: int[] fieldsToReplace = fieldNumbers;
2375: if (!replaceWhenDirty) {
2376: int numberToReplace = fieldNumbers.length;
2377: for (int i = 0; i < fieldNumbers.length; i++) {
2378: if (dirtyFields[fieldNumbers[i]]) {
2379: numberToReplace--;
2380: }
2381: }
2382: if (numberToReplace > 0
2383: && numberToReplace != fieldNumbers.length) {
2384: fieldsToReplace = new int[numberToReplace];
2385: int n = 0;
2386: for (int i = 0; i < fieldNumbers.length; i++) {
2387: if (!dirtyFields[fieldNumbers[i]]) {
2388: fieldsToReplace[n++] = fieldNumbers[i];
2389: }
2390: }
2391: } else if (numberToReplace == 0) {
2392: fieldsToReplace = null;
2393: }
2394: }
2395:
2396: if (fieldsToReplace != null) {
2397: myPC.jdoReplaceFields(fieldsToReplace);
2398: }
2399: } finally {
2400: currFM = prevFM;
2401: }
2402: }
2403: }
2404:
2405: /**
2406: * Called from the StoreManager to refresh data in the PersistenceCapable
2407: * object associated with this StateManager.
2408: * @param fieldNumbers An array of field numbers to be refreshed by the Store
2409: * @param fm The updated values are stored in this object. This object is only valid
2410: * for the duration of this call.
2411: */
2412: public void replaceFields(int fieldNumbers[], FieldManager fm) {
2413: replaceFields(fieldNumbers, fm, true);
2414: }
2415:
2416: /**
2417: * Called from the StoreManager to refresh data in the PersistenceCapable
2418: * object associated with this StateManager. Only not loaded fields are refreshed
2419: *
2420: * @param fieldNumbers An array of field numbers to be refreshed by the Store
2421: * @param fm The updated values are stored in this object. This object is only valid
2422: * for the duration of this call.
2423: */
2424: public void replaceNonLoadedFields(int fieldNumbers[],
2425: FieldManager fm) {
2426: synchronized (currFMmonitor) {
2427: FieldManager prevFM = currFM;
2428: currFM = fm;
2429:
2430: boolean callPostLoad = myFP
2431: .isToCallPostLoadFetchPlan(this .loadedFields);
2432: try {
2433: int[] fieldsToReplace = getFlagsSetTo(loadedFields,
2434: fieldNumbers, false);
2435: if (fieldsToReplace != null
2436: && fieldsToReplace.length > 0) {
2437: myPC.jdoReplaceFields(fieldsToReplace);
2438: }
2439: } finally {
2440: currFM = prevFM;
2441: }
2442: if (callPostLoad && isFetchPlanLoaded()) {
2443: // The fetch plan is now loaded so fire off any necessary post load
2444: postLoad();
2445: }
2446: }
2447: }
2448:
2449: /**
2450: * Method to register an owner StateManager with this embedded/serialised object.
2451: * @param ownerSM The owning State Manager.
2452: * @param ownerFieldNumber The field number in the owner that the embedded/serialised object is stored as
2453: */
2454: public void addEmbeddedOwner(org.jpox.StateManager ownerSM,
2455: int ownerFieldNumber) {
2456: if (ownerSM == null) {
2457: return;
2458: }
2459:
2460: if (embeddedOwners == null) {
2461: embeddedOwners = new ArrayList();
2462: }
2463: embeddedOwners.add(new EmbeddedOwnerRelation(ownerSM,
2464: ownerFieldNumber));
2465: }
2466:
2467: /**
2468: * Method to remove an owner StateManager from this embedded/serialised objects owners list.
2469: * @param ownerSM The owner to remove
2470: * @param ownerFieldNumber The field in the owner where this object is stored
2471: */
2472: public void removeEmbeddedOwner(StateManager ownerSM,
2473: int ownerFieldNumber) {
2474: if (embeddedOwners != null) {
2475: Iterator iter = embeddedOwners.iterator();
2476: while (iter.hasNext()) {
2477: EmbeddedOwnerRelation relation = (EmbeddedOwnerRelation) iter
2478: .next();
2479: if (relation.sm == ownerSM
2480: && relation.fieldNumber == ownerFieldNumber) {
2481: iter.remove();
2482: break;
2483: }
2484: }
2485: }
2486: }
2487:
2488: /**
2489: * Accessor for the owning StateManagers for the managed object when stored embedded.
2490: * Should really only have a single owner but users could, in principle, assign it to multiple.
2491: * @return StateManagers owning this embedded object.
2492: */
2493: public org.jpox.StateManager[] getEmbeddedOwners() {
2494: if (embeddedOwners == null) {
2495: return null;
2496: }
2497: org.jpox.StateManager[] owners = new org.jpox.StateManager[embeddedOwners
2498: .size()];
2499: for (int i = 0; i < owners.length; i++) {
2500: EmbeddedOwnerRelation relation = (EmbeddedOwnerRelation) embeddedOwners
2501: .get(i);
2502: owners[i] = relation.sm;
2503: }
2504: return owners;
2505: }
2506:
2507: /**
2508: * Wrapper class storing the owning state manager, and the field of the
2509: * PC managed by the owning state manager where this object is embedded/serialised.
2510: */
2511: private class EmbeddedOwnerRelation {
2512: private org.jpox.StateManager sm;
2513: private int fieldNumber;
2514:
2515: /**
2516: *
2517: * @param ownerSM the owner StateManager
2518: * @param ownerFieldNumber the absolute owner field number
2519: */
2520: public EmbeddedOwnerRelation(org.jpox.StateManager ownerSM,
2521: int ownerFieldNumber) {
2522: this .sm = ownerSM;
2523: this .fieldNumber = ownerFieldNumber;
2524: }
2525: }
2526:
2527: /**
2528: * Method to replace all loaded SCO fields with wrappers.
2529: * If the loaded field already uses a SCO wrapper nothing happens to that field.
2530: */
2531: public void replaceAllLoadedSCOFieldsWithWrappers() {
2532: boolean[] scoMutableFieldFlags = cmd.getSCOMutableMemberFlags();
2533: for (int i = 0; i < scoMutableFieldFlags.length; i++) {
2534: if (scoMutableFieldFlags[i] && loadedFields[i]) {
2535: Object value = provideField(i);
2536: if (!(value instanceof SCO)) {
2537: wrapSCOField(i, value, false, false, true);
2538: }
2539: }
2540: }
2541: }
2542:
2543: /**
2544: * Method to replace all loaded SCO fields that have wrappers with their value.
2545: * If the loaded field doesnt have a SCO wrapper nothing happens to that field.
2546: */
2547: public void replaceAllLoadedSCOFieldsWithValues() {
2548: boolean[] scoMutableFieldFlags = cmd.getSCOMutableMemberFlags();
2549: for (int i = 0; i < scoMutableFieldFlags.length; i++) {
2550: if (scoMutableFieldFlags[i] && loadedFields[i]) {
2551: Object value = provideField(i);
2552: if (value instanceof SCO) {
2553: unwrapSCOField(i, value, true);
2554: }
2555: }
2556: }
2557: }
2558:
2559: /**
2560: * Method to unwrap a SCO field (if it is wrapped currently).
2561: * If the field is not a SCO field will just return the value.
2562: * If "replaceFieldIfChanged" is set, we replace the value in the object with the unwrapped value.
2563: * @param fieldNumber The field number
2564: * @param value The value for the field
2565: * @param replaceFieldIfChanged Whether to replace the field value in the object if unwrapping the value
2566: * @return The unwrapped field value
2567: */
2568: public Object unwrapSCOField(int fieldNumber, Object value,
2569: boolean replaceFieldIfChanged) {
2570: if (value == null) {
2571: return value;
2572: }
2573: if (getSecondClassMutableFields()[fieldNumber]
2574: && value instanceof SCO) {
2575: SCO sco = (SCO) value;
2576:
2577: // Not a SCO wrapper, or is a SCO wrapper but not owned by this object
2578: Object unwrappedValue = sco.getValue();
2579: if (replaceFieldIfChanged) {
2580: AbstractMemberMetaData fmd = cmd
2581: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
2582: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2583: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
2584: "026030", StringUtils.toJVMIDString(myPC),
2585: myID, fmd.getName()));
2586: }
2587: replaceField(fieldNumber, unwrappedValue, false);
2588: }
2589: return unwrappedValue;
2590: }
2591: return value;
2592: }
2593:
2594: /**
2595: * Method to create a new SCO wrapper for the specified field.
2596: * If the field is not a SCO field will just return the value.
2597: * @param fieldNumber The field number
2598: * @param value The value to initialise the wrapper with (if any)
2599: * @param forInsert Whether the creation of any wrapper should insert this value into the datastore
2600: * @param forUpdate Whether the creation of any wrapper should update the datastore with this value
2601: * @param replaceFieldIfChanged Whether to replace the field in the object if wrapping the value
2602: * @return The wrapper (or original value if not wrappable)
2603: */
2604: public Object wrapSCOField(int fieldNumber, Object value,
2605: boolean forInsert, boolean forUpdate,
2606: boolean replaceFieldIfChanged) {
2607: if (value == null) {
2608: // We don't wrap null objects currently
2609: return value;
2610: }
2611:
2612: if (value instanceof PersistenceCapable) {
2613: // Special case of SCO that we should split into a separate method for clarity, nothing to do with wrapping
2614: AbstractMemberMetaData fmd = cmd
2615: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
2616: if (fmd.getEmbeddedMetaData() != null
2617: && fmd.getEmbeddedMetaData().getOwnerMember() != null) {
2618: // Embedded field, so assign the embedded/serialised object "owner-field" if specified
2619: JDOStateManagerImpl subSM = (JDOStateManagerImpl) myOM
2620: .findStateManager(value);
2621: int ownerAbsFieldNum = subSM.cmd
2622: .getAbsolutePositionOfMember(fmd
2623: .getEmbeddedMetaData().getOwnerMember());
2624: if (ownerAbsFieldNum >= 0) {
2625: miscFlags |= MISC_UPDATING_EMBEDDING_FIELDS_WITH_OWNER;
2626: subSM.replaceField(ownerAbsFieldNum, myPC, true);
2627: miscFlags &= ~MISC_UPDATING_EMBEDDING_FIELDS_WITH_OWNER;
2628: }
2629: }
2630: }
2631:
2632: if (getSecondClassMutableFields()[fieldNumber]) {
2633: if (!(value instanceof SCO)
2634: || myPC != ((SCO) value).getOwner()) {
2635: // Not a SCO wrapper, or is a SCO wrapper but not owned by this object
2636: AbstractMemberMetaData fmd = cmd
2637: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
2638: if (replaceFieldIfChanged) {
2639: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2640: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
2641: "026029", StringUtils
2642: .toJVMIDString(myPC), myID, fmd
2643: .getName()));
2644: }
2645: }
2646: return SCOUtils.newSCOInstance(this , fmd,
2647: fmd.getType(), (value != null ? value
2648: .getClass() : null), value, forInsert,
2649: forUpdate, replaceFieldIfChanged);
2650: }
2651: }
2652:
2653: return value;
2654: }
2655:
2656: /**
2657: * Method to change the object state to read-field.
2658: * @param isLoaded if the field was previously loaded
2659: */
2660: private void transitionReadField(boolean isLoaded) {
2661: if (myLC == null) {
2662: return;
2663: }
2664: synchronized (myPC) {
2665: preStateChange();
2666: try {
2667: myLC = myLC.transitionReadField(this , isLoaded);
2668: } finally {
2669: postStateChange();
2670: }
2671: }
2672: }
2673:
2674: /**
2675: * Method to change the object state to write-field.
2676: */
2677: private void transitionWriteField() {
2678: synchronized (myPC) {
2679: preStateChange();
2680: try {
2681: myLC = myLC.transitionWriteField(this );
2682: } finally {
2683: postStateChange();
2684: }
2685: }
2686: }
2687:
2688: // ------------------------- Lifecycle Methods -----------------------------
2689:
2690: /**
2691: * Method to mark an object for reachability.
2692: * Provides the basis for "persistence-by-reachability", but run at commit time only.
2693: * The reachability algorithm is also run at makePersistent, but directly via InsertRequest.
2694: * @param reachables List of object ids currently logged as reachable
2695: */
2696: public void runReachability(Set reachables) {
2697: if (reachables == null) {
2698: return;
2699: }
2700: if (!reachables.contains(getInternalObjectId())) {
2701: // Make sure all changes are persisted
2702: flush();
2703:
2704: if (isDeleted(myPC)) {
2705: // This object is deleted so nothing further will be reachable
2706: return;
2707: }
2708:
2709: // This object was enlisted so make sure all of its fields are loaded before continuing
2710: if (getObjectManager().isEnlistedInTransaction(
2711: getInternalObjectId())) {
2712: loadUnloadedFields();
2713: }
2714:
2715: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
2716: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007000",
2717: StringUtils.toJVMIDString(myPC),
2718: getObjectId(myPC), myLC));
2719: }
2720: // Add this object id since not yet reached
2721: reachables.add(getInternalObjectId());
2722:
2723: // Go through all (loaded FetchPlan) fields for reachability using ReachabilityFieldManager
2724: int[] loadedFieldNumbers = getFlagsSetTo(loadedFields,
2725: getAllFieldNumbers(), true);
2726: if (loadedFieldNumbers != null
2727: && loadedFieldNumbers.length > 0) {
2728: provideFields(loadedFieldNumbers,
2729: new ReachabilityFieldManager(this , reachables));
2730: }
2731: }
2732: }
2733:
2734: /**
2735: * Method to make the object persistent.
2736: */
2737: public void makePersistent() {
2738: if (myLC.isDeleted()
2739: && !myOM.getOMFContext().getApiAdapter()
2740: .allowPersistOfDeletedObject()) {
2741: // API doesnt allow repersist of deleted objects
2742: return;
2743: }
2744:
2745: if (dirty && !myLC.isDeleted && myLC.isTransactional
2746: && myOM.isDelayDatastoreOperationsEnabled()) {
2747: // Already provisionally persistent, but delaying til commit so just re-run reachability
2748: // to bring in any new objects that are now reachable
2749: provideFields(cmd.getAllMemberPositions(),
2750: new PersistFieldManager(this , false));
2751: return;
2752: }
2753:
2754: getCallbackHandler().prePersist(myPC);
2755:
2756: if (flushedNew) {
2757: // With CompoundIdentity bidir relations when the SM is created for this object ("initialiseForPersistentNew") the persist
2758: // of the PK PC fields can cause the flush of this object, and so it is already persisted by the time we ge here
2759: registerTransactional();
2760: return;
2761: }
2762:
2763: if (cmd.isEmbeddedOnly()) {
2764: // Cant persist an object of this type since can only be embedded
2765: return;
2766: }
2767:
2768: // If this is an embedded/serialised object becoming persistent in its own right, assign an identity.
2769: if (myID == null) {
2770: setIdentity();
2771: }
2772:
2773: dirty = true;
2774:
2775: if (myOM.isDelayDatastoreOperationsEnabled()) {
2776: // Delaying datastore flush til later
2777: myOM.markDirty(this , false);
2778: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2779: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("026028",
2780: StringUtils.toJVMIDString(myPC)));
2781: }
2782: registerTransactional();
2783:
2784: if (myLC.isTransactional && myLC.isDeleted()) {
2785: // Re-persist of a previously deleted object
2786: myLC = myLC.transitionMakePersistent(this );
2787: }
2788:
2789: // Run reachability on all fields of this PC - JDO2 [12.6.7]
2790: provideFields(cmd.getAllMemberPositions(),
2791: new PersistFieldManager(this , false));
2792: } else {
2793: // Persist the object and all reachables
2794: internalMakePersistent();
2795: registerTransactional();
2796: }
2797: }
2798:
2799: /**
2800: * Method to persist the object to the datastore.
2801: */
2802: private void internalMakePersistent() {
2803: activity = ActivityState.INSERTING;
2804: boolean[] tmpDirtyFields = dirtyFields;
2805: try {
2806: getCallbackHandler().preStore(myPC); // This comes after setting the INSERTING flag so we know we are inserting it now
2807:
2808: //in InstanceLifecycleEvents this object could get dirty if a field is changed in preStore or
2809: //postCreate, we clear dirty flags to make sure this object will not be flushed again
2810: clearDirtyFlags();
2811:
2812: getStoreManager().insertObject(this );
2813: flushedNew = true;
2814:
2815: getCallbackHandler().postStore(myPC);
2816: } catch (NotYetFlushedException ex) {
2817: //happening on cyclic relationships
2818: //if not yet flushed error, we rollback dirty fields, so we can retry inserting
2819: dirtyFields = tmpDirtyFields;
2820: myOM.markDirty(this , false);
2821: dirty = true;
2822: //we throw exception, so the owning relationship will mark it's foreign key to update later
2823: throw ex;
2824: } finally {
2825: activity = ActivityState.NONE;
2826: }
2827: }
2828:
2829: /**
2830: * Tests whether this object is being inserted.
2831: * @return true if this instance is inserting.
2832: */
2833: public boolean isInserting() {
2834: return (activity == ActivityState.INSERTING);
2835: }
2836:
2837: /**
2838: * Tests whether this object is being deleted.
2839: * @return true if this instance is being deleted.
2840: */
2841: public boolean isDeleting() {
2842: return (activity == ActivityState.DELETING);
2843: }
2844:
2845: /**
2846: * Accessor for whether the instance is newly persistent yet hasnt yet been flushed to the datastore.
2847: * @return Whether not yet flushed to the datastore
2848: */
2849: public boolean isWaitingToBeFlushedToDatastore() {
2850: // Return true if object is new and not yet flushed to datastore
2851: return myLC.stateType() == LifeCycleState.P_NEW && !flushedNew;
2852: }
2853:
2854: /**
2855: * Returns whether the field of this object is inserted in the datastore.
2856: * Only applies during the makePersistent process.
2857: * @param fieldNumber Number of the field (-1 => datastore identity, if used)
2858: * @return Whether it is inserted to the level of this field
2859: */
2860: public boolean isInserted(int fieldNumber) {
2861: if (!isInserting()) {
2862: return true;
2863: }
2864: if (latestInsertedDatastoreClass == null) {
2865: // Not yet inserted anything
2866: return false;
2867: }
2868:
2869: AbstractMemberMetaData fmd = cmd
2870: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
2871: if (fmd == null) {
2872: // Specified field doesnt exist for this object type!
2873: return false;
2874: }
2875: return latestInsertedDatastoreClass.managesClass(fmd
2876: .getClassName());
2877: }
2878:
2879: /**
2880: * Returns whether this object is inserted in the datastore far enough to be considered
2881: * of the supplied type. For example if we have base class A, B extends A and this object
2882: * is a B, and we pass in A here then this returns whether the A part of the object is now
2883: * inserted.
2884: * @param className Name of class that we want to check the insertion level for.
2885: * @return Whether the object is inserted in the datastore to this level
2886: */
2887: public boolean isInserted(String className) {
2888: if (!isInserting()) {
2889: // TODO Cater for calling this when it is already passed the makePersistent process
2890: return false;
2891: }
2892:
2893: if (latestInsertedDatastoreClass != null) {
2894: DatastoreClass datastoreCls = latestInsertedDatastoreClass;
2895: while (datastoreCls != null) {
2896: if (datastoreCls.managesClass(className)) {
2897: return true; // This datastore class manages the specified class so it is inserted
2898: }
2899: datastoreCls = datastoreCls.getSuperDatastoreClass();
2900: }
2901: }
2902:
2903: return false;
2904: }
2905:
2906: /**
2907: * Latest datastore class used during insertion. Only used during makePersistent.
2908: * Only the most recent datastore class is stored since with inheritance the tables are inserted
2909: * in order, starting at the root.
2910: */
2911: private DatastoreClass latestInsertedDatastoreClass = null;
2912:
2913: /**
2914: * Change the activity state.
2915: * @param activityState the new state
2916: * @param table Datastore class that has just been processed
2917: */
2918: public void changeActivityState(ActivityState activityState,
2919: DatastoreClass table) {
2920: if (activityState == ActivityState.INSERTING_CALLBACKS) {
2921: latestInsertedDatastoreClass = table;
2922: if (table.managesClass(cmd.getFullClassName())) {
2923: // Full insertion has just completed so notify all interested StateManagers
2924: activity = activityState;
2925: if (insertionNotifyList != null) {
2926: synchronized (insertionNotifyList) {
2927: for (int i = 0; i < insertionNotifyList.size(); i++) {
2928: JDOStateManagerImpl notifySM = (JDOStateManagerImpl) insertionNotifyList
2929: .get(i);
2930: notifySM.insertionCompleted(this );
2931: }
2932: insertionNotifyList.clear();
2933: insertionNotifyList = null;
2934: }
2935: }
2936: } else {
2937: // Not yet inserted fully so keep going
2938: }
2939: } else {
2940: activity = activityState;
2941: }
2942: }
2943:
2944: /**
2945: * Method to add a notifier that we must contact when we have finished our insertion.
2946: * @param sm the state manager
2947: * @param activityState the ActivityState (unused)
2948: */
2949: public void addInsertionNotifier(StateManager sm,
2950: ActivityState activityState) {
2951: // TODO Use the second param to add the StateManager to other lists for other events
2952: if (insertionNotifyList == null) {
2953: insertionNotifyList = new ArrayList();
2954: }
2955: insertionNotifyList.add(sm);
2956: }
2957:
2958: /**
2959: * Marks the given field as being required to be updated when the specified object has been inserted.
2960: * @param pc The Persistable object
2961: * @param fieldNumber Number of the field.
2962: */
2963: public void updateFieldAfterInsert(Object pc, int fieldNumber) {
2964: JDOStateManagerImpl otherSM = (JDOStateManagerImpl) myOM
2965: .findStateManager(pc);
2966:
2967: // Register the other SM to update us when it is inserted
2968: otherSM.addInsertionNotifier(this ,
2969: ActivityState.INSERTING_CALLBACKS);
2970:
2971: // Register that we should update this field when the other SM informs us
2972: if (fieldsToBeUpdatedAfterObjectInsertion == null) {
2973: fieldsToBeUpdatedAfterObjectInsertion = new HashMap();
2974: }
2975: FieldContainer cont = (FieldContainer) fieldsToBeUpdatedAfterObjectInsertion
2976: .get(otherSM);
2977: if (cont == null) {
2978: cont = new FieldContainer(fieldNumber);
2979: } else {
2980: cont.set(fieldNumber);
2981: }
2982: fieldsToBeUpdatedAfterObjectInsertion.put(otherSM, cont);
2983:
2984: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2985: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("026021", cmd
2986: .getMetaDataForManagedMemberAtAbsolutePosition(
2987: fieldNumber).getFullFieldName(),
2988: StringUtils.toJVMIDString(myPC)));
2989: }
2990: }
2991:
2992: /**
2993: * Method called by another StateManager when this object has registered that it needs to know
2994: * when the other object has been inserted.
2995: * @param sm State Manager of the other object that has just been inserted
2996: */
2997: void insertionCompleted(JDOStateManagerImpl sm) {
2998: if (fieldsToBeUpdatedAfterObjectInsertion == null) {
2999: return;
3000: }
3001:
3002: // Go through our insertion update list and mark all required fields as dirty
3003: FieldContainer fldCont = (FieldContainer) fieldsToBeUpdatedAfterObjectInsertion
3004: .get(sm);
3005: if (fldCont != null) {
3006: dirty = true;
3007: int[] fieldsToUpdate = fldCont.getFields();
3008: for (int i = 0; i < fieldsToUpdate.length; i++) {
3009: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
3010: JPOXLogger.PERSISTENCE
3011: .debug(LOCALISER
3012: .msg(
3013: "026022",
3014: cmd
3015: .getMetaDataForManagedMemberAtAbsolutePosition(
3016: fieldsToUpdate[i])
3017: .getFullFieldName(),
3018: myID));
3019: }
3020: dirtyFields[fieldsToUpdate[i]] = true;
3021: }
3022: fieldsToBeUpdatedAfterObjectInsertion.remove(sm);
3023: if (fieldsToBeUpdatedAfterObjectInsertion.size() == 0) {
3024: fieldsToBeUpdatedAfterObjectInsertion = null;
3025: }
3026:
3027: // Perform our update
3028: flush();
3029: }
3030: }
3031:
3032: /**
3033: * Method to set the value for an external field stored against this object
3034: * @param mapping The mapping for the (external) field
3035: * @param value The value that this field has
3036: */
3037: public void setExternalFieldValueForMapping(
3038: JavaTypeMapping mapping, Object value) {
3039: if (externalFieldValuesByMapping == null) {
3040: externalFieldValuesByMapping = new HashMap();
3041: }
3042: externalFieldValuesByMapping.put(mapping, value);
3043: }
3044:
3045: /**
3046: * Accessor for the value of an external field.
3047: * This is used when inserting this object so that we can insert the external field values too.
3048: * @param mapping The external field mapping
3049: * @return The value for this mapping
3050: */
3051: public Object getValueForExternalField(JavaTypeMapping mapping) {
3052: if (externalFieldValuesByMapping == null) {
3053: return null;
3054: }
3055: return externalFieldValuesByMapping.get(mapping);
3056: }
3057:
3058: /** Private class storing the fields to be updated for a StateManager, when it is inserted */
3059: private class FieldContainer {
3060: boolean[] fieldsToUpdate = new boolean[getAllFieldNumbers().length];
3061:
3062: /**
3063: * Constructor
3064: * @param fieldNumber the absolute field number to flag true
3065: */
3066: public FieldContainer(int fieldNumber) {
3067: fieldsToUpdate[fieldNumber] = true;
3068: }
3069:
3070: /**
3071: * Flag to true the <code>fieldNumber</code>
3072: * @param fieldNumber the absolute field number to flag true
3073: */
3074: public void set(int fieldNumber) {
3075: fieldsToUpdate[fieldNumber] = true;
3076: }
3077:
3078: /**
3079: * Array with absolute field numbers with true flag
3080: * @return array of absolute field numbers
3081: */
3082: public int[] getFields() {
3083: return getFlagsSetTo(fieldsToUpdate, true);
3084: }
3085: }
3086:
3087: /**
3088: * Makes Transactional Transient instances persistent.
3089: */
3090: public void makePersistentTransactionalTransient() {
3091: preStateChange();
3092: try {
3093: if (myLC.isTransactional && !myLC.isPersistent) {
3094: // make the transient instance persistent in the datastore, if is transactional and !persistent
3095: makePersistent();
3096: myLC = myLC.transitionMakePersistent(this );
3097: }
3098: } finally {
3099: postStateChange();
3100: }
3101:
3102: }
3103:
3104: /**
3105: * Method to change the object state to transactional.
3106: */
3107: public void makeTransactional() {
3108: preStateChange();
3109: try {
3110: if (myLC == null) {
3111: initializeSM(LifeCycleState.T_CLEAN);
3112: setRestoreValues(true);
3113: } else {
3114: myLC = myLC.transitionMakeTransactional(this );
3115: }
3116: } finally {
3117: postStateChange();
3118: }
3119: }
3120:
3121: /**
3122: * Method to change the object state to nontransactional.
3123: */
3124: public void makeNontransactional() {
3125: preStateChange();
3126: try {
3127: myLC = myLC.transitionMakeNontransactional(this );
3128: } finally {
3129: postStateChange();
3130: }
3131: }
3132:
3133: /**
3134: * Method to change the object state to transient.
3135: * @param state Object containing the state of any fetchplan processing
3136: */
3137: public void makeTransient(FetchPlanState state) {
3138: if (makingTransient) {
3139: return; // In the process of becoming transient
3140: }
3141:
3142: try {
3143: makingTransient = true;
3144: if (state == null) {
3145: // No FetchPlan in use so just unset the owner of all loaded SCO fields
3146: int[] fieldNumbers = getFlagsSetTo(loadedFields,
3147: getSecondClassMutableFieldNumbers(), true);
3148: if (fieldNumbers != null && fieldNumbers.length > 0) {
3149: provideFields(fieldNumbers, new UnsetOwners());
3150: }
3151: } else {
3152: // Make all loaded SCO fields transient appropriate to this fetch plan
3153: loadUnloadedFieldsInFetchPlan();
3154: int[] fieldNumbers = getFlagsSetTo(loadedFields,
3155: getAllFieldNumbers(), true);
3156: if (fieldNumbers != null && fieldNumbers.length > 0) {
3157: // TODO Fix this to just access the fields of the FieldManager yet this actually does a replaceField
3158: replaceFields(fieldNumbers,
3159: new MakeTransientFieldManager(this ,
3160: getSecondClassMutableFields(),
3161: myFP, state));
3162: }
3163: }
3164:
3165: preStateChange();
3166: try {
3167: myLC = myLC.transitionMakeTransient(this ,
3168: state != null, myOM
3169: .isRunningDetachAllOnCommit());
3170: } finally {
3171: postStateChange();
3172: }
3173: } finally {
3174: makingTransient = false;
3175: }
3176: }
3177:
3178: /**
3179: * Method to detach this object.
3180: * If the object is detachable then it will be migrated to DETACHED state, otherwise will migrate
3181: * to TRANSIENT. Used by "DetachAllOnCommit".
3182: * @param state State for the detachment process
3183: */
3184: public void detach(FetchPlanState state) {
3185: if (myLC.isDeleted() || myOM.getApiAdapter().isDetached(myPC)
3186: || detaching) {
3187: // Already deleted, detached or being detached
3188: return;
3189: }
3190:
3191: // Check if detachable ... if so then we detach a copy, otherwise we return a transient copy
3192: boolean detachable = myOM.getApiAdapter().isDetachable(myPC);
3193:
3194: if (detachable) {
3195: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
3196: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010009",
3197: StringUtils.toJVMIDString(myPC), ""
3198: + state.getCurrentFetchDepth()));
3199: }
3200:
3201: // Call any "pre-detach" listeners
3202: getCallbackHandler().preDetach(myPC);
3203: }
3204:
3205: try {
3206: detaching = true;
3207:
3208: // Handle any field loading/unloading before the detach
3209: if ((myOM.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_LOAD_FIELDS) != 0) {
3210: // Load any unloaded fetch-plan fields
3211: loadUnloadedFieldsInFetchPlan();
3212: }
3213:
3214: // Detach all (loaded) fields in the FetchPlan
3215: FieldManager detachFieldManager = new DetachFieldManager(
3216: this , getSecondClassMutableFields(), myFP, state,
3217: false);
3218: for (int i = 0; i < loadedFields.length; i++) {
3219: if (loadedFields[i]) {
3220: try {
3221: // Just fetch the field since we are usually called in postCommit() so dont want to update it
3222: detachFieldManager.fetchObjectField(i);
3223: } catch (EndOfFetchPlanGraphException eofpge) {
3224: // Do nothing
3225: }
3226: }
3227: }
3228:
3229: if (detachable) {
3230: // Migrate the lifecycle state to DETACHED_CLEAN
3231: myLC = myLC.transitionDetach(this );
3232:
3233: // Update the object with its detached state
3234: myPC.jdoReplaceFlags();
3235: ((Detachable) myPC).jdoReplaceDetachedState();
3236:
3237: // Call any "post-detach" listeners
3238: getCallbackHandler().postDetach(myPC, myPC); // there is no copy, so give the same object
3239:
3240: PersistenceCapable toCheckPC = myPC;
3241: Object toCheckID = myID;
3242: disconnect();
3243:
3244: if (!toCheckPC.jdoIsDetached()) {
3245: // Sanity check on the objects detached state
3246: throw new JPOXUserException(LOCALISER.msg("026025",
3247: toCheckPC.getClass().getName(), toCheckID));
3248: }
3249: } else {
3250: // Make the object transient
3251: makeTransient(null);
3252: }
3253: } finally {
3254: detaching = false;
3255: }
3256: }
3257:
3258: /**
3259: * Method to make detached copy of this instance
3260: * If the object is detachable then the copy will be migrated to DETACHED state, otherwise will migrate
3261: * the copy to TRANSIENT. Used by "ObjectManager.detachObjectCopy()".
3262: * @param state State for the detachment process
3263: * @return the detached PersistenceCapable instance
3264: * @since 1.1
3265: */
3266: public Object detachCopy(FetchPlanState state) {
3267: if (myLC.isDeleted()) {
3268: throw new JPOXUserException(LOCALISER.msg("026023", myPC
3269: .getClass().getName(), myID));
3270: }
3271: if (myOM.getApiAdapter().isDetached(myPC)) {
3272: throw new JPOXUserException(LOCALISER.msg("026024", myPC
3273: .getClass().getName(), myID));
3274: }
3275: if (dirty) {
3276: myOM.flushInternal(false);
3277: }
3278: if (detaching) {
3279: // Object in the process of detaching (recursive) so return the object which will be the detached object
3280: return referencedPC;
3281: }
3282:
3283: PersistenceCapable detachedPC = myPC.jdoNewInstance(this );
3284: referencedPC = detachedPC;
3285:
3286: // Check if detachable ... if so then we detach a copy, otherwise we return a transient copy
3287: boolean detachable = myOM.getApiAdapter().isDetachable(myPC);
3288:
3289: // make sure a detaching PC is not read by another thread while we are detaching
3290: synchronized (referencedPC) {
3291: if (detachable) {
3292: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
3293: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
3294: "010010", StringUtils.toJVMIDString(myPC),
3295: "" + state.getCurrentFetchDepth(),
3296: StringUtils.toJVMIDString(detachedPC)));
3297: }
3298:
3299: // Call any "pre-detach" listeners
3300: getCallbackHandler().preDetach(myPC);
3301: }
3302: try {
3303: detaching = true;
3304:
3305: // Handle any field loading/unloading before the detach
3306: if ((myOM.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_LOAD_FIELDS) != 0) {
3307: // Load any unloaded fetch-plan fields
3308: loadUnloadedFieldsInFetchPlan();
3309: }
3310:
3311: if (myLC == myOM.getOMFContext().getApiAdapter()
3312: .getLifeCycleState(LifeCycleState.HOLLOW)
3313: || myLC == myOM.getOMFContext().getApiAdapter()
3314: .getLifeCycleState(
3315: LifeCycleState.P_NONTRANS)) {
3316: // Migrate any HOLLOW/P_NONTRANS to P_CLEAN etc
3317: myLC = myLC.transitionReadField(this , true);
3318: }
3319:
3320: // Create a SM for our copy object
3321: JDOStateManagerImpl smDetachedPC = new JDOStateManagerImpl(
3322: myOM, cmd);
3323: smDetachedPC.initialiseForDetached(detachedPC,
3324: getExternalObjectId(myPC), getVersion(myPC));
3325: detachedPC.jdoReplaceStateManager(smDetachedPC);
3326: smDetachedPC.referencedPC = myPC;
3327:
3328: smDetachedPC.replaceFields(getFieldsNumbersToDetach(),
3329: new DetachFieldManager(this ,
3330: getSecondClassMutableFields(), myFP,
3331: state, true));
3332:
3333: smDetachedPC.referencedPC = null;
3334: if (detachable) {
3335: // Update the object with its detached state - not to be confused with the "state" object above
3336: detachedPC.jdoReplaceFlags();
3337: ((Detachable) detachedPC).jdoReplaceDetachedState();
3338: } else {
3339: smDetachedPC.makeTransient(null);
3340: }
3341:
3342: // Remove its StateManager since now detached or transient
3343: detachedPC.jdoReplaceStateManager(null);
3344: } catch (Exception e) {
3345: // What could possible be thrown here ?
3346: JPOXLogger.PERSISTENCE.debug(
3347: "DETACH ERROR : Error thrown while detaching "
3348: + StringUtils.toJVMIDString(myPC)
3349: + " (id=" + myID + ")", e);
3350: } finally {
3351: detaching = false;
3352: referencedPC = null;
3353: }
3354:
3355: if (detachable
3356: && !myOM.getApiAdapter().isDetached(detachedPC)) {
3357: // Sanity check on the objects detached state
3358: throw new JPOXUserException(LOCALISER.msg("026025",
3359: detachedPC.getClass().getName(), myID));
3360: }
3361:
3362: if (detachable) {
3363: // Call any "post-detach" listeners
3364: getCallbackHandler().postDetach(myPC, detachedPC);
3365: }
3366: }
3367: return detachedPC;
3368: }
3369:
3370: /**
3371: * Return an array of field numbers that must be included in the detached object
3372: * @return the field numbers array
3373: */
3374: private int[] getFieldsNumbersToDetach() {
3375: // This will cause the detach of any other fields in the FetchPlan.
3376: int[] fieldsToDetach = myFP.getFieldsInActualFetchPlan();
3377: if ((myOM.getFetchPlan().getDetachmentOptions() & FetchPlan.DETACH_UNLOAD_FIELDS) == 0) {
3378: // Detach fetch-plan fields plus any other loaded fields
3379: int[] allFieldNumbers = getAllFieldNumbers();
3380: int[] loadedFieldNumbers = getFlagsSetTo(loadedFields,
3381: allFieldNumbers, true);
3382: if (loadedFieldNumbers != null
3383: && loadedFieldNumbers.length > 0) {
3384: boolean[] flds = new boolean[allFieldNumbers.length];
3385: for (int i = 0; i < fieldsToDetach.length; i++) {
3386: flds[fieldsToDetach[i]] = true;
3387: }
3388: for (int i = 0; i < loadedFieldNumbers.length; i++) {
3389: flds[loadedFieldNumbers[i]] = true;
3390: }
3391: fieldsToDetach = getFlagsSetTo(flds, true);
3392: }
3393: }
3394: return fieldsToDetach;
3395: }
3396:
3397: /**
3398: * Accessor for the referenced PC object when we are attaching or detaching.
3399: * When attaching and this is the detached object this returns the newly attached object.
3400: * When attaching and this is the newly attached object this returns the detached object.
3401: * When detaching and this is the newly detached object this returns the attached object.
3402: * When detaching and this is the attached object this returns the newly detached object.
3403: * @return The referenced object (or null).
3404: */
3405: public Object getReferencedPC() {
3406: return referencedPC;
3407: }
3408:
3409: /**
3410: * Method to attach the object managed by this StateManager.
3411: * @param embedded Whether it is embedded
3412: * @since 1.2
3413: */
3414: public void attach(boolean embedded) {
3415: if (attaching) {
3416: return;
3417: }
3418:
3419: attaching = true;
3420: try {
3421: // Check if the object is already persisted
3422: boolean persistent = false;
3423: if (embedded) {
3424: persistent = true;
3425: } else {
3426: if (!myOM.getOMFContext().getPersistenceConfiguration()
3427: .getAttachSameDatastore()) {
3428: // We cant assume that this object was detached from this datastore so we check it
3429: try {
3430: getStoreManager().locateObject(this );
3431: persistent = true;
3432: } catch (JPOXObjectNotFoundException onfe) {
3433: // Not currently present!
3434: }
3435: } else {
3436: // Assumed detached from this datastore
3437: persistent = true;
3438: }
3439: }
3440:
3441: // Call any "pre-attach" listeners
3442: getCallbackHandler().preAttach(myPC);
3443:
3444: // Retrieve the updated values from the detached object
3445: myPC.jdoReplaceStateManager(this );
3446: retrieveDetachState(this );
3447:
3448: if (!persistent) {
3449: // Persist the object into this datastore first
3450: makePersistent();
3451: }
3452:
3453: // Migrate the lifecycle state to persistent
3454: myLC = myLC.transitionAttach(this );
3455:
3456: int[] attachFieldNumbers = getFieldNumbersOfLoadedOrDirtyFields(
3457: loadedFields, dirtyFields);
3458: if (attachFieldNumbers != null) {
3459: // Only update the fields that were detached, and only update them if there are any to update
3460: provideFields(attachFieldNumbers,
3461: new AttachFieldManager(this ,
3462: getSecondClassMutableFields(),
3463: dirtyFields, persistent, true, false));
3464: }
3465:
3466: // Call any "post-attach" listeners
3467: getCallbackHandler().postAttach(myPC, myPC);
3468: } finally {
3469: attaching = false;
3470: }
3471: }
3472:
3473: /**
3474: * Method to attach a copy of the detached persistable instance and return the (attached) copy.
3475: * @param obj the detached persistable instance to be attached
3476: * @param embedded Whether the object is stored embedded/serialised in another object
3477: * @return The attached copy
3478: * @since 1.1
3479: */
3480: public Object attachCopy(Object obj, boolean embedded) {
3481: if (attaching) {
3482: return myPC;
3483: }
3484: attaching = true;
3485:
3486: PersistenceCapable detachedPC = (PersistenceCapable) obj;
3487: try {
3488: // Check if the object is already persisted
3489: boolean persistent = false;
3490: if (embedded) {
3491: persistent = true;
3492: } else {
3493: if (!myOM.getOMFContext().getPersistenceConfiguration()
3494: .getAttachSameDatastore()) {
3495: // We cant assume that this object was detached from this datastore so we check it
3496: try {
3497: getStoreManager().locateObject(this );
3498: persistent = true;
3499: } catch (JPOXObjectNotFoundException onfe) {
3500: // Not currently present!
3501: }
3502: } else {
3503: // Assumed detached from this datastore
3504: persistent = true;
3505: }
3506: }
3507:
3508: // Call any "pre-attach" listeners
3509: getCallbackHandler().preAttach(detachedPC);
3510:
3511: if (myOM.getApiAdapter().isDeleted(detachedPC)) {
3512: // The detached object has been deleted
3513: myLC = myLC.transitionDeletePersistent(this );
3514: }
3515:
3516: if (!myOM.getTransaction().getOptimistic()
3517: && (myLC == myOM.getApiAdapter().getLifeCycleState(
3518: LifeCycleState.HOLLOW) || myLC == myOM
3519: .getApiAdapter().getLifeCycleState(
3520: LifeCycleState.P_NONTRANS))) {
3521: // Pessimistic txns and in HOLLOW/P_NONTRANS, so move to P_CLEAN
3522: // TODO Move this into the lifecycle state classes as a "transitionAttach"
3523: myLC = myLC.transitionMakeTransactional(this );
3524: }
3525:
3526: if (persistent) {
3527: // Make sure that all non-container SCO fields are loaded so we can make valid dirty checks
3528: // for whether these fields have been updated whilst detached. The detached object doesnt know if the contents
3529: // have been changed.
3530: loadSCONonContainerFields();
3531: }
3532:
3533: // Add a state manager to the detached PC so that we can retrieve its detached state
3534: JDOStateManagerImpl smDetachedPC = new JDOStateManagerImpl(
3535: myOM, cmd);
3536: smDetachedPC.initialiseForDetached(detachedPC,
3537: getExternalObjectId(detachedPC), null);
3538: detachedPC.jdoReplaceStateManager(smDetachedPC);
3539:
3540: // Cross-reference the attached and detached objects for the attach process
3541: smDetachedPC.referencedPC = myPC;
3542: this .referencedPC = detachedPC;
3543:
3544: // Retrieve the updated values from the detached object
3545: retrieveDetachState(smDetachedPC);
3546:
3547: if (!persistent) {
3548: // Object is not yet persisted! so make it persistent
3549:
3550: // Make sure all field values in the attach object are ready for inserts (but dont trigger any cascade attaches)
3551: internalAttachCopy(this , smDetachedPC,
3552: smDetachedPC.loadedFields,
3553: smDetachedPC.dirtyFields, persistent,
3554: smDetachedPC.myVersion, false);
3555:
3556: makePersistent();
3557: }
3558:
3559: // Go through all related fields and attach them (including relationships)
3560: internalAttachCopy(this , smDetachedPC,
3561: smDetachedPC.loadedFields,
3562: smDetachedPC.dirtyFields, persistent,
3563: smDetachedPC.myVersion, true);
3564:
3565: // Remove the state manager from the detached PC
3566: detachedPC.jdoReplaceStateManager(null);
3567:
3568: // Remove the corss-referencing now we have finished the attach process
3569: smDetachedPC.referencedPC = null;
3570: this .referencedPC = null;
3571:
3572: // Call any "post-attach" listeners
3573: getCallbackHandler().postAttach(myPC, detachedPC);
3574: } finally {
3575: attaching = false;
3576: }
3577: return myPC;
3578: }
3579:
3580: /**
3581: * Attach the fields of a persistent object.
3582: * @param sm StateManager for the attached object.
3583: * @param smDetached StateManager for the detached object.
3584: * @param loadedFields Fields that were detached with the object
3585: * @param dirtyFields Fields that have been modified while detached
3586: * @param persistent whether the object is already persistent
3587: * @param version the version
3588: * @param cascade Whether to cascade the attach to related fields
3589: * @since 1.1
3590: */
3591: private void internalAttachCopy(org.jpox.StateManager sm,
3592: org.jpox.StateManager smDetached, boolean[] loadedFields,
3593: boolean[] dirtyFields, boolean persistent, Object version,
3594: boolean cascade) {
3595: // Need to take all loaded fields plus all modified fields
3596: // (maybe some werent detached but have been modified) and attach them
3597: int[] attachFieldNumbers = getFieldNumbersOfLoadedOrDirtyFields(
3598: loadedFields, dirtyFields);
3599: sm.setVersion(version);
3600: if (attachFieldNumbers != null) {
3601: // Only update the fields that were detached, and only update them if there are any to update
3602: smDetached.provideFields(attachFieldNumbers,
3603: new AttachFieldManager(sm,
3604: getSecondClassMutableFields(), dirtyFields,
3605: persistent, cascade, true));
3606: }
3607: }
3608:
3609: /**
3610: * Convenience accessor to return the field numbers for the input loaded and dirty field arrays.
3611: * @param loadedFields Fields that were detached with the object
3612: * @param dirtyFields Fields that have been modified while detached
3613: */
3614: private int[] getFieldNumbersOfLoadedOrDirtyFields(
3615: boolean[] loadedFields, boolean[] dirtyFields) {
3616: // Find the number of fields that are loaded or dirty
3617: int numFields = 0;
3618: for (int i = 0; i < loadedFields.length; i++) {
3619: if (loadedFields[i] || dirtyFields[i]) {
3620: numFields++;
3621: }
3622: }
3623:
3624: int[] fieldNumbers = new int[numFields];
3625: int n = 0;
3626: for (int i = 0; i < loadedFields.length; i++) {
3627: if (loadedFields[i] || dirtyFields[i]) {
3628: fieldNumbers[n++] = getAllFieldNumbers()[i];
3629: }
3630: }
3631: return fieldNumbers;
3632: }
3633:
3634: /**
3635: * Method to delete the object from persistence.
3636: */
3637: public void deletePersistent() {
3638: if (!myLC.isDeleted()) {
3639: if (myOM.isDelayDatastoreOperationsEnabled()) {
3640: // Optimistic transactions, with all updates delayed til flush/commit
3641:
3642: // Call any lifecycle listeners waiting for this event
3643: getCallbackHandler().preDelete(myPC);
3644:
3645: // Delay deletion until flush/commit so run reachability now to tag all reachable instances as necessary
3646: myOM.markDirty(this , false);
3647:
3648: // Reachability
3649: if (myLC.stateType() == LifeCycleState.P_CLEAN
3650: || myLC.stateType() == LifeCycleState.P_DIRTY
3651: || myLC.stateType() == LifeCycleState.HOLLOW
3652: || myLC.stateType() == LifeCycleState.P_NONTRANS
3653: || myLC.stateType() == LifeCycleState.P_NONTRANS_DIRTY) {
3654: // Make sure all fields are loaded so we can perform reachability
3655: loadUnloadedFields();
3656: }
3657: provideFields(getAllFieldNumbers(),
3658: new DeleteFieldManager(this ));
3659:
3660: // Update lifecycle state (after running reachability since it will unload all fields)
3661: dirty = true;
3662: preStateChange();
3663: try {
3664: // TODO This unloads all fields, and we need them if in optimistic mode (see below)
3665: myLC = myLC.transitionDeletePersistent(this );
3666: } finally {
3667: postStateChange();
3668: }
3669: } else {
3670: // Datastore transactions, with all updates processed now
3671:
3672: // Call any lifecycle listeners waiting for this event.
3673: getCallbackHandler().preDelete(myPC);
3674:
3675: // Update lifecycle state
3676: dirty = true;
3677: preStateChange();
3678: try {
3679: myLC = myLC.transitionDeletePersistent(this );
3680: } finally {
3681: postStateChange();
3682: }
3683:
3684: // Delete the object from the datastore (includes reachability)
3685: internalDeletePersistent();
3686:
3687: // Call any lifecycle listeners waiting for this event.
3688: getCallbackHandler().postDelete(myPC);
3689: }
3690: }
3691: }
3692:
3693: /**
3694: * Method to delete the object from the datastore.
3695: */
3696: private void internalDeletePersistent() {
3697: if (isDeleting()) {
3698: throw new JPOXUserException(LOCALISER.msg("026008"));
3699: }
3700:
3701: activity = ActivityState.DELETING;
3702: try {
3703: if (dirty) {
3704: clearDirtyFlags();
3705: /*
3706: * Clear the PM's knowledge of our being dirty. This will
3707: * call our flush() method, which will do nothing.
3708: */
3709: myOM.flushInternal(false);
3710: }
3711:
3712: getStoreManager().deleteObject(this );
3713: } finally {
3714: activity = ActivityState.NONE;
3715: }
3716: }
3717:
3718: /**
3719: * Locate the object in the datastore.
3720: * @throws JPOXObjectNotFoundException if the object doesnt exist.
3721: */
3722: public void locate() {
3723: // Validate the object existence
3724: getStoreManager().locateObject(this );
3725: }
3726:
3727: /**
3728: * Nullify fields with reference to PersistenceCapable or SCO instances
3729: */
3730: public void nullifyFields() {
3731: if (!myLC.isDeleted() && !myOM.getApiAdapter().isDetached(myPC)) {
3732: // Update any relationships for fields of this object that aren't dependent
3733: replaceFields(getNonPrimaryKeyFieldNumbers(),
3734: new NullifyRelationFieldManager(this ));
3735: flush();
3736: }
3737: }
3738:
3739: /**
3740: * Validates whether the persistence capable instance exists in the datastore.
3741: * If the instance doesn't exist in the datastore, this method will fail raising a JPOXObjectNotFoundException.
3742: * If the object is transactional then does nothing.
3743: * If the object has unloaded (non-SCO, non-PK) fetch plan fields then fetches them.
3744: * Else it checks the existence of the object in the datastore.
3745: */
3746: public void validate() {
3747: if (!myLC.isTransactional) {
3748: // Find all FetchPlan fields that are not PK, not SCO and still not loaded
3749: int[] fieldNumbers = getFlagsSetTo(loadedFields, myFP
3750: .getFieldsInActualFetchPlan(), false);
3751: if (fieldNumbers != null && fieldNumbers.length > 0) {
3752: fieldNumbers = getFlagsSetTo(getNonPrimaryKeyFields(),
3753: fieldNumbers, true);
3754: }
3755: if (fieldNumbers != null && fieldNumbers.length > 0) {
3756: fieldNumbers = getFlagsSetTo(
3757: getSecondClassMutableFields(), fieldNumbers,
3758: false);
3759: }
3760: if (fieldNumbers != null && fieldNumbers.length > 0) {
3761: // Some fetch plan fields are not loaded so try to load them, and this by itself validates the existence
3762: // Load the fields in the current FetchPlan (JDO2 spec 12.6.5)
3763: transitionReadField(false);
3764:
3765: fieldNumbers = myFP.getFieldsInActualFetchPlan();
3766: if (fieldNumbers != null) {
3767: boolean callPostLoad = myFP
3768: .isToCallPostLoadFetchPlan(this .loadedFields);
3769: setTransactionalVersion(null); // Make sure we get the latest version
3770: getStoreManager().fetchObject(this , fieldNumbers);
3771: if (callPostLoad) {
3772: postLoad();
3773: }
3774:
3775: // Update the L2 cache to have this object (more loaded fields)
3776: myOM.putObjectIntoCache(this , false, true);
3777: }
3778: } else {
3779: // Validate the object existence
3780: getStoreManager().locateObject(this );
3781: transitionReadField(false);
3782: }
3783: }
3784: }
3785:
3786: // ------------------------- Object State Methods --------------------------
3787:
3788: /**
3789: * Method to change the object state to evicted.
3790: */
3791: public void evict() {
3792: if (myLC != myOM.getOMFContext().getApiAdapter()
3793: .getLifeCycleState(LifeCycleState.P_CLEAN)
3794: && myLC != myOM.getOMFContext().getApiAdapter()
3795: .getLifeCycleState(LifeCycleState.P_NONTRANS)) {
3796: return;
3797: }
3798:
3799: preStateChange();
3800: try {
3801: try {
3802: getCallbackHandler().preClear(myPC);
3803:
3804: getCallbackHandler().postClear(myPC);
3805: } finally {
3806: myLC = myLC.transitionEvict(this );
3807: }
3808: } finally {
3809: postStateChange();
3810: }
3811: }
3812:
3813: /**
3814: * Method to refresh the object.
3815: */
3816: public void refresh() {
3817: preStateChange();
3818: try {
3819: myLC = myLC.transitionRefresh(this );
3820: } finally {
3821: postStateChange();
3822: }
3823: }
3824:
3825: /**
3826: * Method to retrieve the object.
3827: * @param fgOnly Only load the current fetch group fields
3828: */
3829: public void retrieve(boolean fgOnly) {
3830: preStateChange();
3831: try {
3832: myLC = myLC.transitionRetrieve(this , fgOnly);
3833: } finally {
3834: postStateChange();
3835: }
3836: }
3837:
3838: /**
3839: * Method to retrieve the object.
3840: * @param fetchPlan the fetch plan to load fields
3841: */
3842: public void retrieve(FetchPlan fetchPlan) {
3843: preStateChange();
3844: try {
3845: myLC = myLC.transitionRetrieve(this , fetchPlan);
3846: } finally {
3847: postStateChange();
3848: }
3849: }
3850:
3851: // --------------------------- Process Methods -----------------------------
3852:
3853: /**
3854: * Method called before a change in state.
3855: */
3856: private void preStateChange() {
3857: changingState = true;
3858: }
3859:
3860: /**
3861: * Method called after a change in state.
3862: */
3863: private void postStateChange() {
3864: changingState = false;
3865: if (postLoadPending && isFetchPlanLoaded()) {
3866: // Only call postLoad when all FetchPlan fields are loaded
3867: postLoadPending = false;
3868: postLoad();
3869: }
3870: }
3871:
3872: /**
3873: * Method called before a write of the specified field.
3874: * @param field The field to write
3875: * @return true if the field was already dirty before
3876: */
3877: private boolean preWriteField(int field) {
3878: boolean wasDirty = dirty;
3879: /*
3880: * If we're writing a field in the process of inserting it must be due
3881: * to jdoPreStore(). We haven't actually done the INSERT yet so we
3882: * don't want to mark anything as dirty, which would make us want to do
3883: * an UPDATE later.
3884: */
3885: if (activity != ActivityState.INSERTING
3886: && activity != ActivityState.INSERTING_CALLBACKS) {
3887: //TODO dirty already??? this is not correct, only gets dirty after state transition
3888: // dirty = true;
3889: if (!wasDirty) // (only do it for first dirty event).
3890: {
3891: // Call any lifecycle listeners waiting for this event
3892: getCallbackHandler().preDirty(myPC);
3893: }
3894:
3895: transitionWriteField();
3896:
3897: dirty = true;
3898: dirtyFields[field] = true;
3899: loadedFields[field] = true;
3900: }
3901: return wasDirty;
3902: }
3903:
3904: /**
3905: * Method called after the write of a field.
3906: * @param wasDirty whether before writing this field the pc was dirty
3907: */
3908: private void postWriteField(boolean wasDirty) {
3909: if (dirty && !wasDirty) // (only do it for first dirty event).
3910: {
3911: // Call any lifecycle listeners waiting for this event
3912: getCallbackHandler().postDirty(myPC);
3913: }
3914:
3915: if (activity == ActivityState.NONE && !flushing
3916: && !(myLC.isTransactional && !myLC.isPersistent)) {
3917: if (detaching && referencedPC == null) {
3918: // detachAllOnCommit caused a field to be dirty so ignore it
3919: return;
3920: } else {
3921: // Not during flush, and not transactional-transient, and not inserting - so mark as dirty
3922: myOM.markDirty(this , true);
3923: }
3924: }
3925: }
3926:
3927: /**
3928: * Called whenever the default fetch group fields have all been loaded.
3929: * Updates jdoFlags and calls jdoPostLoad() as appropriate.
3930: * <p>
3931: * If it's called in the midst of a life-cycle transition both actions will
3932: * be deferred until the transition is complete.
3933: * <em>This deferral is important</em>. Without it, we could enter user
3934: * code (jdoPostLoad()) while still making a state transition, and that way
3935: * lies madness.
3936: * <p>
3937: * As an example, consider a jdoPostLoad() that calls other enhanced methods
3938: * that read fields (jdoPostLoad() itself is not enhanced). A P_NONTRANS
3939: * object accessed within a transaction would produce the following infinite
3940: * loop:
3941: * <p>
3942: * <blockquote>
3943: *
3944: * <pre>
3945: *
3946: * isLoaded()
3947: * transitionReadField()
3948: * refreshLoadedFields()
3949: * jdoPostLoad()
3950: * isLoaded()
3951: * ...
3952: *
3953: * </pre>
3954: *
3955: * </blockquote>
3956: * <p>
3957: * because the transition from P_NONTRANS to P_CLEAN can never be completed.
3958: */
3959: private void postLoad() {
3960: if (changingState) {
3961: postLoadPending = true;
3962: } else {
3963: /*
3964: * A transactional object whose DFG fields are loaded does not need
3965: * to contact us in order to read those fields, so we can safely set
3966: * READ_OK.
3967: *
3968: * A non-transactional object needs to notify us on all field reads
3969: * so that we can decide whether or not any transition should occur,
3970: * so we leave the flags at LOAD_REQUIRED.
3971: */
3972: if (jdoDfgFlags == PersistenceCapable.LOAD_REQUIRED
3973: && myLC.isTransactional()) {
3974: jdoDfgFlags = PersistenceCapable.READ_OK;
3975: myPC.jdoReplaceFlags();
3976: }
3977:
3978: getCallbackHandler().postLoad(myPC);
3979: }
3980: }
3981:
3982: /**
3983: * Method invoked just before a transaction starts for the ObjectManager managing us.
3984: * @param tx The transaction
3985: */
3986: public void preBegin(org.jpox.Transaction tx) {
3987: preStateChange();
3988: try {
3989: myLC = myLC.transitionBegin(this , tx);
3990: } finally {
3991: postStateChange();
3992: }
3993: }
3994:
3995: /**
3996: * This method is invoked just after a commit is performed in a Transaction
3997: * involving the PersistenceCapable managed by this StateManager
3998: * @param tx The Transaction
3999: */
4000: public void postCommit(org.jpox.Transaction tx) {
4001: preStateChange();
4002: try {
4003: myLC = myLC.transitionCommit(this , tx);
4004: } finally {
4005: postStateChange();
4006: }
4007: }
4008:
4009: /**
4010: * This method is invoked just before a rollback is performed in a Transaction
4011: * involving the PersistenceCapable managed by this StateManager.
4012: * @param tx The Transaction been rolled back
4013: */
4014: public void preRollback(org.jpox.Transaction tx) {
4015: preStateChange();
4016: try {
4017: myOM.clearDirty(this );
4018: myLC = myLC.transitionRollback(this , tx);
4019: } finally {
4020: postStateChange();
4021: }
4022: }
4023:
4024: /** Flag to signify that we are currently storing the PC object, so we dont detach it on any serialisation. */
4025: private boolean storingPC = false;
4026:
4027: /**
4028: * Method to set the storing PC flag.
4029: */
4030: public void setStoringPC() {
4031: storingPC = true;
4032: }
4033:
4034: /**
4035: * Method to unset the storing PC flag.
4036: */
4037: public void unsetStoringPC() {
4038: storingPC = false;
4039: }
4040:
4041: /**
4042: * Guarantee that the serializable transactional and persistent fields
4043: * are loaded into the instance. This method is called by the generated
4044: * jdoPreSerialize method prior to serialization of the instance.
4045: *
4046: * @param pc the calling PersistenceCapable instance
4047: */
4048: public void preSerialize(PersistenceCapable pc) {
4049: if (disconnectClone(pc)) {
4050: return;
4051: }
4052:
4053: // Retrieve all fields prior to serialisation
4054: retrieve(false);
4055:
4056: myLC = myLC.transitionSerialize(this );
4057:
4058: if (!storingPC && pc instanceof Detachable) {
4059: if (!myLC.isDeleted && myLC.isPersistent) {
4060: if (myLC.isDirty) {
4061: flush();
4062: }
4063:
4064: // Normal PC Detachable object being serialised so load up the detached state into the instance
4065: // JDO2 spec "For Detachable classes, the jdoPreSerialize method must also initialize the jdoDetachedState
4066: // instance so that the detached state is serialized along with the instance."
4067: ((Detachable) pc).jdoReplaceDetachedState();
4068: }
4069: }
4070: }
4071:
4072: /**
4073: * Flushes any outstanding changes to the object to the datastore.
4074: * This will process :-
4075: * <ul>
4076: * <li>Any objects that have been marked as provisionally persistent yet havent been flushed to the datastore.</li>
4077: * <li>Any objects that have been marked as provisionally deleted yet havent been flushed to the datastore.</li>
4078: * <li>Any fields that have been updated.</li>
4079: * </ul>
4080: */
4081: public void flush() {
4082: if (dirty) {
4083: if (flushing) {
4084: // In the case of persisting a new object using autoincrement id within an optimistic
4085: // transaction, flush() will initially be called at the point of recognising that the
4086: // id is generated in the datastore, and will then be called again at the point of doing
4087: // the InsertRequest for the object itself. Just return since we are flushing right now
4088: return;
4089: }
4090: if (activity == ActivityState.INSERTING
4091: || activity == ActivityState.INSERTING_CALLBACKS) {
4092: return;
4093: }
4094:
4095: flushing = true;
4096: try {
4097: if (myLC.stateType() == LifeCycleState.P_NEW
4098: && !flushedNew) {
4099: // Newly persisted object but not yet flushed to datastore (e.g optimistic transactions)
4100: if (!isEmbedded()) {
4101: // internalMakePersistent does preStore, postStore
4102: internalMakePersistent();
4103: } else {
4104: getCallbackHandler().preStore(myPC);
4105:
4106: getCallbackHandler().postStore(myPC);
4107: }
4108: dirty = false;
4109: } else if (myLC.stateType() == LifeCycleState.P_DELETED) {
4110: // Object marked as deleted but not yet deleted from datastore
4111: getCallbackHandler().preDelete(myPC);
4112: if (!isEmbedded()) {
4113: internalDeletePersistent();
4114: }
4115: getCallbackHandler().postDelete(myPC);
4116: } else if (myLC.stateType() == LifeCycleState.P_NEW_DELETED) {
4117: // Newly persisted object marked as deleted but not yet deleted from datastore
4118: if (flushedNew) {
4119: // Only delete it if it was actually persisted into the datastore
4120: getCallbackHandler().preDelete(myPC);
4121: if (!isEmbedded()) {
4122: internalDeletePersistent();
4123: }
4124: flushedNew = false; // No longer newly persisted flushed object since has been deleted
4125: getCallbackHandler().postDelete(myPC);
4126: } else {
4127: // Was never persisted to the datastore so nothing to do
4128: dirty = false;
4129: }
4130: } else {
4131: // Updated object with changes to flush to datastore
4132: if (!isDeleting()) {
4133: getCallbackHandler().preStore(myPC);
4134: }
4135:
4136: int[] dirtyFieldNumbers = getFlagsSetTo(
4137: dirtyFields, true);
4138: if (dirtyFieldNumbers == null) {
4139: throw new JPOXException(LOCALISER.msg("026010"))
4140: .setFatal();
4141: }
4142: if (!isEmbedded()) {
4143: getStoreManager().updateObject(this ,
4144: dirtyFieldNumbers);
4145:
4146: // Update the object in the cache(s)
4147: myOM.putObjectIntoCache(this , true, true);
4148: }
4149:
4150: clearDirtyFlags();
4151:
4152: getCallbackHandler().postStore(myPC);
4153: }
4154: } finally {
4155: flushing = false;
4156: }
4157: }
4158: }
4159:
4160: /**
4161: * Initialize SM reference in PC and Oid
4162: * @param newState The new StateManager state
4163: **/
4164: private void initializeSM(int newState) {
4165: final JDOStateManagerImpl this SM = this ;
4166: myLC = myOM.getOMFContext().getApiAdapter().getLifeCycleState(
4167: newState);
4168:
4169: try {
4170: if (myLC.isPersistent()) {
4171: myOM.addStateManager(this );
4172: }
4173:
4174: // Everything OK so far. Now we can set SM reference in PC
4175: // It can be done only after myLC is set to deligate validation
4176: // to the LC and objectId verified for uniqueness
4177: AccessController.doPrivileged(
4178: // Need to have privileges to perform jdoReplaceStateManager.
4179: new PrivilegedAction() {
4180: public Object run() {
4181: try {
4182: myPC.jdoReplaceStateManager(this SM);
4183: return null;
4184: } catch (SecurityException e) {
4185: throw new JDOFatalUserException(
4186: "EXC_CannotSetStateManager", e);
4187: }
4188: }
4189: });
4190:
4191: } catch (SecurityException e) {
4192: throw new JPOXUserException(e.getMessage());
4193:
4194: } catch (JPOXException jpe) {
4195: if (myOM.getStateManagerById(myID) == this ) {
4196: myOM.removeStateManager(this );
4197: }
4198: throw jpe;
4199: }
4200: }
4201:
4202: /**
4203: * Method to disconnect any cloned persistence capable objects from their
4204: * StateManager.
4205: * @param pc The PersistenceCapable object
4206: * @return Whether the object was disconnected.
4207: **/
4208: protected boolean disconnectClone(PersistenceCapable pc) {
4209: if (detaching) {
4210: return false;
4211: }
4212: if (pc != myPC) {
4213: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
4214: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("026001",
4215: StringUtils.toJVMIDString(pc), this ));
4216: }
4217:
4218: // Reset jdoFlags in the clone to PersistenceCapable.READ_WRITE_OK
4219: // and clear its state manager.
4220: pc.jdoReplaceFlags();
4221: pc.jdoReplaceStateManager(null);
4222: return true;
4223: } else {
4224: return false;
4225: }
4226: }
4227:
4228: /**
4229: * Convenience method to unset the owners of all SCO fields in the PC object.
4230: */
4231: private void unsetOwnerInSCOFields() {
4232: // Call unsetOwner() on all loaded SCO fields.
4233: int[] fieldNumbers = getFlagsSetTo(loadedFields,
4234: getSecondClassMutableFieldNumbers(), true);
4235: if (fieldNumbers != null && fieldNumbers.length > 0) {
4236: provideFields(fieldNumbers, new UnsetOwners());
4237: }
4238: }
4239:
4240: /**
4241: * Disconnect the StateManager from the PersistenceManager and PC object.
4242: */
4243: public void disconnect() {
4244: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
4245: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("026011",
4246: StringUtils.toJVMIDString(myPC), this ));
4247: }
4248:
4249: //we are transitioning to TRANSIENT state, so if any postLoad
4250: //action is pending we do it before. This usually happens when
4251: //we make transient instances using the fetch plan and some
4252: //fields were loaded during this action which triggered a
4253: //jdoPostLoad event
4254: if (postLoadPending) {
4255: changingState = false; //hack to make sure postLoad does not return without processing
4256: postLoadPending = false;
4257: postLoad();
4258: }
4259:
4260: unsetOwnerInSCOFields();
4261:
4262: myOM.removeStateManager(this );
4263: jdoDfgFlags = PersistenceCapable.READ_WRITE_OK;
4264: myPC.jdoReplaceFlags();
4265:
4266: disconnecting = true;
4267: try {
4268: replaceStateManager(null);
4269: } finally {
4270: disconnecting = false;
4271: }
4272:
4273: clearSavedFields();
4274: myOM = null;
4275: myFP = null;
4276: myPC = null;
4277: myID = null;
4278: myLC = null;
4279: cmd = null;
4280: }
4281:
4282: /**
4283: * Registers the pc class in the cache
4284: */
4285: public void registerTransactional() {
4286: myOM.addStateManager(this );
4287: }
4288:
4289: // ------------------------------ Detach Methods ---------------------------
4290:
4291: /**
4292: * Convenience method to retrieve the detach state from the passed State Manager's object
4293: * @param sm The State Manager
4294: */
4295: public void retrieveDetachState(org.jpox.StateManager sm) {
4296: if (sm.getObject() instanceof Detachable) {
4297: ((JDOStateManagerImpl) sm).miscFlags |= MISC_RETRIEVING_DETACHED_STATE;
4298: ((Detachable) sm.getObject()).jdoReplaceDetachedState();
4299: ((JDOStateManagerImpl) sm).miscFlags &= ~MISC_RETRIEVING_DETACHED_STATE;
4300: }
4301: }
4302:
4303: /**
4304: * Convenience method to reset the detached state in the current object.
4305: */
4306: public void resetDetachState() {
4307: if (getObject() instanceof Detachable) {
4308: miscFlags |= MISC_RESETTING_DETACHED_STATE;
4309: try {
4310: ((Detachable) getObject()).jdoReplaceDetachedState();
4311: } finally {
4312: miscFlags &= ~MISC_RESETTING_DETACHED_STATE;
4313: }
4314: }
4315: }
4316:
4317: /**
4318: * Method to update the "detached state" in the detached object
4319: * to obtain the "detached state" from the detached object, or
4320: * to reset it (to null).
4321: * @param pc The PersistenceCapable beind updated
4322: * @param currentState The current state values
4323: * @return The detached state to assign to the object
4324: */
4325: public Object[] replacingDetachedState(Detachable pc,
4326: Object[] currentState) {
4327: if ((miscFlags & MISC_RESETTING_DETACHED_STATE) != 0) {
4328: return null;
4329: } else if ((miscFlags & MISC_RETRIEVING_DETACHED_STATE) != 0) {
4330: // Retrieving the detached state from the detached object
4331: // Don't need the id or version since they can't change
4332: BitSet jdoLoadedFields = (BitSet) currentState[2];
4333: for (int i = 0; i < this .loadedFields.length; i++) {
4334: this .loadedFields[i] = jdoLoadedFields.get(i);
4335: }
4336:
4337: BitSet jdoModifiedFields = (BitSet) currentState[3];
4338: for (int i = 0; i < dirtyFields.length; i++) {
4339: dirtyFields[i] = jdoModifiedFields.get(i);
4340: }
4341: setVersion(currentState[1]);
4342: return currentState;
4343: } else {
4344: // Updating the detached state in the detached object with our state
4345: Object[] state = new Object[4];
4346: state[0] = myID;
4347: state[1] = getVersion(myPC);
4348:
4349: // Loaded fields
4350: BitSet loadedState = new BitSet();
4351: for (int i = 0; i < loadedFields.length; i++) {
4352: if (loadedFields[i]) {
4353: loadedState.set(i);
4354: } else {
4355: loadedState.clear(i);
4356: }
4357: }
4358: state[2] = loadedState;
4359:
4360: // Modified fields
4361: BitSet modifiedState = new BitSet();
4362: for (int i = 0; i < dirtyFields.length; i++) {
4363: if (dirtyFields[i]) {
4364: modifiedState.set(i);
4365: } else {
4366: modifiedState.clear(i);
4367: }
4368: }
4369: state[3] = modifiedState;
4370:
4371: return state;
4372: }
4373: }
4374:
4375: // ------------------------------ Helper Methods ---------------------------
4376:
4377: /**
4378: * Method to dump a PersistenceCapable object to the specified
4379: * PrintWriter.
4380: * @param pc The PersistenceCapable object
4381: * @param out The PrintWriter
4382: **/
4383: private static void dumpPC(PersistenceCapable pc, PrintWriter out) {
4384: out.println(StringUtils.toJVMIDString(pc));
4385:
4386: if (pc == null) {
4387: return;
4388: }
4389:
4390: out.print("jdoStateManager = "
4391: + peekField(pc, "jdoStateManager"));
4392: out.print("jdoFlags = ");
4393: Object flagsObj = peekField(pc, "jdoFlags");
4394: if (flagsObj instanceof Byte) {
4395: out
4396: .println(jdoFlagsToString(((Byte) flagsObj)
4397: .byteValue()));
4398: } else {
4399: out.println(flagsObj);
4400: }
4401:
4402: Class c = pc.getClass();
4403:
4404: do {
4405: String[] fieldNames = HELPER.getFieldNames(c);
4406:
4407: for (int i = 0; i < fieldNames.length; ++i) {
4408: out.print(fieldNames[i]);
4409: out.print(" = ");
4410: out.println(peekField(pc, fieldNames[i]));
4411: }
4412:
4413: c = c.getSuperclass();
4414: } while (c != null
4415: && PersistenceCapable.class.isAssignableFrom(c));
4416: }
4417:
4418: /**
4419: * Utility to dump the contents of the StateManager.
4420: *
4421: * @param out PrintWriter to dump to
4422: **/
4423: public void dump(PrintWriter out) {
4424: out.println("myPM = " + myOM);
4425: out.println("myID = " + myID);
4426: out.println("myLC = " + myLC);
4427: out.println("cmd = " + cmd);
4428: out.println("srm = " + getStoreManager());
4429: out.println("fieldCount = " + getHighestFieldNumber());
4430: out.println("dirty = " + dirty);
4431: out.println("flushing = " + flushing);
4432: out.println("changingState = " + changingState);
4433: out.println("postLoadPending = " + postLoadPending);
4434: out.println("disconnecting = " + disconnecting);
4435: out.println("dirtyFields = "
4436: + StringUtils.booleanArrayToString(dirtyFields));
4437: out
4438: .println("getSecondClassMutableFields() = "
4439: + StringUtils
4440: .booleanArrayToString(getSecondClassMutableFields()));
4441: out.println("getAllFieldNumbers() = "
4442: + StringUtils.intArrayToString(getAllFieldNumbers()));
4443: out
4444: .println("secondClassMutableFieldNumbers = "
4445: + StringUtils
4446: .intArrayToString(getSecondClassMutableFieldNumbers()));
4447:
4448: out.println();
4449: out.println("jdoFlags = " + jdoFlagsToString(jdoDfgFlags));
4450: out.println("loadedFields = "
4451: + StringUtils.booleanArrayToString(loadedFields));
4452: out.print("myPC = ");
4453: dumpPC(myPC, out);
4454:
4455: out.println();
4456: out.println("savedFlags = " + jdoFlagsToString(savedFlags));
4457: out.println("savedLoadedFields = "
4458: + StringUtils.booleanArrayToString(savedLoadedFields));
4459:
4460: out.print("savedImage = ");
4461: dumpPC(savedImage, out);
4462: }
4463:
4464: /**
4465: * Utility to convert JDO specific flags to a String.
4466: * @param flags The JDO flags
4467: * @return String version
4468: **/
4469: private static String jdoFlagsToString(byte flags) {
4470: switch (flags) {
4471: case PersistenceCapable.LOAD_REQUIRED:
4472: return "LOAD_REQUIRED";
4473: case PersistenceCapable.READ_OK:
4474: return "READ_OK";
4475: case PersistenceCapable.READ_WRITE_OK:
4476: return "READ_WRITE_OK";
4477: default:
4478: return "???";
4479: }
4480: }
4481: }
|