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.jcs.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.jcs.jcache.cdi.skip-close"); 057 058 private volatile CacheResolverFactoryImpl defaultCacheResolverFactory = null; // 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<CreationalContext<?>>(); 062 private final ConcurrentMap<MethodKey, MethodMeta> methods = new ConcurrentHashMap<MethodKey, MethodMeta>(); 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 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<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)) // target doesnt hold annotations 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 { // TODO: release at the right moment, @PreDestroy? question is: do we assume it is thread safe? 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 // first check if keys are specified explicitely 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 // if not then use all parameters but value ones 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; // we need a class to ensure inheritance don't fall in the same key 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 // 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(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}