Package com.sun.messaging.jmq.jmsclient

Source Code of com.sun.messaging.jmq.jmsclient.XAResourceForRA

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

/*
* @(#)XAResourceForRA.java  1.15 07/19/07
*/

package com.sun.messaging.jmq.jmsclient;

import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jms.*;
import javax.transaction.xa.*;

import com.sun.messaging.jmq.util.JMQXid;
import com.sun.messaging.jmq.util.XidImpl;

import com.sun.messaging.jms.ra.OnMessageRunner;
import com.sun.messaging.jms.ra.ResourceAdapter;
import com.sun.messaging.jms.ra.util.XAResourceMapForRAMC;

/**
  * JMQ implementation class for XAResource
  *
  * <p>The XAResource interface is a Java mapping of the industry standard
  * XA interface based on the X/Open CAE Specification (Distributed
  * Transaction Processing: The XA Specification).
  *
  * <p>The XA interface defines the contract between a Resource Manager
  * and a Transaction Manager in a distributed transaction processing
  * (DTP) environment. A JDBC driver or a JMS provider implements
  * this interface to support the association between a global transaction
  * and a database or message service connection.
  *
  * <p>The XAResource interface can be supported by any transactional
  * resource that is intended to be used by application programs in an
  * environment where transactions are controlled by an external
  * transaction manager. An example of such a resource is a database
  * management system. An application may access data through multiple
  * database connections. Each database connection is enlisted with
  * the transaction manager as a transactional resource. The transaction
  * manager obtains an XAResource for each connection participating
  * in a global transaction. The transaction manager uses the
  * <code>start</code> method
  * to associate the global transaction with the resource, and it uses the
  * <code>end</code> method to disassociate the transaction from
  * the resource. The resource
  * manager is responsible for associating the global transaction to all
  * work performed on its data between the start and end method invocations.
  *
  * <p>At transaction commit time, the resource managers are informed by
  * the transaction manager to prepare, commit, or rollback a transaction
  * according to the two-phase commit protocol.</p>
  *
  * @see javax.transaction.xa.XAResource
  */

public class XAResourceForRA implements XAResource, XAResourceForJMQ {

    //private SessionImpl _session;

    /* This XAResource depends on the connection being
     * valid across start,end,prepare,commit operations
     * as is the case for the j2ee 1.4 resource adapter
     * connection
     */
    private ConnectionImpl epConnection;
    //private Transaction _transaction = null;

    private int transactionTimeout = 0; //transactions do not timeout

    //Id of the omr that this is associated with
    private OnMessageRunner omr;

    private int id;

    //transaction ID - remains invalid until set by start
    private long transactionID = -1L;

    //JmqXid
    private JMQXid jmqXid = null;

    protected boolean started = false;

    private boolean setRollback = false;
    private Exception rollbackCause = null;
   
    /**
     * Possible states of this XAResource
     */
    public static final int CREATED    = 0; // after first creation, or after commit() or rollback()
    public static final int STARTED    = 1; // after start() called
    public static final int FAILED     = 2; // after end(fail) called
    public static final int INCOMPLETE = 3; // after end(suspend) called
    public static final int COMPLETE   = 4; // after end (success) called
    public static final int PREPARED   = 5; // after prepare() called
   
    /**
     * State of this XAresource
     */
    private int resourceState = CREATED;
   
    //xaTable to store xa transaction state.  key/value=xid/xaState
    private static Hashtable xaTable = new Hashtable();
   
    //transaction state (xaState) stored in the xaTable
    public static final Integer XA_START = Integer.valueOf(Transaction.TRANSACTION_STARTED);
    public static final Integer XA_END = Integer.valueOf(Transaction.TRANSACTION_ENDED);
    public static final Integer XA_PREPARE = Integer.valueOf(Transaction.TRANSACTION_PREPARED);
   
    //use this property to turn off xa transaction tracking
    public static boolean turnOffXATracking = Boolean.getBoolean("imq.ra.turnOffXATracking");
    //set to true by default - track xa transaction state
    public static boolean XATracking = !turnOffXATracking;
   
    /* Loggers */
    private static transient final String _className = "com.sun.messaging.jmq.jmsclient.XAResourceForRA";
    private static transient final String _lgrName = "com.sun.messaging.jmq.jmsclient.XAResourceForRA";
    private static transient final Logger _logger = Logger.getLogger(_lgrName);
    private static transient final String _lgrMIDPrefix = "MQJMSRA_XARMC";
    private static transient final String _lgrMID_EET = _lgrMIDPrefix + "1001: ";
    private static transient final String _lgrMID_INF = _lgrMIDPrefix + "1101: ";
    private static transient final String _lgrMID_WRN = _lgrMIDPrefix + "2001: ";
    private static transient final String _lgrMID_ERR = _lgrMIDPrefix + "3001: ";
    private static transient final String _lgrMID_EXC = _lgrMIDPrefix + "4001: ";
   
    public XAResourceForRA(OnMessageRunner omr, ConnectionImpl epConnection) throws JMSException {
        this.omr = omr;
        id = omr.getId();
        this.epConnection = epConnection;       
    }

