1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.proxy2.asm;
18
19 import java.io.Serializable;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.UndeclaredThrowableException;
23 import java.util.concurrent.atomic.AtomicInteger;
24
25 import org.apache.commons.lang3.ArrayUtils;
26 import org.apache.commons.lang3.ObjectUtils;
27 import org.apache.commons.proxy2.Interceptor;
28 import org.apache.commons.proxy2.Invocation;
29 import org.apache.commons.proxy2.Invoker;
30 import org.apache.commons.proxy2.ObjectProvider;
31 import org.apache.commons.proxy2.ProxyUtils;
32 import org.apache.commons.proxy2.exception.ProxyFactoryException;
33 import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator;
34 import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory;
35 import org.apache.commons.proxy2.impl.ProxyClassCache;
36 import org.objectweb.asm.ClassWriter;
37 import org.objectweb.asm.Label;
38 import org.objectweb.asm.Opcodes;
39 import org.objectweb.asm.Type;
40 import org.objectweb.asm.commons.GeneratorAdapter;
41
42 public class ASMProxyFactory extends AbstractSubclassingProxyFactory
43 {
44 private static final ProxyClassCache PROXY_CLASS_CACHE = new ProxyClassCache(new ProxyGenerator());
45
46 @Override
47 public <T> T createDelegatorProxy(final ClassLoader classLoader, final ObjectProvider<?> delegateProvider,
48 final Class<?>... proxyClasses)
49 {
50 return createProxy(classLoader, new DelegatorInvoker(delegateProvider), proxyClasses);
51 }
52
53 @Override
54 public <T> T createInterceptorProxy(final ClassLoader classLoader, final Object target,
55 final Interceptor interceptor, final Class<?>... proxyClasses)
56 {
57 return createProxy(classLoader, new InterceptorInvoker(target, interceptor), proxyClasses);
58 }
59
60 @Override
61 public <T> T createInvokerProxy(final ClassLoader classLoader, final Invoker invoker,
62 final Class<?>... proxyClasses)
63 {
64 return createProxy(classLoader, new InvokerInvoker(invoker), proxyClasses);
65 }
66
67 private <T> T createProxy(final ClassLoader classLoader, final AbstractInvoker invoker,
68 final Class<?>... proxyClasses)
69 {
70 final Class<?> proxyClass = PROXY_CLASS_CACHE.getProxyClass(classLoader, proxyClasses);
71 try
72 {
73 @SuppressWarnings("unchecked")
74 final T result = (T) proxyClass.getConstructor(Invoker.class).newInstance(invoker);
75 return result;
76 }
77 catch (Exception e)
78 {
79 throw e instanceof RuntimeException ? ((RuntimeException) e) : new RuntimeException(e);
80 }
81 }
82
83 private static class ProxyGenerator extends AbstractProxyClassGenerator implements Opcodes
84 {
85 private static final AtomicInteger CLASS_NUMBER = new AtomicInteger(0);
86 private static final String CLASSNAME_PREFIX = "CommonsProxyASM_";
87 private static final String HANDLER_NAME = "__handler";
88 private static final Type INVOKER_TYPE = Type.getType(Invoker.class);
89
90 @Override
91 public Class<?> generateProxyClass(final ClassLoader classLoader, final Class<?>... proxyClasses)
92 {
93 final Class<?> superclass = getSuperclass(proxyClasses);
94 final String proxyName = CLASSNAME_PREFIX + CLASS_NUMBER.incrementAndGet();
95 final Method[] implementationMethods = getImplementationMethods(proxyClasses);
96 final Class<?>[] interfaces = toInterfaces(proxyClasses);
97 final String classFileName = proxyName.replace('.', '/');
98
99 try
100 {
101 final byte[] proxyBytes = generateProxy(superclass, classFileName, implementationMethods, interfaces);
102 return loadClass(classLoader, proxyName, proxyBytes);
103 }
104 catch (final Exception e)
105 {
106 throw new ProxyFactoryException(e);
107 }
108 }
109
110 private static byte[] generateProxy(final Class<?> classToProxy, final String proxyName,
111 final Method[] methods, final Class<?>... interfaces) throws ProxyFactoryException
112 {
113 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
114
115 final Type proxyType = Type.getObjectType(proxyName);
116
117
118 final String[] interfaceNames = new String[interfaces.length];
119 for (int i = 0; i < interfaces.length; i++)
120 {
121 interfaceNames[i] = Type.getType(interfaces[i]).getInternalName();
122 }
123
124 final Type superType = Type.getType(classToProxy);
125 cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, proxyType.getInternalName(), null, superType.getInternalName(),
126 interfaceNames);
127
128
129 cw.visitField(ACC_FINAL + ACC_PRIVATE, HANDLER_NAME, INVOKER_TYPE.getDescriptor(), null, null).visitEnd();
130
131 init(cw, proxyType, superType);
132
133 for (final Method method : methods)
134 {
135 processMethod(cw, method, proxyType, HANDLER_NAME);
136 }
137
138 return cw.toByteArray();
139 }
140
141 private static void init(final ClassWriter cw, final Type proxyType, Type superType)
142 {
143 final GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, new org.objectweb.asm.commons.Method("<init>",
144 Type.VOID_TYPE, new Type[] { INVOKER_TYPE }), null, null, cw);
145
146 mg.loadThis();
147 mg.invokeConstructor(superType, org.objectweb.asm.commons.Method.getMethod("void <init> ()"));
148
149
150 mg.loadThis();
151 mg.loadArg(0);
152 mg.putField(proxyType, HANDLER_NAME, INVOKER_TYPE);
153 mg.returnValue();
154 mg.endMethod();
155 }
156
157 private static void processMethod(final ClassWriter cw, final Method method, final Type proxyType,
158 final String handlerName) throws ProxyFactoryException
159 {
160 final Type sig = Type.getType(method);
161 final Type[] exceptionTypes = getTypes(method.getExceptionTypes());
162
163
164 final int access = (ACC_PUBLIC | ACC_PROTECTED) & method.getModifiers();
165 final org.objectweb.asm.commons.Method m = org.objectweb.asm.commons.Method.getMethod(method);
166 final GeneratorAdapter mg = new GeneratorAdapter(access, m, null, getTypes(method.getExceptionTypes()), cw);
167
168 final Label tryBlock = exceptionTypes.length > 0 ? mg.mark() : null;
169
170 mg.push(Type.getType(method.getDeclaringClass()));
171
172
173
174
175
176
177
178 mg.push(method.getName());
179
180
181 mg.push(sig.getArgumentTypes().length);
182 final Type classType = Type.getType(Class.class);
183 mg.newArray(classType);
184
185
186 for (int i = 0; i < sig.getArgumentTypes().length; i++)
187 {
188
189 mg.dup();
190
191
192 mg.push(i);
193 mg.push(sig.getArgumentTypes()[i]);
194 mg.arrayStore(classType);
195 }
196
197
198 mg.invokeVirtual(classType, org.objectweb.asm.commons.Method
199 .getMethod("java.lang.reflect.Method getDeclaredMethod(String, Class[])"));
200
201
202
203
204
205
206 mg.loadThis();
207
208 mg.getField(proxyType, handlerName, INVOKER_TYPE);
209
210 mg.swap();
211
212
213 mg.loadThis();
214
215 mg.swap();
216
217
218
219
220 mg.push(sig.getArgumentTypes().length);
221 final Type objectType = Type.getType(Object.class);
222 mg.newArray(objectType);
223
224
225 for (int i = 0; i < sig.getArgumentTypes().length; i++)
226 {
227
228 mg.dup();
229
230
231 mg.push(i);
232
233 mg.loadArg(i);
234 mg.valueOf(sig.getArgumentTypes()[i]);
235 mg.arrayStore(objectType);
236 }
237
238
239 mg.invokeInterface(INVOKER_TYPE, org.objectweb.asm.commons.Method
240 .getMethod("Object invoke(Object, java.lang.reflect.Method, Object[])"));
241
242
243 mg.unbox(sig.getReturnType());
244
245
246 mg.returnValue();
247
248
249 if (exceptionTypes.length > 0)
250 {
251 final Type caughtExceptionType = Type.getType(InvocationTargetException.class);
252 mg.catchException(tryBlock, mg.mark(), caughtExceptionType);
253
254 final Label throwCause = new Label();
255
256 mg.invokeVirtual(caughtExceptionType,
257 org.objectweb.asm.commons.Method.getMethod("Throwable getCause()"));
258
259 for (int i = 0; i < exceptionTypes.length; i++)
260 {
261 mg.dup();
262 mg.push(exceptionTypes[i]);
263 mg.swap();
264 mg.invokeVirtual(classType,
265 org.objectweb.asm.commons.Method.getMethod("boolean isInstance(Object)"));
266
267 mg.ifZCmp(GeneratorAdapter.NE, throwCause);
268 }
269
270
271 final int cause = mg.newLocal(Type.getType(Exception.class));
272 mg.storeLocal(cause);
273 final Type undeclaredType = Type.getType(UndeclaredThrowableException.class);
274 mg.newInstance(undeclaredType);
275 mg.dup();
276 mg.loadLocal(cause);
277 mg.invokeConstructor(undeclaredType, new org.objectweb.asm.commons.Method("<init>", Type.VOID_TYPE,
278 new Type[] { Type.getType(Throwable.class) }));
279 mg.throwException();
280
281 mg.mark(throwCause);
282 mg.throwException();
283 }
284
285
286 mg.endMethod();
287 }
288
289 private static Type[] getTypes(Class<?>... src)
290 {
291 final Type[] result = new Type[src.length];
292 for (int i = 0; i < result.length; i++)
293 {
294 result[i] = Type.getType(src[i]);
295 }
296 return result;
297 }
298
299
300
301
302
303
304
305 private static Class<?> loadClass(final ClassLoader loader, String className, byte[] b)
306 {
307
308 try
309 {
310 final Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class,
311 int.class, int.class);
312
313
314 final boolean accessible = method.isAccessible();
315 if (!accessible)
316 {
317 method.setAccessible(true);
318 }
319 try
320 {
321 return (Class<?>) method
322 .invoke(loader, className, b, Integer.valueOf(0), Integer.valueOf(b.length));
323 }
324 finally
325 {
326 if (!accessible)
327 {
328 method.setAccessible(false);
329 }
330 }
331 }
332 catch (Exception e)
333 {
334 throw e instanceof RuntimeException ? ((RuntimeException) e) : new RuntimeException(e);
335 }
336 }
337 }
338
339 @SuppressWarnings("serial")
340 private static class DelegatorInvoker extends AbstractInvoker
341 {
342 private final ObjectProvider<?> delegateProvider;
343
344 protected DelegatorInvoker(ObjectProvider<?> delegateProvider)
345 {
346 this.delegateProvider = delegateProvider;
347 }
348
349 @Override
350 public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
351 {
352 try
353 {
354 return method.invoke(delegateProvider.getObject(), args);
355 }
356 catch (InvocationTargetException e)
357 {
358 throw e.getTargetException();
359 }
360 }
361 }
362
363 @SuppressWarnings("serial")
364 private static class InterceptorInvoker extends AbstractInvoker
365 {
366 private final Object target;
367 private final Interceptor methodInterceptor;
368
369 public InterceptorInvoker(Object target, Interceptor methodInterceptor)
370 {
371 this.target = target;
372 this.methodInterceptor = methodInterceptor;
373 }
374
375 @Override
376 public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
377 {
378 final ReflectionInvocation invocation = new ReflectionInvocation(target, proxy, method, args);
379 return methodInterceptor.intercept(invocation);
380 }
381 }
382
383 @SuppressWarnings("serial")
384 private abstract static class AbstractInvoker implements Invoker, Serializable
385 {
386 @Override
387 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
388 {
389 if (isHashCode(method))
390 {
391 return Integer.valueOf(System.identityHashCode(proxy));
392 }
393 if (isEqualsMethod(method))
394 {
395 return Boolean.valueOf(proxy == args[0]);
396 }
397 return invokeImpl(proxy, method, args);
398 }
399
400 protected abstract Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable;
401 }
402
403 @SuppressWarnings("serial")
404 private static class InvokerInvoker extends AbstractInvoker
405 {
406 private final Invoker invoker;
407
408 public InvokerInvoker(Invoker invoker)
409 {
410 this.invoker = invoker;
411 }
412
413 @Override
414 public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
415 {
416 return invoker.invoke(proxy, method, args);
417 }
418 }
419
420 protected static boolean isHashCode(Method method)
421 {
422 return "hashCode".equals(method.getName()) && Integer.TYPE.equals(method.getReturnType())
423 && method.getParameterTypes().length == 0;
424 }
425
426 protected static boolean isEqualsMethod(Method method)
427 {
428 return "equals".equals(method.getName()) && Boolean.TYPE.equals(method.getReturnType())
429 && method.getParameterTypes().length == 1 && Object.class.equals(method.getParameterTypes()[0]);
430 }
431
432 private static class ReflectionInvocation implements Invocation
433 {
434 private final Method method;
435 private final Object[] arguments;
436 private final Object proxy;
437 private final Object target;
438
439 public ReflectionInvocation(final Object target, final Object proxy, final Method method,
440 final Object[] arguments)
441 {
442 this.method = method;
443 this.arguments = ObjectUtils.defaultIfNull(ArrayUtils.clone(arguments), ProxyUtils.EMPTY_ARGUMENTS);
444 this.proxy = proxy;
445 this.target = target;
446 }
447
448 @Override
449 public Object[] getArguments()
450 {
451 return arguments;
452 }
453
454 @Override
455 public Method getMethod()
456 {
457 return method;
458 }
459
460 @Override
461 public Object getProxy()
462 {
463 return proxy;
464 }
465
466 @Override
467 public Object proceed() throws Throwable
468 {
469 try
470 {
471 return method.invoke(target, arguments);
472 }
473 catch (InvocationTargetException e)
474 {
475 throw e.getTargetException();
476 }
477 }
478 }
479 }