001 /* ==================================================================== 002 * The Apache Software License, Version 1.1 003 * 004 * Copyright (c) 2003 The Apache Software Foundation. All rights 005 * reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * 1. Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * 2. Redistributions in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * 3. The end-user documentation included with the redistribution, 020 * if any, must include the following acknowledgment: 021 * "This product includes software developed by the 022 * Apache Software Foundation (http://www.apache.org/)." 023 * Alternately, this acknowledgment may appear in the software itself, 024 * if and wherever such third-party acknowledgments normally appear. 025 * 026 * 4. The names "The Jakarta Project", "Commons", and "Apache Software 027 * Foundation" must not be used to endorse or promote products derived 028 * from this software without prior written permission. For written 029 * permission, please contact apache@apache.org. 030 * 031 * 5. Products derived from this software may not be called "Apache", 032 * nor may "Apache" appear in their name, without prior written 033 * permission of the Apache Software Foundation. 034 * 035 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 036 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 037 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 038 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 039 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 040 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 041 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 042 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 043 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 044 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 045 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 046 * SUCH DAMAGE. 047 * ==================================================================== 048 * 049 * This software consists of voluntary contributions made by many 050 * individuals on behalf of the Apache Software Foundation. For more 051 * information on the Apache Software Foundation, please see 052 * <http://www.apache.org/>. 053 * 054 */ 055 package org.jpu.patterns.proxy; 056 057 import java.io.Serializable; 058 import java.lang.reflect.InvocationHandler; 059 import java.lang.reflect.InvocationTargetException; 060 import java.lang.reflect.Method; 061 import java.lang.reflect.Proxy; 062 import java.util.Arrays; 063 import java.util.Collection; 064 import java.util.HashSet; 065 import java.util.List; 066 import java.util.Map; 067 import java.util.Set; 068 069 import org.apache.commons.collections.map.IdentityMap; 070 import org.apache.commons.lang.exception.NestableRuntimeException; 071 import org.apache.commons.logging.Log; 072 import org.apache.commons.logging.LogFactory; 073 import org.jpu.patterns.common.JPUClassUtils; 074 075 /** 076 * This class is designed to make writing <code>InvocationHandler</code>'s easier and less error-prone, as well as to facilitate 077 * more efficient delegation and a more natural inheritance-based delegation model. Its features are summarized below: 078 * <ul> 079 * <li>Facilitates collaboration among the classes in an invocation handler inheritance hierarchy. 080 * Calls to the <code>invoke()</code> method (inherited from <code>InvocationHandler</code>) are delegated to the protected 081 * method {@link #doInvoke(Object, Method, Object[], Object[])} which each subclass can override. This method returns a <code>boolean</code> 082 * indicating whether it was able to handle the request at its level. Each subclass implementation can call 083 * <code>super.doInvoke()</code> to delegate the invocation to the superclass, 084 * and can take appropriate action based on the <code>boolean</code> returned. The subclass may choose to perform some action 085 * in addition to, or in place of, the one defined in the superclass. 086 * </li> 087 * <li>Support for automatic "self-delegation". This means that if the invocation handler defines a method with the same signature as 088 * the method that was invoked on the proxy, it will be called automatically. The proxy will be made available to the delegate 089 * via the method {@link #getProxy()}. Internal caching ensures that self-delegation incurs minimal overhead, and is in fact 090 * more efficient than the cascaded <code>if/else</code> clauses that tend to clutter <code>invoke()</code> methods. It also 091 * results in more maintainable invocation handlers because much of the delegation work is handled automatically. 092 * <p> 093 * Automatic self-delegation is enabled for all matching methods by default but can be disabled for certain methods, or for all 094 * methods, by overriding {@link #autoDelegateToSelf(Object,Method,Object[])}, which returns a boolean indicating whether automatic self-delegation should 095 * be performed for a given invocation. If automatic self-delegation is disabled, you can still invoke the delegation logic 096 * explicitly by calling {@link #delegate(Object,Object,Method,Object[],Object[])}. This method will return a <code>boolean</code> indicating whether it found (and 097 * called) a matching method. 098 * </li> 099 * <li>Automatic rethrowing of exceptions in a manner designed to avoid <code>UndeclaredThrowableException</code>'s which result when 100 * the invocation handler fails to catch and rethrow a checked exception of a type that is not listed in the invoked method's 101 * <code>throws</code> clause. This is a common source of bugs in invocation handlers, because the compiler cannot 102 * perform the same exception-related checks on an invocation handler that it can on a statically implemented 103 * method. 104 * <p> 105 * <code>JPUInvocationHandler</code> will call {@link #throwUnlistedException(Object,Method,Object[],Throwable)} for any exception that would otherwise result in 106 * an <code>UndeclaredThrowableException</code> exception. This method's default implementation simply throws 107 * <code>NestableRuntimeException</code> passing the unlisted exception to its constructor, but the subclass can override as 108 * needed. 109 * </li> 110 * </ul> 111 * The {@link org.jpu.patterns.proxy.ProxyBean} class extends <code>JPUInvocationHandler</code> and adds support for automatic 112 * implementation of simple getter and setter methods along with change tracking. 113 */ 114 public class JPUInvocationHandler implements InvocationHandler, Serializable { 115 /** 116 */ 117 protected static class DelegationState { 118 public DelegationState(Object theTarget, Method theInterfaceMethod, Object theProxy, DelegationState theParent) { 119 target = theTarget; 120 interfaceMethod = theInterfaceMethod; 121 proxy = theProxy; 122 parent = theParent; 123 } 124 public Object target = null; 125 public Method interfaceMethod = null; 126 public Object proxy = null; 127 public DelegationState parent = null; 128 } 129 130 /** 131 * Defines entries in the self-delegation cache. 132 */ 133 protected static class DelegationCacheValue { 134 public Method targetMethod = null; 135 } 136 137 /** 138 * Throws an unchecked exception indiciating that the given method was not handled by the invocation handler. 139 * The default implementation throws {@link UnhandledMethodCallException}, but subclasses are free to override. 140 */ 141 protected void throwUnhandledException(Method method) { 142 throw new UnhandledMethodCallException( "Method " + method + " is unhandled in class " + getClass().getName() ); 143 } 144 145 /** 146 * Method required by the <code>InvocationHandler</code> interface. Calls {@link #doInvoke(Object,Method,Object[],Object[])}, catching any exceptions 147 * thrown from it and rethrowing them using the {@link #rethrow(Object,Method,Object[],Throwable)} method (which subclasses can override). If <code>doInvoke()</code> 148 * returns <code>false</code> (meaning the invocation was not handled), throws an exception by calling {@link #throwUnhandledException(Method)}, 149 * which subclasses can override. 150 */ 151 public Object invoke(Object proxy, Method interfaceMethod, Object[] args) throws Throwable { 152 if ( _log.isDebugEnabled() ) { 153 _log.debug( "Method '" + interfaceMethod + "' invoked on object of class '" + proxy.getClass().getName() + "'." ); 154 } 155 try { 156 Object[] result = new Object[1]; 157 boolean handled = false; 158 try { 159 handled = doInvoke(proxy, interfaceMethod, args, result); 160 if ( ! handled ) { 161 if ( interfaceMethod.getDeclaringClass().equals(Object.class) ) { 162 result[0] = interfaceMethod.invoke(this, args); 163 handled = true; 164 } 165 } 166 } 167 catch( InvocationTargetException ite ) { 168 throw ite.getTargetException(); 169 } 170 if ( ! handled ) { 171 throwUnhandledException(interfaceMethod); 172 } 173 return result[0]; 174 } 175 catch( Throwable throwable ) { 176 rethrow( proxy, interfaceMethod, args, throwable ); 177 return null; // <-- Placate compiler. 178 } 179 } 180 181 /** 182 * Rethrows exceptions thrown from {@link #doInvoke(Object,Method,Object[],Object[])}. If the given <code>Throwable</code> is an 183 * <code>Error</code> or <code>RuntimeException</code>, it is rethrown as is. Else if the exception is 184 * of a type that is compatible with the interface method's <code>throws</code> clause, it is rethrown as 185 * is. Else {@link #throwUnhandledException(Method)} is called, which subclasses can override. 186 */ 187 protected void rethrow(Object proxy, Method interfaceMethod, Object[] args, Throwable t) throws Throwable { 188 if ( t instanceof Error || t instanceof RuntimeException ) { 189 throw t; 190 } 191 List exceptions = Arrays.asList( interfaceMethod.getExceptionTypes() ); 192 if ( JPUClassUtils.areAnyAssignable( exceptions, t.getClass() ) ) { 193 throw t; 194 } 195 throwUnlistedException(proxy, interfaceMethod, args, t); 196 } 197 198 /** 199 * This method is called when an exception is thrown from {@link #doInvoke(Object,Method,Object[],Object[])} that is an illegal checked exception 200 * for the interface method that was invoked; that is, the exception is not assignment compatible with any exception 201 * class listed in the interface method's <code>throws</code> clause. This method must throw either an unchecked exception or a checked 202 * exception appearing in the throws clause of the method. 203 * <p> 204 * The default implementation throws a <code>NestableRuntimeException</code> wrapping the given <code>Throwable</code>, 205 * but subclasses are free to override. 206 */ 207 protected void throwUnlistedException(Object proxy, Method interfaceMethod, Object[] args, Throwable t) throws Throwable { 208 throw new NestableRuntimeException(t); 209 } 210 211 /** 212 * Internal method used to ensure that a delegation call would not result in infinite recursion. 213 */ 214 protected boolean wouldDelegationInfinitelyRecurse(DelegationState state, Object target, Method interfaceMethod) { 215 boolean verdict = false; 216 while ( state != null ) { 217 if ( state.target == target && state.interfaceMethod == interfaceMethod ) { 218 verdict = true; 219 break; 220 } 221 state = state.parent; 222 } 223 return verdict; 224 } 225 226 /** 227 * Returns whether self delegation is performed automatically for any matching method invocations. 228 * Default is <code>true</code>, but subclasses are free to override. 229 */ 230 protected boolean autoDelegateToSelf(Object proxy, Method interfaceMethod, Object[] args) { 231 return true; 232 } 233 234 /** 235 * Returns the method on the invocation handler to which invocations on the given interface method 236 * should be delegated, or <code>null</code> if no such method exists. Uses caching to ensure 237 * that this lookup incurs minimal overhead. 238 */ 239 protected Method getDelegationTargetMethod(Method interfaceMethod, Class targetClass) throws Throwable { 240 synchronized( JPUInvocationHandler.class ) { 241 Map methodMap = (Map)_subclasses.get( targetClass ); 242 if ( methodMap == null ) { 243 methodMap = new IdentityMap(); 244 _subclasses.put( targetClass, methodMap ); 245 } 246 DelegationCacheValue value = (DelegationCacheValue)methodMap.get(interfaceMethod); 247 if ( value == null ) { 248 _misses++; 249 value = new DelegationCacheValue(); 250 251 Method targetMethod = null; 252 try { 253 targetMethod = targetClass.getMethod( interfaceMethod.getName(), interfaceMethod.getParameterTypes() ); 254 } 255 catch( Exception e ) { 256 } 257 if ( targetMethod != null && targetMethod.getDeclaringClass().equals(JPUInvocationHandler.class) ) { 258 targetMethod = null; 259 } 260 value.targetMethod = targetMethod; 261 methodMap.put(interfaceMethod, value); 262 } 263 else { 264 _hits++; 265 } 266 return value.targetMethod; 267 } 268 } 269 270 /** 271 * Alias for "<code>getDelegationTargetMethod( interfaceMethod, getClass() )</code>". 272 */ 273 protected Method getDelegationMethod(Method interfaceMethod) throws Throwable { 274 return getDelegationTargetMethod( interfaceMethod, getClass() ); 275 } 276 277 /** 278 * Returns the number of cache misses registered for the self-delegation cache since {@link #resetCacheCounters()} was last called. 279 */ 280 public static long getCacheMisses() { 281 synchronized( JPUInvocationHandler.class ) { 282 return _misses; 283 } 284 } 285 286 /** 287 * Returns the number of cache hits registered for the self-delegation cache since {@link #resetCacheCounters()} was last called. 288 */ 289 public static long getCacheHits() { 290 synchronized( JPUInvocationHandler.class ) { 291 return _hits; 292 } 293 } 294 295 /** 296 * Resets the counters used to measure the hit and miss rate of the self-delegation cache. 297 */ 298 public static void resetCacheCounters() { 299 _misses = 0; 300 _hits = 0; 301 } 302 303 /** 304 * Attempts to delegate the given method invocation to "<code>target</code>". If no matching 305 * method could be found (as determined by a call to {@link #getDelegationTargetMethod(Method,Class)}), 306 * this method returns <code>false</code>. Else it invokes the method, setting <code>result[0]</code> 307 * to its return value, and returns <code>true</code>. Any exception thrown by 308 * the invoked method will be allowed to propagate up. 309 */ 310 protected boolean delegate(Object proxy, Object target, Method interfaceMethod, Object[] args, Object[] result) throws Throwable { 311 if ( interfaceMethod == null ) { 312 return false; 313 } 314 Method implementationMethod = getDelegationTargetMethod( interfaceMethod, target.getClass() ); 315 if ( implementationMethod == null ) { 316 return false; 317 } 318 319 boolean handled = false; 320 DelegationState oldDelegationState = getDelegationState(); 321 if ( ! wouldDelegationInfinitelyRecurse(oldDelegationState, target, interfaceMethod) ) { 322 323 // Call it. 324 DelegationState newDelegationState = new DelegationState(target, interfaceMethod, proxy, oldDelegationState); 325 setDelegationState(newDelegationState); 326 try { 327 result[0] = implementationMethod.invoke(target, args); 328 } 329 finally { 330 setDelegationState(oldDelegationState ); 331 } 332 handled = true; 333 } 334 return handled; 335 } 336 337 /** 338 * Returns the state of the self-delegation action that is currently in progress, or <code>null</code> if no self-delegation is 339 * in progress. 340 */ 341 protected DelegationState getDelegationState() { 342 return (DelegationState)_delegationState.get(); 343 } 344 345 /** 346 * Sets the current self-delegation state to the given value. 347 */ 348 protected void setDelegationState(DelegationState delegationState) { 349 _delegationState.set( delegationState ); 350 } 351 352 /** 353 * Returns the proxy for which this invocation handler is currently handling an invocation. If no invocation is in progress, 354 * this method returns <code>null</code>. 355 */ 356 protected Object getProxy() { 357 DelegationState state = getDelegationState(); 358 return state == null ? null : state.proxy; 359 } 360 361 /** 362 * Returns the interface method through which the current invocation was called. If no 363 * invocation is in progress, this method returns <code>null</code>. 364 */ 365 protected Method getCurrentInterfaceMethod() { 366 DelegationState state = getDelegationState(); 367 return state == null ? null : state.interfaceMethod; 368 } 369 370 /** 371 * Convenience alias for "<code>newProxy(null)</code>". 372 */ 373 public Object newProxy() { 374 return newProxy(null); 375 } 376 377 /** 378 * Convenience alias for "<code>newProxy(moreClasses, null)</code>". 379 */ 380 public Object newProxy(Class[] moreClasses) { 381 return newProxy(moreClasses, null); 382 } 383 384 /** 385 * Returns all interfaces that this invocation handler's proxy implements. This is a list 386 * consisting of all interfaces returned by {@link #doGetImplementedInterfaces(Collection)}, plus 387 * those passed via the <code>moreClasses</code> parameter. 388 */ 389 public Class[] getInterfaces(Class[] moreClasses) { 390 Set interfaces = new HashSet(); 391 doGetImplementedInterfaces(interfaces); 392 if ( moreClasses != null ) { 393 interfaces.addAll( Arrays.asList(moreClasses) ); 394 } 395 396 Class[] interfacesArray = (Class[])interfaces.toArray( new Class[0] ); 397 return interfacesArray; 398 } 399 400 /** 401 * Uses <code>Proxy.newProxyInstance()</code> to generate a proxy instance backed by this 402 * invocation handler. The generated proxy will implement all interfaces returned by 403 * {@link #getInterfaces(Class[])}. 404 * Once the proxy is generated, this method calls 405 * {@link #initializeProxy(Object)}, which 406 * subclasses can override. 407 * 408 * @param moreClasses Interfaces to implement in addition to those returned by 409 * {@link #doGetImplementedInterfaces(Collection)}. 410 * Can be <code>null</code>. 411 * 412 * @param loader The <code>ClassLoader</code> to pass as the first parameter to 413 * <code>Proxy.newProxyInstance()</code>. If <code>null</code>, 414 * {@link #getDefaultClassLoader()} is 415 * called and its return value is used as the <code>ClassLoader</code>. 416 * 417 * @return The generated proxy instance. 418 */ 419 public Object newProxy(Class[] moreClasses, ClassLoader loader) { 420 if ( loader == null ) { 421 loader = getDefaultClassLoader(); 422 } 423 424 Class[] interfacesArray = getInterfaces(moreClasses); 425 426 if ( _log.isDebugEnabled() ) { 427 _log.debug( "Generating a proxy of interfaces " + interfacesArray + " using ClassLoader '" + loader + "'" ); 428 } 429 430 Object newProxy = Proxy.newProxyInstance( 431 loader, 432 interfacesArray, 433 this ); 434 435 { 436 DelegationState oldDelegationState = getDelegationState(); 437 DelegationState newDelegationState = new DelegationState( 438 this, null, newProxy, oldDelegationState); 439 setDelegationState(newDelegationState ); 440 try { 441 initializeProxy(newProxy); 442 } 443 finally { 444 setDelegationState( oldDelegationState ); 445 } 446 } 447 448 return newProxy; 449 } 450 451 /** 452 * Returns the <code>ClassLoader</code> to pass to the first parameter of <code>Proxy.newInstance()</code> 453 * if <code>null</code> is passed as the second parameter to {@link #newProxy(Class[], ClassLoader)}. 454 * This methods default implementation returns "<code>getClass().getClassLoader()</code>", 455 * but subclasses are free to override. 456 */ 457 protected ClassLoader getDefaultClassLoader() { 458 return Thread.currentThread().getContextClassLoader(); 459 } 460 461 /** 462 * Default implementation does nothing, but subclasses can override to perform any sort 463 * of initialization on a newly-generated proxy instance. 464 */ 465 public void initializeProxy(Object newProxy) {} 466 467 /** 468 * Overrides the method of {@link JPUInvocationHandler}. 469 * If <code>autoDelegateToSelf()</code> returns <code>true</code>, calls {@link #delegate(Object,Object,Method,Object[],Object[])}. 470 * Returns whether the invocation was handled or not. 471 */ 472 protected boolean doInvoke(Object proxy, Method method, Object[] args, Object[] result) throws Throwable { 473 boolean handled = false; 474 if ( autoDelegateToSelf(proxy, method, args) && delegate(proxy, this, method, args, result) ) { 475 return true; 476 } 477 return handled; 478 } 479 480 /** 481 * Default implementation does nothing, but subclasses can override to add to <code>dest</code> 482 * the <code>Class</code> instances of any interfaces the generated proxy should implement, 483 * in addition to those explicitly passed via the first parameter to 484 * {@link #newProxy(Class[], ClassLoader)}. 485 */ 486 protected void doGetImplementedInterfaces(Collection dest) { } 487 488 private static ThreadLocal _delegationState = new InheritableThreadLocal(); 489 private static Log _log = LogFactory.getLog(JPUInvocationHandler.class); 490 private static long _misses = 0L; 491 private static long _hits = 0L; 492 private static Map _subclasses = new IdentityMap(); 493 } // JPUInvocationHandler 494