View Javadoc

1   package com.atlassian.plugins.rest.module.util;
2   
3   import com.atlassian.plugins.rest.module.ChainingClassLoader;
4   import com.google.common.base.Function;
5   import com.google.common.collect.MapMaker;
6   import net.sf.cglib.proxy.Callback;
7   import net.sf.cglib.proxy.Enhancer;
8   import net.sf.cglib.proxy.Factory;
9   
10  import java.lang.reflect.Constructor;
11  import java.lang.reflect.InvocationHandler;
12  import java.lang.reflect.Method;
13  import java.lang.reflect.Modifier;
14  import java.lang.reflect.Proxy;
15  import java.util.Map;
16  
17  /**
18   * Proxy creation utilities
19   */
20  public class ProxyUtils
21  {
22      private static Map<Class<?>, ConstructorAndArgs> generatorCache = new MapMaker().weakKeys().makeComputingMap(new Function<Class<?>, ConstructorAndArgs>()
23      {
24          public ConstructorAndArgs apply(Class<?> from)
25          {
26              return new ConstructorAndArgs(from);
27          }
28      });
29  
30      public static <T> T create(Class<T> clazz, Callback callback)
31      {
32          return (T) generatorCache.get(clazz).create(callback);
33      }
34  }
35  
36  /*
37  This class encapsulates a proxy and it's construction args. These should be cached per class.
38   */
39  
40  class ConstructorAndArgs
41  {
42      private Class<?> clazz;
43      private Object prototype;
44      private Object[] args;
45      private Constructor<?> constructor;
46  
47      ConstructorAndArgs(Class<?> clazz)
48      {
49          this.clazz = clazz;
50          initialise();
51      }
52  
53      private void initialise()
54      {
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          {
64              if ((constructor.getModifiers() & Modifier.PUBLIC) != 0)
65              {
66                  this.constructor = constructor;
67  
68                  int size = constructor.getParameterTypes().length;
69                  args = new Object[size];
70  
71                  for (int i = 0; i < args.length; i++)
72                      args[i] = createEmptyValue(constructor.getParameterTypes()[i]);
73  
74                  prototype = clazz.cast(enhancer.create(constructor.getParameterTypes(), args));
75                  return;
76              }
77          }
78  
79          throw new IllegalArgumentException("Class has no accessible constructor");
80      }
81  
82  
83      private static Object createEmptyValue(Class aClass)
84      {
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      {
96          return Proxy.newProxyInstance(_interface.getClassLoader(), new Class[]{_interface}, UnsupportedOperationInvocationHandler.INSTANCE);
97      }
98  
99      public Object create(Callback... callback)
100     {
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 {
107     public static UnsupportedOperationInvocationHandler INSTANCE = new UnsupportedOperationInvocationHandler();
108 
109     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
110     {
111         throw new UnsupportedOperationException();
112     }
113 }