Coverage Report - org.apache.ojb.broker.core.proxy.AbstractIndirectionHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractIndirectionHandler
N/A
N/A
2.833
AbstractIndirectionHandler$TemporaryBrokerWrapper
N/A
N/A
2.833
 
 1  
 package org.apache.ojb.broker.core.proxy;
 2  
 
 3  
 /* Copyright 2002-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import org.apache.ojb.broker.*;
 19  
 import org.apache.ojb.broker.core.PersistenceBrokerThreadMapping;
 20  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 21  
 
 22  
 import java.lang.reflect.Method;
 23  
 import java.util.ArrayList;
 24  
 
 25  
 /**
 26  
  * Abstract implementation for the indirection handler used by ojb's proxies.
 27  
  *
 28  
  * @version $Id: AbstractIndirectionHandler.java,v 1.1 2007-08-24 22:17:32 ewestfal Exp $
 29  
  */
 30  
 public abstract class AbstractIndirectionHandler implements IndirectionHandler
 31  
 {
 32  
     static final long serialVersionUID = -1993879565033755826L;
 33  
 
 34  
     /** The key for acquiring the above broker */
 35  
     private PBKey _brokerKey;
 36  
     /** The real subject which this is hidden by the proxy */
 37  
     private Object _realSubject = null;
 38  
     /** Represents the identity of the real subject. When the real subject is not
 39  
      *  yet materialized, it can be loaded from the underlying db by this identity object */
 40  
     private Identity _id = null;
 41  
     /** The materialization listeners */
 42  
     private transient ArrayList _listeners;
 43  
 
 44  
         /**
 45  
          * Creates a new indirection handler for the indicated object.
 46  
          *
 47  
          * @param brokerKey
 48  
          *            The key of the persistence broker
 49  
          * @param id
 50  
          *            The identity of the subject
 51  
          */
 52  
         public AbstractIndirectionHandler(PBKey brokerKey, Identity id)
 53  
         {
 54  
                 setBrokerKey(brokerKey);
 55  
                 setIdentity(id);
 56  
         }
 57  
 
 58  
         /**
 59  
          * Returns the identity of the subject.
 60  
          *
 61  
          * @return The identity
 62  
          */
 63  
         public Identity getIdentity()
 64  
         {
 65  
                 return _id;
 66  
         }
 67  
 
 68  
         /**
 69  
          * Sets the identity of the subject of this indirection handler.
 70  
          *
 71  
          * @param identity
 72  
          */
 73  
         protected void setIdentity(Identity identity)
 74  
         {
 75  
                 _id = identity;
 76  
         }
 77  
 
 78  
         /**
 79  
          * Returns the key of the persistence broker used by this indirection
 80  
          * handler.
 81  
          *
 82  
          * @return The broker key
 83  
          */
 84  
         public PBKey getBrokerKey()
 85  
         {
 86  
                 return _brokerKey;
 87  
         }
 88  
 
 89  
         /**
 90  
          * Sets the key of the persistence broker used by this indirection handler.
 91  
          *
 92  
          * @param brokerKey
 93  
          *            The broker key
 94  
          */
 95  
         protected void setBrokerKey(PBKey brokerKey)
 96  
         {
 97  
                 _brokerKey = brokerKey;
 98  
         }
 99  
 
 100  
         /**
 101  
          * Adds a materialization listener.
 102  
          *
 103  
          * @param listener
 104  
          *            The listener to add
 105  
          */
 106  
         public synchronized void addListener(MaterializationListener listener)
 107  
         {
 108  
                 if (_listeners == null)
 109  
                 {
 110  
                         _listeners = new ArrayList();
 111  
                 }
 112  
                 // add listener only once
 113  
                 if (!_listeners.contains(listener))
 114  
                 {
 115  
                         _listeners.add(listener);
 116  
                 }
 117  
         }
 118  
 
 119  
         /**
 120  
          * Removes a materialization listener.
 121  
          *
 122  
          * @param listener
 123  
          *            The listener to remove
 124  
          */
 125  
         public synchronized void removeListener(MaterializationListener listener)
 126  
         {
 127  
                 if (_listeners != null)
 128  
                 {
 129  
                         _listeners.remove(listener);
 130  
                 }
 131  
         }
 132  
 
 133  
         /**
 134  
          * Calls beforeMaterialization on all registered listeners in the reverse
 135  
          * order of registration.
 136  
          */
 137  
         protected void beforeMaterialization()
 138  
         {
 139  
                 if (_listeners != null)
 140  
                 {
 141  
                         MaterializationListener listener;
 142  
 
 143  
                         for (int idx = _listeners.size() - 1; idx >= 0; idx--)
 144  
                         {
 145  
                                 listener = (MaterializationListener) _listeners.get(idx);
 146  
                                 listener.beforeMaterialization(this, _id);
 147  
                         }
 148  
                 }
 149  
         }
 150  
 
 151  
         /**
 152  
          * Calls afterMaterialization on all registered listeners in the reverse
 153  
          * order of registration.
 154  
          */
 155  
         protected void afterMaterialization()
 156  
         {
 157  
                 if (_listeners != null)
 158  
                 {
 159  
                         MaterializationListener listener;
 160  
 
 161  
                         // listeners may remove themselves during the afterMaterialization
 162  
                         // callback.
 163  
                         // thus we must iterate through the listeners vector from back to
 164  
                         // front
 165  
                         // to avoid index problems.
 166  
                         for (int idx = _listeners.size() - 1; idx >= 0; idx--)
 167  
                         {
 168  
                                 listener = (MaterializationListener) _listeners.get(idx);
 169  
                                 listener.afterMaterialization(this, _realSubject);
 170  
                         }
 171  
                 }
 172  
         }
 173  
 
 174  
     /**
 175  
      * Gets the persistence broker used by this indirection handler.
 176  
      * If no PBKey is available a runtime exception will be thrown.
 177  
      *
 178  
      * @return a PersistenceBroker
 179  
      */
 180  
     protected TemporaryBrokerWrapper getBroker() throws PBFactoryException
 181  
     {
 182  
         PersistenceBrokerInternal broker;
 183  
         boolean needsClose = false;
 184  
 
 185  
         if (getBrokerKey() == null)
 186  
         {
 187  
             /*
 188  
             arminw:
 189  
             if no PBKey is set we throw an exception, because we don't
 190  
             know which PB (connection) should be used.
 191  
             */
 192  
             throw new OJBRuntimeException("Can't find associated PBKey. Need PBKey to obtain a valid" +
 193  
                                           "PersistenceBroker instance from intern resources.");
 194  
         }
 195  
         // first try to use the current threaded broker to avoid blocking
 196  
         broker = PersistenceBrokerThreadMapping.currentPersistenceBroker(getBrokerKey());
 197  
         // current broker not found, create a intern new one
 198  
         if ((broker == null) || broker.isClosed())
 199  
         {
 200  
             broker = (PersistenceBrokerInternal) PersistenceBrokerFactory.createPersistenceBroker(getBrokerKey());
 201  
             /** Specifies whether we obtained a fresh broker which we have to close after we used it */
 202  
             needsClose = true;
 203  
         }
 204  
         return new TemporaryBrokerWrapper(broker, needsClose);
 205  
     }
 206  
 
 207  
         /**
 208  
          * [Copied from {@link java.lang.reflect.InvocationHandler}]:<br/>
 209  
          * Processes a method invocation on a proxy instance and returns the result.
 210  
          * This method will be invoked on an invocation handler when a method is
 211  
          * invoked on a proxy instance that it is associated with.
 212  
          *
 213  
          * @param proxy
 214  
          *            The proxy instance that the method was invoked on
 215  
          *
 216  
          * @param method
 217  
          *            The <code>Method</code> instance corresponding to the
 218  
          *            interface method invoked on the proxy instance. The declaring
 219  
          *            class of the <code>Method</code> object will be the
 220  
          *            interface that the method was declared in, which may be a
 221  
          *            superinterface of the proxy interface that the proxy class
 222  
          *            inherits the method through.
 223  
          *
 224  
          * @param args
 225  
          *            An array of objects containing the values of the arguments
 226  
          *            passed in the method invocation on the proxy instance, or
 227  
          *            <code>null</code> if interface method takes no arguments.
 228  
          *            Arguments of primitive types are wrapped in instances of the
 229  
          *            appropriate primitive wrapper class, such as
 230  
          *            <code>java.lang.Integer</code> or
 231  
          *            <code>java.lang.Boolean</code>.
 232  
          *
 233  
          * @return The value to return from the method invocation on the proxy
 234  
          *         instance. If the declared return type of the interface method is
 235  
          *         a primitive type, then the value returned by this method must be
 236  
          *         an instance of the corresponding primitive wrapper class;
 237  
          *         otherwise, it must be a type assignable to the declared return
 238  
          *         type. If the value returned by this method is <code>null</code>
 239  
          *         and the interface method's return type is primitive, then a
 240  
          *         <code>NullPointerException</code> will be thrown by the method
 241  
          *         invocation on the proxy instance. If the value returned by this
 242  
          *         method is otherwise not compatible with the interface method's
 243  
          *         declared return type as described above, a
 244  
          *         <code>ClassCastException</code> will be thrown by the method
 245  
          *         invocation on the proxy instance.
 246  
          *
 247  
          * @throws PersistenceBrokerException
 248  
          *             The exception to throw from the method invocation on the
 249  
          *             proxy instance. The exception's type must be assignable
 250  
          *             either to any of the exception types declared in the
 251  
          *             <code>throws</code> clause of the interface method or to
 252  
          *             the unchecked exception types
 253  
          *             <code>java.lang.RuntimeException</code> or
 254  
          *             <code>java.lang.Error</code>. If a checked exception is
 255  
          *             thrown by this method that is not assignable to any of the
 256  
          *             exception types declared in the <code>throws</code> clause
 257  
          *             of the interface method, then an
 258  
          *             {@link java.lang.reflect.UndeclaredThrowableException}
 259  
          *             containing the exception that was thrown by this method will
 260  
          *             be thrown by the method invocation on the proxy instance.
 261  
          *
 262  
          * @see java.lang.reflect.UndeclaredThrowableException
 263  
          */
 264  
         public Object invoke(Object proxy, Method method, Object[] args)
 265  
         {
 266  
                 Object subject;
 267  
                 String methodName = method.getName();
 268  
 
 269  
                 try
 270  
                 {
 271  
                         // [andrew clute]
 272  
                         // short-circuit any calls to a finalize methjod if the subject
 273  
                         // has not been retrieved yet
 274  
                         if ("finalize".equals(methodName) && _realSubject == null)
 275  
                         {
 276  
                                 return null;
 277  
                         }
 278  
 
 279  
                         // [andrew clute]
 280  
                         // When trying to serialize a proxy, we need to determine how to
 281  
                         // handle it
 282  
                         if ("writeReplace".equals(methodName))
 283  
                         {
 284  
                                 if (_realSubject == null)
 285  
                                 {
 286  
                                         // Unmaterialized proxies are replaced by simple
 287  
                                         // serializable
 288  
                                         // objects that can be unserialized without classloader
 289  
                                         // issues
 290  
                                         return generateSerializableProxy();
 291  
                                 } else
 292  
                                 {
 293  
                                         // Materiliazed objects should be passed back as they might
 294  
                                         // have
 295  
                                         // been mutated
 296  
                                         return getRealSubject();
 297  
                                 }
 298  
                         }
 299  
 
 300  
                         // [tomdz]
 301  
                         // Previously the hashcode of the identity would have been used
 302  
                         // but this requires a compatible hashCode implementation in the
 303  
                         // proxied object (which is somewhat unlikely, even the default
 304  
                         // hashCode implementation does not fulfill this requirement)
 305  
                         // for those that require this behavior, a custom indirection
 306  
                         // handler can be used, or the hashCode of the identity
 307  
                         /*
 308  
                          * if ("hashCode".equals(methodName)) { return new
 309  
                          * Integer(_id.hashCode()); }
 310  
                          */
 311  
 
 312  
                         // [tomdz]
 313  
                         // this would handle toString differently for non-materialized
 314  
                         // proxies
 315  
                         // (to avoid materialization due to logging)
 316  
                         // however toString should be a normal business method which
 317  
                         // materializes the proxy
 318  
                         // if this is not desired, then the ProxyHandler.toString(Object)
 319  
                         // method
 320  
                         // should be used instead (e.g. for logging within OJB)
 321  
                         /*
 322  
                          * if ((realSubject == null) && "toString".equals(methodName)) {
 323  
                          * return "unmaterialized proxy for " + id; }
 324  
                          */
 325  
 
 326  
                         // BRJ: make sure that the object to be compared is a real object
 327  
                         // otherwise equals may return false.
 328  
                         if ("equals".equals(methodName) && args[0] != null)
 329  
                         {
 330  
                 TemporaryBrokerWrapper tmp = getBroker();
 331  
                 try
 332  
                 {
 333  
                     args[0] = tmp.broker.getProxyFactory().getRealObject(args[0]);
 334  
                 }
 335  
                 finally
 336  
                 {
 337  
                     tmp.close();
 338  
                 }
 339  
             }
 340  
 
 341  
                         if ("getIndirectionHandler".equals(methodName) && args[0] != null)
 342  
                         {
 343  
                                 return this;
 344  
                         }
 345  
 
 346  
                         subject = getRealSubject();
 347  
 
 348  
             //kuali modification start
 349  
                         try {
 350  
                 method.setAccessible(true);
 351  
             } catch (SecurityException ex) {
 352  
                 LoggerFactory.getLogger(IndirectionHandler.class).warn(
 353  
                         "Error calling setAccessible for method " + method.getName(), ex);
 354  
             }
 355  
             //kuali modification end
 356  
 
 357  
                         return method.invoke(subject, args);
 358  
                         // [olegnitz] I've changed the following strange lines
 359  
                         // to the above one. Why was this done in such complicated way?
 360  
                         // Is it possible that subject doesn't implement the method's
 361  
                         // interface?
 362  
                         // Method m = subject.getClass().getMethod(method.getName(),
 363  
                         // method.getParameterTypes());
 364  
                         // return m.invoke(subject, args);
 365  
                 } catch (Exception ex)
 366  
                 {
 367  
                         throw new PersistenceBrokerException("Error invoking method " + method.getName(), ex);
 368  
                 }
 369  
         }
 370  
 
 371  
         /**
 372  
          * Returns the proxies real subject. The subject will be materialized if
 373  
          * necessary.
 374  
          *
 375  
          * @return The subject
 376  
          */
 377  
         public Object getRealSubject() throws PersistenceBrokerException
 378  
         {
 379  
                 if (_realSubject == null)
 380  
                 {
 381  
                         beforeMaterialization();
 382  
                         _realSubject = materializeSubject();
 383  
                         afterMaterialization();
 384  
                 }
 385  
                 return _realSubject;
 386  
         }
 387  
 
 388  
         /**
 389  
          * [olegnitz] This looks stupid, but is really necessary for OTM: the
 390  
          * materialization listener replaces the real subject by its clone to ensure
 391  
          * transaction isolation. Is there a better way to do this?
 392  
          */
 393  
         public void setRealSubject(Object object)
 394  
         {
 395  
                 _realSubject = object;
 396  
         }
 397  
 
 398  
         /**
 399  
          * Retrieves the real subject from the underlying RDBMS. Override this
 400  
          * method if the object is to be materialized in a specific way.
 401  
          *
 402  
          * @return The real subject of the proxy
 403  
          */
 404  
         protected synchronized Object materializeSubject() throws PersistenceBrokerException
 405  
         {
 406  
                 TemporaryBrokerWrapper tmp = getBroker();
 407  
         try
 408  
                 {
 409  
                         Object realSubject = tmp.broker.getObjectByIdentity(_id);
 410  
                         if (realSubject == null)
 411  
                         {
 412  
                                 LoggerFactory.getLogger(IndirectionHandler.class).warn(
 413  
                                                 "Can not materialize object for Identity " + _id + " - using PBKey " + getBrokerKey());
 414  
                         }
 415  
                         return realSubject;
 416  
                 } catch (Exception ex)
 417  
                 {
 418  
                         throw new PersistenceBrokerException(ex);
 419  
                 } finally
 420  
                 {
 421  
                         tmp.close();
 422  
                 }
 423  
         }
 424  
 
 425  
         /**
 426  
          * Determines whether the real subject already has been materialized.
 427  
          *
 428  
          * @return <code>true</code> if the real subject has already been loaded
 429  
          */
 430  
         public boolean alreadyMaterialized()
 431  
         {
 432  
                 return _realSubject != null;
 433  
         }
 434  
 
 435  
         /**
 436  
          * Generate a simple object that is serializable and placeholder for
 437  
          * proxies.
 438  
          *
 439  
          */
 440  
         private Object generateSerializableProxy()
 441  
         {
 442  
                 return new OJBSerializableProxy(getIdentity().getObjectsRealClass(), this);
 443  
         }
 444  
 
 445  
     //===================================================================
 446  
     // inner class
 447  
     //===================================================================
 448  
     /**
 449  
      * wrapper class for temporary used broker instances.
 450  
      */
 451  
     static final class TemporaryBrokerWrapper
 452  
     {
 453  
         /** Specifies whether we obtained a fresh broker which we have to close after we used it */
 454  
         boolean needsClose;
 455  
         PersistenceBrokerInternal broker;
 456  
 
 457  
         public TemporaryBrokerWrapper(PersistenceBrokerInternal broker, boolean needsClose)
 458  
         {
 459  
             this.broker = broker;
 460  
             this.needsClose = needsClose;
 461  
         }
 462  
 
 463  
         /**
 464  
          * Cleanup the used broker instance, it's mandatory to call
 465  
          * this method after use.
 466  
          */
 467  
         public void close()
 468  
         {
 469  
             if(needsClose)
 470  
             {
 471  
                 broker.close();
 472  
             }
 473  
         }
 474  
     }
 475  
 }