Author: arminw
Date: Thu Jan 4 19:04:34 2007
New Revision: 492879
URL: http://svn.apache.org/viewvc?view=rev&rev=492879
Log:
fix NPE issues with not existing objects of PK/FK. This could be the case if a
PK of an object is the FK to a 1:1 reference. If the reference is deleted the
PK of the main object still exists and OJB will create a proxy object for the
1:1 reference. On materialization of the proxy OJB can't find the reference,
thus we return 'null' for all object methods and log a warn message.
Modified:
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/proxy/AbstractIndirectionHandler.java
Modified:
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/proxy/AbstractIndirectionHandler.java
URL:
http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/proxy/AbstractIndirectionHandler.java?view=diff&rev=492879&r1=492878&r2=492879
==============================================================================
---
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/proxy/AbstractIndirectionHandler.java
(original)
+++
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/core/proxy/AbstractIndirectionHandler.java
Thu Jan 4 19:04:34 2007
@@ -26,9 +26,9 @@
import org.apache.ojb.broker.PersistenceBrokerFactory;
import org.apache.ojb.broker.PersistenceBrokerInternal;
import org.apache.ojb.broker.core.PersistenceBrokerThreadMapping;
-import org.apache.ojb.broker.metadata.MetadataException;
import org.apache.ojb.broker.metadata.MetadataManager;
import org.apache.ojb.broker.util.logging.LoggerFactory;
+import org.apache.ojb.broker.util.logging.Logger;
/**
* Abstract implementation for the indirection handler used by ojb's proxies.
@@ -37,6 +37,7 @@
*/
public abstract class AbstractIndirectionHandler implements IndirectionHandler
{
+ private static Logger log =
LoggerFactory.getLogger(AbstractIndirectionHandler.class);
private static final long serialVersionUID = 2L;
@@ -54,19 +55,20 @@
/** The materialization listeners */
private transient ArrayList _listeners;
- /**
- * Creates a new indirection handler for the indicated object.
- *
- * @param brokerKey
- * The key of the persistence broker
- * @param id
- * The identity of the subject
- */
- public AbstractIndirectionHandler(PBKey brokerKey, Identity id)
- {
- setBrokerKey(brokerKey);
- setIdentity(id);
- }
+ /**
+ * Creates a new indirection handler for the indicated object.
+ *
+ * @param brokerKey
+ * The key of the persistence broker
+ * @param id
+ * The identity of the subject
+ */
+ public AbstractIndirectionHandler(PBKey brokerKey, Identity id)
+ {
+ setBrokerKey(brokerKey);
+ setIdentity(id);
+ _perThreadDescriptorsEnabled =
MetadataManager.getInstance().isEnablePerThreadChanges();
+ }
/**
* Reactivates metadata profile used when creating proxy, if needed.
@@ -87,126 +89,126 @@
}
}
- /**
- * Returns the identity of the subject.
- *
- * @return The identity
- */
- public Identity getIdentity()
- {
- return _id;
- }
-
- /**
- * Sets the identity of the subject of this indirection handler.
- *
- * @param identity
- */
- protected void setIdentity(Identity identity)
- {
- _id = identity;
- }
-
- /**
- * Returns the key of the persistence broker used by this indirection
- * handler.
- *
- * @return The broker key
- */
- public PBKey getBrokerKey()
- {
- return _brokerKey;
- }
-
- /**
- * Sets the key of the persistence broker used by this indirection
handler.
- *
- * @param brokerKey
- * The broker key
- */
- protected void setBrokerKey(PBKey brokerKey)
- {
- _brokerKey = brokerKey;
- }
-
- /**
- * Adds a materialization listener.
- *
- * @param listener
- * The listener to add
- */
- public synchronized void addListener(MaterializationListener listener)
- {
- if (_listeners == null)
- {
- _listeners = new ArrayList();
- }
- // add listener only once
- if (!_listeners.contains(listener))
- {
- _listeners.add(listener);
- }
- }
-
- /**
- * Removes a materialization listener.
- *
- * @param listener
- * The listener to remove
- */
- public synchronized void removeListener(MaterializationListener
listener)
- {
- if (_listeners != null)
- {
- _listeners.remove(listener);
- }
- }
-
- /**
- * Calls beforeMaterialization on all registered listeners in the
reverse
- * order of registration.
- */
- protected void beforeMaterialization()
- {
- if (_listeners != null)
- {
- MaterializationListener listener;
+ /**
+ * Returns the identity of the subject.
+ *
+ * @return The identity
+ */
+ public Identity getIdentity()
+ {
+ return _id;
+ }
+
+ /**
+ * Sets the identity of the subject of this indirection handler.
+ *
+ * @param identity
+ */
+ protected void setIdentity(Identity identity)
+ {
+ _id = identity;
+ }
+
+ /**
+ * Returns the key of the persistence broker used by this indirection
+ * handler.
+ *
+ * @return The broker key
+ */
+ public PBKey getBrokerKey()
+ {
+ return _brokerKey;
+ }
+
+ /**
+ * Sets the key of the persistence broker used by this indirection handler.
+ *
+ * @param brokerKey
+ * The broker key
+ */
+ protected void setBrokerKey(PBKey brokerKey)
+ {
+ _brokerKey = brokerKey;
+ }
+
+ /**
+ * Adds a materialization listener.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public synchronized void addListener(MaterializationListener listener)
+ {
+ if (_listeners == null)
+ {
+ _listeners = new ArrayList();
+ }
+ // add listener only once
+ if (!_listeners.contains(listener))
+ {
+ _listeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes a materialization listener.
+ *
+ * @param listener
+ * The listener to remove
+ */
+ public synchronized void removeListener(MaterializationListener listener)
+ {
+ if (_listeners != null)
+ {
+ _listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Calls beforeMaterialization on all registered listeners in the reverse
+ * order of registration.
+ */
+ protected void beforeMaterialization()
+ {
+ if (_listeners != null)
+ {
+ MaterializationListener listener;
if (_perThreadDescriptorsEnabled) {
loadProfileIfNeeded();
}
- for (int idx = _listeners.size() - 1; idx >= 0; idx--)
- {
- listener = (MaterializationListener)
_listeners.get(idx);
- listener.beforeMaterialization(this, _id);
- }
- }
- }
-
- /**
- * Calls afterMaterialization on all registered listeners in the reverse
- * order of registration.
- */
- protected void afterMaterialization()
- {
- if (_listeners != null)
- {
- MaterializationListener listener;
+ for (int idx = _listeners.size() - 1; idx >= 0; idx--)
+ {
+ listener = (MaterializationListener) _listeners.get(idx);
+ listener.beforeMaterialization(this, _id);
+ }
+ }
+ }
+
+ /**
+ * Calls afterMaterialization on all registered listeners in the reverse
+ * order of registration.
+ */
+ protected void afterMaterialization()
+ {
+ if (_listeners != null)
+ {
+ MaterializationListener listener;
if (_perThreadDescriptorsEnabled) {
loadProfileIfNeeded();
}
- // listeners may remove themselves during the
afterMaterialization
- // callback.
- // thus we must iterate through the listeners vector
from back to
- // front to avoid index problems.
- for (int idx = _listeners.size() - 1; idx >= 0; idx--)
- {
- listener = (MaterializationListener)
_listeners.get(idx);
- listener.afterMaterialization(this,
_realSubject);
- }
- }
- }
+ // listeners may remove themselves during the afterMaterialization
+ // callback.
+ // thus we must iterate through the listeners vector from back to
+ // front to avoid index problems.
+ for (int idx = _listeners.size() - 1; idx >= 0; idx--)
+ {
+ listener = (MaterializationListener) _listeners.get(idx);
+ listener.afterMaterialization(this, _realSubject);
+ }
+ }
+ }
/**
* Gets the persistence broker used by this indirection handler.
@@ -231,7 +233,7 @@
if no PBKey is set we throw an exception, because we don't
know which PB (connection) should be used.
*/
- throw new OJBRuntimeException("Can't find associated PBKey. Need
PBKey to obtain a valid" +
+ throw new OJBRuntimeException("Can't find associated PBKey. Need
PBKey to obtain a valid " +
"PersistenceBroker instance from
intern resources.");
}
// first try to use the current threaded broker to avoid blocking
@@ -246,129 +248,129 @@
return new TemporaryBrokerWrapper(broker, needsClose);
}
- /**
- * [Copied from [EMAIL PROTECTED]
java.lang.reflect.InvocationHandler}]:<br/>
- * Processes a method invocation on a proxy instance and returns the
result.
- * This method will be invoked on an invocation handler when a method is
- * invoked on a proxy instance that it is associated with.
- *
- * @param proxy
- * The proxy instance that the method was invoked on
- *
- * @param method
- * The <code>Method</code> instance corresponding to the
- * interface method invoked on the proxy instance. The
declaring
- * class of the <code>Method</code> object will be the
- * interface that the method was declared in, which may be a
- * superinterface of the proxy interface that the proxy class
- * inherits the method through.
- *
- * @param args
- * An array of objects containing the values of the arguments
- * passed in the method invocation on the proxy instance, or
- * <code>null</code> if interface method takes no arguments.
- * Arguments of primitive types are wrapped in instances of
the
- * appropriate primitive wrapper class, such as
- * <code>java.lang.Integer</code> or
- * <code>java.lang.Boolean</code>.
- *
- * @return The value to return from the method invocation on the proxy
- * instance. If the declared return type of the interface
method is
- * a primitive type, then the value returned by this method
must be
- * an instance of the corresponding primitive wrapper class;
- * otherwise, it must be a type assignable to the declared
return
- * type. If the value returned by this method is
<code>null</code>
- * and the interface method's return type is primitive, then a
- * <code>NullPointerException</code> will be thrown by the
method
- * invocation on the proxy instance. If the value returned by
this
- * method is otherwise not compatible with the interface
method's
- * declared return type as described above, a
- * <code>ClassCastException</code> will be thrown by the method
- * invocation on the proxy instance.
- *
- * @throws PersistenceBrokerException
- * The exception to throw from the method invocation on the
- * proxy instance. The exception's type must be assignable
- * either to any of the exception types declared in the
- * <code>throws</code> clause of the interface method or to
- * the unchecked exception types
- * <code>java.lang.RuntimeException</code> or
- * <code>java.lang.Error</code>. If a checked exception is
- * thrown by this method that is not assignable to any of
the
- * exception types declared in the <code>throws</code>
clause
- * of the interface method, then an
- * [EMAIL PROTECTED]
java.lang.reflect.UndeclaredThrowableException}
- * containing the exception that was thrown by this method
will
- * be thrown by the method invocation on the proxy instance.
- *
- * @see java.lang.reflect.UndeclaredThrowableException
- */
- public Object invoke(Object proxy, Method method, Object[] args)
- {
- Object subject;
- String methodName = method.getName();
-
- try
- {
- // [andrew clute]
- // short-circuit any calls to a finalize methjod if the
subject
- // has not been retrieved yet
- if ("finalize".equals(methodName) && _realSubject ==
null)
- {
- return null;
- }
-
- // [andrew clute]
- // When trying to serialize a proxy, we need to
determine how to
- // handle it
- if ("writeReplace".equals(methodName))
- {
- if (_realSubject == null)
- {
- // Unmaterialized proxies are replaced
by simple
- // serializable
- // objects that can be unserialized
without classloader
- // issues
- return generateSerializableProxy();
- } else
- {
- // Materiliazed objects should be
passed back as they might
- // have
- // been mutated
- return getRealSubject();
- }
- }
-
- // [tomdz]
- // Previously the hashcode of the identity would have
been used
- // but this requires a compatible hashCode
implementation in the
- // proxied object (which is somewhat unlikely, even the
default
- // hashCode implementation does not fulfill this
requirement)
- // for those that require this behavior, a custom
indirection
- // handler can be used, or the hashCode of the identity
- /*
- * if ("hashCode".equals(methodName)) { return new
- * Integer(_id.hashCode()); }
- */
-
- // [tomdz]
- // this would handle toString differently for
non-materialized
- // proxies
- // (to avoid materialization due to logging)
- // however toString should be a normal business method
which
- // materializes the proxy
- // if this is not desired, then the
ProxyHandler.toString(Object)
- // method
- // should be used instead (e.g. for logging within OJB)
- /*
- * if ((realSubject == null) &&
"toString".equals(methodName)) {
- * return "unmaterialized proxy for " + id; }
- */
-
- // BRJ: make sure that the object to be compared is a
real object
- // otherwise equals may return false.
- if ("equals".equals(methodName) && args[0] != null)
- {
+ /**
+ * [Copied from [EMAIL PROTECTED]
java.lang.reflect.InvocationHandler}]:<br/>
+ * Processes a method invocation on a proxy instance and returns the
result.
+ * This method will be invoked on an invocation handler when a method is
+ * invoked on a proxy instance that it is associated with.
+ *
+ * @param proxy
+ * The proxy instance that the method was invoked on
+ *
+ * @param method
+ * The <code>Method</code> instance corresponding to the
+ * interface method invoked on the proxy instance. The declaring
+ * class of the <code>Method</code> object will be the
+ * interface that the method was declared in, which may be a
+ * superinterface of the proxy interface that the proxy class
+ * inherits the method through.
+ *
+ * @param args
+ * An array of objects containing the values of the arguments
+ * passed in the method invocation on the proxy instance, or
+ * <code>null</code> if interface method takes no arguments.
+ * Arguments of primitive types are wrapped in instances of the
+ * appropriate primitive wrapper class, such as
+ * <code>java.lang.Integer</code> or
+ * <code>java.lang.Boolean</code>.
+ *
+ * @return The value to return from the method invocation on the proxy
+ * instance. If the declared return type of the interface method is
+ * a primitive type, then the value returned by this method must be
+ * an instance of the corresponding primitive wrapper class;
+ * otherwise, it must be a type assignable to the declared return
+ * type. If the value returned by this method is <code>null</code>
+ * and the interface method's return type is primitive, then a
+ * <code>NullPointerException</code> will be thrown by the method
+ * invocation on the proxy instance. If the value returned by this
+ * method is otherwise not compatible with the interface method's
+ * declared return type as described above, a
+ * <code>ClassCastException</code> will be thrown by the method
+ * invocation on the proxy instance.
+ *
+ * @throws PersistenceBrokerException
+ * The exception to throw from the method invocation on the
+ * proxy instance. The exception's type must be assignable
+ * either to any of the exception types declared in the
+ * <code>throws</code> clause of the interface method or to
+ * the unchecked exception types
+ * <code>java.lang.RuntimeException</code> or
+ * <code>java.lang.Error</code>. If a checked exception is
+ * thrown by this method that is not assignable to any of the
+ * exception types declared in the <code>throws</code> clause
+ * of the interface method, then an
+ * [EMAIL PROTECTED]
java.lang.reflect.UndeclaredThrowableException}
+ * containing the exception that was thrown by this method will
+ * be thrown by the method invocation on the proxy instance.
+ *
+ * @see java.lang.reflect.UndeclaredThrowableException
+ */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ {
+ Object subject;
+ String methodName = method.getName();
+
+ try
+ {
+ // [andrew clute]
+ // short-circuit any calls to a finalize methjod if the subject
+ // has not been retrieved yet
+ if ("finalize".equals(methodName) && _realSubject == null)
+ {
+ return null;
+ }
+
+ // [andrew clute]
+ // When trying to serialize a proxy, we need to determine how to
+ // handle it
+ if ("writeReplace".equals(methodName))
+ {
+ if (_realSubject == null)
+ {
+ // Unmaterialized proxies are replaced by simple
+ // serializable
+ // objects that can be unserialized without classloader
+ // issues
+ return generateSerializableProxy();
+ } else
+ {
+ // Materiliazed objects should be passed back as they might
+ // have
+ // been mutated
+ return getRealSubject();
+ }
+ }
+
+ // [tomdz]
+ // Previously the hashcode of the identity would have been used
+ // but this requires a compatible hashCode implementation in the
+ // proxied object (which is somewhat unlikely, even the default
+ // hashCode implementation does not fulfill this requirement)
+ // for those that require this behavior, a custom indirection
+ // handler can be used, or the hashCode of the identity
+ /*
+ * if ("hashCode".equals(methodName)) { return new
+ * Integer(_id.hashCode()); }
+ */
+
+ // [tomdz]
+ // this would handle toString differently for non-materialized
+ // proxies
+ // (to avoid materialization due to logging)
+ // however toString should be a normal business method which
+ // materializes the proxy
+ // if this is not desired, then the ProxyHandler.toString(Object)
+ // method
+ // should be used instead (e.g. for logging within OJB)
+ /*
+ * if ((realSubject == null) && "toString".equals(methodName)) {
+ * return "unmaterialized proxy for " + id; }
+ */
+
+ // BRJ: make sure that the object to be compared is a real object
+ // otherwise equals may return false.
+ if ("equals".equals(methodName) && args[0] != null)
+ {
TemporaryBrokerWrapper tmp = getBroker();
try
{
@@ -380,99 +382,115 @@
}
}
- if ("getIndirectionHandler".equals(methodName) &&
args[0] != null)
- {
- return this;
- }
-
- subject = getRealSubject();
- return method.invoke(subject, args);
- // [olegnitz] I've changed the following strange lines
- // to the above one. Why was this done in such
complicated way?
- // Is it possible that subject doesn't implement the
method's
- // interface?
- // Method m =
subject.getClass().getMethod(method.getName(),
- // method.getParameterTypes());
- // return m.invoke(subject, args);
- } catch (Exception ex)
- {
- throw new PersistenceBrokerException("Error invoking
method " + method.getName(), ex);
- }
- }
-
- /**
- * Returns the proxies real subject. The subject will be materialized if
- * necessary.
- *
- * @return The subject
- */
- public Object getRealSubject() throws PersistenceBrokerException
- {
- if (_realSubject == null)
- {
- beforeMaterialization();
- _realSubject = materializeSubject();
- afterMaterialization();
- }
- return _realSubject;
- }
-
- /**
- * [olegnitz] This looks stupid, but is really necessary for OTM: the
- * materialization listener replaces the real subject by its clone to
ensure
- * transaction isolation. Is there a better way to do this?
- */
- public void setRealSubject(Object object)
- {
- _realSubject = object;
- }
-
- /**
- * Retrieves the real subject from the underlying RDBMS. Override this
- * method if the object is to be materialized in a specific way.
- *
- * @return The real subject of the proxy
- */
- protected synchronized Object materializeSubject() throws
PersistenceBrokerException
- {
- TemporaryBrokerWrapper tmp = getBroker();
+ if ("getIndirectionHandler".equals(methodName) && args[0] != null)
+ {
+ return this;
+ }
+
+ // now materialize the real object
+ subject = getRealSubject();
+
+ if("toString".equals(methodName) && subject == null)
+ {
+ return null;
+ }
+ /*
+ arminw: If the real subject doesn't exist, return 'null' for all
+ method calls. This could happen e.g. when the FK of a 1:1 reference
+ is the PK of main object. In this case the reference can be null
but
+ the FK to the referenced object always exists (because it's the PK
of the
+ main object)
+ TODO: Should we log a warn message to indicate abnormal Proxy
object behavior?
+ */
+ if(subject == null)
+ {
+ log.warn("Real object of this proxy object doesn't exist, all
method will return 'null': " + getIdentity());
+ return null;
+ }
+ else
+ {
+ return method.invoke(subject, args);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new PersistenceBrokerException("Error invoking method " +
method.getName(), ex);
+ }
+ }
+
+ /**
+ * Returns the proxies real subject. The subject will be materialized if
+ * necessary.
+ *
+ * @return The subject
+ */
+ public Object getRealSubject() throws PersistenceBrokerException
+ {
+ if (_realSubject == null)
+ {
+ beforeMaterialization();
+ _realSubject = materializeSubject();
+ afterMaterialization();
+ }
+ return _realSubject;
+ }
+
+ /**
+ * [olegnitz] This looks stupid, but is really necessary for OTM: the
+ * materialization listener replaces the real subject by its clone to
ensure
+ * transaction isolation. Is there a better way to do this?
+ */
+ public void setRealSubject(Object object)
+ {
+ _realSubject = object;
+ }
+
+ /**
+ * Retrieves the real subject from the underlying RDBMS. Override this
+ * method if the object is to be materialized in a specific way.
+ *
+ * @return The real subject of the proxy
+ */
+ protected synchronized Object materializeSubject() throws
PersistenceBrokerException
+ {
+ TemporaryBrokerWrapper tmp = getBroker();
try
- {
- Object realSubject =
tmp.broker.getObjectByIdentity(_id);
- if (realSubject == null)
- {
-
LoggerFactory.getLogger(IndirectionHandler.class).warn(
- "Can not materialize object for
Identity " + _id + " - using PBKey " + getBrokerKey());
- }
- return realSubject;
- } catch (Exception ex)
- {
- throw new PersistenceBrokerException(ex);
- } finally
- {
- tmp.close();
- }
- }
-
- /**
- * Determines whether the real subject already has been materialized.
- *
- * @return <code>true</code> if the real subject has already been loaded
- */
- public boolean alreadyMaterialized()
- {
- return _realSubject != null;
- }
-
- /**
- * Generate a simple object that is serializable and placeholder for
- * proxies.
- *
- */
- private Object generateSerializableProxy()
- {
- return new
OJBSerializableProxy(getIdentity().getObjectsRealClass(), this);
- }
+ {
+ Object realSubject = tmp.broker.getObjectByIdentity(_id);
+ if (realSubject == null)
+ {
+ LoggerFactory.getLogger(IndirectionHandler.class).warn(
+ "Can not materialize object for Identity " + _id + " -
using PBKey " + getBrokerKey());
+ }
+ return realSubject;
+ } catch (Exception ex)
+ {
+ throw new PersistenceBrokerException(ex);
+ } finally
+ {
+ tmp.close();
+ }
+ }
+
+ /**
+ * Determines whether the real subject already has been materialized.
+ *
+ * @return <code>true</code> if the real subject has already been loaded
+ */
+ public boolean alreadyMaterialized()
+ {
+ return _realSubject != null;
+ }
+
+ /**
+ * Generate a simple object that is serializable and placeholder for
+ * proxies.
+ *
+ */
+ private Object generateSerializableProxy()
+ {
+ return new OJBSerializableProxy(getIdentity().getObjectsRealClass(),
this);
+ }
/**
* Returns the metadata profile key used when creating this proxy.
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]