Introduced ContextSourceTransactionManagerDelegate and ContextSourceAndDataSourceTransactionManager.

This commit is contained in:
Mattias Arthursson
2007-02-12 17:29:51 +00:00
parent 3fffbf1408
commit 760440ae6e
2 changed files with 351 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
package org.springframework.ldap.transaction.core;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.ldap.core.ContextSource;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionSuspensionNotSupportedException;
import org.springframework.transaction.support.DefaultTransactionStatus;
/**
* A Transaction Manager to manage LDAP and JDBC operations within the same
* transaction. Note that even though the same logical transaction is used, this
* is <b>not</b> a JTA XA transaction; no two-phase commit will be performed,
* and thus commit and rollback may result in unexpected results.
*
* @author Mattias Arthursson
*/
public class ContextSourceAndDataSourceTransactionManager extends
DataSourceTransactionManager {
private static final long serialVersionUID = 6832868697460384648L;
private ContextSourceTransactionManagerDelegate ldapManagerDelegate = new ContextSourceTransactionManagerDelegate();
public ContextSourceAndDataSourceTransactionManager() {
super();
// Override the default behaviour.
setNestedTransactionAllowed(false);
}
/*
* (non-Javadoc)
*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction(java.lang.Object)
*/
protected boolean isExistingTransaction(Object transaction) {
ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) transaction;
return super.isExistingTransaction(actualTransactionObject
.getDataSourceTransactionObject());
}
/*
* (non-Javadoc)
*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction()
*/
protected Object doGetTransaction() throws TransactionException {
Object dataSourceTransactionObject = super.doGetTransaction();
Object contextSourceTransactionObject = ldapManagerDelegate
.doGetTransaction();
return new ContextSourceAndDataSourceTransactionObject(
contextSourceTransactionObject, dataSourceTransactionObject);
}
/*
* (non-Javadoc)
*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin(java.lang.Object,
* org.springframework.transaction.TransactionDefinition)
*/
protected void doBegin(Object transaction, TransactionDefinition definition)
throws TransactionException {
ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) transaction;
super.doBegin(actualTransactionObject.getDataSourceTransactionObject(),
definition);
ldapManagerDelegate.doBegin(actualTransactionObject
.getLdapTransactionObject(), definition);
}
/*
* (non-Javadoc)
*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCleanupAfterCompletion(java.lang.Object)
*/
protected void doCleanupAfterCompletion(Object transaction) {
ContextSourceAndDataSourceTransactionObject actualTransactionObject = (ContextSourceAndDataSourceTransactionObject) transaction;
super.doCleanupAfterCompletion(actualTransactionObject
.getDataSourceTransactionObject());
ldapManagerDelegate.doCleanupAfterCompletion(actualTransactionObject
.getLdapTransactionObject());
}
/*
* (non-Javadoc)
*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus)
*/
protected void doCommit(DefaultTransactionStatus status)
throws TransactionException {
try {
super.doCommit(new DataSourceTransactionStatus(status
.getTransaction(), status.isNewTransaction(), status
.isNewSynchronization(), status.isReadOnly(), status
.isDebug(), status.getSuspendedResources()));
} catch (TransactionException ex) {
if(isRollbackOnCommitFailure()){
// If we are to rollback on commit failure, just rethrow the
throw ex;
}
}
ldapManagerDelegate.doCommit(new ContextSourceTransactionStatus(status
.getTransaction(), status.isNewTransaction(), status
.isNewSynchronization(), status.isReadOnly(), status.isDebug(),
status.getSuspendedResources()));
}
/*
* (non-Javadoc)
*
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus)
*/
protected void doRollback(DefaultTransactionStatus status)
throws TransactionException {
super.doRollback(new DataSourceTransactionStatus(status
.getTransaction(), status.isNewTransaction(), status
.isNewSynchronization(), status.isReadOnly(), status.isDebug(),
status.getSuspendedResources()));
ldapManagerDelegate.doRollback(new ContextSourceTransactionStatus(
status.getTransaction(), status.isNewTransaction(), status
.isNewSynchronization(), status.isReadOnly(), status
.isDebug(), status.getSuspendedResources()));
}
public ContextSource getContextSource() {
return ldapManagerDelegate.getContextSource();
}
public void setContextSource(ContextSource contextSource) {
ldapManagerDelegate.setContextSource(contextSource);
}
protected void setRenamingStrategy(
TempEntryRenamingStrategy renamingStrategy) {
ldapManagerDelegate.setRenamingStrategy(renamingStrategy);
}
private class ContextSourceAndDataSourceTransactionObject {
private Object ldapTransactionObject;
private Object dataSourceTransactionObject;
public ContextSourceAndDataSourceTransactionObject(
Object ldapTransactionObject, Object dataSourceTransactionObject) {
this.ldapTransactionObject = ldapTransactionObject;
this.dataSourceTransactionObject = dataSourceTransactionObject;
}
public Object getDataSourceTransactionObject() {
return dataSourceTransactionObject;
}
public Object getLdapTransactionObject() {
return ldapTransactionObject;
}
}
protected Object doSuspend(Object transaction) throws TransactionException {
throw new TransactionSuspensionNotSupportedException(
"Transaction manager [" + getClass().getName()
+ "] does not support transaction suspension");
}
protected void doResume(Object transaction, Object suspendedResources)
throws TransactionException {
throw new TransactionSuspensionNotSupportedException(
"Transaction manager [" + getClass().getName()
+ "] does not support transaction suspension");
}
private class DataSourceTransactionStatus extends DefaultTransactionStatus {
public DataSourceTransactionStatus(Object transaction,
boolean newTransaction, boolean newSynchronization,
boolean readOnly, boolean debug, Object suspendedResources) {
super(((ContextSourceAndDataSourceTransactionObject) transaction)
.getDataSourceTransactionObject(), newTransaction,
newSynchronization, readOnly, debug, suspendedResources);
}
}
private class ContextSourceTransactionStatus extends
DefaultTransactionStatus {
public ContextSourceTransactionStatus(Object transaction,
boolean newTransaction, boolean newSynchronization,
boolean readOnly, boolean debug, Object suspendedResources) {
super(((ContextSourceAndDataSourceTransactionObject) transaction)
.getLdapTransactionObject(), newTransaction,
newSynchronization, readOnly, debug, suspendedResources);
}
}
}

