/* JFox, the OpenSource J2EE Application Server
 *
 * Copyright (C) 2002 huihoo.com
 * Distributable under GNU LGPL license
 * See the GNU Lesser General Public License for more details.
 */

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;

/**
 * 
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class PoolableXAConnection implements PoolableObject, XAConnection {
  private XAConnection xaconn = null;
  private XAResource xares = null;

  // the underlying connection
  private Connection underlying = null;

  private ConnectionEventListener txConnListener = null;

  Logger logger = Logger.getLogger(PoolableXAConnection.class.getName());

  //如果 getConnection(user,pass) 与池中的 user pass 不同,则不缓存到池
  private boolean poolable = true;
  //如果在操作的时候发生了系统级错误,则该 XAConnection 将被销毁,不再缓存到池
  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();
  }

  /**
   * 在返回给用户使用之前,enlist XAResource
   * @throws Exception
   */
  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);
    }
  }

  /**
   * rollback 会调用 XASynchronization.afterCompletion(),解除事务和 XAConnection 的关联
   *
   * 如果还处在事务中,说明用户忘记了提交事务,那么将调用 rollback() ()
   * @see XASynchronization#afterCompletion(int)
   * @throws Exception
   */
  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);
    }
  }

  //标记该 PoolabeXAConnection 已经出错了,不再返回到池中
  // TODO: 出错了,是否应该调用 xaconn.close ,调用的时候会出错的
  private void markedError() {
    logger.warn("xaconn error occurred, to be marked error");
    errored = true;
  }

  /**
   * 如果使用的是特别的 user 和 password 建立的 XAConnection,
   * 将调用 notPoolable,以便不返回池
   * @see XAConnectionFactory#makeObject(String, String)
   */
  void notPoolable() {
    poolable = false;
  }

  void setPool(XAConnectionPool pool) {
    this.pool = pool;
  }

  public static void main(String[] args) {

  }

  /**
   * 完成两件事:
   * 1。在用户调用 XAConnection.close 的时候,如果处在事务中,delist 该资源
   * 2。在 XAConnection 出错的时候,标记该 PoolableXAConnection 不再返回池中
   */
  private class TxConnectionListener implements ConnectionEventListener {

    public void connectionClosed(ConnectionEvent event) {
      logger.debug("connection closed event: " + event);
      /*
       *有可能事务 commit 之后,有人再调用 conn.close(),
       * 那么 由于该 PoolableXAConnection 已经返回池中,则直接返回
       */
      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);
            // XAConnection 并没有关闭,还是有效的,所以应该返回池中
            pool.restoreObject(PoolableXAConnection.this);
          }
          else {
            logger.warn("can not getXAResource from " + xaconn);
          }
        }
      }
      catch (Exception e) {
        throw new RuntimeException("TxConnectionListener.connectionClosed() error", e);
      }
    }

    /**
     * 标记该 PoolabeXAConnection 已经出错了,不再返回到池中
     * @param event
     */
    public void connectionErrorOccurred(ConnectionEvent event) {
      // 对于 Oracle,在调用 XAConnection.close 的时候,发出 error 事件
      logger.debug("connection error occurred: " + event.getSQLException());
      PoolableXAConnection.this.markedError();
    }
  }

  class XASynchronization implements Synchronization {

    public void beforeCompletion() {

    }

    /**
     *
     * 将 PoolableXAConnection 返回池中
     * 由 passivate 接口负责解除事务和 XAConnection 之间的关联
     * @param status
     */
    public void afterCompletion(int status) {
      logger.debug("PoolableXAConnection.XASynchronization.afterCompletion " + status);
      pool.restoreObject(PoolableXAConnection.this);

    }
  }

}