Source Code Cross Referenced for JDOStateManagerImpl.java in  » Database-ORM » JPOX » org » jpox » state » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » JPOX » org.jpox.state 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.