DAOUtilTransactionManager.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.portal.service.database;

import fr.paris.lutece.portal.service.plugin.PluginEvent;
import fr.paris.lutece.portal.service.plugin.PluginEventListener;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.util.sql.TransactionManager;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import java.io.PrintWriter;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;

import javax.sql.DataSource;

/**
 * DataSource transaction manager. This transaction manager use Spring transaction manager, and do not use {@link TransactionManager}. This transaction manager
 * allow to use transaction in a given plugin, but does not influence other plugins. To create transactions throw multiple plugins, use
 * {@link LuteceTransactionManager}
 */
public class DAOUtilTransactionManager extends DataSourceTransactionManager implements PluginEventListener
{
    private static final long serialVersionUID = -654531540978261621L;
    private transient Logger _logger = LogManager.getLogger( "lutece.debug.sql.tx" );
    private String _strPluginName;
    private boolean _bInit;

    /**
     * Registers the listener to {@link PluginService}.
     */
    public DAOUtilTransactionManager( )
    {
        PluginService.registerPluginEventListener( this );
    }

    /**
     * Gets the plugin name
     * 
     * @return the plugin name
     */
    public String getPluginName( )
    {
        return _strPluginName;
    }

    /**
     * Sets the plugin name
     * 
     * @param strPluginName
     *            the plugin name
     */
    public void setPluginName( String strPluginName )
    {
        _strPluginName = strPluginName;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void processPluginEvent( PluginEvent event )
    {
        if ( getPluginName( ).equals( event.getPlugin( ).getName( ) ) )
        {
            if ( ( event.getEventType( ) == PluginEvent.PLUGIN_INSTALLED ) || ( event.getEventType( ) == PluginEvent.PLUGIN_POOL_CHANGED ) )
            {
                if ( StringUtils.isNotBlank( event.getPlugin( ).getDbPoolName( ) )
                        && !AppConnectionService.NO_POOL_DEFINED.equals( event.getPlugin( ).getDbPoolName( ) ) )
                {
                    try
                    {
                        getLogger( ).debug( "DAOUtilTransactionManager changed datasource status..." );
                        setDataSource( AppConnectionService.getPoolManager( ).getDataSource( event.getPlugin( ).getDbPoolName( ) ) );
                        _bInit = true;
                    }
                    catch( Exception ex )
                    {
                        _bInit = false;
                        getLogger( ).error(
                                "An error occured getting pool for DAOUtilTransactionManager for plugin {} , please check plugin is activated and pool is correctly set : {}",
                                event.getPlugin( ).getName( ), ex.getMessage( ), ex );
                    }
                }
                else
                {
                    getLogger( ).debug( "Pool for plugin {} is set to null, clearing transaction manager", event.getPlugin( ).getName( ) );
                    setDataSource( null );
                    _bInit = false;
                }
            }
            else
                if ( event.getEventType( ) == PluginEvent.PLUGIN_UNINSTALLED )
                {
                    setDataSource( null );
                    _bInit = false;
                }
        }
    }

    private Logger getLogger( )
    {
        if ( _logger == null )
        {
            _logger = LogManager.getLogger( "lutece.debug.sql.tx" );
        }
        return _logger;
    }

    /**
     * Returns a "fake" datasource to avoid spring checks failure when pool are not initialized. Returns the current datasource otherwise.
     *
     * @return the data source
     */
    @Override
    public DataSource getDataSource( )
    {
        if ( _bInit )
        {
            return super.getDataSource( );
        }

        /**
         * Empty datasource
         */
        return new EmptyDataSource( );
    }

    /**
     * Empty datasource
     */
    private static class EmptyDataSource implements DataSource
    {
        @Override
        public <T> T unwrap( Class<T> iface ) throws SQLException
        {
            return null;
        }

        @Override
        public boolean isWrapperFor( Class<?> iface ) throws SQLException
        {
            return false;
        }

        @Override
        public void setLoginTimeout( int seconds ) throws SQLException
        {
            // Do nothing
        }

        @Override
        public void setLogWriter( PrintWriter out ) throws SQLException
        {
            // Do nothing
        }

        @Override
        public int getLoginTimeout( ) throws SQLException
        {
            return 0;
        }

        @Override
        public PrintWriter getLogWriter( ) throws SQLException
        {
            return null;
        }

        @Override
        public Connection getConnection( String username, String password ) throws SQLException
        {
            return null;
        }

        @Override
        public Connection getConnection( ) throws SQLException
        {
            return null;
        }

        /**
         * Get the parent logger
         * 
         * @return the logger
         * @throws SQLFeatureNotSupportedException
         *             if this method is not supported
         */
        public java.util.logging.Logger getParentLogger( ) throws SQLFeatureNotSupportedException
        {
            throw new SQLFeatureNotSupportedException( );
        }
    }
}