fr.paris.lutece.util.sql.Transaction.java Source code

Java tutorial

Introduction

Here is the source code for fr.paris.lutece.util.sql.Transaction.java

Source

/*
 * Copyright (c) 2002-2014, Mairie de 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.sql;

import fr.paris.lutece.portal.service.database.AppConnectionService;
import fr.paris.lutece.portal.service.database.PluginConnectionService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.util.AppException;

import org.apache.commons.lang.StringUtils;

import org.apache.log4j.Logger;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * Transaction
 */
public class Transaction {
    /**
     * Status for opened transactions
     */
    public static final int OPENED = -1;

    /**
     * Status for committed transactions
     */
    public static final int COMMITTED = 0;

    /**
     * Status for roll backed transactions
     */
    public static final int ROLLEDBACK = 1;
    private static final String DEFAULT_MODULE_NAME = "core";
    private static final String LOGGER_DEBUG_SQL = "lutece.debug.sql.";

    /**
     * The last SQL query executed by this transaction
     */
    private String _strSQL = StringUtils.EMPTY;

    /** JDBC Connection */
    private Connection _connection;

    /** Connection Service providing connection from a defined pool */
    private PluginConnectionService _connectionService;

    /** Plugin name */
    private String _strPluginName;

    /** The debug logger */
    private Logger _logger;
    private PreparedStatement _statement;
    private int _nStatus = OPENED;
    private boolean _bAutoCommit;

    /**
     * Constructor
     */
    public Transaction() {
        beginTransaction(null);
    }

    /**
     * Constructor
     * @param plugin The plugin owner of the transaction
     */
    public Transaction(Plugin plugin) {
        beginTransaction(plugin);
    }

    /**
     * Gets a prepared statement
     * @param strSQL The SQL statement
     * @return The prepared statement
     * @throws SQLException If an SQL error occurs
     */
    public PreparedStatement prepareStatement(String strSQL) throws SQLException {
        // Close the previous statement if exists
        if (_statement != null) {
            _statement.close();
        }

        // Get a new statement 
        _strSQL = strSQL;

        if (_connection == null) {
            throw new SQLException("Plugin : '" + _strPluginName
                    + "' - Connection has been closed. The new prepared statement can not be created : " + strSQL);
        }

        _statement = _connection.prepareStatement(_strSQL);

        return _statement;
    }

    /**
     * The current prepared statement
     * @return The current statement
     */
    public PreparedStatement getStatement() {
        return _statement;
    }

    /**
     * Execute the current statement
     * @throws SQLException If an SQL error occurs
     */
    public void executeStatement() throws SQLException {
        _logger.debug("Plugin : '" + _strPluginName + "' - EXECUTE STATEMENT : " + _strSQL);
        _statement.executeUpdate();
    }

    /**
     * Commit the transaction
     */
    public void commit() {
        try {
            if (_connection == null) {
                throw new SQLException("Plugin : '" + _strPluginName
                        + "' - Transaction has already been closed and can not be committed");
            }

            _connection.commit();
            _logger.debug("Plugin : '" + _strPluginName + "' - COMMIT TRANSACTION");
            closeTransaction(COMMITTED);
        } catch (SQLException e) {
            rollback(e);
        }
    }

    /**
     * Rollback the transaction
     */
    public void rollback() {
        rollback(null);
    }

    /**
     * Rollback the transaction
     * @param e The exception that cause the rollback
     */
    public void rollback(Exception e) {
        if (e != null) {
            _logger.error("Transaction Error - Rollback in progress " + e.getMessage(), e.getCause());
        }

        try {
            if (_connection != null) {
                _connection.rollback();
                _logger.debug("Plugin : '" + _strPluginName + "' - ROLLBACK TRANSACTION");
            } else {
                _logger.debug("Plugin : '" + _strPluginName + "' - TRANSACTION HAS ALREADY BEEN ROLLED BACK");
            }
        } catch (SQLException ex) {
            _logger.error("Transaction Error - Rollback error : " + ex.getMessage(), ex.getCause());
        } finally {
            closeTransaction(ROLLEDBACK);
        }
    }

    /**
     * Return the transaction status
     * @return The transaction status
     */
    public int getStatus() {
        return _nStatus;
    }

    /**
     * Get the underlying connection.
     * @return The connection of this transaction. If the transaction has not
     *         begin, then return null.
     */
    protected Connection getConnection() {
        return _connection;
    }

    /**
     * Begin a transaction
     * @param plugin The plugin owner of the transaction
     */
    protected void beginTransaction(Plugin plugin) {
        if (plugin != null) {
            _strPluginName = plugin.getName();
            _connectionService = plugin.getConnectionService();
        } else {
            _strPluginName = DEFAULT_MODULE_NAME;
            _connectionService = AppConnectionService.getDefaultConnectionService();
        }

        if (_connectionService == null) {
            throw new AppException(
                    "Database access error. Please check component installations and db.properties.");
        }

        _logger = Logger.getLogger(LOGGER_DEBUG_SQL + _strPluginName);
        _logger.debug("Plugin : '" + _strPluginName + "' - BEGIN TRANSACTION");

        try {
            _connection = _connectionService.getConnection();

            // Save the autocommit configuration of the connection
            _bAutoCommit = _connection.getAutoCommit();
            _connection.setAutoCommit(false);
        } catch (SQLException e) {
            rollback(e);
        }
    }

    /**
     * Close the transaction
     * @param nStatus The status of the transaction
     */
    private void closeTransaction(int nStatus) {
        _nStatus = nStatus;

        try {
            if (_statement != null) {
                _statement.close();
            }

            // Restore the autocommit configuration of the connection
            if (_connection != null) {
                _connection.setAutoCommit(_bAutoCommit);
            }
        } catch (SQLException ex) {
            _logger.error("Transaction Error - Unable to close transaction " + ex.getMessage(), ex.getCause());
        } finally {
            _connectionService.freeConnection(_connection);
            _connection = null;
        }
    }

    /**
     * Checks that the transaction has been committed (or rolled back) before
     * being destroyed and release all transaction resources (statement,
     * connection, ...) if not. {@inheritDoc }
     */
    @Override
    protected void finalize() throws Throwable {
        if (_nStatus == OPENED) {
            _logger.error("The transaction has not been commited");
            closeTransaction(OPENED);
        }

        super.finalize();
    }
}