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