View Javadoc

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   * Proxy creation utilities
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  This class encapsulates a proxy and it's construction args. These should be cached per class.
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          //todo: add more types
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 }