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

com.pivotal.gemfirexd.internal.client.net.NetXAResource Maven / Gradle / Ivy

The newest version!
/*

   Derby - Class com.pivotal.gemfirexd.internal.client.net.NetXAResource

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   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.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

/*
 * Changes for GemFireXD distributed data platform (some marked by "GemStone changes")
 *
 * Portions Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
/**********************************************************************
 *
 *
 *  Component Name =
 *
 *      Package Name = com.pivotal.gemfirexd.internal.client.net
 *
 *  Descriptive Name = class implements XAResource
 *
 *  Status = New code
 *
 *  Function = Handle XA methods
 *
 *  List of Classes
 *              - NetXAResource
 *
 *  Restrictions : None
 *
 **********************************************************************/
package com.pivotal.gemfirexd.internal.client.net;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import javax.sql.XAConnection;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import com.gemstone.gemfire.internal.shared.ClientSharedUtils;
import com.pivotal.gemfirexd.internal.client.ClientXid;
import com.pivotal.gemfirexd.internal.client.am.Connection;
import com.pivotal.gemfirexd.internal.client.am.SqlException;
import com.pivotal.gemfirexd.internal.client.am.ClientMessageId;
import com.pivotal.gemfirexd.internal.shared.common.reference.SQLState;

public class NetXAResource implements XAResource {
    public static final int TMTIMEOUT = 0x00000100;
    public static final int ACTIVE_ONLY = -1;
    public static final int XA_NULL_XID = -1; // null Xid has Format Id of -1
    public static final int INITIAL_CALLINFO_ELEMENTS = 1;
    public static final int RECOVER_XID_ARRAY_LENGTH = 10;
    public static final ClientXid nullXid = new ClientXid();

    // xaFunction defines, shows which queued XA function is being performed
    public static final int XAFUNC_NONE = 0;
    public static final int XAFUNC_COMMIT = 1;
    public static final int XAFUNC_END = 2;
    public static final int XAFUNC_FORGET = 3;
    public static final int XAFUNC_PREPARE = 4;
    public static final int XAFUNC_RECOVER = 5;
    public static final int XAFUNC_ROLLBACK = 6;
    public static final int XAFUNC_START = 7;
    public static final String XAFUNCSTR_NONE = "No XA Function";
    public static final String XAFUNCSTR_COMMIT = "XAResource.commit()";
    public static final String XAFUNCSTR_END = "XAResource.end()";
    public static final String XAFUNCSTR_FORGET = "XAResource.forget()";
    public static final String XAFUNCSTR_PREPARE = "XAResource.prepare()";
    public static final String XAFUNCSTR_RECOVER = "XAResource.recover()";
    public static final String XAFUNCSTR_ROLLBACK = "XAResource.rollback()";
    public static final String XAFUNCSTR_START = "XAResource.start()";

    public int nextElement = 0;

    // XAResources with same RM group list
    protected static Vector xaResourceSameRMGroup_ = new Vector();
    protected int sameRMGroupIndex_ = 0;
    protected NetXAResource nextSameRM_ = null;
    protected boolean ignoreMe_ = false;



    public com.pivotal.gemfirexd.internal.client.am.SqlException exceptionsOnXA = null;

    XAConnection xaconn_;
    com.pivotal.gemfirexd.internal.client.net.NetXAConnection netXAConn_;
    com.pivotal.gemfirexd.internal.client.net.NetConnection conn_;
    private boolean keepIsolationLevel;
    int rmId_; // unique RmId generated by XAConnection
    // TODO: change to a single callInfo field (not an array)
    NetXACallInfo callInfoArray_[] =
            new NetXACallInfo[INITIAL_CALLINFO_ELEMENTS];
    int numXACallInfo_ = INITIAL_CALLINFO_ELEMENTS;
    int connectionCount_ = 1;
    int activeXATransCount_ = 0;
    String rmIdx_; // userid in case we need to create a secondary connection
    String rmIdy_; // password in case we need to create a secondary connection
    // TODO: remove port and ipaddr_
    int port_;     // port needed to make secondary connection for recover in DS mode.
    String ipaddr_;  // ip address needed to make secondary connection for recover in DS mode.

    private List specialRegisters_ = Collections.synchronizedList(new LinkedList());

