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