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         * @see org.apache.hivemind.ServiceInterceptorFactory#createInterceptor(org.apache.hivemind.InterceptorStack, org.apache.hivemind.internal.Module, java.util.List)
043         */
044        public void createInterceptor(InterceptorStack stack, Module invokingModule, Object parameters)
045        {
046            final Object parameter = ((List) parameters).get( 0 ); 
047            Defense.isAssignable( parameter, MethodInterceptor.class, "Implementation Object" );
048            MethodInterceptor methodInterceptor = ( MethodInterceptor )parameter;
049            createInterceptor(stack, invokingModule, methodInterceptor);
050        }
051     
052        /**
053         * @see org.apache.hivemind.ServiceInterceptorFactory#createInterceptor(org.apache.hivemind.InterceptorStack, org.apache.hivemind.internal.Module, java.util.List)
054         */
055        public void createInterceptor(InterceptorStack stack, Module invokingModule, MethodInterceptor methodInterceptor)
056        {
057            final Class[] interfaces = new Class[]{stack.getServiceInterface()};
058            final ClassLoader classLoader = invokingModule.getClassResolver().getClassLoader();
059            final InvocationHandler invocationHandler = new MethodInterceptorInvocationHandler( methodInterceptor, stack );
060            stack.push( Proxy.newProxyInstance( classLoader, interfaces, invocationHandler ) );
061        }
062        
063        /**
064         * A java proxy InvocationHandler implementation which allows a MethodInterceptor to intercept the method invocation.
065         */
066        private final class MethodInterceptorInvocationHandler implements InvocationHandler
067        {
068            private final MethodInterceptor methodInterceptor;
069            private final InterceptorStack stack;
070            private final Object target;
071    
072            /**
073             * Constructs a MethodInterceptorInvocationHandler
074             *
075             * @param stack       the interceptor stack
076             */
077            public MethodInterceptorInvocationHandler( MethodInterceptor methodInterceptor, InterceptorStack stack )
078            {
079                this.stack = stack;
080                this.target = stack.peek();
081                this.methodInterceptor = methodInterceptor;
082            }
083    
084            /**
085             * Calls the MethodInterceptor's invoke method.
086             * @param proxy  a reference to the proxy instance
087             * @param method the method being invoked
088             * @param args   the arguments to the method
089             * @return the value returned by the MethodInterceptor
090             * @throws Throwable
091             */
092            public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
093            {
094                return methodInterceptor.invoke( new MethodInvocationImpl( target, method, args, stack.peek() ) );
095            }
096        }
097    
098        /**
099         * A java reflection-based implementation of a MethodInvocation
100         */
101        private final class MethodInvocationImpl implements MethodInvocation
102        {
103            private final Object next;
104            private final Method method;
105            private final Object[] arguments;
106            private final Object proxy;
107    
108            /**
109             * Constructs a MethodInvocationImpl object.
110             *
111             * @param next      the next object
112             * @param method    the method
113             * @param arguments the arguments
114             * @param proxy     the outermost proxy object (allows calling another method instead).
115             */
116            public MethodInvocationImpl( Object next, Method method, Object[] arguments, Object proxy )
117            {
118                this.next = next;
119                this.method = method;
120                this.arguments = arguments;
121                this.proxy = proxy;
122            }
123    
124            /**
125             * Invokes the method on the next object.
126             *
127             * @return value returned by invoking the method on the next object
128             * @throws Throwable throwable thrown by invoking method on the next object
129             */
130            public final Object proceed() throws Throwable
131            {
132                try
133                {
134                    return method.invoke( next, arguments );
135                }
136                catch( InvocationTargetException e )
137                {
138                    throw e.getTargetException();
139                }
140            }
141    
142            public final Method getMethod()
143            {
144                return method;
145            }
146    
147            public final AccessibleObject getStaticPart()
148            {
149                return method;
150            }
151    
152            public final Object getThis()
153            {
154                return proxy;
155            }
156    
157            public final Object[] getArguments()
158            {
159                return arguments;
160            }
161        }
162    }