646931c5fc50cdcba5614a0a21be6a2d65d8fa45
[utils] /
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.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
93         private boolean inmemory;
94                 
95         
96         /**
97          * Constructs derby database class to allow creation of derby database
98          * instances.
99          */
100         public DerbyDatabase() {
101                 inmemory = true;
102         }
103
104         public DerbyDatabase(boolean aInMemoryFlag) {
105                 inmemory = aInMemoryFlag;
106         }
107
108         /*
109          * (non-Javadoc)
110          * 
111          * @see org.wamblee.persistence.Database#start()
112          */
113         public void doStart() {
114                 try {
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 = " + getExternalJdbcUrl() + "\n    user = " + getUsername() + "\n    password = " 
136                                         + 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:") + DATABASE_NAME;
227         }
228
229         /*
230          * (non-Javadoc)
231          * 
232          * @see org.wamblee.persistence.Database#getExternalJdbcUrl()
233          */
234         public String getExternalJdbcUrl() {
235                 return "jdbc:derby://localhost:1527/" + (inmemory ? "memory:" : "") + DATABASE_NAME;
236         }
237
238         /**
239          * Shuts down the derby database and cleans up all created files.
240          * 
241          */
242         private void shutdownDerby() {
243                 try {
244                         DriverManager.getConnection("jdbc:derby:;shutdown=true");
245                         throw new RuntimeException("Derby did not shutdown, "
246                                         + " should always throw exception at shutdown");
247                 } catch (Exception e) {
248                         LOGGER.info("Derby has been shut down.");
249                 }
250         }
251
252         /**
253          * Gets the user name.
254          */
255         public String getUsername() {
256                 return USERNAME;
257         }
258
259         /**
260          * Gets the password.
261          * 
262          * @return
263          */
264         public String getPassword() {
265                 return PASSWORD;
266         }
267
268         /*
269          * (non-Javadoc)
270          * 
271          * @see org.wamblee.persistence.Database#createConnection()
272          */
273         public Connection createConnection() throws SQLException {
274                 Connection c = DriverManager.getConnection(getJdbcUrl(), getUsername(),
275                                 getPassword());
276
277                 return c;
278         }
279
280
281         /**
282          * Stops the derby database and cleans up all derby files.
283          */
284         public void doStop() {
285                 try {
286                         // shutdown network server.
287                         getControl().shutdown();
288                         waitUntilStartedOrStopped(false);
289
290                         // shutdown inmemory access.
291                         shutdownDerby();
292                         cleanPersistentStorage();
293                 } catch (Exception e) {
294                         LOGGER.log(Level.WARNING, "Problem stopping database", e);
295                 }
296         }
297
298         /**
299          * Cleans up persistent storage of Derby.
300          */
301         private void cleanPersistentStorage() {
302                 File lFile = new File(DATABASE_PATH);
303
304                 if (lFile.isFile()) {
305                         TestCase.fail("A regular file by the name " + DATABASE_PATH
306                                         + " exists, clean this up first");
307                 }
308
309                 if (!lFile.isDirectory()) {
310                         return; // no-op already cleanup up.
311                 }
312                 FileSystemUtils.deleteDirRecursively(DATABASE_PATH);
313         }
314 }