From d8b0740d3117a4c2bbde38efd5233a0d828604de Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 4 May 2010 20:37:35 +0000 Subject: [PATCH] Connection leak checking is now implemented. DatabaseUtils now has start() and stop() methods that must be called. Schema generation for eclipse link implemented. Schemas now generated below the target directory. Derby.log now generated below the target directory. Removed clean plugin setting in root pom --- pom.xml | 22 -------- .../eclipselink/EclipselinkJpaCustomizer.java | 11 +++- .../support/persistence/AbstractDatabase.java | 23 ++++++++ .../wamblee/support/persistence/Database.java | 7 +++ .../support/persistence/DatabaseUtils.java | 54 ++++++++++++++++--- .../support/persistence/DerbyDatabase.java | 1 + .../support/persistence/JpaTester.java | 6 ++- .../persistence/DatabaseBuilderTest.java | 9 ++++ .../persistence/DatabaseUtilsTestBase.java | 6 ++- .../persistence/MyEntityExampleTestBase.java | 2 + .../toplink/ToplinkJpaCustomizer.java | 12 +++-- 11 files changed, 118 insertions(+), 35 deletions(-) diff --git a/pom.xml b/pom.xml index a5a9652c..0303d4ab 100644 --- a/pom.xml +++ b/pom.xml @@ -356,28 +356,6 @@ - - maven-clean-plugin - 2.4 - - - - . - - derby.log - createDDL.jdbc - dropDDL.jdbc - - - **/important.log - **/another-important.log - - false - - - - - org.codehaus.mojo jalopy-maven-plugin diff --git a/test/eclipselink/src/main/java/org/wamblee/support/persistence/eclipselink/EclipselinkJpaCustomizer.java b/test/eclipselink/src/main/java/org/wamblee/support/persistence/eclipselink/EclipselinkJpaCustomizer.java index 97df5de9..b03b256d 100644 --- a/test/eclipselink/src/main/java/org/wamblee/support/persistence/eclipselink/EclipselinkJpaCustomizer.java +++ b/test/eclipselink/src/main/java/org/wamblee/support/persistence/eclipselink/EclipselinkJpaCustomizer.java @@ -17,9 +17,11 @@ package org.wamblee.support.persistence.eclipselink; import org.dbunit.dataset.filter.ITableFilterSimple; +import org.wamblee.io.FileSystemUtils; import org.wamblee.support.persistence.JpaCustomizer; import org.wamblee.support.persistence.PersistenceUnitDescription; +import java.io.File; import java.util.Map; /** @@ -42,11 +44,18 @@ public class EclipselinkJpaCustomizer implements JpaCustomizer { aJpaProperties.put("eclipselink.session.customizer", JndiSessionCustomizer.class.getName()); - // DDL generation for toplink + // DDL generation aJpaProperties.put("eclipselink.ddl-generation", "create-tables"); // Use JTA transaction type aJpaProperties.put("javax.persistence.transactionType", "JTA"); + + // DDL generation + FileSystemUtils.createDir(new File("target/sql")); + aJpaProperties.put("eclipselink.application-location", "target/sql"); + aJpaProperties.put("eclipselink.create-ddl-jdbc-file-name", "create-schema.sql"); + aJpaProperties.put("eclipselink.drop-ddl-jdbc-file-name", "drop-schema.sql"); + aJpaProperties.put("eclipselink.ddl-generation.output-mode", "both"); } @Override diff --git a/test/enterprise/src/main/java/org/wamblee/support/persistence/AbstractDatabase.java b/test/enterprise/src/main/java/org/wamblee/support/persistence/AbstractDatabase.java index 2e8f8d83..6eaba50b 100644 --- a/test/enterprise/src/main/java/org/wamblee/support/persistence/AbstractDatabase.java +++ b/test/enterprise/src/main/java/org/wamblee/support/persistence/AbstractDatabase.java @@ -20,6 +20,8 @@ import java.util.logging.Logger; import javax.sql.DataSource; +import junit.framework.Assert; + import org.apache.commons.dbcp.ConnectionFactory; import org.apache.commons.dbcp.DriverManagerConnectionFactory; import org.apache.commons.dbcp.PoolableConnectionFactory; @@ -28,6 +30,12 @@ import org.apache.commons.pool.impl.GenericObjectPool; public abstract class AbstractDatabase implements Database { + /** + * Set this system property to a non-null value to ignore connection leaks + * when {@link #stop()} is called. + */ + private static final String IGNORE_CONNECTION_LEAK_PROPERTY = "org.wamblee.database.ignoreconnectionleaks"; + private static final Logger LOGGER = Logger .getLogger(AbstractDatabase.class.getName()); @@ -64,6 +72,11 @@ public abstract class AbstractDatabase implements Database { itsDataSource = new PoolingDataSource(connectionPool); } + @Override + public int getActiveConnections() { + return connectionPool.getNumActive(); + } + private static void ingoredVariable(PoolableConnectionFactory aFactory) { // Empty } @@ -85,6 +98,16 @@ public abstract class AbstractDatabase implements Database { } started = false; try { + if (connectionPool.getNumActive() > 0) { + String msg = "JDBC connection pool still has " + + connectionPool.getNumActive() + + " active connection(s), this is a potential resource leak in the code\n"; + // backdoor to ignore connection leaks. Use this system property only if you + // can safely ignore the connection leaks. + if (System.getProperty(IGNORE_CONNECTION_LEAK_PROPERTY) == null) { + Assert.fail(msg); + } + } connectionPool.close(); connectionPool.close(); } catch (Exception e) { diff --git a/test/enterprise/src/main/java/org/wamblee/support/persistence/Database.java b/test/enterprise/src/main/java/org/wamblee/support/persistence/Database.java index 987646ec..70ab8c5b 100755 --- a/test/enterprise/src/main/java/org/wamblee/support/persistence/Database.java +++ b/test/enterprise/src/main/java/org/wamblee/support/persistence/Database.java @@ -29,6 +29,13 @@ public interface Database { * database has been started. */ DataSource start(); + + /** + * Gets the number of active connections from the pool. This is useful for + * determining resource leaks. + * @return Active connections. + */ + int getActiveConnections(); /** * Gets the Jdbc Url to connect to this database. diff --git a/test/enterprise/src/main/java/org/wamblee/support/persistence/DatabaseUtils.java b/test/enterprise/src/main/java/org/wamblee/support/persistence/DatabaseUtils.java index d2abd90a..cd7b3e26 100644 --- a/test/enterprise/src/main/java/org/wamblee/support/persistence/DatabaseUtils.java +++ b/test/enterprise/src/main/java/org/wamblee/support/persistence/DatabaseUtils.java @@ -21,6 +21,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; import java.util.logging.Logger; import javax.sql.DataSource; @@ -66,9 +67,47 @@ public class DatabaseUtils { private DataSource dataSource; private ITableFilterSimple tables; + private IDatabaseTester dbtester; + /** + * List of connections that were created for dbtesters. + * This list will be closed in the {@link #stop()} method. + */ + private List connections; + + /** + * Constructs the database utils. + * Before use, {@link #start()} must be called. + * @param aDataSource Datasource. + * @param aTables Tables to work with. + */ public DatabaseUtils(DataSource aDataSource, ITableFilterSimple aTables) { dataSource = aDataSource; tables = aTables; + dbtester = new DataSourceDatabaseTester(dataSource); + connections = new ArrayList(); + } + + /** + * Starts the database utils. + */ + public void start() { + // Empty. No operation currently. + } + + /** + * Stops the database utils, closing any JDBC connections that were created + * by this utility. Note that connections obtained from the datasource directly + * must still be closed by the user. The involved connections are only those that + * are created by this utility. + */ + public void stop() { + for (IDatabaseConnection connection: connections) { + try { + connection.close(); + } catch (SQLException e) { + LOG.log(Level.WARNING, "Could not close connection", e); + } + } } public IDatabaseTester createDbTester() throws Exception { @@ -76,8 +115,9 @@ public class DatabaseUtils { } public IDatabaseTester createDbTester(String[] aTables) throws Exception { - IDatabaseTester dbtester = new DataSourceDatabaseTester(dataSource); - dbtester.setDataSet(dbtester.getConnection().createDataSet(aTables)); + IDatabaseConnection connection = dbtester.getConnection(); + connections.add(connection); + dbtester.setDataSet(connection.createDataSet(aTables)); return dbtester; } @@ -90,7 +130,7 @@ public class DatabaseUtils { final String[] tables = getTableNames(aTables); executeInTransaction(new JdbcUnitOfWork() { public Void execute(Connection aConnection) throws Exception { - for (int i = tables.length-1; i >= 0; i--) { + for (int i = tables.length - 1; i >= 0; i--) { aOperation.execute(tables[i]); } return null; @@ -188,8 +228,8 @@ public class DatabaseUtils { public void emptyTable(String aTable) throws Exception { executeSql("delete from " + aTable); } - - public void dropTables() throws Exception { + + public void dropTables() throws Exception { dropTables(tables); } @@ -207,11 +247,11 @@ public class DatabaseUtils { return dataset.getTableNames(); } }); - for (int i = sortedTables.length-1; i >= 0; i--) { + for (int i = sortedTables.length - 1; i >= 0; i--) { dropTable(sortedTables[i]); } } - + /** * @return * @throws SQLException diff --git a/test/enterprise/src/main/java/org/wamblee/support/persistence/DerbyDatabase.java b/test/enterprise/src/main/java/org/wamblee/support/persistence/DerbyDatabase.java index 33bd4f44..2c33e997 100755 --- a/test/enterprise/src/main/java/org/wamblee/support/persistence/DerbyDatabase.java +++ b/test/enterprise/src/main/java/org/wamblee/support/persistence/DerbyDatabase.java @@ -110,6 +110,7 @@ public class DerbyDatabase extends AbstractDatabase { */ public void doStart() { try { + System.setProperty("derby.stream.error.file", "target/derby.log"); // just in case a previous run was killed without the // cleanup cleanPersistentStorage(); diff --git a/test/enterprise/src/main/java/org/wamblee/support/persistence/JpaTester.java b/test/enterprise/src/main/java/org/wamblee/support/persistence/JpaTester.java index 52e284ee..34a79750 100644 --- a/test/enterprise/src/main/java/org/wamblee/support/persistence/JpaTester.java +++ b/test/enterprise/src/main/java/org/wamblee/support/persistence/JpaTester.java @@ -12,7 +12,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ + */ package org.wamblee.support.persistence; import javax.sql.DataSource; @@ -82,6 +82,7 @@ public class JpaTester { dataSource = db.start(); dbUtils = new DatabaseUtils(dataSource, persistenceUnit.getTables()); + dbUtils.start(); dbUtils.dropTables(); dbUtils.dropTables(JpaCustomizerBuilder.getCustomizer().getJpaTables()); @@ -101,6 +102,9 @@ public class JpaTester { if (jpaBuilder != null) { jpaBuilder.stop(); } + if (dbUtils != null) { + dbUtils.stop(); + } if (db != null) { db.stop(); } diff --git a/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseBuilderTest.java b/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseBuilderTest.java index 044b73a9..e986964e 100644 --- a/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseBuilderTest.java +++ b/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseBuilderTest.java @@ -18,6 +18,7 @@ package org.wamblee.support.persistence; import org.junit.Test; import org.wamblee.support.persistence.DatabaseBuilder; import org.wamblee.support.persistence.DatabaseDescription; +import static junit.framework.TestCase.*; public class DatabaseBuilderTest { @@ -28,4 +29,12 @@ public class DatabaseBuilderTest { System.out.println(description); } } + + @Test + public void testConnectionLeak() { + Database db = DatabaseBuilder.getDatabase(); + db.start(); + assertEquals(0, db.getActiveConnections()); + db.stop(); // will print msg on stdou when there is leak. + } } diff --git a/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseUtilsTestBase.java b/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseUtilsTestBase.java index 9012dbd5..789fabf2 100644 --- a/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseUtilsTestBase.java +++ b/test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseUtilsTestBase.java @@ -54,12 +54,16 @@ public class DatabaseUtilsTestBase { builder = new JpaBuilder(dataSource, persistenceUnit); builder.start(); + assertEquals(0, db.getActiveConnections()); dbtester = dbutils.createDbTester(); } @After - public void tearDown() { + public void tearDown() throws Exception { + dbtester.getConnection().close(); builder.stop(); + dbutils.stop(); + assertEquals(0, db.getActiveConnections()); db.stop(); } diff --git a/test/enterprise/src/test/java/org/wamblee/support/persistence/MyEntityExampleTestBase.java b/test/enterprise/src/test/java/org/wamblee/support/persistence/MyEntityExampleTestBase.java index dcefee59..f6da97e8 100644 --- a/test/enterprise/src/test/java/org/wamblee/support/persistence/MyEntityExampleTestBase.java +++ b/test/enterprise/src/test/java/org/wamblee/support/persistence/MyEntityExampleTestBase.java @@ -63,10 +63,12 @@ public class MyEntityExampleTestBase { builder = jpaTester.getJpaBuilder(); dbtester = jpaTester.getDbTester(); dbutils = jpaTester.getDbUtils(); + dbutils.start(); } @After public void tearDown() { + dbutils.stop(); jpaTester.stop(); } diff --git a/test/toplink-essentials/src/main/java/org/wamblee/support/persistence/toplink/ToplinkJpaCustomizer.java b/test/toplink-essentials/src/main/java/org/wamblee/support/persistence/toplink/ToplinkJpaCustomizer.java index f9be8e47..3a132da2 100644 --- a/test/toplink-essentials/src/main/java/org/wamblee/support/persistence/toplink/ToplinkJpaCustomizer.java +++ b/test/toplink-essentials/src/main/java/org/wamblee/support/persistence/toplink/ToplinkJpaCustomizer.java @@ -15,13 +15,14 @@ */ package org.wamblee.support.persistence.toplink; -import org.dbunit.dataset.filter.ITableFilterSimple; +import java.io.File; +import java.util.Map; +import org.dbunit.dataset.filter.ITableFilterSimple; +import org.wamblee.io.FileSystemUtils; import org.wamblee.support.persistence.JpaCustomizer; import org.wamblee.support.persistence.PersistenceUnitDescription; -import java.util.Map; - /** * * @author $author$ @@ -47,6 +48,11 @@ public class ToplinkJpaCustomizer implements JpaCustomizer { // Use JTA transaction type aJpaProperties.put("javax.persistence.transactionType", "JTA"); + + // DDL generation + FileSystemUtils.createDir(new File("target/sql")); + aJpaProperties.put("toplink.create-ddl-jdbc-file-name", "target/sql/create-schema.sql"); + aJpaProperties.put("toplink.drop-ddl-jdbc-file-name", "target/sql/drop-schema.sql"); } @Override -- 2.31.1