package rename for test libraries.
[utils] / test / enterprise / src / main / java / org / wamblee / test / persistence / AbstractDatabase.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.test.persistence;
17
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20
21 import javax.sql.DataSource;
22
23 import junit.framework.Assert;
24
25 import org.apache.commons.dbcp.ConnectionFactory;
26 import org.apache.commons.dbcp.DriverManagerConnectionFactory;
27 import org.apache.commons.dbcp.PoolableConnectionFactory;
28 import org.apache.commons.dbcp.PoolingDataSource;
29 import org.apache.commons.pool.impl.GenericObjectPool;
30
31 /**
32  * Abstract database class providing the creation of the datasource, 
33  * preventing duplicate starts of the same database, and checking
34  * for connection leaks when the database is stopped. 
35  * 
36  * @author Erik Brakkee
37  *
38  */
39 public abstract class AbstractDatabase implements Database {
40
41     /**
42      * Set this system property to a non-null value to ignore connection leaks
43      * when {@link #stop()} is called.
44      */
45     private static final String IGNORE_CONNECTION_LEAK_PROPERTY = "org.wamblee.database.ignoreconnectionleaks";
46
47     private static final Logger LOGGER = Logger
48         .getLogger(AbstractDatabase.class.getName());
49
50     private static final int CONNECTION_POOL_SIZE = 16;
51
52     private DataSource itsDataSource;
53
54     private GenericObjectPool connectionPool;
55
56     private boolean started;
57
58     /**
59      * Constructs the database. 
60      */
61     protected AbstractDatabase() {
62         started = false;
63     }
64
65     /**
66      * To be implemented by subclasses to start the database. 
67      */
68     protected abstract void doStart();
69
70     /**
71      * To be implemented by subclasses to stop the database. 
72      */
73     protected abstract void doStop();
74
75     /**
76      * This method must be called from the start method.
77      */
78     protected final void createDataSource() {
79         connectionPool = new GenericObjectPool(null);
80         connectionPool.setMaxActive(CONNECTION_POOL_SIZE);
81         ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
82             getJdbcUrl(), getUsername(), getPassword());
83         // The following line must be kept in although it does not appear to be
84         // used, the constructor regsiters the
85         // constructed object at the connection pool.
86         PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
87             connectionFactory, connectionPool, null, null, false, true);
88         ingoredVariable(poolableConnectionFactory);
89         itsDataSource = new PoolingDataSource(connectionPool);
90     }
91
92     @Override
93     public int getActiveConnections() {
94         return connectionPool.getNumActive();
95     }
96
97     private static void ingoredVariable(PoolableConnectionFactory aFactory) {
98         // Empty
99     }
100
101     // / BELOW THIS LINE IS NOT OF INTEREST TO SUBCLASSES.
102
103     /**
104      * Starts the database. 
105      */
106     public final DataSource start() {
107         if (started) {
108             throw new RuntimeException("Database already started");
109         }
110         started = true;
111         doStart();
112         return getDatasource();
113     }
114
115     /**
116      * Stops the database and tests for connection leaks. 
117      * In cast the system property with the name given by {@link #IGNORE_CONNECTION_LEAK_PROPERTY}
118      * is set then the connection leaks are  not checked. 
119      */
120     public final void stop() {
121         if (!started) {
122             return; // nothing to do.
123         }
124         started = false;
125         try {
126             if (connectionPool.getNumActive() > 0) {
127                 String msg = "JDBC connection pool still has " +
128                     connectionPool.getNumActive() +
129                     " active connection(s), this is a potential resource leak in the code\n";
130                 // backdoor to ignore connection leaks. Use this system property only if you 
131                 // can safely ignore the connection leaks. 
132                 if (System.getProperty(IGNORE_CONNECTION_LEAK_PROPERTY) == null) {
133                     Assert.fail(msg);
134                 }
135             }
136             connectionPool.close();
137             connectionPool.close();
138         } catch (Exception e) {
139             LOGGER.log(Level.WARNING, "Could not close pool", e);
140         }
141         doStop();
142     }
143
144     private final DataSource getDatasource() {
145         if (!started) {
146             throw new RuntimeException("Database is not started!");
147         }
148         return itsDataSource;
149     }
150
151     protected String getProperty(String aName) {
152         String value = System.getProperty(aName);
153         if (value != null) {
154             return value;
155         }
156         value = System.getenv(aName);
157         if (value != null) {
158             return value;
159         }
160         throw new RuntimeException("This class expects the '" + aName +
161             "' property to be set");
162     }
163
164 }