001: /*
002: * Copyright 2002-2006 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: * http://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:
017: package org.springframework.transaction.interceptor;
018:
019: import java.io.Serializable;
020: import java.lang.reflect.Method;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.Properties;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.util.ObjectUtils;
030: import org.springframework.util.PatternMatchUtils;
031:
032: /**
033: * Simple {@link TransactionAttributeSource} implementation that
034: * allows attributes to be matched by registered name.
035: *
036: * @author Juergen Hoeller
037: * @since 21.08.2003
038: * @see #isMatch
039: * @see MethodMapTransactionAttributeSource
040: */
041: public class NameMatchTransactionAttributeSource implements
042: TransactionAttributeSource, Serializable {
043:
044: /**
045: * Logger available to subclasses.
046: * <p>Static for optimal serialization.
047: */
048: protected static final Log logger = LogFactory
049: .getLog(NameMatchTransactionAttributeSource.class);
050:
051: /** Keys are method names; values are TransactionAttributes */
052: private Map nameMap = new HashMap();
053:
054: /**
055: * Set a name/attribute map, consisting of method names
056: * (e.g. "myMethod") and TransactionAttribute instances
057: * (or Strings to be converted to TransactionAttribute instances).
058: * @see TransactionAttribute
059: * @see TransactionAttributeEditor
060: */
061: public void setNameMap(Map nameMap) {
062: Iterator it = nameMap.entrySet().iterator();
063: while (it.hasNext()) {
064: Map.Entry entry = (Map.Entry) it.next();
065: String name = (String) entry.getKey();
066:
067: // Check whether we need to convert from String to TransactionAttribute.
068: TransactionAttribute attr = null;
069: if (entry.getValue() instanceof TransactionAttribute) {
070: attr = (TransactionAttribute) entry.getValue();
071: } else {
072: TransactionAttributeEditor editor = new TransactionAttributeEditor();
073: editor.setAsText(entry.getValue().toString());
074: attr = (TransactionAttribute) editor.getValue();
075: }
076:
077: addTransactionalMethod(name, attr);
078: }
079: }
080:
081: /**
082: * Parses the given properties into a name/attribute map.
083: * Expects method names as keys and String attributes definitions as values,
084: * parsable into TransactionAttribute instances via TransactionAttributeEditor.
085: * @see #setNameMap
086: * @see TransactionAttributeEditor
087: */
088: public void setProperties(Properties transactionAttributes) {
089: TransactionAttributeEditor tae = new TransactionAttributeEditor();
090: for (Iterator it = transactionAttributes.keySet().iterator(); it
091: .hasNext();) {
092: String methodName = (String) it.next();
093: String value = transactionAttributes
094: .getProperty(methodName);
095: tae.setAsText(value);
096: TransactionAttribute attr = (TransactionAttribute) tae
097: .getValue();
098: addTransactionalMethod(methodName, attr);
099: }
100: }
101:
102: /**
103: * Add an attribute for a transactional method.
104: * <p>Method names can be exact matches, or of the pattern "xxx*",
105: * "*xxx" or "*xxx*" for matching multiple methods.
106: * @param methodName the name of the method
107: * @param attr attribute associated with the method
108: */
109: public void addTransactionalMethod(String methodName,
110: TransactionAttribute attr) {
111: if (logger.isDebugEnabled()) {
112: logger.debug("Adding transactional method [" + methodName
113: + "] with attribute [" + attr + "]");
114: }
115: this .nameMap.put(methodName, attr);
116: }
117:
118: public TransactionAttribute getTransactionAttribute(Method method,
119: Class targetClass) {
120: // look for direct name match
121: String methodName = method.getName();
122: TransactionAttribute attr = (TransactionAttribute) this .nameMap
123: .get(methodName);
124:
125: if (attr == null) {
126: // Look for most specific name match.
127: String bestNameMatch = null;
128: for (Iterator it = this .nameMap.keySet().iterator(); it
129: .hasNext();) {
130: String mappedName = (String) it.next();
131: if (isMatch(methodName, mappedName)
132: && (bestNameMatch == null || bestNameMatch
133: .length() <= mappedName.length())) {
134: attr = (TransactionAttribute) this .nameMap
135: .get(mappedName);
136: bestNameMatch = mappedName;
137: }
138: }
139: }
140:
141: return attr;
142: }
143:
144: /**
145: * Return if the given method name matches the mapped name.
146: * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
147: * as well as direct equality. Can be overridden in subclasses.
148: * @param methodName the method name of the class
149: * @param mappedName the name in the descriptor
150: * @return if the names match
151: * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
152: */
153: protected boolean isMatch(String methodName, String mappedName) {
154: return PatternMatchUtils.simpleMatch(mappedName, methodName);
155: }
156:
157: public boolean equals(Object other) {
158: if (this == other) {
159: return true;
160: }
161: if (!(other instanceof NameMatchTransactionAttributeSource)) {
162: return false;
163: }
164: NameMatchTransactionAttributeSource otherTas = (NameMatchTransactionAttributeSource) other;
165: return ObjectUtils.nullSafeEquals(this .nameMap,
166: otherTas.nameMap);
167: }
168:
169: public int hashCode() {
170: return NameMatchTransactionAttributeSource.class.hashCode();
171: }
172:
173: public String toString() {
174: return getClass().getName() + ": " + this.nameMap;
175: }
176:
177: }
|