KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > transaction > interceptor > AbstractFallbackTransactionAttributeSource


1 /*
2  * Copyright 2002-2007 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.transaction.interceptor;
18
19 import java.lang.reflect.Method JavaDoc;
20 import java.lang.reflect.Modifier JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.LinkedList JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
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 /**
35  * Abstract implementation of {@link TransactionAttributeSource} that caches
36  * attributes for methods and implements a fallback policy: 1. specific target
37  * method; 2. target class; 3. declaring method; 4. declaring class/interface.
38  *
39  * <p>Defaults to using the target class's transaction attribute if none is
40  * associated with the target method. Any transaction attribute associated with
41  * the target method completely overrides a class transaction attribute.
42  * If none found on the target class, the interface that the invoked method
43  * has been called through (in case of a JDK proxy) will be checked.
44  *
45  * <p>This implementation caches attributes by method after they are first used.
46  * If it is ever desirable to allow dynamic changing of transaction attributes
47  * (which is very unlikely), caching could be made configurable. Caching is
48  * desirable because of the cost of evaluating rollback rules.
49  *
50  * @author Rod Johnson
51  * @author Juergen Hoeller
52  * @since 1.1
53  */

54 public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
55
56     /**
57      * Canonical value held in cache to indicate no transaction attribute was
58      * found for this method, and we don't need to look again.
59      */

60     private final static Object JavaDoc NULL_TRANSACTION_ATTRIBUTE = new Object JavaDoc();
61
62
63     /**
64      * Logger available to subclasses.
65      * <p>As this base class is not marked Serializable, the logger will be recreated
66      * after serialization - provided that the concrete subclass is Serializable.
67      */

68     protected final Log logger = LogFactory.getLog(getClass());
69
70     /**
71      * Cache of TransactionAttributes, keyed by DefaultCacheKey (Method + target Class).
72      * <p>As this base class is not marked Serializable, the cache will be recreated
73      * after serialization - provided that the concrete subclass is Serializable.
74      */

75     final Map JavaDoc attributeCache = new HashMap JavaDoc();
76
77
78     /**
79      * Determine the transaction attribute for this method invocation.
80      * <p>Defaults to the class's transaction attribute if no method attribute is found.
81      * @param method the method for the current invocation (never <code>null</code>)
82      * @param targetClass the target class for this invocation (may be <code>null</code>)
83      * @return TransactionAttribute for this method, or <code>null</code> if the method
84      * is not transactional
85      */

86     public TransactionAttribute getTransactionAttribute(Method JavaDoc method, Class JavaDoc targetClass) {
87         // First, see if we have a cached value.
88
Object JavaDoc cacheKey = getCacheKey(method, targetClass);
89         synchronized (this.attributeCache) {
90             Object JavaDoc cached = this.attributeCache.get(cacheKey);
91             if (cached != null) {
92                 // Value will either be canonical value indicating there is no transaction attribute,
93
// or an actual transaction attribute.
94
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
95                     return null;
96                 }
97                 else {
98                     return (TransactionAttribute) cached;
99                 }
100             }
101             else {
102                 // We need to work it out.
103
TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
104                 // Put it in the cache.
105
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     /**
120      * Determine a cache key for the given method and target class.
121      * <p>Must not produce same key for overloaded methods.
122      * Must produce same key for different instances of the same method.
123      * @param method the method (never <code>null</code>)
124      * @param targetClass the target class (may be <code>null</code>)
125      * @return the cache key (never <code>null</code>)
126      */

127     protected Object JavaDoc getCacheKey(Method JavaDoc method, Class JavaDoc targetClass) {
128         return new DefaultCacheKey(method, targetClass);
129     }
130
131     /**
132      * Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
133      * {@link #getTransactionAttribute} is effectively a caching decorator for this method.
134      * @see #getTransactionAttribute
135      */

136     private TransactionAttribute computeTransactionAttribute(Method JavaDoc method, Class JavaDoc targetClass) {
137         // Don't allow no-public methods as required.
138
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
139             return null;
140         }
141
142         // The method may be on an interface, but we need attributes from the target class.
143
// If the target class is null, the method will be unchanged.
144
Method JavaDoc specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
145
146         // First try is the method in the target class.
147
TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));
148         if (txAtt != null) {
149             return txAtt;
150         }
151
152         // Second try is the transaction attribute on the target class.
153
txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));
154         if (txAtt != null) {
155             return txAtt;
156         }
157
158         if (specificMethod != method) {
159             // Fallback is to look at the original method.
160
txAtt = findTransactionAttribute(findAllAttributes(method));
161             if (txAtt != null) {
162                 return txAtt;
163             }
164             // Last fallback is the class of the original method.
165
return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));
166         }
167         return null;
168     }
169
170
171     /**
172      * Subclasses should implement this to return all attributes for this method.
173      * We need all because of the need to analyze rollback rules.
174      * @param method the method to retrieve attributes for
175      * @return all attributes associated with this method (may be <code>null</code>)
176      */

