6eaba50b880f9a218e35ed54a0a3d22aea8168e4
[utils] / test / enterprise / src / main / java / org / wamblee / support / 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.support.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 public abstract class AbstractDatabase implements Database {
32
33     /**
34      * Set this system property to a non-null value to ignore connection leaks
35      * when {@link #stop()} is called.
36      */
37     private static final String IGNORE_CONNECTION_LEAK_PROPERTY = "org.wamblee.database.ignoreconnectionleaks";
38
39     private static final Logger LOGGER = Logger
40         .getLogger(AbstractDatabase.class.getName());
41
42     private static final int CONNECTION_POOL_SIZE = 16;
43
44     private DataSource itsDataSource;
45
46     private GenericObjectPool connectionPool;
47
48     private boolean started;
49
50     protected AbstractDatabase() {
51         started = false;
52     }
53
54     protected abstract void doStart();
55
56     protected abstract void doStop();
57
58     /**
59      * This method must be called from the start method.
60      */
61     protected final void createDataSource() {
62         connectionPool = new GenericObjectPool(null);
63         connectionPool.setMaxActive(CONNECTION_POOL_SIZE);
64         ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
65             getJdbcUrl(), getUsername(), getPassword());
66         // The following line must be kept in although it does not appear to be
67         // used, the constructor regsiters the
68         // constructed object at the connection pool.
69         PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
70             connectionFactory, connectionPool, null, null, false, true);
71         ingoredVariable(poolableConnectionFactory);
72         itsDataSource = new PoolingDataSource(connectionPool);
73     }
74
75     @Override
76     public int getActiveConnections() {
77         return connectionPool.getNumActive();
78     }
79
80     private static void ingoredVariable(PoolableConnectionFactory aFactory) {
81         // Empty
82     }
83
84     // / BELOW THIS LINE IS NOT OF INTEREST TO SUBCLASSES.
85
86     public final DataSource start() {
87         if (started) {
88             throw new RuntimeException("Database already started");
89         }
90         started = true;
91         doStart();
92         return getDatasource();
93     }
94
95     public final void stop() {
96         if (!started) {
97             return; // nothing to do.
98         }
99         started = false;
100         try {
101             if (connectionPool.getNumActive() > 0) {
102                 String msg = "JDBC connection pool still has " +
103                     connectionPool.getNumActive() +
104                     " active connection(s), this is a potential resource leak in the code\n";
105                 // backdoor to ignore connection leaks. Use this system property only if you 
106                 // can safely ignore the connection leaks. 
107                 if (System.getProperty(IGNORE_CONNECTION_LEAK_PROPERTY) == null) {
108                     Assert.fail(msg);
109                 }
110             }
111             connectionPool.close();
112             connectionPool.close();
113         } catch (Exception e) {
114             LOGGER.log(Level.WARNING, "Could not close pool", e);
115         }
116         doStop();
117     }
118
119     private final DataSource getDatasource() {
120         if (!started) {
121             throw new RuntimeException("Database is not started!");
122         }
123         return itsDataSource;
124     }
125
126     protected String getProperty(String aName) {
127         String value = System.getProperty(aName);
128         if (value != null) {
129             return value;
130         }
131         value = System.getenv(aName);
132         if (value != null) {
133             return value;
134         }
135         throw new RuntimeException("This class expects the '" + aName +
136             "' property to be set");
137     }
138
139 }