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