(no commit message)
[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.Assert;
28 import junit.framework.TestCase;
29
30 import org.apache.derby.drda.NetworkServerControl;
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 extends AbstractDatabase {
51
52     /**
53      * Logger.
54      */
55     private static final Logger LOGGER = Logger.getLogger(DerbyDatabase.class
56         .getName());
57
58     /**
59      * Database user name.
60      */
61     private static final String USERNAME = "sa";
62
63     /**
64      * Database password.
65      */
66     private static final String PASSWORD = "123";
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     private boolean inmemory;
94
95     /**
96      * Constructs derby database class to allow creation of derby database
97      * instances.
98      */
99     public DerbyDatabase() {
100         inmemory = true;
101     }
102
103     public DerbyDatabase(boolean aInMemoryFlag) {
104         inmemory = aInMemoryFlag;
105     }
106
107     /*
108      * (non-Javadoc)
109      * 
110      * @see org.wamblee.persistence.Database#start()
111      */
112     public void doStart() {
113         try {
114             System.setProperty("derby.stream.error.file", "target/derby.log");
115             // just in case a previous run was killed without the
116             // cleanup
117             cleanPersistentStorage();
118
119             if (!inmemory) {
120                 // set database path.
121                 Properties lProperties = System.getProperties();
122                 lProperties.put(SYSTEM_PATH_PROPERTY, DATABASE_PATH);
123             }
124
125             Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
126
127             runDatabase();
128
129             waitUntilStartedOrStopped(true);
130
131             // Force creation of the database.
132             Connection lConnection = createConnection();
133             lConnection.close();
134
135             LOGGER.info("Database started: \n    URL = " +
136                 getExternalJdbcUrl() + "\n    user = " + getUsername() +
137                 "\n    password = " + getPassword());
138
139             createDataSource();
140
141             Runtime.getRuntime().addShutdownHook(new Thread() {
142                 @Override
143                 public void run() {
144                     if (isStarted()) {
145                         LOGGER.warning("Shutting down db");
146                         DerbyDatabase.this.stop();
147                     }
148                 }
149             });
150         } catch (Exception e) {
151             throw new RuntimeException("Problem starting database", e);
152         }
153     }
154
155     /**
156      * Waits until the database server has started or stopped.
157      * 
158      * @param aStarted
159      *            If true, waits until the server is up, if false, waits until
160      *            the server is down.
161      * @throws InterruptedException
162      */
163     private void waitUntilStartedOrStopped(boolean aStarted)
164         throws InterruptedException {
165         long lWaited = 0;
166
167         while (aStarted != isStarted()) {
168             Thread.sleep(POLL_INTERVAL);
169             lWaited += POLL_INTERVAL;
170
171             if (lWaited > MAX_WAIT_TIME) {
172                 Assert.fail("Derby database did not start within " +
173                     MAX_WAIT_TIME + "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 }