1 package com.atlassian.plugins.rest.module.util;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.InvocationHandler;
5 import java.lang.reflect.Method;
6 import java.lang.reflect.Modifier;
7 import java.lang.reflect.Proxy;
8 import java.util.concurrent.ExecutionException;
9
10 import net.sf.cglib.proxy.Callback;
11 import net.sf.cglib.proxy.Enhancer;
12 import net.sf.cglib.proxy.Factory;
13
14 import com.atlassian.plugins.rest.module.ChainingClassLoader;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18
19
20
21
22 public class ProxyUtils {
23 private static LoadingCache<Class<?>, ConstructorAndArgs> generatorCache = CacheBuilder.newBuilder().build(new CacheLoader<Class<?>, ConstructorAndArgs>() {
24 @Override
25 public ConstructorAndArgs load(Class<?> from) throws Exception {
26 return new ConstructorAndArgs(from);
27 }
28 });
29
30 public static <T> T create(Class<T> clazz, Callback callback) {
31 try {
32 return (T) generatorCache.get(clazz).create(callback);
33 } catch (ExecutionException e) {
34 throw new RuntimeException("Failed to construct class: ", e);
35 }
36 }
37 }
38
39
40
41
42
43 class ConstructorAndArgs {
44 private Class<?> clazz;
45 private Object prototype;
46 private Object[] args;
47 private Constructor<?> constructor;
48
49 ConstructorAndArgs(Class<?> clazz) {
50 this.clazz = clazz;
51 initialise();
52 }
53
54 private void initialise() {
55 Enhancer enhancer = new Enhancer();
56 enhancer.setSuperclass(clazz);
57 enhancer.setCallback(new UnsupportedOperationInvocationHandler());
58 enhancer.setClassLoader(new ChainingClassLoader(ProxyUtils.class.getClassLoader(), clazz.getClassLoader()));
59
60 Constructor<?>[] constructors = clazz.getConstructors();
61
62 for (Constructor constructor : constructors) {
63 if ((constructor.getModifiers() & Modifier.PUBLIC) != 0) {
64 this.constructor = constructor;
65
66 int size = constructor.getParameterTypes().length;
67 args = new Object[size];
68
69 for (int i = 0; i < args.length; i++)
70 args[i] = createEmptyValue(constructor.getParameterTypes()[i]);
71
72 prototype = clazz.cast(enhancer.create(constructor.getParameterTypes(), args));
73 return;
74 }
75 }
76
77 throw new IllegalArgumentException("Class has no accessible constructor");
78 }
79
80
81 private static Object createEmptyValue(Class aClass) {
82
83 if (aClass.isInterface())
84 return stubInterface(aClass);
85 else if (aClass == Long.TYPE)
86 return 0L;
87 else
88 return null;
89 }
90
91 private static Object stubInterface(Class _interface) {
92 return Proxy.newProxyInstance(_interface.getClassLoader(), new Class[]{_interface}, UnsupportedOperationInvocationHandler.INSTANCE);
93 }
94
95 public Object create(Callback... callback) {
96 return clazz.cast(((Factory) prototype).newInstance(constructor.getParameterTypes(), args, callback));
97 }
98 }
99
100 class UnsupportedOperationInvocationHandler implements InvocationHandler, net.sf.cglib.proxy.InvocationHandler {
101 public static UnsupportedOperationInvocationHandler INSTANCE = new UnsupportedOperationInvocationHandler();
102
103 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
104 throw new UnsupportedOperationException();
105 }
106 }