1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package no.sesat.commons.ioc;
24
25 import java.lang.reflect.InvocationHandler;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.concurrent.locks.ReentrantReadWriteLock;
35 import org.apache.log4j.Logger;
36
37
38
39
40
41
42
43
44
45
46
47
48 final class BasicInvocationHandler implements InvocationHandler, java.io.Serializable {
49
50 private final transient Map<Method,Map<List<Class<?>>,Method>> methodCache
51 = new HashMap<Method,Map<List<Class<?>>,Method>>();
52 private final transient Map<Method,Map<List<Class<?>>,BaseContext>> contextCache
53 = new HashMap<Method,Map<List<Class<?>>,BaseContext>>();
54
55
56
57 private final transient ReentrantReadWriteLock cacheGate = new ReentrantReadWriteLock();
58
59
60
61 private final List<BaseContext> contexts;
62
63
64
65 private static final Invoker FAIR_DINKUM = new FairDinkumInvoker();
66 private static final Invoker CACTUS = new CactusInvoker();
67
68 private static final Logger LOG = Logger.getLogger(BasicInvocationHandler.class);
69
70 private static final String ERR_METHOD_NOT_IN_INTERFACE
71 = "Unable to proxy to the contexts associated to this BasicInvocationHandler for ";
72 private static final String ERR_METHOD_NOT_IN_EXACT_INTERFACE
73 = "No exact signature to the contexts associated to this BasicInvocationHandler for ";
74 private static final String DEBUG_LOOKING_FOR = " Looking for ";
75 private static final String DEBUG_LOOKING_IN = "Looking in ";
76 private static final String DEBUG_FOUND = "Found method while: ";
77 private static final String DEBUG_NOT_FOUND = "Did not found method while: ";
78 private static final String DEBUG_ADD_TO_CACHE = "Adding to cache ";
79 private static final String DEBUG_CACHE_USED_FOR = "Using cache for ";
80
81
82
83
84
85
86 BasicInvocationHandler(final BaseContext... cxts) {
87 this(Arrays.asList(cxts));
88
89 }
90
91
92
93
94 BasicInvocationHandler(final List<? extends BaseContext> cxts) {
95 contexts = Collections.unmodifiableList(cxts);
96
97 }
98
99
100
101
102
103
104
105
106 public Object invoke(
107 final Object object,
108 final Method method,
109 final Object[] objArr) throws Throwable {
110
111 boolean paramsNotNull = true;
112
113 final List<Class<?>> paramSignature = new ArrayList<Class<?>>();
114 if(objArr != null){
115 for (Object obj : objArr) {
116 paramSignature.add(obj == null ? null : obj.getClass());
117 paramsNotNull &= obj != null;
118 }
119 }
120
121
122
123 final Method cachedMethod = checkCache(method, paramSignature);
124 if(cachedMethod != null){
125 LOG.trace( DEBUG_CACHE_USED_FOR + method);
126 return FAIR_DINKUM.invoke(cachedMethod, getContextFromCache(method,paramSignature), objArr);
127 }
128
129
130 if(paramsNotNull){
131 try{
132 return invokeExactSignature(FAIR_DINKUM, object, method, paramSignature, objArr);
133
134 } catch (NoSuchMethodException ex) {
135
136 LOG.trace( ex);
137 }
138 }
139
140 return invokeSubclassedSignature(FAIR_DINKUM, object, method, paramSignature, objArr);
141
142 }
143
144
145
146 boolean assertContextContract(final Class<? extends BaseContext> cxtClass){
147
148 try{
149 for( Method m : cxtClass.getMethods() ){
150 invokeExactSignature(CACTUS, null, m, Arrays.asList(m.getParameterTypes()), null);
151 }
152 }catch(NoSuchMethodException nsme){
153 LOG.error(nsme.getMessage(), nsme);
154 return false;
155 }catch(IllegalAccessException iae){
156 LOG.error(iae.getMessage(), iae);
157 return false;
158 }catch(InvocationTargetException ite){
159 LOG.error(ite.getMessage(), ite);
160 return false;
161 }
162 return true;
163 }
164
165
166
167
168
169
170
171 private Object invokeExactSignature(
172 final Invoker invoker,
173 final Object object,
174 final Method method,
175 final List<Class<?>> paramSignature,
176 final Object[] objArr) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{
177
178 for (BaseContext cxt : contexts) {
179
180 final Class cls = cxt.getClass();
181 try {
182
183 final Method m = cls.getMethod(method.getName(), paramSignature.toArray(new Class[paramSignature.size()]));
184 if (m != null) {
185 if(LOG.isTraceEnabled()){
186 LOG.trace( DEBUG_FOUND
187 + DEBUG_LOOKING_IN + cls.getName()
188 + DEBUG_LOOKING_FOR + method.getName() + toString(paramSignature));
189 }
190
191 addToCache(method, paramSignature, m, cxt);
192 return invoker.invoke(m, cxt, objArr);
193
194 }
195 } catch (NoSuchMethodException ex) {
196 if(LOG.isTraceEnabled()){
197 LOG.trace( DEBUG_NOT_FOUND
198 + DEBUG_LOOKING_IN + cls.getName()
199 + DEBUG_LOOKING_FOR + method.getName() + toString(paramSignature));
200 }
201 }
202 }
203 throw new NoSuchMethodException(ERR_METHOD_NOT_IN_EXACT_INTERFACE + method.getName());
204 }
205
206
207
208 private Object invokeSubclassedSignature(
209 final Invoker invoker,
210 final Object object,
211 final Method method,
212 final List<Class<?>> paramSignature,
213 final Object[] objArr) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
214
215 for (BaseContext cxt : contexts) {
216
217 final Class cls = cxt.getClass();
218 for (Method m : cls.getMethods()) {
219
220 if (m.getName().equals(method.getName())) {
221
222 final Class[] cArr = m.getParameterTypes();
223 boolean assignableFrom = true;
224 for (int k = 0; assignableFrom && k < cArr.length; ++k) {
225 assignableFrom = paramSignature.get(k) == null || cArr[k].isAssignableFrom(paramSignature.get(k));
226 }
227 if (assignableFrom) {
228 if(LOG.isTraceEnabled()){
229 LOG.trace( DEBUG_FOUND
230 + DEBUG_LOOKING_IN + cls.getName()
231 + DEBUG_LOOKING_FOR + method.getName() + toString(paramSignature));
232 }
233
234 addToCache(method, paramSignature, m, cxt);
235 return invoker.invoke(m, cxt, objArr);
236
237 }else{
238 if(LOG.isTraceEnabled()){
239 LOG.trace( DEBUG_NOT_FOUND
240 + DEBUG_LOOKING_IN + cls.getName()
241 + DEBUG_LOOKING_FOR + method.getName() + toString(paramSignature));
242 }
243 }
244 }
245 }
246 }
247 final NoSuchMethodException e = new NoSuchMethodException(ERR_METHOD_NOT_IN_INTERFACE + method.getName());
248 LOG.error("",e);
249 throw e;
250 }
251
252
253
254
255
256 private Method checkCache(final Method method, final List<Class<?>> paramSignature){
257
258 try{
259 cacheGate.readLock().lock();
260
261 Method cachedMethod = null;
262 final Map<List<Class<?>>,Method> map = methodCache.get(method);
263 if(map != null){
264 cachedMethod = map.get(paramSignature);
265 }
266 return cachedMethod;
267
268 }finally{
269 cacheGate.readLock().unlock();
270 }
271 }
272
273
274
275
276 private BaseContext getContextFromCache(final Method method, final List<Class<?>> paramSignature){
277
278 try{
279 cacheGate.readLock().lock();
280
281 final Map<List<Class<?>>,BaseContext> map = contextCache.get(method);
282 assert map != null;
283
284 final BaseContext cachedContext = map.get(paramSignature);
285 assert cachedContext != null;
286
287 return cachedContext;
288
289 }finally{
290 cacheGate.readLock().unlock();
291 }
292 }
293
294
295
296 private void addToCache(
297 final Method methodFrom,
298 final List<Class<?>> paramSignature,
299 final Method methodTo,
300 final BaseContext contextTo){
301
302 LOG.trace( DEBUG_ADD_TO_CACHE + methodTo);
303
304 try{
305 cacheGate.writeLock().lock();
306
307 Map<List<Class<?>>,Method> methodMap = methodCache.get(methodFrom);
308 if(methodMap == null){
309 methodMap = new HashMap<List<Class<?>>,Method>();
310 methodCache.put(methodFrom, methodMap);
311 }
312 methodMap.put(paramSignature, methodTo);
313
314 Map<List<Class<?>>,BaseContext> contextMap = contextCache.get(methodFrom);
315 if(contextMap == null){
316 contextMap = new HashMap<List<Class<?>>,BaseContext>();
317 contextCache.put(methodFrom, contextMap);
318 }
319 contextMap.put(paramSignature, contextTo);
320
321 }finally{
322 cacheGate.writeLock().unlock();
323 }
324 }
325
326
327
328 private String toString(final List<Class<?>> paramSignature){
329
330 final StringBuilder sb = new StringBuilder();
331 for (Class cls : paramSignature) {
332 sb.append(cls == null ? "null" : cls.getSimpleName() + ", ");
333 }
334 return sb.toString();
335 }
336
337
338
339 private static interface Invoker{
340
341
342
343 Object invoke(final Method method, final BaseContext cxt, final Object[] objArr)
344 throws IllegalAccessException, InvocationTargetException;
345 }
346
347 private static class FairDinkumInvoker implements Invoker{
348 public Object invoke(
349 final Method method,
350 final BaseContext cxt,
351 final Object[] objArr)
352 throws IllegalAccessException, InvocationTargetException{
353
354 method.setAccessible(true);
355 return method.invoke(cxt, objArr);
356 }
357 }
358
359 private static class CactusInvoker implements Invoker{
360 public Object invoke(
361 final Method method,
362 final BaseContext cxt,
363 final Object[] objArr)
364 throws IllegalAccessException, InvocationTargetException{
365
366 return null;
367 }
368 }
369 }