From 52fd84bb577296f78881388ff72b383a4e4808a3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 27 Mar 2013 22:33:17 +0100 Subject: [PATCH] JTA 1.2 support, in particular for the javax.transaction.Transactional annotation Issue: SPR-9139 --- build.gradle | 3 +- .../AnnotationTransactionAttributeSource.java | 11 +- .../JtaTransactionAnnotationParser.java | 76 ++++++++++ .../TransactionAnnotationParser.java | 6 +- ...tationTransactionAttributeSourceTests.java | 138 ++++++++++++++++++ 5 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java diff --git a/build.gradle b/build.gradle index 11952f51f1d..b9768d3506c 100644 --- a/build.gradle +++ b/build.gradle @@ -302,6 +302,7 @@ project("spring-context") { compile(files(project(":spring-core").cglibRepackJar)) optional("javax.ejb:ejb-api:3.0") optional("javax.inject:javax.inject:1") + optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0-b06") optional("org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1") optional("org.eclipse.persistence:javax.persistence:2.0.0") optional("javax.validation:validation-api:1.0.0.GA") @@ -332,7 +333,7 @@ project("spring-tx") { compile("aopalliance:aopalliance:1.0") provided("com.ibm.websphere:uow:6.0.2.17") optional("javax.resource:connector-api:1.5") - optional("org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1") + optional("org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.0.0.Alpha1") optional("javax.ejb:ejb-api:3.0") testCompile("org.eclipse.persistence:javax.persistence:2.0.0") testCompile("org.aspectj:aspectjweaver:${aspectjVersion}") diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSource.java index 461dbe4e065..30cd95157d8 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,8 @@ import org.springframework.util.ClassUtils; * *

This class reads Spring's JDK 1.5+ {@link Transactional} annotation and * exposes corresponding transaction attributes to Spring's transaction infrastructure. - * Also supports EJB3's {@link javax.ejb.TransactionAttribute} annotation (if present). + * Also supports JTA 1.2's {@link javax.transaction.Transactional} and EJB3's + * {@link javax.ejb.TransactionAttribute} annotation (if present). * This class may also serve as base class for a custom TransactionAttributeSource, * or get customized through {@link TransactionAnnotationParser} strategies. * @@ -53,6 +54,9 @@ import org.springframework.util.ClassUtils; public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable { + private static final boolean jta12Present = ClassUtils.isPresent( + "javax.transaction.Transactional", AnnotationTransactionAttributeSource.class.getClassLoader()); + private static final boolean ejb3Present = ClassUtils.isPresent( "javax.ejb.TransactionAttribute", AnnotationTransactionAttributeSource.class.getClassLoader()); @@ -83,6 +87,9 @@ public class AnnotationTransactionAttributeSource extends AbstractFallbackTransa this.publicMethodsOnly = publicMethodsOnly; this.annotationParsers = new LinkedHashSet(2); this.annotationParsers.add(new SpringTransactionAnnotationParser()); + if (jta12Present) { + this.annotationParsers.add(new JtaTransactionAnnotationParser()); + } if (ejb3Present) { this.annotationParsers.add(new Ejb3TransactionAnnotationParser()); } diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java new file mode 100644 index 00000000000..862a6f4df57 --- /dev/null +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.transaction.annotation; + +import java.io.Serializable; +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; +import org.springframework.transaction.interceptor.RollbackRuleAttribute; +import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; +import org.springframework.transaction.interceptor.TransactionAttribute; + +/** + * Strategy implementation for parsing JTA 1.2's {@link javax.transaction.Transactional} annotation. + * + * @author Juergen Hoeller + * @since 4.0 + */ +@SuppressWarnings("serial") +public class JtaTransactionAnnotationParser implements TransactionAnnotationParser, Serializable { + + public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) { + javax.transaction.Transactional ann = AnnotationUtils.getAnnotation(ae, javax.transaction.Transactional.class); + if (ann != null) { + return parseTransactionAnnotation(ann); + } + else { + return null; + } + } + + public TransactionAttribute parseTransactionAnnotation(javax.transaction.Transactional ann) { + RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); + rbta.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + ann.value().toString()); + ArrayList rollBackRules = new ArrayList(); + Class[] rbf = ann.rollbackOn(); + for (Class rbRule : rbf) { + RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule); + rollBackRules.add(rule); + } + Class[] nrbf = ann.dontRollbackOn(); + for (Class rbRule : nrbf) { + NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule); + rollBackRules.add(rule); + } + rbta.getRollbackRules().addAll(rollBackRules); + return rbta; + } + + @Override + public boolean equals(Object other) { + return (this == other || other instanceof JtaTransactionAnnotationParser); + } + + @Override + public int hashCode() { + return JtaTransactionAnnotationParser.class.hashCode(); + } + +} diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionAnnotationParser.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionAnnotationParser.java index cc07a9ae16b..558b464028e 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionAnnotationParser.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionAnnotationParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,13 +24,15 @@ import org.springframework.transaction.interceptor.TransactionAttribute; * Strategy interface for parsing known transaction annotation types. * {@link AnnotationTransactionAttributeSource} delegates to such * parsers for supporting specific annotation types such as Spring's own - * {@link Transactional} or EJB3's {@link javax.ejb.TransactionAttribute}. + * {@link Transactional}, JTA 1.2's {@link javax.transaction.Transactional} + * or EJB3's {@link javax.ejb.TransactionAttribute}. * * @author Juergen Hoeller * @since 2.5 * @see AnnotationTransactionAttributeSource * @see SpringTransactionAnnotationParser * @see Ejb3TransactionAnnotationParser + * @see JtaTransactionAnnotationParser */ public interface TransactionAnnotationParser { diff --git a/spring-tx/src/test/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSourceTests.java b/spring-tx/src/test/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSourceTests.java index ad05997a079..837b90a4d2a 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSourceTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSourceTests.java @@ -258,6 +258,42 @@ public class AnnotationTransactionAttributeSourceTests { assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); } + @Test + public void testTransactionAttributeDeclaredOnClassMethodWithJta() throws Exception { + Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null); + Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null); + + AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); + TransactionAttribute getAgeAttr = atas.getTransactionAttribute(getAgeMethod, JtaAnnotatedBean1.class); + assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior()); + TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, JtaAnnotatedBean1.class); + assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); + } + + @Test + public void testTransactionAttributeDeclaredOnClassWithJta() throws Exception { + Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null); + Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null); + + AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); + TransactionAttribute getAgeAttr = atas.getTransactionAttribute(getAgeMethod, JtaAnnotatedBean2.class); + assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior()); + TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, JtaAnnotatedBean2.class); + assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); + } + + @Test + public void testTransactionAttributeDeclaredOnInterfaceWithJta() throws Exception { + Method getAgeMethod = ITestEjb.class.getMethod("getAge", (Class[]) null); + Method getNameMethod = ITestEjb.class.getMethod("getName", (Class[]) null); + + AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); + TransactionAttribute getAgeAttr = atas.getTransactionAttribute(getAgeMethod, JtaAnnotatedBean3.class); + assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior()); + TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, JtaAnnotatedBean3.class); + assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); + } + public interface ITestBean { @@ -624,4 +660,106 @@ public class AnnotationTransactionAttributeSourceTests { } } + + public static class JtaAnnotatedBean1 implements ITestBean { + + private String name; + + private int age; + + @Override + @javax.transaction.Transactional(javax.transaction.Transactional.TxType.SUPPORTS) + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + @javax.transaction.Transactional + public int getAge() { + return age; + } + + @Override + public void setAge(int age) { + this.age = age; + } + } + + + @javax.transaction.Transactional(javax.transaction.Transactional.TxType.SUPPORTS) + public static class JtaAnnotatedBean2 implements ITestBean { + + private String name; + + private int age; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + @javax.transaction.Transactional + public int getAge() { + return age; + } + + @Override + public void setAge(int age) { + this.age = age; + } + } + + + @javax.transaction.Transactional(javax.transaction.Transactional.TxType.SUPPORTS) + public interface ITestJta { + + @javax.transaction.Transactional + int getAge(); + + void setAge(int age); + + String getName(); + + void setName(String name); + } + + + public static class JtaAnnotatedBean3 implements ITestEjb { + + private String name; + + private int age; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public int getAge() { + return age; + } + + @Override + public void setAge(int age) { + this.age = age; + } + } + }