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

br.com.anteros.spring.transaction.AnterosTransactionManager Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright 2012 Anteros Tecnologia
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package br.com.anteros.spring.transaction;

import java.sql.Connection;

import javax.sql.DataSource;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import br.com.anteros.persistence.session.SQLSession;
import br.com.anteros.persistence.session.SQLSessionFactory;
import br.com.anteros.persistence.transaction.Transaction;

/**
 * 
 * @author Edson Martins [email protected]
 *
 */
@SuppressWarnings("serial")
public class AnterosTransactionManager extends AbstractPlatformTransactionManager implements
		ResourceTransactionManager, BeanFactoryAware, InitializingBean {

	private SQLSessionFactory sessionFactory;

	private DataSource dataSource;

	private boolean autodetectDataSource = true;

	private boolean prepareConnection = true;

	private boolean managedSession = false;

	private boolean earlyFlushBeforeCommit = false;

	private SQLExceptionTranslator jdbcExceptionTranslator;

	private SQLExceptionTranslator defaultJdbcExceptionTranslator;

	private BeanFactory beanFactory;

	public AnterosTransactionManager() {
	}

	public AnterosTransactionManager(SQLSessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
		afterPropertiesSet();
	}

	public void setSessionFactory(SQLSessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public SQLSessionFactory getSessionFactory() {
		return this.sessionFactory;
	}

	public void setDataSource(DataSource dataSource) {
		if (dataSource instanceof TransactionAwareDataSourceProxy) {
			this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
		} else {
			this.dataSource = dataSource;
		}
	}

	public DataSource getDataSource() {
		return this.dataSource;
	}

	public void setAutodetectDataSource(boolean autodetectDataSource) {
		this.autodetectDataSource = autodetectDataSource;
	}

	public void setPrepareConnection(boolean prepareConnection) {
		this.prepareConnection = prepareConnection;
	}

	public void setManagedSession(boolean managedSession) {
		this.managedSession = managedSession;
	}

	public void setEarlyFlushBeforeCommit(boolean earlyFlushBeforeCommit) {
		this.earlyFlushBeforeCommit = earlyFlushBeforeCommit;
	}

	public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
		this.jdbcExceptionTranslator = jdbcExceptionTranslator;
	}

	public SQLExceptionTranslator getJdbcExceptionTranslator() {
		return this.jdbcExceptionTranslator;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	@Override
	public void afterPropertiesSet() {
		if (getSessionFactory() == null) {
			throw new IllegalArgumentException("Property 'sessionFactory' is required");
		}

		if (this.autodetectDataSource && getDataSource() == null) {
			DataSource sfds = SQLSessionFactoryUtils.getDataSource(getSessionFactory());
			if (sfds != null) {
				if (logger.isInfoEnabled()) {
					logger.info("Using DataSource [" + sfds
							+ "] of Anteros SQLSessionFactory for AnterosTransactionManager");
				}
				setDataSource(sfds);
			}
		}
	}

	@Override
	public Object getResourceFactory() {
		return getSessionFactory();
	}

	@Override
	protected Object doGetTransaction() {
		AnterosTransactionObject txObject = new AnterosTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());

		SQLSessionHolder sessionHolder = (SQLSessionHolder) TransactionSynchronizationManager
				.getResource(getSessionFactory());
		if (sessionHolder != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Found thread-bound SQLSession [" + SQLSessionFactoryUtils.toString(sessionHolder.getSession())
						+ "] for Anteros transaction");
			}
			txObject.setSessionHolder(sessionHolder);
		} else if (this.managedSession) {
			try {
				SQLSession session = getSessionFactory().getCurrentSession();
				if (logger.isDebugEnabled()) {
					logger.debug("Found Anteros-managed SQLSession [" + SQLSessionFactoryUtils.toString(session)
							+ "] for Spring-managed transaction");
				}
				txObject.setExistingSession(session);
			} catch (Exception ex) {
				throw new DataAccessResourceFailureException(
						"Could not obtain Anteros-managed SQLSession for Spring-managed transaction", ex);
			}
		}

		if (getDataSource() != null) {
			ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager
					.getResource(getDataSource());
			txObject.setConnectionHolder(conHolder);
		}

		return txObject;
	}

	@Override
	protected boolean isExistingTransaction(Object transaction) {
		AnterosTransactionObject txObject = (AnterosTransactionObject) transaction;
		try {
			return (txObject.hasSpringManagedTransaction() || (this.managedSession && txObject
					.hasManagedTransaction()));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	@SuppressWarnings("deprecation")
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		AnterosTransactionObject txObject = (AnterosTransactionObject) transaction;

		if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
			throw new IllegalTransactionStateException(
					"Pre-bound JDBC Connection found! AnterosTransactionManager does not support "
							+ "running within DataSourceTransactionManager if told to manage the DataSource itself. "
							+ "It is recommended to use a single AnterosTransactionManager for all transactions "
							+ "on a single DataSource, no matter whether Anteros or JDBC access.");
		}

		SQLSession session = null;

		try {
			if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
				SQLSession newSession = getSessionFactory().openSession();
				if (logger.isDebugEnabled()) {
					logger.debug("Opened new SQLSession [" + SQLSessionFactoryUtils.toString(newSession)
							+ "] for Anteros transaction");
				}
				txObject.setSession(newSession);
			}

			session = txObject.getSessionHolder().getSession();

			if (this.prepareConnection) {
				if (logger.isDebugEnabled()) {
					logger.debug("Preparing JDBC Connection of Anteros SQLSession ["
							+ SQLSessionFactoryUtils.toString(session) + "]");
				}
				Connection con = session.getConnection();
				Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
				txObject.setPreviousIsolationLevel(previousIsolationLevel);
			} else {
				if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
					throw new InvalidIsolationLevelException(
							"AnterosTransactionManager is not allowed to support custom isolation levels: "
									+ "make sure that its 'prepareConnection' flag is on (the default) and that the "
									+ "Anteros connection release mode is set to 'on_close' (SpringTransactionFactory's default). "
									+ "Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your "
									+ "Anteros properties should *not* include a 'anteros.transaction.factory_class' property!");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Not preparing JDBC Connection of Anteros Session ["
							+ SQLSessionFactoryUtils.toString(session) + "]");
				}
			}

			Transaction hibTx = session.getTransaction();
			hibTx.begin();
			
			int timeout = determineTimeout(definition);

			txObject.getSessionHolder().setTransaction(hibTx);

			if (getDataSource() != null) {
				Connection con = session.getConnection();
				ConnectionHolder conHolder = new ConnectionHolder(con);
				if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
					conHolder.setTimeoutInSeconds(timeout);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Exposing Anteros transaction as JDBC transaction [" + con + "]");
				}
				TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
				txObject.setConnectionHolder(conHolder);
			}

			if (txObject.isNewSessionHolder()) {
				TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
			}
			txObject.getSessionHolder().setSynchronizedWithTransaction(true);
		}

		catch (Throwable ex) {
			if (txObject.isNewSession()) {
				try {
					if (session.getTransaction().isActive()) {
						session.getTransaction().rollback();
					}
				} catch (Throwable ex2) {
					logger.debug("Could not rollback Session after failed transaction begin", ex);
				} finally {
					SQLSessionFactoryUtils.closeSession(session);
				}
			}
			throw new CannotCreateTransactionException("Could not open Anteros SQLSession for transaction", ex);
		}
	}

	@Override
	protected Object doSuspend(Object transaction) {
		AnterosTransactionObject txObject = (AnterosTransactionObject) transaction;
		txObject.setSessionHolder(null);
		SQLSessionHolder sessionHolder = (SQLSessionHolder) TransactionSynchronizationManager
				.unbindResource(getSessionFactory());
		txObject.setConnectionHolder(null);
		ConnectionHolder connectionHolder = null;
		if (getDataSource() != null) {
			connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
		}
		return new SuspendedResourcesHolder(sessionHolder, connectionHolder);
	}

	@Override
	protected void doResume(Object transaction, Object suspendedResources) {
		SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
		if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
			TransactionSynchronizationManager.unbindResource(getSessionFactory());
		}
		TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder());
		if (getDataSource() != null) {
			TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
		}
	}

	@Override
	protected void prepareForCommit(DefaultTransactionStatus status) {
		if (this.earlyFlushBeforeCommit && status.isNewTransaction()) {
		}
	}

	@Override
	protected void doCommit(DefaultTransactionStatus status) {
		AnterosTransactionObject txObject = (AnterosTransactionObject) status.getTransaction();
		if (status.isDebug()) {
			logger.debug("Committing Anteros transaction on SQLSession ["
					+ SQLSessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]");
		}
		try {
			txObject.getSessionHolder().getTransaction().commit();
		} catch (Exception ex) {
			throw new TransactionSystemException("Could not commit Anteros transaction", ex);
		}
	}

	@Override
	protected void doRollback(DefaultTransactionStatus status) {
		AnterosTransactionObject txObject = (AnterosTransactionObject) status.getTransaction();
		if (status.isDebug()) {
			logger.debug("Rolling back Anteros transaction on SQLSession ["
					+ SQLSessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]");
		}
		try {
			txObject.getSessionHolder().getTransaction().rollback();
		} catch (Exception ex) {
			throw new TransactionSystemException("Could not roll back Anteros transaction", ex);
		} finally {
			if (!txObject.isNewSession() && !this.managedSession) {
				try {
					txObject.getSessionHolder().getSession().clear();
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		}
	}

	@Override
	protected void doSetRollbackOnly(DefaultTransactionStatus status) {
		AnterosTransactionObject txObject = (AnterosTransactionObject) status.getTransaction();
		if (status.isDebug()) {
			logger.debug("Setting Anteros transaction on SQLSession ["
					+ SQLSessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "] rollback-only");
		}
		txObject.setRollbackOnly();
	}

	@Override
	@SuppressWarnings("deprecation")
	protected void doCleanupAfterCompletion(Object transaction) {
		AnterosTransactionObject txObject = (AnterosTransactionObject) transaction;

		if (txObject.isNewSessionHolder()) {
			TransactionSynchronizationManager.unbindResource(getSessionFactory());
		}

		if (getDataSource() != null) {
			TransactionSynchronizationManager.unbindResource(getDataSource());
		}

		SQLSession session = txObject.getSessionHolder().getSession();
		boolean closed;
		try {
			closed = session.isClosed();
		} catch (Exception e1) {
			throw new RuntimeException(e1);
		}
		if (this.prepareConnection && !closed) {
			try {
				Connection con = session.getConnection();
				DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
			} catch (Exception ex) {
				logger.debug("Could not access JDBC Connection of Anteros SQLSession", ex);
			}
		}

		if (txObject.isNewSession()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Closing Anteros SQLSession [" + SQLSessionFactoryUtils.toString(session)
						+ "] after transaction");
			}
			SQLSessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Not closing pre-bound Anteros SQLSession [" + SQLSessionFactoryUtils.toString(session)
						+ "] after transaction");
			}
			if (!this.managedSession) {
				/*
				 * Quando implementar método para desconectar session chamar aqui.
				 */
			}
		}
		txObject.getSessionHolder().clear();
	}

	protected synchronized SQLExceptionTranslator getDefaultJdbcExceptionTranslator() {
		if (this.defaultJdbcExceptionTranslator == null) {
			if (getDataSource() != null) {
				this.defaultJdbcExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(getDataSource());
			} else {
				this.defaultJdbcExceptionTranslator = SQLSessionFactoryUtils
						.newJdbcExceptionTranslator(getSessionFactory());
			}
		}
		return this.defaultJdbcExceptionTranslator;
	}

	private class AnterosTransactionObject extends JdbcTransactionObjectSupport {

		private SQLSessionHolder sessionHolder;

		private boolean newSessionHolder;

		private boolean newSession;

		public void setSession(SQLSession session) {
			this.sessionHolder = new SQLSessionHolder(session);
			this.newSessionHolder = true;
			this.newSession = true;
		}

		public void setExistingSession(SQLSession session) {
			this.sessionHolder = new SQLSessionHolder(session);
			this.newSessionHolder = true;
			this.newSession = false;
		}

		public void setSessionHolder(SQLSessionHolder sessionHolder) {
			this.sessionHolder = sessionHolder;
			this.newSessionHolder = false;
			this.newSession = false;
		}

		public SQLSessionHolder getSessionHolder() {
			return this.sessionHolder;
		}

		public boolean isNewSessionHolder() {
			return this.newSessionHolder;
		}

		public boolean isNewSession() {
			return this.newSession;
		}


		public boolean hasSpringManagedTransaction() {
				return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null);
		}

		public boolean hasManagedTransaction() throws Exception {
			return (this.sessionHolder != null && this.sessionHolder.getSession().getTransaction().isActive());
		}

		public void setRollbackOnly() {
			this.sessionHolder.setRollbackOnly();
			if (hasConnectionHolder()) {
				getConnectionHolder().setRollbackOnly();
			}
		}

		@Override
		public boolean isRollbackOnly() {
			return this.sessionHolder.isRollbackOnly()
					|| (hasConnectionHolder() && getConnectionHolder().isRollbackOnly());
		}

		@Override
		public void flush() {
			try {
				this.sessionHolder.getSession().flush();
			} catch (Exception ex) {
				throw new RuntimeException(ex);
			}
		}
	}

	
	private static class SuspendedResourcesHolder {

		private final SQLSessionHolder sessionHolder;

		private final ConnectionHolder connectionHolder;

		private SuspendedResourcesHolder(SQLSessionHolder sessionHolder, ConnectionHolder conHolder) {
			this.sessionHolder = sessionHolder;
			this.connectionHolder = conHolder;
		}

		private SQLSessionHolder getSessionHolder() {
			return this.sessionHolder;
		}

		private ConnectionHolder getConnectionHolder() {
			return this.connectionHolder;
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy