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

com.sap.cds.transaction.impl.LocalTransactionManager Maven / Gradle / Ivy

package com.sap.cds.transaction.impl;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Supplier;

import org.slf4j.Logger;

import com.sap.cds.transaction.RollbackException;
import com.sap.cds.transaction.SystemException;
import com.sap.cds.transaction.TransactionException;
import com.sap.cds.transaction.TransactionRequiredException;
import com.sap.cds.transaction.impl.SQLProxyBuilder.Call;
import com.sap.cds.transaction.spi.ContainerTransactionManager;

public class LocalTransactionManager implements ContainerTransactionManager {

	private static final ThreadLocal txHolder = new ThreadLocal<>();
	private final Supplier ds;
	private final Logger logger;
	private final Supplier managedDS;

	public LocalTransactionManager(Logger logger, Supplier ds) {
		this.logger = logger;
		this.ds = ds;
		this.managedDS = createManagedDataSource(ds);
	}

	private static Supplier createManagedDataSource(Supplier ds2) {
		SQLProxyBuilder> builder = SQLProxyBuilder.create(Supplier.class, ds2);

		Call getConnection = () -> {
			Connection conn;
			Tx tx = txHolder.get();
			if (tx == null) {
				conn = ds2.get();
				conn.setAutoCommit(true);
			} else {
				conn = tx.getConnection();
				conn = wrapConnection(conn);
			}

			return conn;
		};

		return builder.handle("get", getConnection).build();
	}

	private static Connection wrapConnection(final Connection conn) {
		return SQLProxyBuilder.create(Connection.class, conn).handle("close", SQLProxyBuilder.NOP).build();
	}

	@Override
	public void begin() {
		if (txHolder.get() != null) {
			throw new TransactionException("there is an active transaction");
		}

		Connection conn = ds.get();
		try {
			try {
				conn.setClientInfo("LOCALE", null);
			} catch (SQLException ex) {
				logger.info("setClientInfo not supported", ex);
				// not supported by all DBs
			}
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			throw new SystemException("exception during setAutoCommit", e);
		}
		txHolder.set(new Tx(conn));
	}

	@Override
	public void commit() {
		Tx tx = getActiveTransaction();
		try {
			tx.commit();
		} finally {
			close(tx);
		}
	}

	@Override
	public void rollback() {
		Tx tx = getActiveTransaction();
		try {
			tx.rollback();
		} finally {
			close(tx);
		}
	}

	private Tx getActiveTransaction() {
		Tx tx = txHolder.get();
		if (tx == null) {
			throw new TransactionRequiredException("no transaction is active");
		}
		return tx;
	}

	private void close(Tx tx) {
		txHolder.remove();
		try {
			tx.getConnection().close();
		} catch (SQLException e) {
			logger.error("Exception while closing connection", e);
		}
	}

	@Override
	public boolean isActive() {
		Tx tx = txHolder.get();

		return (tx != null);
	}

	private static class Tx {

		private final Connection conn;
		private boolean rollbackOnly = false;

		public Tx(Connection conn) {
			this.conn = conn;
		}

		public void commit() {
			try {
				if (rollbackOnly) {
					rollback();
					throw new RollbackException(
							"the transaction was marked for rollback only and has been rolled back");
				}
				conn.commit();
			} catch (SQLException e) {
				throw new RollbackException(e);
			}
		}

		public void rollback() {
			try {
				conn.rollback();
			} catch (SQLException e) {
				throw new SystemException("exception during rollback", e);
			}
		}

		public Connection getConnection() {
			return conn;
		}

		public void setRollbackOnly() {
			rollbackOnly = true;
		}

		public boolean isRollbackOnly() {
			return rollbackOnly;
		}

	}

	public Supplier getConnectionSupplier() {
		return managedDS;
	}

	@Override
	public void setRollbackOnly() {
		Tx tx = getActiveTransaction();
		tx.setRollbackOnly();
	}

	@Override
	public boolean isRollbackOnly() {
		Tx tx = getActiveTransaction();
		return tx.isRollbackOnly();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy