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