Now setting connection autocommit to false before
[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             // just in case a previous run was killed without the
114             // cleanup
115             cleanPersistentStorage();
116
117             if (!inmemory) {
118                 // set database path.
119                 Properties lProperties = System.getProperties();
120                 lProperties.put(SYSTEM_PATH_PROPERTY, DATABASE_PATH);
121             }
122
123             Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
124
125             runDatabase();
126
127             waitUntilStartedOrStopped(true);
128
129             // Force creation of the database.
130             Connection lConnection = createConnection();
131             lConnection.close();
132
133             LOGGER.info("Database started: \n    URL = " +
134                 getExternalJdbcUrl() + "\n    user = " + getUsername() +
135                 "\n    password = " + getPassword());
136
137             createDataSource();
138
139             Runtime.getRuntime().addShutdownHook(new Thread() {
140                 @Override
141                 public void run() {
142                     if (isStarted()) {
143                         LOGGER.warning("Shutting down db");
144                         DerbyDatabase.this.stop();
145                     }
146                 }
147             });
148         } catch (Exception e) {
149             throw new RuntimeException("Problem starting database", e);
150         }
151     }
152
153     /**
154      * Waits until the database server has started or stopped.
155      * 
156      * @param aStarted
157      *            If true, waits until the server is up, if false, waits until
158      *            the server is down.
159      * @throws InterruptedException
160      */
161     private void waitUntilStartedOrStopped(boolean aStarted)
162         throws InterruptedException {
163         long lWaited = 0;
164
165         while (aStarted != isStarted()) {
166             Thread.sleep(POLL_INTERVAL);
167             lWaited += POLL_INTERVAL;
168
169             if (lWaited > MAX_WAIT_TIME) {
170                 throw new RuntimeException(
171                     "Derby database did not start within " + MAX_WAIT_TIME +
172                         "ms");
173             }
174         }
175     }
176
177     /**
178      * Checks if the database server has started or not.
179      * 
180      * @return True if started, false otherwise.
181      */
182     private boolean isStarted() {
183         try {
184             getControl().ping();
185
186             return true;
187         } catch (Exception e) {
188             return false;
189         }
190     }
191
192     /**
193      * Gets the controller for the database server.
194      * 
195      * @return Controller.
196      * @throws Exception
197      */
198     private NetworkServerControl getControl() throws Exception {
199         return new NetworkServerControl();
200     }
201
202     /**
203      * Runs the database.
204      * 
205      */
206     private void runDatabase() {
207         try {
208             getControl().start(new PrintWriter(System.out));
209         } catch (Exception e) {
210             throw new RuntimeException(e);
211         }
212     }
213
214     /*
215      * (non-Javadoc)
216      * 
217      * @see org.wamblee.persistence.Database#getJdbcUrl()
218      */
219     public String getJdbcUrl() {
220         return getBaseJdbcUrl() +
221             ";create=true;retrieveMessagesFromServerOnGetMessage=true;";
222     }
223
224     private String getBaseJdbcUrl() {
225         return (inmemory ? "jdbc:derby:memory:" : "jdbc:derby:") +
226             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:" : "") +
236             DATABASE_NAME;
237     }
238
239     /**
240      * Shuts down the derby database and cleans up all created files.
241      * 
242      */
243     private void shutdownDerby() {
244         try {
245             DriverManager.getConnection("jdbc:derby:;shutdown=true");
246             throw new RuntimeException(
247                 "Derby did not shutdown, should always throw exception at shutdown");
248         } catch (Exception e) {
249             LOGGER.info("Derby has been shut down.");
250         }
251     }
252
253     /**
254      * Gets the user name.
255      */
256     public String getUsername() {
257         return USERNAME;
258     }
259
260     /**
261      * Gets the password.
262      * 
263      * @return
264      */
265     public String getPassword() {
266         return PASSWORD;
267     }
268
269     /*
270      * (non-Javadoc)
271      * 
272      * @see org.wamblee.persistence.Database#createConnection()
273      */
274     public Connection createConnection() throws SQLException {
275         Connection c = DriverManager.getConnection(getJdbcUrl(), getUsername(),
276             getPassword());
277
278         return c;
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 }