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 {
24 private static LoadingCache<Class<?>, ConstructorAndArgs> generatorCache = CacheBuilder.newBuilder().build(new CacheLoader<Class<?>, ConstructorAndArgs>()
25 {
26 @Override
27 public ConstructorAndArgs load(Class<?> from) throws Exception
28 {
29 return new ConstructorAndArgs(from);
30 }
31 });
32
33 public static <T> T create(Class<T> clazz, Callback callback)
34 {
35 try
36 {
37 return (T) generatorCache.get(clazz).create(callback);
38 }
39 catch (ExecutionException e)
40 {
41 throw new RuntimeException("Failed to construct class: ", e);
42 }
43 }
44 }
45
46
47
48
49
50 class ConstructorAndArgs
51 {
52 private Class<?> clazz;
53 private Object prototype;
54 private Object[] args;
55 private Constructor<?> constructor;
56
57 ConstructorAndArgs(Class<?> clazz)
58 {
59 this.clazz = clazz;
60 initialise();
61 }
62
63 private void initialise()
64 {
65 Enhancer enhancer = new Enhancer();
66 enhancer.setSuperclass(clazz);
67 enhancer.setCallback(new UnsupportedOperationInvocationHandler());
68 enhancer.setClassLoader(new ChainingClassLoader(ProxyUtils.class.getClassLoader(), clazz.getClassLoader()));
69
70 Constructor<?>[] constructors = clazz.getConstructors();
71
72 for (Constructor constructor : constructors)
73 {
74 if ((constructor.getModifiers() & Modifier.PUBLIC) != 0)
75 {
76 this.constructor = constructor;
77
78 int size = constructor.getParameterTypes().length;
79 args = new Object[size];
80
81 for (int i = 0; i < args.length; i++)
82 args[i] = createEmptyValue(constructor.getParameterTypes()[i]);
83
84 prototype = clazz.cast(enhancer.create(constructor.getParameterTypes(), args));
85 return;
86 }
87 }
88
89 throw new IllegalArgumentException("Class has no accessible constructor");
90 }
91
92
93 private static Object createEmptyValue(Class aClass)
94 {
95
96 if (aClass.isInterface())
97 return stubInterface(aClass);
98 else if (aClass == Long.TYPE)
99 return 0L;
100 else
101 return null;
102 }
103
104 private static Object stubInterface(Class _interface)
105 {
106 return Proxy.newProxyInstance(_interface.getClassLoader(), new Class[]{_interface}, UnsupportedOperationInvocationHandler.INSTANCE);
107 }
108
109 public Object create(Callback... callback)
110 {
111 return clazz.cast(((Factory) prototype).newInstance(constructor.getParameterTypes(), args, callback));
112 }
113 }
114
115 class UnsupportedOperationInvocationHandler implements InvocationHandler, net.sf.cglib.proxy.InvocationHandler
116 {
117 public static UnsupportedOperationInvocationHandler INSTANCE = new UnsupportedOperationInvocationHandler();
118
119 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
120 {
121 throw new UnsupportedOperationException();
122 }
123 }