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:
@@ -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;
|
||||
Reference in New Issue
Block a user