From 63a217a40a7cdf9e8ebf73dfd3887a5797fe4998 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 16 Aug 2011 17:35:01 +0000 Subject: [PATCH] + add XML support for cache abstraction (cache-advice) - DRAFT --- ...tationDrivenCacheBeanDefinitionParser.java | 15 +- .../cache/config/CacheAdviceParser.java | 171 ++++++++++++++++++ .../cache/config/CacheNamespaceHandler.java | 11 ++ .../AbstractFallbackCacheOperationSource.java | 5 - .../cache/interceptor/CacheAspectSupport.java | 7 +- .../interceptor/CacheOperationEditor.java | 84 +++++++++ .../interceptor/CacheProxyFactoryBean.java | 56 ++++++ .../NameMatchCacheOperationSource.java | 148 +++++++++++++++ .../cache/config/spring-cache-3.1.xsd | 108 +++++++++++ .../cache/config/AbstractAnnotationTests.java | 2 +- .../AnnotatedClassCacheableService.java | 2 +- .../config/CacheAdviceNamespaceTests.java | 30 +++ .../cache/config/CacheableService.java | 2 +- .../cache/config/DefaultCacheableService.java | 2 +- .../cache/config/cache-advice.xml | 59 ++++++ 15 files changed, 676 insertions(+), 26 deletions(-) create mode 100644 org.springframework.context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java create mode 100644 org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheOperationEditor.java create mode 100644 org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java create mode 100644 org.springframework.context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java create mode 100644 org.springframework.context/src/test/java/org/springframework/cache/config/CacheAdviceNamespaceTests.java create mode 100644 org.springframework.context/src/test/resources/org/springframework/cache/config/cache-advice.xml diff --git a/org.springframework.context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java index 27e05211f20..49b2bf4829f 100644 --- a/org.springframework.context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java +++ b/org.springframework.context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java @@ -48,11 +48,6 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*; */ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser { - private static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager"; - - private static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager"; - - /** * Parses the '<cache:annotation-driven/>' tag. Will * {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator} @@ -71,13 +66,9 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser 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))); + def.getPropertyValues().add("cacheManager", + new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element))); } /** @@ -124,7 +115,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser interceptorDef.setSource(eleSource); interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registerCacheManagerProperty(element, interceptorDef); - interceptorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName)); + interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName)); String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef); // Create the CacheAdvisor definition. diff --git a/org.springframework.context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java b/org.springframework.context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java new file mode 100644 index 00000000000..8a5bad13d60 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java @@ -0,0 +1,171 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cache.config; + +import java.util.List; + +import org.springframework.beans.factory.config.TypedStringValue; +import org.springframework.beans.factory.parsing.ReaderContext; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.cache.annotation.AnnotationCacheOperationSource; +import org.springframework.cache.interceptor.CacheEvictOperation; +import org.springframework.cache.interceptor.CacheInterceptor; +import org.springframework.cache.interceptor.CacheOperation; +import org.springframework.cache.interceptor.CacheUpdateOperation; +import org.springframework.cache.interceptor.NameMatchCacheOperationSource; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * {@link org.springframework.beans.factory.xml.BeanDefinitionParser + * BeanDefinitionParser} for the {@code } tag. + * + * @author Costin Leau + * + */ +class CacheAdviceParser extends AbstractSingleBeanDefinitionParser { + + /** + * Simple, reusable class used for overriding defaults. + * + * @author Costin Leau + */ + private static class Props { + + private String key, condition; + private String[] caches = null; + + Props(Element root) { + String defaultCache = root.getAttribute("cache"); + key = root.getAttribute("key"); + condition = root.getAttribute("condition"); + + if (StringUtils.hasText(defaultCache)) { + caches = StringUtils.commaDelimitedListToStringArray(defaultCache.trim()); + } + } + + CacheOperation merge(Element element, ReaderContext readerCtx, CacheOperation op) { + String cache = element.getAttribute("cache"); + String k = element.getAttribute("key"); + String c = element.getAttribute("condition"); + + String[] localCaches = caches; + String localKey = key, localCondition = condition; + + // sanity check + if (StringUtils.hasText(cache)) { + localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim()); + } else { + if (caches == null) { + readerCtx.error("No cache specified specified for " + element.getNodeName(), element); + } + } + + if (StringUtils.hasText(k)) { + localKey = k.trim(); + } + + if (StringUtils.hasText(c)) { + localCondition = c.trim(); + } + op.setCacheNames(localCaches); + op.setKey(localKey); + op.setCondition(localCondition); + + return op; + } + } + + private static final String CACHEABLE_ELEMENT = "cacheable"; + private static final String CACHE_EVICT_ELEMENT = "cache-evict"; + private static final String METHOD_ATTRIBUTE = "method"; + private static final String DEFS_ELEMENT = "definitions"; + + @Override + protected Class getBeanClass(Element element) { + return CacheInterceptor.class; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + builder.addPropertyReference("cacheManager", CacheNamespaceHandler.extractCacheManager(element)); + + List cacheDefs = DomUtils.getChildElementsByTagName(element, DEFS_ELEMENT); + if (cacheDefs.size() >= 1) { + // Using attributes source. + List attributeSourceDefinitions = parseDefinitionsSources(cacheDefs, parserContext); + builder.addPropertyValue("cacheOperationSources", attributeSourceDefinitions); + } else { + // Assume annotations source. + builder.addPropertyValue("cacheOperationSources", new RootBeanDefinition( + AnnotationCacheOperationSource.class)); + } + } + + private List parseDefinitionsSources(List definitions, ParserContext parserContext) { + ManagedList defs = new ManagedList(definitions.size()); + + // extract default param for the definition + for (Element element : definitions) { + defs.add(parseDefinitionSource(element, parserContext)); + } + + return defs; + } + + private RootBeanDefinition parseDefinitionSource(Element definition, ParserContext parserContext) { + Props prop = new Props(definition); + // add cacheable first + + ManagedMap cacheOpeMap = new ManagedMap(); + cacheOpeMap.setSource(parserContext.extractSource(definition)); + + List updateCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHEABLE_ELEMENT); + + for (Element opElement : updateCacheMethods) { + String name = opElement.getAttribute(METHOD_ATTRIBUTE); + TypedStringValue nameHolder = new TypedStringValue(name); + nameHolder.setSource(parserContext.extractSource(opElement)); + CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheUpdateOperation()); + + cacheOpeMap.put(nameHolder, op); + } + + List evictCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_EVICT_ELEMENT); + + for (Element opElement : evictCacheMethods) { + String name = opElement.getAttribute(METHOD_ATTRIBUTE); + TypedStringValue nameHolder = new TypedStringValue(name); + nameHolder.setSource(parserContext.extractSource(opElement)); + CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheEvictOperation()); + + cacheOpeMap.put(nameHolder, op); + } + + RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchCacheOperationSource.class); + attributeSourceDefinition.setSource(parserContext.extractSource(definition)); + attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpeMap); + return attributeSourceDefinition; + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java b/org.springframework.context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java index 604a9eeb3b1..29e7f096e61 100644 --- a/org.springframework.context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java +++ b/org.springframework.context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java @@ -17,6 +17,7 @@ package org.springframework.cache.config; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; +import org.w3c.dom.Element; /** * NamespaceHandler allowing for the configuration of @@ -30,8 +31,18 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport; */ public class CacheNamespaceHandler extends NamespaceHandlerSupport { + static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager"; + static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager"; + + static String extractCacheManager(Element element) { + return (element.hasAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE) ? element + .getAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE) + : CacheNamespaceHandler.DEFAULT_CACHE_MANAGER_BEAN_NAME); + } + public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser()); + registerBeanDefinitionParser("advice", new CacheAdviceParser()); } } diff --git a/org.springframework.context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java index 71b9f167c78..5387f8ba832 100644 --- a/org.springframework.context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java +++ b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java @@ -117,11 +117,6 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera 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 CacheOperation computeCacheOperationDefinition(Method method, Class targetClass) { // Don't allow no-public methods as required. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { diff --git a/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java index 758fcb2ba1d..0a057e59aab 100644 --- a/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java +++ b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java @@ -44,12 +44,8 @@ import org.springframework.util.StringUtils; * *

Subclasses are responsible for calling methods in this class in the correct order. * - *

If no caching name has been specified in the CacheOperationDefinition, - * the exposed name will be the fully-qualified class name + "." + method name - * (by default). - * *

Uses the Strategy design pattern. A CacheManager - * implementation will perform the actual transaction management, and a + * implementation will perform the actual cache management, and a * CacheDefinitionSource is used for determining caching operation definitions. * *

A cache aspect is serializable if its CacheManager @@ -155,6 +151,7 @@ public abstract class CacheAspectSupport implements InitializingBean { return ClassUtils.getQualifiedMethodName(specificMethod); } + protected Collection getCaches(CacheOperation operation) { Set cacheNames = operation.getCacheNames(); Collection caches = new ArrayList(cacheNames.size()); diff --git a/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheOperationEditor.java b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheOperationEditor.java new file mode 100644 index 00000000000..fb6b67dbf1c --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheOperationEditor.java @@ -0,0 +1,84 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cache.interceptor; + +import java.beans.PropertyEditorSupport; + +import org.springframework.util.StringUtils; + +/** + * PropertyEditor for {@link CacheOperation} objects. Accepts a String of form + *

action,cache,key,condition + *

where only action and cache are required. Available definitions for action are + * cacheable and evict. + * When specifying multiple caches, use ; as a separator + * + * A typical example would be: + *

cacheable, orders;books, #p0 + * + *

The tokens need to be specified in the order above. + * + * @author Costin Leau + * + * @see org.springframework.transaction.TransactionAttributeEditor + * @see org.springframework.core.Constants + */ +public class CacheOperationEditor extends PropertyEditorSupport { + + /** + * Format is action, cache, key, condition. + * Null or the empty string means that the method is non cacheable. + * @see java.beans.PropertyEditor#setAsText(java.lang.String) + */ + @Override + public void setAsText(String text) throws IllegalArgumentException { + if (StringUtils.hasLength(text)) { + // tokenize it with "," + String[] tokens = StringUtils.commaDelimitedListToStringArray(text); + if (tokens.length < 2) { + throw new IllegalArgumentException( + "too little arguments found, at least the cache action and cache name are required"); + } + + CacheOperation op; + + if ("cacheable".contains(tokens[0])) { + op = new CacheUpdateOperation(); + } + + else if ("evict".contains(tokens[0])) { + op = new CacheEvictOperation(); + } else { + throw new IllegalArgumentException("Invalid cache action specified " + tokens[0]); + } + + op.setCacheNames(StringUtils.delimitedListToStringArray(tokens[1], ";")); + + if (tokens.length > 2) { + op.setKey(tokens[2]); + } + + if (tokens.length > 3) { + op.setCondition(tokens[3]); + } + + setValue(op); + } else { + setValue(null); + } + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java new file mode 100644 index 00000000000..acfca0c2b72 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cache.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. + * + *

This class is intended to cover the typical case of declarative + * cache 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(CacheOperationSource... cacheDefinitionSources) { + this.cachingInterceptor.setCacheOperationSources(cacheDefinitionSources); + } + +} diff --git a/org.springframework.context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java new file mode 100644 index 00000000000..6c4df9fff89 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java @@ -0,0 +1,148 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cache.interceptor; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.ObjectUtils; +import org.springframework.util.PatternMatchUtils; + +/** + * Simple {@link CacheOperationSource} implementation that + * allows attributes to be matched by registered name. + * + * @author Costin Leau + */ +public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable { + + /** + * Logger available to subclasses. + *

Static for optimal serialization. + */ + protected static final Log logger = LogFactory.getLog(NameMatchCacheOperationSource.class); + + /** Keys are method names; values are TransactionAttributes */ + private Map nameMap = new LinkedHashMap(); + + /** + * Set a name/attribute map, consisting of method names + * (e.g. "myMethod") and CacheOperation instances + * (or Strings to be converted to CacheOperation instances). + * @see CacheOperation + * @see CacheOperationEditor + */ + public void setNameMap(Map nameMap) { + for (Map.Entry entry : nameMap.entrySet()) { + addCacheMethod(entry.getKey(), entry.getValue()); + } + } + + /** + * Parses the given properties into a name/attribute map. + * Expects method names as keys and String attributes definitions as values, + * parsable into CacheOperation instances via CacheOperationEditor. + * @see #setNameMap + * @see CacheOperationEditor + */ + public void setProperties(Properties cacheOperations) { + CacheOperationEditor tae = new CacheOperationEditor(); + Enumeration propNames = cacheOperations.propertyNames(); + while (propNames.hasMoreElements()) { + String methodName = (String) propNames.nextElement(); + String value = cacheOperations.getProperty(methodName); + tae.setAsText(value); + CacheOperation op = (CacheOperation) tae.getValue(); + addCacheMethod(methodName, op); + } + } + + /** + * Add an attribute for a cacheable method. + *

Method names can be exact matches, or of the pattern "xxx*", + * "*xxx" or "*xxx*" for matching multiple methods. + * @param methodName the name of the method + * @param operation operation associated with the method + */ + public void addCacheMethod(String methodName, CacheOperation operation) { + if (logger.isDebugEnabled()) { + logger.debug("Adding method [" + methodName + "] with cache operation [" + operation + "]"); + } + this.nameMap.put(methodName, operation); + } + + public CacheOperation getCacheOperation(Method method, Class targetClass) { + // look for direct name match + String methodName = method.getName(); + CacheOperation attr = this.nameMap.get(methodName); + + if (attr == null) { + // Look for most specific name match. + String bestNameMatch = null; + for (String mappedName : this.nameMap.keySet()) { + if (isMatch(methodName, mappedName) + && (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) { + attr = this.nameMap.get(mappedName); + bestNameMatch = mappedName; + } + } + } + + return attr; + } + + /** + * Return if the given method name matches the mapped name. + *

The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, + * as well as direct equality. Can be overridden in subclasses. + * @param methodName the method name of the class + * @param mappedName the name in the descriptor + * @return if the names match + * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) + */ + protected boolean isMatch(String methodName, String mappedName) { + return PatternMatchUtils.simpleMatch(mappedName, methodName); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof NameMatchCacheOperationSource)) { + return false; + } + NameMatchCacheOperationSource otherTas = (NameMatchCacheOperationSource) other; + return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap); + } + + @Override + public int hashCode() { + return NameMatchCacheOperationSource.class.hashCode(); + } + + @Override + public String toString() { + return getClass().getName() + ": " + this.nameMap; + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/resources/org/springframework/cache/config/spring-cache-3.1.xsd b/org.springframework.context/src/main/resources/org/springframework/cache/config/spring-cache-3.1.xsd index 6483791f7a5..f1185f165ba 100644 --- a/org.springframework.context/src/main/resources/org/springframework/cache/config/spring-cache-3.1.xsd +++ b/org.springframework.context/src/main/resources/org/springframework/cache/config/spring-cache-3.1.xsd @@ -89,4 +89,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.springframework.context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java b/org.springframework.context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java index 319ae286b1c..933efb8549f 100644 --- a/org.springframework.context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java +++ b/org.springframework.context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java @@ -86,7 +86,7 @@ public abstract class AbstractAnnotationTests { Object r2 = service.cache(o1); assertSame(r1, r2); - service.invalidate(o1, null); + service.evict(o1, null); Object r3 = service.cache(o1); Object r4 = service.cache(o1); assertNotSame(r1, r3); diff --git a/org.springframework.context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/org.springframework.context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java index ac851832e81..adacf9efd38 100644 --- a/org.springframework.context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java +++ b/org.springframework.context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java @@ -43,7 +43,7 @@ public class AnnotatedClassCacheableService implements CacheableService { } @CacheEvict(value = "default", key = "#p0") - public void invalidate(Object arg1, Object arg2) { + public void evict(Object arg1, Object arg2) { } @Cacheable(value = "default", key = "#p0") diff --git a/org.springframework.context/src/test/java/org/springframework/cache/config/CacheAdviceNamespaceTests.java b/org.springframework.context/src/test/java/org/springframework/cache/config/CacheAdviceNamespaceTests.java new file mode 100644 index 00000000000..f45c83ce33c --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/cache/config/CacheAdviceNamespaceTests.java @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cache.config; + + +/** + * @author Costin Leau + */ +public class CacheAdviceNamespaceTests extends AbstractAnnotationTests { + + + @Override + protected String getConfig() { + return "/org/springframework/cache/config/cache-advice.xml"; + } +} diff --git a/org.springframework.context/src/test/java/org/springframework/cache/config/CacheableService.java b/org.springframework.context/src/test/java/org/springframework/cache/config/CacheableService.java index 707ff972491..a6f3702ead6 100644 --- a/org.springframework.context/src/test/java/org/springframework/cache/config/CacheableService.java +++ b/org.springframework.context/src/test/java/org/springframework/cache/config/CacheableService.java @@ -28,7 +28,7 @@ public interface CacheableService { void invalidate(Object arg1); - void invalidate(Object arg1, Object arg2); + void evict(Object arg1, Object arg2); T conditional(int field); diff --git a/org.springframework.context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/org.springframework.context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java index 3e9db797102..057806358a3 100644 --- a/org.springframework.context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java +++ b/org.springframework.context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java @@ -41,7 +41,7 @@ public class DefaultCacheableService implements CacheableService { } @CacheEvict(value = "default", key = "#p0") - public void invalidate(Object arg1, Object arg2) { + public void evict(Object arg1, Object arg2) { } @Cacheable(value = "default", condition = "#classField == 3") diff --git a/org.springframework.context/src/test/resources/org/springframework/cache/config/cache-advice.xml b/org.springframework.context/src/test/resources/org/springframework/cache/config/cache-advice.xml new file mode 100644 index 00000000000..e0e3f4d92cc --- /dev/null +++ b/org.springframework.context/src/test/resources/org/springframework/cache/config/cache-advice.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +