001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.jcs3.jcache.cdi; 020 021import java.lang.annotation.Annotation; 022import java.lang.reflect.Method; 023import java.lang.reflect.Proxy; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collection; 027import java.util.HashSet; 028import java.util.LinkedList; 029import java.util.List; 030import java.util.Set; 031import java.util.concurrent.ConcurrentHashMap; 032import java.util.concurrent.ConcurrentMap; 033import java.util.logging.Logger; 034 035import javax.annotation.PreDestroy; 036import javax.cache.annotation.CacheDefaults; 037import javax.cache.annotation.CacheKey; 038import javax.cache.annotation.CacheKeyGenerator; 039import javax.cache.annotation.CachePut; 040import javax.cache.annotation.CacheRemove; 041import javax.cache.annotation.CacheRemoveAll; 042import javax.cache.annotation.CacheResolverFactory; 043import javax.cache.annotation.CacheResult; 044import javax.cache.annotation.CacheValue; 045import javax.enterprise.context.ApplicationScoped; 046import javax.enterprise.context.spi.CreationalContext; 047import javax.enterprise.inject.spi.Bean; 048import javax.enterprise.inject.spi.BeanManager; 049import javax.inject.Inject; 050import javax.interceptor.InvocationContext; 051 052@ApplicationScoped 053public class CDIJCacheHelper 054{ 055 private static final Logger LOGGER = Logger.getLogger(CDIJCacheHelper.class.getName()); 056 private static final boolean CLOSE_CACHE = !Boolean.getBoolean("org.apache.commons.jcs3.jcache.cdi.skip-close"); 057 058 private volatile CacheResolverFactoryImpl defaultCacheResolverFactory; // lazy to not create any cache if not needed 059 private final CacheKeyGeneratorImpl defaultCacheKeyGenerator = new CacheKeyGeneratorImpl(); 060 061 private final Collection<CreationalContext<?>> toRelease = new ArrayList<>(); 062 private final ConcurrentMap<MethodKey, MethodMeta> methods = new ConcurrentHashMap<>(); 063 064 @Inject 065 private BeanManager beanManager; 066 067 @PreDestroy 068 private void release() { 069 if (CLOSE_CACHE && defaultCacheResolverFactory != null) 070 { 071 defaultCacheResolverFactory.release(); 072 } 073 for (final CreationalContext<?> cc : toRelease) 074 { 075 try 076 { 077 cc.release(); 078 } 079 catch (final RuntimeException re) 080 { 081 LOGGER.warning(re.getMessage()); 082 } 083 } 084 } 085 086 public MethodMeta findMeta(final InvocationContext ic) 087 { 088 final Method mtd = ic.getMethod(); 089 final Class<?> refType = findKeyType(ic.getTarget()); 090 final MethodKey key = new MethodKey(refType, mtd); 091 MethodMeta methodMeta = methods.get(key); 092 if (methodMeta == null) 093 { 094 synchronized (this) 095 { 096 methodMeta = methods.get(key); 097 if (methodMeta == null) 098 { 099 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 // it is unlikely we have all annotations but for now we have a single meta model 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)) // target doesn't hold annotations 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 { // TODO: release at the right moment, @PreDestroy? question is: do we assume it is thread safe? 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 // first check if keys are specified explicitly 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 // if not then use all parameters but value ones 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; // we need a class to ensure inheritance don't fall in the same key 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 // TODO: split it in 5? 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}