AbstractFallbackTransactionAttributeSource.java
Upload User: jiancairen
Upload Date: 2007-08-27
Package Size: 26458k
Code Size: 7k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2002-2004 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. package org.springframework.transaction.interceptor;
  17. import java.lang.reflect.Method;
  18. import java.util.Collection;
  19. import java.util.HashMap;
  20. import java.util.Iterator;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.springframework.aop.support.AopUtils;
  26. import org.springframework.metadata.Attributes;
  27. /**
  28.  * Abstract implementation of TransactionAttributeSource that caches attributes
  29.  * for methods, and implements fallback policy of 1. most specific method; 
  30.  * 2. target class attribute; 3. declaring method; 4. declaring class.
  31.  *
  32.  * <p>Defaults to using class's transaction attribute if none is associated
  33.  * with the target method. Any transaction attribute associated with the
  34.  * target method completely overrides a class transaction attribute.
  35.  *
  36.  * <p>This implementation caches attributes by method after they are first used.
  37.  * If it's ever desirable to allow dynamic changing of transaction attributes
  38.  * (unlikely) caching could be made configurable. Caching is desirable because
  39.  * of the cost of evaluating rollback rules.
  40.  *
  41.  * @author Rod Johnson
  42.  */
  43. public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
  44. protected final Log logger = LogFactory.getLog(getClass());
  45. /**
  46.  * Canonical value held in cache to indicate no transaction attribute was
  47.  * found for this method, and we don't need to look again
  48.  */
  49. private final static Object NULL_TX_ATTRIBUTE = new Object();
  50. /**
  51.  * Cache of TransactionAttributes, keyed by Method and target class
  52.  */
  53. private HashMap cache = new HashMap();
  54. /**
  55.  * Return the transaction attribute for this method invocation.
  56.  * Defaults to the class's transaction attribute if no method
  57.  * attribute is found
  58.  * @param method method for the current invocation. Can't be null
  59.  * @param targetClass target class for this invocation. May be null.
  60.  * @return TransactionAttribute for this method, or null if the method is non-transactional
  61.  */
  62. public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
  63. // First, see if we have a cached value
  64. Object cacheKey = cacheKey(method, targetClass);
  65. Object cached = cache.get(cacheKey);
  66. if (cached != null) {
  67. // Value will either be canonical value indicating there is no transaction attribute,
  68. // or an actual transaction attribute
  69. if (cached == NULL_TX_ATTRIBUTE) {
  70. return null;
  71. }
  72. else {
  73. return (TransactionAttribute) cached;
  74. }
  75. }
  76. else {
  77. // We need to work it out
  78. TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
  79. // Put it in the cache
  80. if (txAtt == null) {
  81. cache.put(cacheKey, NULL_TX_ATTRIBUTE);
  82. }
  83. else {
  84. cache.put(cacheKey, txAtt);
  85. }
  86. return txAtt;
  87. }
  88. }
  89. private Object cacheKey(Method method, Class targetClass) {
  90. // Class may be null, method can't
  91. return targetClass + "" + System.identityHashCode(method);
  92. }
  93. /**
  94.  * Same return as getTransactionAttribute method, but doesn't cache the result.
  95.  * getTransactionAttribute is a caching decorator for this method.
  96.  */
  97. private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {
  98. // The method may be on an interface, but we need attributes from the target class.
  99. // The AopUtils class provides a convenience method for this. If the target class
  100. // is null, the method will be unchanged.
  101. Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
  102. // First try is the method in the target class
  103. TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));
  104. if (txAtt != null)
  105. return txAtt;
  106. // Second try is the transaction attribute on the target class
  107. txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));
  108. if (txAtt != null)
  109. return txAtt;
  110. if (specificMethod != method ) {
  111. // Fallback is to look at the original method
  112. txAtt = findTransactionAttribute(findAllAttributes(method));
  113. if (txAtt != null)
  114. return txAtt;
  115. // Last fallback is the class of the original method
  116. return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));
  117. }
  118. return null;
  119. }
  120. /**
  121.  * Subclasses should implement this to return all attributes for this method.
  122.  * We need all because of the need to analyze rollback rules.
  123.  * @param m method to retrieve attributes for
  124.  * @return all attributes associated with this method.
  125.  * May return null.
  126.  */
  127. protected abstract Collection findAllAttributes(Method m);
  128. /**
  129.  * Subclasses should implement this to return all attributes for this class.  
  130.  * @param clazz class to retrieve attributes for
  131.  * @return all attributes associated with this class.
  132.  * May return null.
  133.  */
  134. protected abstract Collection findAllAttributes(Class clazz);
  135. /**
  136.  * Return the transaction attribute, given this set of attributes
  137.  * attached to a method or class.
  138.  * Protected rather than private as subclasses may want to customize
  139.  * how this is done: for example, returning a TransactionAttribute
  140.  * affected by the values of other attributes.
  141.  * This implementation takes into account RollbackRuleAttributes, if
  142.  * the TransactionAttribute is a RuleBasedTransactionAttribute.
  143.  * Return null if it's not transactional. 
  144.  * @param atts attributes attached to a method or class. May
  145.  * be null, in which case a null TransactionAttribute will be returned.
  146.  * @return TransactionAttribute configured transaction attribute, or null
  147.  * if none was found
  148.  */
  149. protected TransactionAttribute findTransactionAttribute(Collection atts) {
  150. if (atts == null) {
  151. return null;
  152. }
  153. TransactionAttribute txAttribute = null;
  154. // Check there is a transaction attribute
  155. for (Iterator itr = atts.iterator(); itr.hasNext() && txAttribute == null; ) {
  156. Object att = itr.next();
  157. if (att instanceof TransactionAttribute) {
  158. txAttribute = (TransactionAttribute) att;
  159. }
  160. }
  161. if (txAttribute instanceof RuleBasedTransactionAttribute) {
  162. RuleBasedTransactionAttribute rbta = (RuleBasedTransactionAttribute) txAttribute;
  163. // We really want value: bit of a hack.
  164. List rollbackRules = new LinkedList();
  165. for (Iterator it = atts.iterator(); it.hasNext(); ) {
  166. Object att = it.next();
  167. if (att instanceof RollbackRuleAttribute) {
  168. if (logger.isDebugEnabled()) {
  169. logger.debug("Found RollbackRule: " + att);
  170. }
  171. rollbackRules.add(att);
  172. }
  173. }
  174. // Repeatedly setting this isn't elegant, but it works.
  175. rbta.setRollbackRules(rollbackRules);
  176. }
  177. return txAttribute;
  178. }
  179. }