1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.proxy2.javassist;
19
20 import java.lang.ref.WeakReference;
21 import java.lang.reflect.Method;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.WeakHashMap;
25
26 import javassist.CannotCompileException;
27 import javassist.CtClass;
28 import javassist.CtConstructor;
29 import javassist.CtMethod;
30
31 import org.apache.commons.lang3.ArrayUtils;
32 import org.apache.commons.lang3.ObjectUtils;
33 import org.apache.commons.proxy2.Invocation;
34 import org.apache.commons.proxy2.ProxyUtils;
35
36
37
38
39
40
41
42
43 public abstract class JavassistInvocation implements Invocation
44 {
45
46
47
48
49 private static WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>> loaderToClassCache
50 = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>();
51
52
53 private final Object proxy;
54
55
56 private final Object target;
57
58
59 private final Method method;
60
61
62 private final Object[] arguments;
63
64
65
66
67
68 private static String createCastExpression(Class<?> type, String objectToCast)
69 {
70 if (!type.isPrimitive())
71 {
72 return "( " + ProxyUtils.getJavaClassName(type) + " )" + objectToCast;
73 }
74 else
75 {
76 return "( ( " + ProxyUtils.getWrapperClass(type).getName() + " )" + objectToCast + " )." + type.getName()
77 + "Value()";
78 }
79 }
80
81 private static Class<?> createInvocationClass(ClassLoader classLoader, Method interfaceMethod)
82 throws CannotCompileException
83 {
84 final CtClass ctClass = JavassistUtils.createClass(getSimpleName(interfaceMethod.getDeclaringClass()) + "_"
85 + interfaceMethod.getName() + "_invocation", JavassistInvocation.class);
86 final CtConstructor constructor = new CtConstructor(JavassistUtils.resolve(new Class[] { Object.class,
87 Object.class, Method.class, Object[].class }), ctClass);
88 constructor.setBody("{\n\tsuper($$);\n}");
89 ctClass.addConstructor(constructor);
90 final CtMethod proceedMethod = new CtMethod(JavassistUtils.resolve(Object.class), "proceed",
91 JavassistUtils.resolve(new Class[0]), ctClass);
92 final Class<?>[] argumentTypes = interfaceMethod.getParameterTypes();
93 final StringBuilder proceedBody = new StringBuilder("{\n");
94 if (!Void.TYPE.equals(interfaceMethod.getReturnType()))
95 {
96 proceedBody.append("\treturn ");
97 if (interfaceMethod.getReturnType().isPrimitive())
98 {
99 proceedBody.append("new ");
100 proceedBody.append(ProxyUtils.getWrapperClass(interfaceMethod.getReturnType()).getName());
101 proceedBody.append("( ");
102 }
103 }
104 else
105 {
106 proceedBody.append("\t");
107 }
108 proceedBody.append("( (");
109 proceedBody.append(ProxyUtils.getJavaClassName(interfaceMethod.getDeclaringClass()));
110 proceedBody.append(" )getTarget() ).");
111 proceedBody.append(interfaceMethod.getName());
112 proceedBody.append("(");
113 for (int i = 0; i < argumentTypes.length; ++i)
114 {
115 final Class<?> argumentType = argumentTypes[i];
116 proceedBody.append(createCastExpression(argumentType, "getArguments()[" + i + "]"));
117 if (i != argumentTypes.length - 1)
118 {
119 proceedBody.append(", ");
120 }
121 }
122 if (!Void.TYPE.equals(interfaceMethod.getReturnType()) && interfaceMethod.getReturnType().isPrimitive())
123 {
124 proceedBody.append(") );\n");
125 }
126 else
127 {
128 proceedBody.append(");\n");
129 }
130 if (Void.TYPE.equals(interfaceMethod.getReturnType()))
131 {
132 proceedBody.append("\treturn null;\n");
133 }
134 proceedBody.append("}");
135 final String body = proceedBody.toString();
136 proceedMethod.setBody(body);
137 ctClass.addMethod(proceedMethod);
138
139 @SuppressWarnings("deprecation")
140 final Class<?> invocationClass = ctClass.toClass(classLoader);
141 return invocationClass;
142 }
143
144 private static Map<String, WeakReference<Class<?>>> getClassCache(ClassLoader classLoader)
145 {
146 Map<String, WeakReference<Class<?>>> cache = loaderToClassCache.get(classLoader);
147 if (cache == null)
148 {
149 cache = new HashMap<String, WeakReference<Class<?>>>();
150 loaderToClassCache.put(classLoader, cache);
151 }
152 return cache;
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166 static synchronized Class<?> getMethodInvocationClass(ClassLoader classLoader, Method interfaceMethod)
167 throws CannotCompileException
168 {
169 final Map<String, WeakReference<Class<?>>> classCache = getClassCache(classLoader);
170 final String key = toClassCacheKey(interfaceMethod);
171 final WeakReference<Class<?>> invocationClassRef = classCache.get(key);
172 Class<?> invocationClass;
173 if (invocationClassRef == null)
174 {
175 invocationClass = createInvocationClass(classLoader, interfaceMethod);
176 classCache.put(key, new WeakReference<Class<?>>(invocationClass));
177 }
178 else
179 {
180 synchronized (invocationClassRef)
181 {
182 invocationClass = invocationClassRef.get();
183 if (invocationClass == null)
184 {
185 invocationClass = createInvocationClass(classLoader, interfaceMethod);
186 classCache.put(key, new WeakReference<Class<?>>(invocationClass));
187 }
188 }
189 }
190 return invocationClass;
191 }
192
193 private static String getSimpleName(Class<?> c)
194 {
195 final String name = c.getName();
196 final int ndx = name.lastIndexOf('.');
197 return ndx == -1 ? name : name.substring(ndx + 1);
198 }
199
200 private static String toClassCacheKey(Method method)
201 {
202 return String.valueOf(method);
203 }
204
205
206
207
208
209 protected JavassistInvocation(Object proxy, Object target, Method method, Object[] arguments)
210 {
211 this.proxy = proxy;
212 this.target = target;
213 this.method = method;
214 this.arguments = ObjectUtils.defaultIfNull(ArrayUtils.clone(arguments), ProxyUtils.EMPTY_ARGUMENTS);
215 }
216
217
218
219
220 protected final Object getTarget()
221 {
222 return target;
223 }
224
225 @Override
226 public Object[] getArguments()
227 {
228 return arguments;
229 }
230
231 @Override
232 public Method getMethod()
233 {
234 return method;
235 }
236
237 @Override
238 public Object getProxy()
239 {
240 return proxy;
241 }
242 }