/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.tm;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TimerTask;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.jfox.framework.logger.Logger;


/**
 * 实现 javax.mw.Transaction 接口
 * 增加了 timeout 和 suspend, resume 功能
 * 因为涉及到 XAResource 的比较，因此重新封装了 XAResource 为 KeyXAResource
 *
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

class TransactionImpl extends TimerTask implements Transaction {
    private static Logger logger = Logger.getLogger(TransactionImpl.class.getName());
    private int status = Status.STATUS_NO_TRANSACTION;

    // global tx id
    private Xid xid = null;
    // 用来生成事务分支的计数器
    private static int branchCount = 0;

    // 事务同步器列表
    private List syncs = new ArrayList();
    //已经征用的资源集合
    private List enlistedXARes = new ArrayList();
    //暂停的资源集合
    private List suspendedXARes = new ArrayList();

    public TransactionImpl() {
        status = Status.STATUS_ACTIVE;
        xid = new XidImpl();
        logger.debug(new StringBuffer()
                .append("begin a new Transaction, tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());
    }

    /**
     * @param xaResource
     * @param flag       XAResource.TMSUCCESS or XAResource.TMSUSPEND or XAResource.TMFAIL
     * @return
     * @throws IllegalStateException
     * @throws SystemException
     */
    public synchronized boolean delistResource(XAResource xaResource, int flag) throws IllegalStateException, SystemException {
        logger.debug(new StringBuffer()
                .append("Transaction.delistResource(),flag = " + flag + " tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());

        if(xaResource == null) throw new IllegalArgumentException("null xaRes");
        if(flag != XAResource.TMSUCCESS &&
                flag != XAResource.TMSUSPEND &&
                flag != XAResource.TMFAIL)
            throw new IllegalArgumentException("wrong flag, flag must be one of XAResource.TMSUCCESS, XAResource.TMSUSPEND, XAResource.TMFAIL");

        KeyXAResource keyXAres = new KeyXAResource(xaResource);
        int index = enlistedXARes.indexOf(keyXAres);
        if(index < 0) {
            throw new IllegalArgumentException("xaResource not enlisted");
        }
        switch(status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_MARKED_ROLLBACK:
                break;
            case Status.STATUS_PREPARING:
                throw new IllegalStateException("Already started preparing.");
            case Status.STATUS_ROLLING_BACK:
                throw new IllegalStateException("Already started rolling back.");
            case Status.STATUS_PREPARED:
                throw new IllegalStateException("Already prepared.");
            case Status.STATUS_COMMITTING:
                throw new IllegalStateException("Already started committing.");
            case Status.STATUS_COMMITTED:
                throw new IllegalStateException("Already committed.");
            case Status.STATUS_ROLLEDBACK:
                throw new IllegalStateException("Already rolled back.");
            case Status.STATUS_NO_TRANSACTION:
                throw new IllegalStateException("No Transaction.");
            case Status.STATUS_UNKNOWN:
                throw new IllegalStateException("Unknown state");
            default:
                throw new IllegalStateException("Illegal status: " + StatusHelper.toString(status));
        }

        keyXAres = (KeyXAResource) enlistedXARes.get(index);
        try {
            logger.debug("delist resource " + keyXAres.getXid() + " with flag " + flag);
            xaResource.end(keyXAres.getXid(), flag);
            enlistedXARes.remove(index);
            return true;
        }
        catch(XAException e) {
            logger.warn(e);
            status = Status.STATUS_MARKED_ROLLBACK;
            return false;
        }
    }

    public synchronized boolean enlistResource(XAResource xaResource) throws RollbackException, IllegalStateException, SystemException {
        logger.debug(new StringBuffer()
                .append("Transaction.enlistResource(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());
        if(xaResource == null) throw new IllegalArgumentException("null xaRes");
        switch(status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_PREPARING:
                break;
            case Status.STATUS_PREPARED:
                throw new IllegalStateException("Transaction already prepared.");
            case Status.STATUS_COMMITTING:
                throw new IllegalStateException("Transaction already started committing.");
            case Status.STATUS_COMMITTED:
                throw new IllegalStateException("Transaction already committed.");
            case Status.STATUS_MARKED_ROLLBACK:
                throw new RollbackException("Transaction already marked for rollback");
            case Status.STATUS_ROLLING_BACK:
                throw new RollbackException("Transaction already started rolling back.");
            case Status.STATUS_ROLLEDBACK:
                throw new RollbackException("Transaction already rolled back.");
            case Status.STATUS_NO_TRANSACTION:
                throw new IllegalStateException("No current Transaction.");
            case Status.STATUS_UNKNOWN:
                throw new IllegalStateException("Unknown Transaction status");
            default :
                throw new IllegalStateException("Illegal Transaction status : " + StatusHelper.toString(status));
        }

        int flag = XAResource.TMNOFLAGS;

        KeyXAResource keyXAres = new KeyXAResource(xaResource);
        int index = enlistedXARes.indexOf(keyXAres);
        if(index >= 0) {
            keyXAres = (KeyXAResource) enlistedXARes.get(index);
            /**
             * 如果是同一个引用，就不再征用了，只针对 isSameRM
             */
            if(keyXAres.getXaResource() == xaResource) {
                return false;
            }
            flag = XAResource.TMJOIN;
        }
        else if((index = suspendedXARes.indexOf(keyXAres)) >= 0) { // 已经 suspended
            flag = XAResource.TMRESUME;
            keyXAres = (KeyXAResource) suspendedXARes.get(index);
            suspendedXARes.remove(index);
            enlistedXARes.add(keyXAres);
        }
        else {
            enlistedXARes.add(keyXAres);
        }
        try {
            logger.debug("resource start new branch Transaction " + keyXAres.getXid() + " with flag " + flag);
            xaResource.start(keyXAres.getXid(), flag);
            return true;
        }
        catch(XAException e) {
            logger.warn(e);
            return false;
        }
    }

    public int getStatus() throws SystemException {
        return status;
    }

    public synchronized void registerSynchronization(Synchronization synchronization) throws RollbackException, IllegalStateException, SystemException {
        logger.debug(new StringBuffer()
                .append("Transaction.registerSynchronization(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());
        if(synchronization == null) throw new IllegalArgumentException("Null synchronization");
        switch(status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_PREPARING:
            case Status.STATUS_MARKED_ROLLBACK:
                break;
            case Status.STATUS_PREPARED:
                throw new IllegalStateException("Already prepared.");
            case Status.STATUS_COMMITTING:
                throw new IllegalStateException("Already started committing.");
            case Status.STATUS_COMMITTED:
                throw new IllegalStateException("Already committed.");
            case Status.STATUS_ROLLING_BACK:
                throw new RollbackException("Already started rolling back.");
            case Status.STATUS_ROLLEDBACK:
                throw new RollbackException("Already rolled back.");
            case Status.STATUS_NO_TRANSACTION:
                throw new IllegalStateException("No Transaction.");
            case Status.STATUS_UNKNOWN:
                throw new IllegalStateException("Unknown state");
            default:
                throw new IllegalStateException("Illegal status: " + StatusHelper.toString(status));
        }
        syncs.add(synchronization);
    }

    public synchronized void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
        logger.debug(new StringBuffer()
                .append("Transaction.commit(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());

        switch(status) {
            case Status.STATUS_PREPARING:
                throw new IllegalStateException("Already started preparing.");
            case Status.STATUS_PREPARED:
                throw new IllegalStateException("Already prepared.");
            case Status.STATUS_ROLLING_BACK:
                throw new IllegalStateException("Already started rolling back.");
            case Status.STATUS_ROLLEDBACK:
                throw new IllegalStateException("Already rolled back.");
            case Status.STATUS_COMMITTING:
                throw new IllegalStateException("Already started committing.");
            case Status.STATUS_COMMITTED:
                throw new IllegalStateException("Already committed.");
            case Status.STATUS_NO_TRANSACTION:
                throw new IllegalStateException("No Transaction.");
            case Status.STATUS_UNKNOWN:
                throw new IllegalStateException("Unknown state");
            case Status.STATUS_MARKED_ROLLBACK:
                this.rollback();
                throw new RollbackException("Already marked for rollback");
            case Status.STATUS_ACTIVE:
                break;
            default:
                throw new IllegalStateException("Illegal status: " + StatusHelper.toString(status));
        }

        doBeforeCompletion(); // 可能已经把 StatusHelper 设为 ROLLBACK_ONLY

        try {
            if(status == Status.STATUS_ACTIVE) {
                if(enlistedXARes.size() == 0) {
                    // do nothing, just set status
                    status = Status.STATUS_COMMITTED;
                }
                else if(enlistedXARes.size() == 1) {
                    doOnePhaseCommit();
                }
                else { // Two phase commit
                    doTwoPhaseCommint();
                }
            }
            else if(status == Status.STATUS_MARKED_ROLLBACK) {
                this.rollback();
            }
        }
        finally {
            doAfterCompletion();
            enlistedXARes.clear();
        }
    }

    private void doOnePhaseCommit() throws RollbackException {
        status = Status.STATUS_COMMITTING;
        KeyXAResource keyXARes = (KeyXAResource) enlistedXARes.get(0);
        try {
            endResource(keyXARes, XAResource.TMSUCCESS);
        }
        catch(XAException e) {
            logger.warn("end resource error, to be roll back", e);
            status = Status.STATUS_MARKED_ROLLBACK;
        }

        if(status == Status.STATUS_MARKED_ROLLBACK) {
            try {
                status = Status.STATUS_ROLLING_BACK;
                keyXARes.getXaResource().rollback(keyXARes.getXid());
                status = Status.STATUS_ROLLEDBACK;
            }
            catch(XAException e) {
                logger.warn(e);
            }
            throw new RollbackException("commit failed, already rolled back");
        }
        else {
            try {
                keyXARes.getXaResource().commit(keyXARes.getXid(), true);
            }
            catch(XAException e) {
                logger.warn(e);
            }
        }
        status = Status.STATUS_COMMITTED;
    }

    /**
     * 两阶段提交，首先对所有的资源进行 prepare 操作，
     * 如果 prepare 没有混乱并且返回值也不全是 XA_READON，进行真正提交
     */
    private void doTwoPhaseCommint() throws
            HeuristicMixedException,
            HeuristicRollbackException,
            RollbackException {
        logger.debug("start two phase commit");
        status = Status.STATUS_PREPARING;
        int heuristicCode = XAException.XA_RETRY;
        // do prepare
        // 如果所有的 XAResource 都是 XA_RDONLY，最后将不再 commit
        int prepareStatus = XAResource.XA_RDONLY;
        for(Iterator it = enlistedXARes.iterator(); it.hasNext();) {
            KeyXAResource keyXARes = (KeyXAResource) it.next();
            try {
                logger.debug("prepare " + keyXARes.getXid());
                endResource(keyXARes, XAResource.TMSUCCESS);
                int xaStatus = keyXARes.getXaResource().prepare(keyXARes.getXid());
                if(xaStatus == XAResource.XA_OK) {
                    prepareStatus = XAResource.XA_OK;
                }
                else if(xaStatus == XAResource.XA_RDONLY) {
                }
                else { // Illegal prepare status
                    status = Status.STATUS_MARKED_ROLLBACK;
                    break;
                }
            }
            catch(XAException e) {
                // 虽然出错了，但是因为要 rollback，所以状态仍然是 XA_OK
                logger.warn(e);
                prepareStatus = XAResource.XA_OK;
                switch(e.errorCode) {
                    case XAException.XA_HEURCOM:
                        // 有些资源已经提交了，无法 rollback，所以另外一些也得提交
                        heuristicCode = decideHeuristic(heuristicCode, e.errorCode);
                        break;
                    case XAException.XA_HEURRB:
                    case XAException.XA_HEURMIX:
                    case XAException.XA_HEURHAZ:
                        heuristicCode = decideHeuristic(heuristicCode, e.errorCode);
                        if(status == Status.STATUS_PREPARING)
                            status = Status.STATUS_MARKED_ROLLBACK;
                        break;
                    default:
                        if(status == Status.STATUS_PREPARING)
                            status = Status.STATUS_MARKED_ROLLBACK;
                        break;
                }
                try {
                    keyXARes.getXaResource().forget(keyXARes.getXid());
                }
                catch(XAException ex) {
                    logger.warn(e);
                }
            }
            catch(Throwable e) {
                logger.warn(e);
                status = Status.STATUS_MARKED_ROLLBACK;
            }
        }
        // prepare 完毕，开始第二阶段提交
        status = Status.STATUS_PREPARED;

        if(prepareStatus == XAResource.XA_RDONLY) { // 全部都是只读，无需再提交
            status = Status.STATUS_COMMITTED;
        }
        // 可以进行第二阶段提交，但有可能提交，也有可能回滚
        else {
            // 应该 commit
            if(heuristicCode == XAException.XA_RETRY || heuristicCode == XAException.XA_HEURCOM) {
                status = Status.STATUS_COMMITTING;
                List temp = new ArrayList(enlistedXARes);
                for(Iterator it = temp.iterator(); it.hasNext();) {
                    KeyXAResource keyXARes = (KeyXAResource) it.next();
                    try {
                        keyXARes.getXaResource().commit(keyXARes.getXid(), false);
                    }
                    catch(XAException e) {
                        logger.warn(e);
                        switch(e.errorCode) {
                            case XAException.XA_HEURRB:
                            case XAException.XA_HEURCOM:
                            case XAException.XA_HEURMIX:
                            case XAException.XA_HEURHAZ:
                                heuristicCode = decideHeuristic(heuristicCode, e.errorCode);
                                break;
                            default:
                                break;
                        }
                        try {
                            keyXARes.getXaResource().forget(keyXARes.getXid());
                        }
                        catch(XAException ex) {
                            ex.printStackTrace();
                        }
                    }
                    catch(Throwable t) {
                        t.printStackTrace();
                    }
                }
                status = Status.STATUS_COMMITTED;
                checkHeuristics(heuristicCode);
            }
            // 由于启发式代码，只能 rollback
            else {
                status = Status.STATUS_ROLLING_BACK;
                List temp = new ArrayList(enlistedXARes);
                for(Iterator it = temp.iterator(); it.hasNext();) {
                    KeyXAResource keyXARes = (KeyXAResource) it.next();
                    try {
                        keyXARes.getXaResource().rollback(keyXARes.getXid());
                    }
                    catch(XAException e) {
                        logger.warn(e);
//             Heuristic rollback is not that bad when rolling back.
                        switch(e.errorCode) {
                            case XAException.XA_HEURRB:
                            case XAException.XA_HEURCOM:
                            case XAException.XA_HEURMIX:
                            case XAException.XA_HEURHAZ:
                                decideHeuristic(heuristicCode, e.errorCode);
                                break;
                            default:
                                break;
                        }
                        try {
                            keyXARes.getXaResource().forget(keyXARes.getXid());
                        }
                        catch(XAException ex) {
                            ex.printStackTrace();
                        }
                    }
                    catch(Throwable t) {
                        t.printStackTrace();
                    }
                }
                status = Status.STATUS_ROLLEDBACK;
                checkHeuristics(heuristicCode);
                throw new RollbackException("Unable to commit because of heuristic code, tx=" + toString());
            }
        }
    }

    /**
     * 根据当前的启发式代码，根据 XAException 的启发式代码，计算新的启发式代码
     *
     * @param heuristicCode
     * @param xaExceptionCode
     * @return
     */
    private int decideHeuristic(int heuristicCode, int xaExceptionCode) {
        switch(xaExceptionCode) {
            case XAException.XA_HEURMIX:
                heuristicCode = XAException.XA_HEURMIX;
                break;
            case XAException.XA_HEURRB:
                if(heuristicCode == XAException.XA_RETRY)
                    heuristicCode = XAException.XA_HEURRB;
                else if(heuristicCode == XAException.XA_HEURCOM ||
                        heuristicCode == XAException.XA_HEURHAZ)
                    heuristicCode = XAException.XA_HEURMIX;
                break;
            case XAException.XA_HEURCOM:
                if(heuristicCode == XAException.XA_RETRY)
                    heuristicCode = XAException.XA_HEURCOM;
                else if(heuristicCode == XAException.XA_HEURRB ||
                        heuristicCode == XAException.XA_HEURHAZ)
                    heuristicCode = XAException.XA_HEURMIX;
                break;
            case XAException.XA_HEURHAZ:
                if(heuristicCode == XAException.XA_RETRY)
                    heuristicCode = XAException.XA_HEURHAZ;
                else if(heuristicCode == XAException.XA_HEURCOM ||
                        heuristicCode == XAException.XA_HEURRB)
                    heuristicCode = XAException.XA_HEURMIX;
                break;
            default:
                throw new IllegalArgumentException();
        }
        return heuristicCode;
    }

    /**
     * 根据启发式代码，抛出合适的异常
     *
     * @param heuristicCode
     * @throws HeuristicMixedException
     * @throws HeuristicRollbackException
     */
    private void checkHeuristics(int heuristicCode) throws HeuristicMixedException,
            HeuristicRollbackException {
        switch(heuristicCode) {
            case XAException.XA_HEURHAZ:
            case XAException.XA_HEURMIX:
                throw new HeuristicMixedException();
            case XAException.XA_HEURRB:
                throw new HeuristicRollbackException();
            case XAException.XA_HEURCOM:
                return;
        }
    }


    public synchronized void rollback() throws IllegalStateException, SystemException {
        logger.debug(new StringBuffer()
                .append("Transaction.rollback(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());

        switch(status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_MARKED_ROLLBACK:
                break;
            case Status.STATUS_ROLLEDBACK:
                logger.warn("Transaction.rollback(): already rolled back");
                return;
            default :
                logger.error("Transaction.rollback(): bad status");
                throw new IllegalStateException("Cannot rollback(), " +
                        "tx=" + toString() +
                        " status=" +
                        StatusHelper.toString(status));
        }

        doBeforeCompletion();
        status = Status.STATUS_ROLLING_BACK;
        List temp = new ArrayList(enlistedXARes);
        int size = temp.size();
        for(int i = 0; i < size; i++) {
            KeyXAResource keyXARes = ((KeyXAResource) temp.get(i));
            try {
                endResource(keyXARes, XAResource.TMSUCCESS);
                keyXARes.getXaResource().rollback(keyXARes.getXid());
            }
            catch(XAException e) {
                logger.error("rollback XAResource " + keyXARes.getXaResource() + " failed.", e);
            }
        }
        status = Status.STATUS_ROLLEDBACK;
        doAfterCompletion();
    }

    public synchronized void setRollbackOnly() throws IllegalStateException, SystemException {
        logger.debug(new StringBuffer()
                .append("Transaction.setRollbackOnly(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());
        switch(status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_PREPARING:
            case Status.STATUS_PREPARED:
                status = Status.STATUS_MARKED_ROLLBACK;
                return;
            case Status.STATUS_MARKED_ROLLBACK:
            case Status.STATUS_ROLLING_BACK:
                return;
            case Status.STATUS_COMMITTING:
                throw new IllegalStateException("started committing.");
            case Status.STATUS_COMMITTED:
                throw new IllegalStateException("already committed.");
            case Status.STATUS_ROLLEDBACK:
                throw new IllegalStateException("already rolled back.");
            case Status.STATUS_NO_TRANSACTION:
                throw new IllegalStateException("no Transaction.");
            case Status.STATUS_UNKNOWN:
                throw new IllegalStateException("unknown state");
            default:
                throw new IllegalStateException("Illegal status: " + StatusHelper.toString(status));
        }
    }

    public String toString() {
        return "Transaction [" + xid.toString() + "]";
    }

    /**
     * timeout trigger
     */
    public synchronized void run() {
        timeout();
    }

    void timeout() {
        try {
            logger.debug(new StringBuffer()
                    .append("Transaction timeout, tx= ")
                    .append(this.toString())
                    .append(" status=")
                    .append(StatusHelper.toString(status))
                    .toString());

            this.setRollbackOnly();
        }
        catch(Exception e) {
            logger.warn(e.getMessage(), e);
        }
    }

    private void doBeforeCompletion() {
        try {
            for(int i = 0; i < syncs.size(); i++) {
                ((Synchronization) syncs.get(i)).beforeCompletion();
            }
        }
        catch(Throwable t) {
            logger.warn("Synchronization.beforeCompletion()", t);
            status = Status.STATUS_MARKED_ROLLBACK;
        }
    }

    private void doAfterCompletion() {
        try {
            for(int i = 0; i < syncs.size(); i++) {
                ((Synchronization) syncs.get(i)).afterCompletion(status);
            }
        }
        catch(Throwable t) {
            logger.warn("Synchronization.afterCompletion()", t);
        }
    }

    /**
     * 把已经征用的资源放到解除资源集合中，并清除征集资源集合
     *
     * @throws SystemException
     */
    synchronized void suspend() throws SystemException {
        logger.debug(new StringBuffer()
                .append("Transaction.suspend(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());

        suspendedXARes.addAll(enlistedXARes);
        for(int i = 0; i < suspendedXARes.size(); i++) {
            delistResource(((KeyXAResource) suspendedXARes.get(i)).getXaResource(), XAResource.TMSUSPEND);
        }
        enlistedXARes.clear();
    }

    /**
     * 重新征用 supend 的资源
     *
     * @throws SystemException
     * @throws RollbackException
     */
    synchronized void resume() throws SystemException, RollbackException {
        logger.debug(new StringBuffer()
                .append("Transaction.resume(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .toString());
        List temp = new ArrayList(suspendedXARes);
        for(int i = 0; i < temp.size(); i++) {
            enlistResource(((KeyXAResource) temp.get(i)).getXaResource());
        }
        suspendedXARes.clear();
    }


    private void endResource(KeyXAResource keyXAres, int flag) throws XAException {
        logger.debug(new StringBuffer()
                .append("XAResource.end(), tx= ")
                .append(this.toString())
                .append(" status=")
                .append(StatusHelper.toString(status))
                .append(" flag = " + flag)
                .toString());

        keyXAres.getXaResource().end(keyXAres.getXid(), flag);
        if(flag == XAResource.TMFAIL) status = Status.STATUS_MARKED_ROLLBACK;
        enlistedXARes.remove(keyXAres);
    }

    public static void main(String[] args) {

    }

    /**
     * KeyXAResource 功能
     * 1。关联 xid
     * 2。处理 isSameRM，使能成为 List 的元素
     */
    private class KeyXAResource {
        private XAResource xaRes = null;
        private Xid branchXid = null;

        public KeyXAResource(XAResource xaRes) {
            this.xaRes = xaRes;
        }

        public synchronized Xid getXid() {
            if(branchXid == null) {
                branchXid = XidImpl.createBranchXid(xid, branchCount++);
            }
            return branchXid;
        }

        public XAResource getXaResource() {
            return xaRes;
        }

        public int hashCode() {
            return 0;
        }

        public boolean equals(Object obj) {
            if(!(obj instanceof KeyXAResource)) {
                return false;
            }
            else {
                try {
                    return ((KeyXAResource) obj).getXaResource().isSameRM(xaRes);
                }
                catch(XAException e) {
                    logger.warn(e);
                    return false;
                }
            }
        }
    }
}

