All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avaje.ebeaninternal.server.transaction.JtaTransactionManager Maven / Gradle / Ivy

There is a newer version: 2.8.1
Show newest version
/**
 * Copyright (C) 2009 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.ebeaninternal.server.transaction;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.TransactionSynchronizationRegistry;
import javax.transaction.UserTransaction;

import com.avaje.ebean.LogLevel;
import com.avaje.ebean.config.ExternalTransactionManager;
import com.avaje.ebeaninternal.api.SpiTransaction;

/**
 * Hook into external JTA transaction manager.
 * 
 * @author rbygrave
 */
public class JtaTransactionManager implements ExternalTransactionManager {

    private final static Logger logger = Logger.getLogger(JtaTransactionManager.class.getName());

    private static final String EBEAN_TXN_RESOURCE = "EBEAN_TXN_RESOURCE";
    
    /** 
     * 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 JtaTransactionManager() {
    }

    /**
     * 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();
    }

    private TransactionSynchronizationRegistry getSyncRegistry() {
        try {
            InitialContext ctx = new InitialContext();
            return (TransactionSynchronizationRegistry)ctx.lookup("java:comp/TransactionSynchronizationRegistry");
        } catch (NamingException e){
            throw new PersistenceException(e);
        }
    }
    
    private UserTransaction getUserTransaction() {
        try {
            InitialContext ctx = new InitialContext();
            return (UserTransaction) ctx.lookup("java:comp/UserTransaction");
        } catch (NamingException e){
            // assuming CMT
        	return new DummyUserTransaction();
        }
    }
    
    /**
     * 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() { TransactionSynchronizationRegistry syncRegistry = getSyncRegistry(); SpiTransaction t = (SpiTransaction)syncRegistry.getResource(EBEAN_TXN_RESOURCE); if (t != null){ // we have already seen this transaction return t; } // check current Ebean transaction SpiTransaction currentEbeanTransaction = DefaultTransactionThreadLocal.get(serverName); if (currentEbeanTransaction != null){ // NOT expecting this so log WARNING String msg = "JTA Transaction - no current txn BUT using current Ebean one "+currentEbeanTransaction.getId(); logger.log(Level.WARNING, msg); return currentEbeanTransaction; } UserTransaction ut = getUserTransaction(); if (ut == null){ // no current JTA transaction if (logger.isLoggable(Level.FINE)){ logger.fine("JTA Transaction - no current txn"); } return null; } // This is a transaction that Ebean has not seen before. // "wrap" it in a Ebean specific JtaTransaction String txnId = String.valueOf(System.currentTimeMillis()); JtaTransaction newTrans = new JtaTransaction(txnId, true, LogLevel.NONE, ut, dataSource, transactionManager); // create and register transaction listener JtaTxnListener txnListener = createJtaTxnListener(newTrans); syncRegistry.putResource(EBEAN_TXN_RESOURCE, newTrans); syncRegistry.registerInterposedSynchronization(txnListener); // also put in Ebean ThreadLocal DefaultTransactionThreadLocal.set(serverName, newTrans); return newTrans; } /** * Create a listener to register with JTA 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 JtaTxnListener createJtaTxnListener(SpiTransaction t) { return new JtaTxnListener(transactionManager, t); } private static class DummyUserTransaction implements UserTransaction { public void begin() throws NotSupportedException, SystemException { } public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { } public int getStatus() throws SystemException { return 0; } public void rollback() throws IllegalStateException, SecurityException, SystemException { } public void setRollbackOnly() throws IllegalStateException, SystemException { } public void setTransactionTimeout(int seconds) throws SystemException { } } /** * A JTA Transaction Synchronization that we register to get notified when a * 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 JtaTxnListener implements Synchronization { private final TransactionManager transactionManager; private final SpiTransaction transaction; private final String serverName; private JtaTxnListener(TransactionManager transactionManager, SpiTransaction t){ this.transactionManager = transactionManager; this.transaction = t; this.serverName = transactionManager.getServerName(); } public void beforeCompletion() { // Future note: for JPA2 locking we will // have beforeCommit events to fire } public void afterCompletion(int status) { switch (status) { case Status.STATUS_COMMITTED: if (logger.isLoggable(Level.FINE)){ logger.fine("Jta Txn ["+transaction.getId()+"] committed"); } transactionManager.notifyOfCommit(transaction); // Remove this transaction object as it is completed DefaultTransactionThreadLocal.replace(serverName, null); break; case Status.STATUS_ROLLEDBACK: if (logger.isLoggable(Level.FINE)){ logger.fine("Jta Txn ["+transaction.getId()+"] rollback"); } transactionManager.notifyOfRollback(transaction, null); // Remove this transaction object as it is completed DefaultTransactionThreadLocal.replace(serverName, null); break; default: logger.fine("Jta Txn ["+transaction.getId()+"] status:"+status); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy