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