Connection leak checking is now implemented.
[utils] / test / enterprise / src / main / java / org / wamblee / support / persistence / DerbyDatabase.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.io.File;
19 import java.io.PrintWriter;
20 import java.sql.Connection;
21 import java.sql.DriverManager;
22 import java.sql.SQLException;
23 import java.util.Properties;
24 import java.util.logging.Level;
25 import java.util.logging.Logger;
26
27 import junit.framework.TestCase;
28
29 import org.apache.derby.drda.NetworkServerControl;
30 import org.wamblee.io.FileSystemUtils;
31
32 /**
33  * Derby database setup. The external JDBC url used to connect to a running
34  * instance is
35  * 
36  * <pre>
37  *     jdbc:derby:net://localhost:1527/testdb
38  * </pre>
39  * 
40  * and the driver class is
41  * 
42  * <pre>
43  * com.ibm.db2.jcc.DB2Driver
44  * </pre>
45  * 
46  * The following jars will have to be used <code>db2jcc.jar</code> and
47  * <code>db2jcc_license_c.jar</code>.
48  */
49 public class DerbyDatabase extends AbstractDatabase {
50
51     /**
52      * Logger.
53      */
54     private static final Logger LOGGER = Logger.getLogger(DerbyDatabase.class
55         .getName());
56
57     /**
58      * Database user name.
59      */
60     private static final String USERNAME = "sa";
61
62     /**
63      * Database password.
64      */
65     private static final String PASSWORD = "123";
66     /**
67      * Poll interval for the checking the server status.
68      */
69     private static final int POLL_INTERVAL = 100;
70
71     /**
72      * Maximum time to wait until the server has started or stopped.
73      */
74     private static final int MAX_WAIT_TIME = 10000;
75
76     /**
77      * Database name to use.
78      */
79     private static final String DATABASE_NAME = "testdb";
80
81     /**
82      * Path on the file system where derby files are stored.
83      */
84     private static final String DATABASE_PATH = "target/db/persistence/derby";
85
86     /**
87      * Derby property required to set the file system path
88      * {@link #DATABASE_PATH}.
89      */
90     private static final String SYSTEM_PATH_PROPERTY = "derby.system.home";
91
92     private boolean inmemory;
93
94     /**
95      * Constructs derby database class to allow creation of derby database
96      * instances.
97      */
98     public DerbyDatabase() {
99         inmemory = true;
100     }
101
102     public DerbyDatabase(boolean aInMemoryFlag) {
103         inmemory = aInMemoryFlag;
104     }
105
106     /*
107      * (non-Javadoc)
108      * 
109      * @see org.wamblee.persistence.Database#start()
110      */
111     public void doStart() {
112         try {
113             System.setProperty("derby.stream.error.file", "target/derby.log");
114             // just in case a previous run was killed without the
115             // cleanup
116             cleanPersistentStorage();
117
118             if (!inmemory) {
119                 // set database path.
120                 Properties lProperties = System.getProperties();
121                 lProperties.put(SYSTEM_PATH_PROPERTY, DATABASE_PATH);
122             }
123
124             Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
125
126             runDatabase();
127
128             waitUntilStartedOrStopped(true);
129
130             // Force creation of the database.
131             Connection lConnection = createConnection();
132             lConnection.close();
133
134             LOGGER.info("Database started: \n    URL = " +
135                 getExternalJdbcUrl() + "\n    user = " + getUsername() +
136                 "\n    password = " + getPassword());
137
138             createDataSource();
139
140             Runtime.getRuntime().addShutdownHook(new Thread() {
141                 @Override
142                 public void run() {
143                     if (isStarted()) {
144                         LOGGER.warning("Shutting down db");
145                         DerbyDatabase.this.stop();
146                     }
147                 }
148             });
149         } catch (Exception e) {
150             throw new RuntimeException("Problem starting database", e);
151         }
152     }
153
154     /**
155      * Waits until the database server has started or stopped.
156      * 
157      * @param aStarted
158      *            If true, waits until the server is up, if false, waits until
159      *            the server is down.
160      * @throws InterruptedException
161      */
162     private void waitUntilStartedOrStopped(boolean aStarted)
163         throws InterruptedException {
164         long lWaited = 0;
165
166         while (aStarted != isStarted()) {
167             Thread.sleep(POLL_INTERVAL);
168             lWaited += POLL_INTERVAL;
169
170             if (lWaited > MAX_WAIT_TIME) {
171                 throw new RuntimeException(
172                     "Derby database did not start within " + MAX_WAIT_TIME +
173                         "ms");
174             }
175         }
176     }
177
178     /**
179      * Checks if the database server has started or not.
180      * 
181      * @return True if started, false otherwise.
182      */
183     private boolean isStarted() {
184         try {
185             getControl().ping();
186
187             return true;
188         } catch (Exception e) {
189             return false;
190         }
191     }
192
193     /**
194      * Gets the controller for the database server.
195      * 
196      * @return Controller.
197      * @throws Exception
198      */
199     private NetworkServerControl getControl() throws Exception {
200         return new NetworkServerControl();
201     }
202
203     /**
204      * Runs the database.
205      * 
206      */
207     private void runDatabase() {
208         try {
209             getControl().start(new PrintWriter(System.out));
210         } catch (Exception e) {
211             throw new RuntimeException(e);
212         }
213     }
214
215     /*
216      * (non-Javadoc)
217      * 
218      * @see org.wamblee.persistence.Database#getJdbcUrl()
219      */
220     public String getJdbcUrl() {
221         return getBaseJdbcUrl() +
222             ";create=true;retrieveMessagesFromServerOnGetMessage=true;";
223     }
224
225     private String getBaseJdbcUrl() {
226         return (inmemory ? "jdbc:derby:memory:" : "jdbc:derby:") +
227             DATABASE_NAME;
228     }
229
230     /*
231      * (non-Javadoc)
232      * 
233      * @see org.wamblee.persistence.Database#getExternalJdbcUrl()
234      */
235     public String getExternalJdbcUrl() {
236         return "jdbc:derby://localhost:1527/" + (inmemory ? "memory:" : "") +
237             DATABASE_NAME;
238     }
239
240     /**
241      * Shuts down the derby database and cleans up all created files.
242      * 
243      */
244     private void shutdownDerby() {
245         try {
246             DriverManager.getConnection("jdbc:derby:;shutdown=true");
247             throw new RuntimeException(
248                 "Derby did not shutdown, should always throw exception at shutdown");
249         } catch (Exception e) {
250             LOGGER.info("Derby has been shut down.");
251         }
252     }
253
254     /**
255      * Gets the user name.
256      */
257     public String getUsername() {
258         return USERNAME;
259     }
260
261     /**
262      * Gets the password.
263      * 
264      * @return
265      */
266     public String getPassword() {
267         return PASSWORD;
268     }
269
270     /*
271      * (non-Javadoc)
272      * 
273      * @see org.wamblee.persistence.Database#createConnection()
274      */
275     public Connection createConnection() throws SQLException {
276         Connection c = DriverManager.getConnection(getJdbcUrl(), getUsername(),
277             getPassword());
278
279         return c;
280     }
281
282     /**
283      * Stops the derby database and cleans up all derby files.
284      */
285     public void doStop() {
286         try {
287             // shutdown network server.
288             getControl().shutdown();
289             waitUntilStartedOrStopped(false);
290
291             // shutdown inmemory access.
292             shutdownDerby();
293             cleanPersistentStorage();
294         } catch (Exception e) {
295             LOGGER.log(Level.WARNING, "Problem stopping database", e);
296         }
297     }
298
299     /**
300      * Cleans up persistent storage of Derby.
301      */
302     private void cleanPersistentStorage() {
303         File lFile = new File(DATABASE_PATH);
304
305         if (lFile.isFile()) {
306             TestCase.fail("A regular file by the name " + DATABASE_PATH +
307                 " exists, clean this up first");
308         }
309
310         if (!lFile.isDirectory()) {
311             return; // no-op already cleanup up.
312         }
313         FileSystemUtils.deleteDirRecursively(DATABASE_PATH);
314     }
315 }