    /** The value of the transaction timeout in seconds. */
    private int timeoutSeconds = 0;

    public NetXAResource(XAConnection xaconn, int rmId,
                         String userId, String password,
                         com.pivotal.gemfirexd.internal.client.net.NetXAConnection conn) {
        xaconn_ = xaconn;
        rmId_ = rmId;
        conn_ = conn.getNetConnection();
        netXAConn_ = conn;
        rmIdx_ = userId;
        rmIdy_ = password;
        port_ = conn_.netAgent_.getPort();
        ipaddr_ = conn_.netAgent_.socket_.getLocalAddress().getHostAddress();
        conn.setNetXAResource(this);

        // link the primary connection to the first XACallInfo element
        conn_.currXACallInfoOffset_ = 0;

        // construct the NetXACallInfo object for the array.
        for (int i = 0; i < INITIAL_CALLINFO_ELEMENTS; ++i) {
            callInfoArray_[i] = new NetXACallInfo(null, XAResource.TMNOFLAGS, this,
                    null);
        }

        // initialize the first XACallInfo element with the information from the
        //  primary connection
        callInfoArray_[0].actualConn_ = conn;
        callInfoArray_[0].currConnection_ = true;
        callInfoArray_[0].freeEntry_ = false;
        // ~~~ save conn_ connection variables in callInfoArray_[0]
        callInfoArray_[0].saveConnectionVariables();

        // add this new XAResource to the list of other XAResources for the Same RM
        initForReuse();
    }

    public void commit(Xid xid, boolean onePhase) throws XAException {
        NetAgent netAgent = conn_.netAgent_;
        int rc = XAResource.XA_OK;
        
        exceptionsOnXA = null;
        if (conn_.agent_.loggingEnabled()) {
            conn_.agent_.logWriter_.traceEntry(this, "commit", xid, onePhase);
        }
        if (conn_.isPhysicalConnClosed()) {
            connectionClosedFailure();
        }

        // update the XACallInfo
        NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
        callInfo.xaFlags_ = (onePhase ? XAResource.TMONEPHASE :
                XAResource.TMNOFLAGS);
        callInfo.xid_ = xid;
        callInfo.xaResource_ = this;
        callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
        try {
            netAgent.beginWriteChainOutsideUOW();
            netAgent.netConnectionRequest_.writeXaCommit(conn_, xid);
            netAgent.flowOutsideUOW();
            netAgent.netConnectionReply_.readXaCommit(conn_);
            if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it
                callInfo.xaFunction_ = XAFUNC_COMMIT;
                rc = xaRetValErrorAccumSQL(callInfo, rc);
                callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
            }
            netAgent.endReadChain();
        } catch (SqlException sqle) {
            rc = XAException.XAER_RMERR;
            exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException
                    (sqle, exceptionsOnXA);
        } finally {
            conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
        }
        if (rc != XAResource.XA_OK) {
            throwXAException(rc, false);
        }
    }

    /**
     * Ends the work performed on behalf of a transaction branch. The resource manager dissociates the XA resource from
     * the transaction branch specified and let the transaction be completed.
     * 

* If TMSUSPEND is specified in flags, the transaction branch is temporarily suspended in incomplete state. The * transaction context is in suspened state and must be resumed via start with TMRESUME specified. *

* If TMFAIL is specified, the portion of work has failed. The resource manager may mark the transaction as * rollback-only *

* If TMSUCCESS is specified, the portion of work has completed successfully. * * @param xid A global transaction identifier that is the same as what was used previously in the start method. * @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND * * @throws XAException An error has occurred. Possible XAException values are XAER_RMERR, XAER_RMFAILED, XAER_NOTA, * XAER_INVAL, XAER_PROTO, or XA_RB*. */ public void end(Xid xid, int flags) throws XAException { NetAgent netAgent = conn_.netAgent_; int rc = XAResource.XA_OK; exceptionsOnXA = null; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "end", xid, flags); } if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; callInfo.setReadOnlyTransactionFlag(conn_.readOnlyTransaction_); callInfo.xaFlags_ = flags; callInfo.xid_ = xid; callInfo.xaResource_ = this; callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL try { netAgent.beginWriteChainOutsideUOW(); netAgent.netConnectionRequest_.writeXaEndUnitOfWork(conn_); netAgent.flowOutsideUOW(); rc = netAgent.netConnectionReply_.readXaEndUnitOfWork(conn_); conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending end if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it callInfo.xaFunction_ = XAFUNC_END; rc = xaRetValErrorAccumSQL(callInfo, rc); callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL } netAgent.endReadChain(); } catch (SqlException sqle) { rc = XAException.XAER_RMERR; exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (sqle, exceptionsOnXA); } finally { conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo } if (rc != XAResource.XA_OK) { // The corresponding XA connection association state // is changed by setXaStateForXAException inside the call // to throwXAException according the error code of the XAException // to be thrown. throwXAException(rc, false); }else { conn_.setXAState(Connection.XA_T0_NOT_ASSOCIATED); } } /** * Tell the resource manager to forget about a heuristically (MANUALLY) completed transaction branch. * * @param xid A global transaction identifier * * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, XAER_NOTA, * XAER_INVAL, or XAER_PROTO. */ public void forget(Xid xid) throws XAException { NetAgent netAgent = conn_.netAgent_; int rc = XAResource.XA_OK; exceptionsOnXA = null; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "forget", xid); } if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; callInfo.xid_ = xid; callInfo.xaResource_ = this; callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL try { // flow the required PROTOCOL to the server netAgent.beginWriteChainOutsideUOW(); // sent the commit PROTOCOL netAgent.netConnectionRequest_.writeXaForget(netAgent.netConnection_, xid); netAgent.flowOutsideUOW(); // read the reply to the commit netAgent.netConnectionReply_.readXaForget(netAgent.netConnection_); netAgent.endReadChain(); if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it callInfo.xaFunction_ = XAFUNC_FORGET; rc = xaRetValErrorAccumSQL(callInfo, rc); callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL } } catch (SqlException sqle) { exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (sqle, exceptionsOnXA); throwXAException(XAException.XAER_RMERR); } finally { conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo } if (rc != XAResource.XA_OK) { throwXAException(rc, false); } } /** * Obtain the current transaction timeout value set for this XAResource * instance. If XAResource.setTransactionTimeout was not use prior to * invoking this method, the return value is 0; otherwise, the value * used in the previous setTransactionTimeout call is returned. * * @return the transaction timeout value in seconds. If the returned value * is equal to Integer.MAX_VALUE it means no timeout. */ public int getTransactionTimeout() throws XAException { if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "getTransactionTimeout"); } exceptionsOnXA = null; if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceExit(this, "getTransactionTimeout", timeoutSeconds); } return timeoutSeconds; } /** * Ask the resource manager to prepare for a transaction commit of the transaction specified in xid. * * @param xid A global transaction identifier * * @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. * * @throws XAException An error has occurred. Possible exception values are: XA_RB*, XAER_RMERR, XAER_RMFAIL, * XAER_NOTA, XAER_INVAL, or XAER_PROTO. */ public int prepare(Xid xid) throws XAException { // public interface for prepare // just call prepareX with the recursion flag set to true exceptionsOnXA = null; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "prepare", xid); } if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } /// update the XACallInfo NetAgent netAgent = conn_.netAgent_; int rc = XAResource.XA_OK; NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; callInfo.xid_ = xid; callInfo.xaResource_ = this; callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL try { netAgent.beginWriteChainOutsideUOW(); // sent the prepare PROTOCOL netAgent.netConnectionRequest_.writeXaPrepare(conn_); netAgent.flowOutsideUOW(); // read the reply to the prepare rc = netAgent.netConnectionReply_.readXaPrepare(conn_); if ((callInfo.xaRetVal_ != XAResource.XA_OK) && (callInfo.xaRetVal_ != XAException.XA_RDONLY)) { // xaRetVal has possible error, format it callInfo.xaFunction_ = XAFUNC_PREPARE; rc = xaRetValErrorAccumSQL(callInfo, rc); callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL } netAgent.endReadChain(); } catch (SqlException sqle) { rc = XAException.XAER_RMERR; exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (sqle, exceptionsOnXA); } finally { conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo } if ((rc != XAResource.XA_OK ) && (rc != XAResource.XA_RDONLY)) { throwXAException(rc, false); } if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceExit(this, "prepare", rc); } return rc; } /** * Obtain 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 flag One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be used when no other flags are set in * flags. * * @return The resource manager returns zero or more XIDs for the transaction branches that are currently in a * prepared or heuristically completed state. If an error occurs during the operation, the resource manager * should raise the appropriate XAException. * * @throws XAException An error has occurred. Possible values are XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and * XAER_PROTO. */ public Xid[] recover(int flag) throws XAException { int rc = XAResource.XA_OK; NetAgent netAgent = conn_.netAgent_; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "recover", flag); } exceptionsOnXA = null; if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } Xid[] xidList = null; int numXid = 0; NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; callInfo.xaFlags_ = flag; callInfo.xaResource_ = this; callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL try { netAgent.beginWriteChainOutsideUOW(); // sent the recover PROTOCOL netAgent.netConnectionRequest_.writeXaRecover(conn_, flag); netAgent.flowOutsideUOW(); netAgent.netConnectionReply_.readXaRecover(conn_); if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it callInfo.xaFunction_ = XAFUNC_RECOVER; rc = xaRetValErrorAccumSQL(callInfo, rc); callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL } netAgent.endReadChain(); if (conn_.indoubtTransactions_ != null) { numXid = conn_.indoubtTransactions_.size(); xidList = new Xid[numXid]; int i = 0; nextElement = 0; for (Enumeration e = conn_.indoubtTransactions_.keys(); e.hasMoreElements(); i++) { xidList[i] = (Xid) e.nextElement(); } } } catch (SqlException sqle) { rc = XAException.XAER_RMERR; exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (sqle, exceptionsOnXA); } finally { conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo } if (rc != XAResource.XA_OK) { throwXAException(rc, false); } if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceExit(this, "recover", xidList); } return xidList; } /** * Inform the resource manager to roll back work done on behalf of a transaction branch * * @param xid A global transaction identifier * * @throws XAException An error has occurred */ public void rollback(Xid xid) throws XAException { NetAgent netAgent = conn_.netAgent_; int rc = XAResource.XA_OK; exceptionsOnXA = null; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "rollback", xid); } if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } // update the XACallInfo NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; callInfo.xid_ = xid; callInfo.xaResource_ = this; callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL try { netAgent.beginWriteChainOutsideUOW(); netAgent.netConnectionRequest_.writeXaRollback(conn_, xid); netAgent.flowOutsideUOW(); // read the reply to the rollback rc = netAgent.netConnectionReply_.readXaRollback(conn_); netAgent.endReadChain(); if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it callInfo.xaFunction_ = XAFUNC_END; rc = xaRetValErrorAccumSQL(callInfo, rc); callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL } } catch (SqlException sqle) { rc = XAException.XAER_RMERR; exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (sqle, exceptionsOnXA); } finally { conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo } if (rc != XAResource.XA_OK) { throwXAException(rc, false); } } /** * Set the current transaction timeout value for this XAResource * instance. Once set, this timeout value is effective until * setTransactionTimeout 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 true; otherwise false. If a resource * manager does not support transaction timeout value to be set * explicitly, this method returns false. * * @param seconds the transaction timeout value in seconds. * Value of 0 means the reasource manager's default value. * Value of Integer.MAX_VALUE means no timeout. * @return true if transaction timeout value is set successfully; * otherwise false. * * @exception XAException - An error has occurred. Possible exception * values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL. */ public boolean setTransactionTimeout(int seconds) throws XAException { if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "setTransactionTimeout"); } if (seconds < 0) { // throw an exception if invalid value was specified throw new XAException(XAException.XAER_INVAL); } exceptionsOnXA = null; timeoutSeconds = seconds; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceExit(this, "setTransactionTimeout", true); } return true; } public void setKeepCurrentIsolationLevel(boolean flag) { keepIsolationLevel = flag; } public boolean keepCurrentIsolationLevel() { return keepIsolationLevel; } /** * Start work on behalf of a transaction branch specified in xid * * @param xid A global transaction identifier to be associated with the resource * @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME * * @throws 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 xid, int flags) throws XAException { NetAgent netAgent = conn_.netAgent_; int rc = XAResource.XA_OK; exceptionsOnXA = null; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "start", xid, flags); } if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } // DERBY-1025 - Flow an auto-commit if in auto-commit mode before // entering a global transaction try { if(conn_.autoCommit_) conn_.flowAutoCommit(); } catch (SqlException sqle) { rc = XAException.XAER_RMERR; exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (sqle, exceptionsOnXA); } // update the XACallInfo NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; callInfo.xaFlags_ = flags; callInfo.xaInProgress_ = true; callInfo.xid_ = xid; callInfo.xaResource_ = this; callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL // check and setup the transaction timeout settings if (flags == TMNOFLAGS) { if (timeoutSeconds == Integer.MAX_VALUE) { // Disable the transaction timeout. callInfo.xaTimeoutMillis_ = 0; } else if (timeoutSeconds > 0) { // Use the timeout value specified. callInfo.xaTimeoutMillis_ = 1000*timeoutSeconds; } else if (timeoutSeconds == 0) { // The -1 value means that the timeout codepoint // will not be sent in the request and thus the server // will use the default value. callInfo.xaTimeoutMillis_ = -1; } else { // This should not ever happen due that setTransactionTimeout // does not allow a negative value throwXAException(XAException.XAER_RMERR); } } try { netAgent.beginWriteChainOutsideUOW(); netAgent.netConnectionRequest_.writeXaStartUnitOfWork(conn_); netAgent.flowOutsideUOW(); netAgent.netConnectionReply_.readXaStartUnitOfWork(conn_); if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it callInfo.xaFunction_ = XAFUNC_START; rc = xaRetValErrorAccumSQL(callInfo, rc); callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL } // Setting this is currently required to avoid client from sending // commit for autocommit. if (rc == XAResource.XA_OK) { conn_.setXAState(Connection.XA_T1_ASSOCIATED); } } catch (SqlException sqle) { rc = XAException.XAER_RMERR; exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (sqle, exceptionsOnXA); } finally { conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo } if (rc != XAResource.XA_OK) { throwXAException(rc, false); } } protected void throwXAException(int rc) throws XAException { throwXAException(rc, rc != XAException.XAER_NOTA); } private String getXAExceptionText(int rc) { String xaExceptionText; switch (rc) { case javax.transaction.xa.XAException.XA_RBROLLBACK: xaExceptionText = "XA_RBROLLBACK"; break; case javax.transaction.xa.XAException.XA_RBCOMMFAIL: xaExceptionText = "XA_RBCOMMFAIL"; break; case javax.transaction.xa.XAException.XA_RBDEADLOCK: xaExceptionText = "XA_RBDEADLOCK"; break; case javax.transaction.xa.XAException.XA_RBINTEGRITY: xaExceptionText = "XA_RBINTEGRITY"; break; case javax.transaction.xa.XAException.XA_RBOTHER: xaExceptionText = "XA_RBOTHER"; break; case javax.transaction.xa.XAException.XA_RBPROTO: xaExceptionText = "XA_RBPROTO"; break; case javax.transaction.xa.XAException.XA_RBTIMEOUT: xaExceptionText = "XA_RBTIMEOUT"; break; case javax.transaction.xa.XAException.XA_RBTRANSIENT: xaExceptionText = "XA_RBTRANSIENT"; break; case javax.transaction.xa.XAException.XA_NOMIGRATE: xaExceptionText = "XA_NOMIGRATE"; break; case javax.transaction.xa.XAException.XA_HEURHAZ: xaExceptionText = "XA_HEURHAZ"; break; case javax.transaction.xa.XAException.XA_HEURCOM: xaExceptionText = "XA_HEURCOM"; break; case javax.transaction.xa.XAException.XA_HEURRB: xaExceptionText = "XA_HEURRB"; break; case javax.transaction.xa.XAException.XA_HEURMIX: xaExceptionText = "XA_HEURMIX"; break; case javax.transaction.xa.XAException.XA_RETRY: xaExceptionText = "XA_RETRY"; break; case javax.transaction.xa.XAException.XA_RDONLY: xaExceptionText = "XA_RDONLY"; break; case javax.transaction.xa.XAException.XAER_ASYNC: xaExceptionText = "XAER_ASYNC"; break; case javax.transaction.xa.XAException.XAER_RMERR: xaExceptionText = "XAER_RMERR"; break; case javax.transaction.xa.XAException.XAER_NOTA: xaExceptionText = "XAER_NOTA"; break; case javax.transaction.xa.XAException.XAER_INVAL: xaExceptionText = "XAER_INVAL"; break; case javax.transaction.xa.XAException.XAER_PROTO: xaExceptionText = "XAER_PROTO"; break; case javax.transaction.xa.XAException.XAER_RMFAIL: xaExceptionText = "XAER_RMFAIL"; break; case javax.transaction.xa.XAException.XAER_DUPID: xaExceptionText = "XAER_DUPID"; break; case javax.transaction.xa.XAException.XAER_OUTSIDE: xaExceptionText = "XAER_OUTSIDE"; break; case XAResource.XA_OK: xaExceptionText = "XA_OK"; break; default: xaExceptionText = "Unknown Error"; break; } return xaExceptionText; } protected void throwXAException(int rc, boolean resetFlag) throws XAException { // ~~~ String xaExceptionText; if (resetFlag) { // reset the state of the failed connection NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; callInfo.xaInProgress_ = false; callInfo.xaWasSuspended = false; } xaExceptionText = getXAExceptionText(rc); // save the SqlException chain to add it to the XAException com.pivotal.gemfirexd.internal.client.am.SqlException sqlExceptions = exceptionsOnXA; while (exceptionsOnXA != null) { // one or more SqlExceptions received, format them xaExceptionText = xaExceptionText + " : " + exceptionsOnXA.getMessage(); exceptionsOnXA = (com.pivotal.gemfirexd.internal.client.am.SqlException) exceptionsOnXA.getNextException(); } com.pivotal.gemfirexd.internal.client.am.XaException xaException = new com.pivotal.gemfirexd.internal.client.am.XaException(conn_.agent_.logWriter_, sqlExceptions, xaExceptionText); xaException.errorCode = rc; setXaStateForXAException(rc); throw xaException; } /** * Reset the transaction branch association state to XA_T0_NOT_ASSOCIATED * for XAER_RM* and XA_RB* Exceptions. All other exeptions leave the state * unchanged * * @param rc // return code from XAException * @throws XAException */ private void setXaStateForXAException(int rc) { switch (rc) { // Reset to T0, not associated for XA_RB*, RM* // XAER_RMFAIL and XAER_RMERR will be fatal to the connection // but that is not dealt with here case javax.transaction.xa.XAException.XAER_RMFAIL: case javax.transaction.xa.XAException.XAER_RMERR: case javax.transaction.xa.XAException.XA_RBROLLBACK: case javax.transaction.xa.XAException.XA_RBCOMMFAIL: case javax.transaction.xa.XAException.XA_RBDEADLOCK: case javax.transaction.xa.XAException.XA_RBINTEGRITY: case javax.transaction.xa.XAException.XA_RBOTHER: case javax.transaction.xa.XAException.XA_RBPROTO: case javax.transaction.xa.XAException.XA_RBTIMEOUT: case javax.transaction.xa.XAException.XA_RBTRANSIENT: conn_.setXAState(Connection.XA_T0_NOT_ASSOCIATED); break; // No change for other XAExceptions // javax.transaction.xa.XAException.XA_NOMIGRATE //javax.transaction.xa.XAException.XA_HEURHAZ // javax.transaction.xa.XAException.XA_HEURCOM // javax.transaction.xa.XAException.XA_HEURRB // javax.transaction.xa.XAException.XA_HEURMIX // javax.transaction.xa.XAException.XA_RETRY // javax.transaction.xa.XAException.XA_RDONLY // javax.transaction.xa.XAException.XAER_ASYNC // javax.transaction.xa.XAException.XAER_NOTA // javax.transaction.xa.XAException.XAER_INVAL // javax.transaction.xa.XAException.XAER_PROTO // javax.transaction.xa.XAException.XAER_DUPID // javax.transaction.xa.XAException.XAER_OUTSIDE default: return; } } public boolean isSameRM(XAResource xares) throws XAException { boolean isSame = false; // preset that the RMs are NOT the same exceptionsOnXA = null; if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceEntry(this, "isSameRM", xares); } if (conn_.isPhysicalConnClosed()) { connectionClosedFailure(); } if (xares instanceof com.pivotal.gemfirexd.internal.client.net.NetXAResource) { // both are NetXAResource so check to see if this is the same RM // remember, isSame is initialized to false NetXAResource derbyxares = (NetXAResource) xares; while (true) { if (!conn_.databaseName_.equalsIgnoreCase(derbyxares.conn_.databaseName_)) { break; // database names are not equal, not same RM } if (!conn_.netAgent_.server_.equalsIgnoreCase (derbyxares.conn_.netAgent_.server_)) { // server name strings not equal, compare IP addresses try { // 1st convert "localhost" to actual server name String server1 = this.processLocalHost(conn_.netAgent_.server_); String server2 = this.processLocalHost(derbyxares.conn_.netAgent_.server_); // now convert the server name to ip address InetAddress serverIP1 = InetAddress.getByName(server1); InetAddress serverIP2 = InetAddress.getByName(server2); if (!serverIP1.equals(serverIP2)) { break; // server IPs are not equal, not same RM } } catch (UnknownHostException ue) { break; } } if (conn_.netAgent_.port_ != derbyxares.conn_.netAgent_.port_) { break; // ports are not equal, not same RM } isSame = true; // everything the same, set RMs are the same break; } } if (conn_.agent_.loggingEnabled()) { conn_.agent_.logWriter_.traceExit (this, "isSameRM", isSame); } return isSame; } public static boolean xidsEqual(Xid xid1, Xid xid2) { // determine if the 2 xids contain the same values even if not same object // comapre the format ids if (xid1.getFormatId() != xid2.getFormatId()) { return false; // format ids are not the same } // compare the global transaction ids int xid1Length = xid1.getGlobalTransactionId().length; if (xid1Length != xid2.getGlobalTransactionId().length) { return false; // length of the global trans ids are not the same } byte[] xid1Bytes = xid1.getGlobalTransactionId(); byte[] xid2Bytes = xid2.getGlobalTransactionId(); int i; for (i = 0; i < xid1Length; ++i) { // check all bytes are the same if (xid1Bytes[i] != xid2Bytes[i]) { return false; // bytes in the global trans ids are not the same } } // compare the branch qualifiers xid1Length = xid1.getBranchQualifier().length; if (xid1Length != xid2.getBranchQualifier().length) { return false; // length of the global trans ids are not the same } xid1Bytes = xid1.getBranchQualifier(); xid2Bytes = xid2.getBranchQualifier(); for (i = 0; i < xid1Length; ++i) { // check all bytes are the same if (xid1Bytes[i] != xid2Bytes[i]) { return false; // bytes in the global trans ids are not the same } } return true; // all of the fields are the same, xid1 == xid2 } public List getSpecialRegisters() { return specialRegisters_; } public void addSpecialRegisters(String s) { if (s.substring(0, 1).equals("@")) { // SET statement is coming from Client if (specialRegisters_.remove(s.substring(1))) { specialRegisters_.remove(s); specialRegisters_.add(s.substring(1)); } else { specialRegisters_.remove(s); specialRegisters_.add(s); } } else { // SET statement is coming from Server specialRegisters_.remove(s); specialRegisters_.add(s); } } private void connectionClosedFailure() throws XAException { // throw an XAException XAER_RMFAIL, with a chained SqlException - closed exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (new SqlException(null, new ClientMessageId(SQLState.NO_CURRENT_CONNECTION)), exceptionsOnXA); throwXAException(javax.transaction.xa.XAException.XAER_RMFAIL); } private String getXAFuncStr(int xaFunc) { switch (xaFunc) { case XAFUNC_COMMIT: return XAFUNCSTR_COMMIT; case XAFUNC_END: return XAFUNCSTR_END; case XAFUNC_FORGET: return XAFUNCSTR_FORGET; case XAFUNC_PREPARE: return XAFUNCSTR_PREPARE; case XAFUNC_RECOVER: return XAFUNCSTR_RECOVER; case XAFUNC_ROLLBACK: return XAFUNCSTR_ROLLBACK; case XAFUNC_START: return XAFUNCSTR_START; } return XAFUNCSTR_NONE; } protected int xaRetValErrorAccumSQL(NetXACallInfo callInfo, int currentRC) { // xaRetVal_ is set by the server to be one of the // standard constants from XAException. int rc = callInfo.xaRetVal_; if (rc != XAResource.XA_OK) { // error was detected // create an SqlException to report this error within SqlException accumSql = new SqlException(conn_.netAgent_.logWriter_, new ClientMessageId(SQLState.NET_XARETVAL_ERROR), getXAFuncStr(callInfo.xaFunction_), getXAExceptionText(rc), com.pivotal.gemfirexd.internal.client.am.SqlCode.queuedXAError); exceptionsOnXA = com.pivotal.gemfirexd.internal.client.am.Utils.accumulateSQLException (accumSql, exceptionsOnXA); if (currentRC != XAResource.XA_OK) { // the rc passed into this function had an error also, prioritize error if (currentRC < 0) { // rc passed in was a major error use it instead of current error return currentRC; } } } return rc; } private String processLocalHost(String serverName) { if (serverName.equalsIgnoreCase("localhost")) { // this is a localhost, find hostname try { // GemStone changes BEGIN InetAddress localhostNameIA = ClientSharedUtils.getLocalHost(); /* (original code) InetAddress localhostNameIA = InetAddress.getLocalHost(); */ // GemStone changes END String localhostName = localhostNameIA.getHostName(); return localhostName; } catch (SecurityException se) { return serverName; } catch (UnknownHostException ue) { return serverName; } } // not "localhost", return original server name return serverName; } protected void removeXaresFromSameRMchain() { // check all NetXAResources on the same RM for the NetXAResource to remove try { this.ignoreMe_ = true; // use the ignoreMe_ flag to indicate the // XAResource to remove NetXAResource prevXAResource = null; NetXAResource currXAResource; synchronized (xaResourceSameRMGroup_) { // make sure no one changes this vector list currXAResource = (NetXAResource) xaResourceSameRMGroup_.elementAt(sameRMGroupIndex_); while (currXAResource != null) { // is this the XAResource to remove? if (currXAResource.ignoreMe_) { // this NetXAResource is the one to remove if (prevXAResource != null) { // this XAResource is not first in chain, just move next to prev prevXAResource.nextSameRM_ = currXAResource.nextSameRM_; } else { // this XAResource is first in chain, just move next to root xaResourceSameRMGroup_.set(sameRMGroupIndex_, currXAResource.nextSameRM_); } return; } // this is not the NetXAResource to remove, try the next one prevXAResource = currXAResource; currXAResource = currXAResource.nextSameRM_; } } } finally { this.ignoreMe_ = false; } } public void initForReuse() { // add this new XAResource to the list of other XAResources for the Same RM // first find out if there are any other XAResources for the same RM // then check to make sure it is not already in the chain synchronized (xaResourceSameRMGroup_) { // make sure no one changes this vector list int groupCount = xaResourceSameRMGroup_.size(); int index = 0; int firstFreeElement = -1; NetXAResource xaResourceGroup = null; for (; index < groupCount; ++index) { // check if this group is the same RM xaResourceGroup = (NetXAResource) xaResourceSameRMGroup_.elementAt(index); if (xaResourceGroup == null) { // this is a free element, save its index if first found if (firstFreeElement == -1) { // first free element, save index firstFreeElement = index; } continue; // go to next element } try { if (xaResourceGroup.isSameRM(this)) { // it is the same RM add this XAResource to the chain if not there NetXAResource nextXares = (NetXAResource) xaResourceSameRMGroup_.elementAt(sameRMGroupIndex_); while (nextXares != null) { // is this NetXAResource the one we are trying to add? if (nextXares.equals(this)) { // the XAResource to be added already is in chain, don't add break; } // Xid was not on that NetXAResource, try the next one nextXares = nextXares.nextSameRM_; } if (nextXares == null) { // XAResource to be added is not in the chain already, add it // add it at the head of the chain sameRMGroupIndex_ = index; this.nextSameRM_ = xaResourceGroup.nextSameRM_; xaResourceGroup.nextSameRM_ = this; } return; // done } } catch (XAException xae) { } } // no other same RM was found, add this as first of new group if (firstFreeElement == -1) { // no free element found, add new element to end xaResourceSameRMGroup_.add(this); sameRMGroupIndex_ = groupCount; } else { // use first free element found xaResourceSameRMGroup_.setElementAt(this, firstFreeElement); sameRMGroupIndex_ = firstFreeElement; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy