All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atomikos.datasource.xa.XAResourceTransaction Maven / Gradle / Ivy

There is a newer version: 6.0.0
Show newest version
/**
 * Copyright (C) 2000-2010 Atomikos 
 *
 * This code ("Atomikos TransactionsEssentials"), by itself,
 * is being distributed under the
 * Apache License, Version 2.0 ("License"), a copy of which may be found at
 * http://www.atomikos.com/licenses/apache-license-2.0.txt .
 * You may not use this file except in compliance with the License.
 *
 * While the License grants certain patent license rights,
 * those patent license rights only extend to the use of
 * Atomikos TransactionsEssentials by itself.
 *
 * This code (Atomikos TransactionsEssentials) contains certain interfaces
 * in package (namespace) com.atomikos.icatch
 * (including com.atomikos.icatch.Participant) which, if implemented, may
 * infringe one or more patents held by Atomikos.
 * It should be appreciated that you may NOT implement such interfaces;
 * licensing to implement these interfaces must be obtained separately from Atomikos.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

package com.atomikos.datasource.xa;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Stack;
import java.util.Vector;

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

import com.atomikos.datasource.RecoverableResource;
import com.atomikos.datasource.ResourceException;
import com.atomikos.datasource.ResourceTransaction;
import com.atomikos.icatch.CompositeTransaction;
import com.atomikos.icatch.DataSerializable;
import com.atomikos.icatch.HeurCommitException;
import com.atomikos.icatch.HeurHazardException;
import com.atomikos.icatch.HeurMixedException;
import com.atomikos.icatch.HeurRollbackException;
import com.atomikos.icatch.HeuristicMessage;
import com.atomikos.icatch.Participant;
import com.atomikos.icatch.RollbackException;
import com.atomikos.icatch.StringHeuristicMessage;
import com.atomikos.icatch.SysException;
import com.atomikos.icatch.TransactionControl;
import com.atomikos.icatch.TxState;
import com.atomikos.icatch.system.Configuration;
import com.atomikos.logging.Logger;
import com.atomikos.logging.LoggerFactory;
import com.atomikos.util.SerializationUtils;

/**
 *
 *
 * An implementation of ResourceTransaction for XA transactions.
 */

