package org.jfox.jdbc.xa;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.XAConnection;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
import org.huihoo.jfox.logging.Logger;
import org.huihoo.jfox.pool.PoolableObject;
import org.jfox.jdbc.ClientConnection;
public class PoolableXAConnection implements PoolableObject, XAConnection {
private XAConnection xaconn = null;
private XAResource xares = null;
private Connection underlying = null;
private ConnectionEventListener txConnListener = null;
Logger logger = Logger.getLogger(PoolableXAConnection.class.getName());
private boolean poolable = true;
private boolean errored = false;
private static TransactionManager tm = TxDataSource.getTransactionManager();
private Synchronization txSync = new XASynchronization();
private XAConnectionPool pool = null;
private boolean passivated = true;
private String dbUrl = null;
private String user = null;
private String password = null;;
public PoolableXAConnection(XAConnection xaconn, int transactionIsolation, String dbUrl, String user, String password) {
this.xaconn = xaconn;
this.dbUrl = dbUrl;
this.user = user;
this.password = password;
try {
xares = xaconn.getXAResource();
underlying = xaconn.getConnection();
underlying.setAutoCommit(false);
logger.debug("set underlying connection transaction isolation: " + transactionIsolation);
underlying.setTransactionIsolation(transactionIsolation);
}
catch (SQLException e) {
throw new RuntimeException("can not init PoolableXAConnection, because getConnection failed.", e);
}
txConnListener = new TxConnectionListener();
xaconn.addConnectionEventListener(txConnListener);
txSync = new XASynchronization();
}
public void activate() throws Exception {
logger.debug("activate callback, tx status = " + tm.getStatus());
passivated = false;
if (tm.getStatus() != Status.STATUS_NO_TRANSACTION) {
Transaction tran = tm.getTransaction();
tran.enlistResource(xares);
tran.registerSynchronization(txSync);
XAConnectionManager.associate(tran, this, dbUrl, user, password);
}
}
public void passivate() throws Exception {
logger.debug("passivate callback");
passivated = true;
int txStatus = tm.getStatus();
if (txStatus != Status.STATUS_NO_TRANSACTION) {
XAConnectionManager.disassociate(tm.getTransaction(), dbUrl, user, password);
}
}
public boolean isAvailable() {
return poolable && !errored && !passivated;
}
public XAResource getXAResource() throws SQLException {
if (passivated) {
throw new SQLException("XAConnection have returned to pool, re-retrieve it for use");
}
return xares;
}
public void close() throws SQLException {
poolable = false;
underlying.close();
xaconn.close();
}
public Connection getConnection() throws SQLException {
if (passivated) {
throw new SQLException("XAConnection have returned to pool, re-retrieve it for use");
}
return new ClientConnection(underlying);
}
public void addConnectionEventListener(ConnectionEventListener listener) {
if (!passivated) {
xaconn.addConnectionEventListener(listener);
}
}
public void removeConnectionEventListener(ConnectionEventListener listener) {
if (!passivated) {
xaconn.removeConnectionEventListener(listener);
}
}
private void markedError() {
logger.warn("xaconn error occurred, to be marked error");
errored = true;
}
void notPoolable() {
poolable = false;
}
void setPool(XAConnectionPool pool) {
this.pool = pool;
}
public static void main(String[] args) {
}
private class TxConnectionListener implements ConnectionEventListener {
public void connectionClosed(ConnectionEvent event) {
logger.debug("connection closed event: " + event);
if (passivated) return;
XAConnection xaconn = (XAConnection) event.getSource();
Transaction trans = null;
try {
if (tm.getStatus() != Status.STATUS_NO_TRANSACTION) {
trans = tm.getTransaction();
XAResource res = (XAResource) xaconn.getXAResource();
if (res != null) {
trans.delistResource(res, XAResource.TMSUCCESS);
XAConnectionManager.disassociate(trans, dbUrl, user, password);
pool.restoreObject(PoolableXAConnection.this);
}
else {
logger.warn("can not getXAResource from " + xaconn);
}
}
}
catch (Exception e) {
throw new RuntimeException("TxConnectionListener.connectionClosed() error", e);
}
}
public void connectionErrorOccurred(ConnectionEvent event) {
logger.debug("connection error occurred: " + event.getSQLException());
PoolableXAConnection.this.markedError();
}
}
class XASynchronization implements Synchronization {
public void beforeCompletion() {
}
public void afterCompletion(int status) {
logger.debug("PoolableXAConnection.XASynchronization.afterCompletion " + status);
pool.restoreObject(PoolableXAConnection.this);
}
}
}