001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.proxy;
019    
020    import java.lang.reflect.InvocationHandler;
021    import java.lang.reflect.InvocationTargetException;
022    import java.lang.reflect.Method;
023    import java.lang.reflect.Proxy;
024    
025    /**
026     * A <code>ProxyFactory</code> can be used to create three different &quot;flavors&quot; of proxy objects.
027     *
028     * <ul>
029     *   <li>Delegator - the proxy will delegate to an object provided by an {@link ObjectProvider}</li>
030     *   <li>Interceptor - the proxy will pass each method invocation through an {@link Interceptor}</li>
031     *   <li>Invoker - the proxy will allow an {@link Invoker} to handle all method invocations</li>
032     * </ul>
033     *
034     * <p>
035     * Originally, the ProxyFactory class was an interface.  However, to allow for future changes to the
036     * class without breaking binary or semantic compatibility, it has been changed to a concrete class.
037     *
038     * </p>
039     * <p>
040     * <b>Note</b>: This class uses Java reflection.  For more efficient proxies, try using either
041     * {@link org.apache.commons.proxy.factory.cglib.CglibProxyFactory CglibProxyFactory} or
042     * {@link org.apache.commons.proxy.factory.javassist.JavassistProxyFactory JavassistProxyFactory} instead.
043     * </p>
044     * @author James Carman
045     * @since 1.0
046     */
047    public class ProxyFactory
048    {
049    //----------------------------------------------------------------------------------------------------------------------
050    // Other Methods
051    //----------------------------------------------------------------------------------------------------------------------
052    
053        /**
054         * Returns true if all <code>proxyClasses</code> are interfaces.
055         *
056         * @param proxyClasses the proxy classes
057         * @return true if all <code>proxyClasses</code> are interfaces
058         */
059        public boolean canProxy( Class[] proxyClasses )
060        {
061            for( int i = 0; i < proxyClasses.length; i++ )
062            {
063                Class proxyClass = proxyClasses[i];
064                if( !proxyClass.isInterface() )
065                {
066                    return false;
067                }
068            }
069            return true;
070        }
071    
072        /**
073         * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.  The proxy will be
074         * generated using the current thread's "context class loader."
075         *
076         * @param delegateProvider the delegate provider
077         * @param proxyClasses     the interfaces that the proxy should implement
078         * @return a proxy which delegates to the object provided by the target object provider
079         */
080        public Object createDelegatorProxy( ObjectProvider delegateProvider, Class[] proxyClasses )
081        {
082            return createDelegatorProxy( Thread.currentThread().getContextClassLoader(), delegateProvider, proxyClasses );
083        }
084    
085        /**
086         * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.
087         *
088         * @param classLoader      the class loader to use when generating the proxy
089         * @param delegateProvider the delegate provider
090         * @param proxyClasses     the interfaces that the proxy should implement
091         * @return a proxy which delegates to the object provided by the target <code>delegateProvider>
092         */
093        public Object createDelegatorProxy( ClassLoader classLoader, ObjectProvider delegateProvider,
094                                            Class[] proxyClasses )
095        {
096            return Proxy.newProxyInstance( classLoader, proxyClasses,
097                                           new DelegatorInvocationHandler( delegateProvider ) );
098        }
099    
100        /**
101         * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
102         * <code>target</code> object.  The proxy will be generated using the current thread's "context class loader."
103         *
104         * @param target       the target object
105         * @param interceptor  the method interceptor
106         * @param proxyClasses the interfaces that the proxy should implement
107         * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
108         *         <code>target</code> object.
109         */
110        public Object createInterceptorProxy( Object target, Interceptor interceptor,
111                                              Class[] proxyClasses )
112        {
113            return createInterceptorProxy( Thread.currentThread().getContextClassLoader(), target, interceptor,
114                                           proxyClasses );
115        }
116    
117        /**
118         * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
119         * <code>target</code> object.
120         *
121         * @param classLoader  the class loader to use when generating the proxy
122         * @param target       the target object
123         * @param interceptor  the method interceptor
124         * @param proxyClasses the interfaces that the proxy should implement.
125         * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
126         *         <code>target</code> object.
127         */
128        public Object createInterceptorProxy( ClassLoader classLoader, Object target, Interceptor interceptor,
129                                              Class[] proxyClasses )
130        {
131            return Proxy
132                    .newProxyInstance( classLoader, proxyClasses, new InterceptorInvocationHandler( target, interceptor ) );
133        }
134    
135        /**
136         * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.  The proxy will be
137         * generated using the current thread's "context class loader."
138         *
139         * @param invoker      the invoker
140         * @param proxyClasses the interfaces that the proxy should implement
141         * @return a proxy which uses the provided {@link Invoker} to handle all method invocations
142         */
143        public Object createInvokerProxy( Invoker invoker, Class[] proxyClasses )
144        {
145            return createInvokerProxy( Thread.currentThread().getContextClassLoader(), invoker,
146                                       proxyClasses );
147        }
148    
149        /**
150         * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.
151         *
152         * @param classLoader  the class loader to use when generating the proxy
153         * @param invoker      the invoker
154         * @param proxyClasses the interfaces that the proxy should implement
155         * @return a proxy which uses the provided {@link Invoker} to handle all method invocations
156         */
157        public Object createInvokerProxy( ClassLoader classLoader, Invoker invoker,
158                                          Class[] proxyClasses )
159        {
160            return Proxy.newProxyInstance( classLoader, proxyClasses, new InvokerInvocationHandler( invoker ) );
161        }
162    
163    //----------------------------------------------------------------------------------------------------------------------
164    // Inner Classes
165    //----------------------------------------------------------------------------------------------------------------------
166    
167        private static class DelegatorInvocationHandler implements InvocationHandler
168        {
169            private final ObjectProvider delegateProvider;
170            protected DelegatorInvocationHandler( ObjectProvider delegateProvider )
171            {
172                this.delegateProvider = delegateProvider;
173            }
174    
175            public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
176            {
177                try
178                {
179                    return method.invoke( delegateProvider.getObject(), args );
180                }
181                catch( InvocationTargetException e )
182                {
183                    throw e.getTargetException();
184                }
185            }
186        }
187    
188        private static class InterceptorInvocationHandler implements InvocationHandler
189        {
190            private final Object target;
191            private final Interceptor methodInterceptor;
192            public InterceptorInvocationHandler( Object target, Interceptor methodInterceptor )
193            {
194                this.target = target;
195                this.methodInterceptor = methodInterceptor;
196            }
197    
198            public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
199            {
200                final ReflectionInvocation invocation = new ReflectionInvocation( target, method, args );
201                return methodInterceptor.intercept( invocation );
202            }
203        }
204    
205        private static class ReflectionInvocation implements Invocation
206        {
207            private final Method method;
208            private final Object[] arguments;
209            private final Object target;
210            public ReflectionInvocation( Object target, Method method, Object[] arguments )
211            {
212                this.method = method;
213                this.arguments = ( arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments );
214                this.target = target;
215            }
216    
217            public Object[] getArguments()
218            {
219                return arguments;
220            }
221    
222            public Method getMethod()
223            {
224                return method;
225            }
226    
227            public Object getProxy()
228            {
229                return target;
230            }
231    
232            public Object proceed() throws Throwable
233            {
234                try
235                {
236                    return method.invoke( target, arguments );
237                }
238                catch( InvocationTargetException e )
239                {
240                    throw e.getTargetException();
241                }
242            }
243        }
244    
245        private static class InvokerInvocationHandler implements InvocationHandler
246        {
247            private final Invoker invoker;
248            public InvokerInvocationHandler( Invoker invoker )
249            {
250                this.invoker = invoker;
251            }
252    
253            public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
254            {
255                return invoker.invoke( proxy, method, args );
256            }
257        }
258    }
259