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 {@link 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
- * {@link 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 {@link 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
+ * {@link 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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.