1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.jcs.jcache.cdi;
20
21 import java.lang.annotation.Annotation;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.HashSet;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Set;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.ConcurrentMap;
33 import java.util.logging.Logger;
34
35 import javax.annotation.PreDestroy;
36 import javax.cache.annotation.CacheDefaults;
37 import javax.cache.annotation.CacheKey;
38 import javax.cache.annotation.CacheKeyGenerator;
39 import javax.cache.annotation.CachePut;
40 import javax.cache.annotation.CacheRemove;
41 import javax.cache.annotation.CacheRemoveAll;
42 import javax.cache.annotation.CacheResolverFactory;
43 import javax.cache.annotation.CacheResult;
44 import javax.cache.annotation.CacheValue;
45 import javax.enterprise.context.ApplicationScoped;
46 import javax.enterprise.context.spi.CreationalContext;
47 import javax.enterprise.inject.spi.Bean;
48 import javax.enterprise.inject.spi.BeanManager;
49 import javax.inject.Inject;
50 import javax.interceptor.InvocationContext;
51
52 @ApplicationScoped
53 public class CDIJCacheHelper
54 {
55 private static final Logger LOGGER = Logger.getLogger(CDIJCacheHelper.class.getName());
56 private static final boolean CLOSE_CACHE = !Boolean.getBoolean("org.apache.commons.jcs.jcache.cdi.skip-close");
57
58 private volatile CacheResolverFactoryImpl defaultCacheResolverFactory = null;
59 private final CacheKeyGeneratorImpl defaultCacheKeyGenerator = new CacheKeyGeneratorImpl();
60
61 private final Collection<CreationalContext<?>> toRelease = new ArrayList<CreationalContext<?>>();
62 private final ConcurrentMap<MethodKey, MethodMeta> methods = new ConcurrentHashMap<MethodKey, MethodMeta>();
63
64 @Inject
65 private BeanManager beanManager;
66
67 @PreDestroy
68 private void release() {
69 if (CLOSE_CACHE && defaultCacheResolverFactory != null)
70 {
71 defaultCacheResolverFactory.release();
72 }
73 for (final CreationalContext<?> cc : toRelease)
74 {
75 try
76 {
77 cc.release();
78 }
79 catch (final RuntimeException re)
80 {
81 LOGGER.warning(re.getMessage());
82 }
83 }
84 }
85
86 public MethodMeta findMeta(final InvocationContext ic)
87 {
88 final Method mtd = ic.getMethod();
89 final Class<?> refType = findKeyType(ic.getTarget());
90 final MethodKey key = new MethodKey(refType, mtd);
91 MethodMeta methodMeta = methods.get(key);
92 if (methodMeta == null)
93 {
94 synchronized (this)
95 {
96 methodMeta = methods.get(key);
97 if (methodMeta == null)
98 {
99 methodMeta = createMeta(ic);
100 methods.put(key, methodMeta);
101 }
102 }
103 }
104 return methodMeta;
105 }
106
107 private Class<?> findKeyType(final Object target)
108 {
109 if (null == target)
110 {
111 return null;
112 }
113 return target.getClass();
114 }
115
116
117 private MethodMeta createMeta(final InvocationContext ic)
118 {
119 final CacheDefaults defaults = findDefaults(ic.getTarget() == null ? null : ic.getTarget()
120 .getClass(), ic.getMethod());
121
122 final Class<?>[] parameterTypes = ic.getMethod().getParameterTypes();
123 final Annotation[][] parameterAnnotations = ic.getMethod().getParameterAnnotations();
124 final List<Set<Annotation>> annotations = new ArrayList<Set<Annotation>>();
125 for (final Annotation[] parameterAnnotation : parameterAnnotations)
126 {
127 final Set<Annotation> set = new HashSet<Annotation>(parameterAnnotation.length);
128 set.addAll(Arrays.asList(parameterAnnotation));
129 annotations.add(set);
130 }
131
132 final Set<Annotation> mtdAnnotations = new HashSet<Annotation>();
133 mtdAnnotations.addAll(Arrays.asList(ic.getMethod().getAnnotations()));
134
135 final CacheResult cacheResult = ic.getMethod().getAnnotation(CacheResult.class);
136 final String cacheResultCacheResultName = cacheResult == null ? null : defaultName(ic.getMethod(), defaults, cacheResult.cacheName());
137 final CacheResolverFactory cacheResultCacheResolverFactory = cacheResult == null ?
138 null : cacheResolverFactoryFor(defaults, cacheResult.cacheResolverFactory());
139 final CacheKeyGenerator cacheResultCacheKeyGenerator = cacheResult == null ?
140 null : cacheKeyGeneratorFor(defaults, cacheResult.cacheKeyGenerator());
141
142 final CachePut cachePut = ic.getMethod().getAnnotation(CachePut.class);
143 final String cachePutCachePutName = cachePut == null ? null : defaultName(ic.getMethod(), defaults, cachePut.cacheName());
144 final CacheResolverFactory cachePutCacheResolverFactory = cachePut == null ?
145 null : cacheResolverFactoryFor(defaults, cachePut.cacheResolverFactory());
146 final CacheKeyGenerator cachePutCacheKeyGenerator = cachePut == null ?
147 null : cacheKeyGeneratorFor(defaults, cachePut.cacheKeyGenerator());
148
149 final CacheRemove cacheRemove = ic.getMethod().getAnnotation(CacheRemove.class);
150 final String cacheRemoveCacheRemoveName = cacheRemove == null ? null : defaultName(ic.getMethod(), defaults, cacheRemove.cacheName());
151 final CacheResolverFactory cacheRemoveCacheResolverFactory = cacheRemove == null ?
152 null : cacheResolverFactoryFor(defaults, cacheRemove.cacheResolverFactory());
153 final CacheKeyGenerator cacheRemoveCacheKeyGenerator = cacheRemove == null ?
154 null : cacheKeyGeneratorFor(defaults, cacheRemove.cacheKeyGenerator());
155
156 final CacheRemoveAll cacheRemoveAll = ic.getMethod().getAnnotation(CacheRemoveAll.class);
157 final String cacheRemoveAllCacheRemoveAllName = cacheRemoveAll == null ? null : defaultName(ic.getMethod(), defaults, cacheRemoveAll.cacheName());
158 final CacheResolverFactory cacheRemoveAllCacheResolverFactory = cacheRemoveAll == null ?
159 null : cacheResolverFactoryFor(defaults, cacheRemoveAll.cacheResolverFactory());
160
161 return new MethodMeta(
162 parameterTypes,
163 annotations,
164 mtdAnnotations,
165 keyParameterIndexes(ic.getMethod()),
166 getValueParameter(annotations),
167 getKeyParameters(annotations),
168 cacheResultCacheResultName,
169 cacheResultCacheResolverFactory,
170 cacheResultCacheKeyGenerator,
171 cacheResult,
172 cachePutCachePutName,
173 cachePutCacheResolverFactory,
174 cachePutCacheKeyGenerator,
175 cachePut != null && cachePut.afterInvocation(),
176 cachePut,
177 cacheRemoveCacheRemoveName,
178 cacheRemoveCacheResolverFactory,
179 cacheRemoveCacheKeyGenerator,
180 cacheRemove != null && cacheRemove.afterInvocation(),
181 cacheRemove,
182 cacheRemoveAllCacheRemoveAllName,
183 cacheRemoveAllCacheResolverFactory,
184 cacheRemoveAll != null && cacheRemoveAll.afterInvocation(),
185 cacheRemoveAll);
186 }
187
188 private Integer[] getKeyParameters(final List<Set<Annotation>> annotations)
189 {
190 final Collection<Integer> list = new ArrayList<Integer>();
191 int idx = 0;
192 for (final Set<Annotation> set : annotations)
193 {
194 for (final Annotation a : set)
195 {
196 if (a.annotationType() == CacheKey.class)
197 {
198 list.add(idx);
199 }
200 }
201 idx++;
202 }
203 if (list.isEmpty())
204 {
205 for (int i = 0; i < annotations.size(); i++)
206 {
207 list.add(i);
208 }
209 }
210 return list.toArray(new Integer[list.size()]);
211 }
212
213 private Integer getValueParameter(final List<Set<Annotation>> annotations)
214 {
215 int idx = 0;
216 for (final Set<Annotation> set : annotations)
217 {
218 for (final Annotation a : set)
219 {
220 if (a.annotationType() == CacheValue.class)
221 {
222 return idx;
223 }
224 }
225 }
226 return -1;
227 }
228
229 private String defaultName(final Method method, final CacheDefaults defaults, final String cacheName)
230 {
231 if (!cacheName.isEmpty())
232 {
233 return cacheName;
234 }
235 if (defaults != null)
236 {
237 final String name = defaults.cacheName();
238 if (!name.isEmpty())
239 {
240 return name;
241 }
242 }
243
244 final StringBuilder name = new StringBuilder(method.getDeclaringClass().getName());
245 name.append(".");
246 name.append(method.getName());
247 name.append("(");
248 final Class<?>[] parameterTypes = method.getParameterTypes();
249 for (int pIdx = 0; pIdx < parameterTypes.length; pIdx++)
250 {
251 name.append(parameterTypes[pIdx].getName());
252 if ((pIdx + 1) < parameterTypes.length)
253 {
254 name.append(",");
255 }
256 }
257 name.append(")");
258 return name.toString();
259 }
260
261 private CacheDefaults findDefaults(final Class<?> targetType, final Method method)
262 {
263 if (Proxy.isProxyClass(targetType))
264 {
265 final Class<?> api = method.getDeclaringClass();
266 for (final Class<?> type : targetType
267 .getInterfaces())
268 {
269 if (!api.isAssignableFrom(type))
270 {
271 continue;
272 }
273 return extractDefaults(type);
274 }
275 }
276 return extractDefaults(targetType);
277 }
278
279 private CacheDefaults extractDefaults(final Class<?> type)
280 {
281 CacheDefaults annotation = null;
282 Class<?> clazz = type;
283 while (clazz != null && clazz != Object.class)
284 {
285 annotation = clazz.getAnnotation(CacheDefaults.class);
286 if (annotation != null)
287 {
288 break;
289 }
290 clazz = clazz.getSuperclass();
291 }
292 return annotation;
293 }
294
295 public boolean isIncluded(final Class<?> aClass, final Class<?>[] in, final Class<?>[] out)
296 {
297 if (in.length == 0 && out.length == 0)
298 {
299 return false;
300 }
301 for (final Class<?> potentialIn : in)
302 {
303 if (potentialIn.isAssignableFrom(aClass))
304 {
305 for (final Class<?> potentialOut : out)
306 {
307 if (potentialOut.isAssignableFrom(aClass))
308 {
309 return false;
310 }
311 }
312 return true;
313 }
314 }
315 return false;
316 }
317
318 private CacheKeyGenerator cacheKeyGeneratorFor(final CacheDefaults defaults, final Class<? extends CacheKeyGenerator> cacheKeyGenerator)
319 {
320 if (!CacheKeyGenerator.class.equals(cacheKeyGenerator))
321 {
322 return instance(cacheKeyGenerator);
323 }
324 if (defaults != null)
325 {
326 final Class<? extends CacheKeyGenerator> defaultCacheKeyGenerator = defaults.cacheKeyGenerator();
327 if (!CacheKeyGenerator.class.equals(defaultCacheKeyGenerator))
328 {
329 return instance(defaultCacheKeyGenerator);
330 }
331 }
332 return defaultCacheKeyGenerator;
333 }
334
335 private CacheResolverFactory cacheResolverFactoryFor(final CacheDefaults defaults, final Class<? extends CacheResolverFactory> cacheResolverFactory)
336 {
337 if (!CacheResolverFactory.class.equals(cacheResolverFactory))
338 {
339 return instance(cacheResolverFactory);
340 }
341 if (defaults != null)
342 {
343 final Class<? extends CacheResolverFactory> defaultCacheResolverFactory = defaults.cacheResolverFactory();
344 if (!CacheResolverFactory.class.equals(defaultCacheResolverFactory))
345 {
346 return instance(defaultCacheResolverFactory);
347 }
348 }
349 return defaultCacheResolverFactory();
350 }
351
352 private <T> T instance(final Class<T> type)
353 {
354 final Set<Bean<?>> beans = beanManager.getBeans(type);
355 if (beans.isEmpty())
356 {
357 if (CacheKeyGenerator.class == type) {
358 return (T) defaultCacheKeyGenerator;
359 }
360 if (CacheResolverFactory.class == type) {
361 return (T) defaultCacheResolverFactory();
362 }
363 return null;
364 }
365 final Bean<?> bean = beanManager.resolve(beans);
366 final CreationalContext<?> context = beanManager.createCreationalContext(bean);
367 final Class<? extends Annotation> scope = bean.getScope();
368 final boolean normalScope = beanManager.isNormalScope(scope);
369 try
370 {
371 final Object reference = beanManager.getReference(bean, bean.getBeanClass(), context);
372 if (!normalScope)
373 {
374 toRelease.add(context);
375 }
376 return (T) reference;
377 }
378 finally
379 {
380 if (normalScope)
381 {
382 context.release();
383 }
384 }
385 }
386
387 private CacheResolverFactoryImpl defaultCacheResolverFactory()
388 {
389 if (defaultCacheResolverFactory != null) {
390 return defaultCacheResolverFactory;
391 }
392 synchronized (this) {
393 if (defaultCacheResolverFactory != null) {
394 return defaultCacheResolverFactory;
395 }
396 defaultCacheResolverFactory = new CacheResolverFactoryImpl();
397 }
398 return defaultCacheResolverFactory;
399 }
400
401 private Integer[] keyParameterIndexes(final Method method)
402 {
403 final List<Integer> keys = new LinkedList<Integer>();
404 final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
405
406
407 for (int i = 0; i < method.getParameterTypes().length; i++)
408 {
409 final Annotation[] annotations = parameterAnnotations[i];
410 for (final Annotation a : annotations)
411 {
412 if (a.annotationType().equals(CacheKey.class))
413 {
414 keys.add(i);
415 break;
416 }
417 }
418 }
419
420
421 if (keys.isEmpty())
422 {
423 for (int i = 0; i < method.getParameterTypes().length; i++)
424 {
425 final Annotation[] annotations = parameterAnnotations[i];
426 boolean value = false;
427 for (final Annotation a : annotations)
428 {
429 if (a.annotationType().equals(CacheValue.class))
430 {
431 value = true;
432 break;
433 }
434 }
435 if (!value) {
436 keys.add(i);
437 }
438 }
439 }
440 return keys.toArray(new Integer[keys.size()]);
441 }
442
443 private static final class MethodKey
444 {
445 private final Class<?> base;
446 private final Method delegate;
447 private final int hash;
448
449 private MethodKey(final Class<?> base, final Method delegate)
450 {
451 this.base = base;
452 this.delegate = delegate;
453 this.hash = 31 * delegate.hashCode() + (base == null ? 0 : base.hashCode());
454 }
455
456 @Override
457 public boolean equals(final Object o)
458 {
459 if (this == o)
460 {
461 return true;
462 }
463 if (o == null || getClass() != o.getClass())
464 {
465 return false;
466 }
467 final MethodKey classKey = MethodKey.class.cast(o);
468 return delegate.equals(classKey.delegate) && ((base == null && classKey.base == null) || (base != null && base.equals(classKey.base)));
469 }
470
471 @Override
472 public int hashCode()
473 {
474 return hash;
475 }
476 }
477
478
479 public static class MethodMeta
480 {
481 private final Class<?>[] parameterTypes;
482 private final List<Set<Annotation>> parameterAnnotations;
483 private final Set<Annotation> annotations;
484 private final Integer[] keysIndices;
485 private final Integer valueIndex;
486 private final Integer[] parameterIndices;
487
488 private final String cacheResultCacheName;
489 private final CacheResolverFactory cacheResultResolverFactory;
490 private final CacheKeyGenerator cacheResultKeyGenerator;
491 private final CacheResult cacheResult;
492
493 private final String cachePutCacheName;
494 private final CacheResolverFactory cachePutResolverFactory;
495 private final CacheKeyGenerator cachePutKeyGenerator;
496 private final boolean cachePutAfter;
497 private final CachePut cachePut;
498
499 private final String cacheRemoveCacheName;
500 private final CacheResolverFactory cacheRemoveResolverFactory;
501 private final CacheKeyGenerator cacheRemoveKeyGenerator;
502 private final boolean cacheRemoveAfter;
503 private final CacheRemove cacheRemove;
504
505 private final String cacheRemoveAllCacheName;
506 private final CacheResolverFactory cacheRemoveAllResolverFactory;
507 private final boolean cacheRemoveAllAfter;
508 private final CacheRemoveAll cacheRemoveAll;
509
510 public MethodMeta(Class<?>[] parameterTypes, List<Set<Annotation>> parameterAnnotations, Set<Annotation>
511 annotations, Integer[] keysIndices, Integer valueIndex, Integer[] parameterIndices, String
512 cacheResultCacheName, CacheResolverFactory cacheResultResolverFactory, CacheKeyGenerator
513 cacheResultKeyGenerator, CacheResult cacheResult, String cachePutCacheName, CacheResolverFactory
514 cachePutResolverFactory, CacheKeyGenerator cachePutKeyGenerator, boolean cachePutAfter, CachePut cachePut, String
515 cacheRemoveCacheName, CacheResolverFactory cacheRemoveResolverFactory, CacheKeyGenerator
516 cacheRemoveKeyGenerator, boolean cacheRemoveAfter, CacheRemove cacheRemove, String cacheRemoveAllCacheName,
517 CacheResolverFactory cacheRemoveAllResolverFactory, boolean
518 cacheRemoveAllAfter, CacheRemoveAll cacheRemoveAll)
519 {
520 this.parameterTypes = parameterTypes;
521 this.parameterAnnotations = parameterAnnotations;
522 this.annotations = annotations;
523 this.keysIndices = keysIndices;
524 this.valueIndex = valueIndex;
525 this.parameterIndices = parameterIndices;
526 this.cacheResultCacheName = cacheResultCacheName;
527 this.cacheResultResolverFactory = cacheResultResolverFactory;
528 this.cacheResultKeyGenerator = cacheResultKeyGenerator;
529 this.cacheResult = cacheResult;
530 this.cachePutCacheName = cachePutCacheName;
531 this.cachePutResolverFactory = cachePutResolverFactory;
532 this.cachePutKeyGenerator = cachePutKeyGenerator;
533 this.cachePutAfter = cachePutAfter;
534 this.cachePut = cachePut;
535 this.cacheRemoveCacheName = cacheRemoveCacheName;
536 this.cacheRemoveResolverFactory = cacheRemoveResolverFactory;
537 this.cacheRemoveKeyGenerator = cacheRemoveKeyGenerator;
538 this.cacheRemoveAfter = cacheRemoveAfter;
539 this.cacheRemove = cacheRemove;
540 this.cacheRemoveAllCacheName = cacheRemoveAllCacheName;
541 this.cacheRemoveAllResolverFactory = cacheRemoveAllResolverFactory;
542 this.cacheRemoveAllAfter = cacheRemoveAllAfter;
543 this.cacheRemoveAll = cacheRemoveAll;
544 }
545
546 public boolean isCacheRemoveAfter()
547 {
548 return cacheRemoveAfter;
549 }
550
551 public boolean isCachePutAfter()
552 {
553 return cachePutAfter;
554 }
555
556 public Class<?>[] getParameterTypes()
557 {
558 return parameterTypes;
559 }
560
561 public List<Set<Annotation>> getParameterAnnotations()
562 {
563 return parameterAnnotations;
564 }
565
566 public String getCacheResultCacheName()
567 {
568 return cacheResultCacheName;
569 }
570
571 public CacheResolverFactory getCacheResultResolverFactory()
572 {
573 return cacheResultResolverFactory;
574 }
575
576 public CacheKeyGenerator getCacheResultKeyGenerator()
577 {
578 return cacheResultKeyGenerator;
579 }
580
581 public CacheResult getCacheResult() {
582 return cacheResult;
583 }
584
585 public Integer[] getParameterIndices()
586 {
587 return parameterIndices;
588 }
589
590 public Set<Annotation> getAnnotations()
591 {
592 return annotations;
593 }
594
595 public Integer[] getKeysIndices()
596 {
597 return keysIndices;
598 }
599
600 public Integer getValuesIndex()
601 {
602 return valueIndex;
603 }
604
605 public Integer getValueIndex()
606 {
607 return valueIndex;
608 }
609
610 public String getCachePutCacheName()
611 {
612 return cachePutCacheName;
613 }
614
615 public CacheResolverFactory getCachePutResolverFactory()
616 {
617 return cachePutResolverFactory;
618 }
619
620 public CacheKeyGenerator getCachePutKeyGenerator()
621 {
622 return cachePutKeyGenerator;
623 }
624
625 public CachePut getCachePut()
626 {
627 return cachePut;
628 }
629
630 public String getCacheRemoveCacheName()
631 {
632 return cacheRemoveCacheName;
633 }
634
635 public CacheResolverFactory getCacheRemoveResolverFactory()
636 {
637 return cacheRemoveResolverFactory;
638 }
639
640 public CacheKeyGenerator getCacheRemoveKeyGenerator()
641 {
642 return cacheRemoveKeyGenerator;
643 }
644
645 public CacheRemove getCacheRemove()
646 {
647 return cacheRemove;
648 }
649
650 public String getCacheRemoveAllCacheName()
651 {
652 return cacheRemoveAllCacheName;
653 }
654
655 public CacheResolverFactory getCacheRemoveAllResolverFactory()
656 {
657 return cacheRemoveAllResolverFactory;
658 }
659
660 public boolean isCacheRemoveAllAfter()
661 {
662 return cacheRemoveAllAfter;
663 }
664
665 public CacheRemoveAll getCacheRemoveAll()
666 {
667 return cacheRemoveAll;
668 }
669 }
670 }