CDIJCacheHelper.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- package org.apache.commons.jcs3.jcache.cdi;
- import java.lang.annotation.Annotation;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.HashSet;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Set;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.ConcurrentMap;
- import java.util.logging.Logger;
- import javax.annotation.PreDestroy;
- import javax.cache.annotation.CacheDefaults;
- import javax.cache.annotation.CacheKey;
- import javax.cache.annotation.CacheKeyGenerator;
- import javax.cache.annotation.CachePut;
- import javax.cache.annotation.CacheRemove;
- import javax.cache.annotation.CacheRemoveAll;
- import javax.cache.annotation.CacheResolverFactory;
- import javax.cache.annotation.CacheResult;
- import javax.cache.annotation.CacheValue;
- import javax.enterprise.context.ApplicationScoped;
- import javax.enterprise.context.spi.CreationalContext;
- import javax.enterprise.inject.spi.Bean;
- import javax.enterprise.inject.spi.BeanManager;
- import javax.inject.Inject;
- import javax.interceptor.InvocationContext;
- @ApplicationScoped
- public class CDIJCacheHelper
- {
- private static final Logger LOGGER = Logger.getLogger(CDIJCacheHelper.class.getName());
- private static final boolean CLOSE_CACHE = !Boolean.getBoolean("org.apache.commons.jcs3.jcache.cdi.skip-close");
- private volatile CacheResolverFactoryImpl defaultCacheResolverFactory; // lazy to not create any cache if not needed
- private final CacheKeyGeneratorImpl defaultCacheKeyGenerator = new CacheKeyGeneratorImpl();
- private final Collection<CreationalContext<?>> toRelease = new ArrayList<>();
- private final ConcurrentMap<MethodKey, MethodMeta> methods = new ConcurrentHashMap<>();
- @Inject
- private BeanManager beanManager;
- @PreDestroy
- private void release() {
- if (CLOSE_CACHE && defaultCacheResolverFactory != null)
- {
- defaultCacheResolverFactory.release();
- }
- for (final CreationalContext<?> cc : toRelease)
- {
- try
- {
- cc.release();
- }
- catch (final RuntimeException re)
- {
- LOGGER.warning(re.getMessage());
- }
- }
- }
- public MethodMeta findMeta(final InvocationContext ic)
- {
- final Method mtd = ic.getMethod();
- final Class<?> refType = findKeyType(ic.getTarget());
- final MethodKey key = new MethodKey(refType, mtd);
- MethodMeta methodMeta = methods.get(key);
- if (methodMeta == null)
- {
- synchronized (this)
- {
- methodMeta = methods.get(key);
- if (methodMeta == null)
- {
- methodMeta = createMeta(ic);
- methods.put(key, methodMeta);
- }
- }
- }
- return methodMeta;
- }
- private static Class<?> findKeyType(final Object target)
- {
- if (null == target)
- {
- return null;
- }
- return target.getClass();
- }
- // it is unlikely we have all annotations but for now we have a single meta model
- private MethodMeta createMeta(final InvocationContext ic)
- {
- final CacheDefaults defaults = findDefaults(ic.getTarget() == null ? null : ic.getTarget()
- .getClass(), ic.getMethod());
- final Class<?>[] parameterTypes = ic.getMethod().getParameterTypes();
- final Annotation[][] parameterAnnotations = ic.getMethod().getParameterAnnotations();
- final List<Set<Annotation>> annotations = new ArrayList<>();
- for (final Annotation[] parameterAnnotation : parameterAnnotations)
- {
- final Set<Annotation> set = new HashSet<>(parameterAnnotation.length);
- set.addAll(Arrays.asList(parameterAnnotation));
- annotations.add(set);
- }
- final Set<Annotation> mtdAnnotations = new HashSet<>(Arrays.asList(ic.getMethod().getAnnotations()));
- final CacheResult cacheResult = ic.getMethod().getAnnotation(CacheResult.class);
- final String cacheResultCacheResultName = cacheResult == null ? null : defaultName(ic.getMethod(), defaults, cacheResult.cacheName());
- final CacheResolverFactory cacheResultCacheResolverFactory = cacheResult == null ?
- null : cacheResolverFactoryFor(defaults, cacheResult.cacheResolverFactory());
- final CacheKeyGenerator cacheResultCacheKeyGenerator = cacheResult == null ?
- null : cacheKeyGeneratorFor(defaults, cacheResult.cacheKeyGenerator());
- final CachePut cachePut = ic.getMethod().getAnnotation(CachePut.class);
- final String cachePutCachePutName = cachePut == null ? null : defaultName(ic.getMethod(), defaults, cachePut.cacheName());
- final CacheResolverFactory cachePutCacheResolverFactory = cachePut == null ?
- null : cacheResolverFactoryFor(defaults, cachePut.cacheResolverFactory());
- final CacheKeyGenerator cachePutCacheKeyGenerator = cachePut == null ?
- null : cacheKeyGeneratorFor(defaults, cachePut.cacheKeyGenerator());
- final CacheRemove cacheRemove = ic.getMethod().getAnnotation(CacheRemove.class);
- final String cacheRemoveCacheRemoveName = cacheRemove == null ? null : defaultName(ic.getMethod(), defaults, cacheRemove.cacheName());
- final CacheResolverFactory cacheRemoveCacheResolverFactory = cacheRemove == null ?
- null : cacheResolverFactoryFor(defaults, cacheRemove.cacheResolverFactory());
- final CacheKeyGenerator cacheRemoveCacheKeyGenerator = cacheRemove == null ?
- null : cacheKeyGeneratorFor(defaults, cacheRemove.cacheKeyGenerator());
- final CacheRemoveAll cacheRemoveAll = ic.getMethod().getAnnotation(CacheRemoveAll.class);
- final String cacheRemoveAllCacheRemoveAllName = cacheRemoveAll == null ? null : defaultName(ic.getMethod(), defaults, cacheRemoveAll.cacheName());
- final CacheResolverFactory cacheRemoveAllCacheResolverFactory = cacheRemoveAll == null ?
- null : cacheResolverFactoryFor(defaults, cacheRemoveAll.cacheResolverFactory());
- return new MethodMeta(
- parameterTypes,
- annotations,
- mtdAnnotations,
- keyParameterIndexes(ic.getMethod()),
- getValueParameter(annotations),
- getKeyParameters(annotations),
- cacheResultCacheResultName,
- cacheResultCacheResolverFactory,
- cacheResultCacheKeyGenerator,
- cacheResult,
- cachePutCachePutName,
- cachePutCacheResolverFactory,
- cachePutCacheKeyGenerator,
- cachePut != null && cachePut.afterInvocation(),
- cachePut,
- cacheRemoveCacheRemoveName,
- cacheRemoveCacheResolverFactory,
- cacheRemoveCacheKeyGenerator,
- cacheRemove != null && cacheRemove.afterInvocation(),
- cacheRemove,
- cacheRemoveAllCacheRemoveAllName,
- cacheRemoveAllCacheResolverFactory,
- cacheRemoveAll != null && cacheRemoveAll.afterInvocation(),
- cacheRemoveAll);
- }
- private static Integer[] getKeyParameters(final List<Set<Annotation>> annotations)
- {
- final Collection<Integer> list = new ArrayList<>();
- int idx = 0;
- for (final Set<Annotation> set : annotations)
- {
- for (final Annotation a : set)
- {
- if (a.annotationType() == CacheKey.class)
- {
- list.add(idx);
- }
- }
- idx++;
- }
- if (list.isEmpty())
- {
- for (int i = 0; i < annotations.size(); i++)
- {
- list.add(i);
- }
- }
- return list.toArray(new Integer[0]);
- }
- private static Integer getValueParameter(final List<Set<Annotation>> annotations)
- {
- final int idx = 0;
- for (final Set<Annotation> set : annotations)
- {
- for (final Annotation a : set)
- {
- if (a.annotationType() == CacheValue.class)
- {
- return idx;
- }
- }
- }
- return -1;
- }
- private static String defaultName(final Method method, final CacheDefaults defaults, final String cacheName)
- {
- if (!cacheName.isEmpty())
- {
- return cacheName;
- }
- if (defaults != null)
- {
- final String name = defaults.cacheName();
- if (!name.isEmpty())
- {
- return name;
- }
- }
- final StringBuilder name = new StringBuilder(method.getDeclaringClass().getName());
- name.append(".");
- name.append(method.getName());
- name.append("(");
- final Class<?>[] parameterTypes = method.getParameterTypes();
- for (int pIdx = 0; pIdx < parameterTypes.length; pIdx++)
- {
- name.append(parameterTypes[pIdx].getName());
- if ((pIdx + 1) < parameterTypes.length)
- {
- name.append(",");
- }
- }
- name.append(")");
- return name.toString();
- }
- private static CacheDefaults findDefaults(final Class<?> targetType, final Method method)
- {
- if (Proxy.isProxyClass(targetType)) // target doesn't hold annotations
- {
- final Class<?> api = method.getDeclaringClass();
- for (final Class<?> type : targetType
- .getInterfaces())
- {
- if (!api.isAssignableFrom(type))
- {
- continue;
- }
- return extractDefaults(type);
- }
- }
- return extractDefaults(targetType);
- }
- private static CacheDefaults extractDefaults(final Class<?> type)
- {
- CacheDefaults annotation = null;
- Class<?> clazz = type;
- while (clazz != null && clazz != Object.class)
- {
- annotation = clazz.getAnnotation(CacheDefaults.class);
- if (annotation != null)
- {
- break;
- }
- clazz = clazz.getSuperclass();
- }
- return annotation;
- }
- public boolean isIncluded(final Class<?> aClass, final Class<?>[] in, final Class<?>[] out)
- {
- if (in.length == 0 && out.length == 0)
- {
- return false;
- }
- for (final Class<?> potentialIn : in)
- {
- if (potentialIn.isAssignableFrom(aClass))
- {
- for (final Class<?> potentialOut : out)
- {
- if (potentialOut.isAssignableFrom(aClass))
- {
- return false;
- }
- }
- return true;
- }
- }
- return false;
- }
- private CacheKeyGenerator cacheKeyGeneratorFor(final CacheDefaults defaults, final Class<? extends CacheKeyGenerator> cacheKeyGenerator)
- {
- if (!CacheKeyGenerator.class.equals(cacheKeyGenerator))
- {
- return instance(cacheKeyGenerator);
- }
- if (defaults != null)
- {
- final Class<? extends CacheKeyGenerator> defaultCacheKeyGenerator = defaults.cacheKeyGenerator();
- if (!CacheKeyGenerator.class.equals(defaultCacheKeyGenerator))
- {
- return instance(defaultCacheKeyGenerator);
- }
- }
- return defaultCacheKeyGenerator;
- }
- private CacheResolverFactory cacheResolverFactoryFor(final CacheDefaults defaults, final Class<? extends CacheResolverFactory> cacheResolverFactory)
- {
- if (!CacheResolverFactory.class.equals(cacheResolverFactory))
- {
- return instance(cacheResolverFactory);
- }
- if (defaults != null)
- {
- final Class<? extends CacheResolverFactory> defaultCacheResolverFactory = defaults.cacheResolverFactory();
- if (!CacheResolverFactory.class.equals(defaultCacheResolverFactory))
- {
- return instance(defaultCacheResolverFactory);
- }
- }
- return defaultCacheResolverFactory();
- }
- @SuppressWarnings("unchecked")
- private <T> T instance(final Class<T> type)
- {
- final Set<Bean<?>> beans = beanManager.getBeans(type);
- if (beans.isEmpty())
- {
- if (CacheKeyGenerator.class == type) {
- return (T) defaultCacheKeyGenerator;
- }
- if (CacheResolverFactory.class == type) {
- return (T) defaultCacheResolverFactory();
- }
- return null;
- }
- final Bean<?> bean = beanManager.resolve(beans);
- final CreationalContext<?> context = beanManager.createCreationalContext(bean);
- final Class<? extends Annotation> scope = bean.getScope();
- final boolean normalScope = beanManager.isNormalScope(scope);
- try
- {
- final Object reference = beanManager.getReference(bean, bean.getBeanClass(), context);
- if (!normalScope)
- {
- toRelease.add(context);
- }
- return (T) reference;
- }
- finally
- {
- if (normalScope)
- { // TODO: release at the right moment, @PreDestroy? question is: do we assume it is thread safe?
- context.release();
- }
- }
- }
- private CacheResolverFactoryImpl defaultCacheResolverFactory()
- {
- if (defaultCacheResolverFactory != null) {
- return defaultCacheResolverFactory;
- }
- synchronized (this) {
- if (defaultCacheResolverFactory != null) {
- return defaultCacheResolverFactory;
- }
- defaultCacheResolverFactory = new CacheResolverFactoryImpl();
- }
- return defaultCacheResolverFactory;
- }
- private static Integer[] keyParameterIndexes(final Method method)
- {
- final List<Integer> keys = new LinkedList<>();
- final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
- // first check if keys are specified explicitly
- for (int i = 0; i < method.getParameterTypes().length; i++)
- {
- final Annotation[] annotations = parameterAnnotations[i];
- for (final Annotation a : annotations)
- {
- if (a.annotationType().equals(CacheKey.class))
- {
- keys.add(i);
- break;
- }
- }
- }
- // if not then use all parameters but value ones
- if (keys.isEmpty())
- {
- for (int i = 0; i < method.getParameterTypes().length; i++)
- {
- final Annotation[] annotations = parameterAnnotations[i];
- boolean value = false;
- for (final Annotation a : annotations)
- {
- if (a.annotationType().equals(CacheValue.class))
- {
- value = true;
- break;
- }
- }
- if (!value) {
- keys.add(i);
- }
- }
- }
- return keys.toArray(new Integer[0]);
- }
- private static final class MethodKey
- {
- private final Class<?> base;
- private final Method delegate;
- private final int hash;
- private MethodKey(final Class<?> base, final Method delegate)
- {
- this.base = base; // we need a class to ensure inheritance don't fall in the same key
- this.delegate = delegate;
- this.hash = 31 * delegate.hashCode() + (base == null ? 0 : base.hashCode());
- }
- @Override
- public boolean equals(final Object o)
- {
- if (this == o)
- {
- return true;
- }
- if (o == null || getClass() != o.getClass())
- {
- return false;
- }
- final MethodKey classKey = MethodKey.class.cast(o);
- return delegate.equals(classKey.delegate) &&
- (base == null && classKey.base == null || base != null && base.equals(classKey.base));
- }
- @Override
- public int hashCode()
- {
- return hash;
- }
- }
- // TODO: split it in 5?
- public static class MethodMeta
- {
- private final Class<?>[] parameterTypes;
- private final List<Set<Annotation>> parameterAnnotations;
- private final Set<Annotation> annotations;
- private final Integer[] keysIndices;
- private final Integer valueIndex;
- private final Integer[] parameterIndices;
- private final String cacheResultCacheName;
- private final CacheResolverFactory cacheResultResolverFactory;
- private final CacheKeyGenerator cacheResultKeyGenerator;
- private final CacheResult cacheResult;
- private final String cachePutCacheName;
- private final CacheResolverFactory cachePutResolverFactory;
- private final CacheKeyGenerator cachePutKeyGenerator;
- private final boolean cachePutAfter;
- private final CachePut cachePut;
- private final String cacheRemoveCacheName;
- private final CacheResolverFactory cacheRemoveResolverFactory;
- private final CacheKeyGenerator cacheRemoveKeyGenerator;
- private final boolean cacheRemoveAfter;
- private final CacheRemove cacheRemove;
- private final String cacheRemoveAllCacheName;
- private final CacheResolverFactory cacheRemoveAllResolverFactory;
- private final boolean cacheRemoveAllAfter;
- private final CacheRemoveAll cacheRemoveAll;
- public MethodMeta(final Class<?>[] parameterTypes, final List<Set<Annotation>> parameterAnnotations, final Set<Annotation>
- annotations, final Integer[] keysIndices, final Integer valueIndex, final Integer[] parameterIndices, final String
- cacheResultCacheName, final CacheResolverFactory cacheResultResolverFactory, final CacheKeyGenerator
- cacheResultKeyGenerator, final CacheResult cacheResult, final String cachePutCacheName, final CacheResolverFactory
- cachePutResolverFactory, final CacheKeyGenerator cachePutKeyGenerator, final boolean cachePutAfter, final CachePut cachePut, final String
- cacheRemoveCacheName, final CacheResolverFactory cacheRemoveResolverFactory, final CacheKeyGenerator
- cacheRemoveKeyGenerator, final boolean cacheRemoveAfter, final CacheRemove cacheRemove, final String cacheRemoveAllCacheName,
- final CacheResolverFactory cacheRemoveAllResolverFactory, final boolean
- cacheRemoveAllAfter, final CacheRemoveAll cacheRemoveAll)
- {
- this.parameterTypes = parameterTypes;
- this.parameterAnnotations = parameterAnnotations;
- this.annotations = annotations;
- this.keysIndices = keysIndices;
- this.valueIndex = valueIndex;
- this.parameterIndices = parameterIndices;
- this.cacheResultCacheName = cacheResultCacheName;
- this.cacheResultResolverFactory = cacheResultResolverFactory;
- this.cacheResultKeyGenerator = cacheResultKeyGenerator;
- this.cacheResult = cacheResult;
- this.cachePutCacheName = cachePutCacheName;
- this.cachePutResolverFactory = cachePutResolverFactory;
- this.cachePutKeyGenerator = cachePutKeyGenerator;
- this.cachePutAfter = cachePutAfter;
- this.cachePut = cachePut;
- this.cacheRemoveCacheName = cacheRemoveCacheName;
- this.cacheRemoveResolverFactory = cacheRemoveResolverFactory;
- this.cacheRemoveKeyGenerator = cacheRemoveKeyGenerator;
- this.cacheRemoveAfter = cacheRemoveAfter;
- this.cacheRemove = cacheRemove;
- this.cacheRemoveAllCacheName = cacheRemoveAllCacheName;
- this.cacheRemoveAllResolverFactory = cacheRemoveAllResolverFactory;
- this.cacheRemoveAllAfter = cacheRemoveAllAfter;
- this.cacheRemoveAll = cacheRemoveAll;
- }
- public boolean isCacheRemoveAfter()
- {
- return cacheRemoveAfter;
- }
- public boolean isCachePutAfter()
- {
- return cachePutAfter;
- }
- public Class<?>[] getParameterTypes()
- {
- return parameterTypes;
- }
- public List<Set<Annotation>> getParameterAnnotations()
- {
- return parameterAnnotations;
- }
- public String getCacheResultCacheName()
- {
- return cacheResultCacheName;
- }
- public CacheResolverFactory getCacheResultResolverFactory()
- {
- return cacheResultResolverFactory;
- }
- public CacheKeyGenerator getCacheResultKeyGenerator()
- {
- return cacheResultKeyGenerator;
- }
- public CacheResult getCacheResult() {
- return cacheResult;
- }
- public Integer[] getParameterIndices()
- {
- return parameterIndices;
- }
- public Set<Annotation> getAnnotations()
- {
- return annotations;
- }
- public Integer[] getKeysIndices()
- {
- return keysIndices;
- }
- public Integer getValuesIndex()
- {
- return valueIndex;
- }
- public Integer getValueIndex()
- {
- return valueIndex;
- }
- public String getCachePutCacheName()
- {
- return cachePutCacheName;
- }
- public CacheResolverFactory getCachePutResolverFactory()
- {
- return cachePutResolverFactory;
- }
- public CacheKeyGenerator getCachePutKeyGenerator()
- {
- return cachePutKeyGenerator;
- }
- public CachePut getCachePut()
- {
- return cachePut;
- }
- public String getCacheRemoveCacheName()
- {
- return cacheRemoveCacheName;
- }
- public CacheResolverFactory getCacheRemoveResolverFactory()
- {
- return cacheRemoveResolverFactory;
- }
- public CacheKeyGenerator getCacheRemoveKeyGenerator()
- {
- return cacheRemoveKeyGenerator;
- }
- public CacheRemove getCacheRemove()
- {
- return cacheRemove;
- }
- public String getCacheRemoveAllCacheName()
- {
- return cacheRemoveAllCacheName;
- }
- public CacheResolverFactory getCacheRemoveAllResolverFactory()
- {
- return cacheRemoveAllResolverFactory;
- }
- public boolean isCacheRemoveAllAfter()
- {
- return cacheRemoveAllAfter;
- }
- public CacheRemoveAll getCacheRemoveAll()
- {
- return cacheRemoveAll;
- }
- }
- }