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