View File

@@ -0,0 +1,148 @@
package org.springframework.ldap.transaction.core;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ldap.core.ContextSource;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* This delegate performs all the work for the
* {@link ContextSourceTransactionManager}. The work is delegated in order to
* be able to perform the exact same work for the LDAP part in
* {@link ContextSourceAndDataSourceTransactionManager}.
*
* @author Mattias Arthursson
* @see ContextSourceTransactionManager
* @see ContextSourceAndDataSourceTransactionManager
*/
public class ContextSourceTransactionManagerDelegate {
private static Log log = LogFactory
.getLog(ContextSourceTransactionManager.class);
private ContextSource contextSource;
private TempEntryRenamingStrategy renamingStrategy = new DefaultTempEntryRenamingStrategy();
/**
* Set the ContextSource to work on. Even though the actual ContextSource
* sent to the LdapTemplate instance should be a
* {@link TransactionAwareContextSourceProxy}, the one sent to this method
* should be the target of that proxy. If it is not, the target will be
* extracted and used instead.
*
* @param contextSource
* the ContextSource to work on.
*/
public void setContextSource(ContextSource contextSource) {
if (contextSource instanceof TransactionAwareContextSourceProxy) {
TransactionAwareContextSourceProxy proxy = (TransactionAwareContextSourceProxy) contextSource;
this.contextSource = proxy.getTarget();
} else {
this.contextSource = contextSource;
}
}
public ContextSource getContextSource() {
return contextSource;
}
/*
* (non-Javadoc)
*
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doGetTransaction()
*/
public Object doGetTransaction() throws TransactionException {
DirContextHolder contextHolder = (DirContextHolder) TransactionSynchronizationManager
.getResource(this.contextSource);
ContextSourceTransactionObject txObject = new ContextSourceTransactionObject(
contextHolder);
return txObject;
}
/*
* (non-Javadoc)
*
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doBegin(java.lang.Object,
* org.springframework.transaction.TransactionDefinition)
*/
public void doBegin(Object transaction, TransactionDefinition definition)
throws TransactionException {
ContextSourceTransactionObject txObject = (ContextSourceTransactionObject) transaction;
if (txObject.getContextHolder() == null) {
DirContext newCtx = getContextSource().getReadOnlyContext();
DirContextHolder contextHolder = new DirContextHolder(newCtx,
renamingStrategy);
txObject.setContextHolder(contextHolder);
TransactionSynchronizationManager.bindResource(getContextSource(),
contextHolder);
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus)
*/
public void doCommit(DefaultTransactionStatus status)
throws TransactionException {
ContextSourceTransactionObject txObject = (ContextSourceTransactionObject) status
.getTransaction();
txObject.getContextHolder().getTransactionDataManager().commit();
}
/*
* (non-Javadoc)
*
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus)
*/
public void doRollback(DefaultTransactionStatus status)
throws TransactionException {
ContextSourceTransactionObject txObject = (ContextSourceTransactionObject) status
.getTransaction();
txObject.getContextHolder().getTransactionDataManager().rollback();
}
/*
* (non-Javadoc)
*
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#doCleanupAfterCompletion(java.lang.Object)
*/
public void doCleanupAfterCompletion(Object transaction) {
log.debug("Cleaning stored ContextHolder");
TransactionSynchronizationManager.unbindResource(contextSource);
ContextSourceTransactionObject txObject = (ContextSourceTransactionObject) transaction;
DirContext ctx = txObject.getContextHolder().getCtx();
try {
log.debug("Closing target context");
ctx.close();
} catch (NamingException e) {
e.printStackTrace();
}
txObject.getContextHolder().clear();
}
/**
* Set the {@link TempEntryRenamingStrategy} to be used when renaming
* temporary entries in unbind and rebind operations. Default value is a
* {@link DefaultTempEntryRenamingStrategy}.
*
* @param renamingStrategy
* the {@link TempEntryRenamingStrategy} to use.
*/
public void setRenamingStrategy(TempEntryRenamingStrategy renamingStrategy) {
this.renamingStrategy = renamingStrategy;
}
}