001/* 002 * Copyright 2002-2015 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.cache.jcache.interceptor; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Method; 021import java.util.ArrayList; 022import java.util.List; 023import javax.cache.annotation.CacheDefaults; 024import javax.cache.annotation.CacheKeyGenerator; 025import javax.cache.annotation.CacheMethodDetails; 026import javax.cache.annotation.CachePut; 027import javax.cache.annotation.CacheRemove; 028import javax.cache.annotation.CacheRemoveAll; 029import javax.cache.annotation.CacheResolverFactory; 030import javax.cache.annotation.CacheResult; 031 032import org.springframework.cache.interceptor.CacheResolver; 033import org.springframework.cache.interceptor.KeyGenerator; 034import org.springframework.util.StringUtils; 035 036/** 037 * Implementation of the {@link JCacheOperationSource} interface that reads 038 * the JSR-107 {@link CacheResult}, {@link CachePut}, {@link CacheRemove} and 039 * {@link CacheRemoveAll} annotations. 040 * 041 * @author Stephane Nicoll 042 * @since 4.1 043 */ 044public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJCacheOperationSource { 045 046 @Override 047 protected JCacheOperation<?> findCacheOperation(Method method, Class<?> targetType) { 048 CacheResult cacheResult = method.getAnnotation(CacheResult.class); 049 CachePut cachePut = method.getAnnotation(CachePut.class); 050 CacheRemove cacheRemove = method.getAnnotation(CacheRemove.class); 051 CacheRemoveAll cacheRemoveAll = method.getAnnotation(CacheRemoveAll.class); 052 053 int found = countNonNull(cacheResult, cachePut, cacheRemove, cacheRemoveAll); 054 if (found == 0) { 055 return null; 056 } 057 if (found > 1) { 058 throw new IllegalStateException("More than one cache annotation found on '" + method + "'"); 059 } 060 061 CacheDefaults defaults = getCacheDefaults(method, targetType); 062 if (cacheResult != null) { 063 return createCacheResultOperation(method, defaults, cacheResult); 064 } 065 else if (cachePut != null) { 066 return createCachePutOperation(method, defaults, cachePut); 067 } 068 else if (cacheRemove != null) { 069 return createCacheRemoveOperation(method, defaults, cacheRemove); 070 } 071 else { 072 return createCacheRemoveAllOperation(method, defaults, cacheRemoveAll); 073 } 074 } 075 076 protected CacheDefaults getCacheDefaults(Method method, Class<?> targetType) { 077 CacheDefaults annotation = method.getDeclaringClass().getAnnotation(CacheDefaults.class); 078 if (annotation != null) { 079 return annotation; 080 } 081 return targetType.getAnnotation(CacheDefaults.class); 082 } 083 084 protected CacheResultOperation createCacheResultOperation(Method method, CacheDefaults defaults, CacheResult ann) { 085 String cacheName = determineCacheName(method, defaults, ann.cacheName()); 086 CacheResolverFactory cacheResolverFactory = 087 determineCacheResolverFactory(defaults, ann.cacheResolverFactory()); 088 KeyGenerator keyGenerator = determineKeyGenerator(defaults, ann.cacheKeyGenerator()); 089 090 CacheMethodDetails<CacheResult> methodDetails = createMethodDetails(method, ann, cacheName); 091 092 CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails); 093 CacheResolver exceptionCacheResolver = null; 094 final String exceptionCacheName = ann.exceptionCacheName(); 095 if (StringUtils.hasText(exceptionCacheName)) { 096 exceptionCacheResolver = getExceptionCacheResolver(cacheResolverFactory, methodDetails); 097 } 098 099 return new CacheResultOperation(methodDetails, cacheResolver, keyGenerator, exceptionCacheResolver); 100 } 101 102 protected CachePutOperation createCachePutOperation(Method method, CacheDefaults defaults, CachePut ann) { 103 String cacheName = determineCacheName(method, defaults, ann.cacheName()); 104 CacheResolverFactory cacheResolverFactory = 105 determineCacheResolverFactory(defaults, ann.cacheResolverFactory()); 106 KeyGenerator keyGenerator = determineKeyGenerator(defaults, ann.cacheKeyGenerator()); 107 108 CacheMethodDetails<CachePut> methodDetails = createMethodDetails(method, ann, cacheName); 109 CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails); 110 return new CachePutOperation(methodDetails, cacheResolver, keyGenerator); 111 } 112 113 protected CacheRemoveOperation createCacheRemoveOperation(Method method, CacheDefaults defaults, CacheRemove ann) { 114 String cacheName = determineCacheName(method, defaults, ann.cacheName()); 115 CacheResolverFactory cacheResolverFactory = 116 determineCacheResolverFactory(defaults, ann.cacheResolverFactory()); 117 KeyGenerator keyGenerator = determineKeyGenerator(defaults, ann.cacheKeyGenerator()); 118 119 CacheMethodDetails<CacheRemove> methodDetails = createMethodDetails(method, ann, cacheName); 120 CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails); 121 return new CacheRemoveOperation(methodDetails, cacheResolver, keyGenerator); 122 } 123 124 protected CacheRemoveAllOperation createCacheRemoveAllOperation(Method method, CacheDefaults defaults, CacheRemoveAll ann) { 125 String cacheName = determineCacheName(method, defaults, ann.cacheName()); 126 CacheResolverFactory cacheResolverFactory = 127 determineCacheResolverFactory(defaults, ann.cacheResolverFactory()); 128 129 CacheMethodDetails<CacheRemoveAll> methodDetails = createMethodDetails(method, ann, cacheName); 130 CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails); 131 return new CacheRemoveAllOperation(methodDetails, cacheResolver); 132 } 133 134 private <A extends Annotation> CacheMethodDetails<A> createMethodDetails(Method method, A annotation, String cacheName) { 135 return new DefaultCacheMethodDetails<A>(method, annotation, cacheName); 136 } 137 138 protected CacheResolver getCacheResolver(CacheResolverFactory factory, CacheMethodDetails<?> details) { 139 if (factory != null) { 140 javax.cache.annotation.CacheResolver cacheResolver = factory.getCacheResolver(details); 141 return new CacheResolverAdapter(cacheResolver); 142 } 143 else { 144 return getDefaultCacheResolver(); 145 } 146 } 147 148 protected CacheResolver getExceptionCacheResolver(CacheResolverFactory factory, 149 CacheMethodDetails<CacheResult> details) { 150 151 if (factory != null) { 152 javax.cache.annotation.CacheResolver cacheResolver = factory.getExceptionCacheResolver(details); 153 return new CacheResolverAdapter(cacheResolver); 154 } 155 else { 156 return getDefaultExceptionCacheResolver(); 157 } 158 } 159 160 protected CacheResolverFactory determineCacheResolverFactory(CacheDefaults defaults, 161 Class<? extends CacheResolverFactory> candidate) { 162 163 if (CacheResolverFactory.class != candidate) { 164 return getBean(candidate); 165 } 166 else if (defaults != null && CacheResolverFactory.class != defaults.cacheResolverFactory()) { 167 return getBean(defaults.cacheResolverFactory()); 168 } 169 else { 170 return null; 171 } 172 } 173 174 protected KeyGenerator determineKeyGenerator(CacheDefaults defaults, Class<? extends CacheKeyGenerator> candidate) { 175 if (CacheKeyGenerator.class != candidate) { 176 return new KeyGeneratorAdapter(this, getBean(candidate)); 177 } 178 else if (defaults != null && CacheKeyGenerator.class != defaults.cacheKeyGenerator()) { 179 return new KeyGeneratorAdapter(this, getBean(defaults.cacheKeyGenerator())); 180 } 181 else { 182 return getDefaultKeyGenerator(); 183 } 184 } 185 186 protected String determineCacheName(Method method, CacheDefaults defaults, String candidate) { 187 if (StringUtils.hasText(candidate)) { 188 return candidate; 189 } 190 if (defaults != null && StringUtils.hasText(defaults.cacheName())) { 191 return defaults.cacheName(); 192 } 193 return generateDefaultCacheName(method); 194 } 195 196 /** 197 * Generate a default cache name for the specified {@link Method}. 198 * @param method the annotated method 199 * @return the default cache name, according to JSR-107 200 */ 201 protected String generateDefaultCacheName(Method method) { 202 Class<?>[] parameterTypes = method.getParameterTypes(); 203 List<String> parameters = new ArrayList<String>(parameterTypes.length); 204 for (Class<?> parameterType : parameterTypes) { 205 parameters.add(parameterType.getName()); 206 } 207 208 StringBuilder sb = new StringBuilder(method.getDeclaringClass().getName()); 209 sb.append(".").append(method.getName()); 210 sb.append("(").append(StringUtils.collectionToCommaDelimitedString(parameters)).append(")"); 211 return sb.toString(); 212 } 213 214 private int countNonNull(Object... instances) { 215 int result = 0; 216 for (Object instance : instances) { 217 if (instance != null) { 218 result += 1; 219 } 220 } 221 return result; 222 } 223 224 225 /** 226 * Locate or create an instance of the specified cache strategy {@code type}. 227 * @param type the type of the bean to manage 228 * @return the required bean 229 */ 230 protected abstract <T> T getBean(Class<T> type); 231 232 /** 233 * Return the default {@link CacheResolver} if none is set. 234 */ 235 protected abstract CacheResolver getDefaultCacheResolver(); 236 237 /** 238 * Return the default exception {@link CacheResolver} if none is set. 239 */ 240 protected abstract CacheResolver getDefaultExceptionCacheResolver(); 241 242 /** 243 * Return the default {@link KeyGenerator} if none is set. 244 */ 245 protected abstract KeyGenerator getDefaultKeyGenerator(); 246 247}