Connection leak checking is now implemented.
authorerik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Tue, 4 May 2010 20:37:35 +0000 (20:37 +0000)
committererik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Tue, 4 May 2010 20:37:35 +0000 (20:37 +0000)
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
test/eclipselink/src/main/java/org/wamblee/support/persistence/eclipselink/EclipselinkJpaCustomizer.java
test/enterprise/src/main/java/org/wamblee/support/persistence/AbstractDatabase.java
test/enterprise/src/main/java/org/wamblee/support/persistence/Database.java
test/enterprise/src/main/java/org/wamblee/support/persistence/DatabaseUtils.java
test/enterprise/src/main/java/org/wamblee/support/persistence/DerbyDatabase.java
test/enterprise/src/main/java/org/wamblee/support/persistence/JpaTester.java
test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseBuilderTest.java
test/enterprise/src/test/java/org/wamblee/support/persistence/DatabaseUtilsTestBase.java
test/enterprise/src/test/java/org/wamblee/support/persistence/MyEntityExampleTestBase.java
test/toplink-essentials/src/main/java/org/wamblee/support/persistence/toplink/ToplinkJpaCustomizer.java

diff --git a/pom.xml b/pom.xml
index a5a9652c9cab86098af72545ecdf0329793f57fa..0303d4ab76eb267271af0383dfb259c42be9ba47 100644 (file)
--- a/pom.xml
+++ b/pom.xml
                 </dependencies>
             </plugin>
 
-            <plugin>
-                <artifactId>maven-clean-plugin</artifactId>
-                <version>2.4</version>
-                <configuration>
-                    <filesets>
-                        <fileset>
-                            <directory>.</directory>
-                            <includes>
-                                <include>derby.log</include>
-                                <include>createDDL.jdbc</include>
-                                <include>dropDDL.jdbc</include>
-                            </includes>
-                            <excludes>
-                                <exclude>**/important.log</exclude>
-                                <exclude>**/another-important.log</exclude>
-                            </excludes>
-                            <followSymlinks>false</followSymlinks>
-                        </fileset>
-                    </filesets>
-                </configuration>
-            </plugin>
-
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>jalopy-maven-plugin</artifactId>
index 97df5de99d879efa241c57317c50d172e9491e41..b03b256df7c795c5a9143720b8dcfcebc244e5fe 100644 (file)
@@ -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
index 2e8f8d8335e0070530bd9c042f8e699181429a97..6eaba50b880f9a218e35ed54a0a3d22aea8168e4 100644 (file)
@@ -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) {
index 987646eccae0c45262ab703240d8cb99e680a551..70ab8c5bb7b7dddb86889c6b5fd08256863f4677 100755 (executable)
@@ -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.
index d2abd90aeccd66eb12331370ed11602e202b9e27..cd7b3e26dd0bd56cc1d943e37ef8262b478ce8ab 100644 (file)
@@ -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<IDatabaseConnection> 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<IDatabaseConnection>(); 
+    }
+
+    /**
+     * 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<Void>() {
             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
index 33bd4f4491c5247186f4eb46ac2b8e5484ece7cf..2c33e997a908f5cac273622aa4fc638c1d89adf3 100755 (executable)
@@ -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();
index 52e284ee7e8ee073b389c892c87c9e9f282bba65..34a79750cd090d4d6858f4c9a91d96669d1a2939 100644 (file)
@@ -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();
         }
index 044b73a989d08b6cfcd093263797f8be9ab6a640..e986964efb779a431519ae109217e3ef241bc9a3 100644 (file)
@@ -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.
+    }
 }
index 9012dbd575c5d0bcb193054ba39eb98e2b982d0c..789fabf25f85dc834a7221990946220c78caa466 100644 (file)
@@ -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();
     }
 
index dcefee59030887e23ff381a104e9f2592caadccf..f6da97e80a108f55089240054a89ede85cdf4f18 100644 (file)
@@ -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();
     }
 
index f9be8e4771939607c26806e1680601e3eadde277..3a132da20c54065934edda98f23e86adb7b87e11 100644 (file)
  */ 
 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