001: /*
002: * Copyright 2002-2007 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.config;
018:
019: import java.util.LinkedList;
020: import java.util.List;
021:
022: import org.w3c.dom.Element;
023:
024: import org.springframework.beans.factory.config.TypedStringValue;
025: import org.springframework.beans.factory.support.BeanDefinitionBuilder;
026: import org.springframework.beans.factory.support.ManagedMap;
027: import org.springframework.beans.factory.support.RootBeanDefinition;
028: import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
029: import org.springframework.beans.factory.xml.ParserContext;
030: import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
031: import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
032: import org.springframework.transaction.interceptor.RollbackRuleAttribute;
033: import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
034: import org.springframework.transaction.interceptor.TransactionInterceptor;
035: import org.springframework.util.StringUtils;
036: import org.springframework.util.xml.DomUtils;
037:
038: /**
039: * {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
040: * for the <code><tx:advice></code> tag.
041: *
042: * @author Rob Harrop
043: * @author Juergen Hoeller
044: * @author Adrian Colyer
045: * @since 2.0
046: */
047: class TxAdviceBeanDefinitionParser extends
048: AbstractSingleBeanDefinitionParser {
049:
050: private static final String ATTRIBUTES = "attributes";
051:
052: private static final String TIMEOUT = "timeout";
053:
054: private static final String READ_ONLY = "read-only";
055:
056: private static final String NAME_MAP = "nameMap";
057:
058: private static final String PROPAGATION = "propagation";
059:
060: private static final String ISOLATION = "isolation";
061:
062: private static final String ROLLBACK_FOR = "rollback-for";
063:
064: private static final String NO_ROLLBACK_FOR = "no-rollback-for";
065:
066: protected Class getBeanClass(Element element) {
067: return TransactionInterceptor.class;
068: }
069:
070: protected void doParse(Element element,
071: ParserContext parserContext, BeanDefinitionBuilder builder) {
072: // Set the transaction manager property.
073: String transactionManagerName = (element
074: .hasAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE) ? element
075: .getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE)
076: : "transactionManager");
077: builder.addPropertyReference(
078: TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY,
079: transactionManagerName);
080:
081: List txAttributes = DomUtils.getChildElementsByTagName(element,
082: ATTRIBUTES);
083: if (txAttributes.size() > 1) {
084: parserContext
085: .getReaderContext()
086: .error(
087: "Element <attributes> is allowed at most once inside element <advice>",
088: element);
089: } else if (txAttributes.size() == 1) {
090: // Using attributes source.
091: Element attributeSourceElement = (Element) txAttributes
092: .get(0);
093: RootBeanDefinition attributeSourceDefinition = parseAttributeSource(
094: attributeSourceElement, parserContext);
095: builder.addPropertyValue(
096: TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE,
097: attributeSourceDefinition);
098: } else {
099: // Assume annotations source.
100: Class sourceClass = TxNamespaceUtils
101: .getAnnotationTransactionAttributeSourceClass();
102: builder.addPropertyValue(
103: TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE,
104: new RootBeanDefinition(sourceClass));
105: }
106: }
107:
108: private RootBeanDefinition parseAttributeSource(Element attrEle,
109: ParserContext parserContext) {
110: List methods = DomUtils.getChildElementsByTagName(attrEle,
111: "method");
112: ManagedMap transactionAttributeMap = new ManagedMap(methods
113: .size());
114: transactionAttributeMap.setSource(parserContext
115: .extractSource(attrEle));
116:
117: for (int i = 0; i < methods.size(); i++) {
118: Element methodEle = (Element) methods.get(i);
119:
120: String name = methodEle.getAttribute("name");
121: TypedStringValue nameHolder = new TypedStringValue(name);
122: nameHolder
123: .setSource(parserContext.extractSource(methodEle));
124:
125: RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
126: String propagation = methodEle.getAttribute(PROPAGATION);
127: String isolation = methodEle.getAttribute(ISOLATION);
128: String timeout = methodEle.getAttribute(TIMEOUT);
129: String readOnly = methodEle.getAttribute(READ_ONLY);
130: if (StringUtils.hasText(propagation)) {
131: attribute
132: .setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION
133: + propagation);
134: }
135: if (StringUtils.hasText(isolation)) {
136: attribute
137: .setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION
138: + isolation);
139: }
140: if (StringUtils.hasText(timeout)) {
141: try {
142: attribute.setTimeout(Integer.parseInt(timeout));
143: } catch (NumberFormatException ex) {
144: parserContext.getReaderContext().error(
145: "Timeout must be an integer value: ["
146: + timeout + "]", methodEle);
147: }
148: }
149: if (StringUtils.hasText(readOnly)) {
150: attribute.setReadOnly(Boolean.valueOf(
151: methodEle.getAttribute(READ_ONLY))
152: .booleanValue());
153: }
154:
155: List rollbackRules = new LinkedList();
156: if (methodEle.hasAttribute(ROLLBACK_FOR)) {
157: String rollbackForValue = methodEle
158: .getAttribute(ROLLBACK_FOR);
159: addRollbackRuleAttributesTo(rollbackRules,
160: rollbackForValue);
161: }
162: if (methodEle.hasAttribute(NO_ROLLBACK_FOR)) {
163: String noRollbackForValue = methodEle
164: .getAttribute(NO_ROLLBACK_FOR);
165: addNoRollbackRuleAttributesTo(rollbackRules,
166: noRollbackForValue);
167: }
168: attribute.setRollbackRules(rollbackRules);
169:
170: transactionAttributeMap.put(nameHolder, attribute);
171: }
172:
173: RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(
174: NameMatchTransactionAttributeSource.class);
175: attributeSourceDefinition.setSource(parserContext
176: .extractSource(attrEle));
177: attributeSourceDefinition.getPropertyValues().addPropertyValue(
178: NAME_MAP, transactionAttributeMap);
179: return attributeSourceDefinition;
180: }
181:
182: private void addRollbackRuleAttributesTo(List rollbackRules,
183: String rollbackForValue) {
184: String[] exceptionTypeNames = StringUtils
185: .commaDelimitedListToStringArray(rollbackForValue);
186: for (int i = 0; i < exceptionTypeNames.length; i++) {
187: rollbackRules.add(new RollbackRuleAttribute(StringUtils
188: .trimWhitespace(exceptionTypeNames[i])));
189: }
190: }
191:
192: private void addNoRollbackRuleAttributesTo(List rollbackRules,
193: String noRollbackForValue) {
194: String[] exceptionTypeNames = StringUtils
195: .commaDelimitedListToStringArray(noRollbackForValue);
196: for (int i = 0; i < exceptionTypeNames.length; i++) {
197: rollbackRules.add(new NoRollbackRuleAttribute(StringUtils
198: .trimWhitespace(exceptionTypeNames[i])));
199: }
200: }
201:
202: }
|