KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 2002-2006 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.util.ArrayList JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import org.springframework.beans.factory.BeanClassLoaderAware;
30 import org.springframework.beans.factory.InitializingBean;
31 import org.springframework.util.Assert;
32 import org.springframework.util.ClassUtils;
33 import org.springframework.util.ObjectUtils;
34 import org.springframework.util.PatternMatchUtils;
35
36 /**
37  * Simple {@link TransactionAttributeSource} implementation that
38  * allows attributes to be stored per method in a {@link Map}.
39  *
40  * @author Rod Johnson
41  * @author Juergen Hoeller
42  * @since 24.04.2003
43  * @see #isMatch
44  * @see NameMatchTransactionAttributeSource
45  */

46 public class MethodMapTransactionAttributeSource
47         implements TransactionAttributeSource, BeanClassLoaderAware, InitializingBean {
48
49     /** Logger available to subclasses */
50     protected final Log logger = LogFactory.getLog(getClass());
51
52     /** Map from method name to attribute value */
53     private Map JavaDoc methodMap;
54
55     private ClassLoader JavaDoc beanClassLoader = ClassUtils.getDefaultClassLoader();
56
57     private boolean eagerlyInitialized = false;
58
59     private boolean initialized = false;
60
61     /** Map from Method to TransactionAttribute */
62     private final Map JavaDoc transactionAttributeMap = new HashMap JavaDoc();
63
64     /** Map from Method to name pattern used for registration */
65     private final Map JavaDoc methodNameMap = new HashMap JavaDoc();
66
67
68     /**
69      * Set a name/attribute map, consisting of "FQCN.method" method names
70      * (e.g. "com.mycompany.mycode.MyClass.myMethod") and
71      * {@link TransactionAttribute} instances (or Strings to be converted
72      * to <code>TransactionAttribute</code> instances).
73      * <p>Intended for configuration via setter injection, typically within
74      * a Spring bean factory. Relies on {@link #afterPropertiesSet()}
75      * being called afterwards.
76      * @param methodMap said {@link Map} from method name to attribute value
77      * @see TransactionAttribute
78      * @see TransactionAttributeEditor
79      */

80     public void setMethodMap(Map JavaDoc methodMap) {
81         this.methodMap = methodMap;
82     }
83
84     public void setBeanClassLoader(ClassLoader JavaDoc beanClassLoader) {
85         this.beanClassLoader = beanClassLoader;
86     }
87
88
89     /**
90      * Eagerly initializes the specified
91      * {@link #setMethodMap(java.util.Map) "methodMap"}, if any.
92      * @see #initMethodMap(java.util.Map)
93      */

94     public void afterPropertiesSet() {
95         initMethodMap(this.methodMap);
96         this.eagerlyInitialized = true;
97         this.initialized = true;
98     }
99
100     /**
101      * Initialize the specified {@link #setMethodMap(java.util.Map) "methodMap"}, if any.
102      * @param methodMap Map from method names to <code>TransactionAttribute</code> instances
103      * (or Strings to be converted to <code>TransactionAttribute</code> instances)
104      * @see #setMethodMap
105      */

106     protected void initMethodMap(Map JavaDoc methodMap) {
107         if (methodMap != null) {
108             Iterator JavaDoc it = methodMap.entrySet().iterator();
109             while (it.hasNext()) {
110                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
111                 Object JavaDoc key = entry.getKey();
112                 if (!(key instanceof String JavaDoc)) {
113                     throw new IllegalArgumentException JavaDoc(
114                             "Invalid method map key [" + key + "]: only Strings allowed");
115                 }
116                 Object JavaDoc value = entry.getValue();
117                 // Check whether we need to convert from String to TransactionAttribute.
118
TransactionAttribute attr = null;
119                 if (value instanceof TransactionAttribute) {
120                     attr = (TransactionAttribute) value;
121                 }
122                 else if (value instanceof String JavaDoc) {
123                     TransactionAttributeEditor editor = new TransactionAttributeEditor();
124                     editor.setAsText((String JavaDoc) value);
125                     attr = (TransactionAttribute) editor.getValue();
126                 }
127                 else {
128                     throw new IllegalArgumentException JavaDoc("Value [" + value + "] is neither of type [" +
129                             TransactionAttribute.class.getName() + "] nor a String");
130                 }
131                 addTransactionalMethod((String JavaDoc) key, attr);
132             }
133         }
134     }
135
136
137     /**
138      * Add an attribute for a transactional method.
139      * <p>Method names can end or start with "*" for matching multiple methods.
140      * @param name class and method name, separated by a dot
141      * @param attr attribute associated with the method
142      * @throws IllegalArgumentException in case of an invalid name
143      */

144     public void addTransactionalMethod(String JavaDoc name, TransactionAttribute attr) {
145         Assert.notNull(name, "Name must not be null");
146         int lastDotIndex = name.lastIndexOf(".");
147         if (lastDotIndex == -1) {
148             throw new IllegalArgumentException JavaDoc("'" + name + "' is not a valid method name: format is FQN.methodName");
149         }
150         String JavaDoc className = name.substring(0, lastDotIndex);
151         String JavaDoc methodName = name.substring(lastDotIndex + 1);
152         Class JavaDoc clazz = ClassUtils.resolveClassName(className, this.beanClassLoader);
153         addTransactionalMethod(clazz, methodName, attr);
154     }
155
156     /**
157      * Add an attribute for a transactional method.
158      * Method names can end or start with "*" for matching multiple methods.
159      * @param clazz target interface or class
160      * @param mappedName mapped method name
161      * @param attr attribute associated with the method
162      */

163     public void addTransactionalMethod(Class JavaDoc clazz, String JavaDoc mappedName, TransactionAttribute attr) {
164         Assert.notNull(clazz, "Class must not be null");
165         Assert.notNull(mappedName, "Mapped name must not be null");
166         String JavaDoc name = clazz.getName() + '.' + mappedName;
167
168         // TODO address method overloading? At present this will
169
// simply match all methods that have the given name.
170
// Consider EJB syntax (int, String) etc.?
171
Method JavaDoc[] methods = clazz.getDeclaredMethods();
172         List JavaDoc matchingMethods = new ArrayList JavaDoc();
173         for (int i = 0; i < methods.length; i++) {
174             if (isMatch(methods[i].getName(), mappedName)) {
175                 matchingMethods.add(methods[i]);
176             }
177         }
178         if (matchingMethods.isEmpty()) {
179             throw new IllegalArgumentException JavaDoc(
180                     "Couldn't find method '" + mappedName + "' on class [" + clazz.getName() + "]");
181         }
182
183         // register all matching methods
184
for (Iterator JavaDoc it = matchingMethods.iterator(); it.hasNext();) {
185             Method JavaDoc method = (Method JavaDoc) it.next();
186             String JavaDoc regMethodName = (String JavaDoc) this.methodNameMap.get(method);
187             if (regMethodName == null || (!regMethodName.equals(name) && regMethodName.length() <= name.length())) {
188                 // No already registered method name, or more specific
189
// method name specification now -> (re-)register method.
190
if (logger.isDebugEnabled() && regMethodName != null) {
191                     logger.debug("Replacing attribute for transactional method [" + method + "]: current name '" +
192                             name + "' is more specific than '" + regMethodName + "'");
193                 }
194                 this.methodNameMap.put(method, name);
195                 addTransactionalMethod(method, attr);
196             }
197             else {
198                 if (logger.isDebugEnabled() && regMethodName != null) {
199                     logger.debug("Keeping attribute for transactional method [" + method + "]: current name '" +
200                             name + "' is not more specific than '" + regMethodName + "'");
201                 }
202             }
203         }
204     }
205
206     /**
207      * Add an attribute for a transactional method.
208      * @param method the method
209      * @param attr attribute associated with the method
210      */

211     public void addTransactionalMethod(Method JavaDoc method, TransactionAttribute attr) {
212         Assert.notNull(method, "Method must not be null");
213         Assert.notNull(attr, "TransactionAttribute must not be null");
214         if (logger.isDebugEnabled()) {
215             logger.debug("Adding transactional method [" + method + "] with attribute [" + attr + "]");
216         }
217         this.transactionAttributeMap.put(method, attr);
218     }
219
220     /**
221      * Return if the given method name matches the mapped name.
222      * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*"
223      * matches, as well as direct equality.
224      * @param methodName the method name of the class
225      * @param mappedName the name in the descriptor
226      * @return if the names match
227      * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
228      */

229     protected boolean isMatch(String JavaDoc methodName, String JavaDoc mappedName) {
230         return PatternMatchUtils.simpleMatch(mappedName, methodName);
231     }
232
233
234     public TransactionAttribute getTransactionAttribute(Method JavaDoc method, Class JavaDoc targetClass) {
235         if (this.eagerlyInitialized) {
236             return (TransactionAttribute) this.transactionAttributeMap.get(method);
237         }
238         else {
239             synchronized (this.transactionAttributeMap) {
240                 if (!this.initialized) {
241                     initMethodMap(this.methodMap);
242                     this.initialized = true;
243                 }
244                 return (TransactionAttribute) this.transactionAttributeMap.get(method);
245             }
246         }
247     }
248
249
250     public boolean equals(Object JavaDoc other) {
251         if (this == other) {
252             return true;
253         }
254         if (!(other instanceof MethodMapTransactionAttributeSource)) {
255             return false;
256         }
257         MethodMapTransactionAttributeSource otherTas = (MethodMapTransactionAttributeSource) other;
258         return ObjectUtils.nullSafeEquals(this.methodMap, otherTas.methodMap);
259     }
260
261     public int hashCode() {
262         return MethodMapTransactionAttributeSource.class.hashCode();
263     }
264
265     public String JavaDoc toString() {
266         return getClass().getName() + ": " + this.methodMap;
267     }
268
269 }
270
Popular Tags