Description:
In the class com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection (used when pinGlobalTxToPhysicalConnection=true). There's a static Map (XIDS_TO_PHYSICAL_CONNECTIONS) that tracks the Xid with the XAConnection, however this map is never populated.
The effect is that the SuspendableXAConnection is never pinned to the real XA connection.
Instead its creating new connections at each start/end/resume/prepare/... calls.
Affect versions (tested) 5.0.6, 5.0.8, 5.1.8
How to repeat:
Test case that will fail with an unpatched version of the driver :
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection;
import junit.framework.TestCase;
/**
*
* @author Tony Bussieres
*
*/
public class TestPinnedXAConnection extends TestCase {
public void testXAPinnedConnection() throws Exception {
MysqlXADataSource xads1 = new MysqlXADataSource();
MysqlXADataSource xads2 = new MysqlXADataSource();
xads1.setPinGlobalTxToPhysicalConnection(true);
xads1.setUrl("jdbc:mysql://172.16.13.74:4040/TestDB");
xads1.setUser("testuser");
xads1.setPassword("testpass");
xads1.setLogXaCommands(true);
xads2.setPinGlobalTxToPhysicalConnection(true);
xads2.setUrl("jdbc:mysql://172.16.13.74:4040/TestDB2");
xads2.setUser("testuser");
xads2.setPassword("testpass");
xads2.setLogXaCommands(true);
Xid txid = new MyXid(new byte[] { 0x1 }, new byte[] {0xF}); // fake Xid
XAConnection c1 = xads1.getXAConnection();
assertTrue(c1 instanceof SuspendableXAConnection);
// start a transaction on one connection
c1.getXAResource().start(txid, XAResource.TMNOFLAGS);
c1.getXAResource().end(txid, XAResource.TMSUCCESS);
XAConnection c2 = xads2.getXAConnection();
assertTrue(c2 instanceof SuspendableXAConnection);
// prepare on another one. Since we are using a "pinned" connection we should have the same "currentXAConnection" for both SuspendableXAConnection
c2.getXAResource().prepare(txid); // this will fail without the patch.
c2.getXAResource().commit(txid,false);
/* stack trace generated without the patch :
*
com.mysql.jdbc.jdbc2.optional.MysqlXAException: XAER_RMFAIL: The command cannot be executed when global transaction is in the NON-EXISTING state
at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.mapXAExceptionFromSQLException(MysqlXAConnection.java:600)
at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.dispatchCommand(MysqlXAConnection.java:583)
at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.prepare(MysqlXAConnection.java:385)
at com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection.prepare(SuspendableXAConnection.java:136)
at TestXA.testXAPinnedConnection(TestXA.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
*/
}
class MyXid implements Xid {
byte[] gtxid;
byte[] brid;
public MyXid(byte[] gtxid, byte[] brid) {
this.gtxid = gtxid;
this.brid = brid;
}
public byte[] getBranchQualifier() {
return brid;
}
public int getFormatId() {
return 0;
}
public byte[] getGlobalTransactionId() {
return gtxid;
}
}
}
Suggested fix:
patch (for 5.1.8)
--- mysql-connector-java-5.1.8/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java 2009-07-14 13:12:13.000000000 -0400
+++ /work/download/mysql-connector-java-5.1.8/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java 2009-08-25 13:29:30.000000000 -0400
@@ -75,6 +75,7 @@
if (conn == null) {
conn = new MysqlXAConnection(connectionToWrap,
connectionToWrap.getLogXaCommands());
+ addXAConnectionMapping(xid,conn);
}
return conn;
@@ -83,6 +84,10 @@
private static synchronized void removeXAConnectionMapping(Xid xid) {
XIDS_TO_PHYSICAL_CONNECTIONS.remove(xid);
}
+
+ private static synchronized void addXAConnectionMapping(Xid xid,XAConnection conn) {
+ XIDS_TO_PHYSICAL_CONNECTIONS.put(xid,conn);
+ }
private synchronized void switchToXid(Xid xid) throws XAException {
if (xid == null) {