Source Code Cross Referenced for ObjectManagerImpl.java in  » Database-ORM » JPOX » org » jpox » 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 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**********************************************************************
0002:        Copyright (c) 2006 Andy Jefferson 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:        Contributors:
0016:        2007 Xuan Baldauf - added the use of srm.findObject() to cater for different object lifecycle management policies (in RDBMS and DB4O databases)
0017:        2007 Xuan Baldauf - changes to allow the disabling of clearing of fields when transitioning from PERSISTENT_NEW to TRANSIENT.
0018:             ...
0019:         **********************************************************************/package org.jpox;
0020:
0021:        import java.lang.reflect.Constructor;
0022:        import java.lang.reflect.Modifier;
0023:        import java.util.ArrayList;
0024:        import java.util.Collection;
0025:        import java.util.HashMap;
0026:        import java.util.HashSet;
0027:        import java.util.Iterator;
0028:        import java.util.List;
0029:        import java.util.Map;
0030:        import java.util.Set;
0031:
0032:        import org.jpox.api.ApiAdapter;
0033:        import org.jpox.cache.CachedPC;
0034:        import org.jpox.cache.Level1Cache;
0035:        import org.jpox.cache.Level2Cache;
0036:        import org.jpox.exceptions.ClassNotDetachableException;
0037:        import org.jpox.exceptions.ClassNotPersistableException;
0038:        import org.jpox.exceptions.ClassNotResolvedException;
0039:        import org.jpox.exceptions.JPOXException;
0040:        import org.jpox.exceptions.JPOXObjectNotFoundException;
0041:        import org.jpox.exceptions.JPOXOptimisticException;
0042:        import org.jpox.exceptions.JPOXUserException;
0043:        import org.jpox.exceptions.NoPersistenceInformationException;
0044:        import org.jpox.exceptions.ObjectDetachedException;
0045:        import org.jpox.exceptions.RollbackStateTransitionException;
0046:        import org.jpox.exceptions.TransactionActiveOnCloseException;
0047:        import org.jpox.exceptions.TransactionNotActiveException;
0048:        import org.jpox.identity.OID;
0049:        import org.jpox.identity.OIDFactory;
0050:        import org.jpox.jdo.JDOAdapter;
0051:        import org.jpox.jdo.JPOXJDOHelper;
0052:        import org.jpox.jdo.exceptions.CommitStateTransitionException;
0053:        import org.jpox.metadata.AbstractClassMetaData;
0054:        import org.jpox.metadata.AbstractMemberMetaData;
0055:        import org.jpox.metadata.IdentityType;
0056:        import org.jpox.metadata.MetaDataManager;
0057:        import org.jpox.metadata.TransactionType;
0058:        import org.jpox.state.CallbackHandler;
0059:        import org.jpox.state.DetachState;
0060:        import org.jpox.store.Extent;
0061:        import org.jpox.state.FetchPlanState;
0062:        import org.jpox.state.StateManagerFactory;
0063:        import org.jpox.store.FieldValues;
0064:        import org.jpox.store.SCOID;
0065:        import org.jpox.store.query.Query;
0066:        import org.jpox.store.StoreManager;
0067:        import org.jpox.util.JPOXLogger;
0068:        import org.jpox.util.Localiser;
0069:        import org.jpox.util.StringUtils;
0070:        import org.jpox.util.WeakValueMap;
0071:
0072:        /**
0073:         * Representation of an ObjectManager.
0074:         * An object manager provides management for the persistence of objects into a datastore
0075:         * and the retrieval of these objects using various methods. 
0076:         * <h3>Caching</h3>
0077:         * <p>
0078:         * An ObjectManager has its own Level 1 cache. This stores objects against their identity. The Level 1 Cache
0079:         * is typically a weak referenced map and so cached objects can be garbage collected. Objects are similarly
0080:         * placed in the Level 2 cache of the owning PersistenceManagerFactory. This Level 2 cache caches objects across
0081:         * multiple ObjectManagers, and so we can use this to get objects retrieved by other ObjectManagers. The Caches
0082:         * exist for performance enhancement.
0083:         * <h3>Transactions</h3>
0084:         * <p>
0085:         * Has a single transaction (the "current" transaction).
0086:         * </p>
0087:         * <h3>Persisted Objects</h3>
0088:         * <p>
0089:         * When an object involved in the current transaction it is <i>enlisted</i> (calling enlistInTransaction()).
0090:         * Its' identity is saved (in "txEnlistedIds") for use later in any "persistenceByReachability" process run at commit.
0091:         * Any object that is passed via makePersistent() will be stored (as an identity) in "txKnownPersistedIds" and objects 
0092:         * persisted due to reachability from these objects will also have their identity stored (in "txFlushedNewIds").
0093:         * All of this information is used in the "persistence-by-reachability-at-commit" process which detects if some objects
0094:         * originally persisted are no longer reachable and hence should not be persistent after all.
0095:         * </p>
0096:         * <h3>Queries</h3>
0097:         * <p>
0098:         * ALl queries run during a transaction are registered here. When the transaction commits/rolls back then
0099:         * all queries are notified that the connection is closing so that they can read in any remaining results
0100:         * (and the queries remain usable after). When this manager closes all queries registered here will also be closed
0101:         * meaning that a query has its lifetime defined by the owning PersistenceManager.
0102:         * </p>
0103:         * 
0104:         * @version $Revision: 1.100 $
0105:         */
0106:        public class ObjectManagerImpl implements  ObjectManager {
0107:            /** Localisation utility for output messages */
0108:            protected static final Localiser LOCALISER = Localiser
0109:                    .getInstance("org.jpox.Localisation");
0110:
0111:            /** Factory for this ObjectManager. */
0112:            private final ObjectManagerFactoryImpl omf;
0113:
0114:            /** The owning PersistenceManager/EntityManager object. */
0115:            private Object owner;
0116:
0117:            /** State variable for whether the ObjectManager is closed. */
0118:            private boolean closed;
0119:
0120:            /** Current FetchPlan for the ObjectManager. */
0121:            private FetchPlan fetchPlan;
0122:
0123:            /** The ClassLoader resolver to use for class loading issues. */
0124:            private ClassLoaderResolver clr = null;
0125:
0126:            /** Callback handler for this ObjectManager. */
0127:            private CallbackHandler callbacks;
0128:
0129:            //-- cache level 1 --//
0130:            /** Level 1 Cache */
0131:            private Level1Cache cache;
0132:
0133:            /** Whether to ignore the cache */
0134:            private boolean ignoreCache;
0135:
0136:            //-- convenience operations --//
0137:            /** State variable used when searching for the StateManager for an object, representing the object. */
0138:            private Object objectLookingForStateManager = null;
0139:
0140:            /** State variable used when searching for the StateManager for an object, representing the StateManager. */
0141:            private StateManager foundStateManager = null;
0142:
0143:            //-- transaction --//
0144:            /** Current transaction */
0145:            private Transaction tx;
0146:
0147:            /** Cache of StateManagers enlisted in the current transaction, keyed by the object id. */
0148:            private Map enlistedSMCache = new WeakValueMap();
0149:
0150:            /** Set of the object ids for all objects enlisted in this transaction. Used in reachability at commit to determine what to check. */
0151:            private Set txEnlistedIds = new HashSet();
0152:
0153:            /** List of StateManagers for all current dirty objects managed by this ObjectManager. */
0154:            private List dirtySMs = new ArrayList(10);
0155:
0156:            /** List of StateManagers for all current dirty objects made dirty by reachability. Only used by delete process currently. */
0157:            private List indirectDirtySMs = new ArrayList();
0158:
0159:            /** StateManagers of all objects that have had managed relationships changed in the last flush-cycle. */
0160:            HashSet managedRelationDirtySMs = null;
0161:
0162:            /** State indicator whether we are currently managing the relations. */
0163:            private boolean managingRelations = false;
0164:
0165:            /** Set of Object Ids of objects persisted using persistObject, or known as already persistent in the current transaction. */
0166:            private Set txKnownPersistedIds = new HashSet();
0167:
0168:            /** Set of Object Ids of objects deleted using deleteObject. */
0169:            private Set txKnownDeletedIds = new HashSet();
0170:
0171:            /** Set of Object Ids of objects newly persistent in the current transaction */
0172:            private Set txFlushedNewIds = new HashSet();
0173:
0174:            //-- configuration --//
0175:            /** Whether to detach objects on close of the ObjectManager. */
0176:            private boolean detachOnClose;
0177:
0178:            /** Whether to detach all objects on commit of the transaction. */
0179:            private boolean detachAllOnCommit;
0180:
0181:            /** Whether to copy objects on attaching. */
0182:            private boolean copyOnAttach;
0183:
0184:            /** Whether to operate multithreaded */
0185:            private boolean multithreaded;
0186:
0187:            /** State variable for whether the ObjectManager is currently flushing its operations. */
0188:            private boolean flushing = false;
0189:
0190:            /** State variable for whether we are currently running detachAllOnCommit. */
0191:            private boolean runningDetachAllOnCommit = false;
0192:
0193:            /**
0194:             * Map containing any thread-specific state information, keyed by the thread number string.
0195:             * Used where we don't want to pass information down through a large number of method calls.
0196:             */
0197:            private HashMap contextInfoByThread = new HashMap();
0198:
0199:            /**
0200:             * Constructor.
0201:             * @param omf Object Manager Factory
0202:             * @param owner The owning PM/EM
0203:             * @param userName Username for the datastore
0204:             * @param password Password for the datastore
0205:             * @throws JPOXUserException if an error occurs allocating the necessary requested components
0206:             */
0207:            public ObjectManagerImpl(ObjectManagerFactoryImpl omf,
0208:                    Object owner, String userName, String password) {
0209:                this .owner = owner;
0210:                this .omf = omf;
0211:                closed = false;
0212:
0213:                // Set up class loading
0214:                ClassLoader contextLoader = Thread.currentThread()
0215:                        .getContextClassLoader();
0216:                clr = omf.getOMFContext().getClassLoaderResolver(contextLoader);
0217:                try {
0218:                    ImplementationCreator ic = omf.getOMFContext()
0219:                            .getImplementationCreator();
0220:                    if (ic != null) {
0221:                        clr.registerClassLoader(ic.getClassLoader());
0222:                    }
0223:                } catch (Exception ex) {
0224:                    // do nothing
0225:                }
0226:
0227:                // Set up StoreManager, using that of the OMF
0228:                if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0229:                    JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010000", this ,
0230:                            omf.getOMFContext().getStoreManager()));
0231:                }
0232:
0233:                PersistenceConfiguration config = omf.getOMFContext()
0234:                        .getPersistenceConfiguration();
0235:
0236:                // Configuration from OMF
0237:                setIgnoreCache(config.getIgnoreCache());
0238:                setDetachOnClose(config.getDetachOnClose());
0239:                setDetachAllOnCommit(config.getDetachAllOnCommit());
0240:                setCopyOnAttach(config.getCopyOnAttach());
0241:                setMultithreaded(config.getMultithreaded());
0242:
0243:                // Set up FetchPlan
0244:                fetchPlan = new FetchPlan(omf, clr).setMaxFetchDepth(omf
0245:                        .getMaxFetchDepth());
0246:
0247:                // Set up the Level 1 Cache
0248:                initialiseLevel1Cache();
0249:
0250:                // Set up the transaction suitable for this ObjectManagerFactory
0251:                if (TransactionType.JTA.toString().equalsIgnoreCase(
0252:                        config.getTransactionType())) {
0253:                    tx = new JTATransactionImpl(this );
0254:                } else {
0255:                    tx = new TransactionImpl(this );
0256:                }
0257:            }
0258:
0259:            /**
0260:             * Method to initialise the L1 cache.
0261:             * @throws JPOXUserException if an error occurs setting up the L1 cache
0262:             */
0263:            protected void initialiseLevel1Cache() {
0264:                // Find the L1 cache class name from its plugin name
0265:                String level1Type = omf.getPersistenceConfiguration()
0266:                        .getCacheLevel1Type();
0267:                String level1ClassName = getOMFContext().getPluginManager()
0268:                        .getAttributeValueForExtension("org.jpox.cache_level1",
0269:                                "name", level1Type, "class-name");
0270:                if (level1ClassName == null) {
0271:                    // Plugin of this name not found
0272:                    throw new JPOXUserException(LOCALISER.msg("003001",
0273:                            level1Type)).setFatal();
0274:                }
0275:
0276:                try {
0277:                    // Create an instance of the L1 Cache
0278:                    Class level1CacheClass = clr.classForName(level1ClassName,
0279:                            OMFContext.class.getClassLoader());
0280:                    cache = (Level1Cache) level1CacheClass.newInstance();
0281:                    if (JPOXLogger.CACHE.isDebugEnabled()) {
0282:                        JPOXLogger.CACHE.debug(LOCALISER.msg("003003",
0283:                                level1Type));
0284:                    }
0285:                } catch (Exception e) {
0286:                    // Class name for this L1 cache plugin is not found!
0287:                    throw new JPOXUserException(LOCALISER.msg("003002",
0288:                            level1Type, level1ClassName), e).setFatal();
0289:                }
0290:            }
0291:
0292:            /**
0293:             * Accessor for whether this ObjectManager is closed.
0294:             * @return Whether this manager is closed.
0295:             */
0296:            public boolean isClosed() {
0297:                return closed;
0298:            }
0299:
0300:            /**
0301:             * Accessor for the ClassLoaderResolver
0302:             * @return the ClassLoaderResolver
0303:             */
0304:            public ClassLoaderResolver getClassLoaderResolver() {
0305:                return clr;
0306:            }
0307:
0308:            /**
0309:             * Accessor for the Store Manager.
0310:             * @return StoreManager
0311:             */
0312:            public StoreManager getStoreManager() {
0313:                return getOMFContext().getStoreManager();
0314:            }
0315:
0316:            /**
0317:             * Accessor for the API adapter.
0318:             * @return API adapter.
0319:             */
0320:            public ApiAdapter getApiAdapter() {
0321:                return getOMFContext().getApiAdapter();
0322:            }
0323:
0324:            /**
0325:             * Acessor for the current FetchPlan
0326:             * @return FetchPlan
0327:             * @since 1.1
0328:             */
0329:            public FetchPlan getFetchPlan() {
0330:                // TODO this should be enabled, but with detachAllOnCommit=true it raises exception 
0331:                //assertIsOpen();
0332:                return fetchPlan;
0333:            }
0334:
0335:            /**
0336:             * Method to return the owner PM/EM object.
0337:             * For JDO this will return the PersistenceManager owning this ObjectManager.
0338:             * For JPA this will return the EntityManager owning this ObjectManager.
0339:             * @return The owner manager object
0340:             */
0341:            public Object getOwner() {
0342:                return owner;
0343:            }
0344:
0345:            /**
0346:             * Gets the context in which this ObjectManager is running
0347:             * @return Returns the context.
0348:             */
0349:            public OMFContext getOMFContext() {
0350:                return omf.getOMFContext();
0351:            }
0352:
0353:            /**
0354:             * Accessor for the MetaDataManager for this ObjectManager (and its factory).
0355:             * This is used as the interface to MetaData in the ObjectManager/Factory.
0356:             * @return Returns the MetaDataManager.
0357:             */
0358:            public MetaDataManager getMetaDataManager() {
0359:                return getOMFContext().getMetaDataManager();
0360:            }
0361:
0362:            /**
0363:             * Mutator for whether the Persistence Manager is multithreaded.
0364:             * @param flag Whether to run multithreaded.
0365:             */
0366:            public void setMultithreaded(boolean flag) {
0367:                assertIsOpen();
0368:                this .multithreaded = flag;
0369:            }
0370:
0371:            /**
0372:             * Accessor for whether the Persistence Manager is multithreaded.
0373:             * @return Whether to run multithreaded.
0374:             */
0375:            public boolean getMultithreaded() {
0376:                assertIsOpen();
0377:                return this .multithreaded;
0378:            }
0379:
0380:            /**
0381:             * Mutator for whether to detach objects on close of the ObjectManager.
0382:             * <B>This is a JPOX extension and will not work when in JCA mode.</B>
0383:             * @param flag Whether to detach on close.
0384:             */
0385:            public void setDetachOnClose(boolean flag) {
0386:                assertIsOpen();
0387:                this .detachOnClose = flag;
0388:            }
0389:
0390:            /**
0391:             * Accessor for whether to detach objects on close of the ObjectManager.
0392:             * <B>This is a JPOX extension and will not work when in JCA mode.</B>
0393:             * @return Whether to detach on close.
0394:             */
0395:            public boolean getDetachOnClose() {
0396:                assertIsOpen();
0397:                return detachOnClose;
0398:            }
0399:
0400:            /**
0401:             * Mutator for whether to detach all objects on commit of the transaction.
0402:             * @param flag Whether to detach all on commit.
0403:             */
0404:            public void setDetachAllOnCommit(boolean flag) {
0405:                assertIsOpen();
0406:                this .detachAllOnCommit = flag;
0407:            }
0408:
0409:            /**
0410:             * Accessor for whether to detach all objects on commit of the transaction.
0411:             * @return Whether to detach all on commit.
0412:             */
0413:            public boolean getDetachAllOnCommit() {
0414:                assertIsOpen();
0415:                return detachAllOnCommit;
0416:            }
0417:
0418:            /**
0419:             * Mutator for whether to copy on attaching.
0420:             * @param flag Whether to copy on attaching
0421:             */
0422:            public void setCopyOnAttach(boolean flag) {
0423:                assertIsOpen();
0424:                this .copyOnAttach = flag;
0425:            }
0426:
0427:            /**
0428:             * Accessor for whether to copy on attaching.
0429:             * @return Whether to copy on attaching
0430:             */
0431:            public boolean getCopyOnAttach() {
0432:                assertIsOpen();
0433:                return copyOnAttach;
0434:            }
0435:
0436:            /**
0437:             * Mutator for whether to ignore the cache.
0438:             * @param flag Whether to ignore the cache.
0439:             */
0440:            public void setIgnoreCache(boolean flag) {
0441:                assertIsOpen();
0442:                this .ignoreCache = flag;
0443:            }
0444:
0445:            /**
0446:             * Accessor for whether to ignore the cache.
0447:             * @return Whether to ignore the cache.
0448:             */
0449:            public boolean getIgnoreCache() {
0450:                assertIsOpen();
0451:                return ignoreCache;
0452:            }
0453:
0454:            /**
0455:             * Whether the datastore operations are delayed until commit/flush. 
0456:             * In optimistic transactions this is automatically enabled. In datastore transactions there is a
0457:             * persistence property to enable it.
0458:             * If we are committing/flushing then will return false since the delay is no longer required.
0459:             * @return true if datastore operations are delayed until commit/flush
0460:             */
0461:            public boolean isDelayDatastoreOperationsEnabled() {
0462:                if (flushing || tx.isCommitting()) {
0463:                    // Already sending to the datastore so return false to not confuse things
0464:                    return false;
0465:                }
0466:
0467:                if (tx.getOptimistic()) {
0468:                    return true;
0469:                } else {
0470:                    return getOMFContext().getPersistenceConfiguration()
0471:                            .getDatastoreTransactionsDelayOperations();
0472:                }
0473:            }
0474:
0475:            /**
0476:             * Tests whether this persistable object is in the process of being inserted.
0477:             * @param pc the object to verify the status
0478:             * @return true if this instance is inserting.
0479:             */
0480:            public boolean isInserting(Object pc) {
0481:                StateManager sm = findStateManager(pc);
0482:                if (sm == null) {
0483:                    return false;
0484:                }
0485:                return sm.isInserting();
0486:            }
0487:
0488:            /**
0489:             * Convenience method to find if the specified field of the specified object has been inserted yet.
0490:             * @param pc The object
0491:             * @param fieldNumber The absolute field number
0492:             * @return Whether it has been inserted into the datastore
0493:             */
0494:            public boolean isInserted(Object pc, int fieldNumber) {
0495:                StateManager sm = findStateManager(pc);
0496:                if (sm == null) {
0497:                    return false;
0498:                }
0499:                return sm.isInserted(fieldNumber);
0500:            }
0501:
0502:            /**
0503:             * Convenience method to find if the specified object is inserted down to the specified class level yet.
0504:             * @param pc The object
0505:             * @param className Name of the class that we want to check the insertion level to
0506:             * @return Whether it has been inserted into the datastore
0507:             */
0508:            public boolean isInserted(Object pc, String className) {
0509:                StateManager sm = findStateManager(pc);
0510:                if (sm == null) {
0511:                    return false;
0512:                }
0513:                return sm.isInserted(className);
0514:            }
0515:
0516:            /**
0517:             * Accessor for the current transaction.
0518:             * @return The transaction
0519:             */
0520:            public Transaction getTransaction() {
0521:                assertIsOpen();
0522:                return tx;
0523:            }
0524:
0525:            /**
0526:             * Method to enlist the specified StateManager in the current transaction.
0527:             * @param sm The StateManager
0528:             */
0529:            public synchronized void enlistInTransaction(StateManager sm) {
0530:                assertActiveTransaction();
0531:                if (JPOXLogger.TRANSACTION.isDebugEnabled()) {
0532:                    JPOXLogger.TRANSACTION.debug(LOCALISER.msg("015017",
0533:                            StringUtils.toJVMIDString(sm.getObject()), sm
0534:                                    .getInternalObjectId().toString()));
0535:                }
0536:
0537:                if (omf.getPersistenceByReachabilityAtCommit()) {
0538:                    if (getApiAdapter().isNew(sm.getObject())) {
0539:                        // Add this object to the list of new objects in this transaction
0540:                        txFlushedNewIds.add(sm.getInternalObjectId());
0541:                    } else if (getApiAdapter().isPersistent(sm.getObject())
0542:                            && !getApiAdapter().isDeleted(sm.getObject())) {
0543:                        // Add this object to the list of known valid persisted objects (unless it is a known new object)
0544:                        if (!txFlushedNewIds.contains(sm.getInternalObjectId())) {
0545:                            txKnownPersistedIds.add(sm.getInternalObjectId());
0546:                        }
0547:                    }
0548:                }
0549:
0550:                // Add the object to those enlisted
0551:                if (omf.getPersistenceByReachabilityAtCommit()
0552:                        && !runningPBRAtCommit) {
0553:                    // Keep a note of the id for use by persistence-by-reachability-at-commit
0554:                    txEnlistedIds.add(sm.getInternalObjectId());
0555:                }
0556:                enlistedSMCache.put(sm.getInternalObjectId(), sm);
0557:            }
0558:
0559:            /**
0560:             * Method to evict the specified StateManager from the current transaction.
0561:             * @param sm The StateManager
0562:             */
0563:            public synchronized void evictFromTransaction(StateManager sm) {
0564:                if (JPOXLogger.TRANSACTION.isDebugEnabled()) {
0565:                    JPOXLogger.TRANSACTION.debug(LOCALISER.msg("015019",
0566:                            StringUtils.toJVMIDString(sm.getObject()), sm
0567:                                    .getInternalObjectId().toString()));
0568:                }
0569:
0570:                if (enlistedSMCache.remove(sm.getInternalObjectId()) == null) {
0571:                    //probably because the object was garbage collected
0572:                    if (JPOXLogger.TRANSACTION.isDebugEnabled()) {
0573:                        JPOXLogger.TRANSACTION.debug(LOCALISER.msg("010023",
0574:                                StringUtils.toJVMIDString(sm.getObject()), sm
0575:                                        .getInternalObjectId()));
0576:                    }
0577:                }
0578:            }
0579:
0580:            /**
0581:             * Method to return if an object is enlisted in the current transaction.
0582:             * This is only of use when running "persistence-by-reachability-at-commit"
0583:             * @param id Identity for the object
0584:             * @return Whether it is enlisted in the current transaction
0585:             */
0586:            public boolean isEnlistedInTransaction(Object id) {
0587:                if (id == null) {
0588:                    return false;
0589:                }
0590:                return txEnlistedIds.contains(id);
0591:            }
0592:
0593:            /**
0594:             * Method to add the object managed by the specified StateManager to the cache.
0595:             * @param sm The StateManager
0596:             */
0597:            public synchronized void addStateManager(StateManager sm) {
0598:                // Add to the Level 1/2 Caches
0599:                putObjectIntoCache(sm, true, true);
0600:            }
0601:
0602:            /**
0603:             * Method to remove the object managed by the specified StateManager from the cache.
0604:             * @param sm The StateManager
0605:             */
0606:            public synchronized void removeStateManager(StateManager sm) {
0607:                Object pc = sm.getObject();
0608:
0609:                // Remove from the Level 1 Cache
0610:                removeObjectFromCache(pc, sm.getInternalObjectId(), true, false);
0611:
0612:                // Remove it from any transaction
0613:                enlistedSMCache.remove(sm.getInternalObjectId());
0614:            }
0615:
0616:            /**
0617:             * Accessor for the StateManager of an object given the object id.
0618:             * @param id Id of the object.
0619:             * @return The StateManager
0620:             */
0621:            public synchronized StateManager getStateManagerById(Object id) {
0622:                assertIsOpen();
0623:                return findStateManager(getObjectFromCache(id));
0624:            }
0625:
0626:            /**
0627:             * Method to find the StateManager for an object.
0628:             * @param pc The object we require the StateManager for.
0629:             * @return The StateManager, null if StateManager not found.
0630:             *     See JDO spec for the calling behavior when null is returned
0631:             */
0632:            public synchronized StateManager findStateManager(Object pc) {
0633:                StateManager sm = null;
0634:                Object previousLookingFor = objectLookingForStateManager;
0635:                StateManager previousFound = foundStateManager;
0636:                try {
0637:                    objectLookingForStateManager = pc;
0638:                    foundStateManager = null;
0639:                    // We call "ObjectManagerHelper.getObjectManager(pc)".
0640:                    // This then calls "JDOHelper.getPersistenceManager(pc)".
0641:                    // Which calls "StateManager.getPersistenceManager(pc)".
0642:                    // That then calls "hereIsStateManager(sm, pc)" which sets "foundStateManager".
0643:                    ObjectManager om = ObjectManagerHelper.getObjectManager(pc);
0644:                    if (om != null && this  != om) {
0645:                        throw new JPOXUserException(LOCALISER.msg("010007",
0646:                                getApiAdapter().getIdForObject(pc)));
0647:                    }
0648:                    sm = foundStateManager;
0649:                } finally {
0650:                    objectLookingForStateManager = previousLookingFor;
0651:                    foundStateManager = previousFound;
0652:                }
0653:                return sm;
0654:            }
0655:
0656:            /**
0657:             * Method to add the StateManager for an object to this ObjectManager's list.
0658:             * @param sm The StateManager
0659:             * @param pc The object managed by the StateManager
0660:             */
0661:            public synchronized void hereIsStateManager(StateManager sm,
0662:                    Object pc) {
0663:                if (objectLookingForStateManager == pc) {
0664:                    foundStateManager = sm;
0665:                }
0666:            }
0667:
0668:            /**
0669:             * Method to close the Persistence Manager.
0670:             */
0671:            public synchronized void close() {
0672:                if (closed) {
0673:                    throw new JPOXUserException(LOCALISER.msg("010002"));
0674:                }
0675:                if (tx.isActive()) {
0676:                    throw new TransactionActiveOnCloseException(this );
0677:                }
0678:
0679:                if (detachOnClose) {
0680:                    // JPOX "detach-on-close" extension, detaching all currently cached objects.
0681:                    performDetachOnClose();
0682:                }
0683:
0684:                ObjectManagerListener[] listener = omf.getOMFContext()
0685:                        .getObjectManagerListeners();
0686:
0687:                for (int i = 0; i < listener.length; i++) {
0688:                    listener[i].objectManagerPreClose(this );
0689:                }
0690:
0691:                // Disconnect remaining resources
0692:                disconnectSMCache();
0693:                disconnectLifecycleListener();
0694:
0695:                // Reset the Fetch Plan to its DFG setting
0696:                getFetchPlan().clearGroups().addGroup(FetchPlan.DEFAULT);
0697:            }
0698:
0699:            public void postClose() {
0700:                closed = true;
0701:                tx = null;
0702:
0703:                if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0704:                    JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010001", this ));
0705:                }
0706:            }
0707:
0708:            /**
0709:             * Disconnect SM instances, clear cache and reset settings 
0710:             */
0711:            public void disconnectSMCache() {
0712:                // Clear out the cache (use separate list since sm.disconnect will remove the object from "cache" so we avoid
0713:                // any ConcurrentModification issues)
0714:                Collection cachedSMsClone = new HashSet(cache.values());
0715:                Iterator iter = cachedSMsClone.iterator();
0716:                while (iter.hasNext()) {
0717:                    StateManager sm = (StateManager) iter.next();
0718:                    if (sm != null) {
0719:                        sm.disconnect();
0720:                    }
0721:                }
0722:                cache.clear();
0723:                if (JPOXLogger.CACHE.isDebugEnabled()) {
0724:                    JPOXLogger.CACHE.debug(LOCALISER.msg("003011"));
0725:                }
0726:
0727:            }
0728:
0729:            // ----------------------------- Lifecycle Methods ------------------------------------
0730:
0731:            /**
0732:             * Internal method to evict an object from L1 cache.
0733:             * @param obj The object
0734:             * @throws JPOXException if an error occurs evicting the object
0735:             */
0736:            public void evictObject(Object obj) {
0737:                if (obj == null) {
0738:                    return;
0739:                }
0740:
0741:                try {
0742:                    clr.setPrimary(obj.getClass().getClassLoader());
0743:                    assertClassPersistable(obj.getClass());
0744:                    assertNotDetached(obj);
0745:
0746:                    // we do not directly remove from cache level 1 here. The cache level 1 will be evicted 
0747:                    // automatically on garbage collection, if the object can be evicted. it means not all
0748:                    // jdo states allows the object to be evicted.
0749:                    StateManager sm = findStateManager(obj);
0750:                    if (sm == null) {
0751:                        throw new JPOXUserException(LOCALISER.msg("010007",
0752:                                getApiAdapter().getIdForObject(obj)));
0753:                    }
0754:                    sm.evict();
0755:                } finally {
0756:                    clr.unsetPrimary();
0757:                }
0758:            }
0759:
0760:            /**
0761:             * Method to evict all objects of the specified type (and optionaly its subclasses).
0762:             * @param cls Type of persistable object
0763:             * @param subclasses Whether to include subclasses
0764:             */
0765:            public void evictObjects(Class cls, boolean subclasses) {
0766:                assertIsOpen();
0767:
0768:                ArrayList stateManagersToEvict = new ArrayList();
0769:                stateManagersToEvict.addAll(cache.values());
0770:                Iterator smIter = stateManagersToEvict.iterator();
0771:                while (smIter.hasNext()) {
0772:                    StateManager sm = (StateManager) smIter.next();
0773:                    Object pc = sm.getObject();
0774:                    boolean evict = false;
0775:                    if (!subclasses && pc.getClass() == cls) {
0776:                        evict = true;
0777:                    } else if (subclasses
0778:                            && cls.isAssignableFrom(pc.getClass())) {
0779:                        evict = true;
0780:                    }
0781:
0782:                    if (evict) {
0783:                        sm.evict();
0784:                        removeObjectFromCache(pc, getApiAdapter()
0785:                                .getIdForObject(pc), true, false);
0786:                    }
0787:                }
0788:            }
0789:
0790:            /**
0791:             * Method to evict all current objects from L1 cache.
0792:             */
0793:            public synchronized void evictAllObjects() {
0794:                assertIsOpen();
0795:
0796:                // All persistent non-transactional instances should be evicted here, but not yet supported
0797:                ArrayList stateManagersToEvict = new ArrayList();
0798:                stateManagersToEvict.addAll(cache.values());
0799:
0800:                // Evict StateManagers and remove objects from cache
0801:                // Performed in separate loop to avoid ConcurrentModificationException
0802:                Iterator smIter = stateManagersToEvict.iterator();
0803:                while (smIter.hasNext()) {
0804:                    StateManager sm = (StateManager) smIter.next();
0805:                    Object pc = sm.getObject();
0806:                    sm.evict();
0807:
0808:                    // Evict from L1
0809:                    removeObjectFromCache(pc, getApiAdapter()
0810:                            .getIdForObject(pc), true, false);
0811:                }
0812:            }
0813:
0814:            /**
0815:             * Method to do a refresh of an object, updating it from its
0816:             * datastore representation. Also updates the object in the L1/L2 caches.
0817:             * @param obj The Object
0818:             */
0819:            public void refreshObject(Object obj) {
0820:                if (obj == null) {
0821:                    return;
0822:                }
0823:                try {
0824:                    clr.setPrimary(obj.getClass().getClassLoader());
0825:                    assertClassPersistable(obj.getClass());
0826:                    assertNotDetached(obj);
0827:
0828:                    StateManager sm = findStateManager(obj);
0829:                    if (sm == null) {
0830:                        throw new JPOXUserException(LOCALISER.msg("010007",
0831:                                getApiAdapter().getIdForObject(obj)));
0832:                    }
0833:
0834:                    if (getApiAdapter().isPersistent(obj)
0835:                            && sm.isWaitingToBeFlushedToDatastore()) {
0836:                        // Persistent but not yet flushed so nothing to "refresh" from!
0837:                        return;
0838:                    }
0839:
0840:                    sm.refresh();
0841:
0842:                    // Refresh L2 cache object since it is a copy of this object
0843:                    putObjectIntoCache(sm, false, true);
0844:                } finally {
0845:                    clr.unsetPrimary();
0846:                }
0847:            }
0848:
0849:            /**
0850:             * Method to do a refresh of all objects.
0851:             * @throws JPOXUserException thrown if instances could not be refreshed.
0852:             */
0853:            public synchronized void refreshAllObjects() {
0854:                assertIsOpen();
0855:
0856:                Set toRefresh = new HashSet();
0857:                toRefresh.addAll(enlistedSMCache.values());
0858:                toRefresh.addAll(dirtySMs);
0859:                toRefresh.addAll(indirectDirtySMs);
0860:                if (!tx.isActive()) {
0861:                    toRefresh.addAll(cache.values());
0862:                }
0863:
0864:                List failures = null;
0865:                Iterator iter = toRefresh.iterator();
0866:                while (iter.hasNext()) {
0867:                    try {
0868:                        Object obj = iter.next();
0869:                        StateManager sm;
0870:                        if (getApiAdapter().isPersistable(obj)) {
0871:                            sm = findStateManager(obj);
0872:                        } else {
0873:                            sm = (StateManager) obj;
0874:                        }
0875:                        sm.refresh();
0876:
0877:                        // Refresh L2 cache object since it is a copy of this object
0878:                        putObjectIntoCache(sm, false, true);
0879:                    } catch (RuntimeException e) {
0880:                        if (failures == null) {
0881:                            failures = new ArrayList();
0882:                        }
0883:                        failures.add(e);
0884:                    }
0885:                }
0886:                if (failures != null && !failures.isEmpty()) {
0887:                    throw new JPOXUserException(LOCALISER.msg("011002"),
0888:                            (Exception[]) failures
0889:                                    .toArray(new Exception[failures.size()]));
0890:                }
0891:            }
0892:
0893:            /**
0894:             * Method to retrieve an object.
0895:             * @param obj The object
0896:             * @param fgOnly Whether to retrieve the current fetch group fields only
0897:             */
0898:            public void retrieveObject(Object obj, boolean fgOnly) {
0899:                if (obj == null) {
0900:                    return;
0901:                }
0902:                try {
0903:                    clr.setPrimary(obj.getClass().getClassLoader());
0904:                    assertClassPersistable(obj.getClass());
0905:                    assertNotDetached(obj);
0906:
0907:                    StateManager sm = findStateManager(obj);
0908:                    if (sm == null) {
0909:                        throw new JPOXUserException(LOCALISER.msg("010007",
0910:                                getApiAdapter().getIdForObject(obj)));
0911:                    }
0912:                    sm.retrieve(fgOnly);
0913:                } finally {
0914:                    clr.unsetPrimary();
0915:                }
0916:            }
0917:
0918:            /**
0919:             * Context info for a particular thread. Can be used for storing state information for the current
0920:             * thread where we don't want to pass it through large numbers of method calls (e.g persistence by
0921:             * reachability) where such argument passing would damage the structure of JPOX.
0922:             */
0923:            class ThreadContextInfo {
0924:                /** Map of attached PC object keyed by the id. Present when performing a persist operation. */
0925:                HashMap attachedPCById = null;
0926:            }
0927:
0928:            /**
0929:             * Accessor for the thread context information, for the current thread.
0930:             * If the current thread is not present, will add an info context for it.
0931:             * @return The thread context information
0932:             */
0933:            protected ThreadContextInfo getThreadContextInfo() {
0934:                String threadId = "" + Thread.currentThread().getId();
0935:                synchronized (contextInfoByThread) {
0936:                    ThreadContextInfo threadInfo = (ThreadContextInfo) contextInfoByThread
0937:                            .get(threadId);
0938:                    if (threadInfo == null) {
0939:                        // Thread not present, so add it
0940:                        threadInfo = new ThreadContextInfo();
0941:                        contextInfoByThread.put(threadId, threadInfo);
0942:                    }
0943:                    return threadInfo;
0944:                }
0945:            }
0946:
0947:            /**
0948:             * Method to remove the current thread context info for the current thread.
0949:             */
0950:            protected void removeThreadContextInfo() {
0951:                String threadId = "" + Thread.currentThread().getId();
0952:                synchronized (contextInfoByThread) {
0953:                    contextInfoByThread.remove(threadId);
0954:                }
0955:            }
0956:
0957:            /**
0958:             * Method to make an object persistent.
0959:             * NOT to be called by internal JPOX methods. Only callable by external APIs (JDO/JPA).
0960:             * @param obj The object
0961:             * @return The persisted object
0962:             * @throws JPOXUserException if the object is managed by a different manager
0963:             */
0964:            public Object persistObject(Object obj) {
0965:                assertIsOpen();
0966:                assertWritable();
0967:                if (obj == null) {
0968:                    return null;
0969:                }
0970:
0971:                // Make sure we have attachedPC lookup info initialised in case we need to attach objects multiple times
0972:                ThreadContextInfo threadInfo = getThreadContextInfo();
0973:                if (threadInfo.attachedPCById == null) {
0974:                    threadInfo.attachedPCById = new HashMap();
0975:                }
0976:
0977:                boolean detached = getApiAdapter().isDetached(obj);
0978:
0979:                // Persist the object
0980:                Object persistedPc = persistObjectInternal(obj, null, null, -1,
0981:                        StateManager.PC);
0982:
0983:                // If using reachability at commit and appropriate save it for reachability checks when we commit
0984:                StateManager sm = findStateManager(persistedPc);
0985:                if (sm != null) {
0986:                    if (indirectDirtySMs.contains(sm)) {
0987:                        dirtySMs.add(sm);
0988:                        indirectDirtySMs.remove(sm);
0989:                    } else if (!dirtySMs.contains(sm)) {
0990:                        dirtySMs.add(sm);
0991:                    }
0992:
0993:                    if (omf.getPersistenceByReachabilityAtCommit()) {
0994:                        if (detached || getApiAdapter().isNew(persistedPc)) {
0995:                            txKnownPersistedIds.add(sm.getInternalObjectId());
0996:                        }
0997:                    }
0998:                }
0999:
1000:                // Deallocate attached PC lookup
1001:                threadInfo.attachedPCById.clear();
1002:                threadInfo.attachedPCById = null;
1003:                removeThreadContextInfo(); // Currently remove the whole thread context since only used for this
1004:
1005:                return persistedPc;
1006:            }
1007:
1008:            /**
1009:             * Method to make an object persistent which should be called from internal calls only.
1010:             * All PM/EM calls should go via persistObject(Object obj).
1011:             * @param obj The object
1012:             * @param preInsertChanges Any changes to make before inserting
1013:             * @param ownerSM StateManager of the owner when embedded
1014:             * @param ownerFieldNum Field number in the owner where this is embedded (or -1 if not embedded)
1015:             * @param objectType Type of object (see org.jpox.StateManager, e.g StateManager.PC)
1016:             * @return The persisted object
1017:             * @throws JPOXUserException if the object is managed by a different manager
1018:             */
1019:            public Object persistObjectInternal(Object obj,
1020:                    FieldValues preInsertChanges, StateManager ownerSM,
1021:                    int ownerFieldNum, int objectType) {
1022:                if (obj == null) {
1023:                    return null;
1024:                }
1025:                assertIsOpen();
1026:                assertWritable();
1027:                // TODO Support embeddedOwner/objectType, so we can add StateManager for embedded objects here
1028:
1029:                try {
1030:                    clr.setPrimary(obj.getClass().getClassLoader());
1031:                    assertClassPersistable(obj.getClass());
1032:                    if (!getApiAdapter().isDetached(obj)
1033:                            && JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1034:                        JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010015",
1035:                                StringUtils.toJVMIDString(obj)));
1036:                    }
1037:                    ObjectManager om = ObjectManagerHelper
1038:                            .getObjectManager(obj);
1039:                    if (om != null && om != this ) {
1040:                        // Object managed by a different manager
1041:                        throw new JPOXUserException(LOCALISER
1042:                                .msg("010007", obj));
1043:                    }
1044:
1045:                    Object persistedPc = obj; // Persisted object is the passed in pc (unless being attached as a copy)
1046:                    if (getApiAdapter().isDetached(obj)) {
1047:                        // Detached : attach it
1048:                        assertDetachable(obj);
1049:                        if (copyOnAttach) {
1050:                            // Attach a copy and return the copy
1051:                            persistedPc = attachObjectCopy(obj, getApiAdapter()
1052:                                    .getIdForObject(obj) == null);
1053:                        } else {
1054:                            // Attach the object
1055:                            attachObject(obj, getApiAdapter().getIdForObject(
1056:                                    obj) == null);
1057:                            persistedPc = obj;
1058:                        }
1059:                    } else if (getApiAdapter().isTransactional(obj)
1060:                            && !getApiAdapter().isPersistent(obj)) {
1061:                        // TransientTransactional : persist it
1062:                        StateManager sm = findStateManager(obj);
1063:                        if (sm == null) {
1064:                            throw new JPOXUserException(LOCALISER.msg("010007",
1065:                                    getApiAdapter().getIdForObject(obj)));
1066:                        }
1067:                        sm.makePersistentTransactionalTransient();
1068:                    } else if (!getApiAdapter().isPersistent(obj)) {
1069:                        // Transient : persist it
1070:                        if (this .multithreaded) {
1071:                            synchronized (obj) {
1072:                                StateManager sm = findStateManager(obj);
1073:                                if (sm == null) {
1074:                                    if (objectType != StateManager.PC
1075:                                            && ownerSM != null) {
1076:                                        // SCO object
1077:                                        sm = StateManagerFactory
1078:                                                .newStateManagerForEmbedded(
1079:                                                        this , obj, false);
1080:                                        sm.addEmbeddedOwner(ownerSM,
1081:                                                ownerFieldNum);
1082:                                        sm.setPcObjectType(objectType);
1083:                                        sm.makePersistent();
1084:                                    } else {
1085:                                        // FCO object
1086:                                        sm = StateManagerFactory
1087:                                                .newStateManagerForPersistentNew(
1088:                                                        this , obj,
1089:                                                        preInsertChanges);
1090:                                        sm.makePersistent();
1091:                                    }
1092:                                } else {
1093:                                    if (sm.getReferencedPC() == null) {
1094:                                        // Persist it
1095:                                        sm.makePersistent();
1096:                                    } else {
1097:                                        // Being attached, so use the attached object
1098:                                        persistedPc = sm.getReferencedPC();
1099:                                    }
1100:                                }
1101:                            }
1102:                        } else {
1103:                            StateManager sm = findStateManager(obj);
1104:                            if (sm == null) {
1105:                                if (objectType != StateManager.PC
1106:                                        && ownerSM != null) {
1107:                                    // SCO Object
1108:                                    sm = StateManagerFactory
1109:                                            .newStateManagerForEmbedded(this ,
1110:                                                    obj, false);
1111:                                    sm.addEmbeddedOwner(ownerSM, ownerFieldNum);
1112:                                    sm.setPcObjectType(objectType);
1113:                                    sm.makePersistent();
1114:                                } else {
1115:                                    // FCO object
1116:                                    sm = StateManagerFactory
1117:                                            .newStateManagerForPersistentNew(
1118:                                                    this , obj, preInsertChanges);
1119:                                    sm.makePersistent();
1120:                                }
1121:                            } else {
1122:                                if (sm.getReferencedPC() == null) {
1123:                                    // Persist it
1124:                                    sm.makePersistent();
1125:                                } else {
1126:                                    // Being attached, so use the attached object
1127:                                    persistedPc = sm.getReferencedPC();
1128:                                }
1129:                            }
1130:                        }
1131:                    } else if (getApiAdapter().isPersistent(obj)
1132:                            && getApiAdapter().getIdForObject(obj) == null) {
1133:                        // Embedded/Serialised : have SM but no identity, allow persist in own right
1134:                        // Should we be making a copy of the object here ?
1135:                        if (this .multithreaded) {
1136:                            synchronized (obj) {
1137:                                StateManager sm = findStateManager(obj);
1138:                                sm.makePersistent();
1139:                            }
1140:                        } else {
1141:                            StateManager sm = findStateManager(obj);
1142:                            sm.makePersistent();
1143:                        }
1144:                    } else if (getApiAdapter().isDeleted(obj)) {
1145:                        // Deleted : (re)-persist it (permitted in JPA, but not JDO - see StateManager)
1146:                        StateManager sm = findStateManager(obj);
1147:                        sm.makePersistent();
1148:                    } else {
1149:                        if (getApiAdapter().isPersistent(obj)
1150:                                && getApiAdapter().isTransactional(obj)
1151:                                && getApiAdapter().isDirty(obj)
1152:                                && isDelayDatastoreOperationsEnabled()) {
1153:                            // Object provisionally persistent (but not in datastore) so re-run reachability maybe
1154:                            StateManager sm = findStateManager(obj);
1155:                            sm.makePersistent();
1156:                        }
1157:                    }
1158:
1159:                    return persistedPc;
1160:                } finally {
1161:                    clr.unsetPrimary();
1162:                }
1163:            }
1164:
1165:            /**
1166:             * Method to delete an object from the datastore.
1167:             * NOT to be called by internal JPOX methods. Only callable by external APIs (JDO/JPA).
1168:             * @param obj The object
1169:             */
1170:            public void deleteObject(Object obj) {
1171:                StateManager sm = findStateManager(obj);
1172:                if (sm != null) {
1173:                    // Add the object to the relevant list of dirty StateManagers
1174:                    if (indirectDirtySMs.contains(sm)) {
1175:                        // Object is dirty indirectly, but now user-requested so move to direct list of dirty objects
1176:                        indirectDirtySMs.remove(sm);
1177:                        dirtySMs.add(sm);
1178:                    } else if (!dirtySMs.contains(sm)) {
1179:                        dirtySMs.add(sm);
1180:                    }
1181:                }
1182:
1183:                // Delete the object
1184:                deleteObjectInternal(obj);
1185:
1186:                if (omf.getPersistenceByReachabilityAtCommit()) {
1187:                    if (sm != null) {
1188:                        if (getApiAdapter().isDeleted(obj)) {
1189:                            txKnownDeletedIds.add(sm.getInternalObjectId());
1190:                        }
1191:                    }
1192:                }
1193:            }
1194:
1195:            /**
1196:             * Method to delete an object from persistence which should be called from internal calls only.
1197:             * All PM/EM calls should go via deleteObject(Object obj).
1198:             * @param obj Object to delete
1199:             */
1200:            public void deleteObjectInternal(Object obj) {
1201:                if (obj == null) {
1202:                    return;
1203:                }
1204:                assertIsOpen();
1205:                assertWritable();
1206:
1207:                try {
1208:                    clr.setPrimary(obj.getClass().getClassLoader());
1209:                    assertClassPersistable(obj.getClass());
1210:
1211:                    Object pc = obj;
1212:                    if (getApiAdapter().isDetached(obj)) {
1213:                        // Load up the attached instance with this identity
1214:                        pc = findObject(getApiAdapter().getIdForObject(obj),
1215:                                true, true, null);
1216:                    }
1217:
1218:                    if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1219:                        JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010019",
1220:                                StringUtils.toJVMIDString(pc)));
1221:                    }
1222:
1223:                    // Check that the object is valid for deleting
1224:                    if (getApiAdapter() instanceof  JDOAdapter) // TODO Refactor this
1225:                    {
1226:                        // JDO doesnt allow deletion of transient
1227:                        if (!getApiAdapter().isPersistent(pc)
1228:                                && !getApiAdapter().isTransactional(pc)) {
1229:                            throw new JPOXUserException(LOCALISER.msg("010020"));
1230:                        } else if (!getApiAdapter().isPersistent(pc)
1231:                                && getApiAdapter().isTransactional(pc)) {
1232:                            throw new JPOXUserException(LOCALISER.msg("010021"));
1233:                        }
1234:                    }
1235:
1236:                    // Delete it
1237:                    StateManager sm = findStateManager(pc);
1238:                    if (sm == null) {
1239:                        if (!getApiAdapter().allowDeleteOfNonPersistentObject()) {
1240:                            // Not permitted by the API
1241:                            throw new JPOXUserException(LOCALISER.msg("010007",
1242:                                    getApiAdapter().getIdForObject(pc)));
1243:                        }
1244:
1245:                        // Put StateManager around object so it is P_NEW (unpersisted), then P_NEW_DELETED soon after
1246:                        sm = StateManagerFactory
1247:                                .newStateManagerForPNewToBeDeleted(this , pc);
1248:                    }
1249:
1250:                    // Move to deleted state
1251:                    sm.deletePersistent();
1252:                } finally {
1253:                    clr.unsetPrimary();
1254:                }
1255:            }
1256:
1257:            /**
1258:             * Method to delete an array of objects from the datastore.
1259:             * @param objs The objects
1260:             * @throws JPOXUserException Thrown if an error occurs during the deletion process.
1261:             *     Any exception could have several nested exceptions for each failed object deletion
1262:             */
1263:            public void deleteObjects(Object[] objs) {
1264:                if (objs == null) {
1265:                    return;
1266:                }
1267:
1268:                ArrayList failures = null;
1269:                for (int i = 0; i < objs.length; i++) {
1270:                    try {
1271:                        deleteObject(objs[i]);
1272:                    } catch (RuntimeException e) {
1273:                        if (failures == null) {
1274:                            failures = new ArrayList();
1275:                        }
1276:                        failures.add(e);
1277:                    }
1278:                }
1279:                if (failures != null && !failures.isEmpty()) {
1280:                    throw new JPOXUserException(LOCALISER.msg("011005"),
1281:                            (Exception[]) failures
1282:                                    .toArray(new Exception[failures.size()]));
1283:                }
1284:            }
1285:
1286:            /**
1287:             * Method to delete an array of objects from the datastore.
1288:             * @param objs The objects
1289:             * @throws JPOXUserException Thrown if an error occurs during the deletion process.
1290:             *     Any exception could have several nested exceptions for each failed object deletion
1291:             */
1292:            public void deleteObjects(Collection objs) {
1293:                if (objs == null) {
1294:                    return;
1295:                }
1296:
1297:                ArrayList failures = null;
1298:                Iterator iter = objs.iterator();
1299:                while (iter.hasNext()) {
1300:                    Object obj = iter.next();
1301:                    try {
1302:                        deleteObject(obj);
1303:                    } catch (RuntimeException e) {
1304:                        if (failures == null) {
1305:                            failures = new ArrayList();
1306:                        }
1307:                        failures.add(e);
1308:                    }
1309:                }
1310:                if (failures != null && !failures.isEmpty()) {
1311:                    throw new JPOXUserException(LOCALISER.msg("011005"),
1312:                            (Exception[]) failures
1313:                                    .toArray(new Exception[failures.size()]));
1314:                }
1315:            }
1316:
1317:            /**
1318:             * Method to migrate an object to transient state.
1319:             * @param obj The object
1320:             * @param state Object containing the state of the fetch plan process (if any)
1321:             * @throws JPOXException When an error occurs in making the object transient
1322:             */
1323:            public synchronized void makeObjectTransient(Object obj,
1324:                    FetchPlanState state) {
1325:                if (obj == null) {
1326:                    return;
1327:                }
1328:
1329:                try {
1330:                    clr.setPrimary(obj.getClass().getClassLoader());
1331:                    assertClassPersistable(obj.getClass());
1332:                    assertNotDetached(obj);
1333:
1334:                    if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1335:                        JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010022",
1336:                                StringUtils.toJVMIDString(obj)));
1337:                    }
1338:
1339:                    if (getApiAdapter().isPersistent(obj)) {
1340:                        StateManager sm = findStateManager(obj);
1341:                        sm.makeTransient(state);
1342:                    }
1343:                } finally {
1344:                    clr.unsetPrimary();
1345:                }
1346:            }
1347:
1348:            /**
1349:             * Method to make an object transactional.
1350:             * @param obj The object
1351:             * @throws JPOXException Thrown when an error occurs
1352:             */
1353:            public void makeObjectTransactional(Object obj) {
1354:                if (obj == null) {
1355:                    return;
1356:                }
1357:                try {
1358:                    clr.setPrimary(obj.getClass().getClassLoader());
1359:                    assertClassPersistable(obj.getClass());
1360:                    assertNotDetached(obj);
1361:
1362:                    if (getApiAdapter().isPersistent(obj)) {
1363:                        assertActiveTransaction();
1364:                    }
1365:                    StateManager sm = findStateManager(obj);
1366:                    if (sm == null) {
1367:                        sm = StateManagerFactory
1368:                                .newStateManagerForTransactionalTransient(this ,
1369:                                        obj);
1370:                    }
1371:                    sm.makeTransactional();
1372:                } finally {
1373:                    clr.unsetPrimary();
1374:                }
1375:            }
1376:
1377:            /**
1378:             * Method to make an object nontransactional.
1379:             * @param obj The object
1380:             */
1381:            public void makeObjectNontransactional(Object obj) {
1382:                if (obj == null) {
1383:                    return;
1384:                }
1385:                try {
1386:                    clr.setPrimary(obj.getClass().getClassLoader());
1387:                    assertClassPersistable(obj.getClass());
1388:                    if (!getApiAdapter().isPersistent(obj)
1389:                            && getApiAdapter().isTransactional(obj)
1390:                            && getApiAdapter().isDirty(obj)) {
1391:                        throw new JPOXUserException(LOCALISER.msg("010024"));
1392:                    }
1393:
1394:                    StateManager sm = findStateManager(obj);
1395:                    sm.makeNontransactional();
1396:                } finally {
1397:                    clr.unsetPrimary();
1398:                }
1399:            }
1400:
1401:            /**
1402:             * Method to attach a persistent detached object.
1403:             * If a different object with the same identity as this object exists in the L1 cache then an exception
1404:             * will be thrown.
1405:             * @param pc The persistable object
1406:             * @param sco Whether the PC object is stored without an identity (embedded/serialised)
1407:             */
1408:            public synchronized void attachObject(Object pc, boolean sco) {
1409:                assertIsOpen();
1410:                assertClassPersistable(pc.getClass());
1411:
1412:                ApiAdapter api = getApiAdapter();
1413:                Object id = api.getIdForObject(pc);
1414:                if (id != null && isInserting(pc)) {
1415:                    // Object is being inserted in this transaction so just return
1416:                    return;
1417:                } else if (id == null && !sco) {
1418:                    // Transient object so needs persisting
1419:                    persistObjectInternal(pc, null, null, -1, StateManager.PC);
1420:                    return;
1421:                }
1422:
1423:                if (api.isDetached(pc)) {
1424:                    // Detached, so migrate to attached
1425:                    StateManager l1CachedSM = (StateManager) cache.get(id);
1426:                    if (l1CachedSM != null && l1CachedSM.getObject() != pc) {
1427:                        // attached object with the same id already present in the L1 cache so cannot attach in-situ
1428:                        throw new JPOXUserException(LOCALISER.msg("010017",
1429:                                StringUtils.toJVMIDString(pc)));
1430:                    }
1431:
1432:                    if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1433:                        JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010016",
1434:                                StringUtils.toJVMIDString(pc)));
1435:                    }
1436:                    StateManager sm = StateManagerFactory
1437:                            .newStateManagerForDetached(this , pc, id, api
1438:                                    .getVersionForObject(pc));
1439:                    sm.attach(sco);
1440:                } else {
1441:                    // Not detached so can't attach it. Just return
1442:                    return;
1443:                }
1444:            }
1445:
1446:            /**
1447:             * Method to attach a persistent detached object returning an attached copy of the object.
1448:             * If the object is of class that is not detachable, a ClassNotDetachableException will be thrown.
1449:             * @param pc The object
1450:             * @param sco Whether it has no identity (second-class object)
1451:             * @return The attached object
1452:             */
1453:            public synchronized Object attachObjectCopy(Object pc, boolean sco) {
1454:                assertIsOpen();
1455:                assertClassPersistable(pc.getClass());
1456:                assertDetachable(pc);
1457:
1458:                ApiAdapter api = getApiAdapter();
1459:                Object id = api.getIdForObject(pc);
1460:                if (id != null && isInserting(pc)) {
1461:                    // Object is being inserted in this transaction
1462:                    return pc;
1463:                } else if (id == null && !sco) {
1464:                    // Object was not persisted before so persist it
1465:                    return persistObjectInternal(pc, null, null, -1,
1466:                            StateManager.PC);
1467:                } else if (api.isPersistent(pc)) {
1468:                    // Already persistent hence can't be attached
1469:                    return pc;
1470:                }
1471:
1472:                // Object should exist in this datastore now
1473:                Object pcTarget = null;
1474:                if (sco) {
1475:                    // SCO PC (embedded/serialised)
1476:                    boolean detached = getApiAdapter().isDetached(pc);
1477:                    StateManager smTarget = StateManagerFactory
1478:                            .newStateManagerForEmbedded(this , pc, true);
1479:                    pcTarget = smTarget.getObject();
1480:                    if (detached) {
1481:                        // If the object is detached, re-attach it
1482:                        if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1483:                            JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
1484:                                    "010018", StringUtils.toJVMIDString(pc),
1485:                                    StringUtils.toJVMIDString(pcTarget)));
1486:                        }
1487:                        smTarget.attachCopy(pc, sco);
1488:                    }
1489:                } else {
1490:                    // FCO PC
1491:                    boolean detached = getApiAdapter().isDetached(pc);
1492:                    pcTarget = findObject(id, false, false, pc.getClass()
1493:                            .getName());
1494:                    if (detached) {
1495:                        Object obj = null;
1496:                        HashMap attachedPCById = getThreadContextInfo().attachedPCById; // For the current thread
1497:                        if (attachedPCById != null) // Only used by persistObject process
1498:                        {
1499:                            obj = attachedPCById.get(getApiAdapter()
1500:                                    .getIdForObject(pc));
1501:                        }
1502:                        if (obj != null) {
1503:                            pcTarget = obj;
1504:                        } else {
1505:                            // If the object is detached, re-attach it
1506:                            if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1507:                                JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
1508:                                        "010018",
1509:                                        StringUtils.toJVMIDString(pc),
1510:                                        StringUtils.toJVMIDString(pcTarget)));
1511:                            }
1512:                            findStateManager(pcTarget).attachCopy(pc, sco);
1513:
1514:                            // Save the detached-attached PCs for later reference
1515:                            if (attachedPCById != null) // Only used by persistObject process
1516:                            {
1517:                                attachedPCById.put(getApiAdapter()
1518:                                        .getIdForObject(pc), pcTarget);
1519:                            }
1520:                        }
1521:                    }
1522:                }
1523:
1524:                return pcTarget;
1525:            }
1526:
1527:            /**
1528:             * Method to detach a persistent object without making a copy. Note that 
1529:             * also all the objects which are refered to from this object are detached.
1530:             * If the object is of class that is not detachable a ClassNotDetachableException
1531:             * will be thrown. If the object is not persistent a JDOUserException is thrown.
1532:             * <B>For internal use only</B>
1533:             * @param obj The object
1534:             * @param state State for the detachment process
1535:             */
1536:            public synchronized void detachObject(Object obj,
1537:                    FetchPlanState state) {
1538:                assertIsOpen();
1539:                assertClassPersistable(obj.getClass());
1540:                assertDetachable(obj); // Is this required?
1541:
1542:                if (getApiAdapter().isDetached(obj)) {
1543:                    return;
1544:                }
1545:
1546:                if (!getApiAdapter().isPersistent(obj)) {
1547:                    // Transient object passed so persist it before thinking about detaching
1548:                    if (tx.isActive()) {
1549:                        persistObjectInternal(obj, null, null, -1,
1550:                                StateManager.PC);
1551:                    }
1552:                }
1553:
1554:                StateManager sm = findStateManager(obj);
1555:                if (sm == null) {
1556:                    throw new JPOXUserException(LOCALISER.msg("010007",
1557:                            getApiAdapter().getIdForObject(obj)));
1558:                }
1559:                sm.detach(state);
1560:            }
1561:
1562:            /**
1563:             * Detach a copy of the passed persistent object using the provided detach state.
1564:             * If the object is of class that is not detachable it will be detached as transient.
1565:             * If it is not yet persistent it will be first persisted.
1566:             * @param pc The object
1567:             * @param state State for the detachment process
1568:             * @return The detached object
1569:             */
1570:            public Object detachObjectCopy(Object pc, FetchPlanState state) {
1571:                assertIsOpen();
1572:                assertClassPersistable(pc.getClass());
1573:
1574:                Object thePC = pc;
1575:                try {
1576:                    clr.setPrimary(pc.getClass().getClassLoader());
1577:                    if (!getApiAdapter().isPersistent(pc)
1578:                            && !getApiAdapter().isDetached(pc)) {
1579:                        // Transient object passed so persist it before thinking about detaching
1580:                        if (tx.isActive()) {
1581:                            thePC = persistObjectInternal(pc, null, null, -1,
1582:                                    StateManager.PC);
1583:                        } else {
1584:                            // JDO2 [12.6.8] "If a detachCopy method is called outside an active transaction, the reachability algorithm
1585:                            // will not be run; if any transient instances are reachable via persistent fields, a JDOUserException is thrown 
1586:                            // for each persistent instance containing such fields.
1587:                            throw new JPOXUserException(LOCALISER.msg("010014"));
1588:                        }
1589:                    }
1590:
1591:                    if (getApiAdapter().isDetached(thePC)) {
1592:                        // Passing in a detached (dirty or clean) instance, so get a persistent copy to detach
1593:                        thePC = findObject(getApiAdapter()
1594:                                .getIdForObject(thePC), false, true, null);
1595:                    }
1596:
1597:                    Object detached = ((DetachState) state)
1598:                            .getDetachedCopyObject(thePC);
1599:                    if (detached == null) {
1600:                        StateManager sm = findStateManager(thePC);
1601:                        if (sm == null) {
1602:                            throw new JPOXUserException(LOCALISER.msg("010007",
1603:                                    getApiAdapter().getIdForObject(thePC)));
1604:                        }
1605:
1606:                        detached = sm.detachCopy(state);
1607:                        ((DetachState) state).setDetachedCopyObject(detached,
1608:                                sm.getExternalObjectId(sm.getObject()));
1609:                    } else {
1610:                        // What if the fetch-group here implies a different depth through this object ? we need to continue detaching
1611:                        // TODO Check the detach and proceed further where necessary
1612:                    }
1613:                    return detached;
1614:                } finally {
1615:                    clr.unsetPrimary();
1616:                }
1617:            }
1618:
1619:            /**
1620:             * Method to detach all objects in the ObjectManager.
1621:             */
1622:            public void detachAll() {
1623:                Object[] sms = this .enlistedSMCache.values().toArray();
1624:                FetchPlanState fps = new FetchPlanState();
1625:                for (int i = 0; i < sms.length; i++) {
1626:                    ((StateManager) sms[i]).detach(fps);
1627:                }
1628:            }
1629:
1630:            // ----------------------------- New Instances ----------------------------------
1631:
1632:            /**
1633:             * Method to generate an instance of an interface, abstract class, or concrete PC class.
1634:             * @param cls The class of the interface or abstract class, or concrete class defined in MetaData
1635:             * @return The instance of this type
1636:             */
1637:            public Object newInstance(Class cls) {
1638:                assertIsOpen();
1639:
1640:                if (getApiAdapter().isPersistable(cls)
1641:                        && !Modifier.isAbstract(cls.getModifiers())) {
1642:                    // Concrete PC class so instantiate here
1643:                    try {
1644:                        return cls.newInstance();
1645:                    } catch (IllegalAccessException iae) {
1646:                        throw new JPOXUserException(iae.toString(), iae);
1647:                    } catch (InstantiationException ie) {
1648:                        throw new JPOXUserException(ie.toString(), ie);
1649:                    }
1650:                }
1651:
1652:                // Use ImplementationCreator
1653:                assertHasImplementationCreator();
1654:                return getOMFContext().getImplementationCreator().newInstance(
1655:                        cls, getMetaDataManager(), clr);
1656:            }
1657:
1658:            // ----------------------------- Object Retrieval by Id ----------------------------------
1659:
1660:            /**
1661:             * Method to return if the specified object exists in the datastore.
1662:             * @param obj The (persistable) object
1663:             * @return Whether it exists
1664:             */
1665:            public boolean exists(Object obj) {
1666:                if (obj == null) {
1667:                    return false;
1668:                }
1669:
1670:                Object id = getApiAdapter().getIdForObject(obj);
1671:                if (id == null) {
1672:                    return false;
1673:                }
1674:
1675:                try {
1676:                    findObject(id, true, false, obj.getClass().getName());
1677:                } catch (JPOXObjectNotFoundException onfe) {
1678:                    return false;
1679:                }
1680:
1681:                return true;
1682:            }
1683:
1684:            /**
1685:             * Accessor for the currently managed objects for the current transaction.
1686:             * If the transaction is not active this returns null.
1687:             * @return Collection of managed objects enlisted in the current transaction
1688:             */
1689:            public Set getManagedObjects() {
1690:                if (!tx.isActive()) {
1691:                    return null;
1692:                }
1693:
1694:                Set objs = new HashSet();
1695:                Collection sms = enlistedSMCache.values();
1696:                Iterator smsIter = sms.iterator();
1697:                while (smsIter.hasNext()) {
1698:                    StateManager sm = (StateManager) smsIter.next();
1699:                    objs.add(sm.getObject());
1700:                }
1701:                return objs;
1702:            }
1703:
1704:            /**
1705:             * Accessor for the currently managed objects for the current transaction.
1706:             * If the transaction is not active this returns null.
1707:             * @param classes Classes that we want the enlisted objects for
1708:             * @return Collection of managed objects enlisted in the current transaction
1709:             */
1710:            public Set getManagedObjects(Class[] classes) {
1711:                if (!tx.isActive()) {
1712:                    return null;
1713:                }
1714:
1715:                Set objs = new HashSet();
1716:                Collection sms = enlistedSMCache.values();
1717:                Iterator smsIter = sms.iterator();
1718:                while (smsIter.hasNext()) {
1719:                    StateManager sm = (StateManager) smsIter.next();
1720:                    for (int i = 0; i < classes.length; i++) {
1721:                        if (classes[i] == sm.getObject().getClass()) {
1722:                            objs.add(sm.getObject());
1723:                            break;
1724:                        }
1725:                    }
1726:                }
1727:                return objs;
1728:            }
1729:
1730:            /**
1731:             * Accessor for the currently managed objects for the current transaction.
1732:             * If the transaction is not active this returns null.
1733:             * @param states States that we want the enlisted objects for
1734:             * @return Collection of managed objects enlisted in the current transaction
1735:             */
1736:            public Set getManagedObjects(String[] states) {
1737:                if (!tx.isActive()) {
1738:                    return null;
1739:                }
1740:
1741:                Set objs = new HashSet();
1742:                Collection sms = enlistedSMCache.values();
1743:                Iterator smsIter = sms.iterator();
1744:                while (smsIter.hasNext()) {
1745:                    StateManager sm = (StateManager) smsIter.next();
1746:                    for (int i = 0; i < states.length; i++) {
1747:                        if (getApiAdapter().getObjectState(sm.getObject())
1748:                                .equals(states[i])) {
1749:                            objs.add(sm.getObject());
1750:                            break;
1751:                        }
1752:                    }
1753:                }
1754:                return objs;
1755:            }
1756:
1757:            /**
1758:             * Accessor for the currently managed objects for the current transaction.
1759:             * If the transaction is not active this returns null.
1760:             * @param states States that we want the enlisted objects for
1761:             * @param classes Classes that we want the enlisted objects for
1762:             * @return Collection of managed objects enlisted in the current transaction
1763:             */
1764:            public Set getManagedObjects(String[] states, Class[] classes) {
1765:                if (!tx.isActive()) {
1766:                    return null;
1767:                }
1768:
1769:                Set objs = new HashSet();
1770:                Collection sms = enlistedSMCache.values();
1771:                Iterator smsIter = sms.iterator();
1772:                while (smsIter.hasNext()) {
1773:                    boolean matches = false;
1774:                    StateManager sm = (StateManager) smsIter.next();
1775:                    for (int i = 0; i < states.length; i++) {
1776:                        if (getApiAdapter().getObjectState(sm.getObject())
1777:                                .equals(states[i])) {
1778:                            for (int j = 0; i < classes.length; i++) {
1779:                                if (classes[j] == sm.getObject().getClass()) {
1780:                                    matches = true;
1781:                                    objs.add(sm.getObject());
1782:                                    break;
1783:                                }
1784:                            }
1785:                        }
1786:                        if (matches) {
1787:                            break;
1788:                        }
1789:                    }
1790:                }
1791:                return objs;
1792:            }
1793:
1794:            /**
1795:             * Accessor for the StateManager of an object given the object AID.
1796:             * @param pcClass The class of the PC object
1797:             * @param fv The field values to be loaded
1798:             * @param ignoreCache true if it must ignore the cache
1799:             * @param checkInheritance Whether look to the database to determine which
1800:             * class this object is. This parameter is a hint. Set false, if it's
1801:             * already determined the correct pcClass for this pc "object" in a certain
1802:             * level in the hierarchy. Set to true and it will look to the database.
1803:             * @return Object
1804:             */
1805:            public synchronized Object findObjectUsingAID(Class pcClass,
1806:                    FieldValues fv, boolean ignoreCache,
1807:                    boolean checkInheritance) {
1808:                assertIsOpen();
1809:
1810:                // Create StateManager to generate an identity 
1811:                // TODO Provide a more efficient way of doing this. It creates a PC object just to get the id!
1812:                StateManager sm = StateManagerFactory
1813:                        .newStateManagerForHollowPopulatedAppId(this , pcClass,
1814:                                fv);
1815:                if (!ignoreCache) {
1816:                    // Check the cache
1817:                    Object oid = sm.getInternalObjectId();
1818:                    Object pc = getObjectFromCache(oid);
1819:                    if (pc != null) {
1820:                        sm = findStateManager(pc);
1821:                        sm.loadFieldValues(fv); // Load the values retrieved by the query
1822:                        return pc;
1823:                    }
1824:                    if (checkInheritance) {
1825:                        ApiAdapter api = getApiAdapter();
1826:                        if (oid instanceof  OID
1827:                                || api.isSingleFieldIdentity(oid)) {
1828:                            // Check if this id for any known subclasses is in the cache to save searching
1829:                            String[] subclasses = getMetaDataManager()
1830:                                    .getSubclassesForClass(pcClass.getName(),
1831:                                            true);
1832:                            if (subclasses != null) {
1833:                                for (int i = 0; i < subclasses.length; i++) {
1834:                                    if (oid instanceof  OID) {
1835:                                        oid = OIDFactory.getInstance(this ,
1836:                                                subclasses[i], ((OID) oid)
1837:                                                        .getKeyValue());
1838:                                    } else if (api.isSingleFieldIdentity(oid)) {
1839:                                        oid = api
1840:                                                .getNewSingleFieldIdentity(
1841:                                                        oid.getClass(),
1842:                                                        clr
1843:                                                                .classForName(subclasses[i]),
1844:                                                        api
1845:                                                                .getTargetKeyForSingleFieldIdentity(oid));
1846:                                    }
1847:                                    pc = getObjectFromCache(oid);
1848:                                    if (pc != null) {
1849:                                        sm = findStateManager(pc);
1850:                                        sm.loadFieldValues(fv); // Load the values retrieved by the query
1851:                                        return pc;
1852:                                    }
1853:                                }
1854:                            }
1855:                        }
1856:                    }
1857:                }
1858:
1859:                if (checkInheritance) {
1860:                    sm.checkInheritance(fv); // Find the correct PC class for this object, hence updating the object id
1861:                    if (!ignoreCache) {
1862:                        // Check the cache in case this updated object id is present (since we should use that if available)
1863:                        Object oid = sm.getInternalObjectId();
1864:                        Object pc = getObjectFromCache(oid);
1865:                        if (pc != null) {
1866:                            // We have an object with this new object id already so return it with the retrieved field values imposed
1867:                            sm = findStateManager(pc);
1868:                            sm.loadFieldValues(fv); // Load the values retrieved by the query
1869:                            return pc;
1870:                        }
1871:                    }
1872:                }
1873:                putObjectIntoCache(sm, true, true);
1874:
1875:                return sm.getObject();
1876:            }
1877:
1878:            /**
1879:             * Accessor for an object given the object id. 
1880:             * @param id Id of the object.
1881:             * @param fv Field values for the object
1882:             * @param cls the type which the object is. This type will be used to instanciat the object
1883:             * @param ignoreCache true if it must ignore the cache
1884:             * @return The Object
1885:             */
1886:            public synchronized Object findObject(Object id, FieldValues fv,
1887:                    Class cls, boolean ignoreCache) {
1888:                assertIsOpen();
1889:
1890:                Object pc = null;
1891:                if (!ignoreCache) {
1892:                    pc = getObjectFromCache(id);
1893:                }
1894:
1895:                if (pc == null) {
1896:                    pc = getStoreManager().findObject(this , id);
1897:                }
1898:
1899:                if (pc == null) {
1900:                    // Object not found so load the requested fields into a new object
1901:                    StateManager sm = StateManagerFactory
1902:                            .newStateManagerForHollowPopulated(this , cls, id,
1903:                                    fv);
1904:                    // TODO verify it
1905:
1906:                    pc = sm.getObject();
1907:
1908:                    // Save the object
1909:                    putObjectIntoCache(sm, true, true);
1910:                } else {
1911:                    // Object found in the cache so load the requested fields
1912:                    StateManager sm = findStateManager(pc);
1913:                    if (sm != null) {
1914:                        // Load the requested fields
1915:                        fv.fetchNonLoadedFields(sm);
1916:                    }
1917:                }
1918:
1919:                return pc;
1920:            }
1921:
1922:            /**
1923:             * Accessor for an object given the object id. Checks the inheritance level of the object
1924:             * @param id Id of the object.
1925:             * @param fv Field values for the object
1926:             * @return The Object
1927:             */
1928:            public synchronized Object findObject(Object id, FieldValues fv) {
1929:                assertIsOpen();
1930:
1931:                Object pc = getObjectFromCache(id);
1932:                if (pc == null) {
1933:                    // Find it direct from the store if the store supports that
1934:                    // NOTE : This ignores the provided FieldValues!
1935:                    pc = getStoreManager().findObject(this , id);
1936:
1937:                    if (pc == null) {
1938:                        // Find the class name for this object id so we can attempt to generate it
1939:                        // className is null when id class exists, and object has been validated and doesn't exist
1940:                        String className = getStoreManager()
1941:                                .getClassNameForObjectID(id, clr, this );
1942:                        if (className == null) {
1943:                            throw new JPOXObjectNotFoundException(LOCALISER
1944:                                    .msg("010026"), id);
1945:                        }
1946:
1947:                        if (id instanceof  OID) {
1948:                            id = OIDFactory.getInstance(this , className,
1949:                                    ((OID) id).getKeyValue());
1950:
1951:                            // try again to read object from cache
1952:                            pc = getObjectFromCache(id);
1953:                        }
1954:                        if (pc == null) {
1955:                            // Still not found so create a Hollow instance with the supplied field values
1956:                            try {
1957:                                Class pcClass = clr.classForName(className, id
1958:                                        .getClass().getClassLoader());
1959:                                StateManager sm = StateManagerFactory
1960:                                        .newStateManagerForHollowPopulated(
1961:                                                this , pcClass, id, fv);
1962:                                pc = sm.getObject();
1963:                                putObjectIntoCache(sm, true, true);
1964:                            } catch (ClassNotResolvedException e) {
1965:                                JPOXLogger.PERSISTENCE.warn(LOCALISER.msg(
1966:                                        "010027", id));
1967:                                throw new JPOXUserException(LOCALISER.msg(
1968:                                        "010027", id), e);
1969:                            }
1970:                        }
1971:                    }
1972:                }
1973:                return pc;
1974:            }
1975:
1976:            /**
1977:             * Accessor for an object given the object id. If validate is false, we return the object
1978:             * if found in the cache, or otherwise a Hollow object with that id. If validate is true
1979:             * we check with the datastore and return an object with the FetchPlan fields loaded (unless
1980:             * the object doesnt exist in the datastore, where we throw a JPOXObjectNotFoundException).
1981:             * @param id Id of the object.
1982:             * @param validate Whether to validate the object state
1983:             * @param checkInheritance Whether look to the database to determine which
1984:             * class this object is.
1985:             * @param objectClassName Class name for the object with this id (if known, optional)
1986:             * @return The Object with this id
1987:             */
1988:            public synchronized Object findObject(Object id, boolean validate,
1989:                    boolean checkInheritance, String objectClassName) {
1990:                assertIsOpen();
1991:                if (id == null) {
1992:                    throw new JPOXUserException(LOCALISER.msg("011010"));
1993:                }
1994:
1995:                // try to find object in cache(s)
1996:                Object pc = getObjectFromCache(id);
1997:                boolean fromCache = true;
1998:
1999:                ApiAdapter api = getApiAdapter();
2000:                if (id instanceof  SCOID && pc != null) {
2001:                    if (api.isPersistent(pc) && !api.isNew(pc)
2002:                            && !api.isDeleted(pc) && !api.isTransactional(pc)) {
2003:                        // JDO2 [5.4.4] Cant return HOLLOW nondurable objects
2004:                        throw new JPOXUserException(LOCALISER.msg("010005"));
2005:                    }
2006:                }
2007:
2008:                if (pc != null && api.isTransactional(pc)) {
2009:                    // JDO2 [12.6.5] If there's already an object with the same id and it's transactional, return it
2010:                    return pc;
2011:                }
2012:
2013:                StateManager sm = null;
2014:                if (pc == null) {
2015:                    // Find it direct from the store if the store supports that
2016:                    pc = getStoreManager().findObject(this , id);
2017:
2018:                    if (pc == null) {
2019:                        // Object not found in cache(s) with this identity
2020:                        String className = null;
2021:                        String originalClassName = null;
2022:                        boolean checkedClassName = false;
2023:                        if (id instanceof  SCOID) {
2024:                            throw new JPOXUserException(LOCALISER.msg("010006"));
2025:                        } else if (id instanceof  OID) {
2026:                            // OID, so check that the implied class is managed
2027:                            originalClassName = getStoreManager()
2028:                                    .manageClassForIdentity(id,
2029:                                            getClassLoaderResolver());
2030:                        } else if (api.isSingleFieldIdentity(id)) {
2031:                            // SingleFieldIdentity, so check that the implied class is managed
2032:                            originalClassName = getStoreManager()
2033:                                    .manageClassForIdentity(id,
2034:                                            getClassLoaderResolver());
2035:                        } else if (objectClassName != null) {
2036:                            // Object class name specified so use that directly
2037:                            originalClassName = objectClassName;
2038:                        } else {
2039:                            // We dont know the object class so try to deduce it from what is known by the StoreManager
2040:                            originalClassName = getStoreManager()
2041:                                    .getClassNameForObjectID(id, clr, this );
2042:                            checkedClassName = true;
2043:                        }
2044:
2045:                        if (checkInheritance) {
2046:                            // Verify if correct class inheritance level is set
2047:                            if (!checkedClassName) {
2048:                                className = getStoreManager()
2049:                                        .getClassNameForObjectID(id, clr, this );
2050:                            } else {
2051:                                // We just checked the name of the class in the section above so just use that
2052:                                className = originalClassName;
2053:                            }
2054:
2055:                            if (className == null) {
2056:                                throw new JPOXObjectNotFoundException(LOCALISER
2057:                                        .msg("010026"), id);
2058:                            }
2059:
2060:                            if (originalClassName != null
2061:                                    && !originalClassName.equals(className)) {
2062:                                // Inheritance checking has found a different inherited
2063:                                // object with this identity so create new id
2064:                                if (id instanceof  OID) {
2065:                                    // Create new OID using correct target class
2066:                                    id = OIDFactory
2067:                                            .getInstance(this , className,
2068:                                                    ((OID) id).getKeyValue());
2069:
2070:                                    // try again to read object from cache with this id
2071:                                    pc = getObjectFromCache(id);
2072:                                } else if (api.isSingleFieldIdentity(id)) {
2073:                                    // Create new SingleFieldIdentity using correct targetClass
2074:                                    id = api
2075:                                            .getNewSingleFieldIdentity(
2076:                                                    id.getClass(),
2077:                                                    getClassLoaderResolver()
2078:                                                            .classForName(
2079:                                                                    className),
2080:                                                    api
2081:                                                            .getTargetKeyForSingleFieldIdentity(id));
2082:
2083:                                    // try again to read object from cache with this id
2084:                                    pc = getObjectFromCache(id);
2085:                                }
2086:                            }
2087:                        } else {
2088:                            className = originalClassName;
2089:                        }
2090:
2091:                        if (pc == null) {
2092:                            // Still not found so create a Hollow instance with the supplied field values
2093:                            try {
2094:                                Class pcClass = clr.classForName(className,
2095:                                        (id instanceof  OID) ? null : id
2096:                                                .getClass().getClassLoader());
2097:                                sm = StateManagerFactory
2098:                                        .newStateManagerForHollow(this ,
2099:                                                pcClass, id);
2100:                                pc = sm.getObject();
2101:                                fromCache = false;
2102:                            } catch (ClassNotResolvedException e) {
2103:                                JPOXLogger.PERSISTENCE.warn(LOCALISER.msg(
2104:                                        "010027", id));
2105:                                throw new JPOXUserException(LOCALISER.msg(
2106:                                        "010027", id), e);
2107:                            }
2108:                        }
2109:                    }
2110:                }
2111:
2112:                if (validate) {
2113:                    // User requests validation of the instance so go to the datastore to validate it
2114:                    // loading any fetchplan fields that are needed in the process.
2115:                    if (sm == null) {
2116:                        sm = findStateManager(pc);
2117:                    }
2118:                    sm.validate();
2119:
2120:                    if (!fromCache) {
2121:                        // We created a Hollow PC earlier but then went to the datastore and let it find the real object
2122:                        // This allows the datastore to replace this temporary Hollow object with the real datastore object if required
2123:                        // This doesnt change with RDBMS datastores since we just pull in fields, but for DB4O we pull in object graphs
2124:                        pc = sm.getObject();
2125:                    }
2126:                }
2127:
2128:                if (sm != null) {
2129:                    // Cache the object (update it if already present)
2130:                    putObjectIntoCache(sm, !fromCache, true);
2131:                }
2132:
2133:                return pc;
2134:            }
2135:
2136:            /**
2137:             * This method returns an object id instance corresponding to the pcClass and key arguments.
2138:             * Operates in 2 modes :-
2139:             * <ul>
2140:             * <li>The class uses SingleFieldIdentity and the key is the value of the key field</li>
2141:             * <li>In all other cases the key is the String form of the object id instance</li>
2142:             * </ul>
2143:             * @param pcClass Class of the PersistenceCapable to create the identity for
2144:             * @param key Value of the key for SingleFieldIdentity (or the toString value)
2145:             * @return The new object-id instance
2146:             */
2147:            public Object newObjectId(Class pcClass, Object key) {
2148:                assertIsOpen();
2149:                if (pcClass == null) {
2150:                    throw new JPOXUserException(LOCALISER.msg("010028"));
2151:                }
2152:                assertClassPersistable(pcClass);
2153:
2154:                AbstractClassMetaData cmd = getMetaDataManager()
2155:                        .getMetaDataForClass(pcClass, clr);
2156:                if (cmd == null) {
2157:                    throw new NoPersistenceInformationException(pcClass
2158:                            .getName());
2159:                }
2160:
2161:                // If the class is not yet managed, manage it
2162:                if (!getStoreManager().managesClass(cmd.getFullClassName())) {
2163:                    getStoreManager().addClass(cmd.getFullClassName(), clr);
2164:                }
2165:
2166:                Object id = null;
2167:                if (cmd.usesSingleFieldIdentityClass()) {
2168:                    // Single Field Identity
2169:                    Class idType = clr.classForName(cmd.getObjectidClass());
2170:                    id = getApiAdapter().getNewSingleFieldIdentity(idType,
2171:                            pcClass, key);
2172:                } else if (key instanceof  java.lang.String) {
2173:                    // String-based PK (datastore identity or application identity)
2174:                    if (cmd.getIdentityType() == IdentityType.APPLICATION) {
2175:                        if (Modifier.isAbstract(pcClass.getModifiers())
2176:                                && cmd.getObjectidClass() != null) {
2177:                            try {
2178:                                Constructor c = clr
2179:                                        .classForName(cmd.getObjectidClass())
2180:                                        .getDeclaredConstructor(
2181:                                                new Class[] { java.lang.String.class });
2182:                                id = c
2183:                                        .newInstance(new Object[] { (String) key });
2184:                            } catch (Exception e) {
2185:                                String msg = LOCALISER.msg("010030", cmd
2186:                                        .getObjectidClass(), cmd
2187:                                        .getFullClassName());
2188:                                JPOXLogger.PERSISTENCE.error(msg);
2189:                                JPOXLogger.PERSISTENCE.error(e);
2190:
2191:                                throw new JPOXUserException(msg);
2192:                            }
2193:                        } else {
2194:                            clr.classForName(pcClass.getName(), true);
2195:                            id = getApiAdapter()
2196:                                    .getNewApplicationIdentityObjectId(pcClass,
2197:                                            key);
2198:                        }
2199:                    } else {
2200:                        id = OIDFactory.getInstance(this , (String) key);
2201:                    }
2202:                } else {
2203:                    // Key is not a string, and is not SingleFieldIdentity
2204:                    throw new JPOXUserException(LOCALISER.msg("010029", pcClass
2205:                            .getName(), key.getClass().getName()));
2206:                }
2207:
2208:                return id;
2209:            }
2210:
2211:            /**
2212:             * Method to clear an object from the list of dirty objects.
2213:             * @param sm The StateManager
2214:             */
2215:            public synchronized void clearDirty(StateManager sm) {
2216:                dirtySMs.remove(sm);
2217:                indirectDirtySMs.remove(sm);
2218:            }
2219:
2220:            /**
2221:             * Method to clear all objects marked as dirty (whether directly or indirectly).
2222:             */
2223:            public synchronized void clearDirty() {
2224:                dirtySMs.clear();
2225:                indirectDirtySMs.clear();
2226:            }
2227:
2228:            /**
2229:             * Method to mark an object (StateManager) as dirty.
2230:             * @param sm The StateManager
2231:             * @param directUpdate Whether the object has had a direct update made on it (if known)
2232:             */
2233:            public synchronized void markDirty(StateManager sm,
2234:                    boolean directUpdate) {
2235:                if (tx.isCommitting() && !tx.isActive()) {
2236:                    //post commit cannot change objects (sanity check - avoid changing avoids on detach)
2237:                    throw new JPOXException(
2238:                            "Cannot change objects when transaction is no longer active.");
2239:                }
2240:
2241:                boolean isInDirty = dirtySMs.contains(sm);
2242:                boolean isInIndirectDirty = indirectDirtySMs.contains(sm);
2243:                if (!isDelayDatastoreOperationsEnabled()
2244:                        && !isInDirty
2245:                        && !isInIndirectDirty
2246:                        && dirtySMs.size() >= getOMFContext()
2247:                                .getPersistenceConfiguration()
2248:                                .getDatastoreTransactionFlushLimit()) {
2249:                    // Reached flush limit so flush
2250:                    flushInternal(false);
2251:                }
2252:
2253:                if (directUpdate) {
2254:                    if (isInIndirectDirty) {
2255:                        indirectDirtySMs.remove(sm);
2256:                        dirtySMs.add(sm);
2257:                    } else if (!isInDirty) {
2258:                        dirtySMs.add(sm);
2259:                    }
2260:                } else {
2261:                    if (!isInDirty && !isInIndirectDirty) {
2262:                        // Register as an indirect dirty
2263:                        indirectDirtySMs.add(sm);
2264:                    }
2265:                }
2266:            }
2267:
2268:            /**
2269:             * Method to mark the specified StateManager as needing an update due to managed relation constraints.
2270:             * @param sm The StateManager
2271:             */
2272:            public void markManagedRelationDirty(StateManager sm) {
2273:                if (managedRelationDirtySMs == null) {
2274:                    managedRelationDirtySMs = new HashSet();
2275:                }
2276:                managedRelationDirtySMs.add(sm);
2277:            }
2278:
2279:            /**
2280:             * Returns whether this ObjectManager is currently performing the manage relationships task.
2281:             * @return Whether in the process of managing relations
2282:             */
2283:            public boolean isManagingRelations() {
2284:                return managingRelations;
2285:            }
2286:
2287:            /**
2288:             * Method to perform managed relationships tasks.
2289:             * @throws JPOXUserException if a consistency check fails
2290:             */
2291:            protected void performManagedRelationships() {
2292:                if (getOMFContext().getPersistenceConfiguration()
2293:                        .getManageRelationships()
2294:                        && managedRelationDirtySMs != null
2295:                        && managedRelationDirtySMs.size() > 0) {
2296:                    try {
2297:                        managingRelations = true;
2298:
2299:                        if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2300:                            JPOXLogger.PERSISTENCE.debug(LOCALISER
2301:                                    .msg("013000"));
2302:                        }
2303:
2304:                        if (getOMFContext().getPersistenceConfiguration()
2305:                                .getManageRelationshipsChecks()) {
2306:                            // Tests for negative situations where inconsistently assigned
2307:                            Iterator iter = managedRelationDirtySMs.iterator();
2308:                            while (iter.hasNext()) {
2309:                                StateManager sm = (StateManager) iter.next();
2310:                                sm.checkManagedRelations();
2311:                            }
2312:                        }
2313:
2314:                        // Process updates to manage the other side of the relations
2315:                        Iterator iter = managedRelationDirtySMs.iterator();
2316:                        while (iter.hasNext()) {
2317:                            StateManager sm = (StateManager) iter.next();
2318:                            sm.processManagedRelations();
2319:                            sm.clearManagedRelations();
2320:                        }
2321:                        managedRelationDirtySMs.clear();
2322:
2323:                        if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2324:                            JPOXLogger.PERSISTENCE.debug(LOCALISER
2325:                                    .msg("013001"));
2326:                        }
2327:                    } finally {
2328:                        managingRelations = false;
2329:                    }
2330:                }
2331:            }
2332:
2333:            /**
2334:             * Returns whether the ObjectManager is in the process of flushing.
2335:             * @return true if the ObjectManager is flushing
2336:             */
2337:            public boolean isFlushing() {
2338:                return flushing;
2339:            }
2340:
2341:            /**
2342:             * Method callable from external APIs for user-management of flushing.
2343:             * Called by JDO PM.flush, or JPA EM.flush().
2344:             * Performs management of relations, prior to performing internal flush of all dirty/new/deleted
2345:             * instances to the datastore.
2346:             */
2347:            public void flush() {
2348:                assertIsOpen();
2349:                if (tx.isActive()) {
2350:                    // Managed Relationships
2351:                    performManagedRelationships();
2352:
2353:                    // Perform internal flush
2354:                    flushInternal(true);
2355:                }
2356:            }
2357:
2358:            /**
2359:             * This method flushes all dirty, new, and deleted instances to the
2360:             * datastore. It has no effect if a transaction is not active. If a
2361:             * datastore transaction is active, this method synchronizes the cache with
2362:             * the datastore and reports any exceptions. If an optimistic transaction is
2363:             * active, this method obtains a datastore connection and synchronizes the
2364:             * cache with the datastore using this connection. The connection obtained
2365:             * by this method is held until the end of the transaction.
2366:             * @param flushToDatastore Whether to ensure any changes reach the datastore
2367:             *     Otherwise they will be flushed to the datastore manager and leave it to
2368:             *     decide the opportune moment to actually flush them to the datastore
2369:             * @throws JPOXOptimisticException when optimistic locking error(s) occur
2370:             */
2371:            public synchronized void flushInternal(boolean flushToDatastore) {
2372:                assertIsOpen();
2373:
2374:                if (tx.isActive()) {
2375:                    if (!flushToDatastore && dirtySMs.size() == 0
2376:                            && indirectDirtySMs.size() == 0) {
2377:                        // Nothing to flush so abort now
2378:                        return;
2379:                    }
2380:
2381:                    if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2382:                        JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010003",
2383:                                (dirtySMs.size() + indirectDirtySMs.size())));
2384:                    }
2385:                    flushing = true;
2386:                    try {
2387:                        List optimisticFailures = null;
2388:
2389:                        // Flush all dirty, new, deleted instances to the datastore when transaction is active
2390:                        Object[] toFlushDirect;
2391:                        synchronized (dirtySMs) {
2392:                            toFlushDirect = dirtySMs.toArray();
2393:                            dirtySMs.clear();
2394:                        }
2395:
2396:                        Object[] toFlushIndirect;
2397:                        synchronized (indirectDirtySMs) {
2398:                            toFlushIndirect = indirectDirtySMs.toArray();
2399:                            indirectDirtySMs.clear();
2400:                        }
2401:
2402:                        // a). direct dirty objects
2403:                        for (int i = 0; i < toFlushDirect.length; i++) {
2404:                            StateManager sm = (StateManager) toFlushDirect[i];
2405:                            try {
2406:                                sm.flush();
2407:                            } catch (JPOXOptimisticException oe) {
2408:                                if (optimisticFailures == null) {
2409:                                    optimisticFailures = new ArrayList();
2410:                                }
2411:                                optimisticFailures.add(oe);
2412:                            }
2413:                        }
2414:
2415:                        // b). indirect dirty objects
2416:                        for (int i = 0; i < toFlushIndirect.length; i++) {
2417:                            StateManager sm = (StateManager) toFlushIndirect[i];
2418:                            try {
2419:                                sm.flush();
2420:                            } catch (JPOXOptimisticException oe) {
2421:                                if (optimisticFailures == null) {
2422:                                    optimisticFailures = new ArrayList();
2423:                                }
2424:                                optimisticFailures.add(oe);
2425:                            }
2426:                        }
2427:
2428:                        // Make sure flushes its changes to the datastore
2429:                        if (flushToDatastore) {
2430:                            tx.flush();
2431:                        }
2432:                        if (optimisticFailures != null) {
2433:                            // Throw a single JPOXOptimisticException containing all optimistic failures
2434:                            throw new JPOXOptimisticException(
2435:                                    LOCALISER.msg("010031"),
2436:                                    (Throwable[]) optimisticFailures
2437:                                            .toArray(new Throwable[optimisticFailures
2438:                                                    .size()]));
2439:                        }
2440:                    } finally {
2441:                        if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
2442:                            JPOXLogger.PERSISTENCE.debug(LOCALISER
2443:                                    .msg("010004"));
2444:                        }
2445:                        flushing = false;
2446:                    }
2447:                }
2448:            }
2449:
2450:            /** Flag for whether running persistence-by-reachability-at-commit */
2451:            private boolean runningPBRAtCommit = false;
2452:
2453:            /**
2454:             * Method to perform any post-begin checks.
2455:             */
2456:            public synchronized void postBegin() {
2457:                StateManager[] sms = (StateManager[]) dirtySMs
2458:                        .toArray(new StateManager[dirtySMs.size()]);
2459:                for (int i = 0; i < sms.length; i++) {
2460:                    sms[i].preBegin(tx);
2461:                }
2462:                sms = (StateManager[]) indirectDirtySMs
2463:                        .toArray(new StateManager[indirectDirtySMs.size()]);
2464:                for (int i = 0; i < sms.length; i++) {
2465:                    sms[i].preBegin(tx);
2466:                }
2467:            }
2468:
2469:            /**
2470:             * Method to perform any pre-commit checks.
2471:             */
2472:            public synchronized void preCommit() {
2473:                // Make sure all is flushed before we start
2474:                flush();
2475:
2476:                if (omf.getPersistenceByReachabilityAtCommit()) {
2477:                    // Persistence-by-reachability at commit
2478:                    try {
2479:                        runningPBRAtCommit = true;
2480:                        performReachabilityAtCommit();
2481:                        getTransaction().flush();
2482:                    } catch (Throwable t) {
2483:                        JPOXLogger.PERSISTENCE.error(t);
2484:                        if (t instanceof  JPOXException) {
2485:                            throw (JPOXException) t;
2486:                        } else {
2487:                            throw new JPOXException(
2488:                                    "Unexpected error during precommit", t);
2489:                        }
2490:                    } finally {
2491:                        runningPBRAtCommit = false;
2492:                    }
2493:                }
2494:
2495:                if (detachAllOnCommit) {
2496:                    // "detach-on-commit"
2497:                    performDetachAllOnCommitPreparation();
2498:                }
2499:            }
2500:
2501:            /**
2502:             * Method to perform persistence-by-reachability at commit.
2503:             * Utilises txKnownPersistedIds, and txFlushedNewIds, together with txKnownDeletedIds
2504:             * and runs reachability, performing any necessary delettions of no longer reachable objects.
2505:             */
2506:            private void performReachabilityAtCommit() {
2507:                if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
2508:                    JPOXLogger.REACHABILITY.debug(LOCALISER.msg("010032"));
2509:                }
2510:
2511:                // If we have some new objects in this transaction, and we have some known persisted objects (either
2512:                // from makePersistent in this txn, or enlisted existing objects) then run reachability checks
2513:                if (txKnownPersistedIds.size() > 0
2514:                        && txFlushedNewIds.size() > 0) {
2515:                    Set currentReachables = new HashSet();
2516:
2517:                    // Run "reachability" on all known persistent objects for this txn
2518:                    Object ids[] = txKnownPersistedIds.toArray();
2519:                    Set objectNotFound = new HashSet();
2520:                    for (int i = 0; i < ids.length; i++) {
2521:                        if (!txKnownDeletedIds.contains(ids[i])) {
2522:                            if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
2523:                                JPOXLogger.REACHABILITY
2524:                                        .debug("Performing reachability algorithm on object with id \""
2525:                                                + ids[i] + "\"");
2526:                            }
2527:                            try {
2528:                                StateManager sm = findStateManager(findObject(
2529:                                        ids[i], true, true, null));
2530:                                sm.runReachability(currentReachables);
2531:                                if (i % 10000 == 0 || i == ids.length - 1) {
2532:                                    // Flush every 10000 or on the last one to make sure tx cache is empty
2533:                                    flushInternal(true);
2534:                                }
2535:                            } catch (JPOXObjectNotFoundException ex) {
2536:                                objectNotFound.add(ids[i]);
2537:                            }
2538:                        } else {
2539:                            // Was deleted earlier so ignore
2540:                        }
2541:                    }
2542:
2543:                    // Remove any of the "reachable" instances that are no longer "reachable"
2544:                    txFlushedNewIds.removeAll(currentReachables);
2545:
2546:                    Object nonReachableIds[] = txFlushedNewIds.toArray();
2547:                    if (nonReachableIds != null && nonReachableIds.length > 0) {
2548:                        // For all of instances no longer reachable we need to delete them from the datastore
2549:                        // A). Nullify all of their fields.
2550:                        // TODO See CORE-3276 for a possible change to this
2551:                        for (int i = 0; i < nonReachableIds.length; i++) {
2552:                            if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
2553:                                JPOXLogger.REACHABILITY.debug(LOCALISER.msg(
2554:                                        "010033", nonReachableIds[i]));
2555:                            }
2556:                            try {
2557:                                if (!objectNotFound
2558:                                        .contains(nonReachableIds[i])) {
2559:                                    StateManager sm = findStateManager(findObject(
2560:                                            nonReachableIds[i], true, true,
2561:                                            null));
2562:                                    sm.nullifyFields();
2563:
2564:                                    if (i % 10000 == 0
2565:                                            || i == nonReachableIds.length - 1) {
2566:                                        // Flush every 10000 or on the last one to clear out dirties
2567:                                        flushInternal(true);
2568:                                    }
2569:                                }
2570:                            } catch (JPOXObjectNotFoundException ex) {
2571:                                // just ignore if the object does not exist anymore  
2572:                            }
2573:                        }
2574:
2575:                        // B). Remove the objects
2576:                        for (int i = 0; i < nonReachableIds.length; i++) {
2577:                            try {
2578:                                if (!objectNotFound
2579:                                        .contains(nonReachableIds[i])) {
2580:                                    StateManager sm = findStateManager(findObject(
2581:                                            nonReachableIds[i], true, true,
2582:                                            null));
2583:                                    sm.deletePersistent();
2584:                                    if (i % 10000 == 0
2585:                                            || i == nonReachableIds.length - 1) {
2586:                                        // Flush every 10000 or on the last one to clear out dirties
2587:                                        flushInternal(true);
2588:                                    }
2589:                                }
2590:                            } catch (JPOXObjectNotFoundException ex) {
2591:                                //just ignore if the file does not exist anymore  
2592:                            }
2593:                        }
2594:                    }
2595:                }
2596:
2597:                if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
2598:                    JPOXLogger.REACHABILITY.debug(LOCALISER.msg("010034"));
2599:                }
2600:            }
2601:
2602:            /**
2603:             * Temporary array of StateManagers to detach at commit (to prevent garbage collection). 
2604:             * Set up in preCommit() and used in postCommit().
2605:             */
2606:            private StateManager[] detachAllOnCommitSMs = null;
2607:
2608:            /**
2609:             * Method to perform all necessary preparation for detach-all-on-commit.
2610:             * Identifies all objects affected and makes sure that all fetch plan fields are loaded.
2611:             */
2612:            private void performDetachAllOnCommitPreparation() {
2613:                // JDO2 spec 12.7.3 "Root instances"
2614:                // "Root instances are parameter instances for retrieve, detachCopy, and refresh; result
2615:                // instances for queries. Root instances for DetachAllOnCommit are defined explicitly by
2616:                // the user via the FetchPlan property DetachmentRoots or DetachmentRootClasses. 
2617:                // If not set explicitly, the detachment roots consist of the union of all root instances of
2618:                // methods executed since the last commit or rollback."
2619:                Collection sms = new ArrayList();
2620:                Collection roots = fetchPlan.getDetachmentRoots();
2621:                Class[] rootClasses = fetchPlan.getDetachmentRootClasses();
2622:                if (roots != null && roots.size() > 0) {
2623:                    // Detachment roots specified
2624:                    Iterator rootsIter = roots.iterator();
2625:                    while (rootsIter.hasNext()) {
2626:                        Object obj = rootsIter.next();
2627:                        sms.add(findStateManager(obj));
2628:                    }
2629:                } else if (rootClasses != null && rootClasses.length > 0) {
2630:                    // Detachment root classes specified
2631:                    StateManager[] txSMs = (StateManager[]) enlistedSMCache
2632:                            .values().toArray(
2633:                                    new StateManager[enlistedSMCache.size()]);
2634:                    for (int i = 0; i < txSMs.length; i++) {
2635:                        for (int j = 0; j < rootClasses.length; j++) {
2636:                            // Check if object is of this root type
2637:                            if (txSMs[i].getObject().getClass() == rootClasses[j]) {
2638:                                // This SM is for a valid root object
2639:                                sms.add(txSMs[i]);
2640:                                break;
2641:                            }
2642:                        }
2643:                    }
2644:                } else {
2645:                    // Detach all objects in the L1 cache
2646:                    sms.addAll(cache.values());
2647:                }
2648:
2649:                // Make sure that all FetchPlan fields are loaded
2650:                Iterator smsIter = sms.iterator();
2651:                while (smsIter.hasNext()) {
2652:                    StateManager sm = (StateManager) smsIter.next();
2653:                    Object pc = sm.getObject();
2654:                    if (pc != null && !getApiAdapter().isDetached(pc)
2655:                            && !getApiAdapter().isDeleted(pc)) {
2656:                        // Load all fields (and sub-objects) in the FetchPlan
2657:                        FetchPlanState state = new FetchPlanState();
2658:                        try {
2659:                            sm.loadFieldsInFetchPlan(state);
2660:                        } catch (JPOXObjectNotFoundException onfe) {
2661:                            // This object doesnt exist in the datastore at this point.
2662:                            // Either the user has some other process that has deleted it or they have
2663:                            // defined datastore based cascade delete and it has been deleted that way
2664:                            JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("010013",
2665:                                    StringUtils.toJVMIDString(pc), sm
2666:                                            .getInternalObjectId()));
2667:                            smsIter.remove();
2668:                            // TODO Move the object state to P_DELETED for consistency
2669:                        }
2670:                    }
2671:                }
2672:                detachAllOnCommitSMs = (StateManager[]) sms
2673:                        .toArray(new StateManager[sms.size()]);
2674:            }
2675:
2676:            /**
2677:             * Method to perform detach-all-on-commit, using the data identified by
2678:             * performDetachAllOnCommitPreparation().
2679:             */
2680:            private void performDetachAllOnCommit() {
2681:                try {
2682:                    runningDetachAllOnCommit = true;
2683:
2684:                    // Detach all detachment root objects (causes recursion through the fetch plan)
2685:                    StateManager[] smsToDetach = detachAllOnCommitSMs;
2686:                    DetachState state = new DetachState(getApiAdapter());
2687:                    for (int i = 0; i < smsToDetach.length; i++) {
2688:                        Object pc = smsToDetach[i].getObject();
2689:                        if (pc != null) {
2690:                            smsToDetach[i].detach(state);
2691:                        }
2692:                    }
2693:                    detachAllOnCommitSMs = null; // No longer need these references
2694:                } finally {
2695:                    runningDetachAllOnCommit = false;
2696:                }
2697:            }
2698:
2699:            /**
2700:             * Accessor for whether this ObjectManager is currently running detachAllOnCommit.
2701:             * @return Whether running detachAllOnCommit
2702:             */
2703:            public boolean isRunningDetachAllOnCommit() {
2704:                return runningDetachAllOnCommit;
2705:            }
2706:
2707:            /**
2708:             * Method to perform detach on close (of the ObjectManager).
2709:             */
2710:            private void performDetachOnClose() {
2711:                JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010011"));
2712:                try {
2713:                    // Start a transaction in case we need to load any unloaded fields
2714:                    tx.begin();
2715:
2716:                    List toDetach = new ArrayList();
2717:                    toDetach.addAll(cache.values());
2718:                    Iterator iter = toDetach.iterator();
2719:                    while (iter.hasNext()) {
2720:                        Object obj = iter.next();
2721:                        StateManager sm = (StateManager) obj;
2722:                        if (sm != null) {
2723:                            //TODO why SM is in cache if null state ...
2724:                            if (sm != null
2725:                                    && sm.getObject() != null
2726:                                    && !sm.getObjectManager().getApiAdapter()
2727:                                            .isDeleted(sm.getObject())) {
2728:                                try {
2729:                                    sm.detach(new DetachState(getApiAdapter()));
2730:                                } catch (JPOXObjectNotFoundException onfe) {
2731:                                    // Catch exceptions for any objects that are deleted in other managers whilst having this open
2732:                                }
2733:                            }
2734:                        }
2735:                    }
2736:                    tx.commit();
2737:                } finally {
2738:                    if (tx.isActive()) {
2739:                        tx.rollback();
2740:                    }
2741:                }
2742:                JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("010012"));
2743:            }
2744:
2745:            /**
2746:             * Commit any changes made to objects managed by the persistence manager to the database.
2747:             */
2748:            public synchronized void postCommit() {
2749:                if (detachAllOnCommit) {
2750:                    // Detach-all-on-commit
2751:                    performDetachAllOnCommit();
2752:                }
2753:
2754:                List failures = null;
2755:                try {
2756:                    // Commit all enlisted StateManagers
2757:                    ApiAdapter api = getApiAdapter();
2758:                    StateManager[] sms = (StateManager[]) enlistedSMCache
2759:                            .values().toArray(
2760:                                    new StateManager[enlistedSMCache.size()]);
2761:                    for (int i = 0; i < sms.length; ++i) {
2762:                        try {
2763:                            // Perform any operations required after committing
2764:                            //TODO this if is due to sms that can have lc == null, why?, should not be here then
2765:                            if (sms[i] != null
2766:                                    && sms[i].getObject() != null
2767:                                    && (api.isPersistent(sms[i].getObject()) || api
2768:                                            .isTransactional(sms[i].getObject()))) {
2769:                                sms[i].postCommit(getTransaction());
2770:
2771:                                // TODO Change this check so that we remove all objects that are no longer suitable for caching
2772:                                if (detachAllOnCommit
2773:                                        && api.isDetachable(sms[i].getObject())) {
2774:                                    // "DetachAllOnCommit" - Remove the object from the L1 cache since it is now detached
2775:                                    removeStateManager(sms[i]);
2776:                                }
2777:                            }
2778:                        } catch (RuntimeException e) {
2779:                            if (failures == null) {
2780:                                failures = new ArrayList();
2781:                            }
2782:                            failures.add(e);
2783:                        }
2784:                    }
2785:                } finally {
2786:                    enlistedSMCache.clear();
2787:                    txEnlistedIds.clear();
2788:                    txKnownPersistedIds.clear();
2789:                    txKnownDeletedIds.clear();
2790:                    txFlushedNewIds.clear();
2791:                    fetchPlan.resetDetachmentRoots();
2792:                    if (managedRelationDirtySMs != null) {
2793:                        managedRelationDirtySMs.clear();
2794:                    }
2795:                }
2796:                if (failures != null && !failures.isEmpty()) {
2797:                    throw new CommitStateTransitionException(
2798:                            (Exception[]) failures
2799:                                    .toArray(new Exception[failures.size()]));
2800:                }
2801:            }
2802:
2803:            /**
2804:             * Rollback any changes made to objects managed by the persistence manager
2805:             * to the database.
2806:             */
2807:            public synchronized void preRollback() {
2808:                ArrayList failures = null;
2809:                try {
2810:                    Collection sms = enlistedSMCache.values();
2811:                    Iterator smsIter = sms.iterator();
2812:                    while (smsIter.hasNext()) {
2813:                        StateManager sm = (StateManager) smsIter.next();
2814:                        try {
2815:                            sm.preRollback(getTransaction());
2816:                        } catch (RuntimeException e) {
2817:                            if (failures == null) {
2818:                                failures = new ArrayList();
2819:                            }
2820:                            failures.add(e);
2821:                        }
2822:                    }
2823:                    clearDirty();
2824:                } finally {
2825:                    enlistedSMCache.clear();
2826:                    txEnlistedIds.clear();
2827:                    txKnownPersistedIds.clear();
2828:                    txKnownDeletedIds.clear();
2829:                    txFlushedNewIds.clear();
2830:                    if (managedRelationDirtySMs != null) {
2831:                        managedRelationDirtySMs.clear();
2832:                    }
2833:                }
2834:                if (failures != null && !failures.isEmpty()) {
2835:                    throw new RollbackStateTransitionException(
2836:                            (Exception[]) failures
2837:                                    .toArray(new Exception[failures.size()]));
2838:                }
2839:            }
2840:
2841:            // -------------------------------------- Cache Management ---------------------------------------
2842:
2843:            /**
2844:             * Replace the previous object id for a persistable object with a new one.
2845:             * This is used where we have already added the object to the cache(s) and/or enlisted it in the txn before
2846:             * its real identity was fixed (attributed in the datastore).
2847:             * @param pc The Persistable object
2848:             * @param oldID the old id it was known by
2849:             * @param newID the new id
2850:             */
2851:            public synchronized void replaceObjectId(Object pc, Object oldID,
2852:                    Object newID) {
2853:                if (pc == null || getApiAdapter().getIdForObject(pc) == null) {
2854:                    JPOXLogger.CACHE.warn(LOCALISER.msg("003006"));
2855:                    return;
2856:                }
2857:
2858:                Object o = cache.get(oldID); //use get() because a cache.remove operation returns a weakReference instance
2859:                if (o != null) {
2860:                    // Remove the old variant
2861:                    if (JPOXLogger.CACHE.isDebugEnabled()) {
2862:                        JPOXLogger.CACHE.debug(LOCALISER.msg("003012",
2863:                                StringUtils.toJVMIDString(pc), oldID, newID));
2864:                    }
2865:                    cache.remove(oldID);
2866:                }
2867:
2868:                // Put into both caches
2869:                StateManager sm = findStateManager(pc);
2870:                if (sm != null) {
2871:                    putObjectIntoCache(sm, true, true);
2872:                }
2873:
2874:                if (enlistedSMCache.get(oldID) != null) {
2875:                    // Swap the enlisted object identity
2876:                    if (sm != null) {
2877:                        enlistedSMCache.remove(oldID);
2878:                        enlistedSMCache.put(newID, sm);
2879:                        if (JPOXLogger.TRANSACTION.isDebugEnabled()) {
2880:                            JPOXLogger.TRANSACTION.debug(LOCALISER.msg(
2881:                                    "015018", StringUtils.toJVMIDString(pc),
2882:                                    oldID, newID));
2883:                        }
2884:                    }
2885:                }
2886:
2887:                if (omf.getPersistenceByReachabilityAtCommit()) {
2888:                    if (txEnlistedIds.remove(oldID)) {
2889:                        txEnlistedIds.add(newID);
2890:                    }
2891:                    if (txFlushedNewIds.remove(oldID)) {
2892:                        txFlushedNewIds.add(newID);
2893:                    }
2894:                    if (txKnownPersistedIds.remove(oldID)) {
2895:                        txKnownPersistedIds.add(newID);
2896:                    }
2897:                    if (txKnownDeletedIds.remove(oldID)) {
2898:                        txKnownDeletedIds.add(newID);
2899:                    }
2900:                }
2901:            }
2902:
2903:            /**
2904:             * Convenience method to add an object to the cache(s).
2905:             * @param sm The State Manager
2906:             * @param level1 Whether to put it in the L1 cache
2907:             * @param level2 Whether to put it in the L2 cache
2908:             */
2909:            public synchronized void putObjectIntoCache(StateManager sm,
2910:                    boolean level1, boolean level2) {
2911:                Object id = sm.getInternalObjectId();
2912:                if (id == null || sm.getObject() == null) {
2913:                    JPOXLogger.CACHE.warn(LOCALISER.msg("003006"));
2914:                    return;
2915:                }
2916:
2917:                // Put into Level 1 Cache
2918:                if (level1) {
2919:                    Object oldSM = cache.put(sm.getInternalObjectId(), sm);
2920:                    if (JPOXLogger.CACHE.isDebugEnabled()) {
2921:                        if (oldSM == null) {
2922:                            JPOXLogger.CACHE.debug(LOCALISER.msg("003004",
2923:                                    StringUtils.toJVMIDString(sm.getObject()),
2924:                                    sm.getInternalObjectId()));
2925:                        } else {
2926:                            JPOXLogger.CACHE.debug(LOCALISER.msg("003005",
2927:                                    StringUtils.toJVMIDString(sm.getObject()),
2928:                                    sm.getInternalObjectId()));
2929:                        }
2930:                    }
2931:                }
2932:
2933:                // Put into Level 2 Cache
2934:                if (level2 && this .omf.getCacheLevel2()) {
2935:                    boolean storeInL2Cache = true;
2936:                    AbstractClassMetaData acmd = getMetaDataManager()
2937:                            .getMetaDataForClass(sm.getObject().getClass(), clr);
2938:                    if (acmd != null
2939:                            && acmd.getIdentityType() == IdentityType.APPLICATION) {
2940:                        // If using compound identity dont put it in the L2 Cache (the id uses a PC which we can't link to)
2941:                        int[] pkFieldNumbers = acmd.getPKMemberPositions();
2942:                        for (int i = 0; i < pkFieldNumbers.length; i++) {
2943:                            AbstractMemberMetaData fmd = acmd
2944:                                    .getMetaDataForManagedMemberAtAbsolutePosition(pkFieldNumbers[i]);
2945:                            if (getApiAdapter().isPersistable(fmd.getType())) {
2946:                                storeInL2Cache = false;
2947:                            }
2948:                        }
2949:                    }
2950:
2951:                    if (storeInL2Cache) {
2952:                        if (JPOXLogger.CACHE.isDebugEnabled()) {
2953:                            JPOXLogger.CACHE.debug(LOCALISER.msg("004003",
2954:                                    StringUtils.toJVMIDString(sm.getObject()),
2955:                                    id));
2956:                        }
2957:                        // Store an L2 cacheable form of this object
2958:                        CachedPC pcCopy = sm.getL2CacheableObject();
2959:                        this .omf.getLevel2Cache().put(id, pcCopy);
2960:                    }
2961:                }
2962:            }
2963:
2964:            /**
2965:             * Convenience method to evict an object from the cache(s).
2966:             * @param pc The Persistpable object
2967:             * @param id The Persistable object id
2968:             * @param level1 Whether to evict from the L1 cache
2969:             * @param level2 Whether to evict from the L2 cache
2970:             */
2971:            public synchronized void removeObjectFromCache(Object pc,
2972:                    Object id, boolean level1, boolean level2) {
2973:                // Evict from the L1 cache
2974:                if (level1 && id != null) {
2975:                    if (JPOXLogger.CACHE.isDebugEnabled()) {
2976:                        JPOXLogger.CACHE.debug(LOCALISER.msg("003009",
2977:                                StringUtils.toJVMIDString(pc), id, String
2978:                                        .valueOf(cache.size())));
2979:                    }
2980:                    Object pcRemoved = cache.remove(id);
2981:                    if (pcRemoved == null && JPOXLogger.CACHE.isDebugEnabled()) {
2982:                        // For some reason the object isn't in the L1 cache - garbage collected maybe ?
2983:                        JPOXLogger.CACHE.debug(LOCALISER.msg("003010",
2984:                                StringUtils.toJVMIDString(pc), id));
2985:                    }
2986:                }
2987:
2988:                // Evict from the L2 cache
2989:                if (level2 && omf.getCacheLevel2()
2990:                        && getApiAdapter().getIdForObject(pc) != null) {
2991:                    Level2Cache l2Cache = omf.getLevel2Cache();
2992:                    if (JPOXLogger.CACHE.isDebugEnabled()) {
2993:                        JPOXLogger.CACHE.debug(LOCALISER.msg("004007",
2994:                                StringUtils.toJVMIDString(pc), getApiAdapter()
2995:                                        .getIdForObject(pc), String
2996:                                        .valueOf(l2Cache.getSize())));
2997:                    }
2998:                    l2Cache.evict(getApiAdapter().getIdForObject(pc));
2999:                }
3000:            }
3001:
3002:            /**
3003:             * Convenience method to access an object in the cache.
3004:             * Firstly looks in the L1 cache for this ObjectManager, and if not found looks in the L2 cache.
3005:             * @param id Id of the object
3006:             * @return Persistence Capable object (with connected StateManager).
3007:             */
3008:            public synchronized Object getObjectFromCache(Object id) {
3009:                Object pc = null;
3010:
3011:                // Try Level 1 first
3012:                StateManager sm = (StateManager) cache.get(id);
3013:                if (sm != null) {
3014:                    pc = sm.getObject();
3015:
3016:                    if (JPOXLogger.CACHE.isDebugEnabled()) {
3017:                        JPOXLogger.CACHE.debug(LOCALISER.msg("003008",
3018:                                StringUtils.toJVMIDString(pc), id, ""
3019:                                        + cache.size()));
3020:                    }
3021:                    // Wipe the detach state that may have been added if the object has been serialised in the meantime
3022:                    sm.resetDetachState();
3023:
3024:                    return pc;
3025:                } else {
3026:                    if (JPOXLogger.CACHE.isDebugEnabled()) {
3027:                        JPOXLogger.CACHE.debug(LOCALISER.msg("003007", id, ""
3028:                                + cache.size()));
3029:                    }
3030:                }
3031:
3032:                // Try Level 2 since not in Level 1
3033:                if (omf.getCacheLevel2()) {
3034:                    Level2Cache l2Cache = omf.getLevel2Cache();
3035:                    CachedPC cachedPC = l2Cache.get(id);
3036:
3037:                    // Copy the cached object and connect to a StateManager, with the same object id
3038:                    if (cachedPC != null) {
3039:                        sm = StateManagerFactory.newStateManagerForCachedPC(
3040:                                this , cachedPC.getPersistableObject(), id,
3041:                                cachedPC.getLoadedFields());
3042:                        pc = sm.getObject();
3043:                        if (JPOXLogger.CACHE.isDebugEnabled()) {
3044:                            JPOXLogger.CACHE.debug(LOCALISER.msg("004006",
3045:                                    StringUtils.toJVMIDString(pc), id, ""
3046:                                            + l2Cache.getSize()));
3047:                        }
3048:                        cache.put(id, sm); // Put into L1 cache for easy referencing
3049:                        return pc;
3050:                    } else {
3051:                        if (JPOXLogger.CACHE.isDebugEnabled()) {
3052:                            JPOXLogger.CACHE.debug(LOCALISER.msg("004005", id,
3053:                                    "" + l2Cache.getSize()));
3054:                        }
3055:                    }
3056:                }
3057:
3058:                return null;
3059:            }
3060:
3061:            // ------------------------------------- Queries/Extents --------------------------------------
3062:
3063:            /**
3064:             * Extents are collections of datastore objects managed by the datastore,
3065:             * not by explicit user operations on collections. Extent capability is a
3066:             * boolean property of classes that are persistence capable. If an instance
3067:             * of a class that has a managed extent is made persistent via reachability,
3068:             * the instance is put into the extent implicitly.
3069:             * @param pcClass The class to query
3070:             * @param subclasses Whether to include subclasses in the query.
3071:             * @return returns an Extent that contains all of the instances in the
3072:             * parameter class, and if the subclasses flag is true, all of the instances
3073:             * of the parameter class and its subclasses.
3074:             */
3075:            public synchronized Extent getExtent(Class pcClass,
3076:                    boolean subclasses) {
3077:                assertIsOpen();
3078:                ClassLoaderResolver clr = getClassLoaderResolver();
3079:                try {
3080:                    clr.setPrimary(pcClass.getClassLoader());
3081:                    assertClassPersistable(pcClass);
3082:
3083:                    return getStoreManager().getExtent(this , pcClass,
3084:                            subclasses);
3085:                } catch (JPOXException jpe) {
3086:                    // Convert any JPOX exceptions into what JDO expects
3087:                    throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
3088:                } finally {
3089:                    clr.unsetPrimary();
3090:                }
3091:            }
3092:
3093:            /**
3094:             * Construct an empty query instance.
3095:             * @return The query
3096:             */
3097:            public synchronized Query newQuery() {
3098:                return getOMFContext().getQueryManager().newQuery(
3099:                        "javax.jdo.query.JDOQL", this , null);
3100:            }
3101:
3102:            // ------------------------------------- Callback Listeners --------------------------------------
3103:
3104:            /**
3105:             * This method removes all previously registered lifecycle listeners.
3106:             * It is necessary to make sure, that a cached ObjectManager (in j2ee environment)
3107:             * will have no listener before the listeners are copied from the OMF.
3108:             * Otherwise they might be registered multiple times.
3109:             */
3110:            public void removeAllInstanceLifecycleListeners() {
3111:                if (callbacks != null) {
3112:                    callbacks.close();
3113:                }
3114:            }
3115:
3116:            /**
3117:             * Retrieve the callback handler for this ObjectManager.
3118:             * @return the callback handler
3119:             */
3120:            public CallbackHandler getCallbackHandler() {
3121:                if (callbacks != null) {
3122:                    return callbacks;
3123:                }
3124:
3125:                String callbackHandlerClassName = getOMFContext()
3126:                        .getPluginManager().getAttributeValueForExtension(
3127:                                "org.jpox.callbackhandler", "name",
3128:                                getOMFContext().getApi(), "class-name");
3129:                if (callbackHandlerClassName != null) {
3130:                    try {
3131:                        callbacks = (CallbackHandler) clr.classForName(
3132:                                callbackHandlerClassName,
3133:                                OMFContext.class.getClassLoader())
3134:                                .newInstance();
3135:                        return callbacks;
3136:                    } catch (InstantiationException e) {
3137:                        JPOXLogger.PERSISTENCE.error(LOCALISER.msg("025000",
3138:                                callbackHandlerClassName, e));
3139:                    } catch (IllegalAccessException e) {
3140:                        JPOXLogger.PERSISTENCE.error(LOCALISER.msg("025000",
3141:                                callbackHandlerClassName, e));
3142:                    }
3143:                }
3144:
3145:                return null;
3146:            }
3147:
3148:            /**
3149:             * Method to register a listener for instances of the specified classes.
3150:             * @param listener The listener to sends events to
3151:             * @param classes The classes that it is interested in
3152:             */
3153:            public void addListener(Object listener, Class[] classes) {
3154:                assertIsOpen();
3155:                if (listener == null) {
3156:                    return;
3157:                }
3158:                getCallbackHandler().addListener(listener, classes);
3159:            }
3160:
3161:            /**
3162:             * Method to remove a currently registered listener.
3163:             * @param listener The listener to remove.
3164:             */
3165:            public void removeListener(Object listener) {
3166:                assertIsOpen();
3167:                if (listener != null) {
3168:                    getCallbackHandler().removeListener(listener);
3169:                }
3170:            }
3171:
3172:            /**
3173:             * Disconnect the registered LifecycleListener
3174:             */
3175:            public void disconnectLifecycleListener() {
3176:                // Clear out lifecycle listeners that were registered
3177:                if (callbacks != null) {
3178:                    callbacks.close();
3179:                }
3180:            }
3181:
3182:            // ------------------------------- Assert Utilities ---------------------------------
3183:
3184:            /**
3185:             * Method to assert if this Object Manager is open. 
3186:             * Throws a JPOXUserException if the ObjectManager is closed.
3187:             */
3188:            protected void assertIsOpen() {
3189:                if (isClosed()) {
3190:                    throw new JPOXUserException(LOCALISER.msg("010002"))
3191:                            .setFatal();
3192:                }
3193:            }
3194:
3195:            /**
3196:             * Method to assert if the specified class is Persistence Capable.
3197:             * @param cls The class to check
3198:             * @throws ClassNotPersistableException if class is not persistable
3199:             * @throws NoPersistenceInformationException if no metadata/annotations are found for class
3200:             */
3201:            public void assertClassPersistable(Class cls) {
3202:                if (cls != null
3203:                        && !getOMFContext().getApiAdapter().isPersistable(cls)
3204:                        && !cls.isInterface()) {
3205:                    throw new ClassNotPersistableException(cls.getName());
3206:                }
3207:                if (!hasPersistenceInformationForClass(cls)) {
3208:                    throw new NoPersistenceInformationException(cls.getName());
3209:                }
3210:            }
3211:
3212:            /**
3213:             * Method to assert if the specified object is Detachable. 
3214:             * Throws a ClassNotDetachableException if not capable
3215:             * @param object The object to check
3216:             */
3217:            protected void assertDetachable(Object object) {
3218:                if (object != null && !getApiAdapter().isDetachable(object)) {
3219:                    throw new ClassNotDetachableException(object.getClass()
3220:                            .getName());
3221:                }
3222:            }
3223:
3224:            /**
3225:             * Method to assert if the specified object is detached.
3226:             * Throws a ObjectDetachedException if it is detached.
3227:             * @param object The object to check
3228:             */
3229:            protected void assertNotDetached(Object object) {
3230:                if (object != null && getApiAdapter().isDetached(object)) {
3231:                    throw new ObjectDetachedException(object.getClass()
3232:                            .getName());
3233:                }
3234:            }
3235:
3236:            /**
3237:             * Method to assert if the current transaction is active. Throws a
3238:             * TransactionNotActiveException if not active
3239:             */
3240:            protected void assertActiveTransaction() {
3241:                if (!tx.isActive()) {
3242:                    throw new TransactionNotActiveException();
3243:                }
3244:            }
3245:
3246:            /**
3247:             * Method to assert if the current transaction is active or non transactional
3248:             * writes are allowed.
3249:             * @throws a TransactionNotActiveException if not active and non transactional writes are disabled
3250:             */
3251:            protected void assertWritable() {
3252:                if (!getTransaction().isActive()
3253:                        && !getTransaction().getNontransactionalWrite()) {
3254:                    throw new TransactionNotActiveException();
3255:                }
3256:            }
3257:
3258:            /**
3259:             * Validates that an ImplementationCreator instance is accessible.
3260:             * @throws JPOXUserException if an ImplementationCreator instance does not exist
3261:             */
3262:            protected void assertHasImplementationCreator() {
3263:                if (getOMFContext().getImplementationCreator() == null) {
3264:                    throw new JPOXUserException(LOCALISER.msg("010035"));
3265:                }
3266:            }
3267:
3268:            /**
3269:             * Method to assert if no active transaction and nontransactionalRead is not set.
3270:             * @param operation The operation being performed (used in error reporting)
3271:             * @throws JPOXUserException if the tx is not active and no non-transactional read is available
3272:             */
3273:            protected void assertActiveTransactionOrNontransactionRead(
3274:                    String operation) {
3275:                if (!tx.isActive() && !tx.getNontransactionalRead()) {
3276:                    throw new JPOXUserException(LOCALISER.msg("011009",
3277:                            operation));
3278:                }
3279:            }
3280:
3281:            /**
3282:             * Utility method to check if the specified class has reachable metadata or annotations.
3283:             * @param cls The class to check
3284:             * @return Whether the class has reachable metadata or annotations
3285:             */
3286:            public boolean hasPersistenceInformationForClass(Class cls) {
3287:                if (cls == null) {
3288:                    return false;
3289:                }
3290:
3291:                if ((getMetaDataManager().getMetaDataForClass(cls,
3292:                        getClassLoaderResolver()) != null)) {
3293:                    return true;
3294:                }
3295:
3296:                if (cls.isInterface()) {
3297:                    // JDO2 "persistent-interface"
3298:                    // Try to create an implementation of the interface at runtime. 
3299:                    // It will register the MetaData and make an implementation available
3300:                    try {
3301:                        newInstance(cls);
3302:                    } catch (RuntimeException ex) {
3303:                        JPOXLogger.PERSISTENCE.warn(ex);
3304:                    }
3305:                    return getMetaDataManager().getMetaDataForClass(cls,
3306:                            getClassLoaderResolver()) != null;
3307:                }
3308:                return false;
3309:            }
3310:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.