mkalen 2005/03/03 13:45:26
Modified: src/java/org/apache/ojb/broker/core/proxy Tag:
OJB_1_0_RELEASE CollectionProxyDefaultImpl.java
Log:
Ajust fix for re-activating proxy's metadata profile, to avoid stale local
DescriptorRepository references in QueryReferenceBroker beeing used while
loading data. (Solves MetadataMultithreadedTest#testCollectionProxySwapProfiles)
Revision Changes Path
No revision
No revision
1.7.2.5 +58 -14
db-ojb/src/java/org/apache/ojb/broker/core/proxy/CollectionProxyDefaultImpl.java
Index: CollectionProxyDefaultImpl.java
===================================================================
RCS file:
/home/cvs/db-ojb/src/java/org/apache/ojb/broker/core/proxy/CollectionProxyDefaultImpl.java,v
retrieving revision 1.7.2.4
retrieving revision 1.7.2.5
diff -u -r1.7.2.4 -r1.7.2.5
--- CollectionProxyDefaultImpl.java 25 Feb 2005 00:09:25 -0000 1.7.2.4
+++ CollectionProxyDefaultImpl.java 3 Mar 2005 21:45:26 -0000 1.7.2.5
@@ -90,13 +90,15 @@
{
MetadataManager mm = MetadataManager.getInstance();
_perThreadDescriptorsEnabled = mm.isEnablePerThreadChanges();
- if (_perThreadDescriptorsEnabled) {
+ if (_perThreadDescriptorsEnabled)
+ {
// mkalen: To minimize memory footprint we remember only the
OJB profile key
// (instead of all active class-mappings).
- Object key = mm.getCurrentProfileKey();
- if (key == null) {
+ final Object key = mm.getCurrentProfileKey();
+ if (key == null)
+ {
// mkalen: Unsupported: using proxies with per-thread
metadata changes without profile keys.
- throw new MetadataException("Trying to create a dynamic
proxy with per-thread metadata changes enabled, but no profile key.");
+ throw new MetadataException("Trying to create a Collection
proxy with per-thread metadata changes enabled, but no profile key.");
}
setProfileKey(key);
}
@@ -105,12 +107,18 @@
setQuery(query);
}
+ /**
+ * Reactivates metadata profile used when creating proxy, if needed.
+ * Calls to this method should be guarded by checking
+ * [EMAIL PROTECTED] #_perThreadDescriptorsEnabled} since the profile
never
+ * needs to be reloaded if not using pre-thread metadata changes.
+ */
protected void loadProfileIfNeeded()
{
- Object key = getProfileKey();
+ final Object key = getProfileKey();
if (key != null)
{
- MetadataManager mm = MetadataManager.getInstance();
+ final MetadataManager mm = MetadataManager.getInstance();
if (!key.equals(mm.getCurrentProfileKey()))
{
mm.loadProfile(key);
@@ -120,7 +128,7 @@
/**
* Determines whether the collection data already has been loaded from
the database.
- *
+ *
* @return <code>true</code> if the data is already loaded
*/
public boolean isLoaded()
@@ -139,9 +147,6 @@
PersistenceBroker broker = getBroker();
try
{
- if (_perThreadDescriptorsEnabled) {
- loadProfileIfNeeded();
- }
return broker.getCount(getQuery());
}
catch (Exception ex)
@@ -183,9 +188,6 @@
}
else if (_size != 0)
{
- if (_perThreadDescriptorsEnabled) {
- loadProfileIfNeeded();
- }
// TODO: returned ManageableCollection should extend
Collection to avoid
// this cast
result = (Collection)
broker.getCollectionByQuery(getCollectionClass(), getQuery());
@@ -432,6 +434,48 @@
*/
protected synchronized PersistenceBroker getBroker() throws
PBFactoryException
{
+ /*
+ mkalen:
+ NB! The loadProfileIfNeeded must be called _before_ acquiring a
broker below,
+ since some methods in PersistenceBrokerImpl will keep a local
reference to
+ the descriptor repository that was active during broker
construction/refresh
+ (not checking the repository beeing used on method invocation).
+
+ PersistenceBrokerImpl#getClassDescriptor(Class clazz) is such a
method,
+ that will throw ClassNotPersistenceCapableException on the
following scenario:
+
+ (All happens in one thread only):
+ t0: activate per-thread metadata changes
+ t1: load, register and activate profile A
+ t2: load object O1 witch collection proxy C to objects {O2} (C
stores profile key K(A))
+ t3: close broker from t2
+ t4: load, register and activate profile B
+ t5: reference O1.getO2Collection, causing C loadData() to be
invoked
+ t6: C calls getBroker
+ broker B is created and descriptorRepository is set to
descriptors from profile B
+ t7: C calls loadProfileIfNeeded, re-activating profile A
+ t8: C calls B.getCollectionByQuery
+ t9: B gets callback (via QueryReferenceBroker) to
getClassDescriptor
+ the local descriptorRepository from t6 is used!
+ => We will now try to query for {O2} with profile B
+ (even though we re-activated profile A in t7)
+ => ClassNotPersistenceCapableException
+
+ Keeping loadProfileIfNeeded() at the start of this method
changes everything from t6:
+ t6: C calls loadProfileIfNeeded, re-activating profile A
+ t7: C calls getBroker,
+ broker B is created and descriptorRepository is set to
descriptors from profile A
+ t8: C calls B.getCollectionByQuery
+ t9: B gets callback to getClassDescriptor,
+ the local descriptorRepository from t6 is used
+ => We query for {O2} with profile A
+ => All good :-)
+ */
+ if (_perThreadDescriptorsEnabled)
+ {
+ loadProfileIfNeeded();
+ }
+
PersistenceBroker broker;
if (getBrokerKey() == null)
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]