1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.jcs3.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.jcs3.jcache.cdi.skip-close");
57
58 private volatile CacheResolverFactoryImpl defaultCacheResolverFactory;
59 private final CacheKeyGeneratorImpl defaultCacheKeyGenerator = new CacheKeyGeneratorImpl();
60
61 private final Collection<CreationalContext<?>> toRelease = new ArrayList<>();
62 private final ConcurrentMap<MethodKey, MethodMeta> methods = new ConcurrentHashMap<>();
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 static 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<>();
125 for (final Annotation[] parameterAnnotation : parameterAnnotations)
126 {
127 final Set<Annotation> set = new HashSet<>(parameterAnnotation.length);
128 set.addAll(Arrays.asList(parameterAnnotation));
129 annotations.add(set);
130 }
131
132 final Set<Annotation> mtdAnnotations = new HashSet<>(Arrays.asList(ic.getMethod().getAnnotations()));
133 final CacheResult cacheResult = ic.getMethod().getAnnotation(CacheResult.class);
134 final String cacheResultCacheResultName = cacheResult == null ? null : defaultName(ic.getMethod(), defaults, cacheResult.cacheName());
135 final CacheResolverFactory cacheResultCacheResolverFactory = cacheResult == null ?
136 null : cacheResolverFactoryFor(defaults, cacheResult.cacheResolverFactory());
137 final CacheKeyGenerator cacheResultCacheKeyGenerator = cacheResult == null ?
138 null : cacheKeyGeneratorFor(defaults, cacheResult.cacheKeyGenerator());
139
140 final CachePut cachePut = ic.getMethod().getAnnotation(CachePut.class);
141 final String cachePutCachePutName = cachePut == null ? null : defaultName(ic.getMethod(), defaults, cachePut.cacheName());
142 final CacheResolverFactory cachePutCacheResolverFactory = cachePut == null ?
143 null : cacheResolverFactoryFor(defaults, cachePut.cacheResolverFactory());
144 final CacheKeyGenerator cachePutCacheKeyGenerator = cachePut == null ?
145 null : cacheKeyGeneratorFor(defaults, cachePut.cacheKeyGenerator());
146
147 final CacheRemove cacheRemove = ic.getMethod().getAnnotation(CacheRemove.class);
148 final String cacheRemoveCacheRemoveName = cacheRemove == null ? null : defaultName(ic.getMethod(), defaults, cacheRemove.cacheName());
149 final CacheResolverFactory cacheRemoveCacheResolverFactory = cacheRemove == null ?
150 null : cacheResolverFactoryFor(defaults, cacheRemove.cacheResolverFactory());
151 final CacheKeyGenerator cacheRemoveCacheKeyGenerator = cacheRemove == null ?
152 null : cacheKeyGeneratorFor(defaults, cacheRemove.cacheKeyGenerator());
153
154 final CacheRemoveAll cacheRemoveAll = ic.getMethod().getAnnotation(CacheRemoveAll.class);
155 final String cacheRemoveAllCacheRemoveAllName = cacheRemoveAll == null ? null : defaultName(ic.getMethod(), defaults, cacheRemoveAll.cacheName());
156 final CacheResolverFactory cacheRemoveAllCacheResolverFactory = cacheRemoveAll == null ?
157 null : cacheResolverFactoryFor(defaults, cacheRemoveAll.cacheResolverFactory());
158
159 return new MethodMeta(
160 parameterTypes,
161 annotations,
162 mtdAnnotations,
163 keyParameterIndexes(ic.getMethod()),
164 getValueParameter(annotations),
165 getKeyParameters(annotations),
166 cacheResultCacheResultName,
167 cacheResultCacheResolverFactory,
168 cacheResultCacheKeyGenerator,
169 cacheResult,
170 cachePutCachePutName,
171 cachePutCacheResolverFactory,
172 cachePutCacheKeyGenerator,
173 cachePut != null && cachePut.afterInvocation(),
174 cachePut,
175 cacheRemoveCacheRemoveName,
176 cacheRemoveCacheResolverFactory,
177 cacheRemoveCacheKeyGenerator,
178 cacheRemove != null && cacheRemove.afterInvocation(),
179 cacheRemove,
180 cacheRemoveAllCacheRemoveAllName,
181 cacheRemoveAllCacheResolverFactory,
182 cacheRemoveAll != null && cacheRemoveAll.afterInvocation(),
183 cacheRemoveAll);
184 }
185
186 private static Integer[] getKeyParameters(final List<Set<Annotation>> annotations)
187 {
188 final Collection<Integer> list = new ArrayList<>();
189 int idx = 0;
190 for (final Set<Annotation> set : annotations)
191 {
192 for (final Annotation a : set)
193 {
194 if (a.annotationType() == CacheKey.class)
195 {
196 list.add(idx);
197 }
198 }
199 idx++;
200 }
201 if (list.isEmpty())
202 {
203 for (int i = 0; i < annotations.size(); i++)
204 {
205 list.add(i);
206 }
207 }
208 return list.toArray(new Integer[0]);
209 }
210
211 private static Integer getValueParameter(final List<Set<Annotation>> annotations)
212 {
213 final int idx = 0;
214 for (final Set<Annotation> set : annotations)
215 {
216 for (final Annotation a : set)
217 {
218 if (a.annotationType() == CacheValue.class)
219 {
220 return idx;
221 }
222 }
223 }
224 return -1;
225 }
226
227 private static String defaultName(final Method method, final CacheDefaults defaults, final String cacheName)
228 {
229 if (!cacheName.isEmpty())
230 {
231 return cacheName;
232 }
233 if (defaults != null)
234 {
235 final String name = defaults.cacheName();
236 if (!name.isEmpty())
237 {
238 return name;
239 }
240 }
241
242 final StringBuilder name = new StringBuilder(method.getDeclaringClass().getName());
243 name.append(".");
244 name.append(method.getName());
245 name.append("(");
246 final Class<?>[] parameterTypes = method.getParameterTypes();
247 for (int pIdx = 0; pIdx < parameterTypes.length; pIdx++)
248 {
249 name.append(parameterTypes[pIdx].getName());
250 if ((pIdx + 1) < parameterTypes.length)
251 {
252 name.append(",");
253 }
254 }
255 name.append(")");
256 return name.toString();
257 }
258
259 private static CacheDefaults findDefaults(final Class<?> targetType, final Method method)
260 {
261 if (Proxy.isProxyClass(targetType))
262 {
263 final Class<?> api = method.getDeclaringClass();
264 for (final Class<?> type : targetType
265 .getInterfaces())
266 {
267 if (!api.isAssignableFrom(type))
268 {
269 continue;
270 }
271 return extractDefaults(type);
272 }
273 }
274 return extractDefaults(targetType);
275 }
276
277 private static CacheDefaults extractDefaults(final Class<?> type)
278 {
279 CacheDefaults annotation = null;
280 Class<?> clazz = type;
281 while (clazz != null && clazz != Object.class)
282 {
283 annotation = clazz.getAnnotation(CacheDefaults.class);
284 if (annotation != null)
285 {
286 break;
287 }
288 clazz = clazz.getSuperclass();
289 }
290 return annotation;
291 }
292
293 public boolean isIncluded(final Class<?> aClass, final Class<?>[] in, final Class<?>[] out)
294 {
295 if (in.length == 0 && out.length == 0)
296 {
297 return false;
298 }
299 for (final Class<?> potentialIn : in)
300 {
301 if (potentialIn.isAssignableFrom(aClass))
302 {
303 for (final Class<?> potentialOut : out)
304 {
305 if (potentialOut.isAssignableFrom(aClass))
306 {
307 return false;
308 }
309 }
310 return true;
311 }
312 }
313 return false;
314 }
315
316 private CacheKeyGenerator cacheKeyGeneratorFor(final CacheDefaults defaults, final Class<? extends CacheKeyGenerator> cacheKeyGenerator)
317 {
318 if (!CacheKeyGenerator.class.equals(cacheKeyGenerator))
319 {
320 return instance(cacheKeyGenerator);
321 }
322 if (defaults != null)
323 {
324 final Class<? extends CacheKeyGenerator> defaultCacheKeyGenerator = defaults.cacheKeyGenerator();
325 if (!CacheKeyGenerator.class.equals(defaultCacheKeyGenerator))
326 {
327 return instance(defaultCacheKeyGenerator);
328 }
329 }
330 return defaultCacheKeyGenerator;
331 }
332
333 private CacheResolverFactory cacheResolverFactoryFor(final CacheDefaults defaults, final Class<? extends CacheResolverFactory> cacheResolverFactory)
334 {
335 if (!CacheResolverFactory.class.equals(cacheResolverFactory))
336 {
337 return instance(cacheResolverFactory);
338 }
339 if (defaults != null)
340 {
341 final Class<? extends CacheResolverFactory> defaultCacheResolverFactory = defaults.cacheResolverFactory();
342 if (!CacheResolverFactory.class.equals(defaultCacheResolverFactory))
343 {
344 return instance(defaultCacheResolverFactory);
345 }
346 }
347 return defaultCacheResolverFactory();
348 }
349
350 @SuppressWarnings("unchecked")
351 private <T> T instance(final Class<T> type)
352 {
353 final Set<Bean<?>> beans = beanManager.getBeans(type);
354 if (beans.isEmpty())
355 {
356 if (CacheKeyGenerator.class == type) {
357 return (T) defaultCacheKeyGenerator;
358 }
359 if (CacheResolverFactory.class == type) {
360 return (T) defaultCacheResolverFactory();
361 }
362 return null;
363 }
364 final Bean<?> bean = beanManager.resolve(beans);
365 final CreationalContext<?> context = beanManager.createCreationalContext(bean);
366 final Class<? extends Annotation> scope = bean.getScope();
367 final boolean normalScope = beanManager.isNormalScope(scope);
368 try
369 {
370 final Object reference = beanManager.getReference(bean, bean.getBeanClass(), context);
371 if (!normalScope)
372 {
373 toRelease.add(context);
374 }
375 return (T) reference;
376 }
377 finally
378 {
379 if (normalScope)
380 {
381 context.release();
382 }
383 }
384 }
385
386 private CacheResolverFactoryImpl defaultCacheResolverFactory()
387 {
388 if (defaultCacheResolverFactory != null) {
389 return defaultCacheResolverFactory;
390 }
391 synchronized (this) {
392 if (defaultCacheResolverFactory != null) {
393 return defaultCacheResolverFactory;
394 }
395 defaultCacheResolverFactory = new CacheResolverFactoryImpl();
396 }
397 return defaultCacheResolverFactory;
398 }
399
400 private static Integer[] keyParameterIndexes(final Method method)
401 {
402 final List<Integer> keys = new LinkedList<>();
403 final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
404
405
406 for (int i = 0; i < method.getParameterTypes().length; i++)
407 {
408 final Annotation[] annotations = parameterAnnotations[i];
409 for (final Annotation a : annotations)
410 {
411 if (a.annotationType().equals(CacheKey.class))
412 {
413 keys.add(i);
414 break;
415 }
416 }
417 }
418
419
420 if (keys.isEmpty())
421 {
422 for (int i = 0; i < method.getParameterTypes().length; i++)
423 {
424 final Annotation[] annotations = parameterAnnotations[i];
425 boolean value = false;
426 for (final Annotation a : annotations)
427 {
428 if (a.annotationType().equals(CacheValue.class))
429 {
430 value = true;
431 break;
432 }
433 }
434 if (!value) {
435 keys.add(i);
436 }
437 }
438 }
439 return keys.toArray(new Integer[0]);
440 }
441
442 private static final class MethodKey
443 {
444 private final Class<?> base;
445 private final Method delegate;
446 private final int hash;
447
448 private MethodKey(final Class<?> base, final Method delegate)
449 {
450 this.base = base;
451 this.delegate = delegate;
452 this.hash = 31 * delegate.hashCode() + (base == null ? 0 : base.hashCode());
453 }
454
455 @Override
456 public boolean equals(final Object o)
457 {
458 if (this == o)
459 {
460 return true;
461 }
462 if (o == null || getClass() != o.getClass())
463 {
464 return false;
465 }
466 final MethodKey classKey = MethodKey.class.cast(o);
467 return delegate.equals(classKey.delegate) &&
468 (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(final Class<?>[] parameterTypes, final List<Set<Annotation>> parameterAnnotations, final Set<Annotation>
511 annotations, final Integer[] keysIndices, final Integer valueIndex, final Integer[] parameterIndices, final String
512 cacheResultCacheName, final CacheResolverFactory cacheResultResolverFactory, final CacheKeyGenerator
513 cacheResultKeyGenerator, final CacheResult cacheResult, final String cachePutCacheName, final CacheResolverFactory
514 cachePutResolverFactory, final CacheKeyGenerator cachePutKeyGenerator, final boolean cachePutAfter, final CachePut cachePut, final String
515 cacheRemoveCacheName, final CacheResolverFactory cacheRemoveResolverFactory, final CacheKeyGenerator
516 cacheRemoveKeyGenerator, final boolean cacheRemoveAfter, final CacheRemove cacheRemove, final String cacheRemoveAllCacheName,
517 final CacheResolverFactory cacheRemoveAllResolverFactory, final boolean
518 cacheRemoveAllAfter, final 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 }