001/* 002 * Copyright 2002-2012 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.cache.interceptor; 018 019import java.io.Serializable; 020import java.lang.reflect.Method; 021import java.util.Collection; 022import java.util.LinkedHashMap; 023import java.util.Map; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027 028import org.springframework.util.ObjectUtils; 029import org.springframework.util.PatternMatchUtils; 030 031/** 032 * Simple {@link CacheOperationSource} implementation that allows attributes to be matched 033 * by registered name. 034 * 035 * @author Costin Leau 036 * @since 3.1 037 */ 038@SuppressWarnings("serial") 039public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable { 040 041 /** 042 * Logger available to subclasses. 043 * <p>Static for optimal serialization. 044 */ 045 protected static final Log logger = LogFactory.getLog(NameMatchCacheOperationSource.class); 046 047 048 /** Keys are method names; values are TransactionAttributes */ 049 private Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<String, Collection<CacheOperation>>(); 050 051 052 /** 053 * Set a name/attribute map, consisting of method names 054 * (e.g. "myMethod") and CacheOperation instances 055 * (or Strings to be converted to CacheOperation instances). 056 * @see CacheOperation 057 */ 058 public void setNameMap(Map<String, Collection<CacheOperation>> nameMap) { 059 for (Map.Entry<String, Collection<CacheOperation>> entry : nameMap.entrySet()) { 060 addCacheMethod(entry.getKey(), entry.getValue()); 061 } 062 } 063 064 /** 065 * Add an attribute for a cacheable method. 066 * <p>Method names can be exact matches, or of the pattern "xxx*", 067 * "*xxx" or "*xxx*" for matching multiple methods. 068 * @param methodName the name of the method 069 * @param ops operation associated with the method 070 */ 071 public void addCacheMethod(String methodName, Collection<CacheOperation> ops) { 072 if (logger.isDebugEnabled()) { 073 logger.debug("Adding method [" + methodName + "] with cache operations [" + ops + "]"); 074 } 075 this.nameMap.put(methodName, ops); 076 } 077 078 @Override 079 public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) { 080 // look for direct name match 081 String methodName = method.getName(); 082 Collection<CacheOperation> ops = this.nameMap.get(methodName); 083 084 if (ops == null) { 085 // Look for most specific name match. 086 String bestNameMatch = null; 087 for (String mappedName : this.nameMap.keySet()) { 088 if (isMatch(methodName, mappedName) 089 && (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) { 090 ops = this.nameMap.get(mappedName); 091 bestNameMatch = mappedName; 092 } 093 } 094 } 095 096 return ops; 097 } 098 099 /** 100 * Return if the given method name matches the mapped name. 101 * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, 102 * as well as direct equality. Can be overridden in subclasses. 103 * @param methodName the method name of the class 104 * @param mappedName the name in the descriptor 105 * @return if the names match 106 * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) 107 */ 108 protected boolean isMatch(String methodName, String mappedName) { 109 return PatternMatchUtils.simpleMatch(mappedName, methodName); 110 } 111 112 @Override 113 public boolean equals(Object other) { 114 if (this == other) { 115 return true; 116 } 117 if (!(other instanceof NameMatchCacheOperationSource)) { 118 return false; 119 } 120 NameMatchCacheOperationSource otherTas = (NameMatchCacheOperationSource) other; 121 return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap); 122 } 123 124 @Override 125 public int hashCode() { 126 return NameMatchCacheOperationSource.class.hashCode(); 127 } 128 129 @Override 130 public String toString() { 131 return getClass().getName() + ": " + this.nameMap; 132 } 133}