Fix for LDAP-122: Now properly throwing a CannotCreateTransactionException if the underlying DataSource refuses

creation of a DirContext instance.
This commit is contained in:
Mattias Arthursson
2008-10-22 09:23:34 +00:00
parent 9c0d1afe7a
commit 7b24ab0c0c
3 changed files with 263 additions and 206 deletions

View File

@@ -83,7 +83,7 @@ public class ContextSourceTransactionManagerDelegate extends
* @see org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate#getNewHolder()
*/
protected CompensatingTransactionHolderSupport getNewHolder() {
DirContext newCtx = getContextSource().getReadOnlyContext();
DirContext newCtx = getContextSource().getReadWriteContext();
DirContextHolder contextHolder = new DirContextHolder(
new DefaultCompensatingTransactionOperationManager(
new LdapCompensatingTransactionOperationFactory(

View File

@@ -17,6 +17,9 @@ package org.springframework.transaction.compensating.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.DefaultTransactionStatus;
@@ -32,99 +35,100 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
*/
public abstract class AbstractCompensatingTransactionManagerDelegate {
private static Log log = LogFactory
.getLog(AbstractCompensatingTransactionManagerDelegate.class);
private static Log log = LogFactory.getLog(AbstractCompensatingTransactionManagerDelegate.class);
/**
* Close the target resource - the implementation specific resource held in
* the specified {@link CompensatingTransactionHolderSupport}.
*
* @param transactionHolderSupport
* the {@link CompensatingTransactionHolderSupport} that holds
* the transaction specific target resource.
*/
protected abstract void closeTargetResource(
CompensatingTransactionHolderSupport transactionHolderSupport);
/**
* Close the target resource - the implementation specific resource held in
* the specified {@link CompensatingTransactionHolderSupport}.
*
* @param transactionHolderSupport the
* {@link CompensatingTransactionHolderSupport} that holds the transaction
* specific target resource.
*/
protected abstract void closeTargetResource(CompensatingTransactionHolderSupport transactionHolderSupport);
/**
* Get a new implementation specific
* {@link CompensatingTransactionHolderSupport} instance.
*
* @return a new {@link CompensatingTransactionHolderSupport} instance.
*/
protected abstract CompensatingTransactionHolderSupport getNewHolder();
/**
* Get a new implementation specific
* {@link CompensatingTransactionHolderSupport} instance.
*
* @return a new {@link CompensatingTransactionHolderSupport} instance.
*/
protected abstract CompensatingTransactionHolderSupport getNewHolder();
/**
* Get the key (normally, a DataSource or similar) that should be used for
* transaction synchronization.
*
* @return the transaction synchronization key
*/
protected abstract Object getTransactionSynchronizationKey();
/**
* Get the key (normally, a DataSource or similar) that should be used for
* transaction synchronization.
*
* @return the transaction synchronization key
*/
protected abstract Object getTransactionSynchronizationKey();
/*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction()
*/
public Object doGetTransaction() throws TransactionException {
CompensatingTransactionHolderSupport holder = (CompensatingTransactionHolderSupport) TransactionSynchronizationManager
.getResource(getTransactionSynchronizationKey());
CompensatingTransactionObject txObject = new CompensatingTransactionObject(
holder);
return txObject;
}
/*
* @seeorg.springframework.jdbc.datasource.DataSourceTransactionManager#
* doGetTransaction()
*/
public Object doGetTransaction() throws TransactionException {
CompensatingTransactionHolderSupport holder = (CompensatingTransactionHolderSupport) TransactionSynchronizationManager
.getResource(getTransactionSynchronizationKey());
CompensatingTransactionObject txObject = new CompensatingTransactionObject(holder);
return txObject;
}
/*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin(java.lang.Object,
* org.springframework.transaction.TransactionDefinition)
*/
public void doBegin(Object transaction, TransactionDefinition definition)
throws TransactionException {
CompensatingTransactionObject txObject = (CompensatingTransactionObject) transaction;
/*
* @see
* org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
* (java.lang.Object, org.springframework.transaction.TransactionDefinition)
*/
public void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
try {
CompensatingTransactionObject txObject = (CompensatingTransactionObject) transaction;
if (txObject.getHolder() == null) {
CompensatingTransactionHolderSupport contextHolder = getNewHolder();
txObject.setHolder(contextHolder);
if (txObject.getHolder() == null) {
CompensatingTransactionHolderSupport contextHolder = getNewHolder();
txObject.setHolder(contextHolder);
TransactionSynchronizationManager.bindResource(getTransactionSynchronizationKey(), contextHolder);
}
}
catch (NamingException e) {
throw new CannotCreateTransactionException("Could not create DirContext instance for transaction", e);
}
}
TransactionSynchronizationManager.bindResource(
getTransactionSynchronizationKey(), contextHolder);
}
}
/*
* @see
* org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit
* (org.springframework.transaction.support.DefaultTransactionStatus)
*/
public void doCommit(DefaultTransactionStatus status) throws TransactionException {
CompensatingTransactionObject txObject = (CompensatingTransactionObject) status.getTransaction();
txObject.getHolder().getTransactionOperationManager().commit();
/*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus)
*/
public void doCommit(DefaultTransactionStatus status)
throws TransactionException {
CompensatingTransactionObject txObject = (CompensatingTransactionObject) status
.getTransaction();
txObject.getHolder().getTransactionOperationManager().commit();
}
}
/*
* @see
* org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback
* (org.springframework.transaction.support.DefaultTransactionStatus)
*/
public void doRollback(DefaultTransactionStatus status) throws TransactionException {
CompensatingTransactionObject txObject = (CompensatingTransactionObject) status.getTransaction();
txObject.getHolder().getTransactionOperationManager().rollback();
}
/*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus)
*/
public void doRollback(DefaultTransactionStatus status)
throws TransactionException {
CompensatingTransactionObject txObject = (CompensatingTransactionObject) status
.getTransaction();
txObject.getHolder().getTransactionOperationManager().rollback();
}
/*
* @seeorg.springframework.jdbc.datasource.DataSourceTransactionManager#
* doCleanupAfterCompletion(java.lang.Object)
*/
public void doCleanupAfterCompletion(Object transaction) {
log.debug("Cleaning stored transaction synchronization");
TransactionSynchronizationManager.unbindResource(getTransactionSynchronizationKey());
/*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCleanupAfterCompletion(java.lang.Object)
*/
public void doCleanupAfterCompletion(Object transaction) {
log.debug("Cleaning stored transaction synchronization");
TransactionSynchronizationManager
.unbindResource(getTransactionSynchronizationKey());
CompensatingTransactionObject txObject = (CompensatingTransactionObject) transaction;
CompensatingTransactionHolderSupport transactionHolderSupport = (CompensatingTransactionHolderSupport) txObject
.getHolder();
CompensatingTransactionObject txObject = (CompensatingTransactionObject) transaction;
CompensatingTransactionHolderSupport transactionHolderSupport = (CompensatingTransactionHolderSupport) txObject
.getHolder();
closeTargetResource(transactionHolderSupport);
closeTargetResource(transactionHolderSupport);
txObject.getHolder().clear();
}
txObject.getHolder().clear();
}
}

View File

@@ -16,186 +16,239 @@
package org.springframework.ldap.transaction.compensating.manager;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.directory.DirContext;
import javax.sql.DataSource;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy;
import org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager;
import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.compensating.CompensatingTransactionOperationManager;
import org.springframework.transaction.compensating.support.CompensatingTransactionHolderSupport;
import org.springframework.transaction.compensating.support.CompensatingTransactionObject;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class ContextSourceTransactionManagerTest extends TestCase {
private MockControl contextSourceControl;
private MockControl contextSourceControl;
private ContextSource contextSourceMock;
private ContextSource contextSourceMock;
private MockControl contextControl;
private MockControl contextControl;
private DirContext contextMock;
private DirContext contextMock;
private ContextSourceTransactionManager tested;
private ContextSourceTransactionManager tested;
private MockControl transactionDefinitionControl;
private MockControl transactionDefinitionControl;
private MockControl transactionDataManagerControl;
private MockControl transactionDataManagerControl;
private CompensatingTransactionOperationManager transactionDataManagerMock;
private CompensatingTransactionOperationManager transactionDataManagerMock;
private TransactionDefinition transactionDefinitionMock;
private TransactionDefinition transactionDefinitionMock;
private MockControl renamingStrategyControl;
private MockControl renamingStrategyControl;
private TempEntryRenamingStrategy renamingStrategyMock;
private TempEntryRenamingStrategy renamingStrategyMock;
protected void setUp() throws Exception {
super.setUp();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.clearSynchronization();
}
protected void setUp() throws Exception {
super.setUp();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.clearSynchronization();
}
contextSourceControl = MockControl.createControl(ContextSource.class);
contextSourceMock = (ContextSource) contextSourceControl.getMock();
contextSourceControl = MockControl.createControl(ContextSource.class);
contextSourceMock = (ContextSource) contextSourceControl.getMock();
contextControl = MockControl.createControl(DirContext.class);
contextMock = (DirContext) contextControl.getMock();
contextControl = MockControl.createControl(DirContext.class);
contextMock = (DirContext) contextControl.getMock();
transactionDefinitionControl = MockControl
.createControl(TransactionDefinition.class);
transactionDefinitionMock = (TransactionDefinition) transactionDefinitionControl
.getMock();
transactionDefinitionControl = MockControl.createControl(TransactionDefinition.class);
transactionDefinitionMock = (TransactionDefinition) transactionDefinitionControl.getMock();
transactionDataManagerControl = MockControl
.createControl(CompensatingTransactionOperationManager.class);
transactionDataManagerMock = (CompensatingTransactionOperationManager) transactionDataManagerControl
.getMock();
transactionDataManagerControl = MockControl.createControl(CompensatingTransactionOperationManager.class);
transactionDataManagerMock = (CompensatingTransactionOperationManager) transactionDataManagerControl.getMock();
renamingStrategyControl = MockControl
.createControl(TempEntryRenamingStrategy.class);
renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl
.getMock();
renamingStrategyControl = MockControl.createControl(TempEntryRenamingStrategy.class);
renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl.getMock();
tested = new ContextSourceTransactionManager();
tested.setContextSource(contextSourceMock);
tested.setRenamingStrategy(renamingStrategyMock);
}
tested = new ContextSourceTransactionManager();
tested.setContextSource(contextSourceMock);
tested.setRenamingStrategy(renamingStrategyMock);
}
protected void tearDown() throws Exception {
super.tearDown();
protected void tearDown() throws Exception {
super.tearDown();
contextControl = null;
contextMock = null;
contextControl = null;
contextMock = null;
contextSourceControl = null;
contextSourceMock = null;
contextSourceControl = null;
contextSourceMock = null;
transactionDefinitionControl = null;
transactionDefinitionMock = null;
transactionDefinitionControl = null;
transactionDefinitionMock = null;
transactionDataManagerControl = null;
transactionDataManagerMock = null;
transactionDataManagerControl = null;
transactionDataManagerMock = null;
renamingStrategyControl = null;
renamingStrategyMock = null;
renamingStrategyControl = null;
renamingStrategyMock = null;
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.clearSynchronization();
}
}
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.clearSynchronization();
}
}
public void testDoGetTransaction() {
Object result = tested.doGetTransaction();
public void testDoGetTransaction() {
Object result = tested.doGetTransaction();
assertNotNull(result);
assertTrue(result instanceof CompensatingTransactionObject);
CompensatingTransactionObject transactionObject = (CompensatingTransactionObject) result;
assertNull(transactionObject.getHolder());
}
assertNotNull(result);
assertTrue(result instanceof CompensatingTransactionObject);
CompensatingTransactionObject transactionObject = (CompensatingTransactionObject) result;
assertNull(transactionObject.getHolder());
}
public void testDoGetTransactionTransactionActive() {
CompensatingTransactionHolderSupport expectedContextHolder = new DirContextHolder(
null, null);
TransactionSynchronizationManager.bindResource(contextSourceMock,
expectedContextHolder);
Object result = tested.doGetTransaction();
assertSame(expectedContextHolder,
((CompensatingTransactionObject) result).getHolder());
}
public void testDoGetTransactionTransactionActive() {
CompensatingTransactionHolderSupport expectedContextHolder = new DirContextHolder(null, null);
TransactionSynchronizationManager.bindResource(contextSourceMock, expectedContextHolder);
Object result = tested.doGetTransaction();
assertSame(expectedContextHolder, ((CompensatingTransactionObject) result).getHolder());
}
public void testDoBegin() {
contextSourceControl.expectAndReturn(contextSourceMock
.getReadOnlyContext(), contextMock);
public void testDoBegin() {
contextSourceControl.expectAndReturn(contextSourceMock.getReadWriteContext(), contextMock);
contextSourceControl.replay();
contextSourceControl.replay();
CompensatingTransactionObject expectedTransactionObject = new CompensatingTransactionObject(
null);
tested.doBegin(expectedTransactionObject, transactionDefinitionMock);
CompensatingTransactionObject expectedTransactionObject = new CompensatingTransactionObject(null);
tested.doBegin(expectedTransactionObject, transactionDefinitionMock);
contextSourceControl.verify();
contextSourceControl.verify();
DirContextHolder foundContextHolder = (DirContextHolder) TransactionSynchronizationManager
.getResource(contextSourceMock);
assertSame(contextMock, foundContextHolder.getCtx());
}
DirContextHolder foundContextHolder = (DirContextHolder) TransactionSynchronizationManager
.getResource(contextSourceMock);
assertSame(contextMock, foundContextHolder.getCtx());
}
public void testDoCommit() {
}
public void testDoCommit() {
}
public void testDoRollback() {
public void testDoRollback() {
DirContextHolder expectedContextHolder = new DirContextHolder(null,
contextMock);
expectedContextHolder
.setTransactionOperationManager(transactionDataManagerMock);
TransactionSynchronizationManager.bindResource(contextSourceMock,
expectedContextHolder);
DirContextHolder expectedContextHolder = new DirContextHolder(null, contextMock);
expectedContextHolder.setTransactionOperationManager(transactionDataManagerMock);
TransactionSynchronizationManager.bindResource(contextSourceMock, expectedContextHolder);
transactionDataManagerMock.rollback();
transactionDataManagerControl.replay();
CompensatingTransactionObject transactionObject = new CompensatingTransactionObject(
null);
transactionObject.setHolder(expectedContextHolder);
tested.doRollback(new DefaultTransactionStatus(transactionObject,
false, false, false, false, null));
transactionDataManagerControl.verify();
}
transactionDataManagerMock.rollback();
transactionDataManagerControl.replay();
CompensatingTransactionObject transactionObject = new CompensatingTransactionObject(null);
transactionObject.setHolder(expectedContextHolder);
tested.doRollback(new DefaultTransactionStatus(transactionObject, false, false, false, false, null));
transactionDataManagerControl.verify();
}
public void testDoCleanupAfterCompletion() throws Exception {
DirContextHolder expectedContextHolder = new DirContextHolder(null,
contextMock);
TransactionSynchronizationManager.bindResource(contextSourceMock,
expectedContextHolder);
public void testDoCleanupAfterCompletion() throws Exception {
DirContextHolder expectedContextHolder = new DirContextHolder(null, contextMock);
TransactionSynchronizationManager.bindResource(contextSourceMock, expectedContextHolder);
contextMock.close();
contextControl.replay();
contextMock.close();
contextControl.replay();
tested.doCleanupAfterCompletion(new CompensatingTransactionObject(
expectedContextHolder));
tested.doCleanupAfterCompletion(new CompensatingTransactionObject(expectedContextHolder));
contextControl.verify();
assertNull(TransactionSynchronizationManager
.getResource(contextSourceMock));
assertNull(expectedContextHolder.getTransactionOperationManager());
}
contextControl.verify();
assertNull(TransactionSynchronizationManager.getResource(contextSourceMock));
assertNull(expectedContextHolder.getTransactionOperationManager());
}
public void testSetContextSource_Proxy() {
TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy(
contextSourceMock);
public void testSetContextSource_Proxy() {
TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy(contextSourceMock);
// Perform test
tested.setContextSource(proxy);
ContextSource result = tested.getContextSource();
// Perform test
tested.setContextSource(proxy);
ContextSource result = tested.getContextSource();
// Verify result
assertSame(contextSourceMock, result);
}
// Verify result
assertSame(contextSourceMock, result);
}
public void testTransactionSuspension_UnconnectableDataSource() throws Exception {
MockControl connectionControl = MockControl.createControl(Connection.class);
Connection connectionMock = (Connection) connectionControl.getMock();
MockControl dataSourceControl=MockControl.createControl(DataSource.class);
DataSource dataSourceMock = (DataSource) dataSourceControl.getMock();
dataSourceControl.expectAndReturn(dataSourceMock.getConnection(), connectionMock);
connectionControl.expectAndReturn(connectionMock.getAutoCommit(), false);
connectionMock.rollback();
MockControl unconnectableContextSourceControl = MockControl.createControl(ContextSource.class);
ContextSource unconnectableContextSourceMock = (ContextSource) unconnectableContextSourceControl.getMock();
UncategorizedLdapException connectException = new UncategorizedLdapException("dummy");
unconnectableContextSourceControl.expectAndThrow(unconnectableContextSourceMock.getReadWriteContext(), connectException);
connectionControl.replay();
dataSourceControl.replay();
unconnectableContextSourceControl.replay();
try {
// Create an outer transaction
final PlatformTransactionManager txMgrOuter = new DataSourceTransactionManager(dataSourceMock);
final TransactionStatus txOuter = txMgrOuter.getTransaction(new DefaultTransactionDefinition());
try {
// Create inner transaction (not nested, though: unrelated data
// source)
final ContextSourceTransactionManager txMgrInner = new ContextSourceTransactionManager();
txMgrInner.setContextSource(unconnectableContextSourceMock);
final TransactionStatus txInner = txMgrInner.getTransaction(new DefaultTransactionDefinition(
TransactionDefinition.PROPAGATION_REQUIRES_NEW));
try {
// Do something with the connection that succeeds or fails
// (but we dont get this far)
// etc, etc...
txMgrInner.commit(txInner);
}
catch (Exception e) {
txMgrInner.rollback(txInner);
throw e;
}
txMgrOuter.commit(txOuter);
}
catch (Exception e) {
txMgrOuter.rollback(txOuter);
throw e;
}
fail("Exception should be thrown");
}
catch (CannotCreateTransactionException expected) {
assertSame("Should be thrown exception", connectException, expected.getCause());
}
connectionControl.verify();
dataSourceControl.verify();
unconnectableContextSourceControl.verify();
}
}