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
015package org.apache.hivemind.lib.impl;
016
017import java.lang.reflect.AccessibleObject;
018import java.lang.reflect.InvocationHandler;
019import java.lang.reflect.InvocationTargetException;
020import java.lang.reflect.Method;
021import java.lang.reflect.Proxy;
022import java.util.List;
023
024import org.aopalliance.intercept.MethodInterceptor;
025import org.aopalliance.intercept.MethodInvocation;
026import org.apache.hivemind.InterceptorStack;
027import org.apache.hivemind.ServiceInterceptorFactory;
028import org.apache.hivemind.impl.BaseLocatable;
029import org.apache.hivemind.internal.Module;
030import 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 */
038public 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}