(no commit message)
[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, preventing
33  * duplicate starts of the same database, and checking for connection leaks when
34  * 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. In cast the system
117      * 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
131                 // only if you
132                 // can safely ignore the connection leaks.
133                 if (System.getProperty(IGNORE_CONNECTION_LEAK_PROPERTY) == null) {
134                     Assert.fail(msg);
135                 }
136             }
137             connectionPool.close();
138             connectionPool.close();
139         } catch (Exception e) {
140             LOGGER.log(Level.WARNING, "Could not close pool", e);
141         }
142         doStop();
143     }
144
145     private final DataSource getDatasource() {
146         if (!started) {
147             throw new RuntimeException("Database is not started!");
148         }
149         return itsDataSource;
150     }
151
152     protected String getProperty(String aName) {
153         String value = System.getProperty(aName);
154         if (value != null) {
155             return value;
156         }
157         value = System.getenv(aName);
158         if (value != null) {
159             return value;
160         }
161         throw new RuntimeException("This class expects the '" + aName +
162             "' property to be set");
163     }
164
165 }