| /* |
| |
| Derby - Class org.apache.derby.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. |
| |
| */ |
| /********************************************************************** |
| * |
| * |
| * Component Name = |
| * |
| * Package Name = org.apache.derby.client.net |
| * |
| * Descriptive Name = class implements XAResource |
| * |
| * Status = New code |
| * |
| * Function = Handle XA methods |
| * |
| * List of Classes |
| * - NetXAResource |
| * |
| * Restrictions : None |
| * |
| **********************************************************************/ |
| package org.apache.derby.client.net; |
| |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import javax.sql.XAConnection; |
| import javax.transaction.xa.XAException; |
| import javax.transaction.xa.XAResource; |
| import javax.transaction.xa.Xid; |
| |
| import org.apache.derby.client.ClientXid; |
| import org.apache.derby.client.am.ClientConnection; |
| import org.apache.derby.client.am.SqlException; |
| import org.apache.derby.client.am.ClientMessageId; |
| import org.apache.derby.client.am.SqlCode; |
| import org.apache.derby.client.am.Utils; |
| import org.apache.derby.client.am.XaException; |
| import org.apache.derby.shared.common.reference.SQLState; |
| |
| public class NetXAResource implements XAResource { |
| private static final int INITIAL_CALLINFO_ELEMENTS = 1; |
| static final ClientXid nullXid = new ClientXid(); |
| |
| // xaFunction defines, shows which queued XA function is being performed |
| static final int XAFUNC_COMMIT = 1; |
| private static final int XAFUNC_END = 2; |
| private static final int XAFUNC_FORGET = 3; |
| private static final int XAFUNC_PREPARE = 4; |
| private static final int XAFUNC_RECOVER = 5; |
| static final int XAFUNC_ROLLBACK = 6; |
| private static final int XAFUNC_START = 7; |
| |
| private static final String XAFUNCSTR_NONE = "No XA Function"; |
| private static final String XAFUNCSTR_COMMIT = "XAResource.commit()"; |
| private static final String XAFUNCSTR_END = "XAResource.end()"; |
| private static final String XAFUNCSTR_FORGET = "XAResource.forget()"; |
| private static final String XAFUNCSTR_PREPARE = "XAResource.prepare()"; |
| private static final String XAFUNCSTR_RECOVER = "XAResource.recover()"; |
| private static final String XAFUNCSTR_ROLLBACK = "XAResource.rollback()"; |
| private static final String XAFUNCSTR_START = "XAResource.start()"; |
| |
| SqlException exceptionsOnXA = null; |
| |
| NetXAConnection netXAConn_; |
| NetConnection conn_; |
| private boolean keepIsolationLevel; |
| |
| // TODO: change to a single callInfo field (not an array) |
| NetXACallInfo callInfoArray_[] = |
| new NetXACallInfo[INITIAL_CALLINFO_ELEMENTS]; |
| |
| /** The value of the transaction timeout in seconds. */ |
| private int timeoutSeconds = 0; |
| |
| public NetXAResource(XAConnection xaconn, |
| NetXAConnection conn) { |
| conn_ = conn.getNetConnection(); |
| netXAConn_ = conn; |
| 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, |
| null); |
| } |
| |
| // initialize the first XACallInfo element with the information from the |
| // primary connection |
| callInfoArray_[0].actualConn_ = conn; |
| // ~~~ save conn_ connection variables in callInfoArray_[0] |
| callInfoArray_[0].saveConnectionVariables(); |
| } |
| |
| 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.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 = getSqlExceptionXAErrorCode(sqle); |
| exceptionsOnXA = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| } |
| if (rc != XAResource.XA_OK) { |
| throwXAException(rc); |
| } |
| } |
| |
| /** |
| * Get XAException.errorCode from SqlException |
| * For disconnect exception, return XAER_RMFAIL |
| * For other exceptions return XAER_RMERR |
| * |
| * For server side SQLExceptions during |
| * XA operations the errorCode has already been determined |
| * and wrapped in an XAException for return to the client. |
| * see EmbedXAResource.wrapInXAException |
| * |
| * @param sqle SqlException to evaluate. |
| * @return XAException.XAER_RMFAIL for disconnect exception, |
| * XAException.XAER_RMERR for other exceptions. |
| */ |
| private int getSqlExceptionXAErrorCode(SqlException sqle) { |
| int seErrorCode = sqle.getErrorCode(); |
| return (seErrorCode == 40000 ? XAException.XAER_RMFAIL : XAException.XAER_RMERR); |
| } |
| |
| /** |
| * <p> |
| * 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. |
| * </p> |
| * <p> |
| * 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. |
| * </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 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.xaFlags_ = flags; |
| callInfo.xid_ = xid; |
| callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL |
| try { |
| netAgent.beginWriteChainOutsideUOW(); |
| netAgent.netConnectionRequest_.writeXaEndUnitOfWork(conn_); |
| netAgent.flowOutsideUOW(); |
| rc = netAgent.netConnectionReply_.readXaEndUnitOfWork(conn_); |
| |
| 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 = getSqlExceptionXAErrorCode(sqle); |
| exceptionsOnXA = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| } |
| |
| 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); |
| }else { |
| conn_.setXAState(ClientConnection.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.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 = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| throwXAException(getSqlExceptionXAErrorCode(sqle)); |
| } |
| |
| if (rc != XAResource.XA_OK) { |
| throwXAException(rc); |
| } |
| |
| } |
| |
| /** |
| * 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.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 = getSqlExceptionXAErrorCode(sqle); |
| exceptionsOnXA = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| } |
| |
| if ((rc != XAResource.XA_OK ) && (rc != XAResource.XA_RDONLY)) { |
| throwXAException(rc); |
| } |
| 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; |
| |
| NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; |
| callInfo.xaFlags_ = flag; |
| 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(); |
| xidList = conn_.getIndoubtTransactionIds(); |
| |
| } catch (SqlException sqle) { |
| rc = getSqlExceptionXAErrorCode(sqle); |
| exceptionsOnXA = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| } |
| |
| if (rc != XAResource.XA_OK) { |
| throwXAException(rc); |
| } |
| |
| 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.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 = getSqlExceptionXAErrorCode(sqle); |
| exceptionsOnXA = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| } |
| |
| if (rc != XAResource.XA_OK) { |
| throwXAException(rc); |
| } |
| |
| } |
| |
| /** |
| * 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 = getSqlExceptionXAErrorCode(sqle); |
| exceptionsOnXA = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| } |
| |
| // update the XACallInfo |
| NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_]; |
| callInfo.xaFlags_ = flags; |
| callInfo.xid_ = xid; |
| 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(ClientConnection.XA_T1_ASSOCIATED); |
| } |
| |
| } catch (SqlException sqle) { |
| rc = getSqlExceptionXAErrorCode(sqle); |
| exceptionsOnXA = Utils.accumulateSQLException(sqle, exceptionsOnXA); |
| } |
| |
| if (rc != XAResource.XA_OK) { |
| throwXAException(rc); |
| } |
| } |
| |
| private String getXAExceptionText(int rc) { |
| String xaExceptionText; |
| switch (rc) { |
| case XAException.XA_RBROLLBACK: |
| xaExceptionText = "XA_RBROLLBACK"; |
| break; |
| case XAException.XA_RBCOMMFAIL: |
| xaExceptionText = "XA_RBCOMMFAIL"; |
| break; |
| case XAException.XA_RBDEADLOCK: |
| xaExceptionText = "XA_RBDEADLOCK"; |
| break; |
| case XAException.XA_RBINTEGRITY: |
| xaExceptionText = "XA_RBINTEGRITY"; |
| break; |
| case XAException.XA_RBOTHER: |
| xaExceptionText = "XA_RBOTHER"; |
| break; |
| case XAException.XA_RBPROTO: |
| xaExceptionText = "XA_RBPROTO"; |
| break; |
| case XAException.XA_RBTIMEOUT: |
| xaExceptionText = "XA_RBTIMEOUT"; |
| break; |
| case XAException.XA_RBTRANSIENT: |
| xaExceptionText = "XA_RBTRANSIENT"; |
| break; |
| case XAException.XA_NOMIGRATE: |
| xaExceptionText = "XA_NOMIGRATE"; |
| break; |
| case XAException.XA_HEURHAZ: |
| xaExceptionText = "XA_HEURHAZ"; |
| break; |
| case XAException.XA_HEURCOM: |
| xaExceptionText = "XA_HEURCOM"; |
| break; |
| case XAException.XA_HEURRB: |
| xaExceptionText = "XA_HEURRB"; |
| break; |
| case XAException.XA_HEURMIX: |
| xaExceptionText = "XA_HEURMIX"; |
| break; |
| case XAException.XA_RETRY: |
| xaExceptionText = "XA_RETRY"; |
| break; |
| case XAException.XA_RDONLY: |
| xaExceptionText = "XA_RDONLY"; |
| break; |
| case XAException.XAER_ASYNC: |
| xaExceptionText = "XAER_ASYNC"; |
| break; |
| case XAException.XAER_RMERR: |
| xaExceptionText = "XAER_RMERR"; |
| break; |
| case XAException.XAER_NOTA: |
| xaExceptionText = "XAER_NOTA"; |
| break; |
| case XAException.XAER_INVAL: |
| xaExceptionText = "XAER_INVAL"; |
| break; |
| case XAException.XAER_PROTO: |
| xaExceptionText = "XAER_PROTO"; |
| break; |
| case XAException.XAER_RMFAIL: |
| xaExceptionText = "XAER_RMFAIL"; |
| break; |
| case XAException.XAER_DUPID: |
| xaExceptionText = "XAER_DUPID"; |
| break; |
| case XAException.XAER_OUTSIDE: |
| xaExceptionText = "XAER_OUTSIDE"; |
| break; |
| case XAResource.XA_OK: |
| xaExceptionText = "XA_OK"; |
| break; |
| default: |
| xaExceptionText = "Unknown Error"; |
| break; |
| } |
| return xaExceptionText; |
| } |
| |
| private void throwXAException(int rc) |
| throws XAException { |
| |
| StringBuilder xaExceptionText = new StringBuilder(64); |
| |
| xaExceptionText.append(getXAExceptionText(rc)); |
| // save the SqlException chain to add it to the XAException |
| SqlException sqlExceptions = exceptionsOnXA; |
| |
| while (exceptionsOnXA != null) { // one or more SqlExceptions received, format them |
| xaExceptionText.append(" : ").append(exceptionsOnXA.getMessage()); |
| exceptionsOnXA = (SqlException) |
| exceptionsOnXA.getNextException(); |
| } |
| |
| XaException xaException = new XaException( |
| conn_.agent_.logWriter_, |
| sqlExceptions, |
| xaExceptionText.toString()); |
| |
| 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 exceptions 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 XAException.XAER_RMFAIL: |
| case XAException.XAER_RMERR: |
| case XAException.XA_RBROLLBACK: |
| case XAException.XA_RBCOMMFAIL: |
| case XAException.XA_RBDEADLOCK: |
| case XAException.XA_RBINTEGRITY: |
| case XAException.XA_RBOTHER: |
| case XAException.XA_RBPROTO: |
| case XAException.XA_RBTIMEOUT: |
| case XAException.XA_RBTRANSIENT: |
| conn_.setXAState(ClientConnection.XA_T0_NOT_ASSOCIATED); |
| break; |
| // No change for other XAExceptions |
| // XAException.XA_NOMIGRATE |
| //XAException.XA_HEURHAZ |
| // XAException.XA_HEURCOM |
| // XAException.XA_HEURRB |
| // XAException.XA_HEURMIX |
| // XAException.XA_RETRY |
| // XAException.XA_RDONLY |
| // XAException.XAER_ASYNC |
| // XAException.XAER_NOTA |
| // XAException.XAER_INVAL |
| // XAException.XAER_PROTO |
| // XAException.XAER_DUPID |
| // 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 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 |
| } |
| |
| |
| private void connectionClosedFailure() throws XAException { // throw an XAException XAER_RMFAIL, with a chained SqlException - closed |
| exceptionsOnXA = Utils.accumulateSQLException |
| (new SqlException(null, |
| new ClientMessageId(SQLState.NO_CURRENT_CONNECTION)), |
| exceptionsOnXA); |
| throwXAException(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), |
| SqlCode.queuedXAError, |
| getXAFuncStr(callInfo.xaFunction_), |
| getXAExceptionText(rc)); |
| exceptionsOnXA = 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 { |
| InetAddress localhostNameIA = InetAddress.getLocalHost(); |
| String localhostName = localhostNameIA.getHostName(); |
| return localhostName; |
| } catch (SecurityException se) { |
| return serverName; |
| } catch (UnknownHostException ue) { |
| return serverName; |
| } |
| } |
| // not "localhost", return original server name |
| return serverName; |
| } |
| } |