public class XAResourceTransaction implements ResourceTransaction,
        Externalizable, Participant, DataSerializable
{
	private static final Logger LOGGER = LoggerFactory.createLogger(XAResourceTransaction.class);

    static final long serialVersionUID = -8227293322090019196L;

    protected static String interpretErrorCode ( String resourceName , String opCode , Xid xid , int errorCode ) {

    		String msg = "unkown";
    		switch ( errorCode ) {
    			case XAException.XAER_RMFAIL:
    				msg = "the XA resource has become unavailable";
    				break;
    			case XAException.XA_RBROLLBACK:
    				msg = "the XA resource has rolled back for an unspecified reason";
    				break;
    			case XAException.XA_RBCOMMFAIL:
    				msg = "the XA resource rolled back due to a communication failure";
    				break;
    			case XAException.XA_RBDEADLOCK:
    				msg = "the XA resource has rolled back because of a deadlock";
    				break;
    			case XAException.XA_RBINTEGRITY:
    				msg = "the XA resource has rolled back due to a constraint violation";
    				break;
    			case XAException.XA_RBOTHER:
    				msg = "the XA resource has rolled back for an unknown reason";
    				break;
    			case XAException.XA_RBPROTO:
    				msg = "the XA resource has rolled back because it did not expect this command in the current context";
    				break;
    			case XAException.XA_RBTIMEOUT:
    				msg = "the XA resource has rolled back because the transaction took too long";
    				break;
    			case XAException.XA_RBTRANSIENT:
    				msg = "the XA resource has rolled back for a temporary reason - the transaction can be retried later";
    				break;
    			case XAException.XA_NOMIGRATE:
    				msg = "XA resume attempted in a different place from where suspend happened";
    				break;
    			case XAException.XA_HEURHAZ:
    				msg = "the XA resource may have heuristically completed the transaction";
    				break;
    			case XAException.XA_HEURCOM:
    				msg = "the XA resource has heuristically committed";
    				break;
    			case XAException.XA_HEURRB:
    				msg = "the XA resource has heuristically rolled back";
    				break;
    			case XAException.XA_HEURMIX:
    				msg = "the XA resource has heuristically committed some parts and rolled back other parts";
    				break;
    			case XAException.XA_RETRY:
    				msg = "the XA command had no effect and may be retried";
    				break;
    			case XAException.XA_RDONLY:
    				msg = "the XA resource had no updates to perform for this transaction";
    				break;
    			case XAException.XAER_RMERR:
    				msg = "the XA resource detected an internal error";
    				break;
    			case XAException.XAER_NOTA:
    				msg = "the supplied XID is invalid for this XA resource";
    				break;
    			case XAException.XAER_INVAL:
    				msg = "invalid arguments were given for the XA operation";
    				break;
    			case XAException.XAER_PROTO:
    				msg = "the XA resource did not expect this command in the current context";
    				break;
    			case XAException.XAER_DUPID:
    				msg = "the supplied XID already exists in this XA resource";
    				break;
    			case XAException.XAER_OUTSIDE:
    				msg = "the XA resource is currently involved in a local (non-XA) transaction";
    				break;
    			default: msg = "unknown";
    		}
    		return "XA resource '" + resourceName + "': " + opCode + " for XID '" + xidToHexString ( xid ) +
    			   "' raised " + errorCode + ": " + msg;
    }

    private String tid_ , root_;
    private boolean isXaSuspended_;
    private TxState state_;
    private String resourcename_;
    private transient Xid xid_;
    private transient String xidToHexString;
	private transient String toString;

    private void setXid(Xid xid) {
		this.xid_ = xid;
		xidToHexString=	xidToHexString(xid);
		toString = "XAResourceTransaction: "+xidToHexString;
	}

	private transient XATransactionalResource resource_;
    private transient XAResource xaresource_;
    private Vector heuristicMessages_;
    private transient boolean knownInResource;
    private transient int timeout_;

    public XAResourceTransaction ()
    {
        // needed for externalization mechanism
    }

    XAResourceTransaction(XATransactionalResource resource, CompositeTransaction transaction, String root)
    {
        setResource ( resource );
        TransactionControl control = transaction.getTransactionControl ();
        if ( control != null ) {
            timeout_ = (int) transaction.getTransactionControl ().getTimeout () / 1000;

        }
        tid_ = transaction.getTid ();
        root_ = root;
        resourcename_ = resource.getName ();
        setXid( resource_.createXid ( tid_ ));
        setState ( TxState.ACTIVE );
        heuristicMessages_ = new Vector ();
        isXaSuspended_ = false;
        knownInResource = false;
        addHeuristicMessage ( new StringHeuristicMessage ( "XA resource '"
                + resource.getName () + "' accessed with Xid '" + xidToHexString  + "'" ) );
    }

	void setResource ( XATransactionalResource resource )
	{
		this.resource_ = resource;
	}

	void setState ( TxState state )
	{
		this.state_ = state;
	}

   static String xidToHexString(Xid xid) {
    	String gtrid = StringUtils.byteArrayToHexString(xid.getGlobalTransactionId());
    	String bqual = StringUtils.byteArrayToHexString(xid.getBranchQualifier());

		return gtrid + ":" + bqual;
	}

	

	private void switchToHeuristicState ( String opCode , TxState state , XAException cause )
	{
		String errorMsg = interpretErrorCode ( resourcename_ , opCode , xid_ , cause.errorCode );
		addHeuristicMessage ( new StringHeuristicMessage ( errorMsg ) );
		setState ( state );
	}

	protected void testOrRefreshXAResourceFor2PC () throws XAException
    {
        try {

        	//fix for case 31209: refresh entire XAConnection on heur hazard
        	if ( state_.equals ( TxState.HEUR_HAZARD ) ) forceRefreshXAConnection();
        	else if ( xaresource_ != null ) { // null if connection failure
                xaresource_.isSameRM ( xaresource_ ); // test xa connection for liveness
            }
        } catch ( XAException xa ) {
            // timed out?
           if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( resourcename_
                    + ": XAResource needs refresh", xa );

            if ( resource_ == null ) {
    			// cf bug 67951 - happens on recovery without resource found
    			throwXAExceptionForUnavailableResource();
    		} else {
    			xaresource_ = resource_.getXAResource ();
    		}
        }

    }

	protected void forceRefreshXAConnection() throws XAException
	{
		if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( resourcename_ + ": forcing refresh of XAConnection..." );
		if ( resource_ == null ) {
			// cf bug 67951 - happens on recovery without resource found
			throwXAExceptionForUnavailableResource();
		}

		try {
			xaresource_ = resource_.refreshXAConnection();
		} catch ( ResourceException re ) {
			LOGGER.logWarning ( resourcename_ + ": could not refresh XAConnection" , re );
		}
	}

	private void throwXAExceptionForUnavailableResource() throws XAException
	{
		String msg = resourcename_ + ": resource no longer available - recovery might be at risk!";
		LOGGER.logWarning ( msg );
		XAException err = new XAException ( msg );
		err.errorCode = XAException.XAER_RMFAIL;
		throw err;
	}


    /**
     * Needed for garbage collection of res tx instances: if no new siblings can
     * arrive, this method removes any pointers to res txs in the resource
     */

    private void terminateInResource ()
    {
        if ( resource_ != null )
            resource_.removeSiblingMap ( root_ );
    }

    public void writeExternal ( ObjectOutput out ) throws IOException
    {
        out.writeObject ( xid_ );
        out.writeObject ( tid_ );
        out.writeObject ( root_ );
        out.writeObject ( state_ );
        // CLONE vector to ensure it gets re-written!
        out.writeObject ( heuristicMessages_.clone () );
        out.writeObject ( resourcename_ );
        if ( xaresource_ instanceof Serializable ) {
            // cf case 59238
            out.writeObject ( Boolean.TRUE );
            out.writeObject ( xaresource_ );
        } else {
            out.writeObject ( Boolean.FALSE );
        }
    }

    public void readExternal ( ObjectInput in ) throws IOException,
            ClassNotFoundException
    {

        setXid((Xid) in.readObject ());
        tid_ = (String) in.readObject ();
        root_ = (String) in.readObject ();
        state_ = (TxState) in.readObject ();

        heuristicMessages_ = (Vector) in.readObject ();
        resourcename_ = (String) in.readObject ();

        try {
			Boolean xaresSerializable = (Boolean) in.readObject();
			if (xaresSerializable !=null && xaresSerializable ) {
			    // cf case 59238
			    xaresource_ = ( XAResource ) in.readObject();
			}
		} catch (OptionalDataException e) {
			// happens if boolean is missing - like in older logfiles
			LOGGER.logDebug ("Ignoring missing field" , e );
		}

    }


    /**
     * @see ResourceTransaction.
     */

    public String getTid ()
    {
        return tid_;
    }


    /**
     * @see ResourceTransaction.
     */

    public void addHeuristicMessage ( HeuristicMessage mesg )
            throws IllegalStateException
    {
    	if(mesg!=null && mesg.toString()!=null){
    		heuristicMessages_.addElement ( mesg );
    	}

    }

    /**
     * @see ResourceTransaction.
     */

    public HeuristicMessage[] getHeuristicMessages ()
    {
        HeuristicMessage[] heurArray = new HeuristicMessage[1];
        return (HeuristicMessage[]) heuristicMessages_.toArray ( heurArray );

    }


    /**
     * @see ResourceTransaction.
     */

    public synchronized void suspend () throws ResourceException
    {
        Stack errors = new Stack ();

        //BugzID: 20545
        //State may be IN_DOUBT or TERMINATED when a connection is closed AFTER commit!
        //In that case, don't call END again, and also don't generate any error!
        //This is required for some hibernate connection release strategies.
        if ( state_.equals( ( TxState.ACTIVE ) ) ) {
	        try {
	        	if(LOGGER.isInfoEnabled()){
	        		LOGGER.logInfo("XAResource.end ( " + xidToHexString
		                    + " , XAResource.TMSUCCESS ) on resource "
		                    + resourcename_ + " represented by XAResource instance "
		                    + xaresource_);
	        	}
	            xaresource_.end ( xid_, XAResource.TMSUCCESS );

	        } catch ( XAException xaerr ) {
	            errors.push ( xaerr );
	            String msg = interpretErrorCode ( resourcename_ , "end" , xid_ , xaerr.errorCode );
	            if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( msg, xaerr );
	            throw new ResourceException ( msg, errors );
	        }
	        setState ( TxState.LOCALLY_DONE );
        }
    }
    
    boolean supportsTmJoin() {
    	return !(resource_.usesWeakCompare() || 
    			resource_.acceptsAllXAResources () ||
    			isActive());
    }

    /**
     * @see ResourceTransaction.
     */

    public synchronized void resume () throws ResourceException
    {
        int flag = 0;
        Stack errors = new Stack ();
        String logFlag = "";
        if ( state_.equals ( TxState.LOCALLY_DONE ) ) {// reused instance
            flag = XAResource.TMJOIN;
            logFlag = "XAResource.TMJOIN";
        } else if ( !knownInResource ) {// new instance
            flag = XAResource.TMNOFLAGS;
            logFlag = "XAResource.TMNOFLAGS";
        } else
            throw new IllegalStateException ( "Wrong state for resume: "
                    + state_ );

        try {
        	if(LOGGER.isInfoEnabled()){
        		LOGGER.logInfo("XAResource.start ( " + xidToHexString  + " , " + logFlag
                        + " ) on resource " + resourcename_
                        + " represented by XAResource instance " + xaresource_);
        	}
            xaresource_.start ( xid_, flag );

        } catch ( XAException xaerr ) {
        		String msg = interpretErrorCode ( resourcename_ , "resume" , xid_ , xaerr.errorCode );
            LOGGER.logWarning ( msg ,
                    xaerr );
            errors.push ( xaerr );
            throw new ResourceException ( msg ,
                    errors );
        }
        setState ( TxState.ACTIVE );
        knownInResource = true;
    }

    /**
     * @see Participant
     */

    public void setCascadeList ( java.util.Dictionary allParticipants )
            throws SysException
    {
        // nothing to do: local participant
    }

    public Object getState ()
    {
        return state_;
    }

    /**
     * @see Participant
     */
    public boolean recover () throws SysException
    {
        boolean recovered = false;
        // perform extra initialization

        if ( beforePrepare() ) {
        	//see case 23364: recovery before prepare should do nothing
        	//and certainly not reset the xaresource
        	return false;
        }

        recovered = tryRecoverWithEveryResourceToEnsureOurXidIsNotEndedByPresumedAbort();

        if ( !recovered && getXAResource() != null ) {
        	// cf case 59238: support serializable XAResource
        	recovered = true;
        }
        if (recovered) knownInResource = true;
        return recovered;
    }

    private boolean beforePrepare() {
		return TxState.ACTIVE.equals ( state_ ) || TxState.LOCALLY_DONE.equals ( state_ );
	}

	/**
     * Recovered XIDs can be shared in two resources
     * if they connect to the same back-end RM
     * (remember: we use the TM name for the branch!)
     * So each resource needs to know that our Xid
     * can be recovered, or endRecovery in one of them
     * will incorrectly rollback.
     * 
     * @return True iff at least one resource was found that recovers this instance.
     */
	private boolean tryRecoverWithEveryResourceToEnsureOurXidIsNotEndedByPresumedAbort() {
		boolean ret = false;		
		Enumeration resources = Configuration.getResources ();
        while ( resources.hasMoreElements () ) {
            RecoverableResource res = (RecoverableResource) resources.nextElement ();
            if ( res.recover ( this ) ) {
                ret = true;
            }

        }
		return ret;
	}

    /**
     * @see Participant
     */

    public void setGlobalSiblingCount ( int count )
    {
        // nothing to be done here
    }

    /**
     * @see Participant
     */

    public synchronized void forget ()
    {
        terminateInResource ();
        try { 
            if ( xaresource_ != null ) { // null if recovery failed
                xaresource_.forget ( xid_ );
            }
        } catch ( Exception err ) {
        	LOGGER.logDebug ( "Error forgetting xid: " + xid_ , err );
            // we don't care here
        }
        setState ( TxState.TERMINATED );
    }

    /**
     * @see Participant
     */

    public synchronized int prepare () throws RollbackException,
            HeurHazardException, HeurMixedException, SysException
    {
        int ret = 0;
        Stack errors = new Stack ();
        terminateInResource ();

        if ( TxState.ACTIVE.equals ( state_ ) ) {
        	// tolerate non-delisting apps/servers
            suspend ();
        }

        // duplicate prepares can happen for siblings in serial subtxs!!!
        // in that case, the second prepare just returns READONLY
        if ( state_.equals ( TxState.IN_DOUBT ) )
            return Participant.READ_ONLY;
        else if ( !state_.equals ( TxState.LOCALLY_DONE ) )
            throw new SysException ( "Wrong state for prepare: " + state_ );
        try {
            // refresh xaresource for MQSeries: seems to close XAResource after suspend???
            testOrRefreshXAResourceFor2PC ();
            if(LOGGER.isDebugEnabled()){
            	LOGGER.logDebug( "About to call prepare on XAResource instance: "
                        + xaresource_);
            }
            ret = xaresource_.prepare ( xid_ );

        } catch ( XAException xaerr ) {
        	String msg = interpretErrorCode ( resourcename_ , "prepare" , xid_ , xaerr.errorCode );
            LOGGER.logWarning ( msg , xaerr ); // see case 84253
            if ( (XAException.XA_RBBASE <= xaerr.errorCode)
                    && (xaerr.errorCode <= XAException.XA_RBEND) ) {
                throw new RollbackException ( msg );
            } else {
                errors.push ( xaerr );
                throw new SysException ( msg , errors );
            }
        }
        setState ( TxState.IN_DOUBT );
        if ( ret == XAResource.XA_RDONLY ) {
        	if(LOGGER.isInfoEnabled()){
        		LOGGER.logInfo("XAResource.prepare ( " + xidToHexString
                        + " ) returning XAResource.XA_RDONLY " + "on resource "
                        + resourcename_ + " represented by XAResource instance "
                        + xaresource_);
        	}
            return Participant.READ_ONLY;
        } else {
        	if(LOGGER.isInfoEnabled()){
        		LOGGER.logInfo("XAResource.prepare ( " + xidToHexString  + " ) returning OK "
                        + "on resource " + resourcename_
                        + " represented by XAResource instance " + xaresource_);
        	}
            return Participant.READ_ONLY + 1;
        }
    }

    /**
     * @see Participant.
     */

    public synchronized HeuristicMessage[] rollback ()
            throws HeurCommitException, HeurMixedException,
            HeurHazardException, SysException
    {
        Stack errors = new Stack ();
        terminateInResource ();

        if ( rollbackShouldDoNothing() ) {
            return null;
        } 
        if ( state_.equals ( TxState.TERMINATED ) ) {        	
        	return getHeuristicMessages ();
        }

        if ( state_.equals ( TxState.HEUR_MIXED ) )
            throw new HeurMixedException ( getHeuristicMessages () );
        if ( state_.equals ( TxState.HEUR_COMMITTED ) )
            throw new HeurCommitException ( getHeuristicMessages () );
        if ( xaresource_ == null ) { // if recover failed
        		LOGGER.logWarning ( "XAResourceTransaction " + getXid ()
                    + ": no XAResource to rollback - the required resource is probably not yet intialized?" );
            throw new HeurHazardException ( getHeuristicMessages () );
        }


        try {
            if ( state_.equals ( TxState.ACTIVE ) ) { // first suspend xid
                suspend ();
            }

            // refresh xaresource for MQSeries: seems to close XAResource after suspend???
            testOrRefreshXAResourceFor2PC ();
            if(LOGGER.isInfoEnabled()){
            	LOGGER.logInfo("XAResource.rollback ( " + xidToHexString  + " ) "
                        + "on resource " + resourcename_
                        + " represented by XAResource instance " + xaresource_);
            }
            xaresource_.rollback ( xid_ );

        } catch ( ResourceException resErr ) {
            // failure of suspend
            errors.push ( resErr );
            throw new SysException ( "Error in rollback: "
                    + resErr.getMessage (), errors );
        } catch ( XAException xaerr ) {
        	    String msg = interpretErrorCode ( resourcename_ , "rollback" , xid_ , xaerr.errorCode );
            if ( (XAException.XA_RBBASE <= xaerr.errorCode)
                    && (xaerr.errorCode <= XAException.XA_RBEND) ) { // do nothing, corresponds with semantics of rollback          
                if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( msg );
            } else {
                LOGGER.logWarning ( msg , xaerr );
                switch ( xaerr.errorCode ) {
                case XAException.XA_HEURHAZ:
                	switchToHeuristicState ( "rollback" , TxState.HEUR_HAZARD , xaerr );
                	throw new HeurHazardException ( getHeuristicMessages() );
                case XAException.XA_HEURMIX:
                	switchToHeuristicState ( "rollback" , TxState.HEUR_MIXED , xaerr );
                    throw new HeurMixedException ( getHeuristicMessages () );
                case XAException.XA_HEURCOM:
                	switchToHeuristicState ( "rollback", TxState.HEUR_COMMITTED , xaerr );
                    throw new HeurCommitException ( getHeuristicMessages () );
                case XAException.XA_HEURRB:
                    forget ();
                    break;
                case XAException.XAER_NOTA:
                	   //see case 21552
                	 if(LOGGER.isDebugEnabled()){
                		 LOGGER.logDebug("XAResource.rollback: invalid Xid - already rolled back in resource?");
                	 }
                    setState ( TxState.TERMINATED );
                    //ignore error - corresponds to semantics of rollback!
                    break;
                default:
                	//fix for bug 31209
                	switchToHeuristicState( "rollback", TxState.HEUR_HAZARD , xaerr );
                    errors.push ( xaerr );
                    throw new SysException ( msg , errors );
                } 
            }
        }
        setState ( TxState.TERMINATED );
        return getHeuristicMessages ();
    }

    private boolean rollbackShouldDoNothing() {
		return !knownInResource && beforePrepare();
	}

	/**
     * @see Participant
     */

    public synchronized HeuristicMessage[] commit ( boolean onePhase )
            throws HeurRollbackException, HeurHazardException,
            HeurMixedException, RollbackException, SysException
    {
        Stack errors = new Stack ();
        terminateInResource ();

        if ( state_.equals ( TxState.TERMINATED ) )
            return getHeuristicMessages ();
        if ( state_.equals ( TxState.HEUR_MIXED ) )
            throw new HeurMixedException ( getHeuristicMessages () );
        if ( state_.equals ( TxState.HEUR_ABORTED ) )
            throw new HeurRollbackException ( getHeuristicMessages () );
        if ( xaresource_ == null ) { // null if recovery failed
            LOGGER.logWarning ( "XAResourceTransaction " + getXid ()
                    + ": no XAResource to commit - the required resource is probably not yet intialized?" );
            throw new HeurHazardException ( getHeuristicMessages () );
        }

        try {

            if ( TxState.ACTIVE.equals ( state_ ) ) { // tolerate non-delisting apps/servers
                suspend ();
            }
        } catch ( ResourceException re ) {
            // happens if already rolled back or something else;
            // in any case the transaction can be trusted to act
            // as if rollback already happened
            throw new com.atomikos.icatch.RollbackException ( re.getMessage () );
        }

        if ( !(state_.equals ( TxState.LOCALLY_DONE ) || state_
                .equals ( TxState.IN_DOUBT ) || state_.equals ( TxState.HEUR_HAZARD )) )
            throw new SysException ( "Wrong state for commit: " + state_ );
        try {
            // refresh xaresource for MQSeries: seems to close XAResource after suspend???
            testOrRefreshXAResourceFor2PC ();
            if(LOGGER.isInfoEnabled()){
            	LOGGER.logInfo("XAResource.commit ( " + xidToHexString + " , " + onePhase
                        + " ) on resource " + resourcename_
                        + " represented by XAResource instance " + xaresource_);
            }
            xaresource_.commit ( xid_, onePhase );

        } catch ( XAException xaerr ) {
        	String msg = interpretErrorCode ( resourcename_ , "commit" , xid_ , xaerr.errorCode );
            LOGGER.logWarning ( msg , xaerr );

            if ( (XAException.XA_RBBASE <= xaerr.errorCode)
                    && (xaerr.errorCode <= XAException.XA_RBEND) ) {
                errors.push ( xaerr );

                if ( !onePhase )
                    throw new SysException ( msg , errors );
                else
                    throw new com.atomikos.icatch.RollbackException (
                            "Already rolled back in resource." );
            } else {
                switch ( xaerr.errorCode ) {
                case XAException.XA_HEURHAZ:
                	switchToHeuristicState ( "commit" , TxState.HEUR_HAZARD , xaerr );
                    throw new HeurHazardException ( getHeuristicMessages () );
                case XAException.XA_HEURMIX:
                	switchToHeuristicState ( "commit", TxState.HEUR_MIXED , xaerr );
                    throw new HeurMixedException ( getHeuristicMessages () );
                case XAException.XA_HEURCOM:
                    forget ();
                    break;
                case XAException.XA_HEURRB:
                	switchToHeuristicState ( "commit", TxState.HEUR_ABORTED , xaerr );
                    throw new HeurRollbackException ( getHeuristicMessages () );
                case XAException.XAER_NOTA:
                    if ( ! onePhase ) {
                    	   //see case 21552
                    	LOGGER.logWarning("XAResource.commit: invalid Xid - transaction already committed in resource?");
                        setState ( TxState.TERMINATED );
                        break;
                    }
                default:
                	//fix for bug 31209
                	switchToHeuristicState( "commit", TxState.HEUR_HAZARD , xaerr );
                    errors.push ( xaerr );
                    throw new SysException ( msg , errors );
                } 
            } 
        }
        setState ( TxState.TERMINATED );
        return getHeuristicMessages ();
    }

    /**
     * Absolutely necessary for coordinator to work correctly
     */

    public boolean equals ( Object o )
    {
    	if (this==o) return true;
    	
        // NOTE: basing equals on the xid means that if two
        // different instances for the same xid exist then these
        // will be considered the same, and a second will
        // NOT be added to the coordinator's participant list.
        // However, this is not a problem, since the first added instance
        // will do all commit work (they are equivalent).
        // Note that this can ONLY happen for two invocations of the
        // SAME local composite transaction to the SAME resource.
        // Because INSIDE ONE local transaction there is no
        // internal parallellism, having two invocations to the same
        // resource execute with the same xid is not a problem.

        if (!(o instanceof XAResourceTransaction))  return false;

        XAResourceTransaction other = (XAResourceTransaction) o;
        return xid_.equals ( other.xid_ );
    }

    /**
     * Absolutely necessary for coordinator to work correctly
     */

    public int hashCode ()
    {
        return xidToHexString.hashCode();
    }

    public String toString ()
    {
        return toString;
    }

    /**
     * Get the Xid. Needed by jta mappings.
     *
     * @return Xid The Xid of this restx.
     */

    public Xid getXid ()
    {
        return xid_;
    }

    protected void setRecoveredXAResource ( XAResource xaresource )
    {
    	// See case 25671: only reset xaresource if NOT enlisted!
    	// Otherwise, the delist will fail since XA does not allow
    	// enlist/delist on different xaresource instances.
    	// This should not interfere with recovery since a recovered
    	// instance will NOT have state ACTIVE...
    	if ( ! TxState.ACTIVE.equals ( state_ ) ) {
    		setXAResource ( xaresource );
    	}
    }

    /**
     * Set the XAResource attribute.
     *
     * @param xaresource
     *            The new XAResource to use. This new XAResource represents the
     *            new connection to the XA database. Necessary because on reuse,
     *            the old xaresource may be in use by another thread, for
     *            another transaction.
     */

    public void setXAResource ( XAResource xaresource )
    {
    	if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( this + ": about to switch to XAResource " + xaresource );
    	xaresource_ = xaresource;
    	try {
    		xaresource_.setTransactionTimeout ( timeout_ );
    	} catch ( XAException e ) {
    		String msg = interpretErrorCode ( resourcename_ , "setTransactionTimeout" , xid_ , e.errorCode );
    		LOGGER.logWarning ( msg , e );
    		// we don't care
    	}
    	if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "XAResourceTransaction " + getXid() + ": switched to XAResource " + xaresource );
    }

    /**
     * Perform an XA suspend.
     */

    public void xaSuspend () throws XAException
    {
    	// cf case 61305: make XA suspend idempotent so appserver suspends do not interfere with our suspends (triggered by transaction suspend)
    	if ( !isXaSuspended_ ) {
    		try {
    			if(LOGGER.isInfoEnabled()){
    				LOGGER.logInfo("XAResource.suspend ( " + xidToHexString
        					+ " , XAResource.TMSUSPEND ) on resource "
        					+ resourcename_ + " represented by XAResource instance "
        					+ xaresource_);
    			}
    			xaresource_.end ( xid_, XAResource.TMSUSPEND );

    			isXaSuspended_ = true;
    		}
    		catch ( XAException xaerr ) {
    			String msg = interpretErrorCode ( resourcename_ , "suspend" , xid_ , xaerr.errorCode );
    			LOGGER.logWarning ( msg , xaerr );
    			throw xaerr;
    		}
    	}
    }

    /**
     * Perform an xa resume
     */

    public void xaResume () throws XAException
    {
        try {
        	if(LOGGER.isInfoEnabled()){
        		LOGGER.logInfo("XAResource.start ( " + xidToHexString
                    + " , XAResource.TMRESUME ) on resource "
                    + resourcename_ + " represented by XAResource instance "
                    + xaresource_);
        		}
        	xaresource_.start ( xid_, XAResource.TMRESUME );

            isXaSuspended_ = false;
        }
        catch ( XAException xaerr ) {
			String msg = interpretErrorCode ( resourcename_ , "resume" , xid_ , xaerr.errorCode );
			LOGGER.logWarning ( msg , xaerr );
			throw xaerr;
		}

    }

    /**
     * Test if the resource has been ended with TMSUSPEND.
     *
     * @return boolean True if so.
     */
    public boolean isXaSuspended ()
    {
        return isXaSuspended_;
    }

    /**
     * Test if the restx is active (in use).
     *
     * @return boolean True if so.
     */
    public boolean isActive ()
    {
        return state_.equals ( TxState.ACTIVE );
    }

    /**
     * @see com.atomikos.icatch.Participant#getURI()
     */
    public String getURI ()
    {

        return null;
    }

	String getResourceName()
	{
		return resourcename_;
	}

	XAResource getXAResource()
	{
		return xaresource_;
	}

	public void writeData(DataOutput out) throws IOException {
		byte[] data= SerializationUtils.serialize((Serializable)xid_);
		out.writeInt(data.length);
		out.write(data);	
		out.writeUTF(tid_);
		out.writeUTF(root_);
		out.writeUTF(state_.toString());
		out.writeUTF(resourcename_);
		out.writeInt(heuristicMessages_.size());
		for (Iterator iterator = heuristicMessages_.iterator(); iterator.hasNext();) {
			HeuristicMessage heuristicMessage = (HeuristicMessage) iterator.next();
			out.writeUTF(heuristicMessage.toString());
		}
		
		if (xaresource_ instanceof Serializable) {
			// cf case 59238
			out.writeBoolean(true);
			byte[] bytes = SerializationUtils.serialize((Serializable) xaresource_);
			out.writeInt(bytes.length);
			out.write(bytes);
		} else {
			out.writeBoolean(false);
		}

	}

	public void readData(DataInput in) throws IOException {
		// xid_ ???

		//String branchQualifier = in.readUTF();
		int len = in.readInt();
		byte[] data= new byte[len];
		in.readFully(data);
		xid_= SerializationUtils.deserialize(data);
		
		tid_ = in.readUTF();
		setXid(xid_);

		root_ = in.readUTF();
		state_ = TxState.valueOf(in.readUTF());
		resourcename_ = in.readUTF();
		int nbMessages = in.readInt();
		heuristicMessages_ = new Vector(nbMessages);
		for (int i = 0; i < nbMessages; i++) {
			heuristicMessages_.add(new StringHeuristicMessage(in.readUTF()));
		}
		

		boolean xaresourceSerializable = in.readBoolean();
		if (xaresourceSerializable) {
			int size = in.readInt();
			byte[] bytes = new byte[size];
			in.readFully(bytes);
			xaresource_ = SerializationUtils.deserialize(bytes);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy