Cache provider related exceptions handling
This commit adds the necessary infrastructure to handle exceptions thrown by a cache provider in both Spring's and JCache's caching abstractions. Both interceptors can be configured with a CacheErrorHandler that defines several callbacks on typical cache operations. In particular, handleCacheGetError can be implemented in such a way that an exception thrown by the provider is handled as a cache miss by the caching abstraction. The handler can be configured with both CachingConfigurer and the XML namespace (error-handler property) Issue: SPR-9275
This commit is contained in:
@@ -22,6 +22,7 @@ import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -51,6 +52,8 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
||||
|
||||
protected KeyGenerator keyGenerator;
|
||||
|
||||
protected CacheErrorHandler errorHandler;
|
||||
|
||||
@Autowired(required=false)
|
||||
private Collection<CacheManager> cacheManagerBeans;
|
||||
|
||||
@@ -115,6 +118,7 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
||||
this.cacheManager = config.cacheManager();
|
||||
this.cacheResolver = config.cacheResolver();
|
||||
this.keyGenerator = config.keyGenerator();
|
||||
this.errorHandler = config.errorHandler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cache.annotation;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
@@ -104,4 +105,26 @@ public interface CachingConfigurer {
|
||||
*/
|
||||
KeyGenerator keyGenerator();
|
||||
|
||||
/**
|
||||
* Return the {@link CacheErrorHandler} to use to handle cache-related errors.
|
||||
* <p>By default,{@link org.springframework.cache.interceptor.SimpleCacheErrorHandler}
|
||||
* is used and simply throws the exception back at the client.
|
||||
* <p>Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig extends CachingConfigurerSupport {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheErrorHandler errorHandler() {
|
||||
* // configure and return CacheErrorHandler instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See @{@link EnableCaching} for more complete examples.
|
||||
*/
|
||||
CacheErrorHandler errorHandler();
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cache.annotation;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
@@ -45,4 +46,8 @@ public class CachingConfigurerSupport implements CachingConfigurer {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheErrorHandler errorHandler() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,11 +91,11 @@ import org.springframework.core.Ordered;
|
||||
* <p>For those that wish to establish a more direct relationship between
|
||||
* {@code @EnableCaching} and the exact cache manager bean to be used,
|
||||
* the {@link CachingConfigurer} callback interface may be implemented - notice the
|
||||
* {@code implements} clause and the {@code @Override}-annotated methods below:
|
||||
* the {@code @Override}-annotated methods below:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* public class AppConfig extends CachingConfigurerSupport {
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* // configure and return a class having @Cacheable methods
|
||||
@@ -128,9 +128,14 @@ import org.springframework.core.Ordered;
|
||||
* {@code @EnableCaching} will configure Spring's
|
||||
* {@link org.springframework.cache.interceptor.SimpleKeyGenerator SimpleKeyGenerator}
|
||||
* for this purpose, but when implementing {@code CachingConfigurer}, a key generator
|
||||
* must be provided explicitly. Return {@code new SimpleKeyGenerator()} from this method
|
||||
* if no customization is necessary. See {@link CachingConfigurer} Javadoc for further
|
||||
* details.
|
||||
* must be provided explicitly. Return {@code null} or {@code new SimpleKeyGenerator()}
|
||||
* from this method if no customization is necessary.
|
||||
*
|
||||
* <p>{@link CachingConfigurer} offers additional customization options: it is recommended
|
||||
* to extend from {@link org.springframework.cache.annotation.CachingConfigurerSupport
|
||||
* CachingConfigurerSupport} that provides a default implementation for all methods which
|
||||
* can be useful if you do not need to customize everything. See {@link CachingConfigurer}
|
||||
* Javadoc for further details.
|
||||
*
|
||||
* <p>The {@link #mode()} attribute controls how advice is applied; if the mode is
|
||||
* {@link AdviceMode#PROXY} (the default), then the other attributes such as
|
||||
|
||||
@@ -68,6 +68,9 @@ public class ProxyCachingConfiguration extends AbstractCachingConfiguration<Cach
|
||||
if (this.keyGenerator != null) {
|
||||
interceptor.setKeyGenerator(this.keyGenerator);
|
||||
}
|
||||
if (this.errorHandler != null) {
|
||||
interceptor.setErrorHandler(this.errorHandler);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
@@ -102,6 +103,13 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
||||
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
|
||||
}
|
||||
|
||||
private static BeanDefinition parseErrorHandler(Element element, BeanDefinition def) {
|
||||
String name = element.getAttribute("error-handler");
|
||||
if (StringUtils.hasText(name)) {
|
||||
def.getPropertyValues().add("errorHandler", new RuntimeBeanReference(name.trim()));
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the necessary infrastructure to support the Spring's caching annotations.
|
||||
@@ -123,6 +131,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
||||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parseCacheManagerProperty(element, interceptorDef);
|
||||
parseErrorHandler(element, interceptorDef);
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
@@ -186,6 +195,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
||||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
parseErrorHandler(element, interceptorDef);
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the CacheAdvisor definition.
|
||||
|
||||
115
spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java
vendored
Normal file
115
spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A base component for invoking {@link Cache} operations and using a
|
||||
* configurable {@link CacheErrorHandler} when an exception occurs.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.interceptor.CacheErrorHandler
|
||||
*/
|
||||
public abstract class AbstractCacheInvoker {
|
||||
|
||||
private CacheErrorHandler errorHandler;
|
||||
|
||||
protected AbstractCacheInvoker(CacheErrorHandler errorHandler) {
|
||||
Assert.notNull("ErrorHandler must not be null");
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
protected AbstractCacheInvoker() {
|
||||
this(new SimpleCacheErrorHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CacheErrorHandler} instance to use to handle errors
|
||||
* thrown by the cache provider. By default, a {@link SimpleCacheErrorHandler}
|
||||
* is used who throws any exception as is.
|
||||
*/
|
||||
public void setErrorHandler(CacheErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheErrorHandler} to use.
|
||||
*/
|
||||
public CacheErrorHandler getErrorHandler() {
|
||||
return this.errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#get(Object)} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs. Return {@code null}
|
||||
* if the handler does not throw any exception, which simulates a cache
|
||||
* miss in case of error.
|
||||
* @see Cache#get(Object)
|
||||
*/
|
||||
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
|
||||
try {
|
||||
return cache.get(key);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCacheGetError(e, cache, key);
|
||||
return null; // If the exception is handled, return a cache miss
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#put(Object, Object)} on the specified {@link Cache}
|
||||
* and invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doPut(Cache cache, Object key, Object result) {
|
||||
try {
|
||||
cache.put(key, result);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCachePutError(e, cache, key, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#evict(Object)} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doEvict(Cache cache, Object key) {
|
||||
try {
|
||||
cache.evict(key);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCacheEvictError(e, cache, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#clear()} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doClear(Cache cache) {
|
||||
try {
|
||||
cache.clear();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCacheClearError(e, cache);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,7 +69,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class CacheAspectSupport implements InitializingBean, ApplicationContextAware {
|
||||
public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
implements InitializingBean, ApplicationContextAware {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -168,6 +169,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
"to use or set the cache manager to create a default cache resolver based on it.");
|
||||
Assert.state(this.cacheOperationSource != null, "The 'cacheOperationSources' property is required: " +
|
||||
"If there are no cacheable methods, then don't use a cache aspect.");
|
||||
Assert.state(this.getErrorHandler() != null, "The 'errorHandler' is required.");
|
||||
Assert.state(this.applicationContext != null, "The application context was not injected as it should.");
|
||||
this.initialized = true;
|
||||
}
|
||||
@@ -343,14 +345,14 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
for (Cache cache : context.getCaches()) {
|
||||
if (operation.isCacheWide()) {
|
||||
logInvalidating(context, operation, null);
|
||||
cache.clear();
|
||||
doClear(cache);
|
||||
}
|
||||
else {
|
||||
if (key == null) {
|
||||
key = context.generateKey(result);
|
||||
}
|
||||
logInvalidating(context, operation, key);
|
||||
cache.evict(key);
|
||||
doEvict(cache, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,7 +404,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
|
||||
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
|
||||
for (Cache cache : context.getCaches()) {
|
||||
Cache.ValueWrapper wrapper = cache.get(key);
|
||||
Cache.ValueWrapper wrapper = doGet(cache, key);
|
||||
if (wrapper != null) {
|
||||
return wrapper;
|
||||
}
|
||||
@@ -571,7 +573,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
}
|
||||
|
||||
|
||||
private static class CachePutRequest {
|
||||
private class CachePutRequest {
|
||||
|
||||
private final CacheOperationContext context;
|
||||
|
||||
@@ -585,7 +587,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
||||
public void apply(Object result) {
|
||||
if (this.context.canPutToCache(result)) {
|
||||
for (Cache cache : this.context.getCaches()) {
|
||||
cache.put(this.key, result);
|
||||
doPut(cache, this.key, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
78
spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java
vendored
Normal file
78
spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java
vendored
Normal file
@@ -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.interceptor;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
* A strategy for handling cache-related errors. In most cases, any
|
||||
* exception thrown by the provider should simply be thrown back at
|
||||
* the client but, in some circumstances, the infrastructure may need
|
||||
* to handle cache-provider exceptions in a different way.
|
||||
*
|
||||
* <p>Typically, failing to retrieve an object from the cache with
|
||||
* a given id can be transparently managed as a cache miss by not
|
||||
* throwing back such exception.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface CacheErrorHandler {
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* retrieving an item with the specified {@code key}, possibly
|
||||
* rethrowing it as a fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache
|
||||
* @param key the key used to get the item
|
||||
* @see Cache#get(Object)
|
||||
*/
|
||||
void handleCacheGetError(RuntimeException exception, Cache cache, Object key);
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* updating an item with the specified {@code key} and {@code value},
|
||||
* possibly rethrowing it as a fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache
|
||||
* @param key the key used to update the item
|
||||
* @param value the value to associate with the key
|
||||
* @see Cache#put(Object, Object)
|
||||
*/
|
||||
void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value);
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* clearing an item with the specified {@code key}, possibly rethrowing
|
||||
* it as a fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache
|
||||
* @param key the key used to clear the item
|
||||
*/
|
||||
void handleCacheEvictError(RuntimeException exception, Cache cache, Object key);
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* clearing the specified {@link Cache}, possibly rethrowing it as a
|
||||
* fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache to clear
|
||||
*/
|
||||
void handleCacheClearError(RuntimeException exception, Cache cache);
|
||||
|
||||
}
|
||||
49
spring-context/src/main/java/org/springframework/cache/interceptor/SimpleCacheErrorHandler.java
vendored
Normal file
49
spring-context/src/main/java/org/springframework/cache/interceptor/SimpleCacheErrorHandler.java
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheErrorHandler} that does not handle the
|
||||
* exception at all, simply throwing it back at the client.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class SimpleCacheErrorHandler implements CacheErrorHandler {
|
||||
|
||||
@Override
|
||||
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCacheClearError(RuntimeException exception, Cache cache) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user