Wrap XAConnection's underlying connections to ensure they go back into the pool. Add tests for XA functionality. Ensure connections are created correctly for XA datasource when using InitialSize.
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/9e19d4a4 Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/9e19d4a4 Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/9e19d4a4 Branch: refs/heads/tomee-1.7.x Commit: 9e19d4a441109ffb0583fdb365d32a03e9a062c2 Parents: 75ac9b4 Author: Jonathan Gallimore <[email protected]> Authored: Wed Aug 10 00:29:59 2016 +0100 Committer: Jonathan Gallimore <[email protected]> Committed: Wed Aug 10 00:29:59 2016 +0100 ---------------------------------------------------------------------- .../jdbc/managed/local/ManagedConnection.java | 36 +++++++- .../openejb/resource/jdbc/XADataSourceTest.java | 24 ++++- examples/xa-datasource/pom.xml | 4 +- .../superbiz/injection/jpa/MoviesXATest.java | 95 +------------------- .../tomee/jdbc/TomEEDataSourceCreator.java | 34 +++++-- .../tomee/jdbc/TomcatXADataSourceTest.java | 23 ++++- 6 files changed, 109 insertions(+), 107 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tomee/blob/9e19d4a4/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java index 1e2edfe..bb24f0f 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/managed/local/ManagedConnection.java @@ -23,6 +23,7 @@ import org.apache.openejb.util.Logger; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.Wrapper; @@ -164,7 +165,7 @@ public class ManagedConnection implements InvocationHandler { (key.user == null ? XADataSource.class.cast(key.ds).getXAConnection() : XADataSource.class.cast(key.ds).getXAConnection(key.user, key.pwd)); if (XAConnection.class.isInstance(connection)) { xaConnection = XAConnection.class.cast(connection); - delegate = xaConnection.getConnection(); + delegate = wrapDelegate(xaConnection, xaConnection.getConnection()); xaResource = xaConnection.getXAResource(); } else { delegate = Connection.class.cast(connection); @@ -173,6 +174,13 @@ public class ManagedConnection implements InvocationHandler { return connection; } + private Connection wrapDelegate(final XAConnection xaConnection, final Connection connection) { + return (Connection) Proxy.newProxyInstance( + Thread.currentThread().getContextClassLoader(), + new Class<?>[] { Connection.class }, + new XAConnectionWrapper(xaConnection, connection)); + } + protected void setAutoCommit(final boolean value) throws SQLException { if (delegate == null) { newConnection(); @@ -233,8 +241,8 @@ public class ManagedConnection implements InvocationHandler { private static class ClosingSynchronization implements Synchronization { private final Connection connection; - public ClosingSynchronization(final Connection delegate) { - this.connection = delegate; + public ClosingSynchronization(final Connection connection) { + this.connection = connection; } @Override @@ -285,4 +293,24 @@ public class ManagedConnection implements InvocationHandler { return hash; } } -} \ No newline at end of file + + private class XAConnectionWrapper implements InvocationHandler { + private final XAConnection xaConnection; + private final Connection delegate; + + public XAConnectionWrapper(final XAConnection xaConnection, final Connection delegate) { + this.xaConnection = xaConnection; + this.delegate = delegate; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + if ("close".equals(method.getName()) && (args == null || args.length == 0)) { + xaConnection.close(); + return null; + } else { + return method.invoke(delegate, args); + } + } + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/9e19d4a4/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/XADataSourceTest.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/XADataSourceTest.java b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/XADataSourceTest.java index d01407a..25016b4 100644 --- a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/XADataSourceTest.java +++ b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/XADataSourceTest.java @@ -33,25 +33,37 @@ import org.junit.runner.RunWith; import javax.ejb.EJB; import javax.ejb.EJBException; import javax.ejb.Singleton; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.PersistenceContext; import java.io.File; +import java.lang.management.ManagementFactory; import java.util.Properties; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @RunWith(ApplicationComposer.class) public class XADataSourceTest { + + private static final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + @EJB private XAEJB ejb; @Test - public void checkOperationsWork() { + public void checkOperationsWork() throws Exception { ejb.doSthg(); ejb.assertPersisted(); try { @@ -59,6 +71,16 @@ public class XADataSourceTest { } catch (final EJBException ejbEx) { assertThat(ejbEx.getCause(), instanceOf(IllegalArgumentException.class)); } + + assertEquals(0, getActiveConnections("xadbn")); + assertEquals(0, getActiveConnections("xadbn2")); + } + + private int getActiveConnections(final String dataSourceName) + throws MalformedObjectNameException, MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { + final ObjectName objectName = new ObjectName("openejb.management:ObjectType=datasources,DataSource=" + dataSourceName); + final Object activeConnectionsAttribute = server.getAttribute(objectName, "numActive"); + return (int) (Integer) activeConnectionsAttribute; } @Configuration http://git-wip-us.apache.org/repos/asf/tomee/blob/9e19d4a4/examples/xa-datasource/pom.xml ---------------------------------------------------------------------- diff --git a/examples/xa-datasource/pom.xml b/examples/xa-datasource/pom.xml index fc46986..f73ec8c 100644 --- a/examples/xa-datasource/pom.xml +++ b/examples/xa-datasource/pom.xml @@ -70,9 +70,9 @@ code is dependent on any OpenEJB classes. --> <dependency> - <groupId>org.apache.tomee</groupId> + <groupId>org.apache.openejb</groupId> <artifactId>openejb-core</artifactId> - <version>4.7.5-SNAPSHOT</version> + <version>4.7.5-TT.6</version> <scope>test</scope> </dependency> http://git-wip-us.apache.org/repos/asf/tomee/blob/9e19d4a4/examples/xa-datasource/src/test/java/org/superbiz/injection/jpa/MoviesXATest.java ---------------------------------------------------------------------- diff --git a/examples/xa-datasource/src/test/java/org/superbiz/injection/jpa/MoviesXATest.java b/examples/xa-datasource/src/test/java/org/superbiz/injection/jpa/MoviesXATest.java index 995a8cb..56ee0c6 100644 --- a/examples/xa-datasource/src/test/java/org/superbiz/injection/jpa/MoviesXATest.java +++ b/examples/xa-datasource/src/test/java/org/superbiz/injection/jpa/MoviesXATest.java @@ -48,7 +48,7 @@ public class MoviesXATest { final PersistenceUnit unit = new PersistenceUnit("movie-unit"); unit.setJtaDataSource("movieDatabase"); unit.setNonJtaDataSource("movieDatabaseUnmanaged"); - unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema"); + unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(foreignKeys=true,schemaAction='dropDB,add')"); unit.addClass(Movie.class); return unit; } @@ -70,6 +70,7 @@ public class MoviesXATest { p.put("movieDatabase.UserName", "admin"); p.put("movieDatabase.Password", "admin"); p.put("movieDatabase.MaxActive", "128"); + p.put("movieDatabase.InitialSize", "2"); p.put("movieDatabase.MaxIdle", "25"); p.put("movieDatabase.MinIdle", "10"); p.put("movieDatabase.AccessToUnderlyingConnectionAllowed", "true"); @@ -88,6 +89,7 @@ public class MoviesXATest { p.put("movieDatabaseUnmanaged.UserName", "admin"); p.put("movieDatabaseUnmanaged.Password", "admin"); p.put("movieDatabaseUnmanaged.JtaManaged", "false"); + p.put("movieDatabaseUnmanaged.InitialSize", "2"); p.put("movieDatabaseUnmanaged.MaxActive", "128"); p.put("movieDatabaseUnmanaged.MaxIdle", "25"); p.put("movieDatabaseUnmanaged.MinIdle", "10"); @@ -100,97 +102,6 @@ public class MoviesXATest { p.put("movieDatabaseUnmanaged.MaxOpenPreparedStatements", "1024"); p.put("movieDatabaseUnmanaged.ValidationQuery", "values 1"); - /* - - Configuration for MS SQL Server - - p.put("movieDatabaseXA", "new://Resource?type=javax.sql.XADataSource&class-name=com.microsoft.sqlserver.jdbc.SQLServerXADataSource"); - p.put("movieDatabaseXA.DatabaseName", "moviefun"); - p.put("movieDatabaseXA.URL", "jdbc:sqlserver://localhost:1433;databaseName=moviefun;SelectMethod=cursor;sendStringParametersAsUnicode=false"); - - p.put("movieDatabase", "new://Resource?type=DataSource"); - p.put("movieDatabase.XaDataSource", "movieDatabaseXA"); - p.put("movieDatabase.UserName", "sa"); - p.put("movieDatabase.Password", "XXX"); - p.put("movieDatabase.JtaManaged", "true"); - p.put("movieDatabase.MaxActive", "128"); - p.put("movieDatabase.MaxIdle", "25"); - p.put("movieDatabase.MinIdle", "10"); - p.put("movieDatabase.AccessToUnderlyingConnectionAllowed", "true"); - p.put("movieDatabase.TestOnBorrow", "false"); - p.put("movieDatabase.TestWhileIdle", "true"); - p.put("movieDatabase.TimeBetweenEvictionRuns", "1 minute"); - p.put("movieDatabase.MaxWaitTime", "0 seconds"); - p.put("movieDatabase.PoolPreparedStatements", "true"); - p.put("movieDatabase.MaxOpenPreparedStatements", "1024"); - p.put("movieDatabase.ValidationQuery", "select 1"); - - p.put("movieDatabaseUnmanaged", "new://Resource?type=DataSource"); - p.put("movieDatabaseUnmanaged.LogSql", "true"); - p.put("movieDatabaseUnmanaged.JdbcDriver", "com.microsoft.sqlserver.jdbc.SQLServerDriver"); - p.put("movieDatabaseUnmanaged.JdbcUrl", "jdbc:sqlserver://localhost:1433;databaseName=moviefun;SelectMethod=cursor;sendStringParametersAsUnicode=false"); - p.put("movieDatabaseUnmanaged.UserName", "sa"); - p.put("movieDatabaseUnmanaged.Password", "XXX"); - p.put("movieDatabaseUnmanaged.JtaManaged", "false"); - p.put("movieDatabaseUnmanaged.MaxActive", "128"); - p.put("movieDatabaseUnmanaged.MaxIdle", "25"); - p.put("movieDatabaseUnmanaged.MinIdle", "10"); - p.put("movieDatabaseUnmanaged.AccessToUnderlyingConnectionAllowed", "true"); - p.put("movieDatabaseUnmanaged.TestOnBorrow", "false"); - p.put("movieDatabaseUnmanaged.TestWhileIdle", "true"); - p.put("movieDatabaseUnmanaged.TimeBetweenEvictionRuns", "1 minute"); - p.put("movieDatabaseUnmanaged.MaxWaitTime", "0 seconds"); - p.put("movieDatabaseUnmanaged.PoolPreparedStatements", "true"); - p.put("movieDatabaseUnmanaged.MaxOpenPreparedStatements", "1024"); - p.put("movieDatabaseUnmanaged.ValidationQuery", "select 1"); - - p.put("movieDatabaseXA", "new://Resource?type=javax.sql.XADataSource&class-name=oracle.jdbc.xa.client.OracleXADataSource"); - p.put("movieDatabaseXA.url", "jdbc:oracle:thin:@//localhost:1521/orcl"); - - */ - - /* - - Configuration for Oracle - - p.put("movieDatabase", "new://Resource?type=DataSource"); - p.put("movieDatabase.XaDataSource", "movieDatabaseXA"); - p.put("movieDatabase.JtaManaged", "true"); - p.put("movieDatabase.UserName", "system"); - p.put("movieDatabase.Password", "oracle"); - p.put("movieDatabase.MaxActive", "128"); - p.put("movieDatabase.MaxIdle", "25"); - p.put("movieDatabase.MinIdle", "10"); - p.put("movieDatabase.AccessToUnderlyingConnectionAllowed", "true"); - p.put("movieDatabase.TestOnBorrow", "false"); - p.put("movieDatabase.TestWhileIdle", "true"); - p.put("movieDatabase.TimeBetweenEvictionRuns", "1 minute"); - p.put("movieDatabase.MaxWaitTime", "0 seconds"); - p.put("movieDatabase.PoolPreparedStatements", "true"); - p.put("movieDatabase.MaxOpenPreparedStatements", "1024"); - p.put("movieDatabase.ValidationQuery", "select 1 from dual"); - - p.put("movieDatabaseUnmanaged", "new://Resource?type=DataSource"); - p.put("movieDatabaseUnmanaged.LogSql", "true"); - p.put("movieDatabaseUnmanaged.JdbcDriver", "oracle.jdbc.driver.OracleDriver"); - p.put("movieDatabaseUnmanaged.JdbcUrl", "jdbc:oracle:thin:@//localhost:1521/orcl"); - p.put("movieDatabaseUnmanaged.UserName", "system"); - p.put("movieDatabaseUnmanaged.Password", "oracle"); - p.put("movieDatabaseUnmanaged.JtaManaged", "false"); - p.put("movieDatabaseUnmanaged.MaxActive", "128"); - p.put("movieDatabaseUnmanaged.MaxIdle", "25"); - p.put("movieDatabaseUnmanaged.MinIdle", "10"); - p.put("movieDatabaseUnmanaged.AccessToUnderlyingConnectionAllowed", "true"); - p.put("movieDatabaseUnmanaged.TestOnBorrow", "false"); - p.put("movieDatabaseUnmanaged.TestWhileIdle", "true"); - p.put("movieDatabaseUnmanaged.TimeBetweenEvictionRuns", "1 minute"); - p.put("movieDatabaseUnmanaged.MaxWaitTime", "0 seconds"); - p.put("movieDatabaseUnmanaged.PoolPreparedStatements", "true"); - p.put("movieDatabaseUnmanaged.MaxOpenPreparedStatements", "1024"); - p.put("movieDatabaseUnmanaged.ValidationQuery", "select 1 from dual"); - - */ - System.out.println("Using db: " + db); return p; http://git-wip-us.apache.org/repos/asf/tomee/blob/9e19d4a4/tomee/tomee-jdbc/src/main/java/org/apache/tomee/jdbc/TomEEDataSourceCreator.java ---------------------------------------------------------------------- diff --git a/tomee/tomee-jdbc/src/main/java/org/apache/tomee/jdbc/TomEEDataSourceCreator.java b/tomee/tomee-jdbc/src/main/java/org/apache/tomee/jdbc/TomEEDataSourceCreator.java index 73b6043..4ca7529 100644 --- a/tomee/tomee-jdbc/src/main/java/org/apache/tomee/jdbc/TomEEDataSourceCreator.java +++ b/tomee/tomee-jdbc/src/main/java/org/apache/tomee/jdbc/TomEEDataSourceCreator.java @@ -40,13 +40,16 @@ import org.apache.tomcat.jdbc.pool.PooledConnection; import javax.management.ObjectName; import javax.sql.CommonDataSource; import javax.sql.DataSource; +import javax.sql.XAConnection; import javax.sql.XADataSource; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.sql.Connection; import java.sql.SQLException; import java.util.Map; import java.util.Properties; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class TomEEDataSourceCreator extends PoolDataSourceCreator { @@ -80,17 +83,17 @@ public class TomEEDataSourceCreator extends PoolDataSourceCreator { updateProperties(prop, converted, driver); final PoolConfiguration config = build(PoolProperties.class, converted); - final TomEEDataSource ds = build(TomEEDataSource.class, new TomEEDataSource(config, name), converted); - final String xa = String.class.cast(properties.remove("XaDataSource")); if (xa != null) { - cleanProperty(ds, "xadatasource"); - final XADataSource xaDs = XADataSourceResource.proxy(Thread.currentThread().getContextClassLoader(), xa); - ds.setDataSource(xaDs); + final TomEEDataSource instance = new TomEEDataSource(config, name, xaDs); + return build(TomEEDataSource.class, instance, converted); + + } else { + final TomEEDataSource instance = new TomEEDataSource(config, name); + return build(TomEEDataSource.class, instance, converted); } - return ds; } private void updateProperties(final SuperProperties properties, final Properties converted, final String driver) { @@ -215,7 +218,15 @@ public class TomEEDataSourceCreator extends PoolDataSourceCreator { } public TomEEDataSource(final PoolConfiguration poolConfiguration, final String name) { + this(poolConfiguration, name, null); + } + + public TomEEDataSource(final PoolConfiguration poolConfiguration, final String name, final XADataSource xaDs) { super(readOnly(poolConfiguration)); + if (xaDs != null) { + this.setDataSource(xaDs); + } + try { // just to force the pool to be created and be able to register the mbean createPool(); initJmx(name); @@ -297,6 +308,16 @@ public class TomEEDataSourceCreator extends PoolDataSourceCreator { } } } + + @Override + public Connection getConnection() throws SQLException { + return super.getConnection(); + } + + @Override + public Connection getConnection(final String username, final String password) throws SQLException { + return super.getConnection(username, password); + } } private static class ReadOnlyConnectionpool implements InvocationHandler { @@ -330,6 +351,7 @@ public class TomEEDataSourceCreator extends PoolDataSourceCreator { @Override protected PooledConnection create(final boolean incrementCounter) { final PooledConnection con = super.create(incrementCounter); + if (getPoolProperties().getDataSource() == null) { // using driver // init driver with TCCL ClassLoader cl = Thread.currentThread().getContextClassLoader(); http://git-wip-us.apache.org/repos/asf/tomee/blob/9e19d4a4/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java ---------------------------------------------------------------------- diff --git a/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java b/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java index f44b0bb..68892e5 100644 --- a/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java +++ b/tomee/tomee-jdbc/src/test/java/org/apache/tomee/jdbc/TomcatXADataSourceTest.java @@ -27,17 +27,27 @@ import org.junit.Test; import org.junit.runner.RunWith; import javax.annotation.Resource; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; import javax.sql.DataSource; +import java.lang.management.ManagementFactory; import java.sql.Connection; -import java.sql.SQLException; import java.util.Properties; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; @RunWith(ApplicationComposer.class) public class TomcatXADataSourceTest { + private static final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + @Resource(name = "xadb") private DataSource ds; @@ -70,12 +80,21 @@ public class TomcatXADataSourceTest { } @Test - public void check() throws SQLException { + public void check() throws Exception { assertNotNull(ds); final Connection c = ds.getConnection(); assertNotNull(c); assertThat(c.getMetaData().getConnection(), instanceOf(JDBCXAConnectionWrapper.class)); c.close(); + assertEquals(0, getActiveConnections("xadb")); + } + + + private int getActiveConnections(final String dataSourceName) + throws MalformedObjectNameException, MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { + final ObjectName objectName = new ObjectName("openejb.management:ObjectType=datasources,DataSource=" + dataSourceName); + final Object activeConnectionsAttribute = server.getAttribute(objectName, "Active"); + return (int) (Integer) activeConnectionsAttribute; } }
