com.avaje.ebean.springsupport.txn.SpringAwareJdbcTransactionManager Maven / Gradle / Ivy
/**
* Copyright (C) 2009 the original author or authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebean.springsupport.txn;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import com.avaje.ebean.config.ExternalTransactionManager;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionThreadLocal;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
/**
* A Spring aware TransactionScopeManager.
*
*
* Will look for Spring transactions and use them if they exist.
*
*
* @since 18.05.2009
* @author E Mc Greal
*/
public class SpringAwareJdbcTransactionManager implements ExternalTransactionManager {
private final static Logger logger = Logger.getLogger(SpringAwareJdbcTransactionManager.class.getName());
/**
* The data source.
*/
private DataSource dataSource;
/**
* The Ebean transaction manager.
*/
private TransactionManager transactionManager;
/**
* The EbeanServer name.
*/
private String serverName;
/**
* Instantiates a new spring aware transaction scope manager.
*/
public SpringAwareJdbcTransactionManager() {
}
/**
* Initialise this with the Ebean internal transaction manager.
*/
public void setTransactionManager(Object txnMgr) {
// RB: At this stage not exposing TransactionManager to
// the public API and hence the Object type and casting here
this.transactionManager = (TransactionManager) txnMgr;
this.dataSource = transactionManager.getDataSource();
this.serverName = transactionManager.getServerName();
}
/**
* Looks for a current Spring managed transaction and wraps/returns that as a Ebean transaction.
*
* Returns null if there is no current spring transaction (lazy loading outside a spring txn etc).
*
*/
public Object getCurrentTransaction() {
// Get the current Spring ConnectionHolder associated to the current spring managed transaction
ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (holder == null || !holder.isSynchronizedWithTransaction()) {
// no current Spring transaction
SpiTransaction currentEbeanTransaction = DefaultTransactionThreadLocal.get(serverName);
if (currentEbeanTransaction != null){
// NOT expecting this so log WARNING
String msg = "SpringTransaction - no current spring txn BUT using current Ebean one "+currentEbeanTransaction.getId();
logger.log(Level.WARNING, msg);
} else if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "Spring Txn - no current transaction ");
}
return currentEbeanTransaction;
}
SpringTxnListener springTxnLister = getSpringTxnListener();
if (springTxnLister != null){
// we have already seen this transaction
return springTxnLister.getTransaction();
} else {
// This is a new spring transaction that we have not seen before.
// "wrap" it in a SpringJdbcTransaction for use with Ebean
SpringJdbcTransaction newTrans = new SpringJdbcTransaction(holder, transactionManager);
// Create and register a Spring TransactionSynchronization for this transaction
springTxnLister = createSpringTxnListener(newTrans);
TransactionSynchronizationManager.registerSynchronization(springTxnLister);
// also put in Ebean ThreadLocal
DefaultTransactionThreadLocal.set(serverName, newTrans);
return newTrans;
}
}
/**
* Search for our specific transaction listener.
*
* If it exists then we have already seen and "wrapped" this transaction.
*
*/
private SpringTxnListener getSpringTxnListener() {
if (TransactionSynchronizationManager.isSynchronizationActive()){
List synchronizations = TransactionSynchronizationManager.getSynchronizations();
if (synchronizations != null){
// search for our specific listener
for (int i = 0; i < synchronizations.size(); i++) {
if (synchronizations.get(i) instanceof SpringTxnListener){
return (SpringTxnListener)synchronizations.get(i);
}
}
}
}
return null;
}
/**
* Create a listener to register with Spring to enable Ebean to be
* notified when transactions commit and rollback.
*
* This is used by Ebean to notify it's appropriate listeners and maintain it's server
* cache etc.
*
*/
private SpringTxnListener createSpringTxnListener(SpringJdbcTransaction t) {
return new SpringTxnListener(transactionManager, t);
}
/**
* A Spring TransactionSynchronization that we register with Spring to get
* notified when a Spring managed transaction has been committed or rolled
* back.
*
* When Ebean is notified (of the commit/rollback) it can then manage its
* cache, notify BeanPersistListeners etc.
*
*/
private static class SpringTxnListener extends TransactionSynchronizationAdapter {
private final TransactionManager transactionManager;
private final SpringJdbcTransaction transaction;
private final String serverName;
private SpringTxnListener(TransactionManager transactionManager, SpringJdbcTransaction t){
this.transactionManager = transactionManager;
this.transaction = t;
this.serverName = transactionManager.getServerName();
}
/**
* Return the associated Ebean wrapped transaction.
*/
public SpringJdbcTransaction getTransaction() {
return transaction;
}
@Override
public void beforeCommit(boolean readOnly) {
// Future note: for JPA2 locking we will
// have beforeCommit events to fire
}
@Override
public void afterCompletion(int status) {
switch (status) {
case STATUS_COMMITTED:
if (logger.isLoggable(Level.FINE)){
logger.fine("Spring Txn ["+transaction.getId()+"] committed");
}
transactionManager.notifyOfCommit(transaction);
break;
case STATUS_ROLLED_BACK:
if (logger.isLoggable(Level.FINE)){
logger.fine("Spring Txn ["+transaction.getId()+"] rollback");
}
transactionManager.notifyOfRollback(transaction, null);
break;
default:
// this should never happen
String msg = "Invalid status "+status;
throw new PersistenceException(msg);
}
// Remove this transaction object as it is completed
DefaultTransactionThreadLocal.replace(serverName, null);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy