You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by km...@apache.org on 2008/01/30 21:01:35 UTC
svn commit: r616880 - in /db/derby/code/branches/10.3/java:
engine/org/apache/derby/jdbc/EmbedPooledConnection.java
testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DataSourceTest.java
testing/org/apache/derbyTesting/junit/JDBCDataSource.java
Author: kmarsden
Date: Wed Jan 30 12:01:32 2008
New Revision: 616880
URL: http://svn.apache.org/viewvc?rev=616880&view=rev
Log:
DERBY-2142 NullPointerException while using XAConnection/PooledConnection in a heavily contended multithreaded scenario
port from trunk revision 616141, 616177, 616575
Modified:
db/derby/code/branches/10.3/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java
db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DataSourceTest.java
db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/junit/JDBCDataSource.java
Modified: db/derby/code/branches/10.3/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java?rev=616880&r1=616879&r2=616880&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java (original)
+++ db/derby/code/branches/10.3/java/engine/org/apache/derby/jdbc/EmbedPooledConnection.java Wed Jan 30 12:01:32 2008
@@ -337,23 +337,8 @@
}
}
- // my conneciton handle is being closed
- public synchronized void notifyClose()
- {
- // tell my listeners I am closed
- if (eventListener != null && eventListener.size() > 0)
- {
- ConnectionEvent closeEvent = new ConnectionEvent(this);
- for (Enumeration e = eventListener.elements();
- e.hasMoreElements(); )
- {
- ConnectionEventListener l =
- (ConnectionEventListener)e.nextElement();
- l.connectionClosed(closeEvent);
- }
- }
- }
+
final void checkActive() throws SQLException {
if (!isActive)
@@ -431,13 +416,37 @@
/**
Close called on BrokeredConnection. If this call
returns true then getRealConnection().close() will be called.
-
+
+
+ Notify listners that connection is closed.
Don't close the underlying real connection as
it is pooled.
*/
- public boolean closingConnection() throws SQLException {
- notifyClose();
+ public synchronized boolean closingConnection() throws SQLException {
+ //DERBY-2142-Null out the connection handle BEFORE notifying listeners.
+ //At time of the callback the PooledConnection must be
+ //disassociated from its previous logical connection.
+ //If not there is a risk that the Pooled
+ //Connection could be returned to the pool, ready for pickup by a
+ //new thread. This new thread then might obtain a java.sql.Connection
+ //whose reference might get assigned to the currentConnectionHandle
+ //field, meanwhile the previous thread completes the close making
+ //the newly assigned currentConnectionHandle null, resulting in an NPE.
currentConnectionHandle = null;
+ // tell my listeners I am closed
+ if (eventListener != null && eventListener.size() > 0)
+ {
+ ConnectionEvent closeEvent = new ConnectionEvent(this);
+
+ for (Enumeration e = eventListener.elements();
+ e.hasMoreElements(); )
+ {
+ ConnectionEventListener l =
+ (ConnectionEventListener)e.nextElement();
+ l.connectionClosed(closeEvent);
+ }
+ }
+
return false;
}
Modified: db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DataSourceTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DataSourceTest.java?rev=616880&r1=616879&r2=616880&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DataSourceTest.java (original)
+++ db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DataSourceTest.java Wed Jan 30 12:01:32 2008
@@ -40,6 +40,9 @@
import java.util.Iterator;
import org.apache.derbyTesting.functionTests.tests.jdbcapi.AssertEventCatcher;
+
+import javax.sql.ConnectionEvent;
+import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
@@ -80,7 +83,7 @@
*/
public class DataSourceTest extends BaseJDBCTestCase {
- protected static String dbName =
+ private static final String dbName =
TestConfiguration.getCurrent().getDefaultDatabaseName();
/**
@@ -610,6 +613,82 @@
}
}
+ /**
+ * Test that a PooledConnection can be reused during the close
+ * event raised by the closing of its logical connection.
+ * DERBY-2142.
+ * @throws SQLException
+ *
+ */
+ public void testPooledReuseOnClose() throws SQLException
+ {
+ // TEMP - seems to fail on network client
+ if (!usingEmbedded())
+ return;
+
+ // PooledConnection from a ConnectionPoolDataSource
+ ConnectionPoolDataSource cpds =
+ J2EEDataSource.getConnectionPoolDataSource();
+ subtestPooledReuseOnClose(cpds.getPooledConnection());
+
+ // PooledConnection from an XDataSource
+ XADataSource xads = J2EEDataSource.getXADataSource();
+ subtestPooledReuseOnClose(xads.getXAConnection());
+ }
+
+ /**
+ * Tests that a pooled connection can successfully be reused
+ * (a new connection obtained from it) during the processing
+ * of its close event by its listener.
+ * Sections 11.2 & 12.5 of JDBC 4 specification indicate that the
+ * connection can be returned to the pool when the
+ * ConnectionEventListener.connectionClosed() is called.
+ */
+ private void subtestPooledReuseOnClose(final PooledConnection pc) throws SQLException
+ {
+ final Connection[] newConn = new Connection[1];
+ pc.addConnectionEventListener(new ConnectionEventListener() {
+
+ /**
+ * Mimic a pool handler that returns the PooledConnection
+ * to the pool and then reallocates it to a new logical connection.
+ */
+ public void connectionClosed(ConnectionEvent event) {
+ PooledConnection pce = (PooledConnection) event.getSource();
+ assertSame(pc, pce);
+ try {
+ // open a new logical connection and pass
+ // back to the fixture.
+ newConn[0] = pce.getConnection();
+ } catch (SQLException e) {
+ // Need to catch the exception here because
+ // we cannot throw an exception through
+ // the api method.
+ fail(e.getMessage());
+ }
+ }
+
+ public void connectionErrorOccurred(ConnectionEvent event) {
+ }
+
+ });
+
+ // Open a connection then close it to trigger the
+ // fetching of a new connection in the callback.
+ Connection c1 = pc.getConnection();
+ c1.close();
+
+ // Fetch the connection created in the close callback
+ Connection c2 = newConn[0];
+ assertNotNull(c2);
+
+ // Ensure the connection is useable, this hit a NPE before DERBY-2142
+ // was fixed (for embedded).
+ c2.createStatement().close();
+
+ pc.close();
+ }
+
public void testAllDataSources() throws SQLException, Exception
{
Connection dmc = getConnection();
@@ -645,7 +724,7 @@
if (usingEmbedded())
assertTenConnectionsUnique();
- DataSource dscs = JDBCDataSource.getDataSource(dbName);
+ DataSource dscs = JDBCDataSource.getDataSource();
if (usingEmbedded())
assertToString(dscs);
@@ -699,7 +778,6 @@
aes1.resetState();
XADataSource dsx = J2EEDataSource.getXADataSource();
- JDBCDataSource.setBeanProperty(dsx, "DatabaseName", dbName);
if (usingEmbedded())
assertToString(dsx);
@@ -925,7 +1003,6 @@
// some of this may be tested elsewhere too.
XADataSource dsx = J2EEDataSource.getXADataSource();
- JDBCDataSource.setBeanProperty(dsx, "DatabaseName", dbName);
XAConnection xac = dsx.getXAConnection();
AssertEventCatcher aes6 = new AssertEventCatcher(6);
xac.addConnectionEventListener(aes6);
@@ -1113,7 +1190,6 @@
// handled correctly
// Some more isolation testing using SQL and JDBC api
XADataSource dsx = J2EEDataSource.getXADataSource();
- JDBCDataSource.setBeanProperty(dsx, "DatabaseName", dbName);
XAConnection xac = dsx.getXAConnection();
AssertEventCatcher aes6 = new AssertEventCatcher(6);
xac.addConnectionEventListener(aes6);
@@ -1266,7 +1342,6 @@
ResultSet.HOLD_CURSORS_OVER_COMMIT};
XADataSource dsx = J2EEDataSource.getXADataSource();
- JDBCDataSource.setBeanProperty(dsx, "DatabaseName", dbName);
XAConnection xac = dsx.getXAConnection();
AssertEventCatcher aes6 = new AssertEventCatcher(6);
xac.addConnectionEventListener(aes6);
@@ -1874,7 +1949,7 @@
// for embedded datasources.
// This subtest does not run for network server, the database shutdown
// is done using setDatabaseShutdown.
- public static void testDSRequestAuthentication() throws SQLException {
+ public void testDSRequestAuthentication() throws SQLException {
if (usingDerbyNetClient())
return;
@@ -2057,55 +2132,53 @@
String traceFile;
// DataSource
- ClientDataSource ds = new ClientDataSource();
- ds.setDatabaseName(dbName);
+ DataSource ds = JDBCDataSource.getDataSource();
// DataSource - setTransationAttributes
traceFile = "trace1.out";
- ds.setConnectionAttributes("traceFile="+traceFile);
+ JDBCDataSource.setBeanProperty(ds, "connectionAttributes",
+ "traceFile="+traceFile);
+
// In this scenario, we *only* get a tracefile, if we first get a
// successful connection, followed by an unsuccessful connection.
// So, we cannot just use ds.getConnection()
dsGetBadConnection(ds);
- ds.setConnectionAttributes(null);
+ JDBCDataSource.clearStringBeanProperty(ds, "connectionAttributes");
+
// DataSource - setTraceFile
traceFile = "trace2.out";
- ds.setTraceFile(traceFile);
+ JDBCDataSource.setBeanProperty(ds, "traceFile", traceFile);
ds.getConnection();
- ds.setTraceFile(null);
- ds.setDatabaseName(null);
+ ds = null;
// now with ConnectionPoolDataSource
- ClientConnectionPoolDataSource cpds = new ClientConnectionPoolDataSource();
- cpds.setDatabaseName(dbName);
+ ConnectionPoolDataSource cpds = J2EEDataSource.getConnectionPoolDataSource();
traceFile = "trace3.out";
- cpds.setConnectionAttributes("traceFile="+traceFile);
+ JDBCDataSource.setBeanProperty(cpds, "connectionAttributes",
+ "traceFile="+traceFile);
// DERBY-2468 - trace3.out does not get created
- cpds.getConnection();
- cpds.setConnectionAttributes(null);
+ ((ClientConnectionPoolDataSource) cpds).getConnection();
+ JDBCDataSource.clearStringBeanProperty(cpds, "connectionAttributes");
traceFile = "trace4.out";
- cpds.setTraceFile(traceFile);
- cpds.getConnection();
- cpds.setTraceFile(null);
- cpds.setDatabaseName(null);
+ JDBCDataSource.setBeanProperty(cpds, "traceFile", traceFile);
+ ((ClientConnectionPoolDataSource) cpds).getConnection();
+ cpds = null;
// now with XADataSource
- ClientXADataSource xads = new ClientXADataSource();
- xads.setDatabaseName(dbName);
+ XADataSource xads = J2EEDataSource.getXADataSource();
traceFile = "trace5.out";
- xads.setConnectionAttributes("traceFile="+traceFile);
- xads.getConnection();
+ JDBCDataSource.setBeanProperty(xads, "connectionAttributes",
+ "traceFile="+traceFile);
+ ((ClientXADataSource) xads).getConnection();
// DERBY-2468 - trace5.out does not get created
- xads.setConnectionAttributes(null);
+ JDBCDataSource.clearStringBeanProperty(xads, "connectionAttributes");
traceFile = "trace6.out";
- xads.setTraceFile(traceFile);
- xads.getConnection();
- xads.setTraceFile(null);
- xads.setDatabaseName(null);
+ JDBCDataSource.setBeanProperty(xads, "traceFile", traceFile);
+ ((ClientXADataSource) xads).getConnection();
assertTraceFilesExist();
}
@@ -2249,44 +2322,36 @@
*
* @throws SQLException
*/
- public void testClientDescriptionConnectionAttribute()
+ public void testDescriptionProperty()
throws SQLException, Exception {
-
- if (usingEmbedded())
- return;
- // DataSource
- String setDescription =
- "Everything you ever wanted to know about this datasource";
- String getDescription;
-
// DataSource - setDescription
- ClientDataSource ds = new ClientDataSource();
- ds.setDatabaseName(dbName);
- ds.setDescription(setDescription);
- ds.getConnection();
- getDescription = ds.getDescription();
- assertEquals(setDescription, getDescription);
- ds.setDescription(null);
+ subTestDataSourceDescription(JDBCDataSource.getDataSource());
// ConnectionPoolDataSource - setDescription
- ClientConnectionPoolDataSource cpds =
- new ClientConnectionPoolDataSource();
- cpds.setDatabaseName(dbName);
- cpds.setDescription(setDescription);
- cpds.getConnection();
- getDescription = cpds.getDescription();
- assertEquals(setDescription, getDescription);
- cpds.setDescription(null);
+ subTestDataSourceDescription(
+ (DataSource) J2EEDataSource.getConnectionPoolDataSource());
// XADataSource - setDescription
- ClientXADataSource xads = new ClientXADataSource();
- xads.setDatabaseName(dbName);
- xads.setDescription(setDescription);
- xads.getConnection();
- getDescription = xads.getDescription();
- assertEquals(setDescription, getDescription);
- xads.setDescription(null);
+ subTestDataSourceDescription(
+ (DataSource) J2EEDataSource.getXADataSource());
+
+ }
+
+ /**
+ * Utility method for testing setting and fetching the description
+ * property on a data source.
+ */
+ private void subTestDataSourceDescription(DataSource ds) throws Exception
+ {
+ String setDescription =
+ "Everything you ever wanted to know about this datasource";
+
+ JDBCDataSource.setBeanProperty(ds, "description", setDescription);
+ ds.getConnection();
+ assertEquals(setDescription, JDBCDataSource.getBeanProperty(ds, "description"));
+ JDBCDataSource.clearStringBeanProperty(ds, "description");
+ assertNull(JDBCDataSource.getBeanProperty(ds, "description"));
}
/* ------------------ JDBC30 (and up) Fixtures ------------------ */
Modified: db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/junit/JDBCDataSource.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/junit/JDBCDataSource.java?rev=616880&r1=616879&r2=616880&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/junit/JDBCDataSource.java (original)
+++ db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/junit/JDBCDataSource.java Wed Jan 30 12:01:32 2008
@@ -231,6 +231,22 @@
}
/**
+ * Get a bean property for a data source. This code can be used
+ * on any data source type.
+ * @param ds DataSource to fetch property from
+ * @param property name of property.
+ */
+ public static Object getBeanProperty(Object ds, String property)
+ throws Exception
+ {
+ String getterName = getGetterName(property);
+
+ Method getter = ds.getClass().getMethod(getterName,
+ new Class[0]);
+ return getter.invoke(ds, new Object[0]);
+ }
+
+ /**
* Clear a String Java bean property by setting it to null.
* @param ds ds DataSource to have property cleared
* @param property name of property.
@@ -249,6 +265,10 @@
private static String getSetterName(String attribute) {
return "set" + Character.toUpperCase(attribute.charAt(0))
+ + attribute.substring(1);
+ }
+ private static String getGetterName(String attribute) {
+ return "get" + Character.toUpperCase(attribute.charAt(0))
+ attribute.substring(1);
}