/*
* Copyright 2002-2017 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.aspectj;
import org.junit.Before;
import org.junit.Test;
import org.springframework.tests.transaction.CallCountingTransactionManager;
import static org.junit.Assert.*;
/**
* @author Rod Johnson
* @author Ramnivas Laddad
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class TransactionAspectTests {
private final CallCountingTransactionManager txManager = new CallCountingTransactionManager();
private final TransactionalAnnotationOnlyOnClassWithNoInterface annotationOnlyOnClassWithNoInterface =
new TransactionalAnnotationOnlyOnClassWithNoInterface();
private final ClassWithProtectedAnnotatedMember beanWithAnnotatedProtectedMethod =
new ClassWithProtectedAnnotatedMember();
private final ClassWithPrivateAnnotatedMember beanWithAnnotatedPrivateMethod =
new ClassWithPrivateAnnotatedMember();
private final MethodAnnotationOnClassWithNoInterface methodAnnotationOnly =
new MethodAnnotationOnClassWithNoInterface();
@Before
public void initContext() {
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
}
@Test
public void testCommitOnAnnotatedClass() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
annotationOnlyOnClassWithNoInterface.echo(null);
assertEquals(1, txManager.commits);
}
@Test
public void commitOnAnnotatedProtectedMethod() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
beanWithAnnotatedProtectedMethod.doInTransaction();
assertEquals(1, txManager.commits);
}
@Test
public void commitOnAnnotatedPrivateMethod() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
beanWithAnnotatedPrivateMethod.doSomething();
assertEquals(1, txManager.commits);
}
@Test
public void commitOnNonAnnotatedNonPublicMethodInTransactionalType() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
annotationOnlyOnClassWithNoInterface.nonTransactionalMethod();
assertEquals(0, txManager.begun);
}
@Test
public void commitOnAnnotatedMethod() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
methodAnnotationOnly.echo(null);
assertEquals(1, txManager.commits);
}
@Test
public void notTransactional() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
new NotTransactional().noop();
assertEquals(0, txManager.begun);
}
@Test
public void defaultCommitOnAnnotatedClass() throws Throwable {
final Exception ex = new Exception();
try {
testRollback(() -> annotationOnlyOnClassWithNoInterface.echo(ex), false);
fail("Should have thrown Exception");
}
catch (Exception ex2) {
assertSame(ex, ex2);
}
}
@Test
public void defaultRollbackOnAnnotatedClass() throws Throwable {
final RuntimeException ex = new RuntimeException();
try {
testRollback(() -> annotationOnlyOnClassWithNoInterface.echo(ex), true);
fail("Should have thrown RuntimeException");
}
catch (RuntimeException ex2) {
assertSame(ex, ex2);
}
}
@Test
public void defaultCommitOnSubclassOfAnnotatedClass() throws Throwable {
final Exception ex = new Exception();
try {
testRollback(() -> new SubclassOfClassWithTransactionalAnnotation().echo(ex), false);
fail("Should have thrown Exception");
}
catch (Exception ex2) {
assertSame(ex, ex2);
}
}
@Test
public void defaultCommitOnSubclassOfClassWithTransactionalMethodAnnotated() throws Throwable {
final Exception ex = new Exception();
try {
testRollback(() -> new SubclassOfClassWithTransactionalMethodAnnotation().echo(ex), false);
fail("Should have thrown Exception");
}
catch (Exception ex2) {
assertSame(ex, ex2);
}
}
@Test
public void noCommitOnImplementationOfAnnotatedInterface() throws Throwable {
final Exception ex = new Exception();
testNotTransactional(() -> new ImplementsAnnotatedInterface().echo(ex), ex);
}
@Test
public void noRollbackOnImplementationOfAnnotatedInterface() throws Throwable {
final Exception rollbackProvokingException = new RuntimeException();
testNotTransactional(() -> new ImplementsAnnotatedInterface().echo(rollbackProvokingException),
rollbackProvokingException);
}
protected void testRollback(TransactionOperationCallback toc, boolean rollback) throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
try {
toc.performTransactionalOperation();
}
finally {
assertEquals(1, txManager.begun);
assertEquals(rollback ? 0 : 1, txManager.commits);
assertEquals(rollback ? 1 : 0, txManager.rollbacks);
}
}
protected void testNotTransactional(TransactionOperationCallback toc, Throwable expected) throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
try {
toc.performTransactionalOperation();
}
catch (Throwable t) {
if (expected == null) {
fail("Expected " + expected);
}
assertSame(expected, t);
}
finally {
assertEquals(0, txManager.begun);
}
}
private interface TransactionOperationCallback {
Object performTransactionalOperation() throws Throwable;
}
public static class SubclassOfClassWithTransactionalAnnotation extends TransactionalAnnotationOnlyOnClassWithNoInterface {
}
public static class SubclassOfClassWithTransactionalMethodAnnotation extends MethodAnnotationOnClassWithNoInterface {
}
public static class ImplementsAnnotatedInterface implements ITransactional {
@Override
public Object echo(Throwable t) throws Throwable {
if (t != null) {
throw t;
}
return t;
}
}
public static class NotTransactional {
public void noop() {
}
}
}