Browse Source
+ initial commit of caching abstraction + main API + Spring AOP and AspectJ support + annotation driven, declarative support + initial namespace draftpull/7/head
62 changed files with 4225 additions and 0 deletions
@ -0,0 +1,81 @@
@@ -0,0 +1,81 @@
|
||||
/* |
||||
* 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.aspectj; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.concurrent.Callable; |
||||
|
||||
import org.aspectj.lang.annotation.SuppressAjWarnings; |
||||
import org.aspectj.lang.reflect.MethodSignature; |
||||
import org.springframework.cache.interceptor.CacheAspectSupport; |
||||
import org.springframework.cache.interceptor.CacheDefinitionSource; |
||||
|
||||
/** |
||||
* Abstract superaspect for AspectJ cache aspects. Concrete |
||||
* subaspects will implement the <code>cacheMethodExecution()</code> |
||||
* pointcut using a strategy such as Java 5 annotations. |
||||
* |
||||
* <p>Suitable for use inside or outside the Spring IoC container. |
||||
* Set the "cacheManager" property appropriately, allowing |
||||
* use of any cache implementation supported by Spring. |
||||
* |
||||
* <p><b>NB:</b> If a method implements an interface that is itself |
||||
* cache annotated, the relevant Spring cache definition |
||||
* will <i>not</i> be resolved. |
||||
|
||||
* @author Costin Leau |
||||
*/ |
||||
public abstract aspect AbstractCacheAspect extends CacheAspectSupport { |
||||
|
||||
protected AbstractCacheAspect() { |
||||
} |
||||
|
||||
/** |
||||
* Construct object using the given caching metadata retrieval strategy. |
||||
* @param cds {@link CacheDefinitionSource} implementation, retrieving Spring |
||||
* cache metadata for each joinpoint. |
||||
*/ |
||||
protected AbstractCacheAspect(CacheDefinitionSource... cds) { |
||||
setCacheDefinitionSources(cds); |
||||
} |
||||
|
||||
@SuppressAjWarnings("adviceDidNotMatch") |
||||
Object around(final Object cachedObject) : cacheMethodExecution(cachedObject){ |
||||
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature(); |
||||
Method method = methodSignature.getMethod(); |
||||
|
||||
Callable<Object> ajInvocation = new Callable<Object>() { |
||||
|
||||
public Object call() { |
||||
return proceed(cachedObject); |
||||
} |
||||
}; |
||||
|
||||
try { |
||||
return execute(ajInvocation, thisJoinPoint.getTarget(), method, thisJoinPoint.getArgs()); |
||||
} catch (Exception ex) { |
||||
throw new UnsupportedOperationException("Should not throw exception", ex); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Concrete subaspects must implement this pointcut, to identify |
||||
* cached methods. For each selected joinpoint, {@link CacheOperationDefinition} |
||||
* will be retrieved using Spring's {@link CacheDefinitionSource} interface. |
||||
*/ |
||||
protected abstract pointcut cacheMethodExecution(Object cachedObject); |
||||
} |
||||
@ -0,0 +1,86 @@
@@ -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.aspectj; |
||||
|
||||
import org.springframework.cache.annotation.AnnotationCacheDefinitionSource; |
||||
import org.springframework.cache.annotation.CacheEvict; |
||||
import org.springframework.cache.annotation.Cacheable; |
||||
|
||||
/** |
||||
* Concrete AspectJ cache aspect using Spring {@link Cacheable} annotation |
||||
* for JDK 1.5+. |
||||
* |
||||
* <p>When using this aspect, you <i>must</i> annotate the implementation class |
||||
* (and/or methods within that class), <i>not</i> the interface (if any) that |
||||
* the class implements. AspectJ follows Java's rule that annotations on |
||||
* interfaces are <i>not</i> inherited. |
||||
* |
||||
* <p>A {@link Cacheable} annotation on a class specifies the default caching |
||||
* semantics for the execution of any <b>public</b> operation in the class. |
||||
* |
||||
* <p>A {@link Cacheable} annotation on a method within the class overrides the |
||||
* default caching semantics given by the class annotation (if present). |
||||
* Any method may be annotated (regardless of visibility). |
||||
* Annotating non-public methods directly is the only way |
||||
* to get caching demarcation for the execution of such operations. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public aspect AnnotationCacheAspect extends AbstractCacheAspect { |
||||
|
||||
public AnnotationCacheAspect() { |
||||
super(new AnnotationCacheDefinitionSource(false)); |
||||
} |
||||
|
||||
/** |
||||
* Matches the execution of any public method in a type with the |
||||
* {@link Cacheable} annotation, or any subtype of a type with the |
||||
* {@link Cacheable} annotation. |
||||
*/ |
||||
private pointcut executionOfAnyPublicMethodInAtCacheableType() : |
||||
execution(public * ((@Cacheable *)+).*(..)) && @this(Cacheable); |
||||
|
||||
/** |
||||
* Matches the execution of any public method in a type with the |
||||
* {@link CacheEvict} annotation, or any subtype of a type with the |
||||
* {@link CacheEvict} annotation. |
||||
*/ |
||||
private pointcut executionOfAnyPublicMethodInAtCacheEvictType() : |
||||
execution(public * ((@CacheEvict *)+).*(..)) && @this(CacheEvict); |
||||
|
||||
/** |
||||
* Matches the execution of any method with the |
||||
* Cacheable annotation. |
||||
*/ |
||||
private pointcut executionOfCacheableMethod() : |
||||
execution(* *(..)) && @annotation(Cacheable); |
||||
|
||||
/** |
||||
* Matches the execution of any method with the {@link CacheEvict} annotation. |
||||
*/ |
||||
private pointcut executionOfCacheEvictMethod() : |
||||
execution(* *(..)) && @annotation(CacheEvict); |
||||
|
||||
/** |
||||
* Definition of pointcut from super aspect - matched join points |
||||
* will have Spring cache management applied. |
||||
*/ |
||||
protected pointcut cacheMethodExecution(Object cachedObject) : |
||||
(executionOfAnyPublicMethodInAtCacheableType() || executionOfAnyPublicMethodInAtCacheEvictType() |
||||
|| executionOfCacheableMethod() || executionOfCacheEvictMethod()) |
||||
&& this(cachedObject); |
||||
} |
||||
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
/* |
||||
* 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.aspectj; |
||||
|
||||
import static org.junit.Assert.assertNotSame; |
||||
import static org.junit.Assert.assertSame; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.cache.config.CacheableService; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.support.ClassPathXmlApplicationContext; |
||||
|
||||
/** |
||||
* Abstract annotation test (containing several reusable methods). |
||||
* @author Costin Leau |
||||
*/ |
||||
public abstract class AbstractAnnotationTest { |
||||
|
||||
protected ApplicationContext ctx; |
||||
|
||||
protected CacheableService cs; |
||||
|
||||
protected CacheableService ccs; |
||||
|
||||
protected abstract String getConfig(); |
||||
|
||||
@Before |
||||
public void setup() { |
||||
ctx = new ClassPathXmlApplicationContext(getConfig()); |
||||
cs = ctx.getBean("service", CacheableService.class); |
||||
ccs = ctx.getBean("classService", CacheableService.class); |
||||
} |
||||
|
||||
public void testCacheable(CacheableService service) throws Exception { |
||||
Object o1 = new Object(); |
||||
Object o2 = new Object(); |
||||
|
||||
Object r1 = service.cache(o1); |
||||
Object r2 = service.cache(o1); |
||||
Object r3 = service.cache(o1); |
||||
|
||||
assertSame(r1, r2); |
||||
assertSame(r1, r3); |
||||
} |
||||
|
||||
public void testInvalidate(CacheableService service) throws Exception { |
||||
Object o1 = new Object(); |
||||
Object o2 = new Object(); |
||||
|
||||
Object r1 = service.cache(o1); |
||||
Object r2 = service.cache(o1); |
||||
|
||||
assertSame(r1, r2); |
||||
service.invalidate(o1); |
||||
Object r3 = service.cache(o1); |
||||
Object r4 = service.cache(o1); |
||||
assertNotSame(r1, r3); |
||||
assertSame(r3, r4); |
||||
} |
||||
|
||||
public void testConditionalExpression(CacheableService service) throws Exception { |
||||
Object r1 = service.conditional(4); |
||||
Object r2 = service.conditional(4); |
||||
|
||||
assertNotSame(r1, r2); |
||||
|
||||
Object r3 = service.conditional(3); |
||||
Object r4 = service.conditional(3); |
||||
|
||||
assertSame(r3, r4); |
||||
} |
||||
|
||||
public void testKeyExpression(CacheableService service) throws Exception { |
||||
Object r1 = service.key(5, 1); |
||||
Object r2 = service.key(5, 2); |
||||
|
||||
assertSame(r1, r2); |
||||
|
||||
Object r3 = service.key(1, 5); |
||||
Object r4 = service.key(2, 5); |
||||
|
||||
assertNotSame(r3, r4); |
||||
} |
||||
|
||||
@Test |
||||
public void testCacheable() throws Exception { |
||||
testCacheable(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testInvalidate() throws Exception { |
||||
testInvalidate(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testConditionalExpression() throws Exception { |
||||
testConditionalExpression(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testKeyExpression() throws Exception { |
||||
testKeyExpression(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testClassCacheCacheable() throws Exception { |
||||
testCacheable(ccs); |
||||
} |
||||
|
||||
@Test |
||||
public void testClassCacheInvalidate() throws Exception { |
||||
testInvalidate(ccs); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* 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.aspectj; |
||||
|
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
*/ |
||||
public class AspectJAnnotationTest extends AbstractAnnotationTest { |
||||
|
||||
@Override |
||||
protected String getConfig() { |
||||
return "/org/springframework/cache/config/annotation-cache-aspectj.xml"; |
||||
} |
||||
} |
||||
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
/* |
||||
* 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 java.util.concurrent.atomic.AtomicLong; |
||||
|
||||
import org.springframework.cache.annotation.CacheEvict; |
||||
import org.springframework.cache.annotation.Cacheable; |
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
*/ |
||||
@Cacheable |
||||
public class AnnotatedClassCacheableService implements CacheableService { |
||||
|
||||
private AtomicLong counter = new AtomicLong(); |
||||
|
||||
public Object cache(Object arg1) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
|
||||
public Object conditional(int field) { |
||||
return null; |
||||
} |
||||
|
||||
@CacheEvict |
||||
public void invalidate(Object arg1) { |
||||
} |
||||
|
||||
@Cacheable(key = "#p0") |
||||
public Object key(Object arg1, Object arg2) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
} |
||||
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
/** |
||||
* Basic service interface. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public interface CacheableService<T> { |
||||
|
||||
T cache(Object arg1); |
||||
|
||||
void invalidate(Object arg1); |
||||
|
||||
T conditional(int field); |
||||
|
||||
T key(Object arg1, Object arg2); |
||||
|
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* 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 java.util.concurrent.atomic.AtomicLong; |
||||
|
||||
import org.springframework.cache.annotation.CacheEvict; |
||||
import org.springframework.cache.annotation.Cacheable; |
||||
|
||||
|
||||
/** |
||||
* Simple cacheable service |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public class DefaultCacheableService implements CacheableService<Long> { |
||||
|
||||
private AtomicLong counter = new AtomicLong(); |
||||
|
||||
@Cacheable |
||||
public Long cache(Object arg1) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
|
||||
@CacheEvict |
||||
public void invalidate(Object arg1) { |
||||
} |
||||
|
||||
@Cacheable(condition = "#classField == 3") |
||||
public Long conditional(int classField) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
|
||||
@Cacheable(key = "#p0") |
||||
public Long key(Object arg1, Object arg2) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
} |
||||
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:aop="http://www.springframework.org/schema/aop" |
||||
xmlns:p="http://www.springframework.org/schema/p" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> |
||||
|
||||
<bean id="annotationSource" class="org.springframework.cache.annotation.AnnotationCacheDefinitionSource"/> |
||||
|
||||
<aop:config> |
||||
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/> |
||||
</aop:config> |
||||
|
||||
<bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf"> |
||||
<property name="cacheManager" ref="cacheManager"/> |
||||
<property name="cacheDefinitionSources" ref="annotationSource"/> |
||||
</bean> |
||||
|
||||
<!-- |
||||
<bean id="advisor" class="org.springframework.cache.interceptor.BeanFactoryCacheDefinitionSourceAdvisor"> |
||||
<property name="cacheDefinitionSource" ref="annotationSource"/> |
||||
<property name="adviceBeanName" value="cacheAspect"/> |
||||
</bean> |
||||
--> |
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.MapCacheManager"> |
||||
<property name="caches"> |
||||
<set> |
||||
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> |
||||
</set> |
||||
</property> |
||||
</bean> |
||||
|
||||
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/> |
||||
|
||||
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/> |
||||
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/> |
||||
|
||||
</beans> |
||||
@ -0,0 +1,200 @@
@@ -0,0 +1,200 @@
|
||||
/* |
||||
* 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. |
||||
* |
||||
* @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(); |
||||
} |
||||
@ -0,0 +1,43 @@
@@ -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(); |
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
/** |
||||
* Cache 'key' extractor. Used for creating a key based on the given |
||||
* parameters. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
// CL: could be renamed to KeyFactory
|
||||
public interface KeyGenerator<K> { |
||||
|
||||
K extract(Object... params); |
||||
} |
||||
@ -0,0 +1,125 @@
@@ -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.parseTransactionAnnotation(ae); |
||||
if (attr != null) { |
||||
return attr; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* By default, only public methods can be made cacheable. |
||||
*/ |
||||
@Override |
||||
protected boolean allowPublicMethodsOnly() { |
||||
return this.publicMethodsOnly; |
||||
} |
||||
} |
||||
@ -0,0 +1,46 @@
@@ -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 parseTransactionAnnotation(AnnotatedElement ae); |
||||
} |
||||
@ -0,0 +1,67 @@
@@ -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() default ""; |
||||
|
||||
/** |
||||
* 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 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; |
||||
} |
||||
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
/* |
||||
* 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 cache 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() default ""; |
||||
|
||||
/** |
||||
* 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 ""; |
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* 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.CacheInvalidateDefinition; |
||||
import org.springframework.cache.interceptor.CacheDefinition; |
||||
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 parseTransactionAnnotation(AnnotatedElement ae) { |
||||
Cacheable update = findAnnotation(ae, Cacheable.class); |
||||
|
||||
if (update != null) { |
||||
return parseCacheableAnnotation(update); |
||||
} |
||||
|
||||
CacheEvict invalidate = findAnnotation(ae, CacheEvict.class); |
||||
|
||||
if (invalidate != null) { |
||||
return parseInvalidateAnnotation(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; |
||||
} |
||||
|
||||
public CacheUpdateDefinition parseCacheableAnnotation(Cacheable ann) { |
||||
DefaultCacheUpdateDefinition dcud = new DefaultCacheUpdateDefinition(); |
||||
dcud.setCacheName(ann.value()); |
||||
dcud.setCondition(ann.condition()); |
||||
dcud.setKey(ann.key()); |
||||
|
||||
return dcud; |
||||
} |
||||
|
||||
public CacheInvalidateDefinition parseInvalidateAnnotation(CacheEvict ann) { |
||||
DefaultCacheInvalidateDefinition dcid = new DefaultCacheInvalidateDefinition(); |
||||
dcid.setCacheName(ann.value()); |
||||
dcid.setCondition(ann.condition()); |
||||
dcid.setKey(ann.key()); |
||||
dcid.setCacheWide(ann.allEntries()); |
||||
|
||||
return dcid; |
||||
} |
||||
} |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
* 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); |
||||
this.store = delegate; |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public ConcurrentMap<K, V> getNativeCache() { |
||||
return store; |
||||
} |
||||
|
||||
public V putIfAbsent(K key, V value) { |
||||
return store.putIfAbsent(key, value); |
||||
} |
||||
|
||||
public boolean remove(Object key, Object value) { |
||||
return store.remove(key, value); |
||||
} |
||||
|
||||
public boolean replace(K key, V oldValue, V newValue) { |
||||
return store.replace(key, oldValue, newValue); |
||||
} |
||||
|
||||
public V replace(K key, V value) { |
||||
return store.replace(key, value); |
||||
} |
||||
} |
||||
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
/** |
||||
* 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) { |
||||
setName(beanName); |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public void setStore(ConcurrentMap<K, V> store) { |
||||
this.store = store; |
||||
} |
||||
} |
||||
@ -0,0 +1,158 @@
@@ -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><cache:annotation-driven/></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); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,35 @@
@@ -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()); |
||||
} |
||||
} |
||||
@ -0,0 +1,198 @@
@@ -0,0 +1,198 @@
|
||||
/* |
||||
* 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.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import net.sf.ehcache.Ehcache; |
||||
import net.sf.ehcache.Element; |
||||
import net.sf.ehcache.Status; |
||||
|
||||
import org.springframework.cache.Cache; |
||||
import org.springframework.cache.support.SimpleMapEntry; |
||||
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); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Set<Map.Entry<Object, Object>> entrySet() { |
||||
List<Object> keys = cache.getKeys(); |
||||
Set<Map.Entry<Object, Object>> entries = new LinkedHashSet<Map.Entry<Object, Object>>(keys.size()); |
||||
for (Object key : keys) { |
||||
Element element = cache.get(key); |
||||
if (element != null) { |
||||
entries.add(new SimpleMapEntry(key, element.getObjectValue())); |
||||
} |
||||
} |
||||
|
||||
return Collections.unmodifiableSet(entries); |
||||
} |
||||
|
||||
public Object get(Object key) { |
||||
Element element = cache.get(key); |
||||
return (element != null ? element.getObjectValue() : null); |
||||
} |
||||
|
||||
public boolean isEmpty() { |
||||
return cache.getSize() == 0; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Set<Object> keySet() { |
||||
List<Object> keys = cache.getKeys(); |
||||
Set<Object> keySet = new LinkedHashSet<Object>(keys.size()); |
||||
for (Object key : keys) { |
||||
keySet.add(key); |
||||
} |
||||
return Collections.unmodifiableSet(keySet); |
||||
} |
||||
|
||||
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 void putAll(Map<? extends Object, ? extends Object> m) { |
||||
for (Map.Entry<? extends Object, ? extends Object> entry : m.entrySet()) { |
||||
cache.put(new Element(entry.getKey(), entry.getValue())); |
||||
} |
||||
} |
||||
|
||||
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 int size() { |
||||
return cache.getSize(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Collection<Object> values() { |
||||
List<Object> keys = cache.getKeys(); |
||||
List<Object> values = new ArrayList<Object>(keys.size()); |
||||
for (Object key : keys) { |
||||
Element element = cache.get(key); |
||||
if (element != null) { |
||||
values.add(element.getObjectValue()); |
||||
} |
||||
} |
||||
return Collections.unmodifiableCollection(values); |
||||
} |
||||
|
||||
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; |
||||
} |
||||
} |
||||
@ -0,0 +1,82 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,108 @@
@@ -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.interceptor; |
||||
|
||||
|
||||
/** |
||||
* Base class implementing {@link CacheDefinition}. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
abstract class AbstractCacheDefinition implements CacheDefinition { |
||||
|
||||
private String cacheName = ""; |
||||
private String condition = ""; |
||||
private String key = ""; |
||||
private String name = ""; |
||||
|
||||
|
||||
public String getCacheName() { |
||||
return cacheName; |
||||
} |
||||
|
||||
public String getCondition() { |
||||
return condition; |
||||
} |
||||
|
||||
public String getKey() { |
||||
return key; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setCacheName(String cacheName) { |
||||
this.cacheName = cacheName; |
||||
} |
||||
|
||||
public void setCondition(String condition) { |
||||
this.condition = condition; |
||||
} |
||||
|
||||
public void setKey(String key) { |
||||
this.key = key; |
||||
} |
||||
|
||||
public void setName(String 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(cacheName); |
||||
result.append(','); |
||||
result.append(condition); |
||||
result.append(","); |
||||
result.append(key); |
||||
|
||||
return result; |
||||
} |
||||
} |
||||
@ -0,0 +1,218 @@
@@ -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); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,62 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,240 @@
@@ -0,0 +1,240 @@
|
||||
/* |
||||
* 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.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 { |
||||
|
||||
private static class EmptyHolder implements Serializable { |
||||
} |
||||
|
||||
private static final Object NULL_RETURN = new EmptyHolder(); |
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private CacheManager cacheManager; |
||||
|
||||
private CacheDefinitionSource cacheDefinitionSource; |
||||
|
||||
private final ExpressionEvaluator evaluator = new ExpressionEvaluator(); |
||||
|
||||
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; |
||||
} |
||||
|
||||
/** |
||||
* 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 <K, V> Cache<K, V> getCache(CacheDefinition definition) { |
||||
// TODO: add support for multiple caches
|
||||
// TODO: add behaviour for the default cache
|
||||
String name = definition.getCacheName(); |
||||
if (!StringUtils.hasText(name)) { |
||||
name = cacheManager.getCacheNames().iterator().next(); |
||||
} |
||||
return cacheManager.getCache(name); |
||||
} |
||||
|
||||
protected CacheOperationContext getOperationContext(CacheDefinition definition, Method method, Object[] args, Class<?> targetClass) { |
||||
return new CacheOperationContext(definition, method, args, targetClass); |
||||
} |
||||
|
||||
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); |
||||
Cache<Object, Object> cache = (Cache<Object, Object>) context.getCache(); |
||||
|
||||
if (context.hasConditionPassed()) { |
||||
// check operation
|
||||
if (cacheDef instanceof CacheUpdateDefinition) { |
||||
// check cache first
|
||||
Object key = context.generateKey(); |
||||
retVal = cache.get(key); |
||||
if (retVal == null) { |
||||
retVal = invocation.call(); |
||||
cache.put(key, (retVal == null ? NULL_RETURN : retVal)); |
||||
} |
||||
} |
||||
|
||||
if (cacheDef instanceof CacheInvalidateDefinition) { |
||||
CacheInvalidateDefinition invalidateDef = (CacheInvalidateDefinition) cacheDef; |
||||
retVal = invocation.call(); |
||||
|
||||
// flush the cache (ignore arguments)
|
||||
if (invalidateDef.isCacheWide()) { |
||||
cache.clear(); |
||||
} |
||||
else { |
||||
// check key
|
||||
Object key = context.generateKey(); |
||||
cache.remove(key); |
||||
} |
||||
} |
||||
|
||||
return retVal; |
||||
} |
||||
} |
||||
|
||||
return invocation.call(); |
||||
} |
||||
|
||||
protected class CacheOperationContext { |
||||
|
||||
private CacheDefinition definition; |
||||
private final Cache<?, ?> cache; |
||||
private final Method method; |
||||
private final Object[] args; |
||||
|
||||
// context passed around to avoid multiple creations
|
||||
private final EvaluationContext evalContext; |
||||
|
||||
private final KeyGenerator keyGenerator = new DefaultKeyGenerator(); |
||||
|
||||
public CacheOperationContext(CacheDefinition operationDefinition, Method method, Object[] args, |
||||
Class<?> targetClass) { |
||||
this.definition = operationDefinition; |
||||
this.cache = CacheAspectSupport.this.getCache(definition); |
||||
this.method = method; |
||||
this.args = args; |
||||
|
||||
this.evalContext = evaluator.createEvaluationContext(cache, 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(args); |
||||
} |
||||
|
||||
protected Cache<?, ?> getCache() { |
||||
return cache; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,56 @@
@@ -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; |
||||
|
||||
/** |
||||
* 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 name of the cache against which this operation is performed. |
||||
* |
||||
* @return name of the cache on which the operation is performed. |
||||
*/ |
||||
String getCacheName(); |
||||
|
||||
/** |
||||
* 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(); |
||||
|
||||
} |
||||
@ -0,0 +1,41 @@
@@ -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); |
||||
} |
||||
@ -0,0 +1,68 @@
@@ -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(); |
||||
} |
||||
@ -0,0 +1,41 @@
@@ -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 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 cache against which the method is executed. |
||||
* |
||||
* @return current cache |
||||
*/ |
||||
Cache getCache(); |
||||
} |
||||
@ -0,0 +1,63 @@
@@ -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()); |
||||
} |
||||
} |
||||
@ -0,0 +1,33 @@
@@ -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(); |
||||
} |
||||
@ -0,0 +1,56 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,32 @@
@@ -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(); |
||||
} |
||||
@ -0,0 +1,63 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* 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.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 Cache cache; |
||||
|
||||
public DefaultCacheExpressionRootObject(Cache cache, String methodName) { |
||||
Assert.hasText(methodName, "method name is required"); |
||||
this.methodName = methodName; |
||||
this.cache = cache; |
||||
} |
||||
|
||||
public String getMethodName() { |
||||
return methodName; |
||||
} |
||||
|
||||
public Cache getCache() { |
||||
return cache; |
||||
} |
||||
} |
||||
@ -0,0 +1,44 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -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 { |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
/* |
||||
* 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 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(Cache<?, ?> cache, Method method, Object[] args, Class<?> targetClass) { |
||||
DefaultCacheExpressionRootObject rootObject = new DefaultCacheExpressionRootObject(cache, 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); |
||||
} |
||||
} |
||||
@ -0,0 +1,103 @@
@@ -0,0 +1,103 @@
|
||||
/* |
||||
* 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) { |
||||
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]); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,86 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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.support; |
||||
|
||||
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. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public abstract class AbstractDelegatingCache<K, V> implements Cache<K, V> { |
||||
|
||||
private final Map<K, V> delegate; |
||||
|
||||
public <D extends Map<K, V>> AbstractDelegatingCache(D delegate) { |
||||
Assert.notNull(delegate); |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
public void clear() { |
||||
delegate.clear(); |
||||
} |
||||
|
||||
public boolean containsKey(Object key) { |
||||
return delegate.containsKey(key); |
||||
} |
||||
|
||||
public V get(Object key) { |
||||
return delegate.get(key); |
||||
} |
||||
|
||||
public V put(K key, V value) { |
||||
return delegate.put(key, value); |
||||
} |
||||
|
||||
public V remove(Object key) { |
||||
return delegate.remove(key); |
||||
} |
||||
} |
||||
@ -0,0 +1,62 @@
@@ -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(); |
||||
} |
||||
} |
||||
@ -0,0 +1,35 @@
@@ -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.support; |
||||
|
||||
import org.springframework.cache.KeyGenerator; |
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
*/ |
||||
public class DefaultKeyGenerator implements KeyGenerator<Object> { |
||||
|
||||
public Object extract(Object... params) { |
||||
int hashCode = 17; |
||||
|
||||
for (Object object : params) { |
||||
hashCode = 31 * hashCode + object.hashCode(); |
||||
} |
||||
|
||||
return Integer.valueOf(hashCode); |
||||
} |
||||
} |
||||
@ -0,0 +1,41 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
/* |
||||
* 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.Map; |
||||
import java.util.Map.Entry; |
||||
|
||||
/** |
||||
* Basic {@link Entry} implementation. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public class SimpleMapEntry<K, V> implements Entry<K, V> { |
||||
private K key; |
||||
private V value; |
||||
|
||||
public SimpleMapEntry(K key, V value) { |
||||
this.key = key; |
||||
this.value = value; |
||||
} |
||||
|
||||
public SimpleMapEntry(Map.Entry<K, V> e) { |
||||
this.key = e.getKey(); |
||||
this.value = e.getValue(); |
||||
} |
||||
|
||||
public K getKey() { |
||||
return key; |
||||
} |
||||
|
||||
public V getValue() { |
||||
return value; |
||||
} |
||||
|
||||
public V setValue(V value) { |
||||
V oldValue = this.value; |
||||
this.value = value; |
||||
return oldValue; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public boolean equals(Object o) { |
||||
if (!(o instanceof Map.Entry)) |
||||
return false; |
||||
Map.Entry e = (Map.Entry) o; |
||||
return eq(key, e.getKey()) && eq(value, e.getValue()); |
||||
} |
||||
|
||||
public int hashCode() { |
||||
return ((key == null) ? 0 : key.hashCode()) ^ ((value == null) ? 0 : value.hashCode()); |
||||
} |
||||
|
||||
public String toString() { |
||||
return key + "=" + value; |
||||
} |
||||
|
||||
static boolean eq(Object o1, Object o2) { |
||||
return (o1 == null ? o2 == null : o1.equals(o2)); |
||||
} |
||||
} |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler |
||||
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
http\://www.springframework.org/schema/cache/spring-cache-3.1.xsd=org/springframework/cache/config/spring-cache-3.1.xsd |
||||
http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache-3.1.xsd |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
# Tooling related information for the cache namespace |
||||
http\://www.springframework.org/schema/cache@name=cache Namespace |
||||
http\://www.springframework.org/schema/cache@prefix=cache |
||||
http\://www.springframework.org/schema/cache@icon=org/springframework/cache/config/spring-cache.gif |
||||
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/cache" |
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
||||
xmlns:beans="http://www.springframework.org/schema/beans" |
||||
xmlns:tool="http://www.springframework.org/schema/tool" |
||||
targetNamespace="http://www.springframework.org/schema/cache" |
||||
elementFormDefault="qualified" |
||||
attributeFormDefault="unqualified"> |
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"/> |
||||
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.0.xsd"/> |
||||
|
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
Defines the elements used in the Spring Framework's declarative |
||||
cache management infrastructure. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
|
||||
<xsd:element name="annotation-driven"> |
||||
<xsd:complexType> |
||||
<xsd:annotation> |
||||
<xsd:documentation source="java:org.springframework.cache.annotation.AnnotationCacheOperationDefinitionSource"><![CDATA[ |
||||
Indicates that cache configuration is defined by Java 5 |
||||
annotations on bean classes, and that proxies are automatically |
||||
to be created for the relevant annotated beans. |
||||
|
||||
The default annotations supported are Spring's @Cacheable and @CacheInvalidate. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
<xsd:attribute name="cache-manager" type="xsd:string" default="cacheManager"> |
||||
<xsd:annotation> |
||||
<xsd:documentation source="java:org.springframework.cache.CacheManager"><![CDATA[ |
||||
The bean name of the CacheManager that is to be used to retrieve the backing caches. |
||||
|
||||
This attribute is not required, and only needs to be specified |
||||
explicitly if the bean name of the desired CacheManager |
||||
is not 'cacheManager'. |
||||
]]></xsd:documentation> |
||||
<xsd:appinfo> |
||||
<tool:annotation kind="ref"> |
||||
<tool:expected-type type="org.springframework.cache.CacheManager"/> |
||||
</tool:annotation> |
||||
</xsd:appinfo> |
||||
</xsd:annotation> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="mode" default="proxy"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
Should annotated beans be proxied using Spring's AOP framework, |
||||
or should they rather be weaved with an AspectJ transaction aspect? |
||||
|
||||
AspectJ weaving requires spring-aspects.jar on the classpath, |
||||
as well as load-time weaving (or compile-time weaving) enabled. |
||||
|
||||
Note: The weaving-based aspect requires the @Cacheable and @CacheInvalidate |
||||
annotations to be defined on the concrete class. Annotations in interfaces |
||||
will not work in that case (they will rather only work with interface-based proxies)! |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
<xsd:simpleType> |
||||
<xsd:restriction base="xsd:string"> |
||||
<xsd:enumeration value="proxy"/> |
||||
<xsd:enumeration value="aspectj"/> |
||||
</xsd:restriction> |
||||
</xsd:simpleType> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false"> |
||||
<xsd:annotation> |
||||
<xsd:documentation><![CDATA[ |
||||
Are class-based (CGLIB) proxies to be created? By default, standard |
||||
Java interface-based proxies are created. |
||||
|
||||
Note: Class-based proxies require the @Cacheable and @CacheInvalidate annotations |
||||
to be defined on the concrete class. Annotations in interfaces will not work |
||||
in that case (they will rather only work with interface-based proxies)! |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:attribute> |
||||
<xsd:attribute name="order" type="xsd:int"> |
||||
<xsd:annotation> |
||||
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[ |
||||
Controls the ordering of the execution of the cache advisor |
||||
when multiple advice executes at a specific joinpoint. |
||||
]]></xsd:documentation> |
||||
</xsd:annotation> |
||||
</xsd:attribute> |
||||
</xsd:complexType> |
||||
</xsd:element> |
||||
|
||||
</xsd:schema> |
||||
|
After Width: | Height: | Size: 592 B |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
/* |
||||
* 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.vendor.AbstractNativeCacheTest; |
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
*/ |
||||
public class ConcurrentCacheTest extends AbstractNativeCacheTest<ConcurrentMap<Object, Object>> { |
||||
|
||||
@Override |
||||
protected Cache createCache(ConcurrentMap<Object, Object> nativeCache) { |
||||
return new ConcurrentCache(nativeCache, CACHE_NAME); |
||||
} |
||||
|
||||
@Override |
||||
protected ConcurrentMap<Object, Object> createNativeCache() throws Exception { |
||||
return new ConcurrentHashMap<Object, Object>(); |
||||
} |
||||
} |
||||
@ -0,0 +1,129 @@
@@ -0,0 +1,129 @@
|
||||
/* |
||||
* 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 static org.junit.Assert.assertNotSame; |
||||
import static org.junit.Assert.assertSame; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.support.ClassPathXmlApplicationContext; |
||||
|
||||
/** |
||||
* Abstract annotation test (containing several reusable methods). |
||||
* @author Costin Leau |
||||
*/ |
||||
public abstract class AbstractAnnotationTest { |
||||
|
||||
protected ApplicationContext ctx; |
||||
|
||||
protected CacheableService cs; |
||||
|
||||
protected CacheableService ccs; |
||||
|
||||
protected abstract String getConfig(); |
||||
|
||||
@Before |
||||
public void setup() { |
||||
ctx = new ClassPathXmlApplicationContext(getConfig()); |
||||
cs = ctx.getBean("service", CacheableService.class); |
||||
ccs = ctx.getBean("classService", CacheableService.class); |
||||
} |
||||
|
||||
public void testCacheable(CacheableService service) throws Exception { |
||||
Object o1 = new Object(); |
||||
Object o2 = new Object(); |
||||
|
||||
Object r1 = service.cache(o1); |
||||
Object r2 = service.cache(o1); |
||||
Object r3 = service.cache(o1); |
||||
|
||||
assertSame(r1, r2); |
||||
assertSame(r1, r3); |
||||
} |
||||
|
||||
public void testInvalidate(CacheableService service) throws Exception { |
||||
Object o1 = new Object(); |
||||
Object o2 = new Object(); |
||||
|
||||
Object r1 = service.cache(o1); |
||||
Object r2 = service.cache(o1); |
||||
|
||||
assertSame(r1, r2); |
||||
service.invalidate(o1); |
||||
Object r3 = service.cache(o1); |
||||
Object r4 = service.cache(o1); |
||||
assertNotSame(r1, r3); |
||||
assertSame(r3, r4); |
||||
} |
||||
|
||||
public void testConditionalExpression(CacheableService service) throws Exception { |
||||
Object r1 = service.conditional(4); |
||||
Object r2 = service.conditional(4); |
||||
|
||||
assertNotSame(r1, r2); |
||||
|
||||
Object r3 = service.conditional(3); |
||||
Object r4 = service.conditional(3); |
||||
|
||||
assertSame(r3, r4); |
||||
} |
||||
|
||||
public void testKeyExpression(CacheableService service) throws Exception { |
||||
Object r1 = service.key(5, 1); |
||||
Object r2 = service.key(5, 2); |
||||
|
||||
assertSame(r1, r2); |
||||
|
||||
Object r3 = service.key(1, 5); |
||||
Object r4 = service.key(2, 5); |
||||
|
||||
assertNotSame(r3, r4); |
||||
} |
||||
|
||||
@Test |
||||
public void testCacheable() throws Exception { |
||||
testCacheable(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testInvalidate() throws Exception { |
||||
testInvalidate(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testConditionalExpression() throws Exception { |
||||
testConditionalExpression(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testKeyExpression() throws Exception { |
||||
testKeyExpression(cs); |
||||
} |
||||
|
||||
@Test |
||||
public void testClassCacheCacheable() throws Exception { |
||||
testCacheable(ccs); |
||||
} |
||||
|
||||
@Test |
||||
public void testClassCacheInvalidate() throws Exception { |
||||
testInvalidate(ccs); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
/* |
||||
* 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 java.util.concurrent.atomic.AtomicLong; |
||||
|
||||
import org.springframework.cache.annotation.CacheEvict; |
||||
import org.springframework.cache.annotation.Cacheable; |
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
*/ |
||||
@Cacheable |
||||
public class AnnotatedClassCacheableService implements CacheableService { |
||||
|
||||
private AtomicLong counter = new AtomicLong(); |
||||
|
||||
public Object cache(Object arg1) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
|
||||
public Object conditional(int field) { |
||||
return null; |
||||
} |
||||
|
||||
@CacheEvict |
||||
public void invalidate(Object arg1) { |
||||
} |
||||
|
||||
@Cacheable(key = "#p0") |
||||
public Object key(Object arg1, Object arg2) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
} |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
*/ |
||||
public class AnnotationNamespaceDrivenTest extends AbstractAnnotationTest { |
||||
|
||||
@Override |
||||
protected String getConfig() { |
||||
return "/org/springframework/cache/config/annotationDrivenCacheNamespace.xml"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
*/ |
||||
public class AnnotationTest extends AbstractAnnotationTest { |
||||
|
||||
@Override |
||||
protected String getConfig() { |
||||
return "/org/springframework/cache/config/annotationDrivenCacheConfig.xml"; |
||||
} |
||||
} |
||||
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
/** |
||||
* Basic service interface. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public interface CacheableService<T> { |
||||
|
||||
T cache(Object arg1); |
||||
|
||||
void invalidate(Object arg1); |
||||
|
||||
T conditional(int field); |
||||
|
||||
T key(Object arg1, Object arg2); |
||||
|
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* 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 java.util.concurrent.atomic.AtomicLong; |
||||
|
||||
import org.springframework.cache.annotation.CacheEvict; |
||||
import org.springframework.cache.annotation.Cacheable; |
||||
|
||||
|
||||
/** |
||||
* Simple cacheable service |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public class DefaultCacheableService implements CacheableService<Long> { |
||||
|
||||
private AtomicLong counter = new AtomicLong(); |
||||
|
||||
@Cacheable |
||||
public Long cache(Object arg1) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
|
||||
@CacheEvict |
||||
public void invalidate(Object arg1) { |
||||
} |
||||
|
||||
@Cacheable(condition = "#classField == 3") |
||||
public Long conditional(int classField) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
|
||||
@Cacheable(key = "#p0") |
||||
public Long key(Object arg1, Object arg2) { |
||||
return counter.getAndIncrement(); |
||||
} |
||||
} |
||||
@ -0,0 +1,44 @@
@@ -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.ehcache; |
||||
|
||||
import net.sf.ehcache.Ehcache; |
||||
|
||||
import org.springframework.cache.Cache; |
||||
import org.springframework.cache.vendor.AbstractNativeCacheTest; |
||||
|
||||
/** |
||||
* Integration test for EhCache cache. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public class EhCacheCacheTest extends AbstractNativeCacheTest<Ehcache> { |
||||
|
||||
@Override |
||||
protected Ehcache createNativeCache() throws Exception { |
||||
EhCacheFactoryBean fb = new EhCacheFactoryBean(); |
||||
fb.setBeanName(CACHE_NAME); |
||||
fb.setCacheName(CACHE_NAME); |
||||
fb.afterPropertiesSet(); |
||||
return fb.getObject(); |
||||
} |
||||
|
||||
@Override |
||||
protected Cache createCache(Ehcache nativeCache) { |
||||
return new EhCacheCache(nativeCache); |
||||
} |
||||
} |
||||
@ -0,0 +1,155 @@
@@ -0,0 +1,155 @@
|
||||
/* |
||||
* 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.vendor; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNull; |
||||
import static org.junit.Assert.assertSame; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.cache.Cache; |
||||
|
||||
/** |
||||
* Test for native cache implementations. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
public abstract class AbstractNativeCacheTest<T> { |
||||
|
||||
private T nativeCache; |
||||
private Cache cache; |
||||
protected final static String CACHE_NAME = "testCache"; |
||||
|
||||
@Before |
||||
public void setUp() throws Exception { |
||||
nativeCache = createNativeCache(); |
||||
cache = createCache(nativeCache); |
||||
cache.clear(); |
||||
} |
||||
|
||||
|
||||
protected abstract T createNativeCache() throws Exception; |
||||
|
||||
protected abstract Cache createCache(T nativeCache); |
||||
|
||||
|
||||
@Test |
||||
public void testCacheName() throws Exception { |
||||
assertEquals(CACHE_NAME, cache.getName()); |
||||
} |
||||
|
||||
@Test |
||||
public void testNativeCache() throws Exception { |
||||
assertSame(nativeCache, cache.getNativeCache()); |
||||
} |
||||
|
||||
@Test |
||||
public void testCachePut() throws Exception { |
||||
|
||||
Object key = "enescu"; |
||||
Object value = "george"; |
||||
|
||||
assertNull(cache.get(key)); |
||||
cache.put(key, value); |
||||
assertEquals(value, cache.get(key)); |
||||
} |
||||
|
||||
@Test |
||||
public void testCacheRemove() throws Exception { |
||||
Object key = "enescu"; |
||||
Object value = "george"; |
||||
|
||||
assertNull(cache.get(key)); |
||||
cache.put(key, value); |
||||
assertEquals(value, cache.remove(key)); |
||||
assertNull(cache.get(key)); |
||||
} |
||||
|
||||
@Test |
||||
public void testCacheClear() throws Exception { |
||||
assertNull(cache.get("enescu")); |
||||
cache.put("enescu", "george"); |
||||
assertNull(cache.get("vlaicu")); |
||||
cache.put("vlaicu", "aurel"); |
||||
cache.clear(); |
||||
assertNull(cache.get("vlaicu")); |
||||
assertNull(cache.get("enescu")); |
||||
} |
||||
|
||||
// concurrent map tests
|
||||
@Test |
||||
public void testPutIfAbsent() throws Exception { |
||||
Object key = "enescu"; |
||||
Object value1 = "george"; |
||||
Object value2 = "geo"; |
||||
|
||||
assertNull(cache.get("enescu")); |
||||
cache.put(key, value1); |
||||
cache.putIfAbsent(key, value2); |
||||
assertEquals(value1, cache.get(key)); |
||||
} |
||||
|
||||
@Test |
||||
public void testConcurrentRemove() throws Exception { |
||||
Object key = "enescu"; |
||||
Object value1 = "george"; |
||||
Object value2 = "geo"; |
||||
|
||||
assertNull(cache.get("enescu")); |
||||
cache.put(key, value1); |
||||
// no remove
|
||||
cache.remove(key, value2); |
||||
assertEquals(value1, cache.get(key)); |
||||
// one remove
|
||||
cache.remove(key, value1); |
||||
assertNull(cache.get("enescu")); |
||||
} |
||||
|
||||
@Test |
||||
public void testConcurrentReplace() throws Exception { |
||||
Object key = "enescu"; |
||||
Object value1 = "george"; |
||||
Object value2 = "geo"; |
||||
|
||||
assertNull(cache.get("enescu")); |
||||
cache.put(key, value1); |
||||
cache.replace(key, value2); |
||||
assertEquals(value2, cache.get(key)); |
||||
cache.remove(key); |
||||
cache.replace(key, value1); |
||||
assertNull(cache.get("enescu")); |
||||
} |
||||
|
||||
@Test |
||||
public void testConcurrentReplaceIfEqual() throws Exception { |
||||
Object key = "enescu"; |
||||
Object value1 = "george"; |
||||
Object value2 = "geo"; |
||||
|
||||
assertNull(cache.get("enescu")); |
||||
cache.put(key, value1); |
||||
assertEquals(value1, cache.get(key)); |
||||
// no replace
|
||||
cache.replace(key, value2, value1); |
||||
assertEquals(value1, cache.get(key)); |
||||
cache.replace(key, value1, value2); |
||||
assertEquals(value2, cache.get(key)); |
||||
cache.replace(key, value2, value1); |
||||
assertEquals(value1, cache.get(key)); |
||||
} |
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:aop="http://www.springframework.org/schema/aop" |
||||
xmlns:tx="http://www.springframework.org/schema/tx" |
||||
xmlns:p="http://www.springframework.org/schema/p" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd |
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> |
||||
|
||||
<bean id="apc" class="org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator"/> |
||||
|
||||
<bean id="annotationSource" class="org.springframework.cache.annotation.AnnotationCacheDefinitionSource"/> |
||||
|
||||
<aop:config> |
||||
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/> |
||||
</aop:config> |
||||
|
||||
<bean id="cacheInterceptor" class="org.springframework.cache.interceptor.CacheInterceptor"> |
||||
<property name="cacheManager" ref="cacheManager"/> |
||||
<property name="cacheDefinitionSources" ref="annotationSource"/> |
||||
</bean> |
||||
|
||||
<bean id="advisor" class="org.springframework.cache.interceptor.BeanFactoryCacheDefinitionSourceAdvisor"> |
||||
<property name="cacheDefinitionSource" ref="annotationSource"/> |
||||
<property name="adviceBeanName" value="cacheInterceptor"/> |
||||
</bean> |
||||
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.MapCacheManager"> |
||||
<property name="caches"> |
||||
<set> |
||||
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> |
||||
</set> |
||||
</property> |
||||
</bean> |
||||
|
||||
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/> |
||||
|
||||
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/> |
||||
|
||||
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/> |
||||
|
||||
</beans> |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:aop="http://www.springframework.org/schema/aop" |
||||
xmlns:cache="http://www.springframework.org/schema/cache" |
||||
xmlns:p="http://www.springframework.org/schema/p" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd |
||||
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> |
||||
|
||||
<cache:annotation-driven proxy-target-class="false" order="0"/> |
||||
|
||||
<aop:config> |
||||
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/> |
||||
</aop:config> |
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.MapCacheManager"> |
||||
<property name="caches"> |
||||
<set> |
||||
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> |
||||
</set> |
||||
</property> |
||||
</bean> |
||||
|
||||
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/> |
||||
|
||||
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/> |
||||
|
||||
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/> |
||||
|
||||
</beans> |
||||
Loading…
Reference in new issue