1 16 17 package org.springframework.transaction.interceptor; 18 19 import java.lang.reflect.Method ; 20 import java.lang.reflect.Modifier ; 21 import java.util.Collection ; 22 import java.util.HashMap ; 23 import java.util.Iterator ; 24 import java.util.LinkedList ; 25 import java.util.List ; 26 import java.util.Map ; 27 28 import org.apache.commons.logging.Log; 29 import org.apache.commons.logging.LogFactory; 30 31 import org.springframework.aop.support.AopUtils; 32 import org.springframework.util.ObjectUtils; 33 34 54 public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource { 55 56 60 private final static Object NULL_TRANSACTION_ATTRIBUTE = new Object (); 61 62 63 68 protected final Log logger = LogFactory.getLog(getClass()); 69 70 75 final Map attributeCache = new HashMap (); 76 77 78 86 public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) { 87 Object cacheKey = getCacheKey(method, targetClass); 89 synchronized (this.attributeCache) { 90 Object cached = this.attributeCache.get(cacheKey); 91 if (cached != null) { 92 if (cached == NULL_TRANSACTION_ATTRIBUTE) { 95 return null; 96 } 97 else { 98 return (TransactionAttribute) cached; 99 } 100 } 101 else { 102 TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass); 104 if (txAtt == null) { 106 this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); 107 } 108 else { 109 if (logger.isDebugEnabled()) { 110 logger.debug("Adding transactional method [" + method.getName() + "] with attribute [" + txAtt + "]"); 111 } 112 this.attributeCache.put(cacheKey, txAtt); 113 } 114 return txAtt; 115 } 116 } 117 } 118 119 127 protected Object getCacheKey(Method method, Class targetClass) { 128 return new DefaultCacheKey(method, targetClass); 129 } 130 131 136 private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) { 137 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { 139 return null; 140 } 141 142 Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); 145 146 TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod)); 148 if (txAtt != null) { 149 return txAtt; 150 } 151 152 txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass())); 154 if (txAtt != null) { 155 return txAtt; 156 } 157 158 if (specificMethod != method) { 159 txAtt = findTransactionAttribute(findAllAttributes(method)); 161 if (txAtt != null) { 162 return txAtt; 163 } 164 return findTransactionAttribute(findAllAttributes(method.getDeclaringClass())); 166 } 167 return null; 168 } 169 170 171 177 protected abstract Collection findAllAttributes(Method method); 178 179 184 protected abstract Collection findAllAttributes(Class clazz); 185 186 187 199 protected TransactionAttribute findTransactionAttribute(Collection atts) { 200 if (atts == null) { 201 return null; 202 } 203 204 TransactionAttribute txAttribute = null; 205 206 for (Iterator itr = atts.iterator(); itr.hasNext() && txAttribute == null; ) { 208 Object att = itr.next(); 209 if (att instanceof TransactionAttribute) { 210 txAttribute = (TransactionAttribute) att; 211 } 212 } 213 214 if (txAttribute instanceof RuleBasedTransactionAttribute) { 216 RuleBasedTransactionAttribute rbta = (RuleBasedTransactionAttribute) txAttribute; 217 List rollbackRules = new LinkedList (); 219 for (Iterator it = atts.iterator(); it.hasNext(); ) { 220 Object att = it.next(); 221 if (att instanceof RollbackRuleAttribute) { 222 if (logger.isDebugEnabled()) { 223 logger.debug("Found rollback rule: " + att); 224 } 225 rollbackRules.add(att); 226 } 227 } 228 rbta.setRollbackRules(rollbackRules); 230 } 231 232 return txAttribute; 233 } 234 235 239 protected boolean allowPublicMethodsOnly() { 240 return false; 241 } 242 243 244 247 private static class DefaultCacheKey { 248 249 private final Method method; 250 251 private final Class targetClass; 252 253 public DefaultCacheKey(Method method, Class targetClass) { 254 this.method = method; 255 this.targetClass = targetClass; 256 } 257 258 public boolean equals(Object other) { 259 if (this == other) { 260 return true; 261 } 262 if (!(other instanceof DefaultCacheKey)) { 263 return false; 264 } 265 DefaultCacheKey otherKey = (DefaultCacheKey) other; 266 return (this.method.equals(otherKey.method) && 267 ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass)); 268 } 269 270 public int hashCode() { 271 return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0); 272 } 273 } 274 275 } 276 | Popular Tags |