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
</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>
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;
/**
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
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;
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());
itsDataSource = new PoolingDataSource(connectionPool);
}
+ @Override
+ public int getActiveConnections() {
+ return connectionPool.getNumActive();
+ }
+
private static void ingoredVariable(PoolableConnectionFactory aFactory) {
// Empty
}
}
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) {
* 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.
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;
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 {
}
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;
}
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;
public void emptyTable(String aTable) throws Exception {
executeSql("delete from " + aTable);
}
-
- public void dropTables() throws Exception {
+
+ public void dropTables() throws Exception {
dropTables(tables);
}
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
*/
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();
* 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;
dataSource = db.start();
dbUtils = new DatabaseUtils(dataSource, persistenceUnit.getTables());
+ dbUtils.start();
dbUtils.dropTables();
dbUtils.dropTables(JpaCustomizerBuilder.getCustomizer().getJpaTables());
if (jpaBuilder != null) {
jpaBuilder.stop();
}
+ if (dbUtils != null) {
+ dbUtils.stop();
+ }
if (db != null) {
db.stop();
}
import org.junit.Test;
import org.wamblee.support.persistence.DatabaseBuilder;
import org.wamblee.support.persistence.DatabaseDescription;
+import static junit.framework.TestCase.*;
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.
+ }
}
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();
}
builder = jpaTester.getJpaBuilder();
dbtester = jpaTester.getDbTester();
dbutils = jpaTester.getDbUtils();
+ dbutils.start();
}
@After
public void tearDown() {
+ dbutils.stop();
jpaTester.stop();
}
*/
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$
// 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