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

org.unidal.dal.jdbc.transaction.DefaultTransactionManager Maven / Gradle / Ivy

The newest version!
package org.unidal.dal.jdbc.transaction;

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

import org.unidal.cat.Cat;
import org.unidal.dal.jdbc.DalRuntimeException;
import org.unidal.dal.jdbc.datasource.DataSource;
import org.unidal.dal.jdbc.datasource.DataSourceManager;
import org.unidal.dal.jdbc.engine.QueryContext;
import org.unidal.dal.jdbc.mapping.TableProvider;
import org.unidal.dal.jdbc.mapping.TableProviderManager;
import org.unidal.lookup.annotation.Inject;
import org.unidal.lookup.annotation.Named;
import org.unidal.lookup.logging.LogEnabled;
import org.unidal.lookup.logging.Logger;

@Named(type = TransactionManager.class)
public class DefaultTransactionManager implements TransactionManager, LogEnabled {
   private static ThreadLocalTransactionInfo m_threadLocalData = new ThreadLocalTransactionInfo();

   @Inject
   private TableProviderManager m_tableProviderManager;

   @Inject
   private DataSourceManager m_dataSourceManager;

   private Logger m_logger;

   public void closeConnection() {
      TransactionInfo trxInfo = m_threadLocalData.get();

      if (trxInfo.isInTransaction()) {
         // do nothing when in transaction
      } else {
         try {
            trxInfo.reset();
         } catch (SQLException e) {
            m_logger.warn("Error when closing Connection, message: " + e, e);
         }
      }
   }

   private void closeConnection(Connection connection) {
      if (connection != null) {
         try {
            connection.close();
         } catch (SQLException e) {
            // ignore it
         }
      }
   }

   public void commitTransaction() {
      TransactionInfo trxInfo = m_threadLocalData.get();

      if (!trxInfo.isInTransaction()) {
         throw new DalRuntimeException("There is no active transaction open, can't commit");
      }

      try {
         if (trxInfo.getConnection() != null) {
            trxInfo.getConnection().commit();
         }
      } catch (SQLException e) {
         throw new DalRuntimeException("Unable to commit transaction, message: " + e, e);
      } finally {
         try {
            trxInfo.reset();
         } catch (SQLException e) {
            e.printStackTrace();
         }
      }
   }

   public void enableLogging(Logger logger) {
      m_logger = logger;
   }

   public Connection getConnection(QueryContext ctx) {
      String logicalName = ctx.getEntityInfo().getLogicalName();
      TableProvider tableProvider = m_tableProviderManager.getTableProvider(logicalName);
      String dataSourceName = tableProvider.getDataSourceName(ctx.getQueryHints(), logicalName);
      TransactionInfo trxInfo = m_threadLocalData.get();

      ctx.setDataSourceName(dataSourceName);

      if (trxInfo.isInTransaction()) {
         if (dataSourceName.equals(trxInfo.getDataSourceName())) {
            return trxInfo.getConnection();
         } else {
            throw new DalRuntimeException("Only one datasource can participate in a transaction. Now: "
                  + trxInfo.getDataSourceName() + ", you provided: " + dataSourceName);
         }
      } else { // Not in transaction
         DataSource dataSource = m_dataSourceManager.getDataSource(dataSourceName);
         Connection connection = null;
         SQLException exception = null;

         try {
            connection = trxInfo.getConnection();

            if (connection == null) {
               connection = dataSource.getConnection();
            }

            connection.setAutoCommit(true);
         } catch (SQLException e) {
            exception = e;
         }

         // retry once if pooled connection is closed by server side
         if (exception != null) {
            closeConnection(connection);

            m_logger.warn(String.format("Iffy database(%s) connection closed, try to reconnect.", dataSourceName),
                  exception);

            try {
               connection = dataSource.getConnection();
               connection.setAutoCommit(true);
               exception = null;
            } catch (SQLException e) {
               closeConnection(connection);
               m_logger.warn(String.format("Unable to reconnect to database(%s).", dataSourceName), e);
            }
         }

         if (exception != null) {
            throw new DalRuntimeException("Error when getting connection from DataSource(" + dataSourceName
                  + "), message: " + exception, exception);
         } else {
            trxInfo.setConnection(connection);
            trxInfo.setDataSourceName(dataSourceName);
            trxInfo.setInTransaction(false);
            return connection;
         }
      }
   }

   public boolean isInTransaction() {
      TransactionInfo trxInfo = m_threadLocalData.get();

      return trxInfo.isInTransaction();
   }

   public void rollbackTransaction() {
      TransactionInfo trxInfo = m_threadLocalData.get();

      if (!trxInfo.isInTransaction()) {
         throw new DalRuntimeException("There is no active transaction open, can't rollback");
      }

      try {
         if (trxInfo.getConnection() != null) {
            trxInfo.getConnection().rollback();
         }
      } catch (SQLException e) {
         throw new DalRuntimeException("Unable to rollback transaction, message: " + e, e);
      } finally {
         try {
            trxInfo.reset();
         } catch (SQLException e) {
            Cat.logError(e);
         }
      }
   }

   public void startTransaction(String datasource) {
      TransactionInfo trxInfo = m_threadLocalData.get();

      if (trxInfo.isInTransaction()) {
         throw new DalRuntimeException(
               "Can't start transaction while another transaction has not been committed or rollbacked!");
      } else {
         DataSource ds = m_dataSourceManager.getDataSource(datasource);
         Connection connection = null;

         try {
            connection = ds.getConnection();
            connection.setAutoCommit(false);
            trxInfo.setConnection(connection);
            trxInfo.setDataSourceName(datasource);
            trxInfo.setInTransaction(true);
         } catch (SQLException e) {
            closeConnection(connection);
            throw new DalRuntimeException("Error when getting connection from DataSource(" + datasource
                  + "), message: " + e, e);
         }
      }
   }

   static class ThreadLocalTransactionInfo extends ThreadLocal {
      @Override
      protected TransactionInfo initialValue() {
         return new TransactionInfo();
      }
   }

   static class TransactionInfo {
      private String m_dataSourceName;

      private Connection m_connection;

      private boolean m_inTransaction;

      public Connection getConnection() {
         return m_connection;
      }

      public String getDataSourceName() {
         return m_dataSourceName;
      }

      public boolean isInTransaction() {
         try {
            if (m_connection != null && m_connection.isClosed()) {
               return false;
            }
         } catch (SQLException e) {
            Cat.logError(e);
         }

         return m_inTransaction;
      }

      public void reset() throws SQLException {
         if (m_connection != null) {
            m_connection.close();
         }

         m_connection = null;
         m_dataSourceName = null;
         m_inTransaction = false;
         m_threadLocalData.remove();
      }

      public void setConnection(Connection connection) {
         m_connection = connection;
      }

      public void setDataSourceName(String dataSourceName) {
         m_dataSourceName = dataSourceName;
      }

      public void setInTransaction(boolean inTransaction) {
         m_inTransaction = inTransaction;
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy