Sample class jdbc.wrapper.XADataSourceProxy

Sample code for the JDBC integration topic

/*
 * ============================================================================
 * (C) Copyright IBM Corporation 2006, 2007.  All Rights Reserved.
 * 
 * You may only copy, modify, and distribute these samples internally.
 * These samples have not been tested under all conditions and are provided
 * to you by IBM without obligation of support of any kind.
 *
 * IBM PROVIDES THESE SAMPLES "AS IS" SUBJECT TO ANY STATUTORY WARRANTIES THAT
 * CANNOT BE EXCLUDED. IBM MAKES NO WARRANTIES OR CONDITIONS, EITHER EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR
 * CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
 * NON-INFRINGEMENT REGARDING THESE SAMPLES OR TECHNICAL SUPPORT, IF ANY.
 * ============================================================================
 */
package jdbc.wrapper;

import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.Synchronization;
import javax.transaction.Transaction;
import javax.transaction.xa.XAResource;
import jdbc.wrapper.XAResourceInfoImpl;
import com.ibm.tx.jta.DestroyXAResourceException;
import com.ibm.tx.jta.ExtendedTransactionManager;
import com.ibm.tx.jta.TransactionManagerFactory;
import com.ibm.tx.jta.XAResourceFactory;
import com.ibm.tx.jta.XAResourceNotAvailableException;

public class XADataSourceProxy implements DataSource {
	private static final ExtendedTransactionManager TRANSACTION_MANAGER = TransactionManagerFactory
			.getTransactionManager();

	private XAResourceInfoImpl _xaResourceInfo;
	private XADataSource _xaDataSource;

	private ArrayList<PooledConnection> _availableConnections;
	private Map<Transaction, PooledConnection> _inUseConnectionsByTransaction;

	private static final TraceComponent _tc = Tr.register(
			XADataSourceProxy.class, null, null);

	private boolean _autoCommit;
	private boolean _autoCommitOverride = false;
	public void setAutoCommit(boolean c) {
		_autoCommit = c;
		_autoCommitOverride = true;
	}
	public void close(){}

	public XADataSourceProxy(String xaDataSourceClassName,
			Properties configurationProperties) {
		_xaResourceInfo = new XAResourceInfoImpl(xaDataSourceClassName, configurationProperties);
		_xaDataSource = _xaResourceInfo.createDataSource();
		_availableConnections = new ArrayList<PooledConnection>();
		_inUseConnectionsByTransaction = new HashMap<Transaction, PooledConnection>();
	}

	public Connection getConnection() throws SQLException {
		if (_tc.isEntryEnabled())
			Tr.entry(_tc, "getConnection", this);
		XAConnection xaConnection;
		if (_availableConnections.isEmpty()) {
			xaConnection = null;
		} else {
			xaConnection = (XAConnection) _availableConnections.remove(0);
		}

		// A connection has been requested by the application. If there is
		// a transaction on the thread, enlist the connection in the
		// transaction.
		try {
			final Transaction transaction = TRANSACTION_MANAGER
					.getTransaction();
			final Connection connection;
			if (transaction != null) {
				if (xaConnection == null) {
					xaConnection = (XAConnection) _inUseConnectionsByTransaction.get(transaction);
					if (xaConnection == null) {
						xaConnection = _xaDataSource.getXAConnection();
						_inUseConnectionsByTransaction.put(transaction,	xaConnection);
						final XAResource xaResource = xaConnection.getXAResource();
						if (xaResource instanceof Serializable) {
							transaction.enlistResource(xaResource);
						} else {
							// Enlisting NON-serializable resource
							final int recoveryId = TRANSACTION_MANAGER
									.registerResourceInfo(XADataSourceProxyFactory.class.getName(), 
									                      _xaResourceInfo);
							TRANSACTION_MANAGER.enlist(xaResource, recoveryId);
						}

						// Register a synchronization with the transaction. Once
						// that transaction has completed we will be notified 
						// via afterCompletion at which point we can free-up the 
						// connection and make it available for re-use.
						transaction.registerSynchronization(new SynchronizationImpl(
															xaConnection, transaction));
					} else {
						// Re-using connection within same transaction.
					}
				}
				connection = new ConnectionWrapper(xaConnection.getConnection());
				if (_autoCommitOverride) {
					connection.setAutoCommit(_autoCommit);
				}
			} else {
				if (xaConnection == null) {
					xaConnection = _xaDataSource.getXAConnection();
				}
				connection = xaConnection.getConnection();
			}
			return connection;
		} catch (Exception e) {
			e.printStackTrace();
			throw new SQLException();
		}
	}

	public Connection getConnection(String username, String password)
			throws SQLException {
		throw new UnsupportedOperationException();
	}

	public PrintWriter getLogWriter() throws SQLException {
		return _xaDataSource.getLogWriter();
	}

	public int getLoginTimeout() throws SQLException {
		return _xaDataSource.getLoginTimeout();
	}

	public void setLogWriter(PrintWriter out) throws SQLException {
		_xaDataSource.setLogWriter(out);
	}

	public void setLoginTimeout(int seconds) throws SQLException {
		_xaDataSource.setLoginTimeout(seconds);
	}

	public static class XADataSourceProxyFactory implements XAResourceFactory {
		public void destroyXAResource(XAResource xaRes)
				throws DestroyXAResourceException {
		}
		public XAResource getXAResource(Serializable xaresinfo)
				throws XAResourceNotAvailableException {
			try {
				final XAResourceInfoImpl xaResourceInfo = (XAResourceInfoImpl) xaresinfo;
				return xaResourceInfo.createDataSource().getXAConnection().getXAResource();
			} catch (Exception e) {
				throw new XAResourceNotAvailableException(e);
			}
		}
	}

	private class ConnectionWrapper implements Connection {
		private Connection _connection;

		public ConnectionWrapper(Connection connection) {
			_connection = connection;
		}

		public void clearWarnings() throws SQLException {
			_connection.clearWarnings();
		}

		public void close() throws SQLException {

		}

        // Don't call commit() on the ConnectionWrapper - it will be done via the transaction manager
		public void commit() throws SQLException {
			throw new SQLException();
		}

		public Statement createStatement() throws SQLException {
			return _connection.createStatement();
		}

		public Statement createStatement(int resultSetType,
				int resultSetConcurrency) throws SQLException {
			return _connection.createStatement(resultSetType,
					resultSetConcurrency);
		}

		public Statement createStatement(int resultSetType,
				int resultSetConcurrency, int resultSetHoldability)
				throws SQLException {
			return _connection.createStatement(resultSetType,
					resultSetConcurrency, resultSetHoldability);
		}

		public boolean getAutoCommit() throws SQLException {
			return _connection.getAutoCommit();
		}

		public String getCatalog() throws SQLException {
			return _connection.getCatalog();
		}

		public int getHoldability() throws SQLException {
			return _connection.getHoldability();
		}

		public DatabaseMetaData getMetaData() throws SQLException {
			return _connection.getMetaData();
		}

		public int getTransactionIsolation() throws SQLException {
			return _connection.getTransactionIsolation();
		}

		public Map<String, Class<?>> getTypeMap() throws SQLException {
			return _connection.getTypeMap();
		}

		public SQLWarning getWarnings() throws SQLException {
			return _connection.getWarnings();
		}

		public boolean isClosed() throws SQLException {
			return _connection.isClosed();
		}

		public boolean isReadOnly() throws SQLException {
			return _connection.isReadOnly();
		}

		public String nativeSQL(String sql) throws SQLException {

			return _connection.nativeSQL(sql);
		}

		public CallableStatement prepareCall(String sql) throws SQLException {
			return _connection.prepareCall(sql);
		}

		public CallableStatement prepareCall(String sql, int resultSetType,
				int resultSetConcurrency) throws SQLException {
			return _connection.prepareCall(sql, resultSetType,
					resultSetConcurrency);
		}

		public CallableStatement prepareCall(String sql, int resultSetType,
				int resultSetConcurrency, int resultSetHoldability)
				throws SQLException {
			return _connection.prepareCall(sql, resultSetType,
					resultSetConcurrency);
		}

		public PreparedStatement prepareStatement(String sql)
				throws SQLException {
			return _connection.prepareStatement(sql);
		}

		public PreparedStatement prepareStatement(String sql,
				int autoGeneratedKeys) throws SQLException {
			return _connection.prepareStatement(sql, autoGeneratedKeys);
		}

		public PreparedStatement prepareStatement(String sql,
				int[] columnIndexes) throws SQLException {
			return _connection.prepareStatement(sql, columnIndexes);
		}

		public PreparedStatement prepareStatement(String sql,
				String[] columnNames) throws SQLException {
			return _connection.prepareStatement(sql, columnNames);
		}

		public PreparedStatement prepareStatement(String sql,
				int resultSetType, int resultSetConcurrency)
				throws SQLException {
			return _connection.prepareStatement(sql, resultSetType,
					resultSetConcurrency);
		}

		public PreparedStatement prepareStatement(String sql,
				int resultSetType, int resultSetConcurrency,
				int resultSetHoldability) throws SQLException {
			return _connection.prepareStatement(sql, resultSetType,
					resultSetConcurrency, resultSetHoldability);
		}

		public void releaseSavepoint(Savepoint savepoint) throws SQLException {
			_connection.releaseSavepoint(savepoint);
		}

		public void rollback() throws SQLException {
			throw new SQLException();
		}

		public void rollback(Savepoint savepoint) throws SQLException {
			throw new SQLException();
		}

		public void setAutoCommit(boolean autoCommit) throws SQLException {
			_connection.setAutoCommit(autoCommit);
		}

		public void setCatalog(String catalog) throws SQLException {
			_connection.setCatalog(catalog);
		}

		public void setHoldability(int holdability) throws SQLException {
			_connection.setHoldability(holdability);
		}

		public void setReadOnly(boolean readOnly) throws SQLException {
			_connection.setReadOnly(readOnly);
		}

		public Savepoint setSavepoint() throws SQLException {
			return _connection.setSavepoint();
		}

		public Savepoint setSavepoint(String name) throws SQLException {
			return _connection.setSavepoint(name);
		}

		public void setTransactionIsolation(int level) throws SQLException {
			_connection.setTransactionIsolation(level);
		}

		public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
			_connection.setTypeMap(map);
		}

	}

	private class SynchronizationImpl implements Synchronization {
		private PooledConnection _pooledConnection;
		private Transaction _transaction;

		public SynchronizationImpl(PooledConnection pooledConnection, Transaction transaction) {
			_pooledConnection = pooledConnection;
			_transaction = transaction;
		}

		public void afterCompletion(int status) {
			_availableConnections.add(0, _pooledConnection);
			_inUseConnectionsByTransaction.remove(_transaction);
		}

		public void beforeCompletion() {
		}
	}
}

Version 1.0.0.3.25591