JCS-183 reduce reflection at runtime in our jcache cdi integration

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/jcs/trunk@1806978 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CDIJCacheHelper.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CDIJCacheHelper.java
index b7f8095..a86ca35 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CDIJCacheHelper.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CDIJCacheHelper.java
@@ -18,22 +18,13 @@
  */
 package org.apache.commons.jcs.jcache.cdi;
 
-import javax.annotation.PreDestroy;
-import javax.cache.annotation.CacheDefaults;
-import javax.cache.annotation.CacheKey;
-import javax.cache.annotation.CacheKeyGenerator;
-import javax.cache.annotation.CacheResolverFactory;
-import javax.cache.annotation.CacheValue;
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.context.Dependent;
-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;
 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;
@@ -41,6 +32,23 @@
 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
 {
@@ -49,8 +57,9 @@
 
     private volatile CacheResolverFactoryImpl defaultCacheResolverFactory = null; // lazy to not create any cache if not needed
     private final CacheKeyGeneratorImpl defaultCacheKeyGenerator = new CacheKeyGeneratorImpl();
-    private final ConcurrentMap<Method, String> generatedNames = new ConcurrentHashMap<Method, String>();
-    private final ConcurrentMap<Method, Integer[]> parameterIndexes = new ConcurrentHashMap<Method, Integer[]>();
+
+    private final Collection<CreationalContext<?>> toRelease = new ArrayList<CreationalContext<?>>();
+    private final ConcurrentMap<MethodKey, MethodMeta> methods = new ConcurrentHashMap<MethodKey, MethodMeta>();
 
     @Inject
     private BeanManager beanManager;
@@ -61,9 +70,152 @@
         {
             defaultCacheResolverFactory.release();
         }
+        for (final CreationalContext<?> cc : toRelease)
+        {
+            try
+            {
+                cc.release();
+            }
+            catch (final RuntimeException re)
+            {
+                LOGGER.warning(re.getMessage());
+            }
+        }
     }
 
-    public String defaultName(final Method method, final CacheDefaults defaults, final String cacheName)
+    public MethodMeta findMeta(final InvocationContext ic)
+    {
+        final Method mtd = ic.getMethod();
+        final MethodKey key = new MethodKey(mtd);
+        MethodMeta methodMeta = methods.get(key);
+        if (methodMeta == null)
+        {
+            synchronized (this)
+            {
+                methodMeta = methods.get(key);
+                if (methodMeta == null)
+                {
+                    methodMeta = createMeta(ic);
+                }
+            }
+        }
+        return methodMeta;
+    }
+
+    // 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<Set<Annotation>>();
+        for (final Annotation[] parameterAnnotation : parameterAnnotations)
+        {
+            final Set<Annotation> set = new HashSet<Annotation>(parameterAnnotation.length);
+            set.addAll(Arrays.asList(parameterAnnotation));
+            annotations.add(set);
+        }
+
+        final Set<Annotation> mtdAnnotations = new HashSet<Annotation>();
+        mtdAnnotations.addAll(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 Integer[] getKeyParameters(final List<Set<Annotation>> annotations)
+    {
+        final Collection<Integer> list = new ArrayList<Integer>();
+        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[list.size()]);
+    }
+
+    private Integer getValueParameter(final List<Set<Annotation>> annotations)
+    {
+        int idx = 0;
+        for (final Set<Annotation> set : annotations)
+        {
+            for (final Annotation a : set)
+            {
+                if (a.annotationType() == CacheValue.class)
+                {
+                    return idx;
+                }
+            }
+        }
+        return -1;
+    }
+
+    private String defaultName(final Method method, final CacheDefaults defaults, final String cacheName)
     {
         if (!cacheName.isEmpty())
         {
@@ -78,35 +230,30 @@
             }
         }
 
-        String computedName = generatedNames.get(method);
-        if (computedName == null)
+        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++)
         {
-            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(parameterTypes[pIdx].getName());
-                if ((pIdx + 1) < parameterTypes.length)
-                {
-                    name.append(",");
-                }
+                name.append(",");
             }
-            name.append(")");
-            computedName = name.toString();
-            generatedNames.putIfAbsent(method, computedName);
         }
-        return computedName;
+        name.append(")");
+        return name.toString();
     }
 
-    public CacheDefaults findDefaults(final InvocationContext ic)
+    private CacheDefaults findDefaults(final Class<?> targetType, final Method method)
     {
-        if (ic.getTarget() != null && Proxy.isProxyClass(ic.getTarget().getClass())) // target doesnt hold annotations
+        if (Proxy.isProxyClass(targetType)) // target doesnt hold annotations
         {
-            final Class<?> api = ic.getMethod().getDeclaringClass();
-            for (final Class<?> type : ic.getTarget().getClass().getInterfaces())
+            final Class<?> api = method.getDeclaringClass();
+            for (final Class<?> type : targetType
+                                         .getInterfaces())
             {
                 if (!api.isAssignableFrom(type))
                 {
@@ -115,7 +262,7 @@
                 return extractDefaults(type);
             }
         }
-        return extractDefaults(ic.getTarget().getClass());
+        return extractDefaults(targetType);
     }
 
     private CacheDefaults extractDefaults(final Class<?> type)
@@ -157,7 +304,7 @@
         return false;
     }
 
-    public CacheKeyGenerator cacheKeyGeneratorFor(final CacheDefaults defaults, final Class<? extends CacheKeyGenerator> cacheKeyGenerator)
+    private CacheKeyGenerator cacheKeyGeneratorFor(final CacheDefaults defaults, final Class<? extends CacheKeyGenerator> cacheKeyGenerator)
     {
         if (!CacheKeyGenerator.class.equals(cacheKeyGenerator))
         {
@@ -174,7 +321,7 @@
         return defaultCacheKeyGenerator;
     }
 
-    public CacheResolverFactory cacheResolverFactoryFor(final CacheDefaults defaults, final Class<? extends CacheResolverFactory> cacheResolverFactory)
+    private CacheResolverFactory cacheResolverFactoryFor(final CacheDefaults defaults, final Class<? extends CacheResolverFactory> cacheResolverFactory)
     {
         if (!CacheResolverFactory.class.equals(cacheResolverFactory))
         {
@@ -191,7 +338,7 @@
         return defaultCacheResolverFactory();
     }
 
-    public <T> T instance(final Class<T> type)
+    private <T> T instance(final Class<T> type)
     {
         final Set<Bean<?>> beans = beanManager.getBeans(type);
         if (beans.isEmpty())
@@ -207,19 +354,20 @@
         final Bean<?> bean = beanManager.resolve(beans);
         final CreationalContext<?> context = beanManager.createCreationalContext(bean);
         final Class<? extends Annotation> scope = bean.getScope();
-        final boolean dependent = Dependent.class.equals(scope);
-        if (!dependent && !beanManager.isNormalScope(scope))
-        {
-            LOGGER.warning("Not normal scope beans (" + type.getName() + ") can leak");
-        }
+        final boolean normalScope = beanManager.isNormalScope(scope);
         try
         {
-            return (T) beanManager.getReference(bean, bean.getBeanClass(), context);
+            final Object reference = beanManager.getReference(bean, bean.getBeanClass(), context);
+            if (!normalScope)
+            {
+                toRelease.add(context);
+            }
+            return (T) reference;
         }
         finally
         {
-            if (dependent)
-            { // TODO: depent or pseudo scope?
+            if (normalScope)
+            { // TODO: release at the right moment, @PreDestroy? question is: do we assume it is thread safe?
                 context.release();
             }
         }
@@ -239,51 +387,271 @@
         return defaultCacheResolverFactory;
     }
 
-    public Integer[] keyParameterIndexes(final Method method)
+    private Integer[] keyParameterIndexes(final Method method)
     {
-        Integer[] val = parameterIndexes.get(method);
-        if (val == null)
+        final List<Integer> keys = new LinkedList<Integer>();
+        final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+
+        // first check if keys are specified explicitely
+        for (int i = 0; i < method.getParameterTypes().length; i++)
         {
-            final List<Integer> keys = new LinkedList<Integer>();
-            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
-            
-            // first check if keys are specified explicitely
+            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(CacheKey.class))
+                    if (a.annotationType().equals(CacheValue.class))
                     {
-                        keys.add(i);
+                        value = true;
                         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);
-                    }
+                if (!value) {
+                    keys.add(i);
                 }
             }
-            val = keys.toArray(new Integer[keys.size()]);
-            parameterIndexes.putIfAbsent(method, val);
         }
-        return val;
+        return keys.toArray(new Integer[keys.size()]);
+    }
+
+    private static final class MethodKey
+    {
+        private final Method delegate;
+        private final int hash;
+
+        private MethodKey(final Method delegate)
+        {
+            this.delegate = delegate;
+            this.hash = delegate.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);
+        }
+
+        @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(Class<?>[] parameterTypes, List<Set<Annotation>> parameterAnnotations, Set<Annotation> 
+                annotations, Integer[] keysIndices, Integer valueIndex, Integer[] parameterIndices, String 
+                cacheResultCacheName, CacheResolverFactory cacheResultResolverFactory, CacheKeyGenerator 
+                cacheResultKeyGenerator, CacheResult cacheResult, String cachePutCacheName, CacheResolverFactory 
+                cachePutResolverFactory, CacheKeyGenerator cachePutKeyGenerator, boolean cachePutAfter, CachePut cachePut, String
+                cacheRemoveCacheName, CacheResolverFactory cacheRemoveResolverFactory, CacheKeyGenerator 
+                cacheRemoveKeyGenerator, boolean cacheRemoveAfter, CacheRemove cacheRemove, String cacheRemoveAllCacheName,
+                          CacheResolverFactory cacheRemoveAllResolverFactory, boolean
+                                  cacheRemoveAllAfter, 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;
+        }
     }
 }
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationContextImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationContextImpl.java
index 8cf4614..7f6a539 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationContextImpl.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationContextImpl.java
@@ -18,13 +18,13 @@
  */
 package org.apache.commons.jcs.jcache.cdi;
 
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Set;
+
 import javax.cache.annotation.CacheInvocationContext;
 import javax.cache.annotation.CacheInvocationParameter;
 import javax.interceptor.InvocationContext;
-import java.lang.annotation.Annotation;
-import java.util.HashSet;
-
-import static java.util.Arrays.asList;
 
 public class CacheInvocationContextImpl<A extends Annotation> extends CacheMethodDetailsImpl<A> implements CacheInvocationContext<A>
 {
@@ -32,9 +32,10 @@
 
     private CacheInvocationParameter[] parameters = null;
 
-    public CacheInvocationContextImpl(final InvocationContext delegate, final A cacheAnnotation, final String cacheName)
+    public CacheInvocationContextImpl(final InvocationContext delegate, final A cacheAnnotation, final String cacheName,
+                                      final CDIJCacheHelper.MethodMeta meta)
     {
-        super(delegate, cacheAnnotation, cacheName);
+        super(delegate, cacheAnnotation, cacheName, meta);
     }
 
     @Override
@@ -67,15 +68,15 @@
     {
         final Object[] parameters = delegate.getParameters();
         final Object[] args = parameters == null ? EMPTY_ARGS : parameters;
-        final Class<?>[] parameterTypes = getMethod().getParameterTypes();
-        final Annotation[][] parameterAnnotations = getMethod().getParameterAnnotations();
+        final Class<?>[] parameterTypes = meta.getParameterTypes();
+        final List<Set<Annotation>> parameterAnnotations = meta.getParameterAnnotations();
 
         final CacheInvocationParameter[] parametersAsArray = new CacheInvocationParameter[indexes == null ? args.length : indexes.length];
         if (indexes == null)
         {
             for (int i = 0; i < args.length; i++)
             {
-                parametersAsArray[i] = newCacheInvocationParameterImpl(parameterTypes[i], args[i], parameterAnnotations[i], i);
+                parametersAsArray[i] = newCacheInvocationParameterImpl(parameterTypes[i], args[i], parameterAnnotations.get(i), i);
             }
         }
         else
@@ -83,14 +84,14 @@
             for (int idx = 0; idx < indexes.length; idx++)
             {
                 final int i = indexes[idx];
-                parametersAsArray[i] = newCacheInvocationParameterImpl(parameterTypes[i], args[i], parameterAnnotations[i], i);
+                parametersAsArray[i] = newCacheInvocationParameterImpl(parameterTypes[i], args[i], parameterAnnotations.get(i), i);
             }
         }
         return parametersAsArray;
     }
 
     private CacheInvocationParameterImpl newCacheInvocationParameterImpl(final Class<?> type, final Object arg,
-                                                                         final Annotation[] annotations, final int i) {
-        return new CacheInvocationParameterImpl(type, arg, new HashSet<Annotation>(asList(annotations)), i);
+                                                                         final Set<Annotation> annotations, final int i) {
+        return new CacheInvocationParameterImpl(type, arg, annotations, i);
     }
 }
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyInvocationContextImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyInvocationContextImpl.java
index a861152..6d87d26 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyInvocationContextImpl.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyInvocationContextImpl.java
@@ -18,26 +18,21 @@
  */
 package org.apache.commons.jcs.jcache.cdi;
 
-import javax.cache.annotation.CacheInvocationParameter;
-import javax.cache.annotation.CacheKey;
-import javax.cache.annotation.CacheKeyInvocationContext;
-import javax.cache.annotation.CacheValue;
-import javax.interceptor.InvocationContext;
 import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.LinkedList;
+
+import javax.cache.annotation.CacheInvocationParameter;
+import javax.cache.annotation.CacheKeyInvocationContext;
+import javax.interceptor.InvocationContext;
 
 public class CacheKeyInvocationContextImpl<A extends Annotation> extends CacheInvocationContextImpl<A> implements CacheKeyInvocationContext<A>
 {
-    private final Integer[] keyIndexes;
     private CacheInvocationParameter[] keyParams = null;
     private CacheInvocationParameter valueParam = null;
 
     public CacheKeyInvocationContextImpl(final InvocationContext delegate, final A annotation, final String name,
-                                         final Integer[] keyIndexes)
+                                         final CDIJCacheHelper.MethodMeta methodMeta)
     {
-        super(delegate, annotation, name);
-        this.keyIndexes = keyIndexes;
+        super(delegate, annotation, name, methodMeta);
     }
 
     @Override
@@ -45,25 +40,7 @@
     {
         if (keyParams == null)
         {
-            final Collection<CacheInvocationParameter> keys = new LinkedList<CacheInvocationParameter>();
-            for (final CacheInvocationParameter param : getAllParameters())
-            {
-                for (final Annotation a : param.getAnnotations())
-                {
-                    if (a.annotationType().equals(CacheKey.class))
-                    {
-                        keys.add(param);
-                    }
-                }
-            }
-            if (keys.isEmpty())
-            {
-                keyParams = doGetAllParameters(keyIndexes);
-            }
-            else
-            {
-                keyParams = keys.toArray(new CacheInvocationParameter[keys.size()]);
-            }
+            keyParams = doGetAllParameters(meta.getKeysIndices());
         }
         return keyParams;
     }
@@ -73,17 +50,7 @@
     {
         if (valueParam == null)
         {
-            for (final CacheInvocationParameter param : getAllParameters())
-            {
-                for (final Annotation a : param.getAnnotations())
-                {
-                    if (a.annotationType().equals(CacheValue.class))
-                    {
-                        valueParam = param;
-                        return valueParam;
-                    }
-                }
-            }
+            valueParam = meta.getValueIndex() >= 0 ? doGetAllParameters(new Integer[]{meta.getValueIndex()})[0] : null;
         }
         return valueParam;
     }
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheMethodDetailsImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheMethodDetailsImpl.java
index 4f5ac5b..349b27b 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheMethodDetailsImpl.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheMethodDetailsImpl.java
@@ -33,13 +33,16 @@
     private final Set<Annotation> annotations;
     private final A cacheAnnotation;
     private final String cacheName;
+    protected final CDIJCacheHelper.MethodMeta meta;
 
-    public CacheMethodDetailsImpl(final InvocationContext delegate, final A cacheAnnotation, final String cacheName)
+    public CacheMethodDetailsImpl(final InvocationContext delegate, final A cacheAnnotation, final String cacheName,
+                                  final CDIJCacheHelper.MethodMeta meta)
     {
         this.delegate = delegate;
-        this.annotations = new HashSet<Annotation>(asList(delegate.getMethod().getAnnotations()));
+        this.annotations = meta.getAnnotations();
         this.cacheAnnotation = cacheAnnotation;
         this.cacheName = cacheName;
+        this.meta = meta;
     }
 
     @Override
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CachePutInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CachePutInterceptor.java
index 60909f4..c226b06 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CachePutInterceptor.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CachePutInterceptor.java
@@ -19,12 +19,13 @@
 package org.apache.commons.jcs.jcache.cdi;
 
 import java.io.Serializable;
