001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.lib.impl;
016    
017    import java.lang.reflect.AccessibleObject;
018    import java.lang.reflect.InvocationHandler;
019    import java.lang.reflect.InvocationTargetException;
020    import java.lang.reflect.Method;
021    import java.lang.reflect.Proxy;
022    import java.util.List;
023    
024    import org.aopalliance.intercept.MethodInterceptor;
025    import org.aopalliance.intercept.MethodInvocation;
026    import org.apache.hivemind.InterceptorStack;
027    import org.apache.hivemind.ServiceInterceptorFactory;
028    import org.apache.hivemind.impl.BaseLocatable;
029    import org.apache.hivemind.internal.Module;
030    import org.apache.hivemind.util.Defense;
031    
032    /**
033     * A service interceptor factory supporting the AOP Alliance MethodInterceptor interface.
034     * <b>Note:</b>The current implementation uses JDK proxies as opposed to Javassist! 
035     * @author James Carman
036     * @since 1.1
037     */
038    public class MethodInterceptorFactory extends BaseLocatable implements ServiceInterceptorFactory
039    {
040    
041        /**
042         * 
043         * @see org.apache.hivemind.ServiceInterceptorFactory#createInterceptor(org.apache.hivemind.InterceptorStack, org.apache.hivemind.internal.Module, java.util.List)
044         */
045        public void createInterceptor(InterceptorStack stack, Module invokingModule, List parameters)
046        {
047            final Class[] interfaces = new Class[]{stack.getServiceInterface()};
048            final ClassLoader classLoader = invokingModule.getClassResolver().getClassLoader();
049            final Object parameter = parameters.get( 0 );
050            Defense.isAssignable( parameter, MethodInterceptor.class, "Implementation Object" );
051            MethodInterceptor methodInterceptor = ( MethodInterceptor )parameter;
052            final InvocationHandler invocationHandler = new MethodInterceptorInvocationHandler( methodInterceptor, stack );
053            stack.push( Proxy.newProxyInstance( classLoader, interfaces, invocationHandler ) );
054        }
055        
056        /**
057         * A java proxy InvocationHandler implementation which allows a MethodInterceptor to intercept the method invocation.
058         */
059        private final class MethodInterceptorInvocationHandler implements InvocationHandler
060        {
061            private final MethodInterceptor methodInterceptor;
062            private final InterceptorStack stack;
063            private final Object target;
064    
065            /**
066             * Constructs a MethodInterceptorInvocationHandler
067             *
068             * @param stack       the interceptor stack
069             */
070            public MethodInterceptorInvocationHandler( MethodInterceptor methodInterceptor, InterceptorStack stack )
071            {
072                this.stack = stack;
073                this.target = stack.peek();
074                this.methodInterceptor = methodInterceptor;
075            }
076    
077            /**
078             * Calls the MethodInterceptor's invoke method.
079             * @param proxy  a reference to the proxy instance
080             * @param method the method being invoked
081             * @param args   the arguments to the method
082             * @return the value returned by the MethodInterceptor
083             * @throws Throwable
084             */
085            public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
086            {
087                return methodInterceptor.invoke( new MethodInvocationImpl( target, method, args, stack.peek() ) );
088            }
089        }
090    
091        /**
092         * A java reflection-based implementation of a MethodInvocation
093         */
094        private final class MethodInvocationImpl implements MethodInvocation
095        {
096            private final Object next;
097            private final Method method;
098            private final Object[] arguments;
099            private final Object proxy;
100    
101            /**
102             * Constructs a MethodInvocationImpl object.
103             *
104             * @param next      the next object
105             * @param method    the method
106             * @param arguments the arguments
107             * @param proxy     the outermost proxy object (allows calling another method instead).
108             */
109            public MethodInvocationImpl( Object next, Method method, Object[] arguments, Object proxy )
110            {
111                this.next = next;
112                this.method = method;
113                this.arguments = arguments;
114                this.proxy = proxy;
115            }
116    
117            /**
118             * Invokes the method on the next object.
119             *
120             * @return value returned by invoking the method on the next object
121             * @throws Throwable throwable thrown by invoking method on the next object
122             */
123            public final Object proceed() throws Throwable
124            {
125                try
126                {
127                    return method.invoke( next, arguments );
128                }
129                catch( InvocationTargetException e )
130                {
131                    throw e.getTargetException();
132                }
133            }
134    
135            public final Method getMethod()
136            {
137                return method;
138            }
139    
140            public final AccessibleObject getStaticPart()
141            {
142                return method;
143            }
144    
145            public final Object getThis()
146            {
147                return proxy;
148            }
149    
150            public final Object[] getArguments()
151            {
152                return arguments;
153            }
154        }
155    }