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