Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/core/PersistenceBrokerImpl.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/core/PersistenceBrokerImpl.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/core/PersistenceBrokerImpl.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/core/PersistenceBrokerImpl.java Sat Jul 15 07:20:25 2006 @@ -1,6 +1,6 @@ package org.apache.ojb.broker.core; -/* Copyright 2003-2004 The Apache Software Foundation +/* Copyright 2003-2006 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ */ import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -29,6 +28,8 @@ import org.apache.commons.collections.ListUtils; import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.ojb.broker.Identity; import org.apache.ojb.broker.IdentityFactory; import org.apache.ojb.broker.ManageableCollection; @@ -41,6 +42,8 @@ import org.apache.ojb.broker.TransactionAbortedException; import org.apache.ojb.broker.TransactionInProgressException; import org.apache.ojb.broker.TransactionNotInProgressException; +import org.apache.ojb.broker.TransientObjectException; +import org.apache.ojb.broker.lob.LobHelper; import org.apache.ojb.broker.accesslayer.ChainingIterator; import org.apache.ojb.broker.accesslayer.CollectionCreationContext; import org.apache.ojb.broker.accesslayer.ConnectionManagerIF; @@ -107,6 +110,15 @@ { private Logger logger = LoggerFactory.getLogger(PersistenceBrokerImpl.class); + /** + * A stack trace of this broker user. + * Used when broker leak detection is enabled. + */ + protected String brokerStackTrace; + /** + * Returns <em>true</em> if broker leak detection is enabled. + */ + protected boolean brokerLeakDetection; protected PersistenceConfiguration persistenceConfiguration; protected BrokerHelper brokerHelper; protected MtoNBroker mtoNBroker; @@ -151,6 +163,8 @@ private SqlGenerator sqlGenerator; private IdentityFactory identityFactory; private RelationshipPrefetcherFactory relationshipPrefetcherFactory; + private LobHelper lobHelper; + private PBKey pbKey; /** * List of objects being stored now, allows to avoid infinite @@ -162,12 +176,12 @@ with user implemented equals/hashCode methods of persistence capable objects (e.g. objects are equals but PK fields not) */ - private List nowStoring = new IdentityArrayList(); + private IdentityArrayList nowStoring = new IdentityArrayList(); /** * Lists for object registration during delete operations. * We reuse these list to avoid excessive object creation. - * @see #clearRegistrationLists + * @see #refreshRegistrationLists */ /* arminw: list was cleared before delete method end. Internal we only @@ -177,7 +191,13 @@ with user implemented equals/hashCode methods of persistence capable objects (e.g. objects are equals but PK fields not) */ - private List markedForDelete = new IdentityArrayList(); + private IdentityArrayList markedForDelete = new IdentityArrayList(); + + /** + * Used for performance optimization of method + * [EMAIL PROTECTED] #refreshRelationships(Object, org.apache.ojb.broker.Identity, org.apache.ojb.broker.metadata.ClassDescriptor)} + */ + private IdentityArrayList skipRefreshRelationship = new IdentityArrayList(); /** * The set of identities of all deleted objects during current transaction @@ -254,6 +274,12 @@ proxyFactory = (ProxyFactory)subContainer.getSingletonInstance(ProxyFactory.class); queryFactory = (QueryFactoryNew)subContainer.getSingletonInstance(QueryFactoryNew.class); relationshipPrefetcherFactory = new RelationshipPrefetcherFactory(this); + lobHelper = (LobHelper)subContainer.getSingletonInstance(LobHelper.class); + } + + public LobHelper serviceLobHelper() + { + return lobHelper; } public BatchManager serviceBatchManager() @@ -324,7 +350,17 @@ { return proxyFactory; } - + + public boolean isBrokerLeakDetection() + { + return brokerLeakDetection; + } + + public void setBrokerLeakDetection(boolean brokerLeakDetection) + { + this.brokerLeakDetection = brokerLeakDetection; + } + public boolean isClosed() { return this.isClosed; @@ -332,11 +368,19 @@ public void setClosed(boolean closed) { + // When lookup the PB instance from pool method setClosed(false) + // was called before returning instance from pool, in this case + // OJB have to refresh the instance. if (!closed) { refresh(); + if(brokerLeakDetection) + { + brokerStackTrace = ExceptionUtils.getFullStackTrace( + new Exception("PersistenceBroker caller stack")); + } } - isClosed = closed; + this.isClosed = closed; } /** @@ -360,7 +404,7 @@ /** * Returns the maximum number of sql columns. - * + * * @return The sql limit */ public int getSqlInLimit() @@ -394,7 +438,7 @@ */ public void destroy() { - logger.info("Destroy and cleanup this PB instance. " + this); + if(logger.isEnabledFor(Logger.INFO)) logger.info("Destroy and cleanup this PB instance. " + this); removeAllListeners(); if (connectionManager != null) { @@ -435,8 +479,8 @@ /** * Creates a proxy instance. - * - * @param baseClassForProxy The base class that the Proxy should extend. For dynamic Proxies, the method of + * + * @param baseClassForProxy The base class that the Proxy should extend. For dynamic Proxies, the method of * generation is dependent on the ProxyFactory implementation. * @param realSubjectsIdentity The identity of the subject * @return An instance of the proxy subclass @@ -456,11 +500,11 @@ return constructor.newInstance(new Object[]{ handler }); } else - { + { return getProxyFactory().createProxy(baseClassForProxy,handler); } - + } catch (Exception ex) { @@ -489,7 +533,7 @@ try { fireBrokerEvent(BEFORE_CLOSE_EVENT); - clearRegistrationLists(); + refreshRegistrationLists(); referencesBroker.removePrefetchingListeners(); if (connectionManager != null) { @@ -509,6 +553,7 @@ } finally { + if(skipRefreshRelationship.size() > 0) skipRefreshRelationship.clear(); // reset flag indicating use in managed environment setManaged(false); serviceSessionCache().evictAll(SessionCache.LEVEL_SESSION); @@ -530,7 +575,7 @@ { fireBrokerEvent(BEFORE_ROLLBACK_EVENT); setInTransaction(false); - clearRegistrationLists(); + refreshRegistrationLists(); referencesBroker.removePrefetchingListeners(); /* arminw: @@ -579,7 +624,7 @@ } fireBrokerEvent(BEFORE_COMMIT_EVENT); setInTransaction(false); - clearRegistrationLists(); + refreshRegistrationLists(); referencesBroker.removePrefetchingListeners(); /* arminw: @@ -654,15 +699,15 @@ // only delete if object is not null if (obj != null) { + // replace specified object with the real one obj = getProxyFactory().getRealObject(obj); - /** - * MBAIRD - * 1. if we are marked for delete already, avoid recursing on this object - * - * arminw: - * use object instead Identity object in markedForDelete List, - * because using objects we get a better performance. I can't find - * side-effects in doing so. + + /* + MBAIRD + 1. if we are marked for delete already, avoid recursing on this object + arminw: + use object identity based list, because using objects we get a + better performance. I can't find side-effects in doing so. */ if (markedForDelete.contains(obj)) { @@ -670,10 +715,14 @@ } ClassDescriptor cld = getClassDescriptor(obj.getClass()); + Identity oid = serviceIdentity().buildIdentity(cld, obj); + //BRJ: check for valid pk - if (!serviceBrokerHelper().assertValidPkForDelete(cld, obj)) + //if (!serviceBrokerHelper().assertValidPkForDelete(cld, obj)) + // TODO: arminw: this simple check should do the same - verify + if (oid.isTransient()) { - String msg = "Cannot delete object without valid PKs. " + obj; + String msg = "Cannot delete object without valid PKs: " + obj; logger.error(msg); return; } @@ -683,40 +732,14 @@ * 2. register object in markedForDelete map. */ markedForDelete.add(obj); - Identity oid = serviceIdentity().buildIdentity(cld, obj); // Invoke events on PersistenceBrokerAware instances and listeners BEFORE_DELETE_EVENT.setTarget(obj); fireBrokerEvent(BEFORE_DELETE_EVENT); BEFORE_DELETE_EVENT.setTarget(null); - // 1. delete dependend collections - if (!ignoreReferences && cld.getCollectionDescriptors().size() > 0) - { - deleteCollections(obj, cld.getCollectionDescriptors()); - } - // 2. delete object from directly mapped table - try - { - dbAccess.executeDelete(cld, obj); // use obj not oid to delete, BRJ - } - catch(OptimisticLockException e) - { - // ensure that the outdated object be removed from cache - sessionCache.evict(oid, SessionCache.LEVEL_DEEP); - throw e; - } - - // 3. Add OID to the set of deleted objects - deletedDuringTransaction.add(oid); - - // 4. delete dependend upon objects last to avoid FK violations - if (cld.getObjectReferenceDescriptors().size() > 0) - { - deleteReferences(obj, cld.getObjectReferenceDescriptors(), ignoreReferences); - } - // remove obj from the object cache: - sessionCache.evict(oid, SessionCache.LEVEL_DEEP); + // now perform deletion + performDeletion(cld, obj, oid, ignoreReferences); // Invoke events on PersistenceBrokerAware instances and listeners AFTER_DELETE_EVENT.setTarget(obj); @@ -726,6 +749,41 @@ } /** + * This method perform the delete of the specified object + * based on the [EMAIL PROTECTED] org.apache.ojb.broker.metadata.ClassDescriptor}. + */ + private void performDeletion(final ClassDescriptor cld, final Object obj, final Identity oid, final boolean ignoreReferences) throws PersistenceBrokerException + { + // 1. delete dependend collections + if (!ignoreReferences && cld.getCollectionDescriptors().size() > 0) + { + deleteCollections(obj, cld.getCollectionDescriptors()); + } + // 2. delete object from directly mapped table + try + { + dbAccess.executeDelete(cld, obj); // use obj not oid to delete, BRJ + } + catch(OptimisticLockException e) + { + // ensure that the outdated object be removed from cache + sessionCache.evict(oid, SessionCache.LEVEL_DEEP); + throw e; + } + + // 3. Add OID to the set of deleted objects + deletedDuringTransaction.add(oid); + + // 4. delete dependend upon objects last to avoid FK violations + if (cld.getObjectReferenceDescriptors().size() > 0) + { + deleteReferences(cld, obj, oid, ignoreReferences); + } + // remove obj from the object cache: + sessionCache.evict(oid, SessionCache.LEVEL_DEEP); + } + + /** * Extent aware Delete by Query * @param query * @param cld @@ -749,29 +807,40 @@ if (query instanceof QueryByIdentity) { Identity oid = getIdentity((QueryByIdentity) query); - query = referencesBroker.getPKQuery(oid); - } - - if (cld.isMappedToTable()) - { - dbAccess.executeDelete(query, cld); + try + { + query = referencesBroker.getPKQuery(oid); + } + catch(TransientObjectException e) + { + query = null; + logger.error("Can't delete transient objects from datastore", e); + } } - // if class is an extent, we have to delete all extent classes too - String lastUsedTable = cld.getFullTableName(); - if (cld.isExtent()) + if(query != null) { - Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator(); + if (cld.isMappedToTable()) + { + dbAccess.executeDelete(query, cld); + } - while (extents.hasNext()) + // if class is an extent, we have to delete all extent classes too + String lastUsedTable = cld.getFullTableName(); + if (cld.isExtent()) { - ClassDescriptor extCld = (ClassDescriptor) extents.next(); + Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator(); - // read same table only once - if (!extCld.getFullTableName().equals(lastUsedTable)) + while (extents.hasNext()) { - lastUsedTable = extCld.getFullTableName(); - dbAccess.executeDelete(query, extCld); + ClassDescriptor extCld = (ClassDescriptor) extents.next(); + + // read same table only once + if (!extCld.getFullTableName().equals(lastUsedTable)) + { + lastUsedTable = extCld.getFullTableName(); + this.dbAccess.executeDelete(query, extCld); + } } } } @@ -793,29 +862,44 @@ * will be deleted if auto-delete is true <b>AND</b> * the member field containing the object reference is NOT null. * + * @param cld The [EMAIL PROTECTED] org.apache.ojb.broker.metadata.ClassDescriptor} of the object + * or of a super class. * @param obj Object which we will delete references for - * @param listRds list of ObjectRederenceDescriptors + * @param oid The [EMAIL PROTECTED] Identity} of the object. * @param ignoreReferences With this flag the automatic deletion/unlinking * of references can be suppressed (independent of the used auto-delete setting in metadata), * except [EMAIL PROTECTED] org.apache.ojb.broker.metadata.SuperReferenceDescriptor} * these kind of reference (descriptor) will always be performed. * @throws PersistenceBrokerException if some goes wrong - please see the error message for details */ - private void deleteReferences(Object obj, List listRds, boolean ignoreReferences) throws PersistenceBrokerException + private void deleteReferences(ClassDescriptor cld, Object obj, Identity oid, boolean ignoreReferences) throws PersistenceBrokerException { + List listRds = cld.getObjectReferenceDescriptors(); // get all members of obj that are references and delete them for (Iterator i = listRds.iterator(); i.hasNext();) { ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor)i.next(); - - if ((!ignoreReferences && (rds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)) + if ((!ignoreReferences && rds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT) || rds.isSuperReferenceDescriptor()) { Object referencedObject = rds.getPersistentField().get(obj); - if (referencedObject != null) { - doDelete(referencedObject, ignoreReferences); + if(rds.isSuperReferenceDescriptor()) + { + ClassDescriptor base = cld.getSuperClassDescriptor(); + /* + arminw: If "table-per-subclass" inheritance is used we have to + guarantee that all super-class table entries are deleted too. + Thus we have to perform the recursive deletion of all super-class + table entries. + */ + performDeletion(base, referencedObject, oid, ignoreReferences); + } + else + { + doDelete(referencedObject, ignoreReferences); + } } } } @@ -878,12 +962,14 @@ if(obj == null) return; ClassDescriptor cld = getClassDescriptor(obj.getClass()); + Identity oid = serviceIdentity().buildIdentity(cld, obj); /* if one of the PK fields was null, we assume the objects was new and needs insert */ - boolean insert = serviceBrokerHelper().hasNullPKField(cld, obj); - Identity oid = serviceIdentity().buildIdentity(cld, obj); + // boolean insert = serviceBrokerHelper().hasNullPKField(cld, obj); + // TODO: arminw: this should do the same - verify + boolean insert = oid.isTransient(); /* if PK values are set, lookup cache or db to see whether object needs insert or update @@ -1039,7 +1125,7 @@ { // get all members of obj that are references and store them Collection listRds = cld.getObjectReferenceDescriptors(); - + // skip if nothing to do if ((listRds != null) && (listRds.size() > 0)) { for (Iterator i = listRds.iterator(); i.hasNext();) @@ -1047,7 +1133,7 @@ ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next(); /* arminw: the special references (eg. super-references used for table per subclass - inheritance) must be performed in any case. The "normal" 1:1 references can be + inheritance) must be performed in any case. The "normal" 1:1 references can be ignored when flag "ignoreReferences" is set */ if((!ignoreReferences && rds.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE) @@ -1096,15 +1182,15 @@ Object obj = iter.next(); ClassDescriptor cld = getClassDescriptor(obj.getClass()); if (serviceBrokerHelper().assertValidPkForDelete(cld, obj)) - { + { delete(obj); - } + } } tc.clearDeletedObjects(); tc.clearNewObjects(); } } - + /** * Store/Link collections of objects poiting to <b>obj</b>. * More info please see comments in source. @@ -1142,7 +1228,7 @@ if (referencedObjects instanceof TrackingCollection) { afterStore((TrackingCollection) referencedObjects, cod); - } + } } } } @@ -1250,8 +1336,17 @@ while(it.hasNext()) { refObj = it.next(); - // set FK in refObj if it is materialized - if(getProxyFactory().isMaterialized(refObj)) + /* + TODO: Check this! + arminw: + When it's necessary to 'link' (set the FK) the 1:n reference objects? + 1. set FK in refObj if it is materialized + 2. if the referenced object is a proxy AND the main object needs insert + we have to materialize the real object, because the user may move a collection + of proxy objects from object A to new object B. In this case we have to replace the + FK in the proxy object with new key of object B. + */ + if(insert || getProxyFactory().isMaterialized(refObj)) { ClassDescriptor refCld = getClassDescriptor(getProxyFactory().getRealClass(refObj)); // get the real object before linking @@ -1464,7 +1559,7 @@ /** * Creates the hierarchy of enclosing objects between the target class and the given persistent outer class - * + * * @param targetClass The target inner class (is not instantiated) * @param directPersistentOuterClass The nearest persistent enclosing class * @param directPersistentOuterObject The instance of the nearest persistent enclosing class to use @@ -1486,7 +1581,7 @@ { // recursively create the direct enclosing object of the direct enclosing class Object oneLevelAboveOuterObj = createDirectEnclosingObject(directEnclosingClass, directPersistentOuterClass, directPersistentOuterObject); - + try { return ClassHelper.newInstance(directEnclosingClass, @@ -1527,13 +1622,14 @@ /** * Retrieve all References (also Collection-attributes) of a given instance. * Loading is forced, even if the collection- and reference-descriptors differ. - * @param pInstance the persistent instance to work with + * @param obj the persistent instance to work with + * @param force Force the loading of references */ private void retrieveAllReferences(Object obj, boolean force) throws PersistenceBrokerException { if (logger.isDebugEnabled()) { - logger.debug("Manually retrieving all references for object " + serviceIdentity().buildIdentity(obj)); + logger.debug("Manually retrieving all references for object " + serviceIdentity().buildIdentity(obj)); } ClassDescriptor cld = getClassDescriptor(obj.getClass()); try @@ -1620,8 +1716,7 @@ public void refreshRelationships(Object obj, ClassDescriptor cld) { - Identity oid = serviceIdentity().buildIdentity(cld, obj); - refreshRelationships(obj, oid, cld); + refreshRelationships(obj, serviceIdentity().buildIdentity(obj), cld); } /** @@ -1635,6 +1730,8 @@ */ public void refreshRelationships(Object obj, Identity oid, ClassDescriptor cld) { + if(skipRefreshRelationship.contains(cld)) return; + Iterator iter; CollectionDescriptor cds; ObjectReferenceDescriptor rds; @@ -1651,6 +1748,7 @@ } try { + boolean needsRelationshipRefresh = false; serviceSessionCache().enableMaterializationCache(); if(tmp == null) { @@ -1664,6 +1762,7 @@ if (cds.isRefresh()) { referencesBroker.retrieveCollection(obj, cld, cds, false); + needsRelationshipRefresh = true; } } iter = cld.getObjectReferenceDescriptors().iterator(); @@ -1673,9 +1772,14 @@ if (rds.isRefresh()) { referencesBroker.retrieveReference(obj, cld, rds, false); + needsRelationshipRefresh = true; } } serviceSessionCache().disableMaterializationCache(); + if(!needsRelationshipRefresh) + { + skipRefreshRelationship.add(cld); + } } catch(RuntimeException e) { @@ -1705,7 +1809,7 @@ /** * Retrieves the collection described by the given creation context. - * + * * @param context The creation context * @return The collection */ @@ -1753,8 +1857,8 @@ } return newObj; } - - + + /** * Retrieve an full materialized (dependent on the metadata settings) * object by it's identity from the database, as well as caching the @@ -1776,7 +1880,7 @@ } ClassDescriptor cld = getClassDescriptor(c); - Object newObj = getPlainDBObject(cld,oid); + Object newObj = getPlainDBObject(cld,oid); // loading references is useful only when the Object could be found in db: if (newObj != null) @@ -1838,7 +1942,7 @@ } } } - + /** * returns an Iterator that iterates Objects of class c if calling the .next() * method. The Elements returned come from a SELECT ... WHERE Statement @@ -1867,7 +1971,6 @@ { result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex()); } - return result; } @@ -1928,6 +2031,11 @@ { refreshInstance(obj, id, cld); } + else + { + // refresh LOB-fields + serviceLobHelper().internalAutoRefresh(obj, cld); + } // now refresh all references refreshRelationships(obj, id, cld); } @@ -1977,7 +2085,19 @@ if (query instanceof QueryByIdentity) { Identity oid = getIdentity((QueryByIdentity) query); - result = getObjectByIdentity(oid); + // only non-transient objects can be found in DB + if(oid.isTransient()) + { + if(logger.isEnabledFor(Logger.INFO)) logger.info( + "The object to query was detected as transient, will only lookup the cache for object instance. Identity=" + + oid + ", the query object was " + query); + // transient objects can only be found in session cache + result = serviceSessionCache().lookup(oid, SessionCache.LEVEL_SESSION); + } + else + { + result = getObjectByIdentity(oid); + } } else { @@ -2234,14 +2354,19 @@ * * @param obj * @param cld - * @param oid + * @param oid * @param insert * @param ignoreReferences */ private void storeToDb(Object obj, ClassDescriptor cld, Identity oid, boolean insert, boolean ignoreReferences) { // 1. link and store 1:1 references - storeReferences(obj, cld, insert, ignoreReferences); + // we can skip handling for 1:1 references if + // it's enabled and the class has no super-reference + if(!(ignoreReferences && cld.getSuperReference() == null)) + { + storeReferences(obj, cld, insert, ignoreReferences); + } Object[] pkValues = oid.getPrimaryKeyValues(); if (!serviceBrokerHelper().assertValidPksForStore(cld.getPkFields(), pkValues)) @@ -2281,6 +2406,11 @@ if (insert) { dbAccess.executeInsert(cld, obj); + if(oid.isTransient()) + { + // Create a new Identity based on the current set of primary key values. + oid = serviceIdentity().buildIdentity(cld, obj); + } } // else use UPDATE else @@ -2296,12 +2426,10 @@ throw e; } } - // Create a new Identity based on the current set of primary key values. - Identity newOid = serviceIdentity().buildIdentity(cld, obj); // cache object for symmetry with getObjectByXXX() // Add the object to the cache. - serviceSessionCache().cache(newOid, obj, SessionCache.TYPE_WRITE, SessionCache.LEVEL_DEEP); - // 3. store 1:n and m:n associations + serviceSessionCache().cache(oid, obj, SessionCache.TYPE_WRITE, SessionCache.LEVEL_DEEP); + // 3. store 1:n and m:n associations except if we have to skip if(!ignoreReferences) storeCollections(obj, cld, insert); } @@ -2449,7 +2577,7 @@ { query.setFetchSize(1); query.preprocess(this); - + if (query instanceof QueryBySQL) { if(logger.isDebugEnabled()) logger.debug("Creating SQL-RsIterator for class ["+cld.getClassNameOfObject()+"]"); @@ -2470,12 +2598,12 @@ ChainingIterator chainingIter = new ChainingIterator(); Collection cldInfos = serviceBrokerHelper().getExtentsDescriptors(cld); Iterator extents = cldInfos.iterator(); - + while (extents.hasNext()) { BrokerHelper.CldInfo cldInfo = (BrokerHelper.CldInfo) extents.next(); Query theQuery = serviceBrokerHelper().setConcreteClassCriteria(this, (QueryByCriteria)query, cldInfo); - + if(logger.isDebugEnabled()) logger.debug("Adding RsIterator of class ["+ cldInfo.cld.getClassNameOfObject()+"] to ChainingIterator"); // add the iterator to the chaining iterator. @@ -2520,13 +2648,40 @@ return persistenceConfiguration.getModel(); } + protected void finalize() + { + try + { + super.finalize(); + // if not closed ==> broker leak detected + if (!isClosed) + { + String msg = "Garbage collection: Unclosed PersistenceBroker instance detected, check code for PB leaks."; + if(brokerLeakDetection) + { + logger.error(msg + " Broker caller stack is: " + + SystemUtils.LINE_SEPARATOR + brokerStackTrace); + } + else + { + logger.warn(msg); + } + close(); + } + } + catch(Throwable ignore) + { + // ignore + } + } + /** * clean up the maps for reuse by the next transaction. */ - private void clearRegistrationLists() + private void refreshRegistrationLists() { - nowStoring.clear(); - deletedDuringTransaction.clear(); + if(nowStoring.size() > 0) nowStoring = new IdentityArrayList(); + if(deletedDuringTransaction.size() > 0) deletedDuringTransaction.clear(); /* arminw: for better performance I don't register MtoNBroker as listner, @@ -2550,7 +2705,7 @@ { mtoNBroker.storeMtoNImplementor(m2n); } - + /** * Get the Identity out of the Query. * @param aQuery @@ -2559,21 +2714,21 @@ private Identity getIdentity(QueryByIdentity aQuery) { Object obj = aQuery.getIdentityObject(); - Identity oid = null; - + Identity oid ; if (obj instanceof Identity) { oid = (Identity) obj; } else { - // TODO: This workaround doesn't allow 'null' for PK fields - if (!serviceBrokerHelper().hasNullPKField(getClassDescriptor(obj.getClass()), obj)) - { - oid = serviceIdentity().buildIdentity(obj); - } +// if (!serviceBrokerHelper().hasNullPKField(getClassDescriptor(obj.getClass()), obj)) +// { +// oid = serviceIdentity().buildIdentity(obj); +// } + // TODO: Check this! The above check is no longer needed, because "new objects" will automatic + // have transient Identity objects + oid = serviceIdentity().buildIdentity(obj); } - return oid; }
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/core/QueryReferenceBroker.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/core/QueryReferenceBroker.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/core/QueryReferenceBroker.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/core/QueryReferenceBroker.java Sat Jul 15 07:20:25 2006 @@ -27,6 +27,7 @@ import org.apache.ojb.broker.ManageableCollection; import org.apache.ojb.broker.PBLifeCycleEvent; import org.apache.ojb.broker.PersistenceBrokerException; +import org.apache.ojb.broker.TransientObjectException; import org.apache.ojb.broker.accesslayer.CollectionCreationContext; import org.apache.ojb.broker.accesslayer.OJBIterator; import org.apache.ojb.broker.accesslayer.PagingIterator; @@ -50,6 +51,7 @@ import org.apache.ojb.broker.util.BrokerHelper; import org.apache.ojb.broker.util.logging.Logger; import org.apache.ojb.broker.util.logging.LoggerFactory; +import org.apache.commons.lang.SystemUtils; /** * Encapsulates 1:1 and 1:n references and collection references stuff. @@ -85,7 +87,7 @@ throws ClassNotPersistenceCapableException, PersistenceBrokerException { ManageableCollection collection = pb.getConfiguration().getCollectionFactory().createCollection(context); - Query query = context.getQuery(); + Query query = context.getQuery(); if (query == null) { @@ -152,11 +154,12 @@ else { //warn the user - log.warn("Candidate object ["+candidate - +"] class ["+candidate.getClass().getName() - +"] is not a subtype of ["+itemClass.getName() - +"] or any type of proxy. NOT INCLUDED in result collection"); - } + if(log.isEnabledFor(Logger.WARN)) log.warn(SystemUtils.LINE_SEPARATOR + + "Candidate object ["+candidate + "]" + +" of "+candidate.getClass() + SystemUtils.LINE_SEPARATOR + +"is not a subtype of "+itemClass +" or any type of proxy - e.g. this can be the result of a" + + SystemUtils.LINE_SEPARATOR +"'declared' extent in "+itemClass + "." + + " This can cause side-effects and will NOT INCLUDED in the current generated collection result"); } if (prefetchProxies && (handler != null) && (cld.getProxyPrefetchingLimit() > 0) && addRetrievalTask(candidate, this)) @@ -166,6 +169,7 @@ } } } + if (isRetrievalTasksCreated) { // turn off auto prefetching for related proxies @@ -200,6 +204,7 @@ // catch runtime exc. to guarantee clearing of // materialization cache on failure pb.serviceSessionCache().clearMaterializationCache(); + log.error(e); throw e; } finally @@ -273,7 +278,7 @@ } catch (Throwable e) { - e.printStackTrace(); + log.error(e); throw new PersistenceBrokerException("Can't query for collection",e); } } @@ -325,7 +330,8 @@ } ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) key; - if (ord.isSuperReferenceDescriptor() || ord.isLazy() || (ord.getItemProxyClass() != null)) +// if (ord.isSuperReferenceDescriptor() || ord.isLazy() || (ord.getItemProxyClass() != null)) + if (ord.isLazy() || (ord.getItemProxyClass() != null)) { continue; } @@ -369,12 +375,12 @@ else if ( pb.serviceSessionCache().lookup(id) != null ) { refObj = pb.getObjectByIdentity(id); - if (rds.isSuperReferenceDescriptor()) + if (rds.isSuperReferenceDescriptor()) { // walk the super-references ClassDescriptor superCld = cld.getRepository().getDescriptorFor(rds.getItemClass()); retrieveReferences(refObj, superCld, false); - retrieveCollections(refObj, superCld, false); + retrieveCollections(refObj, superCld, false); } } else if ((m_retrievalTasks != null) @@ -422,6 +428,52 @@ } /** + * Retrieve a single Reference. + * This implementation retrieves a referenced object from the data backend + * if <b>cascade-retrieve</b> is true or if <b>forced</b> is true. + * + * @param obj - object that will have it's field set with a referenced object. + * @param cld - the ClassDescriptor describring obj + * @param rds - the ObjectReferenceDescriptor of the reference attribute to be loaded + * @param forced - if set to true, the reference is loaded even if the rds differs. + */ + public void retrieveProxyReference(Object obj, ClassDescriptor cld, ObjectReferenceDescriptor rds, boolean forced) + { + PersistentField refField; + Object refObj = null; + + pb.serviceSessionCache().enableMaterializationCache(); + try + { + Identity id = getReferencedObjectIdentity(obj, rds, cld); + if (id != null){ + refObj = pb.createProxy(rds.getItemClass(), id); + } + refField = rds.getPersistentField(); + refField.set(obj, refObj); + + if ((refObj != null) && prefetchProxies + && (m_retrievalTasks != null) + && (rds.getProxyPrefetchingLimit() > 0)) + { + IndirectionHandler handler = pb.getProxyFactory().getIndirectionHandler(refObj); + + if ((handler != null) && addRetrievalTask(obj, rds)) + { + new PBMaterializationListener(obj, m_retrievalTasks, rds, rds.getProxyPrefetchingLimit(), pb.getProxyFactory()); + } + } + pb.serviceSessionCache().disableMaterializationCache(); + } + catch(RuntimeException e) + { + pb.serviceSessionCache().clearMaterializationCache(); + throw e; + } + + } + + /** * Retrieve all References * * @param newObj the instance to be loaded or refreshed @@ -459,6 +511,43 @@ } } + /** + * Retrieve all References + * + * @param newObj the instance to be loaded or refreshed + * @param cld the ClassDescriptor of the instance + * @param forced if set to true loading is forced even if cld differs. + */ + public void retrieveProxyReferences(Object newObj, ClassDescriptor cld, boolean forced) throws PersistenceBrokerException + { + Iterator i = cld.getObjectReferenceDescriptors().iterator(); + + // turn off auto prefetching for related proxies + final Class saveClassToPrefetch = classToPrefetch; + classToPrefetch = null; + + pb.serviceSessionCache().enableMaterializationCache(); + try + { + while (i.hasNext()) + { + ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next(); + retrieveProxyReference(newObj, cld, rds, forced); + } + + pb.serviceSessionCache().disableMaterializationCache(); + } + catch(RuntimeException e) + { + pb.serviceSessionCache().clearMaterializationCache(); + throw e; + } + finally + { + classToPrefetch = saveClassToPrefetch; + } + } + /** * retrieves an Object reference's Identity. * <br> @@ -491,7 +580,7 @@ else { // ensure that top-level extents are used for Identities - return new Identity(rds.getItemClass(), pb.getTopLevelClass(rds.getItemClass()), fkValues); + return pb.serviceIdentity().buildIdentity(rds.getItemClass(), pb.getTopLevelClass(rds.getItemClass()), fkValues); } return null; } @@ -559,8 +648,15 @@ } catch (Exception e) { - log.error("Error instantiating obj: " + e.getMessage(), e); - throw new PersistenceBrokerException(e); + log.error("Error while instantiate object " + id + ", msg: "+ e.getMessage(), e); + if(e instanceof PersistenceBrokerException) + { + throw (PersistenceBrokerException) e; + } + else + { + throw new PersistenceBrokerException(e); + } } } else @@ -582,6 +678,27 @@ */ public void retrieveCollection(Object obj, ClassDescriptor cld, CollectionDescriptor cds, boolean forced) { + doRetrieveCollection(obj, cds, forced); + } + + /** + * Retrieve a single Proxied Collection on behalf of <b>obj</b>. + * The Collection is retrieved only if <b>cascade.retrieve is true</b> + * or if <b>forced</b> is set to true. * + * + * @param obj - the object to be updated + * @param cld - the ClassDescriptor describing obj + * @param cds - the CollectionDescriptor describing the collection attribute to be loaded + * @param forced - if set to true a proxy will be placed, even if cds differs. + * + */ + public void retrieveProxyCollection(Object obj, ClassDescriptor cld, CollectionDescriptor cds, boolean forced) + { + doRetrieveCollection(obj, cds, forced); + } + + private void doRetrieveCollection(Object obj, CollectionDescriptor cds, boolean forced) + { if (forced || cds.getCascadeRetrieve()) { if ((m_retrievalTasks != null) && !cds.isLazy() @@ -768,9 +885,16 @@ * * @param oid the Identity of the Object to retrieve * @return The resulting query + * @throws TransientObjectException If the specified [EMAIL PROTECTED] org.apache.ojb.broker.Identity} + * is <em>transient</em> this exception will be thrown, because it's not possible to build + * a valid query. */ - public Query getPKQuery(Identity oid) + public Query getPKQuery(Identity oid) throws TransientObjectException { + if(oid.isTransient()) + { + throw new TransientObjectException("Not allowed to build PK Query for a transient object"); + } Object[] values = oid.getPrimaryKeyValues(); ClassDescriptor cld = pb.getClassDescriptor(oid.getObjectsTopLevelClass()); FieldDescriptor[] fields = cld.getPkFields(); @@ -794,6 +918,24 @@ */ public void retrieveCollections(Object newObj, ClassDescriptor cld, boolean forced) throws PersistenceBrokerException { + doRetrieveCollections(newObj, cld, forced, false); + } + + /** + * Retrieve all Collection attributes of a given instance, and make all of the Proxy Collections + * + * @param newObj the instance to be loaded or refreshed + * @param cld the ClassDescriptor of the instance + * @param forced if set to true, loading is forced even if cld differs + * + */ + public void retrieveProxyCollections(Object newObj, ClassDescriptor cld, boolean forced) throws PersistenceBrokerException + { + doRetrieveCollections(newObj, cld, forced, true); + } + + private void doRetrieveCollections(Object newObj, ClassDescriptor cld, boolean forced, boolean forceProxyCollection) throws PersistenceBrokerException + { Iterator i = cld.getCollectionDescriptors().iterator(); // turn off auto prefetching for related proxies @@ -806,7 +948,14 @@ while (i.hasNext()) { CollectionDescriptor cds = (CollectionDescriptor) i.next(); - retrieveCollection(newObj, cld, cds, forced); + if(forceProxyCollection) + { + retrieveProxyCollection(newObj, cld, cds, forced); + } + else + { + retrieveCollection(newObj, cld, cds, forced); + } } pb.serviceSessionCache().disableMaterializationCache(); } @@ -858,7 +1007,7 @@ PBMaterializationListener(Object owner, - HashMap retrievalTasks, Object key, int limit, ProxyFactory proxyFactory) + HashMap retrievalTasks, Object key, int limit, ProxyFactory proxyFactory) { super(owner, retrievalTasks, key, limit, proxyFactory); } @@ -918,7 +1067,7 @@ protected ProxyFactory _proxyFactory; PBPrefetchingListener(Object owner, HashMap retrievalTasks, - Object key, int limit, ProxyFactory proxyFactory) + Object key, int limit, ProxyFactory proxyFactory) { _retrievalTasks = retrievalTasks; _key = key; @@ -997,7 +1146,7 @@ CollectionProxy _listenedCollection; PBCollectionProxyListener(Object owner, - HashMap retrievalTasks, CollectionDescriptor key, int limit, ProxyFactory proxyFactory) + HashMap retrievalTasks, CollectionDescriptor key, int limit, ProxyFactory proxyFactory) { super(owner, retrievalTasks, key, limit, proxyFactory); } Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/core/ValueContainer.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/core/ValueContainer.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/core/ValueContainer.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/core/ValueContainer.java Sat Jul 15 07:20:25 2006 @@ -103,7 +103,7 @@ public String toString() { return "[" + ClassUtils.getShortClassName(this.getClass()) + ": jdbcType=" - + jdbcType.getTypeAsString() + + jdbcType + ", value=" + value + "]"; } } Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/IsolationLevels.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/IsolationLevels.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/IsolationLevels.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/IsolationLevels.java Sat Jul 15 07:20:25 2006 @@ -176,7 +176,7 @@ /** * Literal constant representing that no isolation level is used. */ - public final static String LITERAL_IL_NO_LOCKING = "no-lock"; + public final static String LITERAL_IL_NO_LOCKING = "none"; /** * Literal constant representing the uncommited read isolation level. Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolation.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolation.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolation.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolation.java Sat Jul 15 07:20:25 2006 @@ -15,12 +15,13 @@ * limitations under the License. */ +import java.io.Serializable; /** * This interface defines method that a Locking Strategy must implement * according to the isolation level it represents. */ -abstract class LockIsolation +abstract class LockIsolation implements Serializable { /** * Returns the isolation level identity. Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolationManager.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolationManager.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolationManager.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockIsolationManager.java Sat Jul 15 07:20:25 2006 @@ -15,14 +15,14 @@ * limitations under the License. */ - +import java.io.Serializable; /** * Factory class used to obtain the proper [EMAIL PROTECTED] LockIsolation} level. * * @version $Id$ */ -class LockIsolationManager +class LockIsolationManager implements Serializable { private LockIsolation readUncommitedStrategy; private LockIsolation readCommitedStrategy; Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManager.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManager.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManager.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManager.java Sat Jul 15 07:20:25 2006 @@ -1,6 +1,6 @@ package org.apache.ojb.broker.locking; -/* Copyright 2002-2004 The Apache Software Foundation +/* Copyright 2002-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,15 +53,16 @@ /** * The maximal time to wait for acquire a lock. * - * @return The time to wait for aquire a lock. + * @return The time to wait for aquire a lock in milliseconds. */ public long getBlockTimeout(); /** - * Set the maximal time to wait for acquire a lock in milliseconds. + * Set the maximal time to wait for acquire a lock. + * <br/> * All so called <em>non-blocking</em> implementation will ignore this setting. * - * @param timeout The time to wait for acquire a lock. + * @param timeout The time to wait for acquire a lock in milliseconds. */ public void setBlockTimeout(long timeout); Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerCommonsImpl.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerCommonsImpl.java?rev=422231&r1=422230&r2=422231&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerCommonsImpl.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerCommonsImpl.java Sat Jul 15 07:20:25 2006 @@ -1,13 +1,5 @@ package org.apache.ojb.broker.locking; -import org.apache.commons.lang.SystemUtils; -import org.apache.commons.transaction.locking.GenericLock; -import org.apache.commons.transaction.locking.GenericLockManager; -import org.apache.commons.transaction.locking.LockException; -import org.apache.commons.transaction.util.LoggerFacade; -import org.apache.ojb.broker.util.logging.Logger; -import org.apache.ojb.broker.util.logging.LoggerFactory; - /* Copyright 2002-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +15,14 @@ * limitations under the License. */ +import org.apache.commons.lang.SystemUtils; +import org.apache.commons.transaction.locking.GenericLock; +import org.apache.commons.transaction.locking.GenericLockManager; +import org.apache.commons.transaction.locking.LockException; +import org.apache.commons.transaction.util.LoggerFacade; +import org.apache.ojb.broker.util.logging.Logger; +import org.apache.ojb.broker.util.logging.LoggerFactory; + /** * A [EMAIL PROTECTED] LockManager} implementation based on apache's commons-transaction * locking part. @@ -30,7 +30,6 @@ * The timeout of locks is currently (OJB 1.0.2) not supported, maybe * in further versions. * - * @author <a href="mailto:[EMAIL PROTECTED]">Armin Waibel</a> * @version $Id$ */ public class LockManagerCommonsImpl implements LockManager @@ -49,12 +48,11 @@ public LockManagerCommonsImpl() { - LoggerFacade logFacade = new LoggerFacadeImpl(); // default lock timeout this.lockTimeout = DEFAULT_LOCK_TIMEOUT; // default time to wait for a lock this.blockTimeout = DEFAULT_BLOCK_TIMEOUT; - lm = new OJBLockManager(logFacade, blockTimeout, GenericLockManager.DEFAULT_CHECK_THRESHHOLD); + lm = new OJBLockManager(new LoggerFacadeImpl(), blockTimeout, GenericLockManager.DEFAULT_CHECK_THRESHHOLD); } public long getLockTimeout() --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
