in src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java [368:744]
private XAReturnValue DTC_XA_Interface(int nType, Xid xid, int xaFlags) throws XAException {
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " Calling XA function for type:" + typeDisplay(nType) + " flags:"
+ flagsDisplay(xaFlags) + " xid:" + XidImpl.xidDisplay(xid));
int formatId = 0;
byte gid[] = null;
byte bid[] = null;
if (xid != null) {
formatId = xid.getFormatId();
gid = xid.getGlobalTransactionId();
bid = xid.getBranchQualifier();
}
String sContext = "DTC_XA_";
int n = 1;
int nStatus = 0;
XAReturnValue returnStatus = new XAReturnValue();
SQLServerCallableStatement cs = null;
try {
synchronized (this) {
if (!xaInitDone) {
try {
synchronized (xaInitLock) {
SQLServerCallableStatement initCS = null;
initCS = (SQLServerCallableStatement) controlConnection
.prepareCall("{call master..xp_sqljdbc_xa_init_ex(?, ?,?)}");
initCS.registerOutParameter(1, Types.INTEGER); // Return status
initCS.registerOutParameter(2, Types.CHAR); // Return error message
initCS.registerOutParameter(3, Types.CHAR); // Return version number
try {
initCS.execute();
} catch (SQLServerException eX) {
try {
initCS.close();
// Mapping between control connection and xaresource is 1:1
controlConnection.close();
} catch (SQLException e3) {
// we really want to ignore this failue
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString()
+ " Ignoring exception when closing failed execution. exception:" + e3);
}
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " exception:" + eX);
throw eX;
} catch (SQLTimeoutException e4) {
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " exception:" + e4);
throw new SQLServerException(e4.getMessage(), SQLState.STATEMENT_CANCELED,
DriverError.NOT_SET, null);
}
// Check for error response from xp_sqljdbc_xa_init.
int initStatus = initCS.getInt(1);
String initErr = initCS.getString(2);
String versionNumberXADLL = initCS.getString(3);
if (xaLogger.isLoggable(Level.FINE))
xaLogger.fine(toString() + " Server XA DLL version:" + versionNumberXADLL);
initCS.close();
if (XA_OK != initStatus) {
assert null != initErr && initErr.length() > 1;
controlConnection.close();
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_failedToInitializeXA"));
Object[] msgArgs = {String.valueOf(initStatus), initErr};
XAException xex = new XAException(form.format(msgArgs));
xex.errorCode = initStatus;
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " exception:" + xex);
throw xex;
}
}
} catch (SQLServerException e1) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_failedToCreateXAConnection"));
Object[] msgArgs = {e1.getMessage()};
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " exception:" + form.format(msgArgs));
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true);
}
xaInitDone = true;
}
}
switch (nType) {
case XA_START:
if (!serverInfoRetrieved) {
String query = "select convert(varchar(100), SERVERPROPERTY('Edition'))as edition, "
+ " convert(varchar(100), SERVERPROPERTY('InstanceName'))as instance,"
+ " convert(varchar(100), SERVERPROPERTY('ProductVersion')) as version, @@VERSION;";
try (Statement stmt = controlConnection.createStatement();
ResultSet rs = stmt.executeQuery(query);) {
serverInfoRetrieved = true;
rs.next();
String edition = rs.getString(1);
architectureMSSQL = ((null != edition) && (edition.contains("(64-bit)"))) ? 64 : 32;
// if InstanceName is null use the default instance without name (MSSQLSERVER)
instanceName = (rs.getString(2) == null) ? "MSSQLSERVER" : rs.getString(2);
version = rs.getString(3);
if (null == version) {
version = "0";
} else if (-1 != version.indexOf('.')) {
version = version.substring(0, version.indexOf('.'));
}
/*
* @@VERSION returns single nvarchar string with SQL version, architecture, build date,
* edition and OS version.
*/
String buildInfo = rs.getString(4);
// SQL Server Linux is x64-compatible only.
if (null != buildInfo && (buildInfo.contains("Linux") || buildInfo.contains("Microsoft SQL Azure"))) {
architectureOS = 64;
} else if (null != buildInfo) {
architectureOS = Integer.parseInt(buildInfo.substring(buildInfo.lastIndexOf('<') + 2,
buildInfo.lastIndexOf('>')));
}
}
// Catch only the thrown exceptions, do not catch run time exceptions.
catch (Exception e) {
if (xaLogger.isLoggable(Level.WARNING))
xaLogger.warning(
toString() + " Cannot retrieve server information: :" + e.getMessage());
}
}
sContext = "START:";
cs = getXACallableStatementHandle(XA_START);
cs.registerOutParameter(n++, Types.INTEGER); // Return status
cs.registerOutParameter(n++, Types.CHAR); // Return error message
cs.setBytes(n++, gid); // Global XID
cs.setBytes(n++, bid); // Branch ID
cs.setInt(n++, xaFlags); // XA transaction flags
cs.registerOutParameter(n++, Types.BINARY); // Returned OLE transaction cookie
cs.setInt(n++, timeoutSeconds); // Transaction timeout in seconds.
cs.setInt(n++, formatId); // Format ID
cs.registerOutParameter(n++, Types.CHAR); // DLL Version number
cs.setInt(n++, Integer.parseInt(version)); // Version of SQL Server
cs.setInt(n++, instanceName.length()); // Length of SQL Server instance name
cs.setBytes(n++, instanceName.getBytes()); // SQL Server instance name
cs.setInt(n++, architectureMSSQL); // Architecture of SQL Server
cs.setInt(n++, architectureOS); // Architecture of OS running SQL Server
cs.setInt(n++, isTransacrionTimeoutSet); // pass 1 if setTransactionTimeout() is called
cs.registerOutParameter(n++, Types.BINARY); // Return UoW
break;
case XA_END:
sContext = "END:";
cs = getXACallableStatementHandle(XA_END);
cs.registerOutParameter(n++, Types.INTEGER);
cs.registerOutParameter(n++, Types.CHAR);
cs.setBytes(n++, gid);
cs.setBytes(n++, bid);
cs.setInt(n++, xaFlags);
cs.setInt(n++, formatId);
cs.registerOutParameter(n++, Types.BINARY); // Return UoW
break;
case XA_PREPARE:
sContext = "PREPARE:";
if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
cs = getXACallableStatementHandle(XA_PREPARE_EX);
else
cs = getXACallableStatementHandle(XA_PREPARE);
cs.registerOutParameter(n++, Types.INTEGER);
cs.registerOutParameter(n++, Types.CHAR);
cs.setBytes(n++, gid);
cs.setBytes(n++, bid);
if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
cs.setInt(n++, xaFlags); // XA transaction flags
cs.setInt(n++, formatId); // Format ID n=5 for loosely coupled, n=6 for tightly coupled
break;
case XA_COMMIT:
sContext = "COMMIT:";
cs = getXACallableStatementHandle(XA_COMMIT);
cs.registerOutParameter(n++, Types.INTEGER);
cs.registerOutParameter(n++, Types.CHAR);
cs.setBytes(n++, gid);
cs.setBytes(n++, bid);
cs.setInt(n++, xaFlags);
cs.setInt(n++, formatId);
break;
case XA_ROLLBACK:
sContext = "ROLLBACK:";
if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
cs = getXACallableStatementHandle(XA_ROLLBACK_EX);
else
cs = getXACallableStatementHandle(XA_ROLLBACK);
cs.registerOutParameter(n++, Types.INTEGER);
cs.registerOutParameter(n++, Types.CHAR);
cs.setBytes(n++, gid);
cs.setBytes(n++, bid);
if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
cs.setInt(n++, xaFlags); // XA transaction flags
cs.setInt(n++, formatId); // Format ID n=5 for loosely coupled, n=6 for tightly coupled
break;
case XA_FORGET:
sContext = "FORGET:";
if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
cs = getXACallableStatementHandle(XA_FORGET_EX);
else
cs = getXACallableStatementHandle(XA_FORGET);
cs.registerOutParameter(n++, Types.INTEGER);
cs.registerOutParameter(n++, Types.CHAR);
cs.setBytes(n++, gid);
cs.setBytes(n++, bid);
if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
cs.setInt(n++, xaFlags); // XA transaction flags
cs.setInt(n++, formatId); // Format ID n=5 for loosely coupled, n=6 for tightly coupled
break;
case XA_RECOVER:
sContext = "RECOVER:";
cs = getXACallableStatementHandle(XA_RECOVER);
cs.registerOutParameter(n++, Types.INTEGER);
cs.registerOutParameter(n++, Types.CHAR);
cs.setInt(n++, xaFlags);
cs.registerOutParameter(n++, Types.BINARY);
// Format Id need not be sent for recover action
break;
default:
assert false : "Unknown execution type:" + nType;
break;
}
/* execute the interface procedure */
cs.execute();
nStatus = cs.getInt(1);
String sErr = cs.getString(2);
if (nType == XA_START) {
String versionNumberXADLL = cs.getString(9);
if (xaLogger.isLoggable(Level.FINE)) {
xaLogger.fine(toString() + " Server XA DLL version:" + versionNumberXADLL);
if (null != cs.getString(16)) {
StringBuffer strBuf = new StringBuffer(cs.getString(16));
strBuf.insert(20, '-');
strBuf.insert(16, '-');
strBuf.insert(12, '-');
strBuf.insert(8, '-');
xaLogger.fine(toString() + " XID to UoW mapping for XA type:XA_START XID: "
+ XidImpl.xidDisplay(xid) + " UoW: " + strBuf.toString());
}
}
}
if (nType == XA_END) {
if (xaLogger.isLoggable(Level.FINE)) {
if (null != cs.getString(7)) {
StringBuffer strBuf = new StringBuffer(cs.getString(7));
strBuf.insert(20, '-');
strBuf.insert(16, '-');
strBuf.insert(12, '-');
strBuf.insert(8, '-');
xaLogger.fine(toString() + " XID to UoW mapping for XA type:XA_END XID: "
+ XidImpl.xidDisplay(xid) + " UoW: " + strBuf.toString());
}
}
}
if (XA_RECOVER == nType && XA_OK != nStatus && recoveryAttempt < 1) {
// if recover failed, attempt to start again - adding the variable to check to attempt only once
// otherwise throw exception that recovery fails
// this is added since before this change, if we restart the MSDTC and attempt to do recovery, driver
// will throw exception
// "The function RECOVER: failed. The status is: -3"
recoveryAttempt++;
DTC_XA_Interface(XA_START, xid, TMNOFLAGS);
return DTC_XA_Interface(XA_RECOVER, xid, xaFlags);
}
// prepare and end can return XA_RDONLY
// Think should we just check for nStatus to be greater than or equal to zero instead of this check
if (((XA_RDONLY == nStatus) && (XA_END != nType && XA_PREPARE != nType))
|| (XA_OK != nStatus && XA_RDONLY != nStatus)) {
assert (null != sErr) && (sErr.length() > 1);
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedFunctionXA"));
Object[] msgArgs = {sContext, String.valueOf(nStatus), sErr};
XAException e = new XAException(form.format(msgArgs));
e.errorCode = nStatus;
// if the request is end make sure we delist from the DTC transaction on rm failure.
if (nType == XA_END && (XAException.XAER_RMFAIL == nStatus)) {
try {
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " Begin un-enlist, enlisted count:" + enlistedTransactionCount);
con.JTAUnenlistConnection();
enlistedTransactionCount--;
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " End un-enlist, enlisted count:" + enlistedTransactionCount);
} catch (SQLServerException e1) {
// ignore this message as the previous error message is more important.
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " Ignoring exception:" + e1);
}
}
throw e;
} else {
if (nType == XA_START) {
// A physical connection may not have been enlisted yet so always enlist.
byte transactionCookie[] = cs.getBytes(6);
if (transactionCookie == null) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_noTransactionCookie"));
Object[] msgArgs = {sContext};
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true);
} else {
try {
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(
toString() + " Begin enlisting, cookie:" + cookieDisplay(transactionCookie)
+ " enlisted count:" + enlistedTransactionCount);
con.JTAEnlistConnection(transactionCookie);
enlistedTransactionCount++;
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " End enlisting, cookie:" + cookieDisplay(transactionCookie)
+ " enlisted count:" + enlistedTransactionCount);
} catch (SQLServerException e1) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToEnlist"));
Object[] msgArgs = {e1.getMessage()};
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true);
}
}
}
if (nType == XA_END) {
try {
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " Begin un-enlist, enlisted count:" + enlistedTransactionCount);
con.JTAUnenlistConnection();
enlistedTransactionCount--;
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " End un-enlist, enlisted count:" + enlistedTransactionCount);
} catch (SQLServerException e1) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToUnEnlist"));
Object[] msgArgs = {e1.getMessage()};
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true);
}
}
if (nType == XA_RECOVER)
{
try {
returnStatus.bData = cs.getBytes(4);
} catch (SQLServerException e1) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_failedToReadRecoveryXIDs"));
Object[] msgArgs = {e1.getMessage()};
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, true);
}
}
}
} catch (SQLServerException | SQLTimeoutException ex) {
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " exception:" + ex);
XAException e = new XAException(ex.toString());
e.errorCode = XAException.XAER_RMERR;
throw e;
}
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer(toString() + " Status:" + nStatus);
returnStatus.nStatus = nStatus;
return returnStatus;
}