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