(no commit message)
[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.log4j.Logger;
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 implements Database {
50
51     /**
52      * Logger.
53      */
54     private static final Logger LOGGER = Logger.getLogger(DerbyDatabase.class);
55
56     /**
57      * Database user name.
58      */
59     private static final String USERNAME = "sa";
60
61     /**
62      * Database password.
63      */
64     private static final String PASSWORD = "123";
65
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     /**
93      * Constructs derby database class to allow creation of derby database
94      * instances.
95      */
96     public DerbyDatabase() {
97         // Empty
98     }
99
100     /*
101      * (non-Javadoc)
102      * 
103      * @see org.wamblee.persistence.Database#start()
104      */
105     public void start() {
106         try {
107             // just in case a previous run was killed without the
108             // cleanup
109             cleanPersistentStorage();
110
111             // set database path.
112             Properties lProperties = System.getProperties();
113             lProperties.put(SYSTEM_PATH_PROPERTY, DATABASE_PATH);
114
115             Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
116
117             runDatabase();
118
119             waitUntilStartedOrStopped(true);
120
121             // Force creation of the database.
122             Connection lConnection = createConnection();
123             lConnection.close();
124         } catch (Exception e) {
125             throw new RuntimeException("Problem starting database", e);
126         }
127     }
128
129     /**
130      * Waits until the database server has started or stopped.
131      * 
132      * @param aStarted
133      *            If true, waits until the server is up, if false, waits until
134      *            the server is down.
135      * @throws InterruptedException
136      */
137     private void waitUntilStartedOrStopped(boolean aStarted)
138             throws InterruptedException {
139         long lWaited = 0;
140
141         while (aStarted != isStarted()) {
142             Thread.sleep(POLL_INTERVAL);
143             lWaited += POLL_INTERVAL;
144
145             if (lWaited > MAX_WAIT_TIME) {
146                 throw new RuntimeException(
147                         "Derby database did not start within " + MAX_WAIT_TIME
148                                 + "ms");
149             }
150         }
151     }
152
153     /**
154      * Checks if the database server has started or not.
155      * 
156      * @return True if started, false otherwise.
157      */
158     private boolean isStarted() {
159         try {
160             getControl().ping();
161
162             return true;
163         } catch (Exception e) {
164             return false;
165         }
166     }
167
168     /**
169      * Gets the controller for the database server.
170      * 
171      * @return Controller.
172      * @throws Exception
173      */
174     private NetworkServerControl getControl() throws Exception {
175         return new NetworkServerControl();
176     }
177
178     /**
179      * Runs the database.
180      * 
181      */
182     private void runDatabase() {
183         try {
184             getControl().start(new PrintWriter(System.out));
185         } catch (Exception e) {
186             throw new RuntimeException(e);
187         }
188     }
189
190     /*
191      * (non-Javadoc)
192      * 
193      * @see org.wamblee.persistence.Database#getJdbcUrl()
194      */
195     public String getJdbcUrl() {
196         return getBaseJdbcUrl()
197                 + ";create=true;retrieveMessagesFromServerOnGetMessage=true;";
198     }
199
200     private String getBaseJdbcUrl() {
201         return "jdbc:derby:" + DATABASE_NAME;
202     }
203
204     /*
205      * (non-Javadoc)
206      * 
207      * @see org.wamblee.persistence.Database#getExternalJdbcUrl()
208      */
209     public String getExternalJdbcUrl() {
210         return "jdbc:derby:net://localhost:1527/" + DATABASE_NAME;
211     }
212
213     /**
214      * Shuts down the derby database and cleans up all created files.
215      * 
216      */
217     private void shutdownDerby() {
218         try {
219             DriverManager.getConnection("jdbc:derby:;shutdown=true");
220             throw new RuntimeException("Derby did not shutdown, "
221                     + " should always throw exception at shutdown");
222         } catch (Exception e) {
223             LOGGER.info("Derby has been shut down.");
224         }
225     }
226
227     /*
228      * (non-Javadoc)
229      * 
230      * @see org.wamblee.persistence.Database#getDriverClassName()
231      */
232     public String getDriverClassName() {
233         return ClientDriver.class.getName();
234     }
235
236     /**
237      * Gets the user name.
238      */
239     public String getUsername() {
240         return USERNAME;
241     }
242
243     /**
244      * Gets the password.
245      * 
246      * @return
247      */
248     public String getPassword() {
249         return PASSWORD;
250     }
251
252     /*
253      * (non-Javadoc)
254      * 
255      * @see org.wamblee.persistence.Database#createConnection()
256      */
257     public Connection createConnection() throws SQLException {
258         Connection c = DriverManager.getConnection(getJdbcUrl(), getUsername(),
259                 getPassword());
260
261         return c;
262     }
263
264     /**
265      * Stops the derby database and cleans up all derby files.
266      */
267     public void stop() {
268         try {
269             // shutdown network server.
270             getControl().shutdown();
271             waitUntilStartedOrStopped(false);
272
273             // shutdown inmemory access.
274             shutdownDerby();
275             cleanPersistentStorage();
276         } catch (Exception e) {
277             LOGGER.warn("Problem stopping database", e);
278         }
279     }
280
281     /**
282      * Cleans up persistent storage of Derby.
283      */
284     private void cleanPersistentStorage() {
285         File lFile = new File(DATABASE_PATH);
286
287         if (lFile.isFile()) {
288             TestCase.fail("A regular file by the name " + DATABASE_PATH
289                     + " exists, clean this up first");
290         }
291
292         if (!lFile.isDirectory()) {
293             return; // no-op already cleanup up.
294         }
295
296         FileSystemUtils.deleteDirRecursively(DATABASE_PATH);
297     }
298 }