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: builder
074: .addPropertyReference(
075: TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY,
076: element
077: .getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE));
078:
079: List txAttributes = DomUtils.getChildElementsByTagName(element,
080: ATTRIBUTES);
081: if (txAttributes.size() > 1) {
082: parserContext
083: .getReaderContext()
084: .error(
085: "Element <attributes> is allowed at most once inside element <advice>",
086: element);
087: } else if (txAttributes.size() == 1) {
088: // Using attributes source.
089: Element attributeSourceElement = (Element) txAttributes
090: .get(0);
091: RootBeanDefinition attributeSourceDefinition = parseAttributeSource(
092: attributeSourceElement, parserContext);
093: builder.addPropertyValue(
094: TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE,
095: attributeSourceDefinition);
096: } else {
097: // Assume annotations source.
098: Class sourceClass = TxNamespaceUtils
099: .getAnnotationTransactionAttributeSourceClass();
100: builder.addPropertyValue(
101: TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE,
102: new RootBeanDefinition(sourceClass));
103: }
104: }
105:
106: private RootBeanDefinition parseAttributeSource(Element attrEle,
107: ParserContext parserContext) {
108: List methods = DomUtils.getChildElementsByTagName(attrEle,
109: "method");
110: ManagedMap transactionAttributeMap = new ManagedMap(methods
111: .size());
112: transactionAttributeMap.setSource(parserContext
113: .extractSource(attrEle));
114:
115: for (int i = 0; i < methods.size(); i++) {
116: Element methodEle = (Element) methods.get(i);
117:
118: String name = methodEle.getAttribute("name");
119: TypedStringValue nameHolder = new TypedStringValue(name);
120: nameHolder
121: .setSource(parserContext.extractSource(methodEle));
122:
123: RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
124: String propagation = methodEle.getAttribute(PROPAGATION);
125: String isolation = methodEle.getAttribute(ISOLATION);
126: String timeout = methodEle.getAttribute(TIMEOUT);
127: String readOnly = methodEle.getAttribute(READ_ONLY);
128: if (StringUtils.hasText(propagation)) {
129: attribute
130: .setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION
131: + propagation);
132: }
133: if (StringUtils.hasText(isolation)) {
134: attribute
135: .setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION
136: + isolation);
137: }
138: if (StringUtils.hasText(timeout)) {
139: try {
140: attribute.setTimeout(Integer.parseInt(timeout));
141: } catch (NumberFormatException ex) {
142: parserContext.getReaderContext().error(
143: "Timeout must be an integer value: ["
144: + timeout + "]", methodEle);
145: }
146: }
147: if (StringUtils.hasText(readOnly)) {
148: attribute.setReadOnly(Boolean.valueOf(
149: methodEle.getAttribute(READ_ONLY))
150: .booleanValue());
151: }
152:
153: List rollbackRules = new LinkedList();
154: if (methodEle.hasAttribute(ROLLBACK_FOR)) {
155: String rollbackForValue = methodEle
156: .getAttribute(ROLLBACK_FOR);
157: addRollbackRuleAttributesTo(rollbackRules,
158: rollbackForValue);
159: }
160: if (methodEle.hasAttribute(NO_ROLLBACK_FOR)) {
161: String noRollbackForValue = methodEle
162: .getAttribute(NO_ROLLBACK_FOR);
163: addNoRollbackRuleAttributesTo(rollbackRules,
164: noRollbackForValue);
165: }
166: attribute.setRollbackRules(rollbackRules);
167:
168: transactionAttributeMap.put(nameHolder, attribute);
169: }
170:
171: RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(
172: NameMatchTransactionAttributeSource.class);
173: attributeSourceDefinition.setSource(parserContext
174: .extractSource(attrEle));
175: attributeSourceDefinition.getPropertyValues().addPropertyValue(
176: NAME_MAP, transactionAttributeMap);
177: return attributeSourceDefinition;
178: }
179:
180: private void addRollbackRuleAttributesTo(List rollbackRules,
181: String rollbackForValue) {
182: String[] exceptionTypeNames = StringUtils
183: .commaDelimitedListToStringArray(rollbackForValue);
184: for (int i = 0; i < exceptionTypeNames.length; i++) {
185: rollbackRules.add(new RollbackRuleAttribute(StringUtils
186: .trimWhitespace(exceptionTypeNames[i])));
187: }
188: }
189:
190: private void addNoRollbackRuleAttributesTo(List rollbackRules,
191: String noRollbackForValue) {
192: String[] exceptionTypeNames = StringUtils
193: .commaDelimitedListToStringArray(noRollbackForValue);
194: for (int i = 0; i < exceptionTypeNames.length; i++) {
195: rollbackRules.add(new NoRollbackRuleAttribute(StringUtils
196: .trimWhitespace(exceptionTypeNames[i])));
197: }
198: }
199:
200: }
|