diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java index cc232cd2f..aaa653c48 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDbUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 the original author or authors. + * Copyright 2010-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. @@ -26,14 +26,13 @@ import com.mongodb.DB; import com.mongodb.Mongo; /** - * Helper class featuring helper methods for internal MongoDb classes. - *
- *
- * Mainly intended for internal use within the framework.
+ * Helper class featuring helper methods for internal MongoDb classes. Mainly intended for internal use within the
+ * framework.
*
* @author Thomas Risberg
* @author Graeme Rocher
* @author Oliver Gierke
+ * @author Randy Watler
* @since 1.0
*/
public abstract class MongoDbUtils {
@@ -131,8 +130,11 @@ public abstract class MongoDbUtils {
holderToUse.addDB(databaseName, db);
}
- TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
- holderToUse.setSynchronizedWithTransaction(true);
+ // synchronize holder only if not yet synchronized
+ if (!holderToUse.isSynchronizedWithTransaction()) {
+ TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
+ holderToUse.setSynchronizedWithTransaction(true);
+ }
if (holderToUse != dbHolder) {
TransactionSynchronizationManager.bindResource(mongo, holderToUse);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsUnitTests.java
index 507680c26..7a67bd959 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoDbUtilsUnitTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 the original author or authors.
+ * Copyright 2012-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.
@@ -20,6 +20,8 @@ import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
+import java.util.List;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -28,7 +30,9 @@ import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
+import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
+import org.springframework.transaction.support.TransactionSynchronizationUtils;
import com.mongodb.DB;
import com.mongodb.Mongo;
@@ -37,12 +41,12 @@ import com.mongodb.Mongo;
* Unit tests for {@link MongoDbUtils}.
*
* @author Oliver Gierke
+ * @author Randy Watler
*/
@RunWith(MockitoJUnitRunner.class)
public class MongoDbUtilsUnitTests {
- @Mock
- Mongo mongo;
+ @Mock Mongo mongo;
@Before
public void setUp() throws Exception {
@@ -81,4 +85,94 @@ public class MongoDbUtilsUnitTests {
assertThat(first, is(notNullValue()));
assertThat(MongoDbUtils.getDB(mongo, "first"), is(sameInstance(first)));
}
+
+ /**
+ * @see DATAMONGO-737
+ */
+ @Test
+ public void handlesTransactionSynchronizationLifecycle() {
+
+ // ensure transaction synchronization manager has no registered
+ // transaction synchronizations or bound resources at start of test
+ assertThat(TransactionSynchronizationManager.getSynchronizations().isEmpty(), is(true));
+ assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty(), is(true));
+
+ // access database for one mongo instance, (registers transaction
+ // synchronization and binds transaction resource)
+ MongoDbUtils.getDB(mongo, "first");
+
+ // ensure transaction synchronization manager has registered
+ // transaction synchronizations and bound resources
+ assertThat(TransactionSynchronizationManager.getSynchronizations().isEmpty(), is(false));
+ assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty(), is(false));
+
+ // simulate transaction completion, (unbinds transaction resource)
+ try {
+ simulateTransactionCompletion();
+ } catch (Exception e) {
+ fail("Unexpected exception thrown during transaction completion: " + e);
+ }
+
+ // ensure transaction synchronization manager has no bound resources
+ // at end of test
+ assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty(), is(true));
+ }
+
+ /**
+ * @see DATAMONGO-737
+ */
+ @Test
+ public void handlesTransactionSynchronizationsLifecycle() {
+
+ // ensure transaction synchronization manager has no registered
+ // transaction synchronizations or bound resources at start of test
+ assertThat(TransactionSynchronizationManager.getSynchronizations().isEmpty(), is(true));
+ assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty(), is(true));
+
+ // access multiple databases for one mongo instance, (registers
+ // transaction synchronizations and binds transaction resources)
+ MongoDbUtils.getDB(mongo, "first");
+ MongoDbUtils.getDB(mongo, "second");
+
+ // ensure transaction synchronization manager has registered
+ // transaction synchronizations and bound resources
+ assertThat(TransactionSynchronizationManager.getSynchronizations().isEmpty(), is(false));
+ assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty(), is(false));
+
+ // simulate transaction completion, (unbinds transaction resources)
+ try {
+ simulateTransactionCompletion();
+ } catch (Exception e) {
+ fail("Unexpected exception thrown during transaction completion: " + e);
+ }
+
+ // ensure transaction synchronization manager has no bound
+ // transaction resources at end of test
+ assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty(), is(true));
+ }
+
+ /**
+ * Simulate transaction rollback/commit completion protocol on managed transaction synchronizations which will unbind
+ * managed transaction resources. Does not swallow exceptions for testing purposes.
+ *
+ * @see TransactionSynchronizationUtils#triggerBeforeCompletion()
+ * @see TransactionSynchronizationUtils#triggerAfterCompletion(int)
+ */
+ private void simulateTransactionCompletion() {
+
+ // triggerBeforeCompletion() implementation without swallowed exceptions
+ List