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 "flavors" 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