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