Introduced ContextSourceTransactionManagerDelegate and ContextSourceAndDataSourceTransactionManager.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user