List:Commits« Previous MessageNext Message »
From:mmatthews Date:June 27 2006 5:00pm
Subject:Connector/J commit: r5440 - in branches/branch_5_0/connector-j: . src/com/mysql/jdbc src/com/mysql/jdbc/jdbc2/optional src/testsuite src/testsuite/sim...
View as plain text  
Added:
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
Modified:
   branches/branch_5_0/connector-j/CHANGES
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
   branches/branch_5_0/connector-j/src/testsuite/BaseTestCase.java
   branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java
Log:
Added connection/datasource property  "pinGlobalTxToPhysicalConnection" 
	  (defaults to "false"). When set to "true", when using XAConnections, the 
	  driver ensures that operations on a given XID are always routed to the 
	  same physical connection. This allows the XAConnection to support 
	  "XA START ... JOIN" after "XA END" has been called, and is also a 
	  workaround for transaction managers that don't maintain thread affinity
	  for a global transaction (most either always maintain thread affinity, 
	  or have it as a configuration option).

Modified: branches/branch_5_0/connector-j/CHANGES
===================================================================
--- branches/branch_5_0/connector-j/CHANGES	2006-06-27 00:01:37 UTC (rev 5439)
+++ branches/branch_5_0/connector-j/CHANGES	2006-06-27 17:00:53 UTC (rev 5440)
@@ -20,7 +20,19 @@
 	  
 	- Fixed BUG#20242 - MysqlValidConnectionChecker for JBoss doesn't
 	  work with MySQLXADataSources.
-      
+	  
+	- Better caching of character set converters (per-connection)
+	  to remove a bottleneck for multibyte character sets.
+	  
+	- Added connection/datasource property  "pinGlobalTxToPhysicalConnection" 
+	  (defaults to "false"). When set to "true", when using XAConnections, the 
+	  driver ensures that operations on a given XID are always routed to the 
+	  same physical connection. This allows the XAConnection to support 
+	  "XA START ... JOIN" after "XA END" has been called, and is also a 
+	  workaround for transaction managers that don't maintain thread affinity
+	  for a global transaction (most either always maintain thread affinity, 
+	  or have it as a configuration option).
+	  
 12-23-05 - Version 5.0.0-beta
 
     - XADataSource implemented (ported from 3.2 branch which won't be 

Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java	2006-06-27 00:01:37 UTC (rev 5439)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java	2006-06-27 17:00:53 UTC (rev 5440)
@@ -1037,6 +1037,12 @@
 			"pedantic", false, "Follow the JDBC spec to the letter.", "3.0.0",
 			MISC_CATEGORY, Integer.MIN_VALUE);
 
