/* * Copyright 2002-2006 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.IOException; import java.io.Serializable; import java.lang.reflect.Method; import javax.ejb.TransactionAttributeType; import junit.framework.TestCase; import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.ProxyFactory; import org.springframework.transaction.CallCountingTransactionManager; import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; import org.springframework.transaction.interceptor.RollbackRuleAttribute; import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionInterceptor; import org.springframework.util.SerializationTestUtils; /** * @author Colin Sampaleanu * @author Juergen Hoeller */ public class AnnotationTransactionAttributeSourceTests extends TestCase { public void testSerializable() throws Exception { TestBean1 tb = new TestBean1(); CallCountingTransactionManager ptm = new CallCountingTransactionManager(); AnnotationTransactionAttributeSource tas = new AnnotationTransactionAttributeSource(); TransactionInterceptor ti = new TransactionInterceptor(ptm, tas); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setInterfaces(new Class[] {ITestBean.class}); proxyFactory.addAdvice(ti); proxyFactory.setTarget(tb); ITestBean proxy = (ITestBean) proxyFactory.getProxy(); proxy.getAge(); assertEquals(1, ptm.commits); ITestBean serializedProxy = (ITestBean) SerializationTestUtils.serializeAndDeserialize(proxy); serializedProxy.getAge(); Advised advised = (Advised) serializedProxy; TransactionInterceptor serializedTi = (TransactionInterceptor) advised.getAdvisors()[0].getAdvice(); CallCountingTransactionManager serializedPtm = (CallCountingTransactionManager) serializedTi.getTransactionManager(); assertEquals(2, serializedPtm.commits); } public void testNullOrEmpty() throws Exception { Method method = Empty.class.getMethod("getAge", (Class[]) null); AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); assertNull(atas.getTransactionAttribute(method, null)); // Try again in case of caching assertNull(atas.getTransactionAttribute(method, null)); } /** * Test the important case where the invocation is on a proxied interface method * but the attribute is defined on the target class. */ public void testTransactionAttributeDeclaredOnClassMethod() throws Exception { Method classMethod = ITestBean.class.getMethod("getAge", (Class[]) null); AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); TransactionAttribute actual = atas.getTransactionAttribute(classMethod, TestBean1.class); RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class)); assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules()); } /** * Test case where attribute is on the interface method. */ public void testTransactionAttributeDeclaredOnInterfaceMethodOnly() throws Exception { Method interfaceMethod = ITestBean2.class.getMethod("getAge", (Class[]) null); AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); TransactionAttribute actual = atas.getTransactionAttribute(interfaceMethod, TestBean2.class); RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules()); } /** * Test that when an attribute exists on both class and interface, class takes precedence. */ public void testTransactionAttributeOnTargetClassMethodOverridesAttributeOnInterfaceMethod() throws Exception { Method interfaceMethod = ITestBean3.class.getMethod("getAge", (Class[]) null); Method interfaceMethod2 = ITestBean3.class.getMethod("getName", (Class[]) null); AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); TransactionAttribute actual = atas.getTransactionAttribute(interfaceMethod, TestBean3.class); assertEquals(TransactionAttribute.PROPAGATION_REQUIRES_NEW, actual.getPropagationBehavior()); assertEquals(TransactionAttribute.ISOLATION_REPEATABLE_READ, actual.getIsolationLevel()); assertEquals(5, actual.getTimeout()); assertTrue(actual.isReadOnly()); RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class)); rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class)); assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules()); TransactionAttribute actual2 = atas.getTransactionAttribute(interfaceMethod2, TestBean3.class); assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, actual2.getPropagationBehavior()); } public void testRollbackRulesAreApplied() throws Exception { Method method = TestBean3.class.getMethod("getAge", (Class[]) null); AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); TransactionAttribute actual = atas.getTransactionAttribute(method, TestBean3.class); RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); rbta.getRollbackRules().add(new RollbackRuleAttribute("java.lang.Exception")); rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class)); assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules()); assertTrue(actual.rollbackOn(new Exception())); assertFalse(actual.rollbackOn(new IOException())); actual = atas.getTransactionAttribute(method, method.getDeclaringClass()); rbta = new RuleBasedTransactionAttribute(); rbta.getRollbackRules().add(new RollbackRuleAttribute("java.lang.Exception")); rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class)); assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules()); assertTrue(actual.rollbackOn(new Exception())); assertFalse(actual.rollbackOn(new IOException())); } /** * Test that transaction attribute is inherited from class * if not specified on method. */ public void testDefaultsToClassTransactionAttribute() throws Exception { Method method = TestBean4.class.getMethod("getAge", (Class[]) null); AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource(); TransactionAttribute actual = atas.getTransactionAttribute(method, TestBean4.class); RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class)); rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class)); assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules()); } public void testTransactionAttributeDeclaredOnClassMethodWithEjb3() 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, Ejb3AnnotatedBean1.class); assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior()); TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, Ejb3AnnotatedBean1.class); assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); } public void testTransactionAttributeDeclaredOnClassWithEjb3() 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, Ejb3AnnotatedBean2.class); assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior()); TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, Ejb3AnnotatedBean2.class); assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); } public void testTransactionAttributeDeclaredOnInterfaceWithEjb3() 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, Ejb3AnnotatedBean3.class); assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior()); TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, Ejb3AnnotatedBean3.class); assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); } public interface ITestBean { int getAge(); void setAge(int age); String getName(); void setName(String name); } public interface ITestBean2 { @Transactional int getAge(); void setAge(int age); String getName(); void setName(String name); } @Transactional public interface ITestBean3 { int getAge(); void setAge(int age); String getName(); void setName(String name); } public static class Empty implements ITestBean { private String name; private int age; public Empty() { } public Empty(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public static class TestBean1 implements ITestBean, Serializable { private String name; private int age; public TestBean1() { } public TestBean1(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Transactional(rollbackFor=Exception.class) public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public static class TestBean2 implements ITestBean2 { private String name; private int age; public TestBean2() { } public TestBean2(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public static class TestBean3 implements ITestBean3 { private String name; private int age; public TestBean3() { } public TestBean3(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.REPEATABLE_READ, timeout=5, readOnly=true, rollbackFor=Exception.class, noRollbackFor={IOException.class}) public int getAge() { return age; } public void setAge(int age) { this.age = age; } } @Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class}) public static class TestBean4 implements ITestBean3 { private String name; private int age; public TestBean4() { } public TestBean4(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public static interface Foo<T> { void doSomething(T theArgument); } public static class MyFoo implements Foo<String> { @Transactional public void doSomething(String theArgument) { System.out.println(theArgument); } } public static class Ejb3AnnotatedBean1 implements ITestBean { private String name; private int age; @javax.ejb.TransactionAttribute(TransactionAttributeType.SUPPORTS) public String getName() { return name; } public void setName(String name) { this.name = name; } @javax.ejb.TransactionAttribute public int getAge() { return age; } public void setAge(int age) { this.age = age; } } @javax.ejb.TransactionAttribute(TransactionAttributeType.SUPPORTS) public static class Ejb3AnnotatedBean2 implements ITestBean { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } @javax.ejb.TransactionAttribute public int getAge() { return age; } public void setAge(int age) { this.age = age; } } @javax.ejb.TransactionAttribute(TransactionAttributeType.SUPPORTS) public interface ITestEjb { @javax.ejb.TransactionAttribute int getAge(); void setAge(int age); String getName(); void setName(String name); } public static class Ejb3AnnotatedBean3 implements ITestEjb { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } }