0001: /*
0002:
0003: Derby - Class org.apache.derby.client.net.NetXAResource
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to You under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021: /**********************************************************************
0022: *
0023: *
0024: * Component Name =
0025: *
0026: * Package Name = org.apache.derby.client.net
0027: *
0028: * Descriptive Name = class implements XAResource
0029: *
0030: * Status = New code
0031: *
0032: * Function = Handle XA methods
0033: *
0034: * List of Classes
0035: * - NetXAResource
0036: *
0037: * Restrictions : None
0038: *
0039: **********************************************************************/package org.apache.derby.client.net;
0040:
0041: import java.net.InetAddress;
0042: import java.net.UnknownHostException;
0043: import java.util.Collections;
0044: import java.util.Enumeration;
0045: import java.util.LinkedList;
0046: import java.util.List;
0047: import java.util.Vector;
0048: import javax.sql.XAConnection;
0049: import javax.transaction.xa.XAException;
0050: import javax.transaction.xa.XAResource;
0051: import javax.transaction.xa.Xid;
0052:
0053: import org.apache.derby.client.ClientXid;
0054: import org.apache.derby.client.am.Connection;
0055: import org.apache.derby.client.am.SqlException;
0056: import org.apache.derby.client.am.ClientMessageId;
0057: import org.apache.derby.shared.common.reference.SQLState;
0058:
0059: public class NetXAResource implements XAResource {
0060: public static final int TMTIMEOUT = 0x00000100;
0061: public static final int ACTIVE_ONLY = -1;
0062: public static final int XA_NULL_XID = -1; // null Xid has Format Id of -1
0063: public static final int INITIAL_CALLINFO_ELEMENTS = 1;
0064: public static final int RECOVER_XID_ARRAY_LENGTH = 10;
0065: public static final ClientXid nullXid = new ClientXid();
0066:
0067: // xaFunction defines, shows which queued XA function is being performed
0068: public static final int XAFUNC_NONE = 0;
0069: public static final int XAFUNC_COMMIT = 1;
0070: public static final int XAFUNC_END = 2;
0071: public static final int XAFUNC_FORGET = 3;
0072: public static final int XAFUNC_PREPARE = 4;
0073: public static final int XAFUNC_RECOVER = 5;
0074: public static final int XAFUNC_ROLLBACK = 6;
0075: public static final int XAFUNC_START = 7;
0076: public static final String XAFUNCSTR_NONE = "No XA Function";
0077: public static final String XAFUNCSTR_COMMIT = "XAResource.commit()";
0078: public static final String XAFUNCSTR_END = "XAResource.end()";
0079: public static final String XAFUNCSTR_FORGET = "XAResource.forget()";
0080: public static final String XAFUNCSTR_PREPARE = "XAResource.prepare()";
0081: public static final String XAFUNCSTR_RECOVER = "XAResource.recover()";
0082: public static final String XAFUNCSTR_ROLLBACK = "XAResource.rollback()";
0083: public static final String XAFUNCSTR_START = "XAResource.start()";
0084:
0085: public int nextElement = 0;
0086:
0087: // XAResources with same RM group list
0088: protected static Vector xaResourceSameRMGroup_ = new Vector();
0089: protected int sameRMGroupIndex_ = 0;
0090: protected NetXAResource nextSameRM_ = null;
0091: protected boolean ignoreMe_ = false;
0092:
0093: public org.apache.derby.client.am.SqlException exceptionsOnXA = null;
0094:
0095: XAConnection xaconn_;
0096: org.apache.derby.client.net.NetXAConnection netXAConn_;
0097: org.apache.derby.client.net.NetConnection conn_;
0098: int rmId_; // unique RmId generated by XAConnection
0099: // TODO: change to a single callInfo field (not an array)
0100: NetXACallInfo callInfoArray_[] = new NetXACallInfo[INITIAL_CALLINFO_ELEMENTS];
0101: int numXACallInfo_ = INITIAL_CALLINFO_ELEMENTS;
0102: int connectionCount_ = 1;
0103: int activeXATransCount_ = 0;
0104: String rmIdx_; // userid in case we need to create a secondary connection
0105: String rmIdy_; // password in case we need to create a secondary connection
0106: // TODO: remove port and ipaddr_
0107: int port_; // port needed to make secondary connection for recover in DS mode.
0108: String ipaddr_; // ip address needed to make secondary connection for recover in DS mode.
0109:
0110: private List specialRegisters_ = Collections
0111: .synchronizedList(new LinkedList());
0112:
0113: public NetXAResource(XAConnection xaconn, int rmId, String userId,
0114: String password,
0115: org.apache.derby.client.net.NetXAConnection conn) {
0116: xaconn_ = xaconn;
0117: rmId_ = rmId;
0118: conn_ = conn.getNetConnection();
0119: netXAConn_ = conn;
0120: rmIdx_ = userId;
0121: rmIdy_ = password;
0122: port_ = conn_.netAgent_.getPort();
0123: ipaddr_ = conn_.netAgent_.socket_.getLocalAddress()
0124: .getHostAddress();
0125: conn.setNetXAResource(this );
0126:
0127: // link the primary connection to the first XACallInfo element
0128: conn_.currXACallInfoOffset_ = 0;
0129:
0130: // construct the NetXACallInfo object for the array.
0131: for (int i = 0; i < INITIAL_CALLINFO_ELEMENTS; ++i) {
0132: callInfoArray_[i] = new NetXACallInfo(null,
0133: XAResource.TMNOFLAGS, this , null);
0134: }
0135:
0136: // initialize the first XACallInfo element with the information from the
0137: // primary connection
0138: callInfoArray_[0].actualConn_ = conn;
0139: callInfoArray_[0].currConnection_ = true;
0140: callInfoArray_[0].freeEntry_ = false;
0141: // ~~~ save conn_ connection variables in callInfoArray_[0]
0142: callInfoArray_[0].saveConnectionVariables();
0143:
0144: // add this new XAResource to the list of other XAResources for the Same RM
0145: initForReuse();
0146: }
0147:
0148: public void commit(Xid xid, boolean onePhase) throws XAException {
0149: NetAgent netAgent = conn_.netAgent_;
0150: int rc = XAResource.XA_OK;
0151:
0152: exceptionsOnXA = null;
0153: if (conn_.agent_.loggingEnabled()) {
0154: conn_.agent_.logWriter_.traceEntry(this , "commit", xid,
0155: onePhase);
0156: }
0157: if (conn_.isPhysicalConnClosed()) {
0158: connectionClosedFailure();
0159: }
0160:
0161: // update the XACallInfo
0162: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0163: callInfo.xaFlags_ = (onePhase ? XAResource.TMONEPHASE
0164: : XAResource.TMNOFLAGS);
0165: callInfo.xid_ = xid;
0166: callInfo.xaResource_ = this ;
0167: callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
0168: try {
0169: netAgent.beginWriteChainOutsideUOW();
0170: netAgent.netConnectionRequest_.writeXaCommit(conn_, xid);
0171: netAgent.flowOutsideUOW();
0172: netAgent.netConnectionReply_.readXaCommit(conn_);
0173: if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it
0174: callInfo.xaFunction_ = XAFUNC_COMMIT;
0175: rc = xaRetValErrorAccumSQL(callInfo, rc);
0176: callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
0177: }
0178: netAgent.endReadChain();
0179: } catch (SqlException sqle) {
0180: rc = XAException.XAER_RMERR;
0181: exceptionsOnXA = org.apache.derby.client.am.Utils
0182: .accumulateSQLException(sqle, exceptionsOnXA);
0183: } finally {
0184: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
0185: }
0186: if (rc != XAResource.XA_OK) {
0187: throwXAException(rc, false);
0188: }
0189: }
0190:
0191: /**
0192: * Ends the work performed on behalf of a transaction branch. The resource manager dissociates the XA resource from
0193: * the transaction branch specified and let the transaction be completed.
0194: * <p/>
0195: * If TMSUSPEND is specified in flags, the transaction branch is temporarily suspended in incomplete state. The
0196: * transaction context is in suspened state and must be resumed via start with TMRESUME specified.
0197: * <p/>
0198: * If TMFAIL is specified, the portion of work has failed. The resource manager may mark the transaction as
0199: * rollback-only
0200: * <p/>
0201: * If TMSUCCESS is specified, the portion of work has completed successfully.
0202: *
0203: * @param xid A global transaction identifier that is the same as what was used previously in the start method.
0204: * @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND
0205: *
0206: * @throws XAException An error has occurred. Possible XAException values are XAER_RMERR, XAER_RMFAILED, XAER_NOTA,
0207: * XAER_INVAL, XAER_PROTO, or XA_RB*.
0208: */
0209:
0210: public void end(Xid xid, int flags) throws XAException {
0211:
0212: NetAgent netAgent = conn_.netAgent_;
0213: int rc = XAResource.XA_OK;
0214: exceptionsOnXA = null;
0215: if (conn_.agent_.loggingEnabled()) {
0216: conn_.agent_.logWriter_.traceEntry(this , "end", xid, flags);
0217: }
0218: if (conn_.isPhysicalConnClosed()) {
0219: connectionClosedFailure();
0220: }
0221:
0222: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0223: callInfo.setReadOnlyTransactionFlag(conn_.readOnlyTransaction_);
0224: callInfo.xaFlags_ = flags;
0225: callInfo.xid_ = xid;
0226: callInfo.xaResource_ = this ;
0227: callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
0228: try {
0229: netAgent.beginWriteChainOutsideUOW();
0230: netAgent.netConnectionRequest_.writeXaEndUnitOfWork(conn_);
0231: netAgent.flowOutsideUOW();
0232: rc = netAgent.netConnectionReply_
0233: .readXaEndUnitOfWork(conn_);
0234: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending end
0235: if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it
0236: callInfo.xaFunction_ = XAFUNC_END;
0237: rc = xaRetValErrorAccumSQL(callInfo, rc);
0238: callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
0239: }
0240: netAgent.endReadChain();
0241: } catch (SqlException sqle) {
0242: rc = XAException.XAER_RMERR;
0243: exceptionsOnXA = org.apache.derby.client.am.Utils
0244: .accumulateSQLException(sqle, exceptionsOnXA);
0245: } finally {
0246: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
0247: }
0248: if (rc != XAResource.XA_OK) {
0249: throwXAException(rc, false);
0250: } else {
0251: conn_.setXAState(Connection.XA_T0_NOT_ASSOCIATED);
0252: }
0253: }
0254:
0255: /**
0256: * Tell the resource manager to forget about a heuristically (MANUALLY) completed transaction branch.
0257: *
0258: * @param xid A global transaction identifier
0259: *
0260: * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, XAER_NOTA,
0261: * XAER_INVAL, or XAER_PROTO.
0262: */
0263:
0264: public void forget(Xid xid) throws XAException {
0265: NetAgent netAgent = conn_.netAgent_;
0266: int rc = XAResource.XA_OK;
0267: exceptionsOnXA = null;
0268:
0269: if (conn_.agent_.loggingEnabled()) {
0270: conn_.agent_.logWriter_.traceEntry(this , "forget", xid);
0271: }
0272: if (conn_.isPhysicalConnClosed()) {
0273: connectionClosedFailure();
0274: }
0275: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0276: callInfo.xid_ = xid;
0277: callInfo.xaResource_ = this ;
0278: callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
0279: try {
0280: // flow the required PROTOCOL to the server
0281: netAgent.beginWriteChainOutsideUOW();
0282:
0283: // sent the commit PROTOCOL
0284: netAgent.netConnectionRequest_.writeXaForget(
0285: netAgent.netConnection_, xid);
0286:
0287: netAgent.flowOutsideUOW();
0288:
0289: // read the reply to the commit
0290: netAgent.netConnectionReply_
0291: .readXaForget(netAgent.netConnection_);
0292:
0293: netAgent.endReadChain();
0294: if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it
0295: callInfo.xaFunction_ = XAFUNC_FORGET;
0296: rc = xaRetValErrorAccumSQL(callInfo, rc);
0297: callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
0298: }
0299: } catch (SqlException sqle) {
0300: exceptionsOnXA = org.apache.derby.client.am.Utils
0301: .accumulateSQLException(sqle, exceptionsOnXA);
0302: throwXAException(XAException.XAER_RMERR);
0303: } finally {
0304: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
0305: }
0306: if (rc != XAResource.XA_OK) {
0307: throwXAException(rc, false);
0308: }
0309:
0310: }
0311:
0312: /**
0313: * Obtain the current transaction timeout value set for this XAResource instance. If
0314: * <CODE>XAResource.setTransactionTimeout</CODE> was not use prior to invoking this method, the return value is the
0315: * default timeout set for the resource manager; otherwise, the value used in the previous
0316: * <CODE>setTransactionTimeout</CODE> call is returned.
0317: *
0318: * @return the transaction timeout value in seconds.
0319: *
0320: * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL.
0321: */
0322: public int getTransactionTimeout() throws XAException {
0323: if (conn_.agent_.loggingEnabled()) {
0324: conn_.agent_.logWriter_.traceEntry(this ,
0325: "getTransactionTimeout");
0326: }
0327: exceptionsOnXA = null;
0328: if (conn_.isPhysicalConnClosed()) {
0329: connectionClosedFailure();
0330: }
0331:
0332: if (conn_.agent_.loggingEnabled()) {
0333: conn_.agent_.logWriter_.traceExit(this ,
0334: "getTransactionTimeout", 0);
0335: }
0336: return 0; // we don't support transaction timeout
0337: }
0338:
0339: /**
0340: * Ask the resource manager to prepare for a transaction commit of the transaction specified in xid.
0341: *
0342: * @param xid A global transaction identifier
0343: *
0344: * @return A value indicating the resource manager's vote on the outcome of the transaction. The possible values
0345: * are: XA_RDONLY or XA_OK. If the resource manager wants to roll back the transaction, it should do so by
0346: * raising an appropriate XAException in the prepare method.
0347: *
0348: * @throws XAException An error has occurred. Possible exception values are: XA_RB*, XAER_RMERR, XAER_RMFAIL,
0349: * XAER_NOTA, XAER_INVAL, or XAER_PROTO.
0350: */
0351: public int prepare(Xid xid) throws XAException { // public interface for prepare
0352: // just call prepareX with the recursion flag set to true
0353: exceptionsOnXA = null;
0354:
0355: if (conn_.agent_.loggingEnabled()) {
0356: conn_.agent_.logWriter_.traceEntry(this , "prepare", xid);
0357: }
0358: if (conn_.isPhysicalConnClosed()) {
0359: connectionClosedFailure();
0360: }
0361:
0362: /// update the XACallInfo
0363: NetAgent netAgent = conn_.netAgent_;
0364: int rc = XAResource.XA_OK;
0365: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0366: callInfo.xid_ = xid;
0367: callInfo.xaResource_ = this ;
0368: callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
0369: try {
0370: netAgent.beginWriteChainOutsideUOW();
0371: // sent the prepare PROTOCOL
0372: netAgent.netConnectionRequest_.writeXaPrepare(conn_);
0373: netAgent.flowOutsideUOW();
0374:
0375: // read the reply to the prepare
0376: rc = netAgent.netConnectionReply_.readXaPrepare(conn_);
0377: if ((callInfo.xaRetVal_ != XAResource.XA_OK)
0378: && (callInfo.xaRetVal_ != XAException.XA_RDONLY)) { // xaRetVal has possible error, format it
0379: callInfo.xaFunction_ = XAFUNC_PREPARE;
0380: rc = xaRetValErrorAccumSQL(callInfo, rc);
0381: callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
0382: }
0383:
0384: netAgent.endReadChain();
0385: } catch (SqlException sqle) {
0386: rc = XAException.XAER_RMERR;
0387: exceptionsOnXA = org.apache.derby.client.am.Utils
0388: .accumulateSQLException(sqle, exceptionsOnXA);
0389: } finally {
0390: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
0391: }
0392: if ((rc != XAResource.XA_OK) && (rc != XAResource.XA_RDONLY)) {
0393: throwXAException(rc, false);
0394: }
0395: if (conn_.agent_.loggingEnabled()) {
0396: conn_.agent_.logWriter_.traceExit(this , "prepare", rc);
0397: }
0398: return rc;
0399: }
0400:
0401: /**
0402: * Obtain a list of prepared transaction branches from a resource manager. The transaction manager calls this method
0403: * during recovery to obtain the list of transaction branches that are currently in prepared or heuristically
0404: * completed states.
0405: *
0406: * @param flag One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be used when no other flags are set in
0407: * flags.
0408: *
0409: * @return The resource manager returns zero or more XIDs for the transaction branches that are currently in a
0410: * prepared or heuristically completed state. If an error occurs during the operation, the resource manager
0411: * should raise the appropriate XAException.
0412: *
0413: * @throws XAException An error has occurred. Possible values are XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and
0414: * XAER_PROTO.
0415: */
0416: public Xid[] recover(int flag) throws XAException {
0417: int rc = XAResource.XA_OK;
0418: NetAgent netAgent = conn_.netAgent_;
0419:
0420: if (conn_.agent_.loggingEnabled()) {
0421: conn_.agent_.logWriter_.traceEntry(this , "recover", flag);
0422: }
0423: exceptionsOnXA = null;
0424: if (conn_.isPhysicalConnClosed()) {
0425: connectionClosedFailure();
0426: }
0427:
0428: Xid[] xidList = null;
0429: int numXid = 0;
0430:
0431: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0432: callInfo.xaFlags_ = flag;
0433: callInfo.xaResource_ = this ;
0434: callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
0435: try {
0436: netAgent.beginWriteChainOutsideUOW();
0437: // sent the recover PROTOCOL
0438: netAgent.netConnectionRequest_.writeXaRecover(conn_, flag);
0439: netAgent.flowOutsideUOW();
0440: netAgent.netConnectionReply_.readXaRecover(conn_);
0441: if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it
0442: callInfo.xaFunction_ = XAFUNC_RECOVER;
0443: rc = xaRetValErrorAccumSQL(callInfo, rc);
0444: callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
0445: }
0446: netAgent.endReadChain();
0447: if (conn_.indoubtTransactions_ != null) {
0448: numXid = conn_.indoubtTransactions_.size();
0449: xidList = new Xid[numXid];
0450: int i = 0;
0451: nextElement = 0;
0452: for (Enumeration e = conn_.indoubtTransactions_.keys(); e
0453: .hasMoreElements(); i++) {
0454: xidList[i] = (Xid) e.nextElement();
0455: }
0456: }
0457: } catch (SqlException sqle) {
0458: rc = XAException.XAER_RMERR;
0459: exceptionsOnXA = org.apache.derby.client.am.Utils
0460: .accumulateSQLException(sqle, exceptionsOnXA);
0461: } finally {
0462: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
0463: }
0464: if (rc != XAResource.XA_OK) {
0465: throwXAException(rc, false);
0466: }
0467:
0468: if (conn_.agent_.loggingEnabled()) {
0469: conn_.agent_.logWriter_.traceExit(this , "recover", xidList);
0470: }
0471: return xidList;
0472: }
0473:
0474: /**
0475: * Inform the resource manager to roll back work done on behalf of a transaction branch
0476: *
0477: * @param xid A global transaction identifier
0478: *
0479: * @throws XAException An error has occurred
0480: */
0481: public void rollback(Xid xid) throws XAException {
0482: NetAgent netAgent = conn_.netAgent_;
0483: int rc = XAResource.XA_OK;
0484: exceptionsOnXA = null;
0485:
0486: if (conn_.agent_.loggingEnabled()) {
0487: conn_.agent_.logWriter_.traceEntry(this , "rollback", xid);
0488: }
0489: if (conn_.isPhysicalConnClosed()) {
0490: connectionClosedFailure();
0491: }
0492:
0493: // update the XACallInfo
0494: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0495: callInfo.xid_ = xid;
0496: callInfo.xaResource_ = this ;
0497: callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
0498: try {
0499: netAgent.beginWriteChainOutsideUOW();
0500: netAgent.netConnectionRequest_.writeXaRollback(conn_, xid);
0501: netAgent.flowOutsideUOW();
0502: // read the reply to the rollback
0503: rc = netAgent.netConnectionReply_.readXaRollback(conn_);
0504: netAgent.endReadChain();
0505: if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it
0506: callInfo.xaFunction_ = XAFUNC_END;
0507: rc = xaRetValErrorAccumSQL(callInfo, rc);
0508: callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
0509: }
0510: } catch (SqlException sqle) {
0511: rc = XAException.XAER_RMERR;
0512: exceptionsOnXA = org.apache.derby.client.am.Utils
0513: .accumulateSQLException(sqle, exceptionsOnXA);
0514: } finally {
0515: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
0516: }
0517: if (rc != XAResource.XA_OK) {
0518: throwXAException(rc, false);
0519: }
0520:
0521: }
0522:
0523: /**
0524: * <P>Set the current transaction timeout value for this <CODE>XAResource</CODE> instance. This value overwrites the
0525: * default transaction timeout value in the resource manager. The newly assigned timeout value is effective for the
0526: * life of this <CODE>XAResource</CODE> instance unless a new value is set.<P>
0527: *
0528: * @param seconds the transaction timeout value in seconds.
0529: *
0530: * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
0531: */
0532: public boolean setTransactionTimeout(int seconds)
0533: throws XAException {
0534: if (conn_.agent_.loggingEnabled()) {
0535: conn_.agent_.logWriter_.traceExit(this ,
0536: "setTransactionTimeout", false);
0537: }
0538: exceptionsOnXA = null;
0539: return false; // we don't support transaction timeout in our layer.
0540: /* int rc = xaSetTransTimeOut(seconds);
0541: if (rc != XAResource.XA_OK)
0542: throwXAException(rc); */
0543: }
0544:
0545: /**
0546: * Start work on behalf of a transaction branch specified in xid
0547: *
0548: * @param xid A global transaction identifier to be associated with the resource
0549: * @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME
0550: *
0551: * @throws XAException An error has occurred. Possible exceptions * are XA_RB*, XAER_RMERR, XAER_RMFAIL,
0552: * XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
0553: */
0554: public synchronized void start(Xid xid, int flags)
0555: throws XAException {
0556:
0557: NetAgent netAgent = conn_.netAgent_;
0558: int rc = XAResource.XA_OK;
0559: exceptionsOnXA = null;
0560: if (conn_.agent_.loggingEnabled()) {
0561: conn_.agent_.logWriter_.traceEntry(this , "start", xid,
0562: flags);
0563: }
0564: if (conn_.isPhysicalConnClosed()) {
0565: connectionClosedFailure();
0566: }
0567:
0568: // DERBY-1025 - Flow an auto-commit if in auto-commit mode before
0569: // entering a global transaction
0570: try {
0571: if (conn_.autoCommit_)
0572: conn_.flowAutoCommit();
0573: } catch (SqlException sqle) {
0574: rc = XAException.XAER_RMERR;
0575: exceptionsOnXA = org.apache.derby.client.am.Utils
0576: .accumulateSQLException(sqle, exceptionsOnXA);
0577: }
0578:
0579: // update the XACallInfo
0580: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0581: callInfo.xaFlags_ = flags;
0582: callInfo.xaInProgress_ = true;
0583: callInfo.xid_ = xid;
0584: callInfo.xaResource_ = this ;
0585: callInfo.xaRetVal_ = XAResource.XA_OK; // initialize XARETVAL
0586: try {
0587: netAgent.beginWriteChainOutsideUOW();
0588: netAgent.netConnectionRequest_
0589: .writeXaStartUnitOfWork(conn_);
0590: netAgent.flowOutsideUOW();
0591: netAgent.netConnectionReply_.readXaStartUnitOfWork(conn_);
0592: if (callInfo.xaRetVal_ != XAResource.XA_OK) { // xaRetVal has possible error, format it
0593: callInfo.xaFunction_ = XAFUNC_START;
0594: rc = xaRetValErrorAccumSQL(callInfo, rc);
0595: callInfo.xaRetVal_ = XAResource.XA_OK; // re-initialize XARETVAL
0596: }
0597: // Setting this is currently required to avoid client from sending
0598: // commit for autocommit.
0599: if (rc == XAResource.XA_OK) {
0600: conn_.setXAState(Connection.XA_T1_ASSOCIATED);
0601: }
0602:
0603: } catch (SqlException sqle) {
0604: rc = XAException.XAER_RMERR;
0605: exceptionsOnXA = org.apache.derby.client.am.Utils
0606: .accumulateSQLException(sqle, exceptionsOnXA);
0607: } finally {
0608: conn_.pendingEndXACallinfoOffset_ = -1; // indicate no pending callinfo
0609: }
0610: if (rc != XAResource.XA_OK) {
0611: throwXAException(rc, false);
0612: }
0613: }
0614:
0615: protected void throwXAException(int rc) throws XAException {
0616: throwXAException(rc, rc != XAException.XAER_NOTA);
0617: }
0618:
0619: private String getXAExceptionText(int rc) {
0620: String xaExceptionText;
0621: switch (rc) {
0622: case javax.transaction.xa.XAException.XA_RBROLLBACK:
0623: xaExceptionText = "XA_RBROLLBACK";
0624: break;
0625: case javax.transaction.xa.XAException.XA_RBCOMMFAIL:
0626: xaExceptionText = "XA_RBCOMMFAIL";
0627: break;
0628: case javax.transaction.xa.XAException.XA_RBDEADLOCK:
0629: xaExceptionText = "XA_RBDEADLOCK";
0630: break;
0631: case javax.transaction.xa.XAException.XA_RBINTEGRITY:
0632: xaExceptionText = "XA_RBINTEGRITY";
0633: break;
0634: case javax.transaction.xa.XAException.XA_RBOTHER:
0635: xaExceptionText = "XA_RBOTHER";
0636: break;
0637: case javax.transaction.xa.XAException.XA_RBPROTO:
0638: xaExceptionText = "XA_RBPROTO";
0639: break;
0640: case javax.transaction.xa.XAException.XA_RBTIMEOUT:
0641: xaExceptionText = "XA_RBTIMEOUT";
0642: break;
0643: case javax.transaction.xa.XAException.XA_RBTRANSIENT:
0644: xaExceptionText = "XA_RBTRANSIENT";
0645: break;
0646: case javax.transaction.xa.XAException.XA_NOMIGRATE:
0647: xaExceptionText = "XA_NOMIGRATE";
0648: break;
0649: case javax.transaction.xa.XAException.XA_HEURHAZ:
0650: xaExceptionText = "XA_HEURHAZ";
0651: break;
0652: case javax.transaction.xa.XAException.XA_HEURCOM:
0653: xaExceptionText = "XA_HEURCOM";
0654: break;
0655: case javax.transaction.xa.XAException.XA_HEURRB:
0656: xaExceptionText = "XA_HEURRB";
0657: break;
0658: case javax.transaction.xa.XAException.XA_HEURMIX:
0659: xaExceptionText = "XA_HEURMIX";
0660: break;
0661: case javax.transaction.xa.XAException.XA_RETRY:
0662: xaExceptionText = "XA_RETRY";
0663: break;
0664: case javax.transaction.xa.XAException.XA_RDONLY:
0665: xaExceptionText = "XA_RDONLY";
0666: break;
0667: case javax.transaction.xa.XAException.XAER_ASYNC:
0668: xaExceptionText = "XAER_ASYNC";
0669: break;
0670: case javax.transaction.xa.XAException.XAER_RMERR:
0671: xaExceptionText = "XAER_RMERR";
0672: break;
0673: case javax.transaction.xa.XAException.XAER_NOTA:
0674: xaExceptionText = "XAER_NOTA";
0675: break;
0676: case javax.transaction.xa.XAException.XAER_INVAL:
0677: xaExceptionText = "XAER_INVAL";
0678: break;
0679: case javax.transaction.xa.XAException.XAER_PROTO:
0680: xaExceptionText = "XAER_PROTO";
0681: break;
0682: case javax.transaction.xa.XAException.XAER_RMFAIL:
0683: xaExceptionText = "XAER_RMFAIL";
0684: break;
0685: case javax.transaction.xa.XAException.XAER_DUPID:
0686: xaExceptionText = "XAER_DUPID";
0687: break;
0688: case javax.transaction.xa.XAException.XAER_OUTSIDE:
0689: xaExceptionText = "XAER_OUTSIDE";
0690: break;
0691: case XAResource.XA_OK:
0692: xaExceptionText = "XA_OK";
0693: break;
0694: default:
0695: xaExceptionText = "Unknown Error";
0696: break;
0697: }
0698: return xaExceptionText;
0699: }
0700:
0701: protected void throwXAException(int rc, boolean resetFlag)
0702: throws XAException { // ~~~
0703: String xaExceptionText;
0704: if (resetFlag) {
0705: // reset the state of the failed connection
0706: NetXACallInfo callInfo = callInfoArray_[conn_.currXACallInfoOffset_];
0707: callInfo.xaInProgress_ = false;
0708: callInfo.xaWasSuspended = false;
0709: }
0710:
0711: xaExceptionText = getXAExceptionText(rc);
0712: // save the SqlException chain to add it to the XAException
0713: org.apache.derby.client.am.SqlException sqlExceptions = exceptionsOnXA;
0714:
0715: while (exceptionsOnXA != null) { // one or more SqlExceptions received, format them
0716: xaExceptionText = xaExceptionText + " : "
0717: + exceptionsOnXA.getMessage();
0718: exceptionsOnXA = (org.apache.derby.client.am.SqlException) exceptionsOnXA
0719: .getNextException();
0720: }
0721: org.apache.derby.client.am.XaException xaException = new org.apache.derby.client.am.XaException(
0722: conn_.agent_.logWriter_, sqlExceptions, xaExceptionText);
0723: xaException.errorCode = rc;
0724: setXaStateForXAException(rc);
0725: throw xaException;
0726: }
0727:
0728: /**
0729: * Reset the transaction branch association state to XA_T0_NOT_ASSOCIATED
0730: * for XAER_RM* and XA_RB* Exceptions. All other exeptions leave the state
0731: * unchanged
0732: *
0733: * @param rc // return code from XAException
0734: * @throws XAException
0735: */
0736: private void setXaStateForXAException(int rc) {
0737: switch (rc) {
0738: // Reset to T0, not associated for XA_RB*, RM*
0739: // XAER_RMFAIL and XAER_RMERR will be fatal to the connection
0740: // but that is not dealt with here
0741: case javax.transaction.xa.XAException.XAER_RMFAIL:
0742: case javax.transaction.xa.XAException.XAER_RMERR:
0743: case javax.transaction.xa.XAException.XA_RBROLLBACK:
0744: case javax.transaction.xa.XAException.XA_RBCOMMFAIL:
0745: case javax.transaction.xa.XAException.XA_RBDEADLOCK:
0746: case javax.transaction.xa.XAException.XA_RBINTEGRITY:
0747: case javax.transaction.xa.XAException.XA_RBOTHER:
0748: case javax.transaction.xa.XAException.XA_RBPROTO:
0749: case javax.transaction.xa.XAException.XA_RBTIMEOUT:
0750: case javax.transaction.xa.XAException.XA_RBTRANSIENT:
0751: conn_.setXAState(Connection.XA_T0_NOT_ASSOCIATED);
0752: break;
0753: // No change for other XAExceptions
0754: // javax.transaction.xa.XAException.XA_NOMIGRATE
0755: //javax.transaction.xa.XAException.XA_HEURHAZ
0756: // javax.transaction.xa.XAException.XA_HEURCOM
0757: // javax.transaction.xa.XAException.XA_HEURRB
0758: // javax.transaction.xa.XAException.XA_HEURMIX
0759: // javax.transaction.xa.XAException.XA_RETRY
0760: // javax.transaction.xa.XAException.XA_RDONLY
0761: // javax.transaction.xa.XAException.XAER_ASYNC
0762: // javax.transaction.xa.XAException.XAER_NOTA
0763: // javax.transaction.xa.XAException.XAER_INVAL
0764: // javax.transaction.xa.XAException.XAER_PROTO
0765: // javax.transaction.xa.XAException.XAER_DUPID
0766: // javax.transaction.xa.XAException.XAER_OUTSIDE
0767: default:
0768: return;
0769: }
0770: }
0771:
0772: public boolean isSameRM(XAResource xares) throws XAException {
0773: boolean isSame = false; // preset that the RMs are NOT the same
0774: exceptionsOnXA = null;
0775:
0776: if (conn_.agent_.loggingEnabled()) {
0777: conn_.agent_.logWriter_.traceEntry(this , "isSameRM", xares);
0778: }
0779: if (conn_.isPhysicalConnClosed()) {
0780: connectionClosedFailure();
0781: }
0782:
0783: if (xares instanceof org.apache.derby.client.net.NetXAResource) { // both are NetXAResource so check to see if this is the same RM
0784: // remember, isSame is initialized to false
0785: NetXAResource derbyxares = (NetXAResource) xares;
0786: while (true) {
0787: if (!conn_.databaseName_
0788: .equalsIgnoreCase(derbyxares.conn_.databaseName_)) {
0789: break; // database names are not equal, not same RM
0790: }
0791: if (!conn_.netAgent_.server_
0792: .equalsIgnoreCase(derbyxares.conn_.netAgent_.server_)) { // server name strings not equal, compare IP addresses
0793: try {
0794: // 1st convert "localhost" to actual server name
0795: String server1 = this
0796: .processLocalHost(conn_.netAgent_.server_);
0797: String server2 = this
0798: .processLocalHost(derbyxares.conn_.netAgent_.server_);
0799: // now convert the server name to ip address
0800: InetAddress serverIP1 = InetAddress
0801: .getByName(server1);
0802: InetAddress serverIP2 = InetAddress
0803: .getByName(server2);
0804: if (!serverIP1.equals(serverIP2)) {
0805: break; // server IPs are not equal, not same RM
0806: }
0807: } catch (UnknownHostException ue) {
0808: break;
0809: }
0810: }
0811: if (conn_.netAgent_.port_ != derbyxares.conn_.netAgent_.port_) {
0812: break; // ports are not equal, not same RM
0813: }
0814: isSame = true; // everything the same, set RMs are the same
0815: break;
0816: }
0817: }
0818:
0819: if (conn_.agent_.loggingEnabled()) {
0820: conn_.agent_.logWriter_.traceExit(this , "isSameRM", isSame);
0821: }
0822: return isSame;
0823: }
0824:
0825: public static boolean xidsEqual(Xid xid1, Xid xid2) { // determine if the 2 xids contain the same values even if not same object
0826: // comapre the format ids
0827: if (xid1.getFormatId() != xid2.getFormatId()) {
0828: return false; // format ids are not the same
0829: }
0830:
0831: // compare the global transaction ids
0832: int xid1Length = xid1.getGlobalTransactionId().length;
0833: if (xid1Length != xid2.getGlobalTransactionId().length) {
0834: return false; // length of the global trans ids are not the same
0835: }
0836: byte[] xid1Bytes = xid1.getGlobalTransactionId();
0837: byte[] xid2Bytes = xid2.getGlobalTransactionId();
0838: int i;
0839: for (i = 0; i < xid1Length; ++i) { // check all bytes are the same
0840: if (xid1Bytes[i] != xid2Bytes[i]) {
0841: return false; // bytes in the global trans ids are not the same
0842: }
0843: }
0844:
0845: // compare the branch qualifiers
0846: xid1Length = xid1.getBranchQualifier().length;
0847: if (xid1Length != xid2.getBranchQualifier().length) {
0848: return false; // length of the global trans ids are not the same
0849: }
0850: xid1Bytes = xid1.getBranchQualifier();
0851: xid2Bytes = xid2.getBranchQualifier();
0852: for (i = 0; i < xid1Length; ++i) { // check all bytes are the same
0853: if (xid1Bytes[i] != xid2Bytes[i]) {
0854: return false; // bytes in the global trans ids are not the same
0855: }
0856: }
0857:
0858: return true; // all of the fields are the same, xid1 == xid2
0859: }
0860:
0861: public List getSpecialRegisters() {
0862: return specialRegisters_;
0863: }
0864:
0865: public void addSpecialRegisters(String s) {
0866: if (s.substring(0, 1).equals("@")) {
0867: // SET statement is coming from Client
0868: if (specialRegisters_.remove(s.substring(1))) {
0869: specialRegisters_.remove(s);
0870: specialRegisters_.add(s.substring(1));
0871: } else {
0872: specialRegisters_.remove(s);
0873: specialRegisters_.add(s);
0874: }
0875: } else { // SET statement is coming from Server
0876: specialRegisters_.remove(s);
0877: specialRegisters_.add(s);
0878: }
0879: }
0880:
0881: private void connectionClosedFailure() throws XAException { // throw an XAException XAER_RMFAIL, with a chained SqlException - closed
0882: exceptionsOnXA = org.apache.derby.client.am.Utils
0883: .accumulateSQLException(new SqlException(null,
0884: new ClientMessageId(
0885: SQLState.NO_CURRENT_CONNECTION)),
0886: exceptionsOnXA);
0887: throwXAException(javax.transaction.xa.XAException.XAER_RMFAIL);
0888: }
0889:
0890: private String getXAFuncStr(int xaFunc) {
0891: switch (xaFunc) {
0892: case XAFUNC_COMMIT:
0893: return XAFUNCSTR_COMMIT;
0894: case XAFUNC_END:
0895: return XAFUNCSTR_END;
0896: case XAFUNC_FORGET:
0897: return XAFUNCSTR_FORGET;
0898: case XAFUNC_PREPARE:
0899: return XAFUNCSTR_PREPARE;
0900: case XAFUNC_RECOVER:
0901: return XAFUNCSTR_RECOVER;
0902: case XAFUNC_ROLLBACK:
0903: return XAFUNCSTR_ROLLBACK;
0904: case XAFUNC_START:
0905: return XAFUNCSTR_START;
0906: }
0907: return XAFUNCSTR_NONE;
0908: }
0909:
0910: protected int xaRetValErrorAccumSQL(NetXACallInfo callInfo,
0911: int currentRC) {
0912:
0913: // xaRetVal_ is set by the server to be one of the
0914: // standard constants from XAException.
0915: int rc = callInfo.xaRetVal_;
0916:
0917: if (rc != XAResource.XA_OK) { // error was detected
0918: // create an SqlException to report this error within
0919: SqlException accumSql = new SqlException(
0920: conn_.netAgent_.logWriter_, new ClientMessageId(
0921: SQLState.NET_XARETVAL_ERROR),
0922: getXAFuncStr(callInfo.xaFunction_),
0923: getXAExceptionText(rc),
0924: org.apache.derby.client.am.SqlCode.queuedXAError);
0925: exceptionsOnXA = org.apache.derby.client.am.Utils
0926: .accumulateSQLException(accumSql, exceptionsOnXA);
0927:
0928: if (currentRC != XAResource.XA_OK) { // the rc passed into this function had an error also, prioritize error
0929: if (currentRC < 0) { // rc passed in was a major error use it instead of current error
0930: return currentRC;
0931: }
0932: }
0933: }
0934: return rc;
0935: }
0936:
0937: private String processLocalHost(String serverName) {
0938: if (serverName.equalsIgnoreCase("localhost")) { // this is a localhost, find hostname
0939: try {
0940: InetAddress localhostNameIA = InetAddress
0941: .getLocalHost();
0942: String localhostName = localhostNameIA.getHostName();
0943: return localhostName;
0944: } catch (SecurityException se) {
0945: return serverName;
0946: } catch (UnknownHostException ue) {
0947: return serverName;
0948: }
0949: }
0950: // not "localhost", return original server name
0951: return serverName;
0952: }
0953:
0954: protected void removeXaresFromSameRMchain() {
0955: // check all NetXAResources on the same RM for the NetXAResource to remove
0956: try {
0957: this .ignoreMe_ = true; // use the ignoreMe_ flag to indicate the
0958: // XAResource to remove
0959: NetXAResource prevXAResource = null;
0960: NetXAResource currXAResource;
0961: synchronized (xaResourceSameRMGroup_) { // make sure no one changes this vector list
0962: currXAResource = (NetXAResource) xaResourceSameRMGroup_
0963: .elementAt(sameRMGroupIndex_);
0964: while (currXAResource != null) { // is this the XAResource to remove?
0965: if (currXAResource.ignoreMe_) { // this NetXAResource is the one to remove
0966: if (prevXAResource != null) { // this XAResource is not first in chain, just move next to prev
0967: prevXAResource.nextSameRM_ = currXAResource.nextSameRM_;
0968: } else { // this XAResource is first in chain, just move next to root
0969: xaResourceSameRMGroup_.set(
0970: sameRMGroupIndex_,
0971: currXAResource.nextSameRM_);
0972: }
0973: return;
0974: }
0975: // this is not the NetXAResource to remove, try the next one
0976: prevXAResource = currXAResource;
0977: currXAResource = currXAResource.nextSameRM_;
0978: }
0979: }
0980: } finally {
0981: this .ignoreMe_ = false;
0982: }
0983: }
0984:
0985: public void initForReuse() {
0986: // add this new XAResource to the list of other XAResources for the Same RM
0987: // first find out if there are any other XAResources for the same RM
0988: // then check to make sure it is not already in the chain
0989: synchronized (xaResourceSameRMGroup_) { // make sure no one changes this vector list
0990: int groupCount = xaResourceSameRMGroup_.size();
0991: int index = 0;
0992: int firstFreeElement = -1;
0993: NetXAResource xaResourceGroup = null;
0994:
0995: for (; index < groupCount; ++index) { // check if this group is the same RM
0996: xaResourceGroup = (NetXAResource) xaResourceSameRMGroup_
0997: .elementAt(index);
0998: if (xaResourceGroup == null) { // this is a free element, save its index if first found
0999: if (firstFreeElement == -1) { // first free element, save index
1000: firstFreeElement = index;
1001: }
1002: continue; // go to next element
1003: }
1004: try {
1005: if (xaResourceGroup.isSameRM(this )) { // it is the same RM add this XAResource to the chain if not there
1006: NetXAResource nextXares = (NetXAResource) xaResourceSameRMGroup_
1007: .elementAt(sameRMGroupIndex_);
1008: while (nextXares != null) { // is this NetXAResource the one we are trying to add?
1009: if (nextXares.equals(this )) { // the XAResource to be added already is in chain, don't add
1010: break;
1011: }
1012: // Xid was not on that NetXAResource, try the next one
1013: nextXares = nextXares.nextSameRM_;
1014: }
1015:
1016: if (nextXares == null) { // XAResource to be added is not in the chain already, add it
1017: // add it at the head of the chain
1018: sameRMGroupIndex_ = index;
1019: this .nextSameRM_ = xaResourceGroup.nextSameRM_;
1020: xaResourceGroup.nextSameRM_ = this ;
1021: }
1022: return; // done
1023: }
1024: } catch (XAException xae) {
1025: }
1026: }
1027:
1028: // no other same RM was found, add this as first of new group
1029: if (firstFreeElement == -1) { // no free element found, add new element to end
1030: xaResourceSameRMGroup_.add(this );
1031: sameRMGroupIndex_ = groupCount;
1032: } else { // use first free element found
1033: xaResourceSameRMGroup_.setElementAt(this,
1034: firstFreeElement);
1035: sameRMGroupIndex_ = firstFreeElement;
1036: }
1037: }
1038: }
1039: }
|