    /**
     * Commits the global transaction specified by xid.
     *
     * @param foreignXid A global transaction identifier
     *
     * @param onePhase If true, the resource manager should use a one-phase
     * commit protocol to commit the work done on behalf of xid.
     *
     * @exception XAException An error has occurred. Possible XAExceptions
     * are XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR,
     * XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
     *
     * <P>If the resource manager did not commit the transaction and the
     *  parameter onePhase is set to true, the resource manager may throw
     *  one of the XA_RB* exceptions. Upon return, the resource manager has
     *  rolled back the branch's work and has released all held resources.
     */
    public synchronized void commit(Xid foreignXid, boolean onePhase) throws XAException {

        if (_logger.isLoggable(Level.FINE)){
        _logger.fine(_lgrMID_INF+"XAResourceForMC ("+this.hashCode()+") Commit  "+printXid(foreignXid)+" (onePhase="+onePhase+")");
        }

        //convert to jmq xid
        JMQXid jmqXid = new JMQXid(foreignXid);
        //Debug.println("MQRA:XAR4RA:commit():onePhase="+onePhase+" tid="+transactionID+" xid="+jmqXid.toString());
        if (onePhase && setRollback) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:commit1p:forcing Rollback due to:" + rollbackCause.getMessage());
            Debug.printStackTrace(rollbackCause);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(rollbackCause);
            throw xae;
        }
        try {
            if (!epConnection._isClosed()) {
                //epConnection.getProtocolHandler().commit(0L,
                //    (onePhase ? XAResource.TMONEPHASE : XAResource.TMNOFLAGS), jmqXid);
           
              if ( onePhase ) {
                //one phase commit
                if ( epConnection.isConnectedToHABroker() ) {
                  //use two phase protocol for HA
                  HAOnePhaseCommit (foreignXid, jmqXid);
                } else {
                  epConnection.getProtocolHandler().commit(0L, XAResource.TMONEPHASE, jmqXid);
                }
              } else {
                //tow phase commit
                if ( epConnection.isConnectedToHABroker() ) {
                  //HA case
                  this.HATwoPhaseCommit (jmqXid);
                } else {
                  //non-HA
                  epConnection.getProtocolHandler().commit(0L,
                            XAResource.TMNOFLAGS, jmqXid);
                }
              }
             
            } else {
                System.err.println("MQRA:XARFRA:commit:ConnectionClosed:throw XAExc txn:1p="+onePhase+":xid="+jmqXid.toString());
                //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:commit:XAException");
                XAException xae = new XAException(XAException.XAER_RMFAIL);
                throw xae;
            }
        } catch (Exception jmse) {
         
          //check remote exception.
          if (jmse instanceof RemoteAcknowledgeException) {
            //notify session that re-create consumer is required.
            RemoteAcknowledgeException rae = (RemoteAcknowledgeException) jmse;
            XASessionImpl session = (XASessionImpl) omr.getEndpointConsumer().getXASession();
           
            session.notifyRemoteAcknowledgeException(rae);
          }
         
          this.epConnection.waitForReconnecting ( jmse );
         
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:commitXAException");
            Debug.printStackTrace(jmse);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(jmse);
            throw xae;
        } finally {
            // finish up this resource and any others joined to it in this transaction
            boolean throwExceptionIfNotFound = false;
            XidImpl savedXid = this.jmqXid;
            XAResourceForJMQ[] resources = XAResourceMapForRAMC.getXAResources(this.jmqXid,throwExceptionIfNotFound);
            for (int i = 0; i < resources.length; i++) {
              XAResourceForJMQ xari = resources[i];
              try {
          xari.clearTransactionInfo();
        } catch (JMSException jmse) {
                Debug.printStackTrace(jmse);
                XAException xae = new XAException(XAException.XAER_RMFAIL);
                xae.initCause(jmse);
                throw xae;
        }
            }
            XAResourceMapForRAMC.unregister(savedXid);
        }
       
        //if (this.isXATracking()) {
        //  xaTable.remove(jmqXid);
        //}
        this.removeXid(jmqXid);
    }
   
    /**
     * Ends the work performed on behalf of a transaction branch.
     * The resource manager disassociates the XA resource from the
     * transaction branch specified and lets the transaction
     * complete.
     *
     * <p>If TMSUSPEND is specified in the flags, the transaction branch
     * is temporarily suspended in an incomplete state. The transaction
     * context is in a suspended state and must be resumed via the
     * <code>start</code> method with TMRESUME specified.</p>
     *
     * <p>If TMFAIL is specified, the portion of work has failed.
     * The resource manager may mark the transaction as rollback-only</p>
     *
     * <p>If TMSUCCESS is specified, the portion of work has completed
     * successfully.</p>
     *
     * @param foreignXid A global transaction identifier that is the same as
     * the identifier used previously in the <code>start</code> method.
     *
     * @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND.
     *
     * @exception XAException An error has occurred. Possible XAException
     * values are XAER_RMERR, XAER_RMFAILED, XAER_NOTA, XAER_INVAL,
     * XAER_PROTO, or XA_RB*.
     */
    public synchronized void end(Xid foreignXid, int flags) throws XAException {

        if (_logger.isLoggable(Level.FINE)){
        _logger.fine(_lgrMID_INF+"XAResourceForRA ("+this.hashCode()+") End     "+printXid(foreignXid)+printFlags(flags));
        }
     
        //convert to jmq xid
        JMQXid jmqXid = new JMQXid(foreignXid);
        //Debug.println("MQRA:XAR4RA:end():flags="+flags+" tid="+transactionID+" xid="+jmqXid.toString());
       
        if (epConnection._isClosed()) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:end:XAException");
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            throw xae;
        }
       
        // update the resource state
        if (isFail(flags)){
          resourceState=FAILED;
        } else if (isSuspend(flags)){
          resourceState=INCOMPLETE;
        } else {
          resourceState=COMPLETE;
        }
       
        if (ResourceAdapter.isRevert6882044()) {
          // revert to pre-6882044 behaviour and forward all events to broker
          sendEndToBroker(flags, jmqXid);
        } else {       
          // now decide whether to send an END packet to the broker on the basis of the resource state
          if (resourceState==COMPLETE){
            // only send an END packet to the broker when all joined resources are complete
            // this works around Glassfish issue 7118
            boolean allComplete = true;
              XAResourceForJMQ[] resources = XAResourceMapForRAMC.getXAResources(jmqXid,true);
              for (int i = 0; i < resources.length; i++) {
              XAResourceForJMQ xari = resources[i];
                if (!xari.isComplete()){
                  allComplete = false;
                }
              }
              if (allComplete){
                   sendEndToBroker(flags, jmqXid);
              }
          } else if (resourceState==FAILED){
              sendEndToBroker(flags, jmqXid);
          } else if (resourceState==INCOMPLETE){
            // don't send the END to the broker
          }
        }
               
        started = false;
       
        if (this.isXATracking()) {
          xaTable.put(jmqXid, XA_END);
        }
    }

  private void sendEndToBroker(int flags, JMQXid jmqXid) throws XAException {
    try {
            //System.out.println("MQRA:XAR4RA:end:sending 0L:tid="+transactionID+" xid="+jmqXid.toString());
            epConnection.getProtocolHandler().endTransaction(0L, flags, jmqXid);
        } catch (JMSException jmse) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:end:XAException");
            Debug.printStackTrace(jmse);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(jmse);
            throw xae;
        }
  }

    public synchronized void _endLocal() throws XAException {
        //End a local transaction for test purposes
        try {
            //System.out.println("\t\t_endLocal:omrId="+omr.getId()+" started="+started+" transactionID="+transactionID);
            epConnection.getProtocolHandler().commit(transactionID, 0, null);
        } catch (JMSException jmse) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:_endLocal:XAException");
            Debug.printStackTrace(jmse);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(jmse);
            throw xae;
        }
        started = false;
    }

    /**
     * Tells the resource manager to forget about a heuristically
     * completed transaction branch.
     *
     * @param foreignXid A global transaction identifier.
     *
     * @exception XAException An error has occurred. Possible exception
     * values are XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
     * XAER_PROTO.
     */
    public void forget(Xid foreignXid) throws XAException {
        //MQ does not support heuristically completed transaction branches
        //This is a NOP
        //convert to jmq xid
        //////JMQXid jmqXid = new JMQXid(foreignXid);
        //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:forget:txid=\n"+jmqXid.toString());
        XidImpl xidToForget = new XidImpl(foreignXid);
        XAResourceMapForRAMC.unregister(xidToForget);
        if (jmqXid!=null){
          if (jmqXid.equals(xidToForget)){
            clearTransactionInfo();
          }
        }
    }

    /**
     * Obtains the current transaction timeout value set for this
     * XAResource instance. If <CODE>XAResource.setTransactionTimeout</CODE>
     * was not used prior to invoking this method, the return value
     * is the default timeout set for the resource manager; otherwise,
     * the value used in the previous <CODE>setTransactionTimeout</CODE>
     * call is returned.
     *
     * @return the transaction timeout value in seconds.
     *
     * @exception XAException An error has occurred. Possible exception
     * values are XAER_RMERR and XAER_RMFAIL.
     */
    public int getTransactionTimeout() throws XAException {
        //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:getTransactionTimeout");
        return transactionTimeout;
    }

    /**
     * This method is called to determine if the resource manager
     * instance represented by the target object is the same as the
     * resource manager instance represented by the parameter <i>foreignXaRes</i>.
     *
     * @param foreignXaRes An XAResource object whose resource manager instance
     *      is to be compared with the resource manager instance of the
     *      target object.
     *
     * @return <i>true</i> if it's the same RM instance; otherwise
     *       <i>false</i>.
     *
     * @exception XAException An error has occurred. Possible exception
     * values are XAER_RMERR and XAER_RMFAIL.
     *
     */
    public boolean isSameRM(XAResource foreignXaRes) throws XAException {
     
      // don't allow a XAResourceImpl to be joined to a XAResourceForMC or a XAResourceForRA
      // (even though they all implement XAResourceForJMQ)
      // as this would imply joining of JMSJCA and JMSRA resources, which has not been tested
           
      if ((foreignXaRes instanceof XAResourceForMC)||(foreignXaRes instanceof XAResourceForRA)){
        return isSameJMQRM((XAResourceForJMQ)foreignXaRes);
      } else {
          return false;
      }

    }

    /**
     * Return whether this XAResourceForJMQ and the specified XAResourceForJMQ
     * represent the same resource manager instance.
     *
     * This is determined by checking whether the two resources
     * have the same brokerSessionID
     *
     * @param xaResource XAResourceForJMQ 
     * @return true if same RM instance, otherwise false.
     */     
   public boolean isSameJMQRM(XAResourceForJMQ xaResource) {
       
      boolean result;
     
      if (ResourceAdapter.isSameRMAllowed()){
        if ((getBrokerSessionID()!=0) && (getBrokerSessionID()==xaResource.getBrokerSessionID())){
          result= true;
        } else {
          result=false;
        }
      } else {
        result=false;
      }
      return result;   
   }
   
    /**
     * Return the brokerSessionID of this object's connection
     * @return
     */
    public long getBrokerSessionID(){
      return this.epConnection.getBrokerSessionID();
    }   

    /**
     * Ask the resource manager to prepare for a transaction commit
     * of the transaction specified in xid.
     *
     * @param foreignXid A global transaction identifier.
     *
     * @exception XAException An error has occurred. Possible exception
     * values are: XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL,
     * or XAER_PROTO.
     *
     * @return A value indicating the resource manager's vote on the
     * outcome of the transaction. The possible values are: XA_RDONLY
     * or XA_OK. If the resource manager wants to roll back the
     * transaction, it should do so by raising an appropriate XAException
     * in the prepare method.
     */
    public synchronized int prepare(Xid foreignXid, boolean onePhase) throws XAException {

        if (_logger.isLoggable(Level.FINE)){
        _logger.fine(_lgrMID_INF+"XAResourceForRA ("+this.hashCode()+") Prepare     "+printXid(foreignXid));
        }

        //JMS does not do RDONLY transactions - right?
        int result = XA_OK;
        //convert to jmq xid
        JMQXid jmqXid = new JMQXid(foreignXid);
        //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:prepare:txid=\n"+jmqXid.toString());
        if (setRollback) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:prepare:forcing Rollback due to:" + rollbackCause.getMessage());
            Debug.printStackTrace(rollbackCause);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(rollbackCause);
            throw xae;
        }
        try {
            if (!epConnection._isClosed()) {
                epConnection.getProtocolHandler().prepare(0L, jmqXid, onePhase);
            } else {
                XAException xae = new XAException(XAException.XAER_RMFAIL);
                throw xae;
            }
        } catch (Exception jmse) {
         
          //check remote exception.
          if (jmse instanceof RemoteAcknowledgeException) {
            //notify session that re-create consumer is required.
            RemoteAcknowledgeException rae = (RemoteAcknowledgeException) jmse;
            ((XASessionImpl) omr.getEndpointConsumer().getXASession()).notifyRemoteAcknowledgeException(rae);
          }
         
          //this.epConnection.waitForReconnecting ( jmse );
         
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:prepareXAException");
            Debug.printStackTrace(jmse);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(jmse);
            throw xae;
        }
       
        // update the resource state
        resourceState=PREPARED;
       
        return result;
    }
   
    /**
     * two-phase commit prepare for HA.
     *
     */
    public synchronized int prepare(Xid foreignXid) throws XAException {
     
       //result code
       int result = XA_OK;
      
       //this.twoPhasePrepared = false;
      
       try {
        //two phase commit
        this.prepare (foreignXid, false);
      } catch (XAException xae) {
       
        if (this.epConnection.isConnectedToHABroker) {
          this.checkPrepareStatus(xae, jmqXid);
        } else {
          //non HA -- propagate exception
          throw xae;
        }
       
      }
     
      //set to true so that in case we need to verify transaction later.
      //this.twoPhasePrepared = true;
     
      if (isXATracking()) {
          xaTable.put (jmqXid, XAResourceForRA.XA_PREPARE);
        }
     
      return result;
    }
   
    /**
     * For XA onePhase commit, if RA is connected to HA brokers,
     * we use two phase MQ protocol to commit a transaction.
     *
     * "JMQXAOnePhase" property is set to true for prepare and commit pkts.
     *
     * "TMNOFLAGS" is used in the onePhase commit pkt.
     *
     *
     * @param foreignXid
     * @param jmqXid
     * @throws JMSException
     * @throws XAException
     */
    private void HAOnePhaseCommit (Xid foreignXid, JMQXid jmqXid) throws JMSException, XAException {
     
      int tstate = Transaction.TRANSACTION_ENDED;
     
      try {
        //prepare xa onephase commit
        this.prepare(foreignXid, true);
       
        tstate = Transaction.TRANSACTION_PREPARED;
       
        if (isXATracking()) {
              xaTable.put(jmqXid, XAResourceForRA.XA_PREPARE);
            }
       
        //param true is to indicate "JMQXAOnePhase" is needed
        //for the commit protocol property.
        epConnection.getProtocolHandler().commit(0L, XAResource.TMNOFLAGS, jmqXid, true);
      } catch (Exception jmse) {
        //check onephase commit status
        this.checkCommitStatus(jmse, tstate, jmqXid, true);
      }
     
      this.removeXid(jmqXid);
    }
   
    private void HATwoPhaseCommit (JMQXid jmqXid) throws JMSException, XAException {
     
      //if (this.twoPhasePrepared == false) {
      //  throw new XAException (XAException.XAER_PROTO);
      //}
     
      try {
        epConnection.getProtocolHandler().commit(0L, XAResource.TMNOFLAGS, jmqXid);
      } catch (JMSException jmse) {
       
        if ( this.isXATracking() ) {
         
          Integer ts = (Integer) xaTable.get(jmqXid);
          //we must gaurantee that the transaction was indeed in prepared state.
          if (ts != null && ts.intValue() == (Transaction.TRANSACTION_PREPARED)) {
            this.checkCommitStatus(jmse, Transaction.TRANSACTION_PREPARED, jmqXid, false);
          }
         
        } else {
          //propagate the exception
          throw jmse;
        }
       
      }
     
      //transaction has successfully committed
      //this.twoPhasePrepared = false;
     
      this.removeXid(jmqXid);
    }
   
    /**
     * check prepared status
     *
     * @param jmse
     * @param tstate -- transaction state when exception occurred
     * @param jmqXid --
     * @throws XAException if the transaction is not in prepared state.
     */
    private void checkPrepareStatus (XAException xae, JMQXid jmqXid) throws XAException {
     
      if (epConnection.imqReconnect == false) {
      throw xae;
    }
     
      try {
       
      SessionImpl.yield();

      epConnection.checkReconnecting(null);

      if (epConnection.isCloseCalled || epConnection.connectionIsBroken) {
        throw xae;
      }
      //check failover broker to see the status
      verifyPrepare(jmqXid);
     
    } catch (XAException xae2) {
      //if any xaexception we simply propagate up
      throw xae2;
    } catch (Exception e) {
      //for any other exceptions, throws an XAException.
      XAException xae3 = new XAException(XAException.XAER_RMFAIL);
            xae3.initCause(e);
            throw xae3;
      }
   
    }
   
    private void verifyPrepare (JMQXid jmqXid) throws XAException, JMSException {
     
      SessionImpl.sessionLogger.log(Level.INFO, "XA verifyPrepare(), jmqXid: " + jmqXid);
      int state = epConnection.protocolHandler.verifyHATransaction(0L, Transaction.TRANSACTION_ENDED, jmqXid);

        switch (state) {
        case 6:
                //transaction is in prepared state
          SessionImpl.sessionLogger.log(Level.INFO, "transaction in prepared state: " + this.transactionID);
              return;          
        case 8:
        case 9:
        default:
            //for the rest of the state, the transaction was rolled back
            //by the broker.
          XAException xae = new XAException(XAException.XA_RBROLLBACK);
          throw xae;
        }
    }
   
    private void checkCommitStatus(Exception cause, int tstate, JMQXid jmqXid, boolean onePhase)
  throws JMSException, XAException {
     
      try {
       
        doCheckCommitStatus (cause, tstate, jmqXid, onePhase);
     
      } catch (Exception ex) {
        //throw the original type of exception.
        if (ex instanceof JMSException) {
        throw (JMSException) ex;
      } else if (ex instanceof XAException) {
        throw (XAException) ex;
      } else {
        XAException xae = new XAException (XAException.XAER_RMFAIL);
        xae.initCause(ex);
        throw xae;
      }
      }
    }
   
    private void doCheckCommitStatus(Exception cause, int tstate, JMQXid jmqXid, boolean onePhase)
      throws Exception {

    if (epConnection.imqReconnect == false) {
      throw cause;
    }

    SessionImpl.yield();

    epConnection.checkReconnecting(null);

    if (epConnection.isCloseCalled || epConnection.connectionIsBroken) {
      throw cause;
    }

    verifyTransaction(tstate, jmqXid, onePhase);
  }

    private void verifyTransaction(int tstate, JMQXid jmqXid, boolean onePhase) throws JMSException, XAException {

        int state = epConnection.protocolHandler.verifyHATransaction(0L, tstate, jmqXid);

        switch (state) {

        case 7:
           //committed
            return;
        case 6:
            //transaction is in prepared state.  ask broker to commit.
            try {
                //protocolHandler.rollback(this.transactionID);
              SessionImpl.sessionLogger.log(Level.INFO, "XA verifyTransaction(): transaction is in prepred state, committing the transaction: " + this.transactionID);
              //epConnection.protocolHandler.commitHATransaction(this.transactionID);
              epConnection.getProtocolHandler().commit(0L, XAResource.TMNOFLAGS, jmqXid, onePhase);
              SessionImpl.sessionLogger.log(Level.INFO, "XA verifyTransaction(): prepared transaction committed successfully: " + this.transactionID);
              //done if we can commit.
              return;
             
            } catch (JMSException jmse) {
                //in case this failed, we try one more time.
                //This is the third failure at this point.
                //If failed again, we log this and throw Exception.
               
                SessionImpl.yield();

                epConnection.checkReconnecting(null);

                //if ( epConnection.isCloseCalled || epConnection.connectionIsBroken) {
                throw jmse;
                //}
               
                //we should rollback at this point.
                //epConnection.protocolHandler.rollback(this.transactionID);
            }
          
        case 8:
        case 9:
        default:
            //for the rest of the state, the transaction was rolled back
            //by the broker.
            //String errorString = AdministeredObject.cr.getKString(
            //    AdministeredObject.cr.X_TRANSACTION_FAILOVER_OCCURRED);

            //TransactionRolledBackException tre =
            //    new TransactionRolledBackException(errorString,
            //                                       AdministeredObject.cr.
            //                                       X_TRANSACTION_FAILOVER_OCCURRED);

            //throwRollbackException(tre);
          XAException xae = new XAException(XAException.XA_RBROLLBACK);
          throw xae;
        }
    }

    /**
     * Obtains a list of prepared transaction branches from a resource
     * manager. The transaction manager calls this method during recovery
     * to obtain the list of transaction branches that are currently in
     * prepared or heuristically completed states.
     *
     * @param flags One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS
     * must be used when no other flags are set in the parameter.
     *
     * @exception XAException An error has occurred. Possible values are
     * XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
     *
     * @return The resource manager returns zero or more XIDs of the
     * transaction branches that are currently in a prepared or
     * heuristically completed state. If an error occurs during the
     * operation, the resource manager should throw the appropriate
     * XAException.
     *
     */
    public Xid[] recover(int flags) throws XAException {
        Xid[] result = null;
        //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:recover:flags="+flags);
        try {
            if (!epConnection._isClosed()) {
                result = epConnection.getProtocolHandler().recover(flags);
            } else {
                XAException xae = new XAException(XAException.XAER_RMFAIL);
                throw xae;
            }
        } catch (Exception jmse) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:recoverXAException");
            Debug.printStackTrace(jmse);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(jmse);
            throw xae;
        }
        return result;
    }

    /**
     * Informs the resource manager to roll back work done on behalf
     * of a transaction branch.
     *
     * @param foreignXid A global transaction identifier.
     *
     * @exception XAException An error has occurred.
     */
    public synchronized void rollback(Xid foreignXid) throws XAException {

        if (_logger.isLoggable(Level.FINE)){
        _logger.fine(_lgrMID_INF+"XAResourceForMC ("+this.hashCode()+") Rollback  "+printXid(foreignXid)+")");
        }   

        //convert to jmq xid
        JMQXid jmqXid = new JMQXid(foreignXid);
        //Debug.println("MQRA:XAR4RA:rollback():tid="+transactionID+" xid="+jmqXid.toString());
        try {
            //send rollback w/redeliver
            if (!epConnection._isClosed()) {
               
              if (epConnection.isConnectedToHABroker()) {
                //handle fail-over for HA connection
                HARollback(jmqXid);
              } else {
                epConnection.getProtocolHandler().rollback(0L, jmqXid, true);
              }
            } else {
                XAException xae = new XAException(XAException.XAER_RMFAIL);
                throw xae;
            }
        } catch (JMSException jmse) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:rollbackXAException");
            Debug.printStackTrace(jmse);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(jmse);
            throw xae;
        } finally {
            // finish up this resource and any others joined to it in this transaction
            boolean throwExceptionIfNotFound = false;
            XidImpl savedXid = this.jmqXid;
            XAResourceForJMQ[] resources = XAResourceMapForRAMC.getXAResources(this.jmqXid,throwExceptionIfNotFound);
            for (int i = 0; i < resources.length; i++) {
              XAResourceForJMQ xari = resources[i];
              try {
          xari.clearTransactionInfo();
        } catch (JMSException jmse) {
                Debug.printStackTrace(jmse);
                XAException xae = new XAException(XAException.XAER_RMFAIL);
                xae.initCause(jmse);
                throw xae;
        }
            }
            XAResourceMapForRAMC.unregister(savedXid);
        }
       
        //if (this.isXATracking()) {
        //  xaTable.remove(jmqXid);
        //}
        this.removeXid(jmqXid);
    }
   
private void HARollback(JMQXid jmqXid) throws JMSException, XAException {
     
      try {
        epConnection.getProtocolHandler().rollback(0L, jmqXid, true);
      } catch (JMSException jmse) {
       
        //yield/pause
        SessionImpl.yield();
        //block until fail-over/re-connected/closed
        this.epConnection.checkReconnecting(null);
       
        //check if we still connected.
        if (epConnection.isCloseCalled || epConnection.connectionIsBroken) {
          throw jmse;
        }
       
        //re-send with I bit set to true
        this.retryRollBack(jmqXid);
      }
     
      this.removeXid(jmqXid);
    }
   
    private void retryRollBack (JMQXid jmqXid) throws JMSException, XAException {
     
      try {
        epConnection.getProtocolHandler().rollback(0L, jmqXid, true, true);
      } catch (JMSException jmse) {
       
        if (isXATracking()) {
         
        Integer tstate = (Integer) xaTable.get(jmqXid);

        if (tstate != null && tstate.intValue() != Transaction.TRANSACTION_PREPARED) {
          // the transaction is not in prepared state, we log the info
          // and continue.
          ConnectionImpl.connectionLogger.log(Level.WARNING, jmse.toString());
        } else {
          throw jmse;
        }
      } else {
        throw jmse;
      }
      }
    }


    /**
     * <P>Sets the current transaction timeout value for this <CODE>XAResource</CODE>
     * instance. Once set, this timeout value is effective until
     * <code>setTransactionTimeout</code> is invoked again with a different
     * value. To reset the timeout value to the default value used by the resource
     * manager, set the value to zero.
     *
     * If the timeout operation is performed successfully, the method returns
     * <i>true</i>; otherwise <i>false</i>. If a resource manager does not
     * support explicitly setting the transaction timeout value, this method
     * returns <i>false</i>.
     *
     * @param transactionTimeout The transaction timeout value in seconds.
     *
     * @return <i>true</i> if the transaction timeout value is set successfully;
     *       otherwise <i>false</i>.
     *
     * @exception XAException An error has occurred. Possible exception values
     * are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
     */
    public boolean setTransactionTimeout(int transactionTimeout) throws XAException {
        //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:setTransactionTimeout:timeout="+transactionTimeout);
        //XXX:GT RFE - transactionTimeout not supported for now
        return false;
        /*
        if (trannsactionTimeout != null && transactionTimeout >= 0) {
            this.transactionTimeout = transactionTimeout;
        } else {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:setTransactionTimeoutXAException");
            //XXX:GT TBF I18N
            XAException xae = new XAException("Invalid transactionTimeout");
            xae.initCause(jmse);
            throw xae;
        }
        */
    }

    /**
     * Starts work on behalf of a transaction branch specified in
     * <code>foreignXid</code>.
     *
     * If TMJOIN is specified, the start applies to joining a transaction
     * previously seen by the resource manager. If TMRESUME is specified,
     * the start applies to resuming a suspended transaction specified in the
     * parameter <code>foreignXid</code>.
     *
     * If neither TMJOIN nor TMRESUME is specified and the transaction
     * specified by <code>foreignXid</code> has previously been seen by the resource
     * manager, the resource manager throws the XAException exception with
     * XAER_DUPID error code.
     *
     * @param foreignXid A global transaction identifier to be associated
     * with the resource.
     *
     * @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME.
     *
     * @exception XAException An error has occurred. Possible exceptions
     * are XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE,
     * XAER_NOTA, XAER_INVAL, or XAER_PROTO.
     *
     */
    public synchronized void start(Xid foreignXid, int flags) throws XAException {

        if (_logger.isLoggable(Level.FINE)){
        _logger.fine(_lgrMID_INF+"XAResourceForRA ("+this.hashCode()+") Start   "+printXid(foreignXid)+printFlags(flags));
        }

        //convert to jmq xid
        JMQXid jmqXid = new JMQXid(foreignXid);
        //Debug.println("MQRA:XAR4RA:start():flags="+flags+" xid="+jmqXid.toString());
       
        // if we're reverting to the pre-6882044 behaviour always send the START to the broker
        // otherwise send the START to the broker only if this is not a TMRESUME
      if (!isResume(flags) || ResourceAdapter.isRevert6882044()){   
       
          try {
              if (!epConnection._isClosed()) {
                  transactionID = epConnection.getProtocolHandler().startTransaction(transactionID, flags, jmqXid);
                  this.jmqXid = jmqXid;
              } else {
                  XAException xae = new XAException(XAException.XAER_RMFAIL);
                  throw xae;
              }
          } catch (JMSException jmse) {
              //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:startXAException");
              Debug.printStackTrace(jmse);
              XAException xae = new XAException(XAException.XAER_RMFAIL);
              xae.initCause(jmse);
              throw xae;
          }
       
          XAResourceMapForRAMC.register(jmqXid, this, isJoin(flags));
         
      }
     
        started = true;
       
        // update the resource state
        resourceState=STARTED;
       
        if (this.isXATracking()) {
          xaTable.put(jmqXid, XA_START);
        }
    }

    public synchronized void _startLocal() throws XAException {
        //Start a local transaction for test purposes
        try {
            transactionID = epConnection.protocolHandler.startTransaction(-1L, 0, null);
            //System.out.println("\t\t_startLocal():omrId="+omr.getId()+" started="+started+" transactionID="+transactionID);
        } catch (JMSException jmse) {
            //Debug.println("*=*=*=*=*=*=*=*=*=*=XAR:_startLocalXAException");
            Debug.printStackTrace(jmse);
            XAException xae = new XAException(XAException.XAER_RMFAIL);
            xae.initCause(jmse);
            throw xae;
        }
        started = true;
    }

    public int getId() {
        return id;
    }

    public synchronized long getTransactionID() {
        return transactionID;
    }

    public boolean started() {
        return started;
    }

    /**
      * Ends a recovery scan.
      */
    //public final static int TMENDRSCAN =   0x00800000;

    /**
      * Disassociates the caller and marks the transaction branch
      * rollback-only.
      */
    //public final static int TMFAIL =       0x20000000;

    /**
      * Caller is joining existing transaction branch.
      */
    //public final static int TMJOIN =       0x00200000;

    /**
      * Use TMNOFLAGS to indicate no flags value is selected.
      */
    //public final static int TMNOFLAGS =     0x00000000;

    /**
      * Caller is using one-phase optimization.
      */
    //public final static int TMONEPHASE =   0x40000000;

    /**
      * Caller is resuming association with a suspended
      * transaction branch.
      */
    //public final static int TMRESUME =     0x08000000;

    /**
      * Starts a recovery scan.
      */
    //public final static int TMSTARTRSCAN = 0x01000000;


    /**
      * Disassociates caller from a transaction branch.
      */
    //public final static int TMSUCCESS =    0x04000000;


    /**
      * Caller is suspending (not ending) its association with
      * a transaction branch.
      */
    //public final static int TMSUSPEND =    0x02000000;

    /**
     * The transaction branch has been read-only and has been committed.
     */
    //public final static int XA_RDONLY = 0x00000003;

    /**
     * The transaction work has been prepared normally.
     */
    //public final static int XA_OK = 0;

    /****** XaResourceImpl private methods *****/

    public void setRollback(boolean value, Exception cause) {
        setRollback = value;
        rollbackCause = cause;
    }
   
  public boolean isComplete() {
    return this.resourceState==COMPLETE;
  }
   
    public void clearTransactionInfo(){
      this.resourceState=CREATED;
    }
   
    private boolean isXATracking() {
      return (epConnection.isConnectedToHABroker() && (XAResourceForRA.XATracking));
    }
   
    /**
     * remove xid in the XATable after commit/rollback successfully.
     * @param jmqXid
     */
    private void removeXid (JMQXid jmqXid) {
     
      if (isXATracking()) {
        //System.out.println("***** removing xid: " + jmqXid + " ,xatable size: " + xaTable.size());
        xaTable.remove(jmqXid);
        //System.out.println("***** removed xid: " + jmqXid + " ,xatable size: " + xaTable.size());
      }
    }
   
    private boolean isJoin(int flags){
      return((flags & XAResource.TMJOIN) == XAResource.TMJOIN);
    }
   
    private boolean isResume(int flags){
      return((flags & XAResource.TMRESUME) == XAResource.TMRESUME);
    }
   
    private boolean isFail(int flags){
      return((flags & XAResource.TMFAIL) == XAResource.TMFAIL);
    }
   
    private boolean isSuspend(int flags){
      return((flags & XAResource.TMSUSPEND) == XAResource.TMSUSPEND);
    }
   
    // Used for debugging only
    private String printXid(Xid foreignXid){
      return ("(GlobalTransactionID="+foreignXid.getGlobalTransactionId()) +
              ", BranchQualifier="+foreignXid.getBranchQualifier()+") ";
    }
   
    // Used for debugging only
    private String printFlags(int flags){
      String result = ("(Flags: ");
        if (isJoin(flags)){
          result=result+("JOIN ");
        }
        if (isNoFlags(flags)){
          result=result+("TMNOFLAGS ");
        }
        if (isFail(flags)){
          result=result+("TMFAIL ");
        }
        if (isOnePhase(flags)){
          result=result+("TMONEPHASE ");
        }
        if (isResume(flags)){
          result=result+("TMRESUME ");
        }
        if (isSuccess(flags)){
          result=result+("TMSUCCESS ");
        }
        if (isSuspend(flags)){
          result=result+("TMSUSPEND ");
        }   
        if (isTMENDRSCAN(flags)){
          result=result+("TMENDRSCAN ");
        }     
        if (TMSTARTRSCAN(flags)){
          result=result+("TMSTARTRSCAN ");
        }
        result=result+(")");
        return result;
    }
   
    // Used for debugging only
    private boolean isNoFlags(int flags){
      return((flags & XAResource.TMNOFLAGS) == XAResource.TMNOFLAGS);
    }
    // Used for debugging only
    private boolean isOnePhase(int flags){
      return((flags & XAResource.TMONEPHASE) == XAResource.TMONEPHASE);
    }

    // Used for debugging only
    private boolean isSuccess(int flags){
      return((flags & XAResource.TMSUCCESS) == XAResource.TMSUCCESS);
    }
  
    // Used for debugging only
    private boolean isTMENDRSCAN(int flags){
      return((flags & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN);
    }     
    // Used for debugging only
    private boolean TMSTARTRSCAN(int flags){
      return((flags & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN);
    }
}
TOP

Related Classes of com.sun.messaging.jmq.jmsclient.XAResourceForRA

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.