-import java.lang.reflect.Method;
+
 import javax.annotation.Priority;
 import javax.cache.Cache;
-import javax.cache.annotation.CacheDefaults;
 import javax.cache.annotation.CacheKeyInvocationContext;
 import javax.cache.annotation.CachePut;
+import javax.cache.annotation.CacheResolver;
+import javax.cache.annotation.CacheResolverFactory;
 import javax.cache.annotation.GeneratedCacheKey;
 import javax.inject.Inject;
 import javax.interceptor.AroundInvoke;
@@ -42,18 +43,23 @@
     @AroundInvoke
     public Object cache(final InvocationContext ic) throws Throwable
     {
-        final CacheDefaults defaults = helper.findDefaults(ic);
+        final CDIJCacheHelper.MethodMeta methodMeta = helper.findMeta(ic);
 
-        final Method method = ic.getMethod();
-        final CachePut cachePut = method.getAnnotation(CachePut.class);
-        final String cacheName = helper.defaultName(method, defaults, cachePut.cacheName());
-        final boolean afterInvocation = cachePut.afterInvocation();
+        final String cacheName = methodMeta.getCacheResultCacheName();
 
+        final CacheResolverFactory cacheResolverFactory = methodMeta.getCacheResultResolverFactory();
         final CacheKeyInvocationContext<CachePut> context = new CacheKeyInvocationContextImpl<CachePut>(
-                ic, cachePut, cacheName, helper.keyParameterIndexes(method));
+                ic, methodMeta.getCachePut(), cacheName, methodMeta);
+        final CacheResolver cacheResolver = cacheResolverFactory.getCacheResolver(context);
+        final Cache<Object, Object> cache = cacheResolver.resolveCache(context);
+
+        final GeneratedCacheKey cacheKey = methodMeta.getCacheResultKeyGenerator().generateCacheKey(context);
+        final CachePut cachePut = methodMeta.getCachePut();
+        final boolean afterInvocation = methodMeta.isCachePutAfter();
+
         if (!afterInvocation)
         {
-            doCache(context, defaults, cachePut);
+            cache.put(cacheKey, context.getValueParameter());
         }
 
         final Object result;
@@ -67,7 +73,7 @@
             {
                 if (helper.isIncluded(t.getClass(), cachePut.cacheFor(), cachePut.noCacheFor()))
                 {
-                    doCache(context, defaults, cachePut);
+                    cache.put(cacheKey, context.getValueParameter());
                 }
             }
 
@@ -76,16 +82,9 @@
 
         if (afterInvocation)
         {
-            doCache(context, defaults, cachePut);
+            cache.put(cacheKey, context.getValueParameter());
         }
 
         return result;
     }
-
-    private void doCache(final CacheKeyInvocationContext<CachePut> context, final CacheDefaults defaults, final CachePut cachePut)
-    {
-        final Cache<Object, Object> cache = helper.cacheResolverFactoryFor(defaults, cachePut.cacheResolverFactory()).getCacheResolver(context).resolveCache(context);
-        final GeneratedCacheKey key = helper.cacheKeyGeneratorFor(defaults, cachePut.cacheKeyGenerator()).generateCacheKey(context);
-        cache.put(key, context.getValueParameter().getValue());
-    }
 }
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveAllInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveAllInterceptor.java
index fe6bb50..cede78f 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveAllInterceptor.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveAllInterceptor.java
@@ -19,12 +19,13 @@
 package org.apache.commons.jcs.jcache.cdi;
 
 import java.io.Serializable;
-import java.lang.reflect.Method;
+
 import javax.annotation.Priority;
 import javax.cache.Cache;
-import javax.cache.annotation.CacheDefaults;
 import javax.cache.annotation.CacheKeyInvocationContext;
 import javax.cache.annotation.CacheRemoveAll;
+import javax.cache.annotation.CacheResolver;
+import javax.cache.annotation.CacheResolverFactory;
 import javax.inject.Inject;
 import javax.interceptor.AroundInvoke;
 import javax.interceptor.Interceptor;