177     protected abstract Collection JavaDoc findAllAttributes(Method JavaDoc method);
178     
179     /**
180      * Subclasses should implement this to return all attributes for this class.
181      * @param clazz class to retrieve attributes for
182      * @return all attributes associated with this class (may be <code>null</code>)
183      */

184     protected abstract Collection JavaDoc findAllAttributes(Class JavaDoc clazz);
185
186
187     /**
188      * Return the transaction attribute, given this set of attributes
189      * attached to a method or class.
190      * <p>Protected rather than private as subclasses may want to customize
191      * how this is done: for example, returning a TransactionAttribute
192      * affected by the values of other attributes.
193      * <p>This implementation takes into account RollbackRuleAttributes,
194      * if the TransactionAttribute is a RuleBasedTransactionAttribute.
195      * @param atts attributes attached to a method or class (may be <code>null</code>)
196      * @return TransactionAttribute the corresponding transaction attribute,
197      * or <code>null</code> if none was found
198      */

199     protected TransactionAttribute findTransactionAttribute(Collection JavaDoc atts) {
200         if (atts == null) {
201             return null;
202         }
203
204         TransactionAttribute txAttribute = null;
205
206         // Check whether there is a transaction attribute.
207
for (Iterator JavaDoc itr = atts.iterator(); itr.hasNext() && txAttribute == null; ) {
208             Object JavaDoc att = itr.next();
209             if (att instanceof TransactionAttribute) {
210                 txAttribute = (TransactionAttribute) att;
211             }
212         }
213
214         // Check if we have a RuleBasedTransactionAttribute.
215
if (txAttribute instanceof RuleBasedTransactionAttribute) {
216             RuleBasedTransactionAttribute rbta = (RuleBasedTransactionAttribute) txAttribute;
217             // We really want value: bit of a hack.
218
List JavaDoc rollbackRules = new LinkedList JavaDoc();
219             for (Iterator JavaDoc it = atts.iterator(); it.hasNext(); ) {
220                 Object JavaDoc 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             // Repeatedly setting this isn't elegant, but it works.
229
rbta.setRollbackRules(rollbackRules);
230         }
231         
232         return txAttribute;
233     }
234
235     /**
236      * Should only public methods be allowed to have transactional semantics?
237      * <p>The default implementation returns <code>false</code>.
238      */

239     protected boolean allowPublicMethodsOnly() {
240         return false;
241     }
242
243
244     /**
245      * Default cache key for the TransactionAttribute cache.
246      */

247     private static class DefaultCacheKey {
248
249         private final Method JavaDoc method;
250
251         private final Class JavaDoc targetClass;
252
253         public DefaultCacheKey(Method JavaDoc method, Class JavaDoc targetClass) {
254             this.method = method;
255             this.targetClass = targetClass;
256         }
257
258         public boolean equals(Object JavaDoc 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