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