DATACMNS-310 - Integrated ChainedTransactionManager from Spring Data Neo4j.
We now integrate a best-effort PlatformTransactionManager that will propagate commits and rollbacks to all PlatformTransactionManager instances registered. This assumes that the exceptional cases which will cause a rollback to happen either before the commits or during the first commit.
This commit is contained in:
committed by
Oliver Gierke
parent
a93c2bff94
commit
a8b912c09f
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright 2011-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.data.transaction;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.transaction.ChainedTransactionManagerTests.TestPlatformTransactionManager.*;
|
||||
import static org.springframework.data.transaction.ChainedTransactionManagerTests.TransactionManagerMatcher.*;
|
||||
import static org.springframework.transaction.HeuristicCompletionException.*;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Factory;
|
||||
import org.junit.Test;
|
||||
import org.springframework.transaction.HeuristicCompletionException;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.UnexpectedRollbackException;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ChainedTransactionManager}.
|
||||
*
|
||||
* @author Michael Hunger
|
||||
* @author Oliver Gierke
|
||||
* @since 1.6
|
||||
*/
|
||||
public class ChainedTransactionManagerTests {
|
||||
|
||||
ChainedTransactionManager tm;
|
||||
|
||||
@Test
|
||||
public void shouldCompleteSuccessfully() throws Exception {
|
||||
|
||||
PlatformTransactionManager transactionManager = createNonFailingTransactionManager("single");
|
||||
setupTransactionManagers(transactionManager);
|
||||
|
||||
createAndCommitTransaction();
|
||||
|
||||
assertThat(transactionManager, isCommitted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowRolledBackExceptionForSingleTMFailure() throws Exception {
|
||||
|
||||
setupTransactionManagers(createFailingTransactionManager("single"));
|
||||
|
||||
try {
|
||||
createAndCommitTransaction();
|
||||
fail("Didn't throw the expected exception");
|
||||
} catch (HeuristicCompletionException e) {
|
||||
assertEquals(HeuristicCompletionException.STATE_ROLLED_BACK, e.getOutcomeState());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCommitAllRegisteredTransactionManagers() {
|
||||
|
||||
PlatformTransactionManager first = createNonFailingTransactionManager("first");
|
||||
PlatformTransactionManager second = createNonFailingTransactionManager("second");
|
||||
|
||||
setupTransactionManagers(first, second);
|
||||
createAndCommitTransaction();
|
||||
|
||||
assertThat(first, isCommitted());
|
||||
assertThat(second, isCommitted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCommitInReverseOrder() {
|
||||
|
||||
PlatformTransactionManager first = createNonFailingTransactionManager("first");
|
||||
PlatformTransactionManager second = createNonFailingTransactionManager("second");
|
||||
|
||||
setupTransactionManagers(first, second);
|
||||
createAndCommitTransaction();
|
||||
|
||||
assertThat("second tm commited before first ", commitTime(first) >= commitTime(second), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowMixedRolledBackExceptionForNonFirstTMFailure() throws Exception {
|
||||
|
||||
setupTransactionManagers(TestPlatformTransactionManager.createFailingTransactionManager("first"),
|
||||
createNonFailingTransactionManager("second"));
|
||||
|
||||
try {
|
||||
createAndCommitTransaction();
|
||||
fail("Didn't throw the expected exception");
|
||||
} catch (HeuristicCompletionException e) {
|
||||
assertHeuristicException(HeuristicCompletionException.STATE_MIXED, e.getOutcomeState());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRollbackAllTransactionManagers() throws Exception {
|
||||
|
||||
PlatformTransactionManager first = createNonFailingTransactionManager("first");
|
||||
PlatformTransactionManager second = createNonFailingTransactionManager("second");
|
||||
|
||||
setupTransactionManagers(first, second);
|
||||
createAndRollbackTransaction();
|
||||
|
||||
assertThat(first, wasRolledback());
|
||||
assertThat(second, wasRolledback());
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = UnexpectedRollbackException.class)
|
||||
public void shouldThrowExceptionOnFailingRollback() throws Exception {
|
||||
PlatformTransactionManager first = createFailingTransactionManager("first");
|
||||
setupTransactionManagers(first);
|
||||
createAndRollbackTransaction();
|
||||
}
|
||||
|
||||
private void setupTransactionManagers(PlatformTransactionManager... transactionManagers) {
|
||||
tm = new ChainedTransactionManager(new TestSynchronizationManager(), transactionManagers);
|
||||
}
|
||||
|
||||
private void createAndRollbackTransaction() {
|
||||
MultiTransactionStatus transaction = tm.getTransaction(new DefaultTransactionDefinition());
|
||||
tm.rollback(transaction);
|
||||
}
|
||||
|
||||
private void createAndCommitTransaction() {
|
||||
MultiTransactionStatus transaction = tm.getTransaction(new DefaultTransactionDefinition());
|
||||
tm.commit(transaction);
|
||||
}
|
||||
|
||||
private static void assertHeuristicException(int expected, int actual) {
|
||||
assertThat(getStateString(actual), is(getStateString(expected)));
|
||||
}
|
||||
|
||||
private static Long commitTime(PlatformTransactionManager transactionManager) {
|
||||
return ((TestPlatformTransactionManager) transactionManager).getCommitTime();
|
||||
}
|
||||
|
||||
static class TestSynchronizationManager implements SynchronizationManager {
|
||||
|
||||
private boolean synchronizationActive;
|
||||
|
||||
public void initSynchronization() {
|
||||
synchronizationActive = true;
|
||||
}
|
||||
|
||||
public boolean isSynchronizationActive() {
|
||||
return synchronizationActive;
|
||||
}
|
||||
|
||||
public void clearSynchronization() {
|
||||
synchronizationActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
static class TestPlatformTransactionManager implements org.springframework.transaction.PlatformTransactionManager {
|
||||
|
||||
private final String name;
|
||||
private Long commitTime;
|
||||
private Long rollbackTime;
|
||||
|
||||
public TestPlatformTransactionManager(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Factory
|
||||
static PlatformTransactionManager createFailingTransactionManager(String name) {
|
||||
return new TestPlatformTransactionManager(name + "-failing") {
|
||||
@Override
|
||||
public void commit(TransactionStatus status) throws TransactionException {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(TransactionStatus status) throws TransactionException {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Factory
|
||||
static PlatformTransactionManager createNonFailingTransactionManager(String name) {
|
||||
return new TestPlatformTransactionManager(name + "-non-failing");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + (isCommitted() ? " (committed) " : " (not committed)");
|
||||
}
|
||||
|
||||
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
|
||||
return new TestTransactionStatus(definition);
|
||||
}
|
||||
|
||||
public void commit(TransactionStatus status) throws TransactionException {
|
||||
commitTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void rollback(TransactionStatus status) throws TransactionException {
|
||||
rollbackTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean isCommitted() {
|
||||
return commitTime != null;
|
||||
}
|
||||
|
||||
public boolean wasRolledBack() {
|
||||
return rollbackTime != null;
|
||||
}
|
||||
|
||||
public Long getCommitTime() {
|
||||
return commitTime;
|
||||
}
|
||||
|
||||
static class TestTransactionStatus implements org.springframework.transaction.TransactionStatus {
|
||||
|
||||
public TestTransactionStatus(TransactionDefinition definition) {
|
||||
}
|
||||
|
||||
public boolean isNewTransaction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasSavepoint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setRollbackOnly() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isRollbackOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object createSavepoint() throws TransactionException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
public void releaseSavepoint(Object savepoint) throws TransactionException {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TransactionManagerMatcher extends
|
||||
org.hamcrest.TypeSafeMatcher<org.springframework.transaction.PlatformTransactionManager> {
|
||||
|
||||
private final boolean commitCheck;
|
||||
|
||||
public TransactionManagerMatcher(boolean commitCheck) {
|
||||
this.commitCheck = commitCheck;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(PlatformTransactionManager platformTransactionManager) {
|
||||
TestPlatformTransactionManager ptm = (TestPlatformTransactionManager) platformTransactionManager;
|
||||
if (commitCheck) {
|
||||
return ptm.isCommitted();
|
||||
} else {
|
||||
return ptm.wasRolledBack();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("that a " + (commitCheck ? "committed" : "rolled-back") + " TransactionManager");
|
||||
}
|
||||
|
||||
@Factory
|
||||
public static TransactionManagerMatcher isCommitted() {
|
||||
return new TransactionManagerMatcher(true);
|
||||
}
|
||||
|
||||
@Factory
|
||||
public static TransactionManagerMatcher wasRolledback() {
|
||||
return new TransactionManagerMatcher(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user