View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.proxy;
19  
20  import java.lang.reflect.InvocationHandler;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Proxy;
24  
25  /**
26   * A <code>ProxyFactory</code> can be used to create three different &quot;flavors&quot; of proxy objects.
27   *
28   * <ul>
29   *   <li>Delegator - the proxy will delegate to an object provided by an {@link ObjectProvider}</li>
30   *   <li>Interceptor - the proxy will pass each method invocation through an {@link Interceptor}</li>
31   *   <li>Invoker - the proxy will allow an {@link Invoker} to handle all method invocations</li>
32   * </ul>
33   *
34   * <p>
35   * Originally, the ProxyFactory class was an interface.  However, to allow for future changes to the
36   * class without breaking binary or semantic compatibility, it has been changed to a concrete class.
37   *
38   * </p>
39   * <p>
40   * <b>Note</b>: This class uses Java reflection.  For more efficient proxies, try using either
41   * {@link org.apache.commons.proxy.factory.cglib.CglibProxyFactory CglibProxyFactory} or
42   * {@link org.apache.commons.proxy.factory.javassist.JavassistProxyFactory JavassistProxyFactory} instead.
43   * </p>
44   * @author James Carman
45   * @since 1.0
46   */
47  public class ProxyFactory
48  {
49  //----------------------------------------------------------------------------------------------------------------------
50  // Other Methods
51  //----------------------------------------------------------------------------------------------------------------------
52  
53      /**
54       * Returns true if all <code>proxyClasses</code> are interfaces.
55       *
56       * @param proxyClasses the proxy classes
57       * @return true if all <code>proxyClasses</code> are interfaces
58       */
59      public boolean canProxy( Class[] proxyClasses )
60      {
61          for( int i = 0; i < proxyClasses.length; i++ )
62          {
63              Class proxyClass = proxyClasses[i];
64              if( !proxyClass.isInterface() )
65              {
66                  return false;
67              }
68          }
69          return true;
70      }
71  
72      /**
73       * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.  The proxy will be
74       * generated using the current thread's "context class loader."
75       *
76       * @param delegateProvider the delegate provider
77       * @param proxyClasses     the interfaces that the proxy should implement
78       * @return a proxy which delegates to the object provided by the target object provider
79       */
80      public Object createDelegatorProxy( ObjectProvider delegateProvider, Class[] proxyClasses )
81      {
82          return createDelegatorProxy( Thread.currentThread().getContextClassLoader(), delegateProvider, proxyClasses );
83      }
84  
85      /**
86       * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.
87       *
88       * @param classLoader      the class loader to use when generating the proxy
89       * @param delegateProvider the delegate provider
90       * @param proxyClasses     the interfaces that the proxy should implement
91       * @return a proxy which delegates to the object provided by the target <code>delegateProvider>
92       */
93      public Object createDelegatorProxy( ClassLoader classLoader, ObjectProvider delegateProvider,
94                                          Class[] proxyClasses )
95      {
96          return Proxy.newProxyInstance( classLoader, proxyClasses,
97                                         new DelegatorInvocationHandler( delegateProvider ) );
98      }
99  
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