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