@@ -41,18 +42,20 @@
     @AroundInvoke
     public Object cache(final InvocationContext ic) throws Throwable
     {
-        final CacheDefaults defaults = helper.findDefaults(ic);
+        final CDIJCacheHelper.MethodMeta methodMeta = helper.findMeta(ic);
 
-        final Method method = ic.getMethod();
-        final CacheRemoveAll cacheRemoveAll = method.getAnnotation(CacheRemoveAll.class);
-        final String cacheName = helper.defaultName(method, defaults, cacheRemoveAll.cacheName());
-        final boolean afterInvocation = cacheRemoveAll.afterInvocation();
+        final String cacheName = methodMeta.getCacheResultCacheName();
 
+        final CacheResolverFactory cacheResolverFactory = methodMeta.getCacheResultResolverFactory();
         final CacheKeyInvocationContext<CacheRemoveAll> context = new CacheKeyInvocationContextImpl<CacheRemoveAll>(
-                ic, cacheRemoveAll, cacheName, helper.keyParameterIndexes(method));
+                ic, methodMeta.getCacheRemoveAll(), cacheName, methodMeta);
+        final CacheResolver cacheResolver = cacheResolverFactory.getCacheResolver(context);
+        final Cache<Object, Object> cache = cacheResolver.resolveCache(context);
+
+        final boolean afterInvocation = methodMeta.isCachePutAfter();
         if (!afterInvocation)
         {
-            removeAll(context, defaults, cacheRemoveAll);
+            cache.removeAll();
         }
 
         final Object result;
@@ -64,9 +67,9 @@
         {
             if (afterInvocation)
             {
-                if (helper.isIncluded(t.getClass(), cacheRemoveAll.evictFor(), cacheRemoveAll.noEvictFor()))
+                if (helper.isIncluded(t.getClass(), methodMeta.getCacheRemoveAll().evictFor(), methodMeta.getCacheRemoveAll().noEvictFor()))
                 {
-                    removeAll(context, defaults, cacheRemoveAll);
+                    cache.removeAll();
                 }
             }
             throw t;
@@ -74,15 +77,9 @@
 
         if (afterInvocation)
         {
-            removeAll(context, defaults, cacheRemoveAll);
+            cache.removeAll();
         }
 
         return result;
     }
-
-    private void removeAll(final CacheKeyInvocationContext<CacheRemoveAll> context, final CacheDefaults defaults, final CacheRemoveAll cacheRemoveAll)
-    {
-        final Cache<Object, Object> cache = helper.cacheResolverFactoryFor(defaults, cacheRemoveAll.cacheResolverFactory()).getCacheResolver(context).resolveCache(context);
-        cache.removeAll();
-    }
 }
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveInterceptor.java
index 29f4965..f28a128 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveInterceptor.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveInterceptor.java
@@ -19,12 +19,13 @@
 package org.apache.commons.jcs.jcache.cdi;
 
 import java.io.Serializable;
-import java.lang.reflect.Method;
+
 import javax.annotation.Priority;
 import javax.cache.Cache;
-import javax.cache.annotation.CacheDefaults;
 import javax.cache.annotation.CacheKeyInvocationContext;
 import javax.cache.annotation.CacheRemove;
+import javax.cache.annotation.CacheResolver;
+import javax.cache.annotation.CacheResolverFactory;
 import javax.cache.annotation.GeneratedCacheKey;
 import javax.inject.Inject;
 import javax.interceptor.AroundInvoke;
@@ -42,19 +43,23 @@
     @AroundInvoke
     public Object cache(final InvocationContext ic) throws Throwable
     {
-        final CacheDefaults defaults = helper.findDefaults(ic);
+        final CDIJCacheHelper.MethodMeta methodMeta = helper.findMeta(ic);
 
-        final Method method = ic.getMethod();
-        final CacheRemove cacheRemove = method.getAnnotation(CacheRemove.class);
-        final String cacheName = helper.defaultName(method, defaults, cacheRemove.cacheName());
-        final boolean afterInvocation = cacheRemove.afterInvocation();
+        final String cacheName = methodMeta.getCacheResultCacheName();
 
-        final CacheKeyInvocationContext<CacheRemove> context =
-                new CacheKeyInvocationContextImpl<CacheRemove>(ic, cacheRemove, cacheName, helper.keyParameterIndexes(method));
+        final CacheResolverFactory cacheResolverFactory = methodMeta.getCacheResultResolverFactory();
+        final CacheKeyInvocationContext<CacheRemove> context = new CacheKeyInvocationContextImpl<CacheRemove>(
+                ic, methodMeta.getCacheRemove(), cacheName, methodMeta);
+        final CacheResolver cacheResolver = cacheResolverFactory.getCacheResolver(context);
+        final Cache<Object, Object> cache = cacheResolver.resolveCache(context);
+
+        final GeneratedCacheKey cacheKey = methodMeta.getCacheResultKeyGenerator().generateCacheKey(context);
+        final CacheRemove cacheRemove = methodMeta.getCacheRemove();
+        final boolean afterInvocation = methodMeta.isCacheRemoveAfter();
 
         if (!afterInvocation)
         {
-            doRemove(context, defaults, cacheRemove);
+            cache.remove(cacheKey);
         }
 
         final Object result;
@@ -68,7 +73,7 @@
             {
                 if (helper.isIncluded(t.getClass(), cacheRemove.evictFor(), cacheRemove.noEvictFor()))
                 {
-                    doRemove(context, defaults, cacheRemove);
+                    cache.remove(cacheKey);
                 }
             }
 
@@ -77,16 +82,9 @@
 
         if (afterInvocation)
         {
-            doRemove(context, defaults, cacheRemove);
+            cache.remove(cacheKey);
         }
 
         return result;
     }
-
-    private void doRemove(final CacheKeyInvocationContext<CacheRemove> context, final CacheDefaults defaults, final CacheRemove cacheRemove)
-    {
-        final Cache<Object, Object> cache = helper.cacheResolverFactoryFor(defaults, cacheRemove.cacheResolverFactory()).getCacheResolver(context).resolveCache(context);
-        final GeneratedCacheKey key = helper.cacheKeyGeneratorFor(defaults, cacheRemove.cacheKeyGenerator()).generateCacheKey(context);
-        cache.remove(key);
-    }
 }
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResultInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResultInterceptor.java
index 1b6a653..e58ad34 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResultInterceptor.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResultInterceptor.java
@@ -22,7 +22,6 @@
 import java.lang.reflect.Method;
 import javax.annotation.Priority;
 import javax.cache.Cache;
-import javax.cache.annotation.CacheDefaults;
 import javax.cache.annotation.CacheKeyInvocationContext;
 import javax.cache.annotation.CacheResolver;
 import javax.cache.annotation.CacheResolverFactory;
@@ -44,20 +43,19 @@
     @AroundInvoke
     public Object cache(final InvocationContext ic) throws Throwable
     {
-        final CacheDefaults defaults = helper.findDefaults(ic);
+        final CDIJCacheHelper.MethodMeta methodMeta = helper.findMeta(ic);
 
-        final Method method = ic.getMethod();
-        final CacheResult cacheResult = method.getAnnotation(CacheResult.class);
-        final String cacheName = helper.defaultName(method, defaults, cacheResult.cacheName());
+        final String cacheName = methodMeta.getCacheResultCacheName();
 
+        final CacheResult cacheResult = methodMeta.getCacheResult();
         final CacheKeyInvocationContext<CacheResult> context = new CacheKeyInvocationContextImpl<CacheResult>(
-                ic, cacheResult, cacheName, helper.keyParameterIndexes(method));
+                ic, cacheResult, cacheName, methodMeta);
 
-        final CacheResolverFactory cacheResolverFactory = helper.cacheResolverFactoryFor(defaults, cacheResult.cacheResolverFactory());
+        final CacheResolverFactory cacheResolverFactory = methodMeta.getCacheResultResolverFactory();
         final CacheResolver cacheResolver = cacheResolverFactory.getCacheResolver(context);
         final Cache<Object, Object> cache = cacheResolver.resolveCache(context);
 
-        final GeneratedCacheKey cacheKey = helper.cacheKeyGeneratorFor(defaults, cacheResult.cacheKeyGenerator()).generateCacheKey(context);
+        final GeneratedCacheKey cacheKey = methodMeta.getCacheResultKeyGenerator().generateCacheKey(context);
 
         Cache<Object, Object> exceptionCache = null; // lazily created