+	private BooleanConnectionProperty pinGlobalTxToPhysicalConnection = new BooleanConnectionProperty(
+			"pinGlobalTxToPhysicalConnection", false, "When using XAConnections, should the driver ensure that "
+			+ " operations on a given XID are always routed to the same physical connection? This allows the XAConnection"
+			+ " to support \"XA START ... JOIN\" after \"XA END\" has been called",
+			"5.0.1", MISC_CATEGORY, Integer.MIN_VALUE);
+	
 	private IntegerConnectionProperty preparedStatementCacheSize = new IntegerConnectionProperty(
 			"prepStmtCacheSize", 25, 0, Integer.MAX_VALUE,
 			"If prepared statement caching is enabled, "
@@ -3553,55 +3559,55 @@
 	 * 
 	 * @return Returns the useUnbufferedInput.
 	 */
-	protected boolean useUnbufferedInput() {
+	public boolean useUnbufferedInput() {
 		return this.useUnbufferedInput.getValueAsBoolean();
 	}
 
-	protected boolean getUseCursorFetch() {
+	public boolean getUseCursorFetch() {
 		return this.useCursorFetch.getValueAsBoolean();
 	}
 
-	protected void setUseCursorFetch(boolean flag) {
+	public void setUseCursorFetch(boolean flag) {
 		this.useCursorFetch.setValue(flag);
 	}
 
-	protected boolean getOverrideSupportsIntegrityEnhancementFacility() {
+	public boolean getOverrideSupportsIntegrityEnhancementFacility() {
 		return this.overrideSupportsIntegrityEnhancementFacility.getValueAsBoolean();
 	}
 
-	protected void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) {
+	public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) {
 		this.overrideSupportsIntegrityEnhancementFacility.setValue(flag);	
 	}
 	
-	protected boolean getNoTimezoneConversionForTimeType() {
+	public boolean getNoTimezoneConversionForTimeType() {
 		return this.noTimezoneConversionForTimeType.getValueAsBoolean();
 	}
 
-	protected void setNoTimezoneConversionForTimeType(boolean flag) {
+	public void setNoTimezoneConversionForTimeType(boolean flag) {
 		this.noTimezoneConversionForTimeType.setValue(flag);
 	}
 
-	protected boolean getUseJDBCCompliantTimezoneShift() {
+	public boolean getUseJDBCCompliantTimezoneShift() {
 		return this.useJDBCCompliantTimezoneShift.getValueAsBoolean();
 	}
 
-	protected void setUseJDBCCompliantTimezoneShift(boolean flag) {
+	public void setUseJDBCCompliantTimezoneShift(boolean flag) {
 		this.useJDBCCompliantTimezoneShift.setValue(flag);
 	}
 	
-	protected boolean getAutoClosePStmtStreams() {
+	public boolean getAutoClosePStmtStreams() {
 		return this.autoClosePStmtStreams.getValueAsBoolean();
 	}
 
-	protected void setAutoClosePStmtStreams(boolean flag) {
+	public void setAutoClosePStmtStreams(boolean flag) {
 		this.autoClosePStmtStreams.setValue(flag);
 	}
 
-	protected boolean getProcessEscapeCodesForPrepStmts() {
+	public boolean getProcessEscapeCodesForPrepStmts() {
 		return this.processEscapeCodesForPrepStmts.getValueAsBoolean();
 	}
 
-	protected void setProcessEscapeCodesForPrepStmts(boolean flag) {
+	public void setProcessEscapeCodesForPrepStmts(boolean flag) {
 		this.processEscapeCodesForPrepStmts.setValue(flag);
 	}
 
@@ -3613,30 +3619,55 @@
 		this.useGmtMillisForDatetimes.setValue(flag);
 	}
 	
-	protected boolean getDumpMetadataOnColumnNotFound() {
+	public boolean getDumpMetadataOnColumnNotFound() {
 		return this.dumpMetadataOnColumnNotFound.getValueAsBoolean();
 	}
 
-	protected void setDumpMetadataOnColumnNotFound(boolean flag) {
+	public void setDumpMetadataOnColumnNotFound(boolean flag) {
 		this.dumpMetadataOnColumnNotFound.setValue(flag);
 	}
 
-	protected String getResourceId() {
+	public String getResourceId() {
 		return this.resourceId.getValueAsString();
 	}
 
-	protected void setResourceId(String resourceId) {
+	public void setResourceId(String resourceId) {
 		this.resourceId.setValue(resourceId);
 	}
 	
-	protected boolean getRewriteBatchedStatements() {
+	public boolean getRewriteBatchedStatements() {
 		return this.rewriteBatchedStatements.getValueAsBoolean();
 	}
 
-	protected void setRewriteBatchedStatements(boolean flag) {
+	public void setRewriteBatchedStatements(boolean flag) {
 		this.rewriteBatchedStatements.setValue(flag);
 	}
 	
+	public boolean getJdbcCompliantTruncationForReads() {
+		return this.jdbcCompliantTruncationForReads;
+	}
+
+	public void setJdbcCompliantTruncationForReads(
+			boolean jdbcCompliantTruncationForReads) {
+		this.jdbcCompliantTruncationForReads = jdbcCompliantTruncationForReads;
+	}
+
+	public boolean getUseJvmCharsetConverters() {
+		return this.useJvmCharsetConverters.getValueAsBoolean();
+	}
+
+	public void setUseJvmCharsetConverters(boolean flag) {
+		this.useJvmCharsetConverters.setValue(flag);
+	}
+
+	public boolean getPinGlobalTxToPhysicalConnection() {
+		return this.pinGlobalTxToPhysicalConnection.getValueAsBoolean();
+	}
+
+	public void setPinGlobalTxToPhysicalConnection(boolean flag) {
+		this.pinGlobalTxToPhysicalConnection.setValue(flag);
+	}
+	
 	/*
 	 * "Aliases" which match the property names to make using 
 	 * from datasources easier.
@@ -3690,20 +3721,5 @@
 		return getPreparedStatementCacheSqlLimit();
 	}
 
-	protected boolean getJdbcCompliantTruncationForReads() {
-		return this.jdbcCompliantTruncationForReads;
-	}
 
-	protected void setJdbcCompliantTruncationForReads(
-			boolean jdbcCompliantTruncationForReads) {
-		this.jdbcCompliantTruncationForReads = jdbcCompliantTruncationForReads;
-	}
-
-	protected boolean getUseJvmCharsetConverters() {
-		return this.useJvmCharsetConverters.getValueAsBoolean();
-	}
-
-	protected void setUseJvmCharsetConverters(boolean flag) {
-		this.useJvmCharsetConverters.setValue(flag);
-	}
 }

Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java	2006-06-27 00:01:37 UTC (rev 5439)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java	2006-06-27 17:00:53 UTC (rev 5440)
@@ -65,6 +65,11 @@
 	 */
 
 	private XAConnection wrapConnection(Connection conn) throws SQLException {
+		if (getPinGlobalTxToPhysicalConnection() || 
+				((com.mysql.jdbc.Connection)conn).getPinGlobalTxToPhysicalConnection()) {
+			return new SuspendableXAConnection((com.mysql.jdbc.Connection) conn);
+		}
+		
 		return new MysqlXAConnection((com.mysql.jdbc.Connection) conn);
 	}
 }
\ No newline at end of file

Added: branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java	2006-06-27 00:01:37 UTC (rev 5439)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java	2006-06-27 17:00:53 UTC (rev 5440)
@@ -0,0 +1,153 @@
+package com.mysql.jdbc.jdbc2.optional;
+
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.sql.XAConnection;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.mysql.jdbc.Connection;
+
+public class SuspendableXAConnection extends MysqlPooledConnection implements
+XAConnection, XAResource {
+
+	public SuspendableXAConnection(Connection connection) {
+		super(connection);
+		this.underlyingConnection = connection;
+	}
+
+	private static final Map XIDS_TO_PHYSICAL_CONNECTIONS = 
+		new HashMap();
+	
+	private Xid currentXid;
+	
+	private XAConnection currentXAConnection;
+	private XAResource currentXAResource;
+	
+	private Connection underlyingConnection;
+	
+	private static synchronized XAConnection findConnectionForXid(Connection connectionToWrap, Xid xid) 
+		throws SQLException {
+		// TODO: check for same GTRID, but different BQUALs...MySQL doesn't allow this yet
+		
+		// Note, we don't need to check for XIDs here, because MySQL itself will complain
+		// with a XAER_NOTA if need be.
+		
+		XAConnection conn = (XAConnection)XIDS_TO_PHYSICAL_CONNECTIONS.get(xid);
+
+		if (conn == null) {
+			conn = new MysqlXAConnection(connectionToWrap);
+		}
+		
+		return conn;
+	}
+	
+	private static synchronized void removeXAConnectionMapping(Xid xid) {
+		XIDS_TO_PHYSICAL_CONNECTIONS.remove(xid);
+	}
+	
+	private synchronized void switchToXid(Xid xid) throws XAException {
+		if (xid == null) {
+			throw new XAException();
+		}
+		
+		try {
+			if (!xid.equals(this.currentXid)) {
+				XAConnection toSwitchTo = findConnectionForXid(this.underlyingConnection, xid);
+				this.currentXAConnection = toSwitchTo;
+				this.currentXid = xid;
+				this.currentXAResource = toSwitchTo.getXAResource();
+			}
+		} catch (SQLException sqlEx) {
+			throw new XAException();
+		}
+	}
+	
+	public XAResource getXAResource() throws SQLException {
+		return this;
+	}
+
+	public void commit(Xid xid, boolean arg1) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.commit(xid, arg1);
+		removeXAConnectionMapping(xid);
+	}
+
+	public void end(Xid xid, int arg1) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.end(xid, arg1);
+	}
+
+	public void forget(Xid xid) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.forget(xid);
+		// remove?
+		removeXAConnectionMapping(xid);
+	}
+
+	public int getTransactionTimeout() throws XAException {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public boolean isSameRM(XAResource xaRes) throws XAException {
+		return xaRes == this;
+	}
+
+	public int prepare(Xid xid) throws XAException {
+		switchToXid(xid);
+		return this.currentXAResource.prepare(xid);
+	}
+
+	public Xid[] recover(int flag) throws XAException {
+		return MysqlXAConnection.recover(this.underlyingConnection, flag);
+	}
+
+	public void rollback(Xid xid) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.rollback(xid);
+		removeXAConnectionMapping(xid);
+	}
+
+	public boolean setTransactionTimeout(int arg0) throws XAException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public void start(Xid xid, int arg1) throws XAException {
+		switchToXid(xid);
+		
+		if (arg1 != XAResource.TMJOIN) {
+			this.currentXAResource.start(xid, arg1);
+			
+			return;
+		}
+		
+		//
+		// Emulate join, by using resume on the same physical connection
+		//
+		
+		this.currentXAResource.start(xid, XAResource.TMRESUME);
+	}
+
+	public synchronized java.sql.Connection getConnection() throws SQLException {
+		if (this.currentXAConnection == null) {
+			return getConnection(false, true);
+		}
+			
+		return this.currentXAConnection.getConnection();
+	}
+
+	public void close() throws SQLException {
+		if (this.currentXAConnection == null) {
+			super.close();
+		} else {
+			removeXAConnectionMapping(this.currentXid);
+			this.currentXAConnection.close();
+		}
+	}
+}

Modified: branches/branch_5_0/connector-j/src/testsuite/BaseTestCase.java
===================================================================
--- branches/branch_5_0/connector-j/src/testsuite/BaseTestCase.java	2006-06-27 00:01:37 UTC (rev 5439)
+++ branches/branch_5_0/connector-j/src/testsuite/BaseTestCase.java	2006-06-27 17:00:53 UTC (rev 5440)
@@ -37,6 +37,7 @@
 import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Properties;
 
 import com.mysql.jdbc.NonRegisteringDriver;
@@ -546,4 +547,10 @@
 			}
 		}
 	}
+	
+	protected boolean isRunningOnJRockit() {
+		String vmVendor = System.getProperty("java.vm.vendor");
+		
+		return (vmVendor != null && vmVendor.toUpperCase(Locale.US).startsWith("BEA"));
+	}
 }

Modified: branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java
===================================================================
--- branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java	2006-06-27 00:01:37 UTC (rev 5439)
+++ branches/branch_5_0/connector-j/src/testsuite/simple/XATest.java	2006-06-27 17:00:53 UTC (rev 5440)
@@ -337,7 +337,77 @@
 			}
 		}
 	}
+	
+	public void testSuspendableTx() throws Exception {
+		if (!versionMeetsMinimum(5, 0) || isRunningOnJdk131()) {
+			return;
+		}
+		
+		Connection conn1 = null;
 
+		MysqlXADataSource suspXaDs = new MysqlXADataSource();
+		suspXaDs.setUrl(BaseTestCase.dbUrl);
+		suspXaDs.setPinGlobalTxToPhysicalConnection(true);
+		suspXaDs.setRollbackOnPooledClose(true);
+		
+		XAConnection xaConn1 = null;
+		
+		Xid xid = createXid();
+		
+		try {
+			/*
+			  	-- works using RESUME
+				xa start 0x123,0x456;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa start 0x123,0x456 resume;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa commit 0x123,0x456 one phase;
+			 */
+			
+			xaConn1 = suspXaDs.getXAConnection();
+			XAResource xaRes1 = xaConn1.getXAResource();
+			conn1 = xaConn1.getConnection();
+			xaRes1.start(xid, XAResource.TMNOFLAGS);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.start(xid, XAResource.TMRESUME);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.commit(xid, true);
+			
+			xaConn1.close();
+			
+			/*
+
+				-- fails using JOIN
+				xa start 0x123,0x456;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa start 0x123,0x456 join;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa commit 0x123,0x456 one phase;
+				*/
+		
+			xaConn1 = suspXaDs.getXAConnection();
+			xaRes1 = xaConn1.getXAResource();
+			conn1 = xaConn1.getConnection();
+			xaRes1.start(xid, XAResource.TMNOFLAGS);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.start(xid, XAResource.TMJOIN);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.commit(xid, true);
+		} finally {
+			if (xaConn1 != null) {
+				xaConn1.close();
+			}
+		}
+	}
+
 	private Xid createXid() throws IOException {
 		ByteArrayOutputStream gtridOut = new ByteArrayOutputStream();
 		DataOutputStream dataOut = new DataOutputStream(gtridOut);

Thread
Connector/J commit: r5440 - in branches/branch_5_0/connector-j: . src/com/mysql/jdbc src/com/mysql/jdbc/jdbc2/optional src/testsuite src/testsuite/sim...mmatthews27 Jun