moved cache abstraction from context.support to context

This commit is contained in:
Costin Leau
2011-02-07 17:41:25 +00:00
parent fbb1fa33a1
commit 4da39b48f7
79 changed files with 195 additions and 56 deletions

View File

@@ -0,0 +1,204 @@
/*
* Copyright 2002-2009 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;
/**
* Interface that defines the common cache operations.
*
* <b>Note:</b> Due to the generic use of caching, it is recommended that
* implementations allow storage of <tt>null</tt> values (for example to
* cache methods that return null).
*
* @author Costin Leau
*/
public interface Cache<K, V> {
/**
* Returns the cache name.
*
* @return the cache name.
*/
String getName();
/**
* Returns the the native, underlying cache provider.
*
* @return
*/
Object getNativeCache();
/**
* Returns <tt>true</tt> if this cache contains a mapping for the specified
* key. More formally, returns <tt>true</tt> if and only if
* this cache contains a mapping for a key <tt>k</tt> such that
* <tt>(key==null ? k==null : key.equals(k))</tt>. (There can be
* at most one such mapping.)
*
* @param key key whose presence in this cache is to be tested.
* @return <tt>true</tt> if this cache contains a mapping for the specified
* key.
*/
boolean containsKey(Object key);
/**
* Returns the value to which this cache maps the specified key. Returns
* <tt>null</tt> if the cache contains no mapping for this key. A return
* value of <tt>null</tt> does not <i>necessarily</i> indicate that the
* cache contains no mapping for the key; it's also possible that the cache
* explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
* operation may be used to distinguish these two cases.
*
* <p>More formally, if this cache contains a mapping from a key
* <tt>k</tt> to a value <tt>v</tt> such that <tt>(key==null ? k==null :
* key.equals(k))</tt>, then this method returns <tt>v</tt>; otherwise
* it returns <tt>null</tt>. (There can be at most one such mapping.)
*
* @param key key whose associated value is to be returned.
* @return the value to which this cache maps the specified key, or
* <tt>null</tt> if the cache contains no mapping for this key.
*
* @see #containsKey(Object)
*/
V get(Object key);
/**
* Associates the specified value with the specified key in this cache
* (optional operation). If the cache previously contained a mapping for
* this key, the old value is replaced by the specified value. (A cache
* <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
* if {@link #containsKey(Object) m.containsKey(k)} would return
* <tt>true</tt>.))
*
* @param key key with which the specified value is to be associated.
* @param value value to be associated with the specified key.
* @return previous value associated with specified key, or <tt>null</tt>
* if there was no mapping for key. A <tt>null</tt> return can
* also indicate that the cache previously associated <tt>null</tt>
* with the specified key, if the implementation supports
* <tt>null</tt> values.
*/
V put(K key, V value);
/**
* If the specified key is not already associated with a value, associate it with the given value.
*
* This is equivalent to:
* <pre>
* if (!cache.containsKey(key))
* return cache.put(key, value);
* else
* return cache.get(key);
* </pre>
*
* @param key key with which the specified value is to be associated.
* @param value value to be associated with the specified key.
* @return previous value associated with specified key, or <tt>null</tt>
* if there was no mapping for key. A <tt>null</tt> return can
* also indicate that the cache previously associated <tt>null</tt>
* with the specified key, if the implementation supports
* <tt>null</tt> values.
*/
V putIfAbsent(K key, V value);
/**
* Removes the mapping for this key from this cache if it is present
* (optional operation). More formally, if this cache contains a mapping
* from key <tt>k</tt> to value <tt>v</tt> such that
* <code>(key==null ? k==null : key.equals(k))</code>, that mapping
* is removed. (The cache can contain at most one such mapping.)
*
* <p>Returns the value to which the cache previously associated the key, or
* <tt>null</tt> if the cache contained no mapping for this key. (A
* <tt>null</tt> return can also indicate that the cache previously
* associated <tt>null</tt> with the specified key if the implementation
* supports <tt>null</tt> values.) The cache will not contain a mapping for
* the specified key once the call returns.
*
* @param key key whose mapping is to be removed from the cache.
* @return previous value associated with specified key, or <tt>null</tt>
* if there was no mapping for key.
*/
V remove(Object key);
/**
* Remove entry for key only if currently mapped to given value.
*
* Similar to:
* <pre>
* if ((cache.containsKey(key) && cache.get(key).equals(value)) {
* cache.remove(key);
* return true;
* }
* else
* return false;
* </pre>
*
* @param key key with which the specified value is associated.
* @param value value associated with the specified key.
* @return true if the value was removed, false otherwise
*/
boolean remove(Object key, Object value);
/**
* Replace entry for key only if currently mapped to given value.
*
* Similar to:
* <pre>
* if ((cache.containsKey(key) && cache.get(key).equals(oldValue)) {
* cache.put(key, newValue);
* return true;
* } else return false;
* </pre>
* @param key key with which the specified value is associated.
* @param oldValue value expected to be associated with the specified key.
* @param newValue value to be associated with the specified key.
* @return true if the value was replaced
*/
boolean replace(K key, V oldValue, V newValue);
/**
* Replace entry for key only if currently mapped to some value.
* Acts as
* <pre>
* if ((cache.containsKey(key)) {
* return cache.put(key, value);
* } else return null;
* </pre>
* except that the action is performed atomically.
* @param key key with which the specified value is associated.
* @param value value to be associated with the specified key.
* @return previous value associated with specified key, or <tt>null</tt>
* if there was no mapping for key. A <tt>null</tt> return can
* also indicate that the cache previously associated <tt>null</tt>
* with the specified key, if the implementation supports
* <tt>null</tt> values.
*/
V replace(K key, V value);
/**
* Removes all mappings from the cache.
*/
void clear();
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2002-2009 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;
import java.util.Collection;
/**
* Entity managing {@link Cache}s.
*
* @author Costin Leau
*/
public interface CacheManager {
/**
* Returns the cache associated with the given name.
*
* @param name cache identifier - cannot be null
* @return associated cache or null if none is found
*/
<K, V> Cache<K, V> getCache(String name);
/**
* Returns a collection of the caches known by this cache manager.
*
* @return names of caches known by the cache manager.
*/
Collection<String> getCacheNames();
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2002-2009 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;
import java.lang.reflect.Method;
/**
* Cache 'key' extractor. Used for creating a key based on the given method
* (used as context) and its parameter.
*
* @author Costin Leau
*/
public interface KeyGenerator<K> {
K extract(Method method, Object... params);
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2010 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.annotation;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.cache.interceptor.AbstractFallbackCacheDefinitionSource;
import org.springframework.cache.interceptor.CacheDefinition;
import org.springframework.util.Assert;
/**
*
* Implementation of the
* {@link org.springframework.cache.interceptor.CacheDefinitionSource}
* interface for working with caching metadata in JDK 1.5+ annotation format.
*
* <p>This class reads Spring's JDK 1.5+ {@link Cacheable} and {@link CacheEvict}
* annotations and
* exposes corresponding caching operation definition to Spring's cache infrastructure.
* This class may also serve as base class for a custom CacheDefinitionSource.
*
* @author Costin Leau
*/
@SuppressWarnings("serial")
public class AnnotationCacheDefinitionSource extends AbstractFallbackCacheDefinitionSource implements
Serializable {
private final boolean publicMethodsOnly;
private final Set<CacheAnnotationParser> annotationParsers;
/**
* Create a default AnnotationCacheOperationDefinitionSource, supporting
* public methods that carry the <code>Cacheable</code> and <code>CacheInvalidate</code>
* annotations.
*/
public AnnotationCacheDefinitionSource() {
this(true);
}
/**
* Create a custom AnnotationCacheOperationDefinitionSource, supporting
* public methods that carry the <code>Cacheable</code> and
* <code>CacheInvalidate</code> annotations.
*
* @param publicMethodsOnly whether to support only annotated public methods
* typically for use with proxy-based AOP), or protected/private methods as well
* (typically used with AspectJ class weaving)
*/
public AnnotationCacheDefinitionSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = new LinkedHashSet<CacheAnnotationParser>(1);
this.annotationParsers.add(new SpringCachingAnnotationParser());
}
/**
* Create a custom AnnotationCacheOperationDefinitionSource.
* @param annotationParsers the CacheAnnotationParser to use
*/
public AnnotationCacheDefinitionSource(CacheAnnotationParser... annotationParsers) {
this.publicMethodsOnly = true;
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
Set<CacheAnnotationParser> parsers = new LinkedHashSet<CacheAnnotationParser>(annotationParsers.length);
Collections.addAll(parsers, annotationParsers);
this.annotationParsers = parsers;
}
@Override
protected CacheDefinition findCacheDefinition(Class<?> clazz) {
return determineCacheDefinition(clazz);
}
@Override
protected CacheDefinition findCacheOperation(Method method) {
return determineCacheDefinition(method);
}
/**
* Determine the cache operation definition for the given method or class.
* <p>This implementation delegates to configured
* {@link CacheAnnotationParser CacheAnnotationParsers}
* for parsing known annotations into Spring's metadata attribute class.
* Returns <code>null</code> if it's not cacheable.
* <p>Can be overridden to support custom annotations that carry caching metadata.
* @param ae the annotated method or class
* @return CacheOperationDefinition the configured caching operation,
* or <code>null</code> if none was found
*/
protected CacheDefinition determineCacheDefinition(AnnotatedElement ae) {
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
CacheDefinition attr = annotationParser.parseCacheAnnotation(ae);
if (attr != null) {
return attr;
}
}
return null;
}
/**
* By default, only public methods can be made cacheable.
*/
@Override
protected boolean allowPublicMethodsOnly() {
return this.publicMethodsOnly;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2010 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.annotation;
import java.lang.reflect.AnnotatedElement;
import org.springframework.cache.interceptor.CacheDefinition;
/**
* Strategy interface for parsing known caching annotation types.
* {@link AnnotationCacheDefinitionSource} delegates to such
* parsers for supporting specific annotation types such as Spring's own
* {@link Cacheable} or {@link CacheEvict}.
*
* @author Costin Leau
*/
public interface CacheAnnotationParser {
/**
* Parses the cache definition for the given method or class,
* based on a known annotation type.
* <p>This essentially parses a known cache annotation into Spring's
* metadata attribute class. Returns <code>null</code> if the method/class
* is not cacheable.
* @param ae the annotated method or class
* @return CacheOperationDefinition the configured caching operation,
* or <code>null</code> if none was found
* @see AnnotationCacheDefinitionSource#determineCacheOperationDefinition
*/
CacheDefinition parseCacheAnnotation(AnnotatedElement ae);
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2002-2009 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation indicating that a method (or all methods on a class) trigger(s)
* a cache invalidate operation.
*
* @author Costin Leau
*/
@Target( { ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
/**
* Qualifier value for the specified cached operation.
* <p>May be used to determine the target cache (or caches), matching the qualifier
* value (or the bean name(s)) of (a) specific bean definition.
*/
String[] value();
/**
* Spring Expression Language (SpEL) attribute for computing the key dynamically.
* <p/>
* Default is "" meaning all method parameters are considered as a key.
*/
String key() default "";
/**
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
* <p/>
* Default is "" meaning the method is always cached.
*/
String condition() default "";
/**
* Whether or not all the entries inside the cache(s) are removed or not. By
* default, only the value under the associated key is removed.
*
* Note that specifying setting this parameter to true and specifying a
* {@link CacheKey key} is not allowed.
*/
boolean allEntries() default false;
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2009 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation indicating that a method (or all the methods on a class) can be cached.
* The method arguments and signature are used for computing the key while the return instance
* as the cache value.
*
* @author Costin Leau
*/
@Target( { ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
/**
* Name of the caches in which the update takes place.
* <p>
* May be used to determine the target cache (or caches), matching the
* qualifier value (or the bean name(s)) of (a) specific bean definition.
*/
String[] value();
/**
* Spring Expression Language (SpEL) attribute for computing the key dynamically.
* <p/>
* Default is "" meaning all method parameters are considered as a key.
*/
String key() default "";
/**
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
* <p/>
* Default is "" meaning the method is always cached.
*/
String condition() default "";
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2010 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.annotation;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import org.springframework.cache.interceptor.CacheDefinition;
import org.springframework.cache.interceptor.CacheInvalidateDefinition;
import org.springframework.cache.interceptor.CacheUpdateDefinition;
import org.springframework.cache.interceptor.DefaultCacheInvalidateDefinition;
import org.springframework.cache.interceptor.DefaultCacheUpdateDefinition;
/**
* Strategy implementation for parsing Spring's {@link Cacheable} and {@link CacheEvict} annotations.
*
* @author Costin Leau
*/
@SuppressWarnings("serial")
public class SpringCachingAnnotationParser implements CacheAnnotationParser, Serializable {
public CacheDefinition parseCacheAnnotation(AnnotatedElement ae) {
Cacheable update = findAnnotation(ae, Cacheable.class);
if (update != null) {
return parseCacheableAnnotation(ae, update);
}
CacheEvict invalidate = findAnnotation(ae, CacheEvict.class);
if (invalidate != null) {
return parseInvalidateAnnotation(ae, invalidate);
}
return null;
}
private <T extends Annotation> T findAnnotation(AnnotatedElement ae, Class<T> annotationType) {
T ann = ae.getAnnotation(annotationType);
if (ann == null) {
for (Annotation metaAnn : ae.getAnnotations()) {
ann = metaAnn.annotationType().getAnnotation(annotationType);
if (ann != null) {
break;
}
}
}
return ann;
}
CacheUpdateDefinition parseCacheableAnnotation(AnnotatedElement target, Cacheable ann) {
DefaultCacheUpdateDefinition dcud = new DefaultCacheUpdateDefinition();
dcud.setCacheNames(ann.value());
dcud.setCondition(ann.condition());
dcud.setKey(ann.key());
dcud.setName(target.toString());
return dcud;
}
CacheInvalidateDefinition parseInvalidateAnnotation(AnnotatedElement target, CacheEvict ann) {
DefaultCacheInvalidateDefinition dcid = new DefaultCacheInvalidateDefinition();
dcid.setCacheNames(ann.value());
dcid.setCondition(ann.condition());
dcid.setKey(ann.key());
dcid.setCacheWide(ann.allEntries());
dcid.setName(target.toString());
return dcid;
}
}

View File

@@ -0,0 +1,10 @@
/**
*
* JDK 1.5+ annotation for caching demarcation.
* Hooked into Spring's caching interception infrastructure
* via CacheDefinitionSource implementation.
*
*/
package org.springframework.cache.annotation;

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2010 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.concurrent;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractDelegatingCache;
/**
* Simple {@link Cache} implementation based on the JDK 1.5+
* java.util.concurrent package. Useful for testing or simple caching scenarios.
*
* @author Costin Leau
*/
public class ConcurrentCache<K, V> extends AbstractDelegatingCache<K, V> {
private final ConcurrentMap<K, V> store;
private final String name;
public ConcurrentCache() {
this("");
}
public ConcurrentCache(String name) {
this(new ConcurrentHashMap<K, V>(), name);
}
public ConcurrentCache(ConcurrentMap<K, V> delegate, String name) {
super(delegate, true);
this.store = delegate;
this.name = name;
}
public String getName() {
return name;
}
public ConcurrentMap<K, V> getNativeCache() {
return store;
}
@SuppressWarnings("unchecked")
public V putIfAbsent(K key, V value) {
if (getAllowNullValues()) {
if (value == null) {
ConcurrentMap raw = store;
return filterNull((V) raw.putIfAbsent(key, NULL_HOLDER));
}
}
return filterNull(store.putIfAbsent(key, value));
}
@SuppressWarnings("unchecked")
public boolean remove(Object key, Object value) {
if (getAllowNullValues()) {
if (value == null) {
ConcurrentMap raw = store;
return raw.remove(key, NULL_HOLDER);
}
}
return store.remove(key, value);
}
@SuppressWarnings("unchecked")
public boolean replace(K key, V oldValue, V newValue) {
if (getAllowNullValues()) {
Object rawOldValue = (oldValue == null ? NULL_HOLDER : oldValue);
Object rawNewValue = (newValue == null ? NULL_HOLDER : newValue);
ConcurrentMap raw = store;
return raw.replace(key, rawOldValue, rawNewValue);
}
return store.replace(key, oldValue, newValue);
}
@SuppressWarnings("unchecked")
public V replace(K key, V value) {
if (getAllowNullValues()) {
if (value == null) {
ConcurrentMap raw = store;
return filterNull((V) raw.replace(key, NULL_HOLDER));
}
}
return filterNull(store.replace(key, value));
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2010 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.concurrent;
import java.util.concurrent.ConcurrentMap;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
/**
* Factory bean for easy configuration of {@link ConcurrentCache} through Spring.
*
* @author Costin Leau
*/
public class ConcurrentCacheFactoryBean<K, V> implements FactoryBean<ConcurrentCache<K, V>>, BeanNameAware,
InitializingBean {
private String name = "";
private ConcurrentCache<K, V> cache;
private ConcurrentMap<K, V> store;
public void afterPropertiesSet() {
cache = (store == null ? new ConcurrentCache<K, V>(name) : new ConcurrentCache<K, V>(store, name));
}
public ConcurrentCache<K, V> getObject() throws Exception {
return cache;
}
public Class<?> getObjectType() {
return (cache != null ? cache.getClass() : ConcurrentCache.class);
}
public boolean isSingleton() {
return true;
}
public void setBeanName(String beanName) {
if (!StringUtils.hasText(name)) {
setName(beanName);
}
}
public void setName(String name) {
this.name = name;
}
public void setStore(ConcurrentMap<K, V> store) {
this.store = store;
}
}

View File

@@ -0,0 +1,10 @@
/**
*
* Implementation package for java.util.concurrent based
* caches. Provides a CacheManager and Cache implementation
* for usage in a Spring context.
*
*/
package org.springframework.cache.concurrent;

View File

@@ -0,0 +1,158 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.config;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cache.annotation.AnnotationCacheDefinitionSource;
import org.springframework.cache.interceptor.BeanFactoryCacheDefinitionSourceAdvisor;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.w3c.dom.Element;
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
* implementation that allows users to easily configure all the infrastructure
* beans required to enable annotation-driven cache demarcation.
*
* <p>By default, all proxies are created as JDK proxies. This may cause some
* problems if you are injecting objects as concrete classes rather than
* interfaces. To overcome this restriction you can set the
* '<code>proxy-target-class</code>' attribute to '<code>true</code>', which
* will result in class-based proxies being created.
*
* @author Costin Leau
*/
class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser {
private static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager";
private static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager";
/**
* The bean name of the internally managed cache advisor (mode="proxy").
*/
public static final String CACHE_ADVISOR_BEAN_NAME = "org.springframework.cache.config.internalCacheAdvisor";
/**
* The bean name of the internally managed cache aspect (mode="aspectj").
*/
public static final String CACHE_ASPECT_BEAN_NAME = "org.springframework.cache.config.internalCacheAspect";
private static final String CACHE_ASPECT_CLASS_NAME = "org.springframework.cache.aspectj.AnnotationCacheAspect";
/**
* Parses the '<code>&lt;cache:annotation-driven/&gt;</code>' tag. Will
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
* with the container as necessary.
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerCacheAspect(element, parserContext);
}
else {
// mode="proxy"
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
private static String extractCacheManager(Element element) {
return (element.hasAttribute(CACHE_MANAGER_ATTRIBUTE) ? element.getAttribute(CACHE_MANAGER_ATTRIBUTE)
: DEFAULT_CACHE_MANAGER_BEAN_NAME);
}
private static void registerCacheManagerProperty(Element element, BeanDefinition def) {
def.getPropertyValues().add("cacheManager", new RuntimeBeanReference(extractCacheManager(element)));
}
/**
* Registers a
* <pre>
* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
* <property name="cacheManagerBeanName" value="cacheManager"/>
* </bean>
*
* </pre>
* @param element
* @param parserContext
*/
private void registerCacheAspect(Element element, ParserContext parserContext) {
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ASPECT_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
def.setBeanClassName(CACHE_ASPECT_CLASS_NAME);
def.setFactoryMethodName("aspectOf");
registerCacheManagerProperty(element, def);
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CACHE_ASPECT_BEAN_NAME));
}
}
/**
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
*/
private static class AopAutoProxyConfigurer {
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ADVISOR_BEAN_NAME)) {
Object eleSource = parserContext.extractSource(element);
// Create the CacheDefinitionSource definition.
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationCacheDefinitionSource.class);
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
// Create the CacheInterceptor definition.
RootBeanDefinition interceptorDef = new RootBeanDefinition(CacheInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerCacheManagerProperty(element, interceptorDef);
interceptorDef.getPropertyValues().add("cacheDefinitionSources", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
// Create the CacheAdvisor definition.
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheDefinitionSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("cacheDefinitionSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
parserContext.getRegistry().registerBeanDefinition(CACHE_ADVISOR_BEAN_NAME, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, CACHE_ADVISOR_BEAN_NAME));
parserContext.registerComponent(compositeDef);
}
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.config;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* <code>NamespaceHandler</code> allowing for the configuration of
* declarative cache management using either XML or using annotations.
*
* <p>This namespace handler is the central piece of functionality in the
* Spring cache management facilities.
*
* @author Costin Leau
*/
public class CacheNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser());
}
}

View File

@@ -0,0 +1,9 @@
/**
*
* Support package for declarative caching configuration,
* with XML schema being the primary configuration format.
*
*/
package org.springframework.cache.config;

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2010 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.ehcache;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
import org.springframework.cache.Cache;
import org.springframework.util.Assert;
/**
* {@link Cache} implementation on top of an {@link Ehcache} instance.
*
* @author Costin Leau
*/
public class EhCacheCache implements Cache<Object, Object> {
private final Ehcache cache;
/**
* Creates a {@link EhCacheCache} instance.
*
* @param ehcache backing Ehcache instance
*/
public EhCacheCache(Ehcache ehcache) {
Assert.notNull(ehcache, "non null ehcache required");
Status status = ehcache.getStatus();
Assert.isTrue(Status.STATUS_ALIVE.equals(status), "an 'alive' ehcache is required - current cache is "
+ status.toString());
this.cache = ehcache;
}
public String getName() {
return cache.getName();
}
public Ehcache getNativeCache() {
return cache;
}
public void clear() {
cache.removeAll();
}
public boolean containsKey(Object key) {
return cache.isKeyInCache(key);
}
public boolean containsValue(Object value) {
return cache.isValueInCache(value);
}
public Object get(Object key) {
Element element = cache.get(key);
return (element != null ? element.getObjectValue() : null);
}
public Object put(Object key, Object value) {
Element previous = cache.getQuiet(key);
cache.put(new Element(key, value));
return (previous != null ? previous.getValue() : null);
}
public Object remove(Object key) {
Object value = null;
if (cache.isKeyInCache(key)) {
Element element = cache.getQuiet(key);
value = (element != null ? element.getObjectValue() : null);
}
cache.remove(key);
return value;
}
public Object putIfAbsent(Object key, Object value) {
// putIfAbsent supported only from Ehcache 2.1
// return cache.putIfAbsent(new Element(key, value));
Element existing = cache.getQuiet(key);
if (existing == null) {
cache.put(new Element(key, value));
return null;
}
return existing.getObjectValue();
}
public boolean remove(Object key, Object value) {
// remove(Element) supported only from Ehcache 2.1
// return cache.removeElement(new Element(key, value));
Element existing = cache.getQuiet(key);
if (existing != null && existing.getObjectValue().equals(value)) {
cache.remove(key);
return true;
}
return false;
}
public Object replace(Object key, Object value) {
// replace(Object, Object) supported only from Ehcache 2.1
// return cache.replace(new Element(key, value));
Element existing = cache.getQuiet(key);
if (existing != null) {
cache.put(new Element(key, value));
return existing.getObjectValue();
}
return null;
}
public boolean replace(Object key, Object oldValue, Object newValue) {
// replace(Object, Object, Object) supported only from Ehcache 2.1
// return cache.replace(new Element(key, oldValue), new Element(key,
// newValue));
Element existing = cache.getQuiet(key);
if (existing != null && existing.getObjectValue().equals(oldValue)) {
cache.put(new Element(key, newValue));
return true;
}
return false;
}
}

View File

@@ -0,0 +1,371 @@
/*
* Copyright 2002-2009 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.ehcache;
import java.io.IOException;
import java.util.Set;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
import net.sf.ehcache.constructs.blocking.BlockingCache;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;
import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache;
import net.sf.ehcache.event.CacheEventListener;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* {@link FactoryBean} that creates a named EHCache {@link net.sf.ehcache.Cache} instance
* (or a decorator that implements the {@link net.sf.ehcache.Ehcache} interface),
* representing a cache region within an EHCache {@link net.sf.ehcache.CacheManager}.
*
* <p>If the specified named cache is not configured in the cache configuration descriptor,
* this FactoryBean will construct an instance of a Cache with the provided name and the
* specified cache properties and add it to the CacheManager for later retrieval. If some
* or all properties are not set at configuration time, this FactoryBean will use defaults.
*
* <p>Note: If the named Cache instance is found, the properties will be ignored and the
* Cache instance will be retrieved from the CacheManager.
*
* <p>Note: As of Spring 3.0, Spring's EHCache support requires EHCache 1.3 or higher.
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @since 1.1.1
* @see #setCacheManager
* @see EhCacheManagerFactoryBean
* @see net.sf.ehcache.Cache
*/
public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware, InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private CacheManager cacheManager;
private String cacheName;
private int maxElementsInMemory = 10000;
private int maxElementsOnDisk = 10000000;
private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy = MemoryStoreEvictionPolicy.LRU;
private boolean overflowToDisk = true;
private boolean eternal = false;
private int timeToLive = 120;
private int timeToIdle = 120;
private boolean diskPersistent = false;
private int diskExpiryThreadIntervalSeconds = 120;
private int diskSpoolBufferSize = 0;
private boolean clearOnFlush = true;
private boolean blocking = false;
private CacheEntryFactory cacheEntryFactory;
private BootstrapCacheLoader bootstrapCacheLoader;
private Set<CacheEventListener> cacheEventListeners;
private String beanName;
private Ehcache cache;
/**
* Set a CacheManager from which to retrieve a named Cache instance.
* By default, <code>CacheManager.getInstance()</code> will be called.
* <p>Note that in particular for persistent caches, it is advisable to
* properly handle the shutdown of the CacheManager: Set up a separate
* EhCacheManagerFactoryBean and pass a reference to this bean property.
* <p>A separate EhCacheManagerFactoryBean is also necessary for loading
* EHCache configuration from a non-default config location.
* @see EhCacheManagerFactoryBean
* @see net.sf.ehcache.CacheManager#getInstance
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* Set a name for which to retrieve or create a cache instance.
* Default is the bean name of this EhCacheFactoryBean.
*/
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
/**
* Specify the maximum number of cached objects in memory.
* Default is 10000 elements.
*/
public void setMaxElementsInMemory(int maxElementsInMemory) {
this.maxElementsInMemory = maxElementsInMemory;
}
/**
* Specify the maximum number of cached objects on disk.
* Default is 10000000 elements.
*/
public void setMaxElementsOnDisk(int maxElementsOnDisk) {
this.maxElementsOnDisk = maxElementsOnDisk;
}
/**
* Set the memory style eviction policy for this cache.
* <p>Supported values are "LRU", "LFU" and "FIFO", according to the
* constants defined in EHCache's MemoryStoreEvictionPolicy class.
* Default is "LRU".
*/
public void setMemoryStoreEvictionPolicy(MemoryStoreEvictionPolicy memoryStoreEvictionPolicy) {
Assert.notNull(memoryStoreEvictionPolicy, "memoryStoreEvictionPolicy must not be null");
this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
}
/**
* Set whether elements can overflow to disk when the in-memory cache
* has reached the maximum size limit. Default is "true".
*/
public void setOverflowToDisk(boolean overflowToDisk) {
this.overflowToDisk = overflowToDisk;
}
/**
* Set whether elements are considered as eternal. If "true", timeouts
* are ignored and the element is never expired. Default is "false".
*/
public void setEternal(boolean eternal) {
this.eternal = eternal;
}
/**
* Set the time in seconds to live for an element before it expires,
* i.e. the maximum time between creation time and when an element expires.
* <p>This is only used if the element is not eternal. Default is 120 seconds.
*/
public void setTimeToLive(int timeToLive) {
this.timeToLive = timeToLive;
}
/**
* Set the time in seconds to idle for an element before it expires, that is,
* the maximum amount of time between accesses before an element expires.
* <p>This is only used if the element is not eternal. Default is 120 seconds.
*/
public void setTimeToIdle(int timeToIdle) {
this.timeToIdle = timeToIdle;
}
/**
* Set whether the disk store persists between restarts of the Virtual Machine.
* Default is "false".
*/
public void setDiskPersistent(boolean diskPersistent) {
this.diskPersistent = diskPersistent;
}
/**
* Set the number of seconds between runs of the disk expiry thread.
* Default is 120 seconds.
*/
public void setDiskExpiryThreadIntervalSeconds(int diskExpiryThreadIntervalSeconds) {
this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
}
/**
* Set the amount of memory to allocate the write buffer for puts to the disk store.
* Default is 0.
*/
public void setDiskSpoolBufferSize(int diskSpoolBufferSize) {
this.diskSpoolBufferSize = diskSpoolBufferSize;
}
/**
* Set whether the memory store should be cleared when flush is called on the cache.
* Default is "true".
*/
public void setClearOnFlush(boolean clearOnFlush) {
this.clearOnFlush = clearOnFlush;
}
/**
* Set whether to use a blocking cache that lets read attempts block
* until the requested element is created.
* <p>If you intend to build a self-populating blocking cache,
* consider specifying a {@link #setCacheEntryFactory CacheEntryFactory}.
* @see net.sf.ehcache.constructs.blocking.BlockingCache
* @see #setCacheEntryFactory
*/
public void setBlocking(boolean blocking) {
this.blocking = blocking;
}
/**
* Set an EHCache {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory}
* to use for a self-populating cache. If such a factory is specified,
* the cache will be decorated with EHCache's
* {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}.
* <p>The specified factory can be of type
* {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory},
* which will lead to the use of an
* {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}.
* <p>Note: Any such self-populating cache is automatically a blocking cache.
* @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache
* @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache
* @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory
*/
public void setCacheEntryFactory(CacheEntryFactory cacheEntryFactory) {
this.cacheEntryFactory = cacheEntryFactory;
}
/**
* Set an EHCache {@link net.sf.ehcache.bootstrap.BootstrapCacheLoader}
* for this cache, if any.
*/
public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) {
this.bootstrapCacheLoader = bootstrapCacheLoader;
}
/**
* Specify EHCache {@link net.sf.ehcache.event.CacheEventListener cache event listeners}
* to registered with this cache.
*/
public void setCacheEventListeners(Set<CacheEventListener> cacheEventListeners) {
this.cacheEventListeners = cacheEventListeners;
}
public void setBeanName(String name) {
this.beanName = name;
}
public void afterPropertiesSet() throws CacheException, IOException {
// If no CacheManager given, fetch the default.
if (this.cacheManager == null) {
if (logger.isDebugEnabled()) {
logger.debug("Using default EHCache CacheManager for cache region '" + this.cacheName + "'");
}
this.cacheManager = CacheManager.getInstance();
}
// If no cache name given, use bean name as cache name.
if (this.cacheName == null) {
this.cacheName = this.beanName;
}
// Fetch cache region: If none with the given name exists,
// create one on the fly.
Ehcache rawCache;
if (this.cacheManager.cacheExists(this.cacheName)) {
if (logger.isDebugEnabled()) {
logger.debug("Using existing EHCache cache region '" + this.cacheName + "'");
}
rawCache = this.cacheManager.getEhcache(this.cacheName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Creating new EHCache cache region '" + this.cacheName + "'");
}
rawCache = createCache();
this.cacheManager.addCache(rawCache);
}
// Decorate cache if necessary.
Ehcache decoratedCache = decorateCache(rawCache);
if (decoratedCache != rawCache) {
this.cacheManager.replaceCacheWithDecoratedCache(rawCache, decoratedCache);
}
this.cache = decoratedCache;
}
/**
* Create a raw Cache object based on the configuration of this FactoryBean.
*/
protected Cache createCache() {
// Only call EHCache 1.6 constructor if actually necessary (for compatibility with EHCache 1.3+)
Cache cache = (!this.clearOnFlush) ?
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
this.bootstrapCacheLoader, this.maxElementsOnDisk, this.diskSpoolBufferSize,
this.clearOnFlush) :
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
this.bootstrapCacheLoader, this.maxElementsOnDisk, this.diskSpoolBufferSize);
if (this.cacheEventListeners != null) {
for (CacheEventListener listener : this.cacheEventListeners) {
cache.getCacheEventNotificationService().registerListener(listener);
}
}
return cache;
}
/**
* Decorate the given Cache, if necessary.
* @param cache the raw Cache object, based on the configuration of this FactoryBean
* @return the (potentially decorated) cache object to be registered with the CacheManager
*/
protected Ehcache decorateCache(Ehcache cache) {
if (this.cacheEntryFactory != null) {
if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) {
return new UpdatingSelfPopulatingCache(cache, (UpdatingCacheEntryFactory) this.cacheEntryFactory);
}
else {
return new SelfPopulatingCache(cache, this.cacheEntryFactory);
}
}
if (this.blocking) {
return new BlockingCache(cache);
}
return cache;
}
public Ehcache getObject() {
return this.cache;
}
public Class<? extends Ehcache> getObjectType() {
return (this.cache != null ? this.cache.getClass() : Ehcache.class);
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright 2002-2011 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.ehcache;
import java.io.IOException;
import java.io.InputStream;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
/**
* {@link FactoryBean} that exposes an EHCache {@link net.sf.ehcache.CacheManager}
* instance (independent or shared), configured from a specified config location.
*
* <p>If no config location is specified, a CacheManager will be configured from
* "ehcache.xml" in the root of the class path (that is, default EHCache initialization
* - as defined in the EHCache docs - will apply).
*
* <p>Setting up a separate EhCacheManagerFactoryBean is also advisable when using
* EhCacheFactoryBean, as it provides a (by default) independent CacheManager instance
* and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is
* also necessary for loading EHCache configuration from a non-default config location.
*
* <p>Note: As of Spring 3.0, Spring's EHCache support requires EHCache 1.3 or higher.
*
* @author Dmitriy Kopylenko
* @author Juergen Hoeller
* @since 1.1.1
* @see #setConfigLocation
* @see #setShared
* @see EhCacheFactoryBean
* @see net.sf.ehcache.CacheManager
*/
public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
private Resource configLocation;
private boolean shared = false;
private String cacheManagerName;
private CacheManager cacheManager;
/**
* Set the location of the EHCache config file. A typical value is "/WEB-INF/ehcache.xml".
* <p>Default is "ehcache.xml" in the root of the class path, or if not found,
* "ehcache-failsafe.xml" in the EHCache jar (default EHCache initialization).
* @see net.sf.ehcache.CacheManager#create(java.io.InputStream)
* @see net.sf.ehcache.CacheManager#CacheManager(java.io.InputStream)
*/
public void setConfigLocation(Resource configLocation) {
this.configLocation = configLocation;
}
/**
* Set whether the EHCache CacheManager should be shared (as a singleton at the VM level)
* or independent (typically local within the application). Default is "false", creating
* an independent instance.
* @see net.sf.ehcache.CacheManager#create()
* @see net.sf.ehcache.CacheManager#CacheManager()
*/
public void setShared(boolean shared) {
this.shared = shared;
}
/**
* Set the name of the EHCache CacheManager (if a specific name is desired).
* @see net.sf.ehcache.CacheManager#setName(String)
*/
public void setCacheManagerName(String cacheManagerName) {
this.cacheManagerName = cacheManagerName;
}
public void afterPropertiesSet() throws IOException, CacheException {
logger.info("Initializing EHCache CacheManager");
if (this.configLocation != null) {
InputStream is = this.configLocation.getInputStream();
try {
this.cacheManager = (this.shared ? CacheManager.create(is) : new CacheManager(is));
}
finally {
is.close();
}
}
else {
this.cacheManager = (this.shared ? CacheManager.create() : new CacheManager());
}
if (this.cacheManagerName != null) {
this.cacheManager.setName(this.cacheManagerName);
}
}
public CacheManager getObject() {
return this.cacheManager;
}
public Class<? extends CacheManager> getObjectType() {
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
}
public boolean isSingleton() {
return true;
}
public void destroy() {
logger.info("Shutting down EHCache CacheManager");
this.cacheManager.shutdown();
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2010 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.ehcache;
import java.util.Collection;
import java.util.LinkedHashSet;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Status;
import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;
import org.springframework.util.Assert;
/**
* CacheManager backed by an Ehcache {@link net.sf.ehcache.CacheManager}.
*
* @author Costin Leau
*/
public class EhcacheCacheManager extends AbstractCacheManager {
private net.sf.ehcache.CacheManager cacheManager;
@Override
protected Collection<Cache<?, ?>> loadCaches() {
Assert.notNull(cacheManager, "a backing Ehcache cache manager is required");
Status status = cacheManager.getStatus();
Assert.isTrue(Status.STATUS_ALIVE.equals(status),
"an 'alive' Ehcache cache manager is required - current cache is " + status.toString());
String[] names = cacheManager.getCacheNames();
Collection<Cache<?, ?>> caches = new LinkedHashSet<Cache<?, ?>>(names.length);
for (String name : names) {
caches.add(new EhCacheCache(cacheManager.getEhcache(name)));
}
return caches;
}
@SuppressWarnings("unchecked")
public <K, V> Cache<K, V> getCache(String name) {
Cache cache = super.getCache(name);
if (cache == null) {
// check the Ehcache cache again
// in case the cache was added at runtime
Ehcache ehcache = cacheManager.getEhcache(name);
if (ehcache != null) {
// reinitialize cache map
afterPropertiesSet();
cache = super.getCache(name);
}
}
return cache;
}
/**
* Sets the backing Ehcache {@link net.sf.ehcache.CacheManager}.
*
* @param cacheManager backing Ehcache {@link net.sf.ehcache.CacheManager}
*/
public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
}

View File

@@ -0,0 +1,11 @@
/**
*
* Support classes for the open source cache
* <a href="http://ehcache.sourceforge.net">Ehcache</a>,
* allowing to set up an EHCache CacheManager and Caches
* as beans in a Spring context.
*
*/
package org.springframework.cache.ehcache;

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.util.Assert;
/**
* Base class implementing {@link CacheDefinition}.
*
* @author Costin Leau
*/
abstract class AbstractCacheDefinition implements CacheDefinition {
private Set<String> cacheNames = Collections.emptySet();
private String condition = "";
private String key = "";
private String name = "";
public Set<String> getCacheNames() {
return cacheNames;
}
public String getCondition() {
return condition;
}
public String getKey() {
return key;
}
public String getName() {
return name;
}
public void setCacheName(String cacheName) {
Assert.hasText(cacheName);
this.cacheNames = Collections.singleton(cacheName);
}
public void setCacheNames(String[] cacheNames) {
Assert.notEmpty(cacheNames);
this.cacheNames = new LinkedHashSet<String>(cacheNames.length);
for (String string : cacheNames) {
this.cacheNames.add(string);
}
}
public void setCondition(String condition) {
Assert.notNull(condition);
this.condition = condition;
}
public void setKey(String key) {
Assert.notNull(key);
this.key = key;
}
public void setName(String name) {
Assert.hasText(name);
this.name = name;
}
/**
* This implementation compares the <code>toString()</code> results.
* @see #toString()
*/
@Override
public boolean equals(Object other) {
return (other instanceof CacheDefinition && toString().equals(other.toString()));
}
/**
* This implementation returns <code>toString()</code>'s hash code.
* @see #toString()
*/
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Return an identifying description for this cache operation definition.
* <p>Has to be overridden in subclasses for correct <code>equals</code>
* and <code>hashCode</code> behavior. Alternatively, {@link #equals}
* and {@link #hashCode} can be overridden themselves.
*/
@Override
public String toString() {
return getDefinitionDescription().toString();
}
/**
* Return an identifying description for this caching definition.
* <p>Available to subclasses, for inclusion in their <code>toString()</code> result.
*/
protected StringBuilder getDefinitionDescription() {
StringBuilder result = new StringBuilder();
result.append("CacheDefinition[");
result.append(name);
result.append("] caches=");
result.append(cacheNames);
result.append(" | condition='");
result.append(condition);
result.append("' | key='");
result.append(key);
result.append("'");
return result;
}
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
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.core.BridgeMethodResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Abstract implementation of {@link CacheDefinition} that caches
* attributes for methods and implements a fallback policy: 1. specific target
* method; 2. target class; 3. declaring method; 4. declaring class/interface.
*
* <p>Defaults to using the target class's caching attribute if none is
* associated with the target method. Any caching attribute associated with
* the target method completely overrides a class caching attribute.
* If none found on the target class, the interface that the invoked method
* has been called through (in case of a JDK proxy) will be checked.
*
* <p>This implementation caches attributes by method after they are first used.
* If it is ever desirable to allow dynamic changing of cacheable attributes
* (which is very unlikely), caching could be made configurable.
* @author Costin Leau
* @see org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource
*/
public abstract class AbstractFallbackCacheDefinitionSource implements CacheDefinitionSource {
/**
* Canonical value held in cache to indicate no caching attribute was
* found for this method, and we don't need to look again.
*/
private final static CacheDefinition NULL_CACHING_ATTRIBUTE = new DefaultCacheUpdateDefinition();
/**
* Logger available to subclasses.
* <p>As this base class is not marked Serializable, the logger will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
protected final Log logger = LogFactory.getLog(getClass());
/**
* Cache of CacheOperationDefinitions, keyed by DefaultCacheKey (Method + target Class).
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
final Map<Object, CacheDefinition> attributeCache = new ConcurrentHashMap<Object, CacheDefinition>();
/**
* Determine the caching attribute for this method invocation.
* <p>Defaults to the class's caching attribute if no method attribute is found.
* @param method the method for the current invocation (never <code>null</code>)
* @param targetClass the target class for this invocation (may be <code>null</code>)
* @return {@link CacheDefinition} for this method, or <code>null</code> if the method
* is not cacheable
*/
public CacheDefinition getCacheDefinition(Method method, Class<?> targetClass) {
// First, see if we have a cached value.
Object cacheKey = getCacheKey(method, targetClass);
CacheDefinition cached = this.attributeCache.get(cacheKey);
if (cached != null) {
if (cached == NULL_CACHING_ATTRIBUTE) {
return null;
}
// Value will either be canonical value indicating there is no caching attribute,
// or an actual caching attribute.
return cached;
}
else {
// We need to work it out.
CacheDefinition cacheDef = computeCacheOperationDefinition(method, targetClass);
// Put it in the cache.
if (cacheDef == null) {
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheDef);
}
this.attributeCache.put(cacheKey, cacheDef);
}
return cacheDef;
}
}
/**
* Determine a cache key for the given method and target class.
* <p>Must not produce same key for overloaded methods.
* Must produce same key for different instances of the same method.
* @param method the method (never <code>null</code>)
* @param targetClass the target class (may be <code>null</code>)
* @return the cache key (never <code>null</code>)
*/
protected Object getCacheKey(Method method, Class<?> targetClass) {
return new DefaultCacheKey(method, targetClass);
}
/**
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
* @see #getTransactionAttribute
*/
private CacheDefinition computeCacheOperationDefinition(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.
CacheDefinition opDef = findCacheOperation(specificMethod);
if (opDef != null) {
return opDef;
}
// Second try is the caching operation on the target class.
opDef = findCacheDefinition(specificMethod.getDeclaringClass());
if (opDef != null) {
return opDef;
}
if (specificMethod != method) {
// Fall back is to look at the original method.
opDef = findCacheOperation(method);
if (opDef != null) {
return opDef;
}
// Last fall back is the class of the original method.
return findCacheDefinition(method.getDeclaringClass());
}
return null;
}
/**
* Subclasses need to implement this to return the caching attribute
* for the given method, if any.
* @param method the method to retrieve the attribute for
* @return all caching attribute associated with this method
* (or <code>null</code> if none)
*/
protected abstract CacheDefinition findCacheOperation(Method method);
/**
* Subclasses need to implement this to return the caching attribute
* for the given class, if any.
* @param clazz the class to retrieve the attribute for
* @return all caching attribute associated with this class
* (or <code>null</code> if none)
*/
protected abstract CacheDefinition findCacheDefinition(Class<?> clazz);
/**
* Should only public methods be allowed to have caching semantics?
* <p>The default implementation returns <code>false</code>.
*/
protected boolean allowPublicMethodsOnly() {
return false;
}
/**
* Default cache key for the CacheOperationDefinition cache.
*/
private static class DefaultCacheKey {
private final Method method;
private final Class<?> targetClass;
public DefaultCacheKey(Method method, Class<?> targetClass) {
this.method = method;
this.targetClass = targetClass;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof DefaultCacheKey)) {
return false;
}
DefaultCacheKey otherKey = (DefaultCacheKey) other;
return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(this.targetClass,
otherKey.targetClass));
}
@Override
public int hashCode() {
return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2010 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.aop.ClassFilter;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
/**
* Advisor driven by a {@link CacheDefinitionSource}, used to include a
* transaction advice bean for methods that are transactional.
*
* @author Costin Leau
*/
@SuppressWarnings("serial")
public class BeanFactoryCacheDefinitionSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
private CacheDefinitionSource cacheDefinitionSource;
private final CacheDefinitionSourcePointcut pointcut = new CacheDefinitionSourcePointcut() {
@Override
protected CacheDefinitionSource getCacheDefinitionSource() {
return cacheDefinitionSource;
}
};
/**
* 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.
* @see CacheInterceptor#setCacheAttributeSource
*/
public void setCacheDefinitionSource(CacheDefinitionSource cacheDefinitionSource) {
this.cacheDefinitionSource = cacheDefinitionSource;
}
/**
* Set the {@link ClassFilter} to use for this pointcut.
* Default is {@link ClassFilter#TRUE}.
*/
public void setClassFilter(ClassFilter classFilter) {
this.pointcut.setClassFilter(classFilter);
}
public Pointcut getPointcut() {
return this.pointcut;
}
}

View File

@@ -0,0 +1,309 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Callable;
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.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.KeyGenerator;
import org.springframework.cache.support.DefaultKeyGenerator;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Base class for caching aspects, such as the {@link CacheInterceptor}
* or an AspectJ aspect.
*
* <p>This enables the underlying Spring caching infrastructure to be used easily
* to implement an aspect for any aspect system.
*
* <p>Subclasses are responsible for calling methods in this class in the correct order.
*
* <p>If no caching name has been specified in the <code>CacheOperationDefinition</code>,
* the exposed name will be the <code>fully-qualified class name + "." + method name</code>
* (by default).
*
* <p>Uses the <b>Strategy</b> design pattern. A <code>CacheManager</code>
* implementation will perform the actual transaction management, and a
* <code>CacheDefinitionSource</code> is used for determining caching operation definitions.
*
* <p>A cache aspect is serializable if its <code>CacheManager</code>
* and <code>CacheDefinitionSource</code> are serializable.
*
* @author Costin Leau
*/
public abstract class CacheAspectSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private CacheManager cacheManager;
private CacheDefinitionSource cacheDefinitionSource;
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
private KeyGenerator<?> keyGenerator = new DefaultKeyGenerator();
public void afterPropertiesSet() {
if (this.cacheManager == null) {
throw new IllegalStateException("Setting the property 'cacheManager' is required");
}
if (this.cacheDefinitionSource == null) {
throw new IllegalStateException("Either 'cacheDefinitionSource' or 'cacheDefinitionSources' is required: "
+ "If there are no cacheable methods, then don't use a cache aspect.");
}
}
/**
* Convenience method to return a String representation of this Method
* for use in logging. Can be overridden in subclasses to provide a
* different identifier for the given method.
* @param method the method we're interested in
* @param targetClass class the method is on
* @return log message identifying this method
* @see org.springframework.util.ClassUtils#getQualifiedMethodName
*/
protected String methodIdentification(Method method, Class<?> targetClass) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
return ClassUtils.getQualifiedMethodName(specificMethod);
}
public CacheManager getCacheManager() {
return cacheManager;
}
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public CacheDefinitionSource getCacheDefinitionSource() {
return cacheDefinitionSource;
}
public KeyGenerator getKeyGenerator() {
return keyGenerator;
}
public <K> void setKeyGenerator(KeyGenerator<K> keyGenerator) {
this.keyGenerator = keyGenerator;
}
/**
* Set multiple cache definition sources which are used to find the cache
* attributes. Will build a CompositeCachingDefinitionSource for the given sources.
*/
public void setCacheDefinitionSources(CacheDefinitionSource... cacheDefinitionSources) {
Assert.notEmpty(cacheDefinitionSources);
this.cacheDefinitionSource = (cacheDefinitionSources.length > 1 ? new CompositeCacheDefinitionSource(
cacheDefinitionSources) : cacheDefinitionSources[0]);
}
protected Collection<Cache<?, ?>> getCaches(CacheDefinition definition) {
Set<String> cacheNames = definition.getCacheNames();
Collection<Cache<?,?>> caches = new ArrayList<Cache<?,?>>(cacheNames.size());
for (String cacheName : cacheNames) {
Cache<Object, Object> cache = cacheManager.getCache(cacheName);
if (cache == null){
throw new IllegalArgumentException("Cannot find cache named ["+cacheName+"] for " + definition);
}
caches.add(cache);
}
return caches;
}
protected CacheOperationContext getOperationContext(CacheDefinition definition, Method method, Object[] args,
Class<?> targetClass) {
return new CacheOperationContext(definition, method, args, targetClass);
}
@SuppressWarnings("unchecked")
protected Object execute(Callable<Object> invocation, Object target,
Method method, Object[] args) throws Exception {
// get backing class
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
if (targetClass == null && target != null) {
targetClass = target.getClass();
}
final CacheDefinition cacheDef = getCacheDefinitionSource()
.getCacheDefinition(method, targetClass);
Object retVal = null;
// analyze caching information
if (cacheDef != null) {
CacheOperationContext context = getOperationContext(cacheDef,
method, args, targetClass);
Collection<Cache<?,?>> caches = context.getCaches();
if (context.hasConditionPassed()) {
// check operation
if (cacheDef instanceof CacheUpdateDefinition) {
Object key = context.generateKey();
if (key == null){
throw new IllegalArgumentException("Null key returned for cache definition " + cacheDef);
}
//
// check usage of single cache
// very common case which allows for some optimization
// in exchange for code readability
//
if (caches.size() == 1) {
Cache cache = caches.iterator().next();
if (cache.containsKey(key)) {
retVal = cache.get(key);
} else {
retVal = invocation.call();
cache.put(key, retVal);
}
}
//
// multi cache path
//
else {
// for each cache
boolean cacheHit = false;
for (Iterator<Cache<?,?>> iterator = caches.iterator(); iterator.hasNext() && !cacheHit;) {
Cache cache = iterator.next();
if (cache.containsKey(key)){
retVal = cache.get(key);
cacheHit = true;
}
}
if (!cacheHit) {
retVal = invocation.call();
}
// update all caches (if needed)
for (Cache cache : caches) {
cache.putIfAbsent(key, retVal);
}
}
}
if (cacheDef instanceof CacheInvalidateDefinition) {
CacheInvalidateDefinition invalidateDef = (CacheInvalidateDefinition) cacheDef;
retVal = invocation.call();
// for each cache
// lazy key initialization
Object key = null;
for (Cache cache : caches) {
// flush the cache (ignore arguments)
if (invalidateDef.isCacheWide()) {
cache.clear();
} else {
// check key
if (key == null) {
key = context.generateKey();
}
cache.remove(key);
}
}
}
return retVal;
}
}
return invocation.call();
}
protected class CacheOperationContext {
private CacheDefinition definition;
private final Collection<Cache<?, ?>> caches;
private final Method method;
private final Object[] args;
// context passed around to avoid multiple creations
private final EvaluationContext evalContext;
private final KeyGenerator<?> keyGenerator = CacheAspectSupport.this.keyGenerator;
public CacheOperationContext(CacheDefinition operationDefinition,
Method method, Object[] args, Class<?> targetClass) {
this.definition = operationDefinition;
this.caches = CacheAspectSupport.this.getCaches(definition);
this.method = method;
this.args = args;
this.evalContext = evaluator.createEvaluationContext(caches, method,
args, targetClass);
}
/**
* Evaluates the definition condition.
*
* @param definition
* @return
*/
protected boolean hasConditionPassed() {
if (StringUtils.hasText(definition.getCondition())) {
return evaluator.condition(definition.getCondition(), method,
evalContext);
}
return true;
}
/**
* Computes the key for the given caching definition.
*
* @param definition
* @param method
* method being invoked
* @param objects
* arguments passed during the method invocation
* @return generated key (null if none can be generated)
*/
protected Object generateKey() {
if (StringUtils.hasText(definition.getKey())) {
return evaluator.key(definition.getKey(), method, evalContext);
}
return keyGenerator.extract(method, args);
}
protected Collection<Cache<?, ?>> getCaches() {
return caches;
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.Set;
/**
* Interface describing Spring-compliant caching operation.
*
* @author Costin Leau
*/
public interface CacheDefinition {
/**
* Returns the name of this operation. Can be <tt>null</tt>.
* In case of Spring's declarative caching, the exposed name will be:
* <tt>fully qualified class name.method name</tt>.
*
* @return the operation name
*/
String getName();
/**
* Returns the names of the cache against which this operation is performed.
*
* @return names of the cache on which the operation is performed.
*/
Set<String> getCacheNames();
/**
* Returns the SpEL expression conditioning the operation.
*
* @return operation condition (as SpEL expression).
*/
String getCondition();
/**
* Returns the SpEL expression identifying the cache key.
*
* @return
*/
String getKey();
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
/**
* Interface used by CacheInterceptor. Implementations know
* how to source cache operation attributes, whether from configuration,
* metadata attributes at source level, or anywhere else.
*
* @author Costin Leau
*/
public interface CacheDefinitionSource {
/**
* Return the cache operation definition for this method.
* Return null if the method is not cacheable.
* @param method method
* @param targetClass target class. May be <code>null</code>, in which
* case the declaring class of the method must be used.
* @return {@link CacheDefinition} the matching cache operation definition,
* or <code>null</code> if none found
*/
CacheDefinition getCacheDefinition(Method method, Class<?> targetClass);
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.util.ObjectUtils;
/**
* Inner class that implements a Pointcut that matches if the underlying
* {@link CacheDefinitionSource} has an attribute for a given method.
*
* @author Costin Leau
*/
@SuppressWarnings("serial")
abstract class CacheDefinitionSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
public boolean matches(Method method, Class<?> targetClass) {
CacheDefinitionSource cas = getCacheDefinitionSource();
return (cas == null || cas.getCacheDefinition(method, targetClass) != null);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CacheDefinitionSourcePointcut)) {
return false;
}
CacheDefinitionSourcePointcut otherPc = (CacheDefinitionSourcePointcut) other;
return ObjectUtils.nullSafeEquals(getCacheDefinitionSource(),
otherPc.getCacheDefinitionSource());
}
@Override
public int hashCode() {
return CacheDefinitionSourcePointcut.class.hashCode();
}
@Override
public String toString() {
return getClass().getName() + ": " + getCacheDefinitionSource();
}
/**
* Obtain the underlying CacheOperationDefinitionSource (may be <code>null</code>).
* To be implemented by subclasses.
*/
protected abstract CacheDefinitionSource getCacheDefinitionSource();
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.Collection;
import org.springframework.cache.Cache;
/**
* Interface describing the root object used during the expression evaluation.
*
* @author Costin Leau
*/
interface CacheExpressionRootObject {
/**
* Returns the name of the method being cached.
*
* @return name of the cached method.
*/
String getMethodName();
/**
* Returns the caches against which the method is executed.
*
* @return current cache
*/
Collection<Cache<?,?>> getCaches();
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* AOP Alliance MethodInterceptor for declarative cache
* management using the common Spring caching infrastructure
* ({@link org.springframework.cache.Cache}).
*
* <p>Derives from the {@link CacheAspectSupport} class which
* contains the integration with Spring's underlying caching API.
* CacheInterceptor simply calls the relevant superclass methods
* in the correct order.
*
* <p>CacheInterceptors are thread-safe.
*
* @author Costin Leau
*/
@SuppressWarnings("serial")
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
@SuppressWarnings("unchecked")
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Callable<Object> aopAllianceInvocation = new Callable<Object>() {
public Object call() throws Exception {
try {
return invocation.proceed();
} catch (Throwable th) {
if (th instanceof Exception) {
throw (Exception) th;
}
throw (Error) th;
}
}
};
return execute(aopAllianceInvocation, invocation.getThis(), method, invocation.getArguments());
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2010 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;
/**
* Interface describing a Spring cache invalidation.
*
* @author Costin Leau
*/
public interface CacheInvalidateDefinition extends CacheDefinition {
/**
* Returns whether the operation affects the entire cache or not.
*
* @return whether the operation is cache wide or not.
*/
boolean isCacheWide();
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2010 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.aop.Pointcut;
import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
/**
* Proxy factory bean for simplified declarative caching handling.
* This is a convenient alternative to a standard AOP
* {@link org.springframework.aop.framework.ProxyFactoryBean}
* with a separate {@link CachingInterceptor} definition.
*
* <p>This class is intended to cover the <i>typical</i> case of declarative
* transaction demarcation: namely, wrapping a singleton target object with a
* caching proxy, proxying all the interfaces that the target implements.
*
* @author Costin Leau
* @see org.springframework.aop.framework.ProxyFactoryBean
* @see CachingInterceptor
*/
public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
private final CacheInterceptor cachingInterceptor = new CacheInterceptor();
private Pointcut pointcut;
@Override
protected Object createMainInterceptor() {
return null;
}
/**
* Set the caching attribute source which is used to find the cache operation
* definition.
*
* @param cacheDefinitionSources cache definition sources
*/
public void setCacheDefinitionSources(CacheDefinitionSource... cacheDefinitionSources) {
this.cachingInterceptor.setCacheDefinitionSources(cacheDefinitionSources);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2010 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;
/**
* Interface describing a Spring cache update.
*
* @author Costin Leau
*/
public interface CacheUpdateDefinition extends CacheDefinition {
/**
* Returns the SpEL expression identifying the cache key.
*
* @return
*/
String getKey();
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.util.Assert;
/**
* Composite {@link CacheDefinitionSource} implementation that iterates
* over a given array of {@link CacheDefinitionSource} instances.
*
* @author Costin Leau
*/
@SuppressWarnings("serial")
public class CompositeCacheDefinitionSource implements CacheDefinitionSource, Serializable {
private final CacheDefinitionSource[] cacheDefinitionSources;
/**
* Create a new CompositeCachingDefinitionSource for the given sources.
* @param cacheDefinitionSourcess the CacheDefinitionSource instances to combine
*/
public CompositeCacheDefinitionSource(CacheDefinitionSource[] cacheDefinitionSources) {
Assert.notNull(cacheDefinitionSources, "cacheDefinitionSource array must not be null");
this.cacheDefinitionSources = cacheDefinitionSources;
}
/**
* Return the CacheDefinitionSource instances that this
* CompositeCachingDefinitionSource combines.
*/
public final CacheDefinitionSource[] getCacheDefinitionSources() {
return this.cacheDefinitionSources;
}
public CacheDefinition getCacheDefinition(Method method, Class<?> targetClass) {
for (CacheDefinitionSource source : cacheDefinitionSources) {
CacheDefinition definition = source.getCacheDefinition(method, targetClass);
if (definition != null) {
return definition;
}
}
return null;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.Collection;
import org.springframework.cache.Cache;
import org.springframework.util.Assert;
/**
* Default implementation of expression root object.
*
* @author Costin Leau
*/
public class DefaultCacheExpressionRootObject implements CacheExpressionRootObject {
private final String methodName;
private final Collection<Cache<?, ?>> caches;
public DefaultCacheExpressionRootObject(Collection<Cache<?,?>> caches, String methodName) {
Assert.hasText(methodName, "method name is required");
this.methodName = methodName;
this.caches = caches;
}
public String getMethodName() {
return methodName;
}
public Collection<Cache<?, ?>> getCaches() {
return caches;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2010 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;
/**
* Default implementation of the {@link CacheInvalidateDefinition} interface.
*
* @author Costin Leau
*/
public class DefaultCacheInvalidateDefinition extends AbstractCacheDefinition implements
CacheInvalidateDefinition {
private boolean cacheWide = false;
public boolean isCacheWide() {
return cacheWide;
}
public void setCacheWide(boolean cacheWide) {
this.cacheWide = cacheWide;
}
@Override
protected StringBuilder getDefinitionDescription() {
StringBuilder sb = super.getDefinitionDescription();
sb.append(",");
sb.append(cacheWide);
return sb;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2010 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;
/**
* Default implementation of the {@link CacheUpdateDefinition} interface.
*
* @author Costin Leau
*/
public class DefaultCacheUpdateDefinition extends AbstractCacheDefinition implements CacheUpdateDefinition {
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.cache.Cache;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Utility class handling the SpEL expression parsing.
* Meant to be used as a reusable, thread-safe component.
*
* Performs internal caching for performance reasons.
*
* @author Costin Leau
*/
class ExpressionEvaluator {
private SpelExpressionParser parser = new SpelExpressionParser();
// shared param discoverer since it caches data internally
private ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private Map<Method, Expression> conditionCache = new ConcurrentHashMap<Method, Expression>();
private Map<Method, Expression> keyCache = new ConcurrentHashMap<Method, Expression>();
private Map<Method, Method> targetMethodCache = new ConcurrentHashMap<Method, Method>();
EvaluationContext createEvaluationContext(Collection<Cache<?, ?>> caches, Method method, Object[] args, Class<?> targetClass) {
DefaultCacheExpressionRootObject rootObject = new DefaultCacheExpressionRootObject(caches, method.getName());
StandardEvaluationContext evaluationContext = new LazyParamAwareEvaluationContext(rootObject,
paramNameDiscoverer, method, args, targetClass, targetMethodCache);
return evaluationContext;
}
boolean condition(String conditionExpression, Method method, EvaluationContext evalContext) {
Expression condExp = conditionCache.get(conditionExpression);
if (condExp == null) {
condExp = parser.parseExpression(conditionExpression);
conditionCache.put(method, condExp);
}
return condExp.getValue(evalContext, boolean.class);
}
Object key(String keyExpression, Method method, EvaluationContext evalContext) {
Expression keyExp = keyCache.get(keyExpression);
if (keyExp == null) {
keyExp = parser.parseExpression(keyExpression);
keyCache.put(method, keyExp);
}
return keyExp.getValue(evalContext);
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;
/**
* Evaluation context class that adds a method parameters as SpEL variables,
* in a lazy manner. The lazy nature eliminates unneeded parsing of classes
* byte code for parameter discovery.
*
* To limit the creation of objects, an ugly constructor is used (rather then a
* dedicated 'closure'-like class for deferred execution).
*
* @author Costin Leau
*/
class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
private final ParameterNameDiscoverer paramDiscoverer;
private final Method method;
private final Object[] args;
private Class<?> targetClass;
private Map<Method, Method> methodCache;
private boolean paramLoaded = false;
LazyParamAwareEvaluationContext(Object rootObject, ParameterNameDiscoverer paramDiscoverer, Method method,
Object[] args, Class<?> targetClass, Map<Method, Method> methodCache) {
super(rootObject);
this.paramDiscoverer = paramDiscoverer;
this.method = method;
this.args = args;
this.targetClass = targetClass;
this.methodCache = methodCache;
}
/**
* Load the param information only when needed.
*/
@Override
public Object lookupVariable(String name) {
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
if (!paramLoaded) {
paramLoaded = true;
loadArgsAsVariables();
variable = super.lookupVariable(name);
}
return variable;
}
private void loadArgsAsVariables() {
// shortcut if no args need to be loaded
if (ObjectUtils.isEmpty(args)) {
return;
}
Method targetMethod = methodCache.get(method);
if (targetMethod == null) {
targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
if (targetMethod == null) {
targetMethod = method;
}
methodCache.put(method, targetMethod);
}
// save arguments as indexed variables
for (int i = 0; i < args.length; i++) {
super.setVariable("p" + i, args[i]);
}
String[] parameterNames = paramDiscoverer.getParameterNames(targetMethod);
// save parameter names (if discovered)
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
super.setVariable(parameterNames[i], args[i]);
}
}
}
}

View File

@@ -0,0 +1,8 @@
/**
* AOP-based solution for declarative caching demarcation.
* Builds on the AOP infrastructure in org.springframework.aop.framework.
* Any POJO can be cache advised with Spring.
*
*/
package org.springframework.cache.interceptor;

View File

@@ -0,0 +1,7 @@
/**
* Spring's generic cache abstraction.
* Concrete implementations are provided in the subpackages.
*
*/
package org.springframework.cache;

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2010 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.support;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.util.Assert;
/**
* Abstract base class implementing the common CacheManager methods. Useful for 'static' environments where the
* backing caches do not change.
*
* @author Costin Leau
*/
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
// fast lookup by name map
private final ConcurrentMap<String, Cache<?, ?>> caches = new ConcurrentHashMap<String, Cache<?, ?>>();
private Collection<String> names;
public void afterPropertiesSet() {
Collection<Cache<?, ?>> cacheSet = loadCaches();
Assert.notEmpty(cacheSet);
caches.clear();
// preserve the initial order of the cache names
Set<String> cacheNames = new LinkedHashSet<String>(cacheSet.size());
for (Cache<?, ?> cache : cacheSet) {
caches.put(cache.getName(), cache);
cacheNames.add(cache.getName());
}
names = Collections.unmodifiableSet(cacheNames);
}
/**
* Loads the caches into the cache manager. Occurs at startup.
* The returned collection should not be null.
*
* @param caches the collection of caches handled by the manager
*/
protected abstract Collection<Cache<?, ?>> loadCaches();
/**
* Returns the internal cache map.
*
* @return internal cache map
*/
protected final ConcurrentMap<String, Cache<?, ?>> getCacheMap() {
return caches;
}
@SuppressWarnings("unchecked")
public <K, V> Cache<K, V> getCache(String name) {
return (Cache<K, V>) caches.get(name);
}
public Collection<String> getCacheNames() {
return names;
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2010 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.support;
import java.io.Serializable;
import java.util.Map;
import org.springframework.cache.Cache;
import org.springframework.util.Assert;
/**
* Abstract base class delegating most of the {@link Map}-like methods
* to the underlying cache.
*
* <b>Note:</b>Allows null values to be stored, even if the underlying map
* does not support them.
*
* @author Costin Leau
*/
public abstract class AbstractDelegatingCache<K, V> implements Cache<K, V> {
private static class NullHolder implements Serializable {
private static final long serialVersionUID = 1L;
}
public static final Object NULL_HOLDER = new NullHolder();
private final Map<K, V> delegate;
private final boolean allowNullValues;
/**
* Creates a new instance using the given delegate.
*
* @param <D> map type
* @param delegate map delegate
*/
public <D extends Map<K, V>> AbstractDelegatingCache(D delegate) {
this(delegate, false);
}
/**
* Creates a new instance using the given delegate.
*
* @param <D> map type
* @param delegate map delegate
* @param allowNullValues flag indicating whether null values are allowed or not
*/
public <D extends Map<K, V>> AbstractDelegatingCache(D delegate, boolean allowNullValues) {
Assert.notNull(delegate);
this.delegate = delegate;
this.allowNullValues = allowNullValues;
}
public boolean getAllowNullValues() {
return allowNullValues;
}
public void clear() {
delegate.clear();
}
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
public V get(Object key) {
return filterNull(delegate.get(key));
}
@SuppressWarnings("unchecked")
public V put(K key, V value) {
if (allowNullValues && value == null) {
Map map = delegate;
Object val = map.put(key, NULL_HOLDER);
if (val == NULL_HOLDER) {
return null;
}
return (V) val;
}
return filterNull(delegate.put(key, value));
}
public V remove(Object key) {
return filterNull(delegate.remove(key));
}
protected V filterNull(V val) {
if (allowNullValues && val == NULL_HOLDER) {
return null;
}
return val;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2010 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.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.util.Assert;
/**
* Composite {@link CacheManager} implementation that iterates
* over a given collection of {@link CacheManager} instances.
*
* @author Costin Leau
*/
public class CompositeCacheManager implements CacheManager {
private CacheManager[] cacheManagers;
public <K, V> Cache<K, V> getCache(String name) {
Cache<K, V> cache = null;
for (CacheManager cacheManager : cacheManagers) {
cache = cacheManager.getCache(name);
if (cache != null) {
return cache;
}
}
return cache;
}
public Collection<String> getCacheNames() {
List<String> names = new ArrayList<String>();
for (CacheManager manager : cacheManagers) {
names.addAll(manager.getCacheNames());
}
return Collections.unmodifiableCollection(names);
}
public void setCacheManagers(CacheManager[] cacheManagers) {
Assert.notEmpty(cacheManagers, "non-null/empty array required");
this.cacheManagers = cacheManagers.clone();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2010 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.support;
import java.lang.reflect.Method;
import org.springframework.cache.KeyGenerator;
/**
* Default key generator. Computes a resulting key based on the hashcode of the
* given parameters.
*
* @author Costin Leau
*/
public class DefaultKeyGenerator implements KeyGenerator<Object> {
public Object extract(Method method, Object... params) {
int hashCode = 17;
for (Object object : params) {
hashCode = 31 * hashCode + object.hashCode();
}
return Integer.valueOf(hashCode);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2010 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.support;
import java.util.Collection;
import org.springframework.cache.Cache;
/**
* Simple cache manager working against a given collection of caches. Useful for testing or simple
* caching declarations.
*
* @author Costin Leau
*/
public class MapCacheManager extends AbstractCacheManager {
private Collection<Cache<?, ?>> caches;
@Override
protected Collection<Cache<?, ?>> loadCaches() {
return caches;
}
public void setCaches(Collection<Cache<?, ?>> caches) {
this.caches = caches;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2010 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.support;
import java.util.Collection;
import org.springframework.cache.Cache;
/**
* Simple cache manager working against a given collection of caches. Useful for testing or simple
* caching declarations.
*
* @author Costin Leau
*/
public class SimpleCacheManager extends AbstractCacheManager {
private Collection<Cache<?, ?>> caches;
@Override
protected Collection<Cache<?, ?>> loadCaches() {
return caches;
}
public void setCaches(Collection<Cache<?, ?>> caches) {
this.caches = caches;
}
}

View File

@@ -0,0 +1,6 @@
/**
* Support classes for the the org.springframework.cache package.
* Provides abstract classes for cache managers and caches.
*/
package org.springframework.cache.support;