1 /* 2 * Copyright 2011 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.cache.interceptor; 18 19 import java.io.Serializable; 20 import java.lang.reflect.Method; 21 import java.util.Collection; 22 import java.util.LinkedHashMap; 23 import java.util.Map; 24 25 import org.apache.commons.logging.Log; 26 import org.apache.commons.logging.LogFactory; 27 import org.springframework.util.ObjectUtils; 28 import org.springframework.util.PatternMatchUtils; 29 30 /** 31 * Simple {@link CacheOperationSource} implementation that allows attributes to be matched 32 * by registered name. 33 * 34 * @author Costin Leau 35 */ 36 @SuppressWarnings("serial") 37 public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable { 38 39 /** 40 * Logger available to subclasses. 41 * <p>Static for optimal serialization. 42 */ 43 protected static final Log logger = LogFactory.getLog(NameMatchCacheOperationSource.class); 44 45 /** Keys are method names; values are TransactionAttributes */ 46 private Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<String, Collection<CacheOperation>>(); 47 48 /** 49 * Set a name/attribute map, consisting of method names 50 * (e.g. "myMethod") and CacheOperation instances 51 * (or Strings to be converted to CacheOperation instances). 52 * @see CacheOperation 53 * @see CacheOperationEditor 54 */ setNameMap(Map<String, Collection<CacheOperation>> nameMap)55 public void setNameMap(Map<String, Collection<CacheOperation>> nameMap) { 56 for (Map.Entry<String, Collection<CacheOperation>> entry : nameMap.entrySet()) { 57 addCacheMethod(entry.getKey(), entry.getValue()); 58 } 59 } 60 61 /** 62 * Add an attribute for a cacheable method. 63 * <p>Method names can be exact matches, or of the pattern "xxx*", 64 * "*xxx" or "*xxx*" for matching multiple methods. 65 * @param methodName the name of the method 66 * @param ops operation associated with the method 67 */ addCacheMethod(String methodName, Collection<CacheOperation> ops)68 public void addCacheMethod(String methodName, Collection<CacheOperation> ops) { 69 if (logger.isDebugEnabled()) { 70 logger.debug("Adding method [" + methodName + "] with cache operations [" + ops + "]"); 71 } 72 this.nameMap.put(methodName, ops); 73 } 74 getCacheOperations(Method method, Class<?> targetClass)75 public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) { 76 // look for direct name match 77 String methodName = method.getName(); 78 Collection<CacheOperation> ops = this.nameMap.get(methodName); 79 80 if (ops == null) { 81 // Look for most specific name match. 82 String bestNameMatch = null; 83 for (String mappedName : this.nameMap.keySet()) { 84 if (isMatch(methodName, mappedName) 85 && (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) { 86 ops = this.nameMap.get(mappedName); 87 bestNameMatch = mappedName; 88 } 89 } 90 } 91 92 return ops; 93 } 94 95 /** 96 * Return if the given method name matches the mapped name. 97 * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, 98 * as well as direct equality. Can be overridden in subclasses. 99 * @param methodName the method name of the class 100 * @param mappedName the name in the descriptor 101 * @return if the names match 102 * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) 103 */ isMatch(String methodName, String mappedName)104 protected boolean isMatch(String methodName, String mappedName) { 105 return PatternMatchUtils.simpleMatch(mappedName, methodName); 106 } 107 108 @Override equals(Object other)109 public boolean equals(Object other) { 110 if (this == other) { 111 return true; 112 } 113 if (!(other instanceof NameMatchCacheOperationSource)) { 114 return false; 115 } 116 NameMatchCacheOperationSource otherTas = (NameMatchCacheOperationSource) other; 117 return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap); 118 } 119 120 @Override hashCode()121 public int hashCode() { 122 return NameMatchCacheOperationSource.class.hashCode(); 123 } 124 125 @Override toString()126 public String toString() { 127 return getClass().getName() + ": " + this.nameMap; 128 } 129 } 130