ChainedTransactionManager.java

/*
 * Copyright (c) 2002-2022, City of Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.util.jpa.transaction;

import java.util.Collections;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import fr.paris.lutece.util.jpa.JPAConstants;

/**
 *
 * Manages multi transaction
 * 
 * @see <a href="http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html?page=6">spring transactions</a>
 *
 */
public class ChainedTransactionManager implements PlatformTransactionManager
{
    private static final Logger _log = LogManager.getLogger( JPAConstants.JPA_LOGGER );
    private List<PlatformTransactionManager> _transactionManagers;

    /**
     * Builds a new ChainedTransactionManager
     */
    public ChainedTransactionManager( )
    {
        // Ctor
    }

    /**
     * Begin a transaction for all transaction managers {@inheritDoc}
     */
    public TransactionStatus getTransaction( TransactionDefinition definition )
    {
        if ( _transactionManagers.isEmpty( ) )
        {
            return null;
        }

        MultiTransactionStatus mts = new MultiTransactionStatus( _transactionManagers.get( 0 ) );

        if ( !TransactionSynchronizationManager.isSynchronizationActive( ) )
        {
            TransactionSynchronizationManager.initSynchronization( );
            mts.setNewSynchonization( );

            if ( _log.isDebugEnabled( ) )
            {
                _log.debug( "Begin transaction : " + mts.toString( ) );
            }
        }

        for ( PlatformTransactionManager transactionManager : _transactionManagers )
        {
            mts.getTransactionStatuses( ).put( transactionManager, transactionManager.getTransaction( definition ) );
        }

        return mts;
    }

    /**
     *
     * {@inheritDoc}
     */
    public void commit( TransactionStatus status )
    {
        for ( PlatformTransactionManager transactionManager : _transactionManagers )
        {
            TransactionStatus transactionStatus = null;

            try
            {
                transactionStatus = ( (MultiTransactionStatus) status ).getTransactionStatus( transactionManager );
                transactionManager.commit( transactionStatus );
            }
            catch( Exception e )
            {
                _log.error( e.getMessage( ), e );
            }
        }

        if ( _log.isDebugEnabled( ) )
        {
            _log.debug( "Ending transaction : " + status.toString( ) );
        }

        if ( ( (MultiTransactionStatus) status ).isNewSynchonization( ) )
        {
            TransactionSynchronizationManager.clear( );
        }
    }

    /**
     *
     * {@inheritDoc}
     */
    public void rollback( TransactionStatus status )
    {
        for ( PlatformTransactionManager dataSourceManager : _transactionManagers )
        {
            try
            {
                dataSourceManager.rollback( ( ( (MultiTransactionStatus) status ).getTransactionStatus( dataSourceManager ) ) );
            }
            catch( Exception ex )
            {
                _log.error( ex.getMessage( ), ex );
            }
        }

        if ( ( (MultiTransactionStatus) status ).isNewSynchonization( ) )
        {
            TransactionSynchronizationManager.clear( );
        }
    }

    /**
     * "Getter method" pour la variable {@link #_transactionManagers}
     * 
     * @return La variable {@link #_transactionManagers}
     */
    public List<PlatformTransactionManager> getTransactionManagers( )
    {
        return _transactionManagers;
    }

    /**
     * "Setter method" pour la variable {@link #_transactionManagers}
     * 
     * @param managers
     *            La nouvelle valeur de la variable {@link #_transactionManagers}
     */
    public void setTransactionManagers( List<PlatformTransactionManager> managers )
    {
        if ( ( managers == null ) || managers.isEmpty( ) )
        {
            _transactionManagers = Collections.emptyList( );
        }
        else
        {
            _transactionManagers = managers;

            if ( _log.isDebugEnabled( ) )
            {
                _log.debug( "Transaction Managers : " + _transactionManagers.toString( ) );
            }
        }
    }
}