/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2007 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.resource.internal.cm.jta;

import javax.resource.ResourceException;
import javax.resource.spi.LocalTransaction;

import javax.transaction.xa.XAResource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;

import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;

/**
 *  A LocalXAWrapper that intercepts the XA calls for an RAR that only
 *   supports LocalTransactions and translates them to the appropriate
 *   Local Transaction methods.
 *
 * @author     Eric.Hardesty@bull.com
 */
public final class LocalXAResource
    implements XAResource {

    /**
     *  The Logger instance where messages are written.
     */
    private static Logger logger = null;

    /**
     *  The boolean to determine if in a current transaction
     */
    protected boolean isInTransaction;

    /**
     *  The LocalTransaction object to make the begin(), commit(),
     *    and rollback() calls.
     */
    protected LocalTransaction localTrans = null;

    public LocalXAResource(LocalTransaction _localTrans, Logger _logger) {
        isInTransaction = false;
        localTrans = _localTrans;
        logger = _logger;
    }

    /**
     *  Commit the localTransaction, the params aren't used for a
     *    local transaction.
     *
     *@param xid transaction xid
     *@param flag for interface compliance
     *@exception  XAException  Exception trying to commit local transaction
     */
    public void commit(Xid xid, boolean flag)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Xid =" + xid + "flag=" + flag);
        }


        if (isInTransaction) {
            // Unbalance start/end. Probably a close is missing.
            // We decide to unset isInTransaction here to avoid a further error
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "XA START without XA END");
            }
            isInTransaction = false;
        }

        try {
            localTrans.commit();
        } catch (ResourceException res) {
            XAException xaE = new XAException(res.getMessage());
            xaE.initCause(res);
            throw xaE;
        }
    }

    /**
     *  No method to map for a local transaction.
     *@param xid transaction xid
     */
    public void end(Xid xid, int i)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Xid =" + xid + "i=" + i);
        }
        if (!isInTransaction) {
            logger.log(BasicLevel.ERROR, "END without START");
            XAException ex = new XAException(XAException.XA_RBPROTO);
            throw(ex);
        }
        isInTransaction = false;

    }

    /**
     *  No method to map for a local transaction.
     *@param xid transaction xid
     */
    public void forget(Xid xid)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
    }

    /**
     *  No method to map for a local transaction, just
     *   return no timeout.
     */
    public int getTransactionTimeout()
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        return -1;
    }

    /**
     * Determine if the wrapper instance is the same as the
     * wrapper instance being passed in
     * @param xaresource An XAResource object
     * @return True if same RM instance, otherwise false.
     * @throws XAException no throw in this implementation
     */

    public boolean isSameRM(XAResource xaresource)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }

        if (xaresource.equals(this)) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "isSameRM = true " + this);
            }
            return true;
        }
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "isSameRM = false " + this);
        }
        return false;
    }

    /**
     *  No method to map for a local transaction, just return XA_OK.
     *@param xid transaction xid
     */
    public int prepare(Xid xid)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        return XAResource.XA_OK;
    }

    /**
     *  No method to map for a local transaction.
     */
    public Xid[] recover(int i)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        return null;
    }

    /**
     *  Rollback the localTransaction, the param isn't used for a
     *    local transaction.
     *@param xid transaction xid
     *
     *@exception  XAException  Exception trying to rollback local transaction
     */
    public void rollback(Xid xid)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Xid =" + xid);
        }

        if (isInTransaction) {
            // Unbalance start/end. Probably a close is missing.
            // We decide to unset isInTransaction here to avoid a further error, but
            // I'm not sure it's a good idea.
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "XA START without XA END");
            }
            isInTransaction = false;
        }

        try {
            localTrans.rollback();
        } catch (ResourceException res) {
            XAException xaE = new XAException("Rollback failed");
            xaE.initCause(res);
            throw xaE;
        }
    }

    /**
     *  No method to map for a local transaction, just
     *   return no timeout.
     */
    public boolean setTransactionTimeout(int i)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        return false;
    }

    /**
     *  Only start a local transaction if a new transaction is being
     *   attempted, just return if joining or resuming.
     *
     *@param xid transaction xid
     *@exception  XAException  Transaction already started or error
     *                          starting a new local transaction
     */
    public void start(Xid xid, int i)
        throws XAException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Xid =" + xid + "i=" + i);
        }
        try {
            if (i != XAResource.TMJOIN && i != XAResource.TMRESUME) {
                if (isInTransaction) {
                    throw new XAException("LocalXAWrapper.start:  Local transaction already started");
                }
                localTrans.begin();
                isInTransaction = true;
            }
        } catch (ResourceException res) {
            XAException xaE = new XAException("LocalTransaction.begin failed");
            xaE.initCause(res);
            throw xaE;
        }

        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "OK");
        }
    }
}
