Add JSR-107 cache annotations support
This commit adds support for the JSR-107 cache annotations alongside the Spring's cache annotations, that is @CacheResult, @CachePut, @CacheRemove and @CacheRemoveAll as well as related annotations @CacheDefaults, @CacheKey and @CacheValue. Spring's caching configuration infrastructure detects the presence of the JSR-107 API and Spring's JCache implementation. Both @EnableCaching and the cache namespace are able to configure the required JCache infrastructure when necessary. Both proxy mode and AspectJ mode are supported. As JSR-107 permits the customization of the CacheResolver to use for both regular and exception caches, JCacheConfigurer has been introduced as an extension of CachingConfigurer and permits to define those. If an exception is cached and should be rethrown, it is cloned and the call stack is rewritten so that it matches the calling thread each time. If the exception cannot be cloned, the original exception is returned. Internally, the interceptors uses Spring's caching abstraction by default with an adapter layer when a JSR-107 component needs to be called. This is the case for CacheResolver and CacheKeyGenerator. The implementation uses Spring's CacheManager abstraction behind the scene. The standard annotations can therefore be used against any CacheManager implementation. Issue: SPR-9616
This commit is contained in:
@@ -898,9 +898,10 @@ project("spring-aspects") {
|
||||
optional(project(":spring-aop")) // for @Async support
|
||||
optional(project(":spring-beans")) // for @Configurable support
|
||||
optional(project(":spring-context")) // for @Enable* support
|
||||
optional(project(":spring-context-support")) // for JavaMail support
|
||||
optional(project(":spring-context-support")) // for JavaMail and JSR-107 support
|
||||
optional(project(":spring-orm")) // for JPA exception translation support
|
||||
optional(project(":spring-tx")) // for JPA, @Transactional support
|
||||
optional("javax.cache:cache-api:1.0.0-RC1")
|
||||
testCompile(project(":spring-core")) // for CodeStyleAspect
|
||||
testCompile(project(":spring-test"))
|
||||
testCompile("javax.mail:javax.mail-api:1.4.7")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
|
||||
import org.aspectj.lang.annotation.SuppressAjWarnings;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.cache.interceptor.CacheAspectSupport;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheOperationSource;
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,7 @@ public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
|
||||
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
|
||||
Method method = methodSignature.getMethod();
|
||||
|
||||
Invoker aspectJInvoker = new Invoker() {
|
||||
CacheOperationInvoker aspectJInvoker = new CacheOperationInvoker() {
|
||||
public Object invoke() {
|
||||
return proceed(cachedObject);
|
||||
}
|
||||
|
||||
@@ -47,4 +47,5 @@ public class AspectJCachingConfiguration extends AbstractCachingConfiguration {
|
||||
}
|
||||
return cacheAspect;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
48
spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java
vendored
Normal file
48
spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.aspectj;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.cache.annotation.AbstractCachingConfiguration;
|
||||
import org.springframework.cache.jcache.config.AbstractJCacheConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
|
||||
/**
|
||||
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
|
||||
* to enable AspectJ-based annotation-driven cache management for standard JSR-107
|
||||
* annotations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.annotation.EnableCaching
|
||||
* @see org.springframework.cache.annotation.CachingConfigurationSelector
|
||||
*/
|
||||
@Configuration
|
||||
public class AspectJJCacheConfiguration extends AbstractJCacheConfiguration {
|
||||
|
||||
@Bean(name=AnnotationConfigUtils.JCACHE_ASPECT_BEAN_NAME)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JCacheCacheAspect cacheAspect() {
|
||||
JCacheCacheAspect cacheAspect = JCacheCacheAspect.aspectOf();
|
||||
cacheAspect.setCacheOperationSource(cacheOperationSource());
|
||||
return cacheAspect;
|
||||
}
|
||||
|
||||
}
|
||||
112
spring-aspects/src/main/java/org/springframework/cache/aspectj/JCacheCacheAspect.aj
vendored
Normal file
112
spring-aspects/src/main/java/org/springframework/cache/aspectj/JCacheCacheAspect.aj
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.cache.annotation.CachePut;
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.aspectj.lang.annotation.SuppressAjWarnings;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.interceptor.JCacheAspectSupport;
|
||||
|
||||
/**
|
||||
* Concrete AspectJ cache aspect using JSR-107 standard annotations.
|
||||
*
|
||||
* <p>When using this aspect, you <i>must</i> annotate the implementation class (and/or
|
||||
* methods within that class), <i>not</i> the interface (if any) that the class
|
||||
* implements. AspectJ follows Java's rule that annotations on interfaces are <i>not</i>
|
||||
* inherited.
|
||||
*
|
||||
* <p>Any method may be annotated (regardless of visibility). Annotating non-public
|
||||
* methods directly is the only way to get caching demarcation for the execution of
|
||||
* such operations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public aspect JCacheCacheAspect extends JCacheAspectSupport {
|
||||
|
||||
@SuppressAjWarnings("adviceDidNotMatch")
|
||||
Object around(final Object cachedObject) : cacheMethodExecution(cachedObject) {
|
||||
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
|
||||
Method method = methodSignature.getMethod();
|
||||
|
||||
CacheOperationInvoker aspectJInvoker = new CacheOperationInvoker() {
|
||||
public Object invoke() {
|
||||
try {
|
||||
return proceed(cachedObject);
|
||||
} catch (Throwable ex) {
|
||||
throw new ThrowableWrapper(ex);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
return execute(aspectJInvoker, thisJoinPoint.getTarget(), method, thisJoinPoint.getArgs());
|
||||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper th) {
|
||||
if (th.getOriginal() instanceof RuntimeException) {
|
||||
throw (RuntimeException) th.getOriginal();
|
||||
}
|
||||
throw th; // Lose original checked exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition of pointcut: matched join points will have JSR-107
|
||||
* cache management applied.
|
||||
*/
|
||||
protected pointcut cacheMethodExecution(Object cachedObject) :
|
||||
(executionOfCacheResultMethod()
|
||||
|| executionOfCachePutMethod()
|
||||
|| executionOfCacheRemoveMethod()
|
||||
|| executionOfCacheRemoveAllMethod())
|
||||
&& this(cachedObject);
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with the @{@link CacheResult} annotation.
|
||||
*/
|
||||
private pointcut executionOfCacheResultMethod() :
|
||||
execution(@CacheResult * *(..));
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with the @{@link CachePut} annotation.
|
||||
*/
|
||||
private pointcut executionOfCachePutMethod() :
|
||||
execution(@CachePut * *(..));
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with the @{@link CacheRemove} annotation.
|
||||
*/
|
||||
private pointcut executionOfCacheRemoveMethod() :
|
||||
execution(@CacheRemove * *(..));
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with the @{@link CacheRemoveAll} annotation.
|
||||
*/
|
||||
private pointcut executionOfCacheRemoveAllMethod() :
|
||||
execution(@CacheRemoveAll * *(..));
|
||||
|
||||
}
|
||||
@@ -71,4 +71,5 @@ public class AspectJAnnotationTests extends AbstractAnnotationTests {
|
||||
assertSame(r3, primary.get(o1).get());
|
||||
assertSame(r4, secondary.get(o1).get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
72
spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJJcacheJavaConfigTests.java
vendored
Normal file
72
spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJJcacheJavaConfigTests.java
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.aspectj;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.cache.config.AnnotatedJCacheableService;
|
||||
import org.springframework.cache.jcache.config.AbstractJCacheAnnotationTests;
|
||||
import org.springframework.cache.jcache.config.JCacheableService;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class AspectJJcacheJavaConfigTests extends AbstractJCacheAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return new AnnotationConfigApplicationContext(EnableCachingConfig.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching(mode = AdviceMode.ASPECTJ)
|
||||
public static class EnableCachingConfig {
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
SimpleCacheManager cm = new SimpleCacheManager();
|
||||
cm.setCaches(Arrays.asList(
|
||||
defaultCache(),
|
||||
new ConcurrentMapCache("primary"),
|
||||
new ConcurrentMapCache("secondary"),
|
||||
new ConcurrentMapCache("exception")));
|
||||
return cm;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AnnotatedJCacheableService cacheableService() {
|
||||
return new AnnotatedJCacheableService(defaultCache());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Cache defaultCache() {
|
||||
return new ConcurrentMapCache("default");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config.aspectj;
|
||||
|
||||
import org.springframework.cache.jcache.config.AbstractJCacheAnnotationTests;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class AspectJJcacheNamespaceConfigTests extends AbstractJCacheAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/config/annotation-jcache-aspectj.xml");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -200,4 +200,5 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
|
||||
arg1.setId(Long.MIN_VALUE);
|
||||
return arg1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
201
spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java
vendored
Normal file
201
spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.config;
|
||||
|
||||
import javax.cache.annotation.CacheDefaults;
|
||||
import javax.cache.annotation.CacheKey;
|
||||
import javax.cache.annotation.CachePut;
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
import javax.cache.annotation.CacheValue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.jcache.config.JCacheableService;
|
||||
import org.springframework.cache.jcache.interceptor.SimpleGeneratedCacheKey;
|
||||
import org.springframework.cache.jcache.support.TestableCacheKeyGenerator;
|
||||
import org.springframework.cache.jcache.support.TestableCacheResolverFactory;
|
||||
|
||||
/**
|
||||
* Repository sample with a @CacheDefaults annotation
|
||||
*
|
||||
* <p>Note: copy/pasted from its original compilation because it needs to be
|
||||
* processed by the AspectJ compiler to wave the required aspects.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@CacheDefaults(cacheName = "default")
|
||||
public class AnnotatedJCacheableService implements JCacheableService<Long> {
|
||||
|
||||
private final AtomicLong counter = new AtomicLong();
|
||||
private final AtomicLong exceptionCounter = new AtomicLong();
|
||||
private final Cache defaultCache;
|
||||
|
||||
public AnnotatedJCacheableService(Cache defaultCache) {
|
||||
this.defaultCache = defaultCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult
|
||||
public Long cache(String id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(exceptionCacheName = "exception", nonCachedExceptions = NullPointerException.class)
|
||||
public Long cacheWithException(@CacheKey String id, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
return 0L; // Never reached
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(skipGet = true)
|
||||
public Long cacheAlwaysInvoke(String id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult
|
||||
public Long cacheWithPartialKey(@CacheKey String id, boolean notUsed) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(cacheResolverFactory = TestableCacheResolverFactory.class)
|
||||
public Long cacheWithCustomCacheResolver(String id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class)
|
||||
public Long cacheWithCustomKeyGenerator(String id, String anotherId) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut
|
||||
public void put(String id, @CacheValue Object value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut(cacheFor = UnsupportedOperationException.class)
|
||||
public void putWithException(@CacheKey String id, @CacheValue Object value, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut(afterInvocation = false)
|
||||
public void earlyPut(String id, @CacheValue Object value) {
|
||||
SimpleGeneratedCacheKey key = new SimpleGeneratedCacheKey(id);
|
||||
Cache.ValueWrapper valueWrapper = defaultCache.get(key);
|
||||
if (valueWrapper == null) {
|
||||
throw new AssertionError("Excepted value to be put in cache with key " + key);
|
||||
}
|
||||
Object actual = valueWrapper.get();
|
||||
if (value != actual) { // instance check on purpose
|
||||
throw new AssertionError("Wrong value set in cache with key " + key + ". " +
|
||||
"Expected=" + value + ", but got=" + actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut(afterInvocation = false)
|
||||
public void earlyPutWithException(@CacheKey String id, @CacheValue Object value, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove
|
||||
public void remove(String id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove(noEvictFor = NullPointerException.class)
|
||||
public void removeWithException(@CacheKey String id, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove(afterInvocation = false)
|
||||
public void earlyRemove(String id) {
|
||||
SimpleGeneratedCacheKey key = new SimpleGeneratedCacheKey(id);
|
||||
Cache.ValueWrapper valueWrapper = defaultCache.get(key);
|
||||
if (valueWrapper != null) {
|
||||
throw new AssertionError("Value with key " + key + " expected to be already remove from cache");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove(afterInvocation = false, evictFor = UnsupportedOperationException.class)
|
||||
public void earlyRemoveWithException(@CacheKey String id, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll
|
||||
public void removeAll() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll(noEvictFor = NullPointerException.class)
|
||||
public void removeAllWithException(boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll(afterInvocation = false)
|
||||
public void earlyRemoveAll() {
|
||||
ConcurrentHashMap<?, ?> nativeCache = (ConcurrentHashMap<?, ?>) defaultCache.getNativeCache();
|
||||
if (!nativeCache.isEmpty()) {
|
||||
throw new AssertionError("Cache was expected to be empty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll(afterInvocation = false, evictFor = UnsupportedOperationException.class)
|
||||
public void earlyRemoveAllWithException(boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void noAnnotation() {
|
||||
}
|
||||
|
||||
@CacheRemove
|
||||
@CacheRemoveAll
|
||||
public void multiAnnotations() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long exceptionInvocations() {
|
||||
return exceptionCounter.get();
|
||||
}
|
||||
|
||||
private void throwException(boolean matchFilter) {
|
||||
long count = exceptionCounter.getAndIncrement();
|
||||
if (matchFilter) {
|
||||
throw new UnsupportedOperationException("Expected exception (" + count + ")");
|
||||
}
|
||||
else {
|
||||
throw new NullPointerException("Expected exception (" + count + ")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,4 +82,5 @@ public interface CacheableService<T> {
|
||||
T multiUpdate(Object arg1);
|
||||
|
||||
TestEntity putRefersToResult(TestEntity arg1);
|
||||
|
||||
}
|
||||
|
||||
@@ -208,4 +208,5 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
||||
arg1.setId(Long.MIN_VALUE);
|
||||
return arg1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,4 +43,5 @@ final class SomeCustomKeyGenerator implements KeyGenerator {
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.springframework.util.ObjectUtils;
|
||||
/**
|
||||
* Simple test entity for use with caching tests.
|
||||
*
|
||||
* @author Michael Pl<EFBFBD>d
|
||||
* @author Michael Plod
|
||||
*/
|
||||
public class TestEntity {
|
||||
|
||||
@@ -53,4 +53,5 @@ public class TestEntity {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
36
spring-aspects/src/test/java/org/springframework/cache/config/annotation-jcache-aspectj.xml
vendored
Normal file
36
spring-aspects/src/test/java/org/springframework/cache/config/annotation-jcache-aspectj.xml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
|
||||
|
||||
<cache:annotation-driven mode="aspectj"/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
|
||||
<property name="caches">
|
||||
<set>
|
||||
<ref bean="defaultCache"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="primary"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="secondary"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="exception"/>
|
||||
</bean>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
</bean>
|
||||
|
||||
<bean id="cacheableService" class="org.springframework.cache.config.AnnotatedJCacheableService">
|
||||
<constructor-arg ref="defaultCache"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.cache.annotation.AbstractCachingConfiguration;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
|
||||
import org.springframework.cache.jcache.interceptor.JCacheOperationSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
|
||||
/**
|
||||
* Abstract JSR-107 specific {@code @Configuration} class providing common
|
||||
* structure for enabling JSR-107 annotation-driven cache management capability.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see JCacheConfigurer
|
||||
*/
|
||||
@Configuration
|
||||
public class AbstractJCacheConfiguration extends AbstractCachingConfiguration<JCacheConfigurer> {
|
||||
|
||||
protected CacheResolver cacheResolver;
|
||||
protected CacheResolver exceptionCacheResolver;
|
||||
|
||||
@Override
|
||||
protected void useCachingConfigurer(JCacheConfigurer config) {
|
||||
super.useCachingConfigurer(config);
|
||||
this.cacheResolver = config.cacheResolver();
|
||||
this.exceptionCacheResolver = config.exceptionCacheResolver();
|
||||
}
|
||||
|
||||
@Bean(name = "jCacheOperationSource")
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JCacheOperationSource cacheOperationSource() {
|
||||
DefaultJCacheOperationSource source = new DefaultJCacheOperationSource();
|
||||
if (this.cacheManager != null) {
|
||||
source.setCacheManager(cacheManager);
|
||||
}
|
||||
if (keyGenerator != null) {
|
||||
source.setKeyGenerator(keyGenerator);
|
||||
}
|
||||
if (this.cacheResolver != null) {
|
||||
source.setCacheResolver(cacheResolver);
|
||||
}
|
||||
if (this.exceptionCacheResolver != null) {
|
||||
source.setExceptionCacheResolver(exceptionCacheResolver);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
import org.springframework.cache.annotation.CachingConfigurer;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
|
||||
/**
|
||||
* Extension of {@link CachingConfigurer} for the JSR-107 implementation.
|
||||
|
||||
* <p>To be implemented by classes annotated with @{@link org.springframework.cache.annotation.EnableCaching}
|
||||
* that wish or need to specify explicitly the {@link CacheResolver} bean(s) to be used for
|
||||
* annotation-driven cache management.
|
||||
*
|
||||
* <p>See @{@link org.springframework.cache.annotation.EnableCaching} for general examples and
|
||||
* context; see {@link #cacheResolver()} and {@link #exceptionCacheResolver()} for detailed
|
||||
* instructions.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see CachingConfigurer
|
||||
* @see org.springframework.cache.annotation.EnableCaching
|
||||
*/
|
||||
public interface JCacheConfigurer extends CachingConfigurer {
|
||||
|
||||
/**
|
||||
* Return the {@link CacheResolver} bean to use to resolve regular caches for
|
||||
* annotation-driven cache management. Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements JCacheConfigurer {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheResolver cacheResolver() {
|
||||
* // configure and return CacheResolver instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See {@link org.springframework.cache.annotation.EnableCaching} for more complete examples.
|
||||
*/
|
||||
CacheResolver cacheResolver();
|
||||
|
||||
/**
|
||||
* Return the {@link CacheResolver} bean to use to resolve exception caches for
|
||||
* annotation-driven cache management. Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements JCacheConfigurer {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheResolver exceptionCacheResolver() {
|
||||
* // configure and return CacheResolver instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See {@link org.springframework.cache.annotation.EnableCaching} for more complete examples.
|
||||
*/
|
||||
CacheResolver exceptionCacheResolver();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.cache.jcache.interceptor.BeanFactoryJCacheOperationSourceAdvisor;
|
||||
import org.springframework.cache.jcache.interceptor.JCacheInterceptor;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
|
||||
/**
|
||||
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
|
||||
* to enable proxy-based annotation-driven JSR-107 cache management.
|
||||
*
|
||||
* <p>Can safely be used alongside Spring's caching support.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.annotation.EnableCaching
|
||||
* @see org.springframework.cache.annotation.CachingConfigurationSelector
|
||||
*/
|
||||
@Configuration
|
||||
public class ProxyJCacheConfiguration extends AbstractJCacheConfiguration {
|
||||
|
||||
@Bean(name = AnnotationConfigUtils.JCACHE_ADVISOR_BEAN_NAME)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public BeanFactoryJCacheOperationSourceAdvisor cacheAdvisor() {
|
||||
BeanFactoryJCacheOperationSourceAdvisor advisor =
|
||||
new BeanFactoryJCacheOperationSourceAdvisor();
|
||||
advisor.setCacheOperationSource(cacheOperationSource());
|
||||
advisor.setAdvice(cacheInterceptor());
|
||||
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
|
||||
return advisor;
|
||||
}
|
||||
|
||||
|
||||
@Bean(name = "jCacheInterceptor")
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JCacheInterceptor cacheInterceptor() {
|
||||
JCacheInterceptor interceptor = new JCacheInterceptor();
|
||||
interceptor.setCacheOperationSource(cacheOperationSource());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Support package for declarative JSR-107 caching configuration. Used
|
||||
* by the regular Spring's caching configuration when it detects the
|
||||
* JSR-107 API and Spring's JCache implementation.
|
||||
* <p>Provide an extension of the {@code CachingConfigurer} that exposes
|
||||
* the exception cache resolver to use, see {@code JCacheConfigurer}.
|
||||
*/
|
||||
package org.springframework.cache.jcache.config;
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.BaseCacheOperation;
|
||||
|
||||
/**
|
||||
* A base interceptor for JSR-107 cache annotations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractCacheInterceptor<O extends BaseCacheOperation<A>, A extends Annotation>
|
||||
implements Serializable {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected abstract Object invoke(CacheOperationInvocationContext<O> context,
|
||||
CacheOperationInvoker invoker) throws Throwable;
|
||||
|
||||
/**
|
||||
* Resolve the cache to use.
|
||||
* @param context the invocation context
|
||||
* @return the cache to use (never null)
|
||||
*/
|
||||
protected Cache resolveCache(CacheOperationInvocationContext<O> context) {
|
||||
Collection<? extends Cache> caches = context.getOperation().getCacheResolver().resolveCaches(context);
|
||||
Cache cache = extractFrom(caches);
|
||||
if (cache == null) {
|
||||
throw new IllegalStateException("Cache could not have been resolved for " + context.getOperation());
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the collection of caches in a single expected element.
|
||||
* <p>Throw an {@link IllegalStateException} if the collection holds more than one element
|
||||
* @return the singe element or {@code null} if the collection is empty
|
||||
*/
|
||||
static Cache extractFrom(Collection<? extends Cache> caches) {
|
||||
if (caches == null || caches.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
else if (caches.size() == 1) {
|
||||
return caches.iterator().next();
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unsupported cache resolution result "
|
||||
+ caches + " JSR-107 only supports a single cache.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cache.interceptor.MethodCacheKey;
|
||||
import org.springframework.cache.jcache.model.JCacheOperation;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link JCacheOperationSource} that caches
|
||||
* attributes for methods and implements a fallback policy: 1. specific
|
||||
* target method; 2. declaring method.
|
||||
*
|
||||
* <p>This implementation caches attributes by method after they are
|
||||
* first used.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource
|
||||
*/
|
||||
public abstract class AbstractFallbackJCacheOperationSource
|
||||
implements JCacheOperationSource {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Map<Object, JCacheOperation<?>> cache =
|
||||
new ConcurrentHashMap<Object, JCacheOperation<?>>(1024);
|
||||
|
||||
@Override
|
||||
public JCacheOperation<?> getCacheOperation(Method method, Class<?> targetClass) {
|
||||
// First, see if we have a cached value.
|
||||
Object cacheKey = new MethodCacheKey(method, targetClass);
|
||||
JCacheOperation<?> cached = this.cache.get(cacheKey);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
else {
|
||||
JCacheOperation<?> operation = computeCacheOperations(method, targetClass);
|
||||
if (operation != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding cacheable method '" + method.getName()
|
||||
+ "' with operation: " + operation);
|
||||
}
|
||||
this.cache.put(cacheKey, operation);
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
}
|
||||
|
||||
private JCacheOperation<?> computeCacheOperations(Method method, Class<?> targetClass) {
|
||||
// Don't allow no-public methods as required.
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||
// If we are dealing with method with generic parameters, find the original method.
|
||||
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
|
||||
// First try is the method in the target class.
|
||||
JCacheOperation<?> operation = findCacheOperation(specificMethod, targetClass);
|
||||
if (operation != null) {
|
||||
return operation;
|
||||
}
|
||||
if (specificMethod != method) {
|
||||
// Fall back is to look at the original method.
|
||||
operation = findCacheOperation(method, targetClass);
|
||||
if (operation != null) {
|
||||
return operation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this to return the caching operation
|
||||
* for the given method, if any.
|
||||
* @param method the method to retrieve the operation for
|
||||
* @param targetType the target class
|
||||
* @return the cache operation associated with this method
|
||||
* (or {@code null} if none)
|
||||
*/
|
||||
protected abstract JCacheOperation<?> findCacheOperation(Method method, Class<?> targetType);
|
||||
|
||||
/**
|
||||
* Should only public methods be allowed to have caching semantics?
|
||||
* <p>The default implementation returns {@code false}.
|
||||
*/
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.jcache.model.BaseKeyCacheOperation;
|
||||
|
||||
/**
|
||||
* A base interceptor for JSR-107 key-based cache annotations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractKeyCacheInterceptor<O extends BaseKeyCacheOperation<A>, A extends Annotation>
|
||||
extends AbstractCacheInterceptor<O, A> {
|
||||
|
||||
/**
|
||||
* Generate a key for the specified invocation.
|
||||
* @param context the context of the invocation
|
||||
* @return the key to use
|
||||
*/
|
||||
protected Object generateKey(CacheOperationInvocationContext<O> context) {
|
||||
KeyGenerator keyGenerator = context.getOperation().getKeyGenerator();
|
||||
Object key = keyGenerator.generate(context.getTarget(), context.getMethod(), context.getArgs());
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Computed cache key " + key + " for operation " + context.getOperation());
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link CacheKeyInvocationContext} based on the specified invocation.
|
||||
* @param context the context of the invocation.
|
||||
* @return the related {@code CacheKeyInvocationContext}
|
||||
*/
|
||||
protected CacheKeyInvocationContext<A> createCacheKeyInvocationContext(
|
||||
CacheOperationInvocationContext<O> context) {
|
||||
return new DefaultCacheKeyInvocationContext<A>(context.getOperation(), context.getTarget(), context.getArgs());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.cache.annotation.CacheDefaults;
|
||||
import javax.cache.annotation.CacheKeyGenerator;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
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 org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.jcache.model.CachePutOperation;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveAllOperation;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveOperation;
|
||||
import org.springframework.cache.jcache.model.CacheResultOperation;
|
||||
import org.springframework.cache.jcache.model.DefaultCacheMethodDetails;
|
||||
import org.springframework.cache.jcache.model.JCacheOperation;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link JCacheOperationSource} interface that reads
|
||||
* the JSR-107 {@link CacheResult}, {@link CachePut}, {@link CacheRemove} and
|
||||
* {@link CacheRemoveAll} annotations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class AnnotationCacheOperationSource
|
||||
extends AbstractFallbackJCacheOperationSource {
|
||||
|
||||
/**
|
||||
* Locate or create an instance of the specified {@code type}.
|
||||
* @param type the type of the bean to manage
|
||||
* @return the required bean
|
||||
*/
|
||||
protected abstract <T> T getBean(Class<T> type);
|
||||
|
||||
/**
|
||||
* Return the default {@link CacheResolver} if none is set.
|
||||
*/
|
||||
protected abstract CacheResolver getDefaultCacheResolver();
|
||||
|
||||
/**
|
||||
* Return the default exception {@link CacheResolver} if none is set.
|
||||
*/
|
||||
protected abstract CacheResolver getDefaultExceptionCacheResolver();
|
||||
|
||||
/**
|
||||
* Return the default {@link KeyGenerator} if none is set.
|
||||
*/
|
||||
protected abstract KeyGenerator getDefaultKeyGenerator();
|
||||
|
||||
|
||||
@Override
|
||||
protected JCacheOperation<?> findCacheOperation(Method method, Class<?> targetType) {
|
||||
CacheResult cacheResult = method.getAnnotation(CacheResult.class);
|
||||
CachePut cachePut = method.getAnnotation(CachePut.class);
|
||||
CacheRemove cacheRemove = method.getAnnotation(CacheRemove.class);
|
||||
CacheRemoveAll cacheRemoveAll = method.getAnnotation(CacheRemoveAll.class);
|
||||
int found = countNonNull(cacheResult, cachePut, cacheRemove, cacheRemoveAll);
|
||||
if (found == 0) {
|
||||
return null;
|
||||
}
|
||||
if (found > 1) {
|
||||
throw new IllegalStateException("More than one cache annotation found on '" + method + "'");
|
||||
}
|
||||
CacheDefaults defaults = getCacheDefaults(method, targetType);
|
||||
|
||||
if (cacheResult != null) {
|
||||
return createCacheResultOperation(method, defaults, cacheResult);
|
||||
}
|
||||
else if (cachePut != null) {
|
||||
return createCachePutOperation(method, defaults, cachePut);
|
||||
}
|
||||
else if (cacheRemove != null) {
|
||||
return createCacheRemoveOperation(method, defaults, cacheRemove);
|
||||
}
|
||||
else {
|
||||
return createCacheRemoveAllOperation(method, defaults, cacheRemoveAll);
|
||||
}
|
||||
}
|
||||
|
||||
protected CacheDefaults getCacheDefaults(Method method, Class<?> targetType) {
|
||||
CacheDefaults annotation = method.getDeclaringClass().getAnnotation(CacheDefaults.class);
|
||||
if (annotation != null) {
|
||||
return annotation;
|
||||
}
|
||||
return targetType.getAnnotation(CacheDefaults.class);
|
||||
}
|
||||
|
||||
|
||||
protected CacheResultOperation createCacheResultOperation(Method method, CacheDefaults defaults,
|
||||
CacheResult ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
KeyGenerator keyGenerator = determineKeyGenerator(defaults, ann.cacheKeyGenerator());
|
||||
|
||||
CacheMethodDetails<CacheResult> methodDetails = createMethodDetails(method, ann, cacheName);
|
||||
|
||||
CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails);
|
||||
CacheResolver exceptionCacheResolver = null;
|
||||
final String exceptionCacheName = ann.exceptionCacheName();
|
||||
if (StringUtils.hasText(exceptionCacheName)) {
|
||||
exceptionCacheResolver = getExceptionCacheResolver(cacheResolverFactory, methodDetails);
|
||||
}
|
||||
|
||||
return new CacheResultOperation(methodDetails, cacheResolver, keyGenerator, exceptionCacheResolver);
|
||||
}
|
||||
|
||||
protected CachePutOperation createCachePutOperation(Method method, CacheDefaults defaults,
|
||||
CachePut ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
KeyGenerator keyGenerator = determineKeyGenerator(defaults, ann.cacheKeyGenerator());
|
||||
|
||||
CacheMethodDetails<CachePut> methodDetails = createMethodDetails(method, ann, cacheName);
|
||||
|
||||
CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails);
|
||||
|
||||
return new CachePutOperation(methodDetails, cacheResolver, keyGenerator);
|
||||
}
|
||||
|
||||
protected CacheRemoveOperation createCacheRemoveOperation(Method method, CacheDefaults defaults,
|
||||
CacheRemove ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
KeyGenerator keyGenerator = determineKeyGenerator(defaults, ann.cacheKeyGenerator());
|
||||
|
||||
CacheMethodDetails<CacheRemove> methodDetails = createMethodDetails(method, ann, cacheName);
|
||||
|
||||
CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails);
|
||||
|
||||
return new CacheRemoveOperation(methodDetails, cacheResolver, keyGenerator);
|
||||
}
|
||||
|
||||
protected CacheRemoveAllOperation createCacheRemoveAllOperation(Method method, CacheDefaults defaults,
|
||||
CacheRemoveAll ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
|
||||
CacheMethodDetails<CacheRemoveAll> methodDetails = createMethodDetails(method, ann, cacheName);
|
||||
|
||||
CacheResolver cacheResolver = getCacheResolver(cacheResolverFactory, methodDetails);
|
||||
|
||||
return new CacheRemoveAllOperation(methodDetails, cacheResolver);
|
||||
}
|
||||
|
||||
private <A extends Annotation> CacheMethodDetails<A> createMethodDetails(
|
||||
Method method, A annotation, String cacheName) {
|
||||
return new DefaultCacheMethodDetails<A>(method, annotation, cacheName);
|
||||
}
|
||||
|
||||
protected CacheResolver getCacheResolver(CacheResolverFactory factory, CacheMethodDetails<?> details) {
|
||||
if (factory != null) {
|
||||
javax.cache.annotation.CacheResolver cacheResolver = factory.getCacheResolver(details);
|
||||
return new CacheResolverAdapter(cacheResolver);
|
||||
}
|
||||
else {
|
||||
return getDefaultCacheResolver();
|
||||
}
|
||||
}
|
||||
|
||||
protected CacheResolver getExceptionCacheResolver(CacheResolverFactory factory,
|
||||
CacheMethodDetails<CacheResult> details) {
|
||||
if (factory != null) {
|
||||
javax.cache.annotation.CacheResolver cacheResolver = factory.getExceptionCacheResolver(details);
|
||||
return new CacheResolverAdapter(cacheResolver);
|
||||
}
|
||||
else {
|
||||
return getDefaultExceptionCacheResolver();
|
||||
}
|
||||
}
|
||||
|
||||
protected CacheResolverFactory determineCacheResolverFactory(CacheDefaults defaults,
|
||||
Class<? extends CacheResolverFactory> candidate) {
|
||||
if (!CacheResolverFactory.class.equals(candidate)) {
|
||||
return getBean(candidate);
|
||||
}
|
||||
else if (defaults != null && !CacheResolverFactory.class.equals(defaults.cacheResolverFactory())) {
|
||||
return getBean(defaults.cacheResolverFactory());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected KeyGenerator determineKeyGenerator(CacheDefaults defaults,
|
||||
Class<? extends CacheKeyGenerator> candidate) {
|
||||
if (!CacheKeyGenerator.class.equals(candidate)) {
|
||||
return new KeyGeneratorAdapter(this, getBean(candidate));
|
||||
}
|
||||
else if (defaults != null && !CacheKeyGenerator.class.equals(defaults.cacheKeyGenerator())) {
|
||||
return new KeyGeneratorAdapter(this, getBean(defaults.cacheKeyGenerator()));
|
||||
}
|
||||
else {
|
||||
return getDefaultKeyGenerator();
|
||||
}
|
||||
}
|
||||
|
||||
protected String determineCacheName(Method method, CacheDefaults defaults, String candidate) {
|
||||
if (StringUtils.hasText(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
if (defaults != null && StringUtils.hasText(defaults.cacheName())) {
|
||||
return defaults.cacheName();
|
||||
}
|
||||
return generateDefaultCacheName(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a default cache name for the specified {@link Method}.
|
||||
*
|
||||
* @param method the annotated method
|
||||
* @return the default cache name, according to JSR-107
|
||||
*/
|
||||
protected String generateDefaultCacheName(Method method) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
List<String> parameters = new ArrayList<String>();
|
||||
for (Class<?> parameterType : parameterTypes) {
|
||||
parameters.add(parameterType.getName());
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(method.getDeclaringClass().getName())
|
||||
.append(".")
|
||||
.append(method.getName())
|
||||
.append("(")
|
||||
.append(StringUtils.collectionToCommaDelimitedString(parameters))
|
||||
.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private int countNonNull(Object... instances) {
|
||||
int result = 0;
|
||||
for (Object o : instances) {
|
||||
if (o != null) {
|
||||
result += 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Advisor driven by a {@link JCacheOperationSource}, used to include a
|
||||
* cache advice bean for methods that are cacheable.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
|
||||
|
||||
private JCacheOperationSource cacheOperationSource;
|
||||
|
||||
private final JCacheOperationSourcePointcut pointcut = new JCacheOperationSourcePointcut() {
|
||||
@Override
|
||||
protected JCacheOperationSource getCacheOperationSource() {
|
||||
return cacheOperationSource;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the cache operation attribute source which is used to find cache
|
||||
* attributes. This should usually be identical to the source reference
|
||||
* set on the cache interceptor itself.
|
||||
*/
|
||||
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
|
||||
this.cacheOperationSource = cacheOperationSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link org.springframework.aop.ClassFilter} to use for this pointcut.
|
||||
* Default is {@link org.springframework.aop.ClassFilter#TRUE}.
|
||||
*/
|
||||
public void setClassFilter(ClassFilter classFilter) {
|
||||
this.pointcut.setClassFilter(classFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
import javax.cache.annotation.CachePut;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.CachePutOperation;
|
||||
|
||||
/**
|
||||
* Intercept methods annotated with {@link CachePut}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CachePutInterceptor extends AbstractKeyCacheInterceptor<CachePutOperation, CachePut> {
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CachePutOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
CacheKeyInvocationContext<CachePut> invocationContext = createCacheKeyInvocationContext(context);
|
||||
CachePutOperation operation = context.getOperation();
|
||||
|
||||
final boolean earlyPut = operation.isEarlyPut();
|
||||
final Object value = invocationContext.getValueParameter().getValue();
|
||||
|
||||
if (earlyPut) {
|
||||
cacheValue(context, value);
|
||||
}
|
||||
|
||||
try {
|
||||
Object result = invoker.invoke();
|
||||
if (!earlyPut) {
|
||||
cacheValue(context, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper t) {
|
||||
Throwable ex = t.getOriginal();
|
||||
if (!earlyPut && operation.getExceptionTypeFilter().match(ex.getClass())) {
|
||||
cacheValue(context, value);
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
protected void cacheValue(CacheOperationInvocationContext<CachePutOperation> context, Object value) {
|
||||
Object key = generateKey(context);
|
||||
Cache cache = resolveCache(context);
|
||||
cache.put(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveAllOperation;
|
||||
|
||||
/**
|
||||
* Intercept methods annotated with {@link CacheRemoveAll}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CacheRemoveAllInterceptor
|
||||
extends AbstractCacheInterceptor<CacheRemoveAllOperation, CacheRemoveAll> {
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheRemoveAllOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
CacheRemoveAllOperation operation = context.getOperation();
|
||||
|
||||
final boolean earlyRemove = operation.isEarlyRemove();
|
||||
|
||||
if (earlyRemove) {
|
||||
removeAll(context);
|
||||
}
|
||||
|
||||
try {
|
||||
Object result = invoker.invoke();
|
||||
if (!earlyRemove) {
|
||||
removeAll(context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper t) {
|
||||
Throwable ex = t.getOriginal();
|
||||
if (!earlyRemove && operation.getExceptionTypeFilter().match(ex.getClass())) {
|
||||
removeAll(context);
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeAll(CacheOperationInvocationContext<CacheRemoveAllOperation> context) {
|
||||
Cache cache = resolveCache(context);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invalidating entire cache '" + cache.getName() + "' for operation "
|
||||
+ context.getOperation());
|
||||
}
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveOperation;
|
||||
|
||||
/**
|
||||
* Intercept methods annotated with {@link CacheRemove}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CacheRemoveEntryInterceptor extends AbstractKeyCacheInterceptor<CacheRemoveOperation, CacheRemove> {
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheRemoveOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
CacheRemoveOperation operation = context.getOperation();
|
||||
|
||||
final boolean earlyRemove = operation.isEarlyRemove();
|
||||
|
||||
if (earlyRemove) {
|
||||
removeValue(context);
|
||||
}
|
||||
|
||||
try {
|
||||
Object result = invoker.invoke();
|
||||
if (!earlyRemove) {
|
||||
removeValue(context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper t) {
|
||||
Throwable ex = t.getOriginal();
|
||||
if (!earlyRemove && operation.getExceptionTypeFilter().match(ex.getClass())) {
|
||||
removeValue(context);
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeValue(CacheOperationInvocationContext<CacheRemoveOperation> context) {
|
||||
Object key = generateKey(context);
|
||||
Cache cache = resolveCache(context);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invalidating key [" + key + "] on cache '" + cache.getName()
|
||||
+ "' for operation " + context.getOperation());
|
||||
}
|
||||
cache.evict(key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationContext;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.jcache.JCacheCache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Spring's {@link CacheResolver} implementation that delegates to a standard
|
||||
* JSR-107 {@link javax.cache.annotation.CacheResolver}.
|
||||
* <p>Used internally to invoke user-based JSR-107 cache resolvers.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class CacheResolverAdapter implements CacheResolver {
|
||||
|
||||
private final javax.cache.annotation.CacheResolver target;
|
||||
|
||||
/**
|
||||
* Create a new instance with the JSR-107 cache resolver to invoke.
|
||||
*/
|
||||
public CacheResolverAdapter(javax.cache.annotation.CacheResolver target) {
|
||||
Assert.notNull(target, "JSR-107 cache resolver must be set.");
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying {@link javax.cache.annotation.CacheResolver} that this
|
||||
* instance is using.
|
||||
*/
|
||||
protected javax.cache.annotation.CacheResolver getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
if (!(context instanceof CacheInvocationContext<?>)) {
|
||||
throw new IllegalStateException("Unexpected context " + context);
|
||||
}
|
||||
CacheInvocationContext<?> cacheInvocationContext = (CacheInvocationContext<?>) context;
|
||||
javax.cache.Cache<Object, Object> cache = target.resolveCache(cacheInvocationContext);
|
||||
Assert.notNull(cache, "Cannot resolve cache for '" + context + "' using '" + target + "'");
|
||||
return Collections.singleton(new JCacheCache(cache));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.jcache.model.CacheResultOperation;
|
||||
import org.springframework.util.SerializationUtils;
|
||||
import org.springframework.util.filter.ExceptionTypeFilter;
|
||||
|
||||
/**
|
||||
* Intercept methods annotated with {@link CacheResult}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOperation, CacheResult> {
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheResultOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
CacheResultOperation operation = context.getOperation();
|
||||
|
||||
final Object cacheKey = generateKey(context);
|
||||
|
||||
Cache cache = resolveCache(context);
|
||||
Cache exceptionCache = resolveExceptionCache(context);
|
||||
|
||||
if (!operation.isAlwaysInvoked()) {
|
||||
Cache.ValueWrapper cachedValue = cache.get(cacheKey);
|
||||
if (cachedValue != null) {
|
||||
return cachedValue.get();
|
||||
}
|
||||
checkForCachedException(exceptionCache, cacheKey);
|
||||
}
|
||||
|
||||
try {
|
||||
Object invocationResult = invoker.invoke();
|
||||
if (invocationResult != null) {
|
||||
cache.put(cacheKey, invocationResult);
|
||||
}
|
||||
return invocationResult;
|
||||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper t) {
|
||||
Throwable original = t.getOriginal();
|
||||
cacheException(exceptionCache, operation.getExceptionTypeFilter(), cacheKey, original);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a cached exception. If the exception is found, throw it directly.
|
||||
*/
|
||||
protected void checkForCachedException(Cache exceptionCache, Object cacheKey) {
|
||||
if (exceptionCache == null) {
|
||||
return;
|
||||
}
|
||||
Cache.ValueWrapper result = exceptionCache.get(cacheKey);
|
||||
if (result != null) {
|
||||
throw rewriteCallStack((Throwable) result.get(), getClass().getName(), "invoke");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void cacheException(Cache exceptionCache, ExceptionTypeFilter filter,
|
||||
Object cacheKey, Throwable t) {
|
||||
if (exceptionCache == null) {
|
||||
return;
|
||||
}
|
||||
if (filter.match(t.getClass())) {
|
||||
exceptionCache.put(cacheKey, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Cache resolveExceptionCache(CacheOperationInvocationContext<CacheResultOperation> context) {
|
||||
CacheResolver exceptionCacheResolver = context.getOperation().getExceptionCacheResolver();
|
||||
if (exceptionCacheResolver != null) {
|
||||
return extractFrom(context.getOperation()
|
||||
.getExceptionCacheResolver().resolveCaches(context));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite the call stack of the specified {@code exception} so that it matches
|
||||
* the current call stack up-to (included) the specified method invocation.
|
||||
* <p>Clone the specified exception. If the exception is not {@code serializable},
|
||||
* the original exception is returned. If no common ancestor can be found, returns
|
||||
* the original exception.
|
||||
* <p>Used to make sure that a cached exception has a valid invocation context.
|
||||
* @param exception the exception to merge with the current call stack
|
||||
* @param className the class name of the common ancestor
|
||||
* @param methodName the method name of the common ancestor
|
||||
* @param <T> the type of the exception
|
||||
* @return a clone exception with a rewritten call stack composed of the current
|
||||
* call stack up to (included) the common ancestor specified by the {@code className} and
|
||||
* {@code methodName} arguments, followed by stack trace elements of the specified
|
||||
* {@code exception} after the common ancestor.
|
||||
*/
|
||||
private static CacheOperationInvoker.ThrowableWrapper rewriteCallStack(Throwable exception,
|
||||
String className, String methodName) {
|
||||
Throwable clone = cloneException(exception);
|
||||
if (clone == null) {
|
||||
return new CacheOperationInvoker.ThrowableWrapper(exception);
|
||||
}
|
||||
|
||||
StackTraceElement[] callStack = new Exception().getStackTrace();
|
||||
StackTraceElement[] cachedCallStack = exception.getStackTrace();
|
||||
|
||||
int index = findCommonAncestorIndex(callStack, className, methodName);
|
||||
int cachedIndex = findCommonAncestorIndex(cachedCallStack, className, methodName);
|
||||
if (index == -1 || cachedIndex == -1) {
|
||||
return new CacheOperationInvoker.ThrowableWrapper(exception); // Cannot find common ancestor
|
||||
}
|
||||
StackTraceElement[] result = new StackTraceElement[cachedIndex + callStack.length - index];
|
||||
System.arraycopy(cachedCallStack, 0, result, 0, cachedIndex);
|
||||
System.arraycopy(callStack, index, result, cachedIndex, callStack.length - index);
|
||||
|
||||
clone.setStackTrace(result);
|
||||
return new CacheOperationInvoker.ThrowableWrapper(clone);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Throwable> T cloneException(T exception) {
|
||||
try {
|
||||
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(exception));
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null; // exception parameter cannot be cloned
|
||||
}
|
||||
}
|
||||
|
||||
private static int findCommonAncestorIndex(StackTraceElement[] callStack, String className, String methodName) {
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
StackTraceElement element = callStack[i];
|
||||
if (className.equals(element.getClassName()) && methodName.equals(element.getMethodName())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationContext;
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.jcache.model.JCacheOperation;
|
||||
|
||||
/**
|
||||
* The default {@link CacheOperationInvocationContext} implementation used
|
||||
* by all interceptors. Also implements {@link CacheInvocationContext} to
|
||||
* act as a proper bridge when calling JSR-107 {@link javax.cache.annotation.CacheResolver}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultCacheInvocationContext<A extends Annotation>
|
||||
implements CacheInvocationContext<A>, CacheOperationInvocationContext<JCacheOperation<A>> {
|
||||
|
||||
private final JCacheOperation<A> operation;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
private final CacheInvocationParameter[] allParameters;
|
||||
|
||||
public DefaultCacheInvocationContext(JCacheOperation<A> operation,
|
||||
Object target, Object[] args) {
|
||||
this.operation = operation;
|
||||
this.target = target;
|
||||
this.args = args;
|
||||
this.allParameters = operation.getAllParameters(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JCacheOperation<A> getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return operation.getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getArgs() {
|
||||
return args.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Annotation> getAnnotations() {
|
||||
return operation.getAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public A getCacheAnnotation() {
|
||||
return operation.getCacheAnnotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheName() {
|
||||
return operation.getCacheName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheInvocationParameter[] getAllParameters() {
|
||||
return allParameters.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(Class<T> cls) {
|
||||
throw new IllegalArgumentException("Could not unwrap to '" + cls.getName() + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("CacheInvocationContext{");
|
||||
sb.append("operation=").append(operation);
|
||||
sb.append(", target=").append(target);
|
||||
sb.append(", args=").append(Arrays.toString(args));
|
||||
sb.append(", allParameters=").append(Arrays.toString(allParameters));
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
|
||||
import org.springframework.cache.jcache.model.BaseKeyCacheOperation;
|
||||
import org.springframework.cache.jcache.model.CachePutOperation;
|
||||
|
||||
/**
|
||||
* The default {@link CacheKeyInvocationContext} implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultCacheKeyInvocationContext<A extends Annotation>
|
||||
extends DefaultCacheInvocationContext<A> implements CacheKeyInvocationContext<A> {
|
||||
|
||||
private final CacheInvocationParameter[] keyParameters;
|
||||
|
||||
private final CacheInvocationParameter valueParameter;
|
||||
|
||||
public DefaultCacheKeyInvocationContext(BaseKeyCacheOperation<A> operation,
|
||||
Object target, Object[] args) {
|
||||
super(operation, target, args);
|
||||
this.keyParameters = operation.getKeyParameters(args);
|
||||
if (operation instanceof CachePutOperation) {
|
||||
this.valueParameter = ((CachePutOperation) operation).getValueParameter(args);
|
||||
}
|
||||
else {
|
||||
this.valueParameter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheInvocationParameter[] getKeyParameters() {
|
||||
return keyParameters.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheInvocationParameter getValueParameter() {
|
||||
return valueParameter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.SimpleCacheResolver;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The default {@link JCacheOperationSource} implementation delegating
|
||||
* default operations to configurable services with sensible defaults
|
||||
* when not present.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultJCacheOperationSource extends AnnotationCacheOperationSource
|
||||
implements InitializingBean, ApplicationContextAware {
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
private KeyGenerator keyGenerator;
|
||||
|
||||
private CacheResolver cacheResolver;
|
||||
|
||||
private CacheResolver exceptionCacheResolver;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.state((cacheResolver != null && exceptionCacheResolver != null)
|
||||
|| cacheManager != null, "'cacheManager' is required if cache resolvers are not set.");
|
||||
Assert.state(this.applicationContext != null, "The application context was not injected as it should.");
|
||||
|
||||
if (keyGenerator == null) {
|
||||
keyGenerator = new KeyGeneratorAdapter(this, new SimpleCacheKeyGenerator());
|
||||
}
|
||||
if (cacheResolver == null) {
|
||||
cacheResolver = new SimpleCacheResolver(cacheManager);
|
||||
}
|
||||
if (exceptionCacheResolver == null) {
|
||||
exceptionCacheResolver = new SimpleExceptionCacheResolver(cacheManager);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default {@link CacheManager} to use to lookup cache by name. Only mandatory
|
||||
* if the {@linkplain CacheResolver cache resolvers} have not been set.
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default {@link KeyGenerator}. If none is set, a default JSR-107 compliant
|
||||
* key generator is used.
|
||||
*/
|
||||
public void setKeyGenerator(KeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CacheResolver} to resolve regular caches. If none is set, a default
|
||||
* implementation using the specified cache manager will be used.
|
||||
*/
|
||||
public void setCacheResolver(CacheResolver cacheResolver) {
|
||||
this.cacheResolver = cacheResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CacheResolver} to resolve exception caches. If none is set, a default
|
||||
* implementation using the specified cache manager will be used.
|
||||
*/
|
||||
public void setExceptionCacheResolver(CacheResolver exceptionCacheResolver) {
|
||||
this.exceptionCacheResolver = exceptionCacheResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> T getBean(Class<T> type) {
|
||||
Map<String, T> map = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, type);
|
||||
if (map.size() == 1) {
|
||||
return map.values().iterator().next();
|
||||
}
|
||||
else {
|
||||
return BeanUtils.instantiateClass(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheResolver getDefaultCacheResolver() {
|
||||
return cacheResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheResolver getDefaultExceptionCacheResolver() {
|
||||
return exceptionCacheResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyGenerator getDefaultKeyGenerator() {
|
||||
return keyGenerator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.interceptor.BasicCacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.CachePutOperation;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveAllOperation;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveOperation;
|
||||
import org.springframework.cache.jcache.model.CacheResultOperation;
|
||||
import org.springframework.cache.jcache.model.JCacheOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for JSR-107 caching aspects, such as the {@link JCacheInterceptor}
|
||||
* or an AspectJ aspect.
|
||||
*
|
||||
* <p>Use the Spring caching abstraction for cache-related operations. No JSR-107
|
||||
* {@link javax.cache.Cache} or {@link javax.cache.CacheManager} are required to
|
||||
* process standard JSR-107 cache annotations.
|
||||
*
|
||||
* <p>The {@link JCacheOperationSource} is used for determining caching operations
|
||||
*
|
||||
* <p>A cache aspect is serializable if its {@code JCacheOperationSource} is serializable.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.interceptor.CacheAspectSupport
|
||||
* @see KeyGeneratorAdapter
|
||||
* @see CacheResolverAdapter
|
||||
*/
|
||||
public class JCacheAspectSupport implements InitializingBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private JCacheOperationSource cacheOperationSource;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
private final CacheResultInterceptor cacheResultInterceptor = new CacheResultInterceptor();
|
||||
|
||||
private final CachePutInterceptor cachePutInterceptor = new CachePutInterceptor();
|
||||
|
||||
private final CacheRemoveEntryInterceptor cacheRemoveEntryInterceptor = new CacheRemoveEntryInterceptor();
|
||||
|
||||
private final CacheRemoveAllInterceptor cacheRemoveAllInterceptor = new CacheRemoveAllInterceptor();
|
||||
|
||||
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
|
||||
Assert.notNull(cacheOperationSource);
|
||||
this.cacheOperationSource = cacheOperationSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CacheOperationSource for this cache aspect.
|
||||
*/
|
||||
public JCacheOperationSource getCacheOperationSource() {
|
||||
return this.cacheOperationSource;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.state(this.cacheOperationSource != null, "The 'cacheOperationSource' property is required: " +
|
||||
"If there are no cacheable methods, then don't use a cache aspect.");
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
||||
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
|
||||
// check whether aspect is enabled
|
||||
// to cope with cases where the AJ is pulled in automatically
|
||||
if (this.initialized) {
|
||||
Class<?> targetClass = getTargetClass(target);
|
||||
JCacheOperation<?> operation = getCacheOperationSource().getCacheOperation(method, targetClass);
|
||||
if (operation != null) {
|
||||
CacheOperationInvocationContext<?> context =
|
||||
createCacheOperationInvocationContext(target, args, operation);
|
||||
return execute(context, invoker);
|
||||
}
|
||||
}
|
||||
|
||||
return invoker.invoke();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private CacheOperationInvocationContext<?> createCacheOperationInvocationContext(Object target,
|
||||
Object[] args,
|
||||
JCacheOperation<?> operation) {
|
||||
return new DefaultCacheInvocationContext<Annotation>(
|
||||
(JCacheOperation<Annotation>) operation, target, args);
|
||||
}
|
||||
|
||||
private Class<?> getTargetClass(Object target) {
|
||||
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
|
||||
if (targetClass == null && target != null) {
|
||||
targetClass = target.getClass();
|
||||
}
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object execute(CacheOperationInvocationContext<?> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
BasicCacheOperation operation = context.getOperation();
|
||||
if (operation instanceof CacheResultOperation) {
|
||||
return cacheResultInterceptor.invoke(
|
||||
(CacheOperationInvocationContext<CacheResultOperation>) context, invoker);
|
||||
}
|
||||
else if (operation instanceof CachePutOperation) {
|
||||
return cachePutInterceptor.invoke(
|
||||
(CacheOperationInvocationContext<CachePutOperation>) context, invoker);
|
||||
}
|
||||
else if (operation instanceof CacheRemoveOperation) {
|
||||
return cacheRemoveEntryInterceptor.invoke(
|
||||
(CacheOperationInvocationContext<CacheRemoveOperation>) context, invoker);
|
||||
}
|
||||
else if (operation instanceof CacheRemoveAllOperation) {
|
||||
return cacheRemoveAllInterceptor.invoke(
|
||||
(CacheOperationInvocationContext<CacheRemoveAllOperation>) context, invoker);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Could not handle " + operation);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
|
||||
/**
|
||||
* AOP Alliance MethodInterceptor for declarative cache
|
||||
* management using JSR-107 caching annotations.
|
||||
*
|
||||
* <p>Derives from the {@link JCacheAspectSupport} class which
|
||||
* contains the integration with Spring's underlying caching API.
|
||||
* JCacheInterceptor simply calls the relevant superclass method.
|
||||
*
|
||||
* <p>JCacheInterceptors are thread-safe.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @see org.springframework.cache.interceptor.CacheInterceptor
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class JCacheInterceptor extends JCacheAspectSupport
|
||||
implements MethodInterceptor, Serializable {
|
||||
|
||||
@Override
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
Method method = invocation.getMethod();
|
||||
|
||||
CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
|
||||
@Override
|
||||
public Object invoke() {
|
||||
try {
|
||||
return invocation.proceed();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new ThrowableWrapper(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
|
||||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper th) {
|
||||
throw th.getOriginal();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.cache.jcache.model.JCacheOperation;
|
||||
|
||||
/**
|
||||
* Interface used by {@link JCacheInterceptor}. Implementations know how to source
|
||||
* cache operation attributes from standard JSR-107 annotations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.interceptor.CacheOperationSource
|
||||
*/
|
||||
public interface JCacheOperationSource {
|
||||
|
||||
/**
|
||||
* Return the cache operations for this method, or {@code null}
|
||||
* if the method contains no <em>JSR-107</em> related metadata.
|
||||
*
|
||||
* @param method the method to introspect
|
||||
* @param targetClass the target class (may be {@code null}, in which case
|
||||
* the declaring class of the method must be used)
|
||||
* @return the cache operation for this method, or {@code null} if none found
|
||||
*/
|
||||
JCacheOperation<?> getCacheOperation(Method method, Class<?> targetClass);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcut;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A Pointcut that matches if the underlying {@link JCacheOperationSource}
|
||||
* has an operation for a given method.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class JCacheOperationSourcePointcut
|
||||
extends StaticMethodMatcherPointcut implements Serializable {
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass) {
|
||||
JCacheOperationSource cas = getCacheOperationSource();
|
||||
return (cas != null && cas.getCacheOperation(method, targetClass) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the underlying {@link JCacheOperationSource} (may be {@code null}).
|
||||
* To be implemented by subclasses.
|
||||
*/
|
||||
protected abstract JCacheOperationSource getCacheOperationSource();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof JCacheOperationSourcePointcut)) {
|
||||
return false;
|
||||
}
|
||||
JCacheOperationSourcePointcut otherPc = (JCacheOperationSourcePointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return JCacheOperationSourcePointcut.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + getCacheOperationSource();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.cache.annotation.CacheKeyGenerator;
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.jcache.model.BaseKeyCacheOperation;
|
||||
import org.springframework.cache.jcache.model.JCacheOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Spring's {@link KeyGenerator} implementation that delegates to a standard
|
||||
* JSR-107 {@link javax.cache.annotation.CacheKeyGenerator}.
|
||||
* <p>Used internally to invoke user-based JSR-107 cache key generators.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class KeyGeneratorAdapter implements KeyGenerator {
|
||||
|
||||
private final JCacheOperationSource cacheOperationSource;
|
||||
|
||||
private final CacheKeyGenerator target;
|
||||
|
||||
public KeyGeneratorAdapter(JCacheOperationSource cacheOperationSource, CacheKeyGenerator target) {
|
||||
Assert.notNull(cacheOperationSource, "cacheOperationSource must be set.");
|
||||
Assert.notNull(target, "cache key generator must be set.");
|
||||
this.cacheOperationSource = cacheOperationSource;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying {@link CacheKeyGenerator} that this instance is using.
|
||||
*/
|
||||
protected CacheKeyGenerator getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object generate(Object target, Method method, Object... params) {
|
||||
JCacheOperation<?> operation = cacheOperationSource.getCacheOperation(method, target.getClass());
|
||||
if (!(BaseKeyCacheOperation.class.isInstance(operation))) {
|
||||
throw new IllegalStateException("Invalid operation, should be a key-based operation " + operation);
|
||||
}
|
||||
CacheKeyInvocationContext<?> invocationContext = createCacheKeyInvocationContext(target, operation, params);
|
||||
|
||||
return this.target.generateCacheKey(invocationContext);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private CacheKeyInvocationContext<?> createCacheKeyInvocationContext(Object target,
|
||||
JCacheOperation<?> operation, Object[] params) {
|
||||
BaseKeyCacheOperation<Annotation> keyCacheOperation = (BaseKeyCacheOperation<Annotation>) operation;
|
||||
return new DefaultCacheKeyInvocationContext<Annotation>(keyCacheOperation, target, params);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheKeyGenerator;
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
import javax.cache.annotation.GeneratedCacheKey;
|
||||
|
||||
/**
|
||||
* A JSR-107 compliant key generator. Uses only the parameters that have been annotated
|
||||
* with {@link javax.cache.annotation.CacheKey} or all of them if none are set, except
|
||||
* the {@link javax.cache.annotation.CacheValue} one.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @see 4.1
|
||||
* @see javax.cache.annotation.CacheKeyInvocationContext#getKeyParameters()
|
||||
*/
|
||||
public class SimpleCacheKeyGenerator implements CacheKeyGenerator {
|
||||
|
||||
@Override
|
||||
public GeneratedCacheKey generateCacheKey(CacheKeyInvocationContext<? extends Annotation> context) {
|
||||
CacheInvocationParameter[] keyParameters = context.getKeyParameters();
|
||||
final Object[] parameters = new Object[keyParameters.length];
|
||||
for (int i = 0; i < keyParameters.length; i++) {
|
||||
parameters[i] = keyParameters[i].getValue();
|
||||
}
|
||||
return new SimpleGeneratedCacheKey(parameters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.BasicCacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.jcache.model.CacheResultOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheResolver} that resolves the exception cache
|
||||
* based on a configurable {@link CacheManager} and the name of the
|
||||
* cache: {@link CacheResultOperation#getExceptionCacheName()}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.jcache.model.CacheResultOperation#getExceptionCacheName()
|
||||
*/
|
||||
public class SimpleExceptionCacheResolver implements CacheResolver {
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
public SimpleExceptionCacheResolver(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
BasicCacheOperation operation = context.getOperation();
|
||||
if (!(operation instanceof CacheResultOperation)) {
|
||||
throw new IllegalStateException("Could not extract exception cache name from " + operation);
|
||||
}
|
||||
CacheResultOperation cacheResultOperation = (CacheResultOperation) operation;
|
||||
String exceptionCacheName = cacheResultOperation.getExceptionCacheName();
|
||||
if (exceptionCacheName != null) {
|
||||
Cache cache = cacheManager.getCache(exceptionCacheName);
|
||||
Assert.notNull(cache, "Cannot find cache named '" + exceptionCacheName + "' for " + operation);
|
||||
return Collections.singleton(cache);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
import javax.cache.annotation.GeneratedCacheKey;
|
||||
|
||||
import org.springframework.cache.interceptor.SimpleKey;
|
||||
|
||||
/**
|
||||
* A {@link SimpleKey} that implements the {@link GeneratedCacheKey} contract
|
||||
* required by JSR-107
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class SimpleGeneratedCacheKey extends SimpleKey implements GeneratedCacheKey {
|
||||
|
||||
public SimpleGeneratedCacheKey(Object... elements) {
|
||||
super(elements);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* AOP-based solution for declarative caching demarcation using JSR-107 annotations.
|
||||
* <p>Strongly based on the infrastructure in org.springframework.cache.interceptor
|
||||
* that deals with Spring's caching annotations.
|
||||
* <p>Builds on the AOP infrastructure in org.springframework.aop.framework.
|
||||
* Any POJO can be cache-advised with Spring.
|
||||
*/
|
||||
package org.springframework.cache.jcache.interceptor;
|
||||
227
spring-context-support/src/main/java/org/springframework/cache/jcache/model/BaseCacheOperation.java
vendored
Normal file
227
spring-context-support/src/main/java/org/springframework/cache/jcache/model/BaseCacheOperation.java
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheKey;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheValue;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.filter.ExceptionTypeFilter;
|
||||
|
||||
/**
|
||||
* A base {@link JCacheOperation} implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class BaseCacheOperation<A extends Annotation> implements JCacheOperation<A> {
|
||||
|
||||
private final CacheMethodDetails<A> methodDetails;
|
||||
|
||||
private final CacheResolver cacheResolver;
|
||||
|
||||
protected final List<CacheParameterDetail> allParameterDetails;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param methodDetails the {@link CacheMethodDetails} related to the cached method
|
||||
* @param cacheResolver the cache resolver to resolve regular caches
|
||||
*/
|
||||
protected BaseCacheOperation(CacheMethodDetails<A> methodDetails, CacheResolver cacheResolver) {
|
||||
Assert.notNull(methodDetails, "method details must not be null.");
|
||||
Assert.notNull(cacheResolver, "cache resolver must not be null.");
|
||||
this.methodDetails = methodDetails;
|
||||
this.cacheResolver = cacheResolver;
|
||||
this.allParameterDetails = initializeAllParameterDetails(methodDetails.getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ExceptionTypeFilter} to use to filter exceptions thrown while
|
||||
* invoking the method.
|
||||
*/
|
||||
public abstract ExceptionTypeFilter getExceptionTypeFilter();
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return methodDetails.getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Annotation> getAnnotations() {
|
||||
return methodDetails.getAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public A getCacheAnnotation() {
|
||||
return methodDetails.getCacheAnnotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheName() {
|
||||
return methodDetails.getCacheName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getCacheNames() {
|
||||
return Collections.singleton(getCacheName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheResolver getCacheResolver() {
|
||||
return cacheResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheInvocationParameter[] getAllParameters(Object... values) {
|
||||
if (allParameterDetails.size() != values.length) {
|
||||
throw new IllegalStateException("Values mismatch, operation has "
|
||||
+ allParameterDetails.size() + " parameter(s) but got " + values.length + " value(s)");
|
||||
}
|
||||
List<CacheInvocationParameter> result = new ArrayList<CacheInvocationParameter>();
|
||||
for (int i = 0; i < allParameterDetails.size(); i++) {
|
||||
result.add(allParameterDetails.get(i).toCacheInvocationParameter(values[i]));
|
||||
}
|
||||
return result.toArray(new CacheInvocationParameter[result.size()]);
|
||||
}
|
||||
|
||||
protected ExceptionTypeFilter createExceptionTypeFiler(Class<? extends Throwable>[] includes,
|
||||
Class<? extends Throwable>[] excludes) {
|
||||
return new ExceptionTypeFilter(asList(includes), asList(excludes), true);
|
||||
}
|
||||
|
||||
|
||||
private static List<CacheParameterDetail> initializeAllParameterDetails(Method method) {
|
||||
List<CacheParameterDetail> result = new ArrayList<CacheParameterDetail>();
|
||||
for (int i = 0; i < method.getParameterCount(); i++) {
|
||||
CacheParameterDetail detail = new CacheParameterDetail(method, i);
|
||||
result.add(detail);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getOperationDescription().append("]").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an identifying description for this caching operation.
|
||||
* <p>Available to subclasses, for inclusion in their {@code toString()} result.
|
||||
*/
|
||||
protected StringBuilder getOperationDescription() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(getClass().getSimpleName());
|
||||
result.append("[");
|
||||
result.append(this.methodDetails);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
protected static class CacheParameterDetail {
|
||||
|
||||
private final Class<?> rawType;
|
||||
|
||||
private final Set<Annotation> annotations;
|
||||
|
||||
private final int parameterPosition;
|
||||
|
||||
private final boolean isKey;
|
||||
|
||||
private final boolean isValue;
|
||||
|
||||
private CacheParameterDetail(Method m, int parameterPosition) {
|
||||
this.rawType = m.getParameterTypes()[parameterPosition];
|
||||
this.annotations = new LinkedHashSet<Annotation>();
|
||||
boolean foundKeyAnnotation = false;
|
||||
boolean foundValueAnnotation = false;
|
||||
for (Annotation annotation : m.getParameterAnnotations()[parameterPosition]) {
|
||||
annotations.add(annotation);
|
||||
if (CacheKey.class.isAssignableFrom(annotation.annotationType())) {
|
||||
foundKeyAnnotation = true;
|
||||
}
|
||||
if (CacheValue.class.isAssignableFrom(annotation.annotationType())) {
|
||||
foundValueAnnotation = true;
|
||||
}
|
||||
}
|
||||
this.parameterPosition = parameterPosition;
|
||||
this.isKey = foundKeyAnnotation;
|
||||
this.isValue = foundValueAnnotation;
|
||||
}
|
||||
|
||||
public int getParameterPosition() {
|
||||
return parameterPosition;
|
||||
}
|
||||
|
||||
protected boolean isKey() {
|
||||
return isKey;
|
||||
}
|
||||
|
||||
protected boolean isValue() {
|
||||
return isValue;
|
||||
}
|
||||
|
||||
public CacheInvocationParameter toCacheInvocationParameter(Object value) {
|
||||
return new CacheInvocationParameterImpl(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class CacheInvocationParameterImpl implements CacheInvocationParameter {
|
||||
|
||||
private final CacheParameterDetail detail;
|
||||
|
||||
private final Object value;
|
||||
|
||||
private CacheInvocationParameterImpl(CacheParameterDetail detail, Object value) {
|
||||
this.detail = detail;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getRawType() {
|
||||
return detail.rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Annotation> getAnnotations() {
|
||||
return detail.annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameterPosition() {
|
||||
return detail.parameterPosition;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
/**
|
||||
* A base {@link JCacheOperation} that operates with a key.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class BaseKeyCacheOperation<A extends Annotation> extends BaseCacheOperation<A> {
|
||||
|
||||
private final KeyGenerator keyGenerator;
|
||||
|
||||
private final List<CacheParameterDetail> keyParameterDetails;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param methodDetails the {@link CacheMethodDetails} related to the cached method
|
||||
* @param cacheResolver the cache resolver to resolve regular caches
|
||||
* @param keyGenerator the key generator to compute cache keys
|
||||
*/
|
||||
protected BaseKeyCacheOperation(CacheMethodDetails<A> methodDetails,
|
||||
CacheResolver cacheResolver, KeyGenerator keyGenerator) {
|
||||
super(methodDetails, cacheResolver);
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.keyParameterDetails = initializeKeyParameterDetails(allParameterDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link KeyGenerator} to use to compute cache keys.
|
||||
*/
|
||||
public KeyGenerator getKeyGenerator() {
|
||||
return keyGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheInvocationParameter} for the parameters that are to be
|
||||
* used to compute the key.
|
||||
* <p>Per the spec, if some method parameters are annotated with
|
||||
* {@link javax.cache.annotation.CacheKey}, only those parameters should be part
|
||||
* of the key. If none are annotated, all parameters except the parameter annotated
|
||||
* with {@link javax.cache.annotation.CacheValue} should be part of the key.
|
||||
* <p>The method arguments must match the signature of the related method invocation
|
||||
* @param values the parameters value for a particular invocation
|
||||
* @return the {@link CacheInvocationParameter} instances for the parameters to be
|
||||
* used to compute the key
|
||||
*/
|
||||
public CacheInvocationParameter[] getKeyParameters(Object... values) {
|
||||
List<CacheInvocationParameter> result = new ArrayList<CacheInvocationParameter>();
|
||||
for (CacheParameterDetail keyParameterDetail : keyParameterDetails) {
|
||||
int parameterPosition = keyParameterDetail.getParameterPosition();
|
||||
if (parameterPosition >= values.length) {
|
||||
throw new IllegalStateException("Values mismatch, key parameter at position "
|
||||
+ parameterPosition + " cannot be matched against " + values.length + " value(s)");
|
||||
}
|
||||
result.add(keyParameterDetail.toCacheInvocationParameter(values[parameterPosition]));
|
||||
}
|
||||
return result.toArray(new CacheInvocationParameter[result.size()]);
|
||||
}
|
||||
|
||||
|
||||
private static List<CacheParameterDetail> initializeKeyParameterDetails(List<CacheParameterDetail> allParameters) {
|
||||
List<CacheParameterDetail> all = new ArrayList<CacheParameterDetail>();
|
||||
List<CacheParameterDetail> annotated = new ArrayList<CacheParameterDetail>();
|
||||
for (CacheParameterDetail allParameter : allParameters) {
|
||||
if (!allParameter.isValue()) {
|
||||
all.add(allParameter);
|
||||
}
|
||||
if (allParameter.isKey()) {
|
||||
annotated.add(allParameter);
|
||||
}
|
||||
}
|
||||
return annotated.size() == 0 ? all : annotated;
|
||||
}
|
||||
|
||||
}
|
||||
102
spring-context-support/src/main/java/org/springframework/cache/jcache/model/CachePutOperation.java
vendored
Normal file
102
spring-context-support/src/main/java/org/springframework/cache/jcache/model/CachePutOperation.java
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CachePut;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.util.filter.ExceptionTypeFilter;
|
||||
|
||||
/**
|
||||
* The {@link JCacheOperation} implementation for a {@link CachePut} operation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see CachePut
|
||||
*/
|
||||
public class CachePutOperation extends BaseKeyCacheOperation<CachePut> {
|
||||
|
||||
private final ExceptionTypeFilter exceptionTypeFilter;
|
||||
|
||||
private final CacheParameterDetail valueParameterDetail;
|
||||
|
||||
public CachePutOperation(CacheMethodDetails<CachePut> methodDetails,
|
||||
CacheResolver cacheResolver, KeyGenerator keyGenerator) {
|
||||
super(methodDetails, cacheResolver, keyGenerator);
|
||||
CachePut ann = methodDetails.getCacheAnnotation();
|
||||
this.exceptionTypeFilter = createExceptionTypeFiler(ann.cacheFor(), ann.noCacheFor());
|
||||
this.valueParameterDetail = initializeValueParameterDetail(methodDetails.getMethod(), allParameterDetails);
|
||||
if (valueParameterDetail == null) {
|
||||
throw new IllegalArgumentException("No parameter annotated with @CacheValue was found for " +
|
||||
"" + methodDetails.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExceptionTypeFilter getExceptionTypeFilter() {
|
||||
return exceptionTypeFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the cache should be updated before invoking the method. By default,
|
||||
* the cache is updated after the method invocation.
|
||||
* @see javax.cache.annotation.CachePut#afterInvocation()
|
||||
*/
|
||||
public boolean isEarlyPut() {
|
||||
return !getCacheAnnotation().afterInvocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheInvocationParameter} for the parameter holding the value
|
||||
* to cache.
|
||||
* <p>The method arguments must match the signature of the related method invocation
|
||||
* @param values the parameters value for a particular invocation
|
||||
* @return the {@link CacheInvocationParameter} instance for the value parameter
|
||||
*/
|
||||
public CacheInvocationParameter getValueParameter(Object... values) {
|
||||
int parameterPosition = valueParameterDetail.getParameterPosition();
|
||||
if (parameterPosition >= values.length) {
|
||||
throw new IllegalStateException("Values mismatch, value parameter at position "
|
||||
+ parameterPosition + " cannot be matched against " + values.length + " value(s)");
|
||||
}
|
||||
return valueParameterDetail.toCacheInvocationParameter(values[parameterPosition]);
|
||||
}
|
||||
|
||||
|
||||
private static CacheParameterDetail initializeValueParameterDetail(Method method,
|
||||
List<CacheParameterDetail> allParameters) {
|
||||
CacheParameterDetail result = null;
|
||||
for (CacheParameterDetail parameter : allParameters) {
|
||||
if (parameter.isValue()) {
|
||||
if (result == null) {
|
||||
result = parameter;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("More than one @CacheValue found on " + method + "");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.util.filter.ExceptionTypeFilter;
|
||||
|
||||
/**
|
||||
* The {@link JCacheOperation} implementation for a {@link CacheRemoveAll} operation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see CacheRemoveAll
|
||||
*/
|
||||
public class CacheRemoveAllOperation extends BaseCacheOperation<CacheRemoveAll> {
|
||||
|
||||
private final ExceptionTypeFilter exceptionTypeFilter;
|
||||
|
||||
public CacheRemoveAllOperation(CacheMethodDetails<CacheRemoveAll> methodDetails, CacheResolver cacheResolver) {
|
||||
super(methodDetails, cacheResolver);
|
||||
CacheRemoveAll ann = methodDetails.getCacheAnnotation();
|
||||
this.exceptionTypeFilter = createExceptionTypeFiler(ann.evictFor(), ann.noEvictFor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExceptionTypeFilter getExceptionTypeFilter() {
|
||||
return exceptionTypeFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the cache should be cleared before invoking the method. By default, the
|
||||
* cache is cleared after the method invocation.
|
||||
* @see javax.cache.annotation.CacheRemoveAll#afterInvocation()
|
||||
*/
|
||||
public boolean isEarlyRemove() {
|
||||
return !getCacheAnnotation().afterInvocation();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.util.filter.ExceptionTypeFilter;
|
||||
|
||||
/**
|
||||
* The {@link JCacheOperation} implementation for a {@link CacheRemove} operation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see CacheRemove
|
||||
*/
|
||||
public class CacheRemoveOperation extends BaseKeyCacheOperation<CacheRemove> {
|
||||
|
||||
private final ExceptionTypeFilter exceptionTypeFilter;
|
||||
|
||||
public CacheRemoveOperation(CacheMethodDetails<CacheRemove> methodDetails,
|
||||
CacheResolver cacheResolver, KeyGenerator keyGenerator) {
|
||||
super(methodDetails, cacheResolver, keyGenerator);
|
||||
CacheRemove ann = methodDetails.getCacheAnnotation();
|
||||
this.exceptionTypeFilter = createExceptionTypeFiler(ann.evictFor(), ann.noEvictFor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExceptionTypeFilter getExceptionTypeFilter() {
|
||||
return exceptionTypeFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the cache entry should be remove before invoking the method. By default, the
|
||||
* cache entry is removed after the method invocation.
|
||||
* @see javax.cache.annotation.CacheRemove#afterInvocation()
|
||||
*/
|
||||
public boolean isEarlyRemove() {
|
||||
return !getCacheAnnotation().afterInvocation();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.filter.ExceptionTypeFilter;
|
||||
|
||||
/**
|
||||
* The {@link JCacheOperation} implementation for a {@link CacheResult} operation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see CacheResult
|
||||
*/
|
||||
public class CacheResultOperation extends BaseKeyCacheOperation<CacheResult> {
|
||||
|
||||
private final ExceptionTypeFilter exceptionTypeFilter;
|
||||
|
||||
private final CacheResolver exceptionCacheResolver;
|
||||
|
||||
private final String exceptionCacheName;
|
||||
|
||||
public CacheResultOperation(CacheMethodDetails<CacheResult> methodDetails,
|
||||
CacheResolver cacheResolver, KeyGenerator keyGenerator,
|
||||
CacheResolver exceptionCacheResolver) {
|
||||
super(methodDetails, cacheResolver, keyGenerator);
|
||||
CacheResult ann = methodDetails.getCacheAnnotation();
|
||||
this.exceptionTypeFilter = createExceptionTypeFiler(ann.cachedExceptions(), ann.nonCachedExceptions());
|
||||
this.exceptionCacheResolver = exceptionCacheResolver;
|
||||
String exceptionCacheNameCandidate = ann.exceptionCacheName();
|
||||
this.exceptionCacheName = StringUtils.hasText(exceptionCacheNameCandidate) ? exceptionCacheNameCandidate : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExceptionTypeFilter getExceptionTypeFilter() {
|
||||
return exceptionTypeFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the method should always be invoked regardless of a cache hit. By
|
||||
* default, the method is only invoked in case of a cache miss.
|
||||
* @see javax.cache.annotation.CacheResult#skipGet()
|
||||
*/
|
||||
public boolean isAlwaysInvoked() {
|
||||
return getCacheAnnotation().skipGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheResolver} instance to use to resolve the cache to
|
||||
* use for matching exceptions thrown by this operation.
|
||||
*/
|
||||
public CacheResolver getExceptionCacheResolver() {
|
||||
return exceptionCacheResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the cache to cache exceptions. Return {@link null} if
|
||||
* caching exceptions should be disabled.
|
||||
* @see javax.cache.annotation.CacheResult#exceptionCacheName()
|
||||
*/
|
||||
public String getExceptionCacheName() {
|
||||
return exceptionCacheName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
|
||||
/**
|
||||
* The default {@link CacheMethodDetails} implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultCacheMethodDetails<A extends Annotation> implements CacheMethodDetails<A> {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Set<Annotation> annotations;
|
||||
|
||||
private final A cacheAnnotation;
|
||||
|
||||
private final String cacheName;
|
||||
|
||||
public DefaultCacheMethodDetails(Method method, A cacheAnnotation,
|
||||
String cacheName) {
|
||||
this.method = method;
|
||||
this.annotations = Collections.unmodifiableSet(
|
||||
new LinkedHashSet<Annotation>(asList(method.getAnnotations())));
|
||||
this.cacheAnnotation = cacheAnnotation;
|
||||
this.cacheName = cacheName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Annotation> getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public A getCacheAnnotation() {
|
||||
return cacheAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheName() {
|
||||
return cacheName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("details[");
|
||||
sb.append("method=").append(method);
|
||||
sb.append(", cacheAnnotation=").append(cacheAnnotation);
|
||||
sb.append(", cacheName='").append(cacheName).append('\'');
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
53
spring-context-support/src/main/java/org/springframework/cache/jcache/model/JCacheOperation.java
vendored
Normal file
53
spring-context-support/src/main/java/org/springframework/cache/jcache/model/JCacheOperation.java
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
|
||||
import org.springframework.cache.interceptor.BasicCacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
|
||||
/**
|
||||
* Model the base of JSR-107 cache operation.
|
||||
* <p>A cache operation can be statically cached as it does not contain
|
||||
* any runtime operation of a specific cache invocation.
|
||||
*
|
||||
* @param <A> the type of the JSR-107 annotation
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface JCacheOperation<A extends Annotation>
|
||||
extends CacheMethodDetails<A>, BasicCacheOperation {
|
||||
|
||||
/**
|
||||
* Return the {@link CacheResolver} instance to use to resolve the cache to
|
||||
* use for this operation.
|
||||
*/
|
||||
CacheResolver getCacheResolver();
|
||||
|
||||
/**
|
||||
* Return the {@link CacheInvocationParameter} instances based on the specified
|
||||
* method arguments.
|
||||
* <p>The method arguments must match the signature of the related method invocation
|
||||
* @param values the parameters value for a particular invocation
|
||||
*/
|
||||
CacheInvocationParameter[] getAllParameters(Object... values);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Resolved model of JSR-107 cache annotations. Used internally by the interceptors
|
||||
* in org.springframework.cache.jcache.interceptor.
|
||||
*/
|
||||
package org.springframework.cache.jcache.model;
|
||||
66
spring-context-support/src/test/java/org/springframework/cache/jcache/AbstractJCacheTests.java
vendored
Normal file
66
spring-context-support/src/test/java/org/springframework/cache/jcache/AbstractJCacheTests.java
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.SimpleCacheResolver;
|
||||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
import org.springframework.cache.jcache.interceptor.SimpleExceptionCacheResolver;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractJCacheTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Rule
|
||||
public final TestName name = new TestName();
|
||||
|
||||
protected final CacheManager cacheManager = createSimpleCacheManager("default", "simpleCache");
|
||||
|
||||
protected final CacheResolver defaultCacheResolver = new SimpleCacheResolver(cacheManager);
|
||||
|
||||
protected final CacheResolver defaultExceptionCacheResolver = new SimpleExceptionCacheResolver(cacheManager);
|
||||
|
||||
protected final KeyGenerator defaultKeyGenerator = new SimpleKeyGenerator();
|
||||
|
||||
protected static CacheManager createSimpleCacheManager(String... cacheNames) {
|
||||
SimpleCacheManager result = new SimpleCacheManager();
|
||||
List<Cache> caches = new ArrayList<Cache>();
|
||||
for (String cacheName : cacheNames) {
|
||||
caches.add(new ConcurrentMapCache(cacheName));
|
||||
}
|
||||
result.setCaches(caches);
|
||||
result.afterPropertiesSet();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.jcache.interceptor.SimpleGeneratedCacheKey;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractJCacheAnnotationTests {
|
||||
|
||||
public static final String DEFAULT_CACHE = "default";
|
||||
|
||||
public static final String EXCEPTION_CACHE = "exception";
|
||||
|
||||
@Rule
|
||||
public final TestName name = new TestName();
|
||||
|
||||
private JCacheableService<?> service;
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
protected abstract ApplicationContext getApplicationContext();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ApplicationContext context = getApplicationContext();
|
||||
service = context.getBean(JCacheableService.class);
|
||||
cacheManager = context.getBean("cacheManager", CacheManager.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cache() {
|
||||
String keyItem = name.getMethodName();
|
||||
|
||||
Object first = service.cache(keyItem);
|
||||
Object second = service.cache(keyItem);
|
||||
assertSame(first, second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheException() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(EXCEPTION_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
assertNull(cache.get(key));
|
||||
|
||||
try {
|
||||
service.cacheWithException(keyItem, true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
|
||||
Cache.ValueWrapper result = cache.get(key);
|
||||
assertNotNull(result);
|
||||
assertEquals(UnsupportedOperationException.class, result.get().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheExceptionVetoed() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(EXCEPTION_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
assertNull(cache.get(key));
|
||||
|
||||
try {
|
||||
service.cacheWithException(keyItem, false);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
|
||||
@Test
|
||||
public void cacheExceptionRewriteCallStack() {
|
||||
final String keyItem = name.getMethodName();
|
||||
|
||||
UnsupportedOperationException first = null;
|
||||
long ref = service.exceptionInvocations();
|
||||
try {
|
||||
service.cacheWithException(keyItem, true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
first = e;
|
||||
}
|
||||
// Sanity check, this particular call has called the service
|
||||
assertEquals("First call should not have been cached", ref + 1, service.exceptionInvocations());
|
||||
|
||||
UnsupportedOperationException second = methodInCallStack(keyItem);
|
||||
// Sanity check, this particular call has *not* called the service
|
||||
assertEquals("Second call should have been cached", ref + 1, service.exceptionInvocations());
|
||||
|
||||
assertEquals(first.getCause(), second.getCause());
|
||||
assertEquals(first.getMessage(), second.getMessage());
|
||||
assertFalse("Original stack must not contain any reference to methodInCallStack",
|
||||
contain(first, AbstractJCacheAnnotationTests.class.getName(), "methodInCallStack"));
|
||||
assertTrue("Cached stack should have been rewritten with a reference to methodInCallStack",
|
||||
contain(second, AbstractJCacheAnnotationTests.class.getName(), "methodInCallStack"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheAlwaysInvoke() {
|
||||
String keyItem = name.getMethodName();
|
||||
|
||||
Object first = service.cacheAlwaysInvoke(keyItem);
|
||||
Object second = service.cacheAlwaysInvoke(keyItem);
|
||||
assertNotSame(first, second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWithPartialKey() {
|
||||
String keyItem = name.getMethodName();
|
||||
|
||||
Object first = service.cacheWithPartialKey(keyItem, true);
|
||||
Object second = service.cacheWithPartialKey(keyItem, false);
|
||||
assertSame(first, second); // second argument not used, see config
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWithCustomCacheResolver() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
service.cacheWithCustomCacheResolver(keyItem);
|
||||
|
||||
assertNull(cache.get(key)); // Cache in mock cache
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWithCustomKeyGenerator() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
service.cacheWithCustomKeyGenerator(keyItem, "ignored");
|
||||
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
assertNull(cache.get(key));
|
||||
|
||||
service.put(keyItem, value);
|
||||
|
||||
Cache.ValueWrapper result = cache.get(key);
|
||||
assertNotNull(result);
|
||||
assertEquals(value, result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putWithException() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
assertNull(cache.get(key));
|
||||
|
||||
try {
|
||||
service.putWithException(keyItem, value, true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
|
||||
Cache.ValueWrapper result = cache.get(key);
|
||||
assertNotNull(result);
|
||||
assertEquals(value, result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putWithExceptionVetoPut() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
assertNull(cache.get(key));
|
||||
|
||||
try {
|
||||
service.putWithException(keyItem, value, false);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyPut() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
assertNull(cache.get(key));
|
||||
|
||||
service.earlyPut(keyItem, value);
|
||||
|
||||
Cache.ValueWrapper result = cache.get(key);
|
||||
assertNotNull(result);
|
||||
assertEquals(value, result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyPutWithException() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
assertNull(cache.get(key));
|
||||
|
||||
try {
|
||||
service.earlyPutWithException(keyItem, value, true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
|
||||
Cache.ValueWrapper result = cache.get(key);
|
||||
assertNotNull(result);
|
||||
assertEquals(value, result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyPutWithExceptionVetoPut() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
assertNull(cache.get(key));
|
||||
|
||||
try {
|
||||
service.earlyPutWithException(keyItem, value, false);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
// This will be cached anyway as the earlyPut has updated the cache before
|
||||
Cache.ValueWrapper result = cache.get(key);
|
||||
assertNotNull(result);
|
||||
assertEquals(value, result.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
cache.put(key, value);
|
||||
|
||||
service.remove(keyItem);
|
||||
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeWithException() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
cache.put(key, value);
|
||||
|
||||
try {
|
||||
service.removeWithException(keyItem, true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeWithExceptionVetoRemove() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
cache.put(key, value);
|
||||
|
||||
try {
|
||||
service.removeWithException(keyItem, false);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
Cache.ValueWrapper wrapper = cache.get(key);
|
||||
assertNotNull(wrapper);
|
||||
assertEquals(value, wrapper.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyRemove() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
cache.put(key, value);
|
||||
|
||||
service.earlyRemove(keyItem);
|
||||
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyRemoveWithException() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
cache.put(key, value);
|
||||
|
||||
try {
|
||||
service.earlyRemoveWithException(keyItem, true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyRemoveWithExceptionVetoRemove() {
|
||||
String keyItem = name.getMethodName();
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(keyItem);
|
||||
Object value = new Object();
|
||||
cache.put(key, value);
|
||||
|
||||
try {
|
||||
service.earlyRemoveWithException(keyItem, false);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
// This will be remove anyway as the earlyRemove has removed the cache before
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAll() {
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(name.getMethodName());
|
||||
cache.put(key, new Object());
|
||||
|
||||
service.removeAll();
|
||||
|
||||
assertTrue(isEmpty(cache));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAllWithException() {
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(name.getMethodName());
|
||||
cache.put(key, new Object());
|
||||
|
||||
try {
|
||||
service.removeAllWithException(true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
|
||||
assertTrue(isEmpty(cache));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAllWithExceptionVetoRemove() {
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(name.getMethodName());
|
||||
cache.put(key, new Object());
|
||||
|
||||
try {
|
||||
service.removeAllWithException(false);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
assertNotNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyRemoveAll() {
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(name.getMethodName());
|
||||
cache.put(key, new Object());
|
||||
|
||||
service.earlyRemoveAll();
|
||||
|
||||
assertTrue(isEmpty(cache));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyRemoveAllWithException() {
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(name.getMethodName());
|
||||
cache.put(key, new Object());
|
||||
|
||||
try {
|
||||
service.earlyRemoveAllWithException(true);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
assertTrue(isEmpty(cache));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void earlyRemoveAllWithExceptionVetoRemove() {
|
||||
Cache cache = getCache(DEFAULT_CACHE);
|
||||
|
||||
Object key = createKey(name.getMethodName());
|
||||
cache.put(key, new Object());
|
||||
|
||||
try {
|
||||
service.earlyRemoveAllWithException(false);
|
||||
fail("Should have thrown an exception");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// This is what we expect
|
||||
}
|
||||
// This will be remove anyway as the earlyRemove has removed the cache before
|
||||
assertTrue(isEmpty(cache));
|
||||
}
|
||||
|
||||
protected boolean isEmpty(Cache cache) {
|
||||
ConcurrentHashMap<?, ?> nativeCache = (ConcurrentHashMap<?, ?>) cache.getNativeCache();
|
||||
return nativeCache.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
private Object createKey(Object... params) {
|
||||
return new SimpleGeneratedCacheKey(params);
|
||||
}
|
||||
|
||||
private Cache getCache(String name) {
|
||||
Cache cache = cacheManager.getCache(name);
|
||||
assertNotNull("required cache " + name + " does not exist", cache);
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* The only purpose of this method is to invoke a particular method on the
|
||||
* service so that the call stack is different.
|
||||
*/
|
||||
private UnsupportedOperationException methodInCallStack(String keyItem) {
|
||||
try {
|
||||
service.cacheWithException(keyItem, true);
|
||||
throw new IllegalStateException("Should have thrown an exception");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean contain(Throwable t, String className, String methodName) {
|
||||
for (StackTraceElement element : t.getStackTrace()) {
|
||||
if (className.equals(element.getClassName()) && methodName.equals(element.getMethodName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.SimpleCacheResolver;
|
||||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
import org.springframework.cache.jcache.interceptor.AnnotatedJCacheableService;
|
||||
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return new AnnotationConfigApplicationContext(EnableCachingConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullCachingConfig() throws Exception {
|
||||
AnnotationConfigApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(FullCachingConfig.class);
|
||||
DefaultJCacheOperationSource cos = context.getBean(DefaultJCacheOperationSource.class);
|
||||
assertSame(context.getBean(KeyGenerator.class), cos.getDefaultKeyGenerator());
|
||||
assertSame(context.getBean("cacheResolver", CacheResolver.class),
|
||||
cos.getDefaultCacheResolver());
|
||||
assertSame(context.getBean("exceptionCacheResolver", CacheResolver.class),
|
||||
cos.getDefaultExceptionCacheResolver());
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public static class EnableCachingConfig {
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
SimpleCacheManager cm = new SimpleCacheManager();
|
||||
cm.setCaches(Arrays.asList(
|
||||
defaultCache(),
|
||||
new ConcurrentMapCache("primary"),
|
||||
new ConcurrentMapCache("secondary"),
|
||||
new ConcurrentMapCache("exception")));
|
||||
return cm;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JCacheableService<?> cacheableService() {
|
||||
return new AnnotatedJCacheableService(defaultCache());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Cache defaultCache() {
|
||||
return new ConcurrentMapCache("default");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public static class FullCachingConfig implements JCacheConfigurer {
|
||||
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
return new NoOpCacheManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public KeyGenerator keyGenerator() {
|
||||
return new SimpleKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheResolver cacheResolver() {
|
||||
return new SimpleCacheResolver(cacheManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheResolver exceptionCacheResolver() {
|
||||
return new SimpleCacheResolver(cacheManager());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JCacheNamespaceDrivenTests extends AbstractJCacheAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/jcache/config/jCacheNamespaceDriven.xml");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JCacheStandaloneConfigTests extends AbstractJCacheAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/jcache/config/jCacheStandaloneConfig.xml");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.config;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public interface JCacheableService<T> {
|
||||
|
||||
T cache(String id);
|
||||
|
||||
T cacheWithException(String id, boolean matchFilter);
|
||||
|
||||
T cacheAlwaysInvoke(String id);
|
||||
|
||||
T cacheWithPartialKey(String id, boolean notUsed);
|
||||
|
||||
T cacheWithCustomCacheResolver(String id);
|
||||
|
||||
T cacheWithCustomKeyGenerator(String id, String anotherId);
|
||||
|
||||
void put(String id, Object value);
|
||||
|
||||
void putWithException(String id, Object value, boolean matchFilter);
|
||||
|
||||
void earlyPut(String id, Object value);
|
||||
|
||||
void earlyPutWithException(String id, Object value, boolean matchFilter);
|
||||
|
||||
void remove(String id);
|
||||
|
||||
void removeWithException(String id, boolean matchFilter);
|
||||
|
||||
void earlyRemove(String id);
|
||||
|
||||
void earlyRemoveWithException(String id, boolean matchFilter);
|
||||
|
||||
void removeAll();
|
||||
|
||||
void removeAllWithException(boolean matchFilter);
|
||||
|
||||
void earlyRemoveAll();
|
||||
|
||||
void earlyRemoveAllWithException(boolean matchFilter);
|
||||
|
||||
long exceptionInvocations();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import javax.cache.annotation.CacheDefaults;
|
||||
import javax.cache.annotation.CacheKey;
|
||||
import javax.cache.annotation.CachePut;
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
import javax.cache.annotation.CacheValue;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.jcache.config.JCacheableService;
|
||||
import org.springframework.cache.jcache.support.TestableCacheKeyGenerator;
|
||||
import org.springframework.cache.jcache.support.TestableCacheResolverFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Repository sample with a @CacheDefaults annotation
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@CacheDefaults(cacheName = "default")
|
||||
public class AnnotatedJCacheableService implements JCacheableService<Long> {
|
||||
|
||||
private final AtomicLong counter = new AtomicLong();
|
||||
|
||||
private final AtomicLong exceptionCounter = new AtomicLong();
|
||||
|
||||
private final Cache defaultCache;
|
||||
|
||||
public AnnotatedJCacheableService(Cache defaultCache) {
|
||||
this.defaultCache = defaultCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult
|
||||
public Long cache(String id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(exceptionCacheName = "exception", nonCachedExceptions = NullPointerException.class)
|
||||
public Long cacheWithException(@CacheKey String id, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
return 0L; // Never reached
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(skipGet = true)
|
||||
public Long cacheAlwaysInvoke(String id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult
|
||||
public Long cacheWithPartialKey(@CacheKey String id, boolean notUsed) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(cacheResolverFactory = TestableCacheResolverFactory.class)
|
||||
public Long cacheWithCustomCacheResolver(String id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class)
|
||||
public Long cacheWithCustomKeyGenerator(String id, String anotherId) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut
|
||||
public void put(String id, @CacheValue Object value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut(cacheFor = UnsupportedOperationException.class)
|
||||
public void putWithException(@CacheKey String id, @CacheValue Object value, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut(afterInvocation = false)
|
||||
public void earlyPut(String id, @CacheValue Object value) {
|
||||
SimpleGeneratedCacheKey key = new SimpleGeneratedCacheKey(id);
|
||||
Cache.ValueWrapper valueWrapper = defaultCache.get(key);
|
||||
if (valueWrapper == null) {
|
||||
throw new AssertionError("Excepted value to be put in cache with key " + key);
|
||||
}
|
||||
Object actual = valueWrapper.get();
|
||||
if (value != actual) { // instance check on purpose
|
||||
throw new AssertionError("Wrong value set in cache with key " + key + ". " +
|
||||
"Expected=" + value + ", but got=" + actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut(afterInvocation = false)
|
||||
public void earlyPutWithException(@CacheKey String id, @CacheValue Object value, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove
|
||||
public void remove(String id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove(noEvictFor = NullPointerException.class)
|
||||
public void removeWithException(@CacheKey String id, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove(afterInvocation = false)
|
||||
public void earlyRemove(String id) {
|
||||
SimpleGeneratedCacheKey key = new SimpleGeneratedCacheKey(id);
|
||||
Cache.ValueWrapper valueWrapper = defaultCache.get(key);
|
||||
if (valueWrapper != null) {
|
||||
throw new AssertionError("Value with key " + key + " expected to be already remove from cache");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemove(afterInvocation = false, evictFor = UnsupportedOperationException.class)
|
||||
public void earlyRemoveWithException(@CacheKey String id, boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll
|
||||
public void removeAll() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll(noEvictFor = NullPointerException.class)
|
||||
public void removeAllWithException(boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll(afterInvocation = false)
|
||||
public void earlyRemoveAll() {
|
||||
ConcurrentHashMap<?, ?> nativeCache = (ConcurrentHashMap<?, ?>) defaultCache.getNativeCache();
|
||||
if (!nativeCache.isEmpty()) {
|
||||
throw new AssertionError("Cache was expected to be empty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheRemoveAll(afterInvocation = false, evictFor = UnsupportedOperationException.class)
|
||||
public void earlyRemoveAllWithException(boolean matchFilter) {
|
||||
throwException(matchFilter);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void noAnnotation() {
|
||||
}
|
||||
|
||||
@CacheRemove
|
||||
@CacheRemoveAll
|
||||
public void multiAnnotations() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long exceptionInvocations() {
|
||||
return exceptionCounter.get();
|
||||
}
|
||||
|
||||
private void throwException(boolean matchFilter) {
|
||||
long count = exceptionCounter.getAndIncrement();
|
||||
if (matchFilter) {
|
||||
throw new UnsupportedOperationException("Expected exception (" + count + ")");
|
||||
}
|
||||
else {
|
||||
throw new NullPointerException("Expected exception (" + count + ")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.cache.annotation.CacheDefaults;
|
||||
import javax.cache.annotation.CacheKeyGenerator;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.jcache.AbstractJCacheTests;
|
||||
import org.springframework.cache.jcache.model.BaseKeyCacheOperation;
|
||||
import org.springframework.cache.jcache.model.CachePutOperation;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveAllOperation;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveOperation;
|
||||
import org.springframework.cache.jcache.model.CacheResultOperation;
|
||||
import org.springframework.cache.jcache.model.JCacheOperation;
|
||||
import org.springframework.cache.jcache.support.TestableCacheKeyGenerator;
|
||||
import org.springframework.cache.jcache.support.TestableCacheResolver;
|
||||
import org.springframework.cache.jcache.support.TestableCacheResolverFactory;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class AnnotationCacheOperationSourceTests extends AbstractJCacheTests {
|
||||
|
||||
private final DefaultJCacheOperationSource source = new DefaultJCacheOperationSource();
|
||||
|
||||
private final StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
source.setApplicationContext(applicationContext);
|
||||
source.setKeyGenerator(defaultKeyGenerator);
|
||||
source.setCacheResolver(defaultCacheResolver);
|
||||
source.setExceptionCacheResolver(defaultExceptionCacheResolver);
|
||||
source.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cache() {
|
||||
CacheResultOperation op = getDefaultCacheOperation(CacheResultOperation.class, String.class);
|
||||
assertDefaults(op);
|
||||
assertNull("Exception caching not enabled so resolver should not be set", op.getExceptionCacheResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWithException() {
|
||||
CacheResultOperation op = getDefaultCacheOperation(CacheResultOperation.class, String.class, boolean.class);
|
||||
assertDefaults(op);
|
||||
assertEquals(defaultExceptionCacheResolver, op.getExceptionCacheResolver());
|
||||
assertEquals("exception", op.getExceptionCacheName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void put() {
|
||||
CachePutOperation op = getDefaultCacheOperation(CachePutOperation.class, String.class, Object.class);
|
||||
assertDefaults(op);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove() {
|
||||
CacheRemoveOperation op = getDefaultCacheOperation(CacheRemoveOperation.class, String.class);
|
||||
assertDefaults(op);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeAll() {
|
||||
CacheRemoveAllOperation op = getDefaultCacheOperation(CacheRemoveAllOperation.class);
|
||||
assertEquals(defaultCacheResolver, op.getCacheResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noAnnotation() {
|
||||
assertNull(getCacheOperation(AnnotatedJCacheableService.class, name.getMethodName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiAnnotations() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
getCacheOperation(AnnotatedJCacheableService.class, name.getMethodName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultCacheNameWithCandidate() {
|
||||
Method m = ReflectionUtils.findMethod(Object.class, "toString");
|
||||
assertEquals("foo", source.determineCacheName(m, null, "foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultCacheNameWithDefaults() {
|
||||
Method m = ReflectionUtils.findMethod(Object.class, "toString");
|
||||
CacheDefaults mock = mock(CacheDefaults.class);
|
||||
given(mock.cacheName()).willReturn("");
|
||||
assertEquals("java.lang.Object.toString()", source.determineCacheName(m, mock, ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultCacheNameNoDefaults() {
|
||||
Method m = ReflectionUtils.findMethod(Object.class, "toString");
|
||||
assertEquals("java.lang.Object.toString()", source.determineCacheName(m, null, ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultCacheNameWithParameters() {
|
||||
Method m = ReflectionUtils.findMethod(Comparator.class, "compare", Object.class, Object.class);
|
||||
assertEquals("java.util.Comparator.compare(java.lang.Object,java.lang.Object)",
|
||||
source.determineCacheName(m, null, ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customCacheResolver() {
|
||||
CacheResultOperation operation =
|
||||
getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class);
|
||||
assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class);
|
||||
assertJCacheResolver(operation.getExceptionCacheResolver(), null);
|
||||
assertEquals(defaultKeyGenerator, operation.getKeyGenerator());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customKeyGenerator() {
|
||||
CacheResultOperation operation =
|
||||
getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class);
|
||||
assertEquals(defaultCacheResolver, operation.getCacheResolver());
|
||||
assertNull(operation.getExceptionCacheResolver());
|
||||
assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customKeyGeneratorSpringBean() {
|
||||
TestableCacheKeyGenerator bean = new TestableCacheKeyGenerator();
|
||||
applicationContext.getBeanFactory().registerSingleton("fooBar", bean);
|
||||
CacheResultOperation operation =
|
||||
getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class);
|
||||
assertEquals(defaultCacheResolver, operation.getCacheResolver());
|
||||
assertNull(operation.getExceptionCacheResolver());
|
||||
KeyGeneratorAdapter adapter = (KeyGeneratorAdapter) operation.getKeyGenerator();
|
||||
assertSame(bean, adapter.getTarget()); // take bean from context
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customKeyGeneratorAndCacheResolver() {
|
||||
CacheResultOperation operation = getCacheOperation(CacheResultOperation.class,
|
||||
CustomServiceWithDefaults.class, name.getMethodName(), Long.class);
|
||||
assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class);
|
||||
assertJCacheResolver(operation.getExceptionCacheResolver(), null);
|
||||
assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customKeyGeneratorAndCacheResolverWithExceptionName() {
|
||||
CacheResultOperation operation = getCacheOperation(CacheResultOperation.class,
|
||||
CustomServiceWithDefaults.class, name.getMethodName(), Long.class);
|
||||
assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class);
|
||||
assertJCacheResolver(operation.getExceptionCacheResolver(), TestableCacheResolver.class);
|
||||
assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class);
|
||||
}
|
||||
|
||||
private void assertDefaults(BaseKeyCacheOperation<?> operation) {
|
||||
assertEquals(defaultCacheResolver, operation.getCacheResolver());
|
||||
assertEquals(defaultKeyGenerator, operation.getKeyGenerator());
|
||||
}
|
||||
|
||||
protected <T extends JCacheOperation<?>> T getDefaultCacheOperation(Class<T> operationType, Class<?>... parameterTypes) {
|
||||
return getCacheOperation(operationType, AnnotatedJCacheableService.class, name.getMethodName(), parameterTypes);
|
||||
}
|
||||
|
||||
protected <T extends JCacheOperation<?>> T getCacheOperation(Class<T> operationType, Class<?> targetType,
|
||||
String methodName, Class<?>... parameterTypes) {
|
||||
JCacheOperation<?> result = getCacheOperation(targetType, methodName, parameterTypes);
|
||||
assertNotNull(result);
|
||||
assertEquals(operationType, result.getClass());
|
||||
return operationType.cast(result);
|
||||
}
|
||||
|
||||
private JCacheOperation<?> getCacheOperation(Class<?> targetType, String methodName, Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(targetType, methodName, parameterTypes);
|
||||
Assert.notNull(method, "requested method '" + methodName + "'does not exist");
|
||||
return source.getCacheOperation(method, targetType);
|
||||
}
|
||||
|
||||
private void assertJCacheResolver(CacheResolver actual,
|
||||
Class<? extends javax.cache.annotation.CacheResolver> expectedTargetType) {
|
||||
if (expectedTargetType == null) {
|
||||
assertNull(actual);
|
||||
}
|
||||
else {
|
||||
assertEquals("Wrong cache resolver implementation", CacheResolverAdapter.class, actual.getClass());
|
||||
CacheResolverAdapter adapter = (CacheResolverAdapter) actual;
|
||||
assertEquals("Wrong target JCache implementation", expectedTargetType, adapter.getTarget().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertCacheKeyGenerator(KeyGenerator actual,
|
||||
Class<? extends CacheKeyGenerator> expectedTargetType) {
|
||||
assertEquals("Wrong cache resolver implementation", KeyGeneratorAdapter.class, actual.getClass());
|
||||
KeyGeneratorAdapter adapter = (KeyGeneratorAdapter) actual;
|
||||
assertEquals("Wrong target CacheKeyGenerator implementation", expectedTargetType, adapter.getTarget().getClass());
|
||||
}
|
||||
|
||||
|
||||
static class CustomService {
|
||||
|
||||
@CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class)
|
||||
public Object customKeyGenerator(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class)
|
||||
public Object customKeyGeneratorSpringBean(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CacheResult(cacheResolverFactory = TestableCacheResolverFactory.class)
|
||||
public Object customCacheResolver(Long id) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@CacheDefaults(cacheResolverFactory = TestableCacheResolverFactory.class,
|
||||
cacheKeyGenerator = TestableCacheKeyGenerator.class)
|
||||
static class CustomServiceWithDefaults {
|
||||
|
||||
@CacheResult
|
||||
public Object customKeyGeneratorAndCacheResolver(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CacheResult(exceptionCacheName = "exception")
|
||||
public Object customKeyGeneratorAndCacheResolverWithExceptionName(Long id) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationContext;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheResolver;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.jcache.AbstractJCacheTests;
|
||||
import org.springframework.cache.jcache.model.CacheResultOperation;
|
||||
import org.springframework.cache.jcache.model.DefaultCacheMethodDetails;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CacheResolverAdapterTests extends AbstractJCacheTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
|
||||
@Test
|
||||
public void resolveSimpleCache() {
|
||||
DefaultCacheInvocationContext<?> dummyContext = createDummyContext();
|
||||
CacheResolverAdapter adapter = new CacheResolverAdapter(getCacheResolver(dummyContext, "testCache"));
|
||||
Collection<? extends Cache> caches = adapter.resolveCaches(dummyContext);
|
||||
assertNotNull(caches);
|
||||
assertEquals(1, caches.size());
|
||||
assertEquals("testCache", caches.iterator().next().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveUnknownCache() {
|
||||
DefaultCacheInvocationContext<?> dummyContext = createDummyContext();
|
||||
CacheResolverAdapter adapter = new CacheResolverAdapter(getCacheResolver(dummyContext, null));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
adapter.resolveCaches(dummyContext);
|
||||
}
|
||||
|
||||
protected CacheResolver getCacheResolver(CacheInvocationContext<? extends Annotation> context, String cacheName) {
|
||||
CacheResolver cacheResolver = mock(CacheResolver.class);
|
||||
final javax.cache.Cache cache;
|
||||
if (cacheName == null) {
|
||||
cache = null;
|
||||
}
|
||||
else {
|
||||
cache = mock(javax.cache.Cache.class);
|
||||
given(cache.getName()).willReturn(cacheName);
|
||||
}
|
||||
given(cacheResolver.resolveCache(context)).willReturn(cache);
|
||||
return cacheResolver;
|
||||
}
|
||||
|
||||
protected DefaultCacheInvocationContext<?> createDummyContext() {
|
||||
Method method = ReflectionUtils.findMethod(Sample.class, "get", String.class);
|
||||
Assert.notNull(method);
|
||||
CacheResult cacheAnnotation = method.getAnnotation(CacheResult.class);
|
||||
CacheMethodDetails<CacheResult> methodDetails =
|
||||
new DefaultCacheMethodDetails<>(method, cacheAnnotation, "test");
|
||||
CacheResultOperation operation = new CacheResultOperation(methodDetails,
|
||||
defaultCacheResolver, defaultKeyGenerator, defaultExceptionCacheResolver);
|
||||
return new DefaultCacheInvocationContext<CacheResult>(operation, new Sample(), new Object[] {"id"});
|
||||
}
|
||||
|
||||
|
||||
static class Sample {
|
||||
|
||||
@CacheResult
|
||||
private Object get(String id) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.interceptor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.jcache.AbstractJCacheTests;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JCacheInterceptorTests extends AbstractJCacheTests {
|
||||
|
||||
private final CacheOperationInvoker dummyInvoker = new DummyInvoker(null);
|
||||
|
||||
@Test
|
||||
public void severalCachesNotSupported() {
|
||||
JCacheInterceptor interceptor = createInterceptor(createOperationSource(
|
||||
cacheManager, new TestCacheResolver("default", "exception"),
|
||||
defaultExceptionCacheResolver, defaultKeyGenerator));
|
||||
|
||||
AnnotatedJCacheableService service = new AnnotatedJCacheableService(cacheManager.getCache("default"));
|
||||
Method m = ReflectionUtils.findMethod(AnnotatedJCacheableService.class, "cache", String.class);
|
||||
|
||||
try {
|
||||
interceptor.execute(dummyInvoker, service, m, new Object[] {"myId"});
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
assertTrue(e.getMessage().contains("JSR-107 only supports a single cache."));
|
||||
}
|
||||
catch (Throwable t) {
|
||||
fail("Unexpected: " + t);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCacheCouldBeResolved() {
|
||||
JCacheInterceptor interceptor = createInterceptor(createOperationSource(
|
||||
cacheManager, new TestCacheResolver(), // Returns empty list
|
||||
defaultExceptionCacheResolver, defaultKeyGenerator));
|
||||
|
||||
AnnotatedJCacheableService service = new AnnotatedJCacheableService(cacheManager.getCache("default"));
|
||||
Method m = ReflectionUtils.findMethod(AnnotatedJCacheableService.class, "cache", String.class);
|
||||
|
||||
try {
|
||||
interceptor.execute(dummyInvoker, service, m, new Object[] {"myId"});
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
assertTrue(e.getMessage().contains("Cache could not have been resolved for"));
|
||||
}
|
||||
catch (Throwable t) {
|
||||
fail("Unexpected: " + t);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheManagerMandatoryIfCacheResolverNotSetSet() {
|
||||
thrown.expect(IllegalStateException.class);
|
||||
thrown.expectMessage("'cacheManager' is required");
|
||||
createOperationSource(null, null, null, defaultKeyGenerator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheManagerOptionalIfCacheResolversSet() {
|
||||
createOperationSource(null, defaultCacheResolver, defaultExceptionCacheResolver, defaultKeyGenerator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheResultReturnsProperType() throws Throwable {
|
||||
JCacheInterceptor interceptor = createInterceptor(createOperationSource(
|
||||
cacheManager, defaultCacheResolver,
|
||||
defaultExceptionCacheResolver, defaultKeyGenerator));
|
||||
|
||||
AnnotatedJCacheableService service = new AnnotatedJCacheableService(cacheManager.getCache("default"));
|
||||
Method m = ReflectionUtils.findMethod(AnnotatedJCacheableService.class, "cache", String.class);
|
||||
|
||||
CacheOperationInvoker invoker = new DummyInvoker(0L);
|
||||
Object execute = interceptor.execute(invoker, service, m, new Object[] {"myId"});
|
||||
assertNotNull("result cannot be null.", execute);
|
||||
assertEquals("Wrong result type", Long.class, execute.getClass());
|
||||
assertEquals("Wrong result", 0L, execute);
|
||||
}
|
||||
|
||||
protected JCacheOperationSource createOperationSource(CacheManager cacheManager,
|
||||
CacheResolver cacheResolver,
|
||||
CacheResolver exceptionCacheResolver,
|
||||
KeyGenerator keyGenerator) {
|
||||
DefaultJCacheOperationSource source = new DefaultJCacheOperationSource();
|
||||
source.setApplicationContext(new StaticApplicationContext());
|
||||
source.setCacheManager(cacheManager);
|
||||
source.setCacheResolver(cacheResolver);
|
||||
source.setExceptionCacheResolver(exceptionCacheResolver);
|
||||
source.setKeyGenerator(keyGenerator);
|
||||
source.afterPropertiesSet();
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
protected JCacheInterceptor createInterceptor(JCacheOperationSource source) {
|
||||
JCacheInterceptor interceptor = new JCacheInterceptor();
|
||||
interceptor.setCacheOperationSource(source);
|
||||
interceptor.afterPropertiesSet();
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
||||
private class TestCacheResolver implements CacheResolver {
|
||||
|
||||
private final String[] cacheNames;
|
||||
|
||||
private TestCacheResolver(String... cacheNames) {
|
||||
this.cacheNames = cacheNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
List<Cache> result = new ArrayList<Cache>();
|
||||
for (String cacheName : cacheNames) {
|
||||
result.add(cacheManager.getCache(cacheName));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyInvoker implements CacheOperationInvoker {
|
||||
|
||||
private final Object result;
|
||||
|
||||
private DummyInvoker(Object result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke() throws ThrowableWrapper {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cache.jcache.AbstractJCacheTests;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractCacheOperationTests<O extends JCacheOperation<?>> extends AbstractJCacheTests {
|
||||
|
||||
protected final SampleObject sampleInstance = new SampleObject();
|
||||
|
||||
protected abstract O createSimpleOperation();
|
||||
|
||||
|
||||
@Test
|
||||
public void simple() {
|
||||
O operation = createSimpleOperation();
|
||||
assertEquals("Wrong cache name", "simpleCache", operation.getCacheName());
|
||||
assertEquals("Unexpected number of annotation on " + operation.getMethod(),
|
||||
1, operation.getAnnotations().size());
|
||||
assertEquals("Wrong method annotation", operation.getCacheAnnotation(),
|
||||
operation.getAnnotations().iterator().next());
|
||||
|
||||
assertNotNull("cache resolver should be set", operation.getCacheResolver());
|
||||
}
|
||||
|
||||
protected void assertCacheInvocationParameter(CacheInvocationParameter actual, Class<?> targetType,
|
||||
Object value, int position) {
|
||||
assertEquals("wrong parameter type for " + actual, targetType, actual.getRawType());
|
||||
assertEquals("wrong parameter value for " + actual, value, actual.getValue());
|
||||
assertEquals("wrong parameter position for " + actual, position, actual.getParameterPosition());
|
||||
}
|
||||
|
||||
protected <A extends Annotation> CacheMethodDetails<A> create(Class<A> annotationType,
|
||||
Class<?> targetType, String methodName,
|
||||
Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(targetType, methodName, parameterTypes);
|
||||
Assert.notNull(method, "requested method '" + methodName + "'does not exist");
|
||||
A cacheAnnotation = method.getAnnotation(annotationType);
|
||||
return new DefaultCacheMethodDetails<A>(method, cacheAnnotation, getCacheName(cacheAnnotation));
|
||||
}
|
||||
|
||||
private static String getCacheName(Annotation annotation) {
|
||||
Object cacheName = AnnotationUtils.getValue(annotation, "cacheName");
|
||||
return cacheName != null ? cacheName.toString() : "test";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CachePut;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CachePutOperationTests extends AbstractCacheOperationTests<CachePutOperation> {
|
||||
|
||||
@Override
|
||||
protected CachePutOperation createSimpleOperation() {
|
||||
CacheMethodDetails<CachePut> methodDetails = create(CachePut.class,
|
||||
SampleObject.class, "simplePut", Long.class, SampleObject.class);
|
||||
return createDefaultOperation(methodDetails);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simplePut() {
|
||||
CachePutOperation operation = createSimpleOperation();
|
||||
|
||||
CacheInvocationParameter[] allParameters = operation.getAllParameters(2L, sampleInstance);
|
||||
assertEquals(2, allParameters.length);
|
||||
assertCacheInvocationParameter(allParameters[0], Long.class, 2L, 0);
|
||||
assertCacheInvocationParameter(allParameters[1], SampleObject.class, sampleInstance, 1);
|
||||
|
||||
CacheInvocationParameter valueParameter = operation.getValueParameter(2L, sampleInstance);
|
||||
assertNotNull(valueParameter);
|
||||
assertCacheInvocationParameter(valueParameter, SampleObject.class, sampleInstance, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCacheValue() {
|
||||
CacheMethodDetails<CachePut> methodDetails = create(CachePut.class,
|
||||
SampleObject.class, "noCacheValue", Long.class);
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
createDefaultOperation(methodDetails);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiCacheValues() {
|
||||
CacheMethodDetails<CachePut> methodDetails = create(CachePut.class,
|
||||
SampleObject.class, "multiCacheValues", Long.class, SampleObject.class, SampleObject.class);
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
createDefaultOperation(methodDetails);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeWithWrongParameters() {
|
||||
CachePutOperation operation = createSimpleOperation();
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
operation.getValueParameter(2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullPutConfig() {
|
||||
CacheMethodDetails<CachePut> methodDetails = create(CachePut.class,
|
||||
SampleObject.class, "fullPutConfig", Long.class, SampleObject.class);
|
||||
CachePutOperation operation = createDefaultOperation(methodDetails);
|
||||
assertTrue(operation.isEarlyPut());
|
||||
assertNotNull(operation.getExceptionTypeFilter());
|
||||
assertTrue(operation.getExceptionTypeFilter().match(IOException.class));
|
||||
assertFalse(operation.getExceptionTypeFilter().match(NullPointerException.class));
|
||||
}
|
||||
|
||||
private CachePutOperation createDefaultOperation(CacheMethodDetails<CachePut> methodDetails) {
|
||||
return new CachePutOperation(methodDetails, defaultCacheResolver, defaultKeyGenerator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CacheRemoveAllOperationTests extends AbstractCacheOperationTests<CacheRemoveAllOperation> {
|
||||
|
||||
@Override
|
||||
protected CacheRemoveAllOperation createSimpleOperation() {
|
||||
CacheMethodDetails<CacheRemoveAll> methodDetails = create(CacheRemoveAll.class,
|
||||
SampleObject.class, "simpleRemoveAll");
|
||||
|
||||
return new CacheRemoveAllOperation(methodDetails, defaultCacheResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleRemoveAll() {
|
||||
CacheRemoveAllOperation operation = createSimpleOperation();
|
||||
|
||||
CacheInvocationParameter[] allParameters = operation.getAllParameters();
|
||||
assertEquals(0, allParameters.length);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CacheRemoveOperationTests extends AbstractCacheOperationTests<CacheRemoveOperation> {
|
||||
|
||||
@Override
|
||||
protected CacheRemoveOperation createSimpleOperation() {
|
||||
CacheMethodDetails<CacheRemove> methodDetails = create(CacheRemove.class,
|
||||
SampleObject.class, "simpleRemove", Long.class);
|
||||
|
||||
return new CacheRemoveOperation(methodDetails, defaultCacheResolver, defaultKeyGenerator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleRemove() {
|
||||
CacheRemoveOperation operation = createSimpleOperation();
|
||||
|
||||
CacheInvocationParameter[] allParameters = operation.getAllParameters(2L);
|
||||
assertEquals(1, allParameters.length);
|
||||
assertCacheInvocationParameter(allParameters[0], Long.class, 2L, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheKey;
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CacheResultOperationTests extends AbstractCacheOperationTests<CacheResultOperation> {
|
||||
|
||||
@Override
|
||||
protected CacheResultOperation createSimpleOperation() {
|
||||
CacheMethodDetails<CacheResult> methodDetails = create(CacheResult.class,
|
||||
SampleObject.class, "simpleGet", Long.class);
|
||||
|
||||
return new CacheResultOperation(methodDetails, defaultCacheResolver, defaultKeyGenerator,
|
||||
defaultExceptionCacheResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleGet() {
|
||||
CacheResultOperation operation = createSimpleOperation();
|
||||
|
||||
assertNotNull(operation.getKeyGenerator());
|
||||
assertNotNull(operation.getExceptionCacheResolver());
|
||||
|
||||
assertNull(operation.getExceptionCacheName());
|
||||
assertEquals(defaultExceptionCacheResolver, operation.getExceptionCacheResolver());
|
||||
|
||||
CacheInvocationParameter[] allParameters = operation.getAllParameters(2L);
|
||||
assertEquals(1, allParameters.length);
|
||||
assertCacheInvocationParameter(allParameters[0], Long.class, 2L, 0);
|
||||
|
||||
CacheInvocationParameter[] keyParameters = operation.getKeyParameters(2L);
|
||||
assertEquals(1, keyParameters.length);
|
||||
assertCacheInvocationParameter(keyParameters[0], Long.class, 2L, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiParameterKey() {
|
||||
CacheMethodDetails<CacheResult> methodDetails = create(CacheResult.class,
|
||||
SampleObject.class, "multiKeysGet", Long.class, Boolean.class, String.class);
|
||||
CacheResultOperation operation = createDefaultOperation(methodDetails);
|
||||
|
||||
CacheInvocationParameter[] keyParameters = operation.getKeyParameters(3L, Boolean.TRUE, "Foo");
|
||||
assertEquals(2, keyParameters.length);
|
||||
assertCacheInvocationParameter(keyParameters[0], Long.class, 3L, 0);
|
||||
assertCacheInvocationParameter(keyParameters[1], String.class, "Foo", 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeWithWrongParameters() {
|
||||
CacheMethodDetails<CacheResult> methodDetails = create(CacheResult.class,
|
||||
SampleObject.class, "anotherSimpleGet", String.class, Long.class);
|
||||
CacheResultOperation operation = createDefaultOperation(methodDetails);
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
operation.getAllParameters("bar"); // missing one argument
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tooManyKeyValues() {
|
||||
CacheMethodDetails<CacheResult> methodDetails = create(CacheResult.class,
|
||||
SampleObject.class, "anotherSimpleGet", String.class, Long.class);
|
||||
CacheResultOperation operation = createDefaultOperation(methodDetails);
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
operation.getKeyParameters("bar"); // missing one argument
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedGet() {
|
||||
CacheMethodDetails<CacheResult> methodDetails = create(CacheResult.class,
|
||||
SampleObject.class, "annotatedGet", Long.class, String.class);
|
||||
CacheResultOperation operation = createDefaultOperation(methodDetails);
|
||||
CacheInvocationParameter[] parameters = operation.getAllParameters(2L, "foo");
|
||||
|
||||
Set<Annotation> firstParameterAnnotations = parameters[0].getAnnotations();
|
||||
assertEquals(1, firstParameterAnnotations.size());
|
||||
assertEquals(CacheKey.class, firstParameterAnnotations.iterator().next().annotationType());
|
||||
|
||||
Set<Annotation> secondParameterAnnotations = parameters[1].getAnnotations();
|
||||
assertEquals(1, secondParameterAnnotations.size());
|
||||
assertEquals(Value.class, secondParameterAnnotations.iterator().next().annotationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullGetConfig() {
|
||||
CacheMethodDetails<CacheResult> methodDetails = create(CacheResult.class,
|
||||
SampleObject.class, "fullGetConfig", Long.class);
|
||||
CacheResultOperation operation = createDefaultOperation(methodDetails);
|
||||
assertTrue(operation.isAlwaysInvoked());
|
||||
assertNotNull(operation.getExceptionTypeFilter());
|
||||
assertTrue(operation.getExceptionTypeFilter().match(IOException.class));
|
||||
assertFalse(operation.getExceptionTypeFilter().match(NullPointerException.class));
|
||||
}
|
||||
|
||||
private CacheResultOperation createDefaultOperation(CacheMethodDetails<CacheResult> methodDetails) {
|
||||
return new CacheResultOperation(methodDetails,
|
||||
defaultCacheResolver, defaultKeyGenerator, defaultCacheResolver);
|
||||
}
|
||||
|
||||
}
|
||||
80
spring-context-support/src/test/java/org/springframework/cache/jcache/model/SampleObject.java
vendored
Normal file
80
spring-context-support/src/test/java/org/springframework/cache/jcache/model/SampleObject.java
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
package org.springframework.cache.jcache.model;
|
||||
|
||||
import javax.cache.annotation.CacheKey;
|
||||
import javax.cache.annotation.CachePut;
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
import javax.cache.annotation.CacheValue;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class SampleObject {
|
||||
|
||||
// Simple
|
||||
|
||||
@CacheResult(cacheName = "simpleCache")
|
||||
public SampleObject simpleGet(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CachePut(cacheName = "simpleCache")
|
||||
public void simplePut(Long id, @CacheValue SampleObject instance) {
|
||||
}
|
||||
|
||||
@CacheRemove(cacheName = "simpleCache")
|
||||
public void simpleRemove(Long id) {
|
||||
}
|
||||
|
||||
@CacheRemoveAll(cacheName = "simpleCache")
|
||||
public void simpleRemoveAll() {
|
||||
}
|
||||
|
||||
@CacheResult(cacheName = "testSimple")
|
||||
public SampleObject anotherSimpleGet(String foo, Long bar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// @CacheKey
|
||||
|
||||
@CacheResult
|
||||
public SampleObject multiKeysGet(@CacheKey Long id, Boolean notUsed,
|
||||
@CacheKey String domain) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// @CacheValue
|
||||
|
||||
@CachePut(cacheName = "simpleCache")
|
||||
public void noCacheValue(Long id) {
|
||||
}
|
||||
|
||||
@CachePut(cacheName = "simpleCache")
|
||||
public void multiCacheValues(Long id, @CacheValue SampleObject instance,
|
||||
@CacheValue SampleObject anotherInstance) {
|
||||
}
|
||||
|
||||
// Parameter annotation
|
||||
|
||||
@CacheResult(cacheName = "simpleCache")
|
||||
public SampleObject annotatedGet(@CacheKey Long id, @Value("${foo}") String foo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Full config
|
||||
|
||||
@CacheResult(cacheName = "simpleCache", skipGet = true,
|
||||
cachedExceptions = Exception.class, nonCachedExceptions = RuntimeException.class)
|
||||
public SampleObject fullGetConfig(@CacheKey Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CachePut(cacheName = "simpleCache", afterInvocation = false,
|
||||
cacheFor = Exception.class, noCacheFor = RuntimeException.class)
|
||||
public void fullPutConfig(@CacheKey Long id, @CacheValue SampleObject instance) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.cache.jcache.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.cache.annotation.CacheKeyGenerator;
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
import javax.cache.annotation.GeneratedCacheKey;
|
||||
|
||||
import org.springframework.cache.jcache.interceptor.SimpleGeneratedCacheKey;
|
||||
|
||||
/**
|
||||
* A simple test key generator that only takes the first key arguments into
|
||||
* account. To be used with a multi parameters key to validate it has been
|
||||
* used properly.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TestableCacheKeyGenerator implements CacheKeyGenerator {
|
||||
|
||||
@Override
|
||||
public GeneratedCacheKey generateCacheKey(CacheKeyInvocationContext<? extends Annotation> context) {
|
||||
return new SimpleGeneratedCacheKey(context.getKeyParameters()[0]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.support;
|
||||
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.cache.Cache;
|
||||
import javax.cache.annotation.CacheInvocationContext;
|
||||
import javax.cache.annotation.CacheResolver;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TestableCacheResolver implements CacheResolver {
|
||||
|
||||
@Override
|
||||
public <K, V> Cache<K, V> resolveCache(CacheInvocationContext<? extends Annotation> cacheInvocationContext) {
|
||||
String cacheName = cacheInvocationContext.getCacheName();
|
||||
Cache<K, V> mock = mock(Cache.class);
|
||||
given(mock.getName()).willReturn(cacheName);
|
||||
return mock;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.jcache.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.cache.annotation.CacheMethodDetails;
|
||||
import javax.cache.annotation.CacheResolver;
|
||||
import javax.cache.annotation.CacheResolverFactory;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TestableCacheResolverFactory implements CacheResolverFactory {
|
||||
|
||||
@Override
|
||||
public CacheResolver getCacheResolver(CacheMethodDetails<? extends Annotation> cacheMethodDetails) {
|
||||
return new TestableCacheResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheResolver getExceptionCacheResolver(CacheMethodDetails<CacheResult> cacheMethodDetails) {
|
||||
return new TestableCacheResolver();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/cache
|
||||
http://www.springframework.org/schema/cache/spring-cache.xsd">
|
||||
|
||||
<cache:annotation-driven proxy-target-class="false" order="0"/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
|
||||
<property name="caches">
|
||||
<set>
|
||||
<ref bean="defaultCache"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="primary"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="secondary"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="exception"/>
|
||||
</bean>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
</bean>
|
||||
|
||||
<bean id="cacheableService" class="org.springframework.cache.jcache.interceptor.AnnotatedJCacheableService">
|
||||
<constructor-arg ref="defaultCache"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
|
||||
|
||||
<bean id="annotationSource" class="org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource">
|
||||
<property name="cacheManager" ref="cacheManager"/>
|
||||
</bean>
|
||||
|
||||
<bean id="cacheInterceptor" class="org.springframework.cache.jcache.interceptor.JCacheInterceptor">
|
||||
<property name="cacheOperationSource" ref="annotationSource"/>
|
||||
</bean>
|
||||
|
||||
<bean id="advisor" class="org.springframework.cache.jcache.interceptor.BeanFactoryJCacheOperationSourceAdvisor">
|
||||
<property name="cacheOperationSource" ref="annotationSource"/>
|
||||
<property name="adviceBeanName" value="cacheInterceptor"/>
|
||||
</bean>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
|
||||
<property name="caches">
|
||||
<set>
|
||||
<ref bean="defaultCache"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="primary"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="secondary"/>
|
||||
</bean>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="exception"/>
|
||||
</bean>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
</bean>
|
||||
|
||||
<bean id="cacheableService" class="org.springframework.cache.jcache.interceptor.AnnotatedJCacheableService">
|
||||
<constructor-arg ref="defaultCache"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,11 +35,12 @@ import org.springframework.util.CollectionUtils;
|
||||
* Spring's annotation-driven cache management capability.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
*/
|
||||
@Configuration
|
||||
public abstract class AbstractCachingConfiguration implements ImportAware {
|
||||
public abstract class AbstractCachingConfiguration<C extends CachingConfigurer> implements ImportAware {
|
||||
|
||||
protected AnnotationAttributes enableCaching;
|
||||
|
||||
@@ -51,7 +52,7 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
|
||||
private Collection<CacheManager> cacheManagerBeans;
|
||||
|
||||
@Autowired(required=false)
|
||||
private Collection<CachingConfigurer> cachingConfigurers;
|
||||
private Collection<C> cachingConfigurers;
|
||||
|
||||
|
||||
@Override
|
||||
@@ -82,9 +83,8 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
|
||||
"Refactor the configuration such that CachingConfigurer is " +
|
||||
"implemented only once or not at all.");
|
||||
}
|
||||
CachingConfigurer cachingConfigurer = cachingConfigurers.iterator().next();
|
||||
this.cacheManager = cachingConfigurer.cacheManager();
|
||||
this.keyGenerator = cachingConfigurer.keyGenerator();
|
||||
C cachingConfigurer = cachingConfigurers.iterator().next();
|
||||
useCachingConfigurer(cachingConfigurer);
|
||||
}
|
||||
else if (!CollectionUtils.isEmpty(cacheManagerBeans)) {
|
||||
int nManagers = cacheManagerBeans.size();
|
||||
@@ -95,8 +95,7 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
|
||||
"to make explicit which CacheManager should be used for " +
|
||||
"annotation-driven cache management.");
|
||||
}
|
||||
CacheManager cacheManager = cacheManagerBeans.iterator().next();
|
||||
this.cacheManager = cacheManager;
|
||||
this.cacheManager = cacheManagerBeans.iterator().next();
|
||||
// keyGenerator remains null; will fall back to default within CacheInterceptor
|
||||
}
|
||||
else {
|
||||
@@ -105,4 +104,13 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
|
||||
"from your configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the configuration from the nominated {@link CachingConfigurer}.
|
||||
*/
|
||||
protected void useCachingConfigurer(C config) {
|
||||
this.cacheManager = config.cacheManager();
|
||||
this.keyGenerator = config.keyGenerator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,24 +16,39 @@
|
||||
|
||||
package org.springframework.cache.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AdviceModeImportSelector;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Selects which implementation of {@link AbstractCachingConfiguration} should be used
|
||||
* Select which implementation of {@link AbstractCachingConfiguration} should be used
|
||||
* based on the value of {@link EnableCaching#mode} on the importing {@code @Configuration}
|
||||
* class.
|
||||
* <p>Detect the presence of JSR-107 and enables JCache support accordingly.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
* @see ProxyCachingConfiguration
|
||||
* @see AnnotationConfigUtils#CACHE_ASPECT_CONFIGURATION_CLASS_NAME
|
||||
* @see AnnotationConfigUtils#JCACHE_ASPECT_CONFIGURATION_CLASS_NAME
|
||||
*/
|
||||
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
|
||||
|
||||
private static final String PROXY_JCACHE_CONFIGURATION_CLASS =
|
||||
"org.springframework.cache.jcache.config.ProxyJCacheConfiguration";
|
||||
|
||||
private static final boolean jsr107Present = ClassUtils.isPresent(
|
||||
"javax.cache.Cache", CachingConfigurationSelector.class.getClassLoader());
|
||||
private static final boolean jCacheImplPresent = ClassUtils.isPresent(
|
||||
PROXY_JCACHE_CONFIGURATION_CLASS, CachingConfigurationSelector.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @return {@link ProxyCachingConfiguration} or {@code AspectJCacheConfiguration} for
|
||||
@@ -43,12 +58,47 @@ public class CachingConfigurationSelector extends AdviceModeImportSelector<Enabl
|
||||
public String[] selectImports(AdviceMode adviceMode) {
|
||||
switch (adviceMode) {
|
||||
case PROXY:
|
||||
return new String[] { AutoProxyRegistrar.class.getName(), ProxyCachingConfiguration.class.getName() };
|
||||
return getProxyImports();
|
||||
case ASPECTJ:
|
||||
return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
|
||||
return getAspectJImports();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}.
|
||||
* <p>Take care of adding the necessary JSR-107 import if it is available.
|
||||
*/
|
||||
private String[] getProxyImports() {
|
||||
List<String> result = new ArrayList<String>();
|
||||
result.add(AutoProxyRegistrar.class.getName());
|
||||
result.add(ProxyCachingConfiguration.class.getName());
|
||||
if (isJCacheAvailable()) {
|
||||
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
|
||||
}
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#ASPECTJ}.
|
||||
* <p>Take care of adding the necessary JSR-107 import if it is available.
|
||||
*/
|
||||
private String[] getAspectJImports() {
|
||||
List<String> result = new ArrayList<String>();
|
||||
result.add(AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
|
||||
if (isJCacheAvailable()) {
|
||||
result.add(AnnotationConfigUtils.JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
|
||||
}
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the JSR-107 API and Spring's jCache implementation are available
|
||||
* in the classpath.
|
||||
*/
|
||||
private boolean isJCacheAvailable() {
|
||||
return jsr107Present && jCacheImplPresent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,6 +75,12 @@ import org.springframework.core.Ordered;
|
||||
* proxy- or AspectJ-based advice that weaves the interceptor into the call stack when
|
||||
* {@link org.springframework.cache.annotation.Cacheable @Cacheable} methods are invoked.
|
||||
*
|
||||
* <p>If the JSR-107 API and Spring's JCache implementation are present, the necessary
|
||||
* components to manage standard cache annotations are also registered. This creates the
|
||||
* proxy- or AspectJ-based advice that weaves the interceptor into the call stack when
|
||||
* methods annotated with {@code CacheResult}, {@code CachePut}, {@code CacheRemove} or
|
||||
* {@code CacheRemoveAll} are invoked.
|
||||
*
|
||||
* <p><strong>A bean of type {@link org.springframework.cache.CacheManager CacheManager}
|
||||
* must be registered</strong>, as there is no reasonable default that the framework can
|
||||
* use as a convention. And whereas the {@code <cache:annotation-driven>} element assumes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,7 +35,7 @@ import org.springframework.context.annotation.Role;
|
||||
* @see CachingConfigurationSelector
|
||||
*/
|
||||
@Configuration
|
||||
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
|
||||
public class ProxyCachingConfiguration extends AbstractCachingConfiguration<CachingConfigurer> {
|
||||
|
||||
@Bean(name=AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cache.config;
|
||||
|
||||
import static org.springframework.context.annotation.AnnotationConfigUtils.*;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.aop.config.AopNamespaceUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -29,7 +31,7 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.w3c.dom.Element;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
@@ -43,11 +45,23 @@ import org.w3c.dom.Element;
|
||||
* '{@code proxy-target-class}' attribute to '{@code true}', which will
|
||||
* result in class-based proxies being created.
|
||||
*
|
||||
* <p>If the JSR-107 API and Spring's JCache implementation are present,
|
||||
* the necessary infrastructure beans required to handle methods annotated
|
||||
* with {@code CacheResult}, {@code CachePut}, {@code CacheRemove} or
|
||||
* {@code CacheRemoveAll} are also registered.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
*/
|
||||
class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final boolean jsr107Present = ClassUtils.isPresent(
|
||||
"javax.cache.Cache", AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static final boolean jCacheImplPresent = ClassUtils.isPresent(
|
||||
JCACHE_OPERATION_SOURCE_CLASS, AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Parses the '{@code <cache:annotation-driven>}' tag. Will
|
||||
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary
|
||||
@@ -62,46 +76,39 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
||||
}
|
||||
else {
|
||||
// mode="proxy"
|
||||
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
|
||||
registerCacheAdvisor(element, parserContext);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerCacheAspect(Element element, ParserContext parserContext) {
|
||||
SpringCachingConfigurer.registerCacheAspect(element, parserContext);
|
||||
if (jsr107Present && jCacheImplPresent) { // Register JCache aspect
|
||||
JCacheCachingConfigurer.registerCacheAspect(element, parserContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCacheAdvisor(Element element, ParserContext parserContext) {
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
|
||||
SpringCachingConfigurer.registerCacheAdvisor(element, parserContext);
|
||||
if (jsr107Present && jCacheImplPresent) { // Register JCache advisor
|
||||
JCacheCachingConfigurer.registerCacheAdvisor(element, parserContext);
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseCacheManagerProperty(Element element, BeanDefinition def) {
|
||||
def.getPropertyValues().add("cacheManager",
|
||||
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a
|
||||
* <pre class="code">
|
||||
* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
|
||||
* <property name="cacheManager" ref="cacheManager"/>
|
||||
* <property name="keyGenerator" ref="keyGenerator"/>
|
||||
* </bean>
|
||||
* </pre>
|
||||
*/
|
||||
private void registerCacheAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(CACHE_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
parseCacheManagerProperty(element, def);
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, def);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CACHE_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
|
||||
* Configure the necessary infrastructure to support the Spring's caching annotations.
|
||||
*/
|
||||
private static class AopAutoProxyConfigurer {
|
||||
|
||||
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
|
||||
private static class SpringCachingConfigurer {
|
||||
|
||||
private static void registerCacheAdvisor(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ADVISOR_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
|
||||
@@ -139,5 +146,93 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
||||
parserContext.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a
|
||||
* <pre class="code">
|
||||
* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
|
||||
* <property name="cacheManager" ref="cacheManager"/>
|
||||
* <property name="keyGenerator" ref="keyGenerator"/>
|
||||
* </bean>
|
||||
* </pre>
|
||||
*/
|
||||
private static void registerCacheAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(CACHE_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
parseCacheManagerProperty(element, def);
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, def);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CACHE_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the necessary infrastructure to support the standard JSR-107 caching annotations.
|
||||
*/
|
||||
private static class JCacheCachingConfigurer {
|
||||
|
||||
private static void registerCacheAdvisor(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(JCACHE_ADVISOR_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
|
||||
// Create the CacheOperationSource definition.
|
||||
BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);
|
||||
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
|
||||
// Create the CacheInterceptor definition.
|
||||
RootBeanDefinition interceptorDef = new RootBeanDefinition(JCACHE_INTERCEPTOR_CLASS);
|
||||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the CacheAdvisor definition.
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(JCACHE_ADVISOR_FACTORY_CLASS);
|
||||
advisorDef.setSource(eleSource);
|
||||
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
|
||||
if (element.hasAttribute("order")) {
|
||||
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
|
||||
}
|
||||
parserContext.getRegistry().registerBeanDefinition(JCACHE_ADVISOR_BEAN_NAME, advisorDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
|
||||
eleSource);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, JCACHE_ADVISOR_BEAN_NAME));
|
||||
parserContext.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerCacheAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(JCACHE_ASPECT_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(JCACHE_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);
|
||||
String sourceName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
def.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, JCACHE_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static RootBeanDefinition createJCacheOperationSourceBeanDefinition(
|
||||
Element element, Object eleSource) {
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(JCACHE_OPERATION_SOURCE_CLASS);
|
||||
sourceDef.setSource(eleSource);
|
||||
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parseCacheManagerProperty(element, sourceDef);
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, sourceDef);
|
||||
return sourceDef;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link CacheOperation} that caches
|
||||
@@ -68,14 +67,13 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* Cache of CacheOperations, keyed by DefaultCacheKey (Method + target Class).
|
||||
* Cache of CacheOperations, keyed by {@link MethodCacheKey} (Method + target Class).
|
||||
* <p>As this base class is not marked Serializable, the cache will be recreated
|
||||
* after serialization - provided that the concrete subclass is Serializable.
|
||||
*/
|
||||
final Map<Object, Collection<CacheOperation>> attributeCache =
|
||||
new ConcurrentHashMap<Object, Collection<CacheOperation>>(1024);
|
||||
|
||||
|
||||
/**
|
||||
* Determine the caching attribute for this method invocation.
|
||||
* <p>Defaults to the class's caching attribute if no method attribute is found.
|
||||
@@ -123,7 +121,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
* @return the cache key (never {@code null})
|
||||
*/
|
||||
protected Object getCacheKey(Method method, Class<?> targetClass) {
|
||||
return new DefaultCacheKey(method, targetClass);
|
||||
return new MethodCacheKey(method, targetClass);
|
||||
}
|
||||
|
||||
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
|
||||
@@ -188,38 +186,4 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default cache key for the CacheOperation cache.
|
||||
*/
|
||||
private static class DefaultCacheKey {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
public DefaultCacheKey(Method method, Class<?> targetClass) {
|
||||
this.method = method;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof DefaultCacheKey)) {
|
||||
return false;
|
||||
}
|
||||
DefaultCacheKey otherKey = (DefaultCacheKey) other;
|
||||
return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(this.targetClass,
|
||||
otherKey.targetClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
spring-context/src/main/java/org/springframework/cache/interceptor/BasicCacheOperation.java
vendored
Normal file
34
spring-context/src/main/java/org/springframework/cache/interceptor/BasicCacheOperation.java
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.interceptor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The base interface that all cache operations must implement.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface BasicCacheOperation {
|
||||
|
||||
/**
|
||||
* Return the cache name(s) associated to the operation.
|
||||
*/
|
||||
Set<String> getCacheNames();
|
||||
|
||||
}
|
||||
@@ -182,7 +182,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
return new CacheOperationContext(operation, method, args, target, targetClass);
|
||||
}
|
||||
|
||||
protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
|
||||
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
|
||||
// check whether aspect is enabled
|
||||
// to cope with cases where the AJ is pulled in automatically
|
||||
if (this.initialized) {
|
||||
@@ -204,7 +204,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
private Object execute(Invoker invoker, CacheOperationContexts contexts) {
|
||||
private Object execute(CacheOperationInvoker invoker, CacheOperationContexts contexts) {
|
||||
// Process any early evictions
|
||||
processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.NO_RESULT);
|
||||
|
||||
@@ -343,12 +343,6 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
}
|
||||
|
||||
|
||||
public interface Invoker {
|
||||
|
||||
Object invoke();
|
||||
}
|
||||
|
||||
|
||||
private class CacheOperationContexts {
|
||||
|
||||
private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts =
|
||||
|
||||
@@ -45,12 +45,13 @@ public class CacheInterceptor extends CacheAspectSupport implements MethodInterc
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
Method method = invocation.getMethod();
|
||||
|
||||
Invoker aopAllianceInvoker = new Invoker() {
|
||||
CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
|
||||
@Override
|
||||
public Object invoke() {
|
||||
try {
|
||||
return invocation.proceed();
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new ThrowableWrapper(ex);
|
||||
}
|
||||
}
|
||||
@@ -58,17 +59,9 @@ public class CacheInterceptor extends CacheAspectSupport implements MethodInterc
|
||||
|
||||
try {
|
||||
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
|
||||
} catch (ThrowableWrapper th) {
|
||||
throw th.original;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ThrowableWrapper extends RuntimeException {
|
||||
private final Throwable original;
|
||||
|
||||
ThrowableWrapper(Throwable original) {
|
||||
this.original = original;
|
||||
catch (CacheOperationInvoker.ThrowableWrapper th) {
|
||||
throw th.getOriginal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Represent the context of the invocation of a cache operation.
|
||||
*
|
||||
* <p>The cache operation is static and independent of a particular invocation,
|
||||
* this gathers the operation and a particular invocation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface CacheOperationInvocationContext<O extends BasicCacheOperation> {
|
||||
|
||||
/**
|
||||
* Return the cache operation
|
||||
*/
|
||||
O getOperation();
|
||||
|
||||
/**
|
||||
* Return the target instance on which the method was invoked
|
||||
*/
|
||||
Object getTarget();
|
||||
|
||||
/**
|
||||
* Return the method
|
||||
*/
|
||||
Method getMethod();
|
||||
|
||||
/**
|
||||
* Return the argument used to invoke the method
|
||||
*/
|
||||
Object[] getArgs();
|
||||
|
||||
}
|
||||
58
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java
vendored
Normal file
58
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.interceptor;
|
||||
|
||||
/**
|
||||
* Abstract the invocation of a cache operation.
|
||||
*
|
||||
* <p>Provide a special exception that can be used to indicate that the
|
||||
* underlying invocation has thrown a checked exception, allowing the
|
||||
* callers to threat these in a different manner if necessary.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface CacheOperationInvoker {
|
||||
|
||||
/**
|
||||
* Invoke the cache operation defined by this instance. Can throw a
|
||||
* {@link ThrowableWrapper} if that operation wants to explicitly
|
||||
* indicate that a checked exception has occurred.
|
||||
* @return the result of the operation
|
||||
* @throws ThrowableWrapper if a checked exception has been thrown
|
||||
*/
|
||||
Object invoke() throws ThrowableWrapper;
|
||||
|
||||
/**
|
||||
* Wrap any exception thrown while invoking {@link #invoke()}
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static class ThrowableWrapper extends RuntimeException {
|
||||
|
||||
private final Throwable original;
|
||||
|
||||
public ThrowableWrapper(Throwable original) {
|
||||
super(original.getMessage(), original);
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
public Throwable getOriginal() {
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
41
spring-context/src/main/java/org/springframework/cache/interceptor/CacheResolver.java
vendored
Normal file
41
spring-context/src/main/java/org/springframework/cache/interceptor/CacheResolver.java
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.interceptor;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
* Determine the {@link Cache} instance(s) to use for an intercepted method invocation.
|
||||
*
|
||||
* <p>Implementations MUST be thread-safe.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface CacheResolver {
|
||||
|
||||
/**
|
||||
* Return the cache(s) to use for the specified invocation.
|
||||
*
|
||||
* @param context the context of the particular invocation
|
||||
* @return the cache(s) to use (never null)
|
||||
*/
|
||||
Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
|
||||
|
||||
}
|
||||
47
spring-context/src/main/java/org/springframework/cache/interceptor/MethodCacheKey.java
vendored
Normal file
47
spring-context/src/main/java/org/springframework/cache/interceptor/MethodCacheKey.java
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Represent a method on a particular {@link Class} and is suitable as a key.
|
||||
* <p>Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public final class MethodCacheKey {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
public MethodCacheKey(Method method, Class<?> targetClass) {
|
||||
Assert.notNull(method, "method must be set.");
|
||||
Assert.notNull(targetClass, "targetClass must be set.");
|
||||
this.method = method;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof MethodCacheKey)) {
|
||||
return false;
|
||||
}
|
||||
MethodCacheKey otherKey = (MethodCacheKey) other;
|
||||
return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(this.targetClass,
|
||||
otherKey.targetClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
|
||||
}
|
||||
|
||||
}
|
||||
54
spring-context/src/main/java/org/springframework/cache/interceptor/SimpleCacheResolver.java
vendored
Normal file
54
spring-context/src/main/java/org/springframework/cache/interceptor/SimpleCacheResolver.java
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cache.interceptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheResolver} that resolves the {@link Cache} instance(s)
|
||||
* based on a configurable {@link CacheManager} and the name of the
|
||||
* cache(s): {@link BasicCacheOperation#getCacheNames()}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see BasicCacheOperation#getCacheNames()
|
||||
*/
|
||||
public class SimpleCacheResolver implements CacheResolver {
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
public SimpleCacheResolver(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
Collection<Cache> result = new ArrayList<Cache>();
|
||||
for (String cacheName : context.getOperation().getCacheNames()) {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
Assert.notNull(cache, "Cannot find cache named '" + cacheName + "' for " + context.getOperation());
|
||||
result.add(cache);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,11 +30,12 @@ import org.springframework.util.StringUtils;
|
||||
* @see SimpleKeyGenerator
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class SimpleKey implements Serializable {
|
||||
public class SimpleKey implements Serializable {
|
||||
|
||||
public static final SimpleKey EMPTY = new SimpleKey();
|
||||
|
||||
private final Object[] params;
|
||||
private final int hashCode;
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,9 +46,9 @@ public final class SimpleKey implements Serializable {
|
||||
Assert.notNull(elements, "Elements must not be null");
|
||||
this.params = new Object[elements.length];
|
||||
System.arraycopy(elements, 0, this.params, 0, elements.length);
|
||||
this.hashCode = Arrays.deepHashCode(this.params);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj || (obj instanceof SimpleKey
|
||||
@@ -55,13 +56,13 @@ public final class SimpleKey implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.deepHashCode(this.params);
|
||||
public final int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SimpleKey [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
|
||||
return getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,6 +48,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
* @see ContextAnnotationAutowireCandidateResolver
|
||||
* @see CommonAnnotationBeanPostProcessor
|
||||
@@ -147,6 +148,48 @@ public class AnnotationConfigUtils {
|
||||
public static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
|
||||
"org.springframework.cache.aspectj.AspectJCachingConfiguration";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed JSR-107 cache advisor.
|
||||
*/
|
||||
public static final String JCACHE_ADVISOR_BEAN_NAME =
|
||||
"org.springframework.cache.config.internalJCacheAdvisor";
|
||||
|
||||
/**
|
||||
* The class name of the JSR-107 cache operation source.
|
||||
*/
|
||||
public static final String JCACHE_OPERATION_SOURCE_CLASS
|
||||
= "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource";
|
||||
|
||||
/**
|
||||
* The class name of the JSR-107 cache interceptor.
|
||||
*/
|
||||
public static final String JCACHE_INTERCEPTOR_CLASS =
|
||||
"org.springframework.cache.jcache.interceptor.JCacheInterceptor";
|
||||
|
||||
/**
|
||||
* The class name of the JSR-107 cache advisor factory.
|
||||
*/
|
||||
public static final String JCACHE_ADVISOR_FACTORY_CLASS =
|
||||
"org.springframework.cache.jcache.interceptor.BeanFactoryJCacheOperationSourceAdvisor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed JSR-107 cache aspect.
|
||||
*/
|
||||
public static final String JCACHE_ASPECT_BEAN_NAME =
|
||||
"org.springframework.cache.config.internalJCacheAspect";
|
||||
|
||||
/**
|
||||
* The class name of the AspectJ JSR-107 cache aspect.
|
||||
*/
|
||||
public static final String JCACHE_ASPECT_CLASS_NAME =
|
||||
"org.springframework.cache.aspectj.JCacheCacheAspect";
|
||||
|
||||
/**
|
||||
* The name of the AspectJ JSR-107 cache aspect @{@code Configuration} class.
|
||||
*/
|
||||
public static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME =
|
||||
"org.springframework.cache.aspectj.AspectJJCacheConfiguration";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed JPA annotation processor.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.util.filter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An {@link InstanceFilter} implementation that handles exception types. A type
|
||||
* will match against a given candidate if it is assignable to that candidate.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class ExceptionTypeFilter extends InstanceFilter<Class<? extends Throwable>> {
|
||||
|
||||
public ExceptionTypeFilter(Collection<? extends Class<? extends Throwable>> includes,
|
||||
Collection<? extends Class<? extends Throwable>> excludes, boolean matchIfEmpty) {
|
||||
super(includes, excludes, matchIfEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean match(Class<? extends Throwable> instance, Class<? extends Throwable> candidate) {
|
||||
return candidate.isAssignableFrom(instance);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.util.filter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple instance filter that checks if a given instance match based on
|
||||
* a collection of includes and excludes element.
|
||||
*
|
||||
* <p>Subclasses may want to override {@link #match(Object, Object)} to provide
|
||||
* a custom matching algorithm.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class InstanceFilter<T> {
|
||||
|
||||
private final Collection<? extends T> includes;
|
||||
|
||||
private final Collection<? extends T> excludes;
|
||||
|
||||
private final boolean matchIfEmpty;
|
||||
|
||||
/**
|
||||
* Create a new instance based on includes/excludes collections.
|
||||
* <p>A particular element will match if it "matches" the one of the element in the
|
||||
* includes list and does not match one of the element in the excludes list.
|
||||
* <p>Subclasses may redefine what matching means. By default, an element match with
|
||||
* another if it is equals according to {@link Object#equals(Object)}
|
||||
* <p>If both collections are empty, {@code matchIfEmpty} defines if
|
||||
* an element matches or not.
|
||||
*
|
||||
* @param includes the collection of includes
|
||||
* @param excludes the collection of excludes
|
||||
* @param matchIfEmpty the matching result if both the includes and the excludes
|
||||
* collections are empty
|
||||
*/
|
||||
public InstanceFilter(Collection<? extends T> includes,
|
||||
Collection<? extends T> excludes, boolean matchIfEmpty) {
|
||||
|
||||
this.includes = includes != null ? includes : Collections.<T>emptyList();
|
||||
this.excludes = excludes != null ? excludes : Collections.<T>emptyList();
|
||||
this.matchIfEmpty = matchIfEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified {code instance} matches this filter.
|
||||
*/
|
||||
public boolean match(T instance) {
|
||||
Assert.notNull(instance, "The instance to match is mandatory.");
|
||||
|
||||
boolean includesSet = !includes.isEmpty();
|
||||
boolean excludesSet = !excludes.isEmpty();
|
||||
if (!includesSet && !excludesSet) {
|
||||
return matchIfEmpty;
|
||||
}
|
||||
|
||||
boolean matchIncludes = match(instance, includes);
|
||||
boolean matchExcludes = match(instance, excludes);
|
||||
|
||||
if (!includesSet) {
|
||||
return !matchExcludes;
|
||||
}
|
||||
|
||||
if (!excludesSet) {
|
||||
return matchIncludes;
|
||||
}
|
||||
return matchIncludes && !matchExcludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified {@code instance} is equal to the
|
||||
* specified {@code candidate}.
|
||||
*
|
||||
* @param instance the instance to handle
|
||||
* @param candidate a candidate defined by this filter
|
||||
* @return {@code true} if the instance matches the candidate
|
||||
*/
|
||||
protected boolean match(T instance, T candidate) {
|
||||
return instance.equals(candidate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified {@code instance} matches one of the candidates.
|
||||
* <p>If the candidates collection is {@code null}, returns {@code false}.
|
||||
*
|
||||
* @param instance the instance to check
|
||||
* @param candidates a list of candidates
|
||||
* @return {@code true} if the instance match or the candidates collection is null
|
||||
*/
|
||||
protected boolean match(T instance, Collection<? extends T> candidates) {
|
||||
for (T candidate : candidates) {
|
||||
if (match(instance, candidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "includes=" + includes + ", excludes=" + excludes + ", matchIfEmpty=" + matchIfEmpty;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.util.filter;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ExceptionTypeFilterTests {
|
||||
|
||||
@Test
|
||||
public void subClassMatch() {
|
||||
ExceptionTypeFilter filter = new ExceptionTypeFilter(
|
||||
asList(RuntimeException.class), null, true);
|
||||
assertTrue(filter.match(RuntimeException.class));
|
||||
assertTrue(filter.match(IllegalStateException.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.util.filter;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class InstanceFilterTests {
|
||||
|
||||
@Test
|
||||
public void emptyFilterApplyMatchIfEmpty() {
|
||||
InstanceFilter<String> filter = new InstanceFilter<String>(null, null, true);
|
||||
match(filter, "foo");
|
||||
match(filter, "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesFilter() {
|
||||
InstanceFilter<String> filter = new InstanceFilter<String>(
|
||||
asList("First", "Second"), null, true);
|
||||
match(filter, "Second");
|
||||
doNotMatch(filter, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void excludesFilter() {
|
||||
InstanceFilter<String> filter = new InstanceFilter<String>(
|
||||
null, asList("First", "Second"), true);
|
||||
doNotMatch(filter, "Second");
|
||||
match(filter, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesAndExcludesFilters() {
|
||||
InstanceFilter<String> filter = new InstanceFilter<String>(
|
||||
asList("foo", "Bar"), asList("First", "Second"), true);
|
||||
doNotMatch(filter, "Second");
|
||||
match(filter, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesAndExcludesFiltersConflict() {
|
||||
InstanceFilter<String> filter = new InstanceFilter<String>(
|
||||
asList("First"), asList("First"), true);
|
||||
doNotMatch(filter, "First");
|
||||
}
|
||||
|
||||
private <T> void match(InstanceFilter<T> filter, T candidate) {
|
||||
assertTrue("filter '" + filter + "' should match " + candidate, filter.match(candidate));
|
||||
}
|
||||
|
||||
private <T> void doNotMatch(InstanceFilter<T> filter, T candidate) {
|
||||
assertFalse("filter '" + filter + "' should not match " + candidate, filter.match(candidate));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user