1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.configuration;
19
20 import java.io.FileInputStream;
21 import java.sql.Connection;
22 import java.sql.SQLException;
23 import java.util.Iterator;
24 import java.util.List;
25
26 import javax.sql.DataSource;
27
28 import junit.framework.TestCase;
29
30 import org.apache.commons.configuration.event.ConfigurationErrorListener;
31 import org.apache.commons.configuration.test.HsqlDB;
32 import org.apache.commons.dbcp.BasicDataSource;
33 import org.dbunit.database.DatabaseConnection;
34 import org.dbunit.database.IDatabaseConnection;
35 import org.dbunit.dataset.IDataSet;
36 import org.dbunit.dataset.xml.XmlDataSet;
37 import org.dbunit.operation.DatabaseOperation;
38
39 /***
40 * Test for database stored configurations. Note, when running this Unit
41 * Test in Eclipse it sometimes takes a couple tries. Otherwise you may get
42 * database is already in use by another process errors.
43 *
44 * @version $Revision: 558330 $, $Date: 2007-07-21 17:26:19 +0200 (Sa, 21 Jul 2007) $
45 */
46 public class TestDatabaseConfiguration extends TestCase
47 {
48 public final String DATABASE_DRIVER = "org.hsqldb.jdbcDriver";
49 public final String DATABASE_URL = "jdbc:hsqldb:target/test-classes/testdb";
50 public final String DATABASE_USERNAME = "sa";
51 public final String DATABASE_PASSWORD = "";
52
53 /*** Constant for the configuration table.*/
54 private static final String TABLE = "configuration";
55
56 /*** Constant for the multi configuration table.*/
57 private static final String TABLE_MULTI = "configurations";
58
59 /*** Constant for the column with the keys.*/
60 private static final String COL_KEY = "key";
61
62 /*** Constant for the column with the values.*/
63 private static final String COL_VALUE = "value";
64
65 /*** Constant for the column with the configuration name.*/
66 private static final String COL_NAME = "name";
67
68 /*** Constant for the name of the test configuration.*/
69 private static final String CONFIG_NAME = "test";
70
71 private static HsqlDB hsqlDB = null;
72
73 private DataSource datasource;
74
75 /*** An error listener for testing whether internal errors occurred.*/
76 private ConfigurationErrorListenerImpl listener;
77
78 protected void setUp() throws Exception
79 {
80
81
82
83
84
85
86
87
88 if (hsqlDB == null)
89 {
90 hsqlDB = new HsqlDB(DATABASE_URL, DATABASE_DRIVER, "conf/testdb.script");
91 }
92
93 BasicDataSource datasource = new BasicDataSource();
94 datasource.setDriverClassName(DATABASE_DRIVER);
95 datasource.setUrl(DATABASE_URL);
96 datasource.setUsername(DATABASE_USERNAME);
97 datasource.setPassword(DATABASE_PASSWORD);
98
99 this.datasource = datasource;
100
101
102
103 IDatabaseConnection connection = new DatabaseConnection(datasource.getConnection());
104 IDataSet dataSet = new XmlDataSet(new FileInputStream("conf/dataset.xml"));
105
106 try
107 {
108 DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
109 }
110 finally
111 {
112 connection.close();
113 }
114 }
115
116 protected void tearDown() throws Exception{
117 datasource.getConnection().commit();
118 datasource.getConnection().close();
119
120
121 if(listener != null)
122 {
123 assertEquals("An internal error occurred", 0, listener.getErrorCount());
124 }
125 super.tearDown();
126 }
127
128 /***
129 * Creates a database configuration with default values.
130 *
131 * @return the configuration
132 */
133 private PotentialErrorDatabaseConfiguration setUpConfig()
134 {
135 return new PotentialErrorDatabaseConfiguration(datasource, TABLE, COL_KEY, COL_VALUE);
136 }
137
138 /***
139 * Creates a database configuration that supports multiple configurations in
140 * a table with default values.
141 *
142 * @return the configuration
143 */
144 private DatabaseConfiguration setUpMultiConfig()
145 {
146 return new DatabaseConfiguration(datasource, TABLE_MULTI, COL_NAME, COL_KEY, COL_VALUE, CONFIG_NAME);
147 }
148
149 /***
150 * Creates an error listener and adds it to the specified configuration.
151 *
152 * @param config the configuration
153 */
154 private void setUpErrorListener(PotentialErrorDatabaseConfiguration config)
155 {
156
157 config.removeErrorListener((ConfigurationErrorListener) config.getErrorListeners().iterator().next());
158 listener = new ConfigurationErrorListenerImpl();
159 config.addErrorListener(listener);
160 config.failOnConnect = true;
161 }
162
163 /***
164 * Prepares a test for a database error. Sets up a config and registers an
165 * error listener.
166 *
167 * @return the initialized configuration
168 */
169 private PotentialErrorDatabaseConfiguration setUpErrorConfig()
170 {
171 PotentialErrorDatabaseConfiguration config = setUpConfig();
172 setUpErrorListener(config);
173 return config;
174 }
175
176 /***
177 * Checks the error listener for an expected error. The properties of the
178 * error event will be compared with the expected values.
179 *
180 * @param type the expected type of the error event
181 * @param key the expected property key
182 * @param value the expected property value
183 */
184 private void checkErrorListener(int type, String key, Object value)
185 {
186 listener.verify(type, key, value);
187 assertTrue(
188 "Wrong event source",
189 listener.getLastEvent().getSource() instanceof DatabaseConfiguration);
190 assertTrue("Wrong exception",
191 listener.getLastEvent().getCause() instanceof SQLException);
192 listener = null;
193 }
194
195 public void testAddPropertyDirectSingle()
196 {
197 DatabaseConfiguration config = setUpConfig();
198 config.addPropertyDirect("key", "value");
199
200 assertTrue("missing property", config.containsKey("key"));
201 }
202
203 public void testAddPropertyDirectMultiple()
204 {
205 DatabaseConfiguration config = setUpMultiConfig();
206 config.addPropertyDirect("key", "value");
207
208 assertTrue("missing property", config.containsKey("key"));
209 }
210
211 public void testAddNonStringProperty()
212 {
213 DatabaseConfiguration config = setUpConfig();
214 config.addPropertyDirect("boolean", Boolean.TRUE);
215
216 assertTrue("missing property", config.containsKey("boolean"));
217 }
218
219 public void testGetPropertyDirectSingle()
220 {
221 Configuration config = setUpConfig();
222
223 assertEquals("property1", "value1", config.getProperty("key1"));
224 assertEquals("property2", "value2", config.getProperty("key2"));
225 assertEquals("unknown property", null, config.getProperty("key3"));
226 }
227
228 public void testGetPropertyDirectMultiple()
229 {
230 Configuration config = setUpMultiConfig();
231
232 assertEquals("property1", "value1", config.getProperty("key1"));
233 assertEquals("property2", "value2", config.getProperty("key2"));
234 assertEquals("unknown property", null, config.getProperty("key3"));
235 }
236
237 public void testClearPropertySingle()
238 {
239 Configuration config = setUpConfig();
240 config.clearProperty("key");
241
242 assertFalse("property not cleared", config.containsKey("key"));
243 }
244
245 public void testClearPropertyMultiple()
246 {
247 Configuration config = setUpMultiConfig();
248 config.clearProperty("key");
249
250 assertFalse("property not cleared", config.containsKey("key"));
251 }
252
253 public void testClearSingle()
254 {
255 Configuration config = setUpConfig();
256 config.clear();
257
258 assertTrue("configuration is not cleared", config.isEmpty());
259 }
260
261 public void testClearMultiple()
262 {
263 Configuration config = setUpMultiConfig();
264 config.clear();
265
266 assertTrue("configuration is not cleared", config.isEmpty());
267 }
268
269 public void testGetKeysSingle()
270 {
271 Configuration config = setUpConfig();
272 Iterator it = config.getKeys();
273
274 assertEquals("1st key", "key1", it.next());
275 assertEquals("2nd key", "key2", it.next());
276 }
277
278 public void testGetKeysMultiple()
279 {
280 Configuration config = setUpMultiConfig();
281 Iterator it = config.getKeys();
282
283 assertEquals("1st key", "key1", it.next());
284 assertEquals("2nd key", "key2", it.next());
285 }
286
287 public void testContainsKeySingle()
288 {
289 Configuration config = setUpConfig();
290 assertTrue("missing key1", config.containsKey("key1"));
291 assertTrue("missing key2", config.containsKey("key2"));
292 }
293
294 public void testContainsKeyMultiple()
295 {
296 Configuration config = setUpMultiConfig();
297 assertTrue("missing key1", config.containsKey("key1"));
298 assertTrue("missing key2", config.containsKey("key2"));
299 }
300
301 public void testIsEmptySingle()
302 {
303 Configuration config1 = setUpConfig();
304 assertFalse("The configuration is empty", config1.isEmpty());
305 }
306
307 public void testIsEmptyMultiple()
308 {
309 Configuration config1 = setUpMultiConfig();
310 assertFalse("The configuration named 'test' is empty", config1.isEmpty());
311
312 Configuration config2 = new DatabaseConfiguration(datasource, TABLE_MULTI, COL_NAME, COL_KEY, COL_VALUE, "testIsEmpty");
313 assertTrue("The configuration named 'testIsEmpty' is not empty", config2.isEmpty());
314 }
315
316 public void testGetList()
317 {
318 Configuration config1 = new DatabaseConfiguration(datasource, "configurationList", COL_KEY, COL_VALUE);
319 List list = config1.getList("key3");
320 assertEquals(3,list.size());
321 }
322
323 public void testGetKeys()
324 {
325 Configuration config1 = new DatabaseConfiguration(datasource, "configurationList", COL_KEY, COL_VALUE);
326 Iterator i = config1.getKeys();
327 assertTrue(i.hasNext());
328 Object key = i.next();
329 assertEquals("key3",key.toString());
330 assertFalse(i.hasNext());
331 }
332
333 public void testClearSubset()
334 {
335 Configuration config = setUpConfig();
336
337 Configuration subset = config.subset("key1");
338 subset.clear();
339
340 assertTrue("the subset is not empty", subset.isEmpty());
341 assertFalse("the parent configuration is empty", config.isEmpty());
342 }
343
344 /***
345 * Tests whether the configuration has already an error listener registered
346 * that is used for logging.
347 */
348 public void testLogErrorListener()
349 {
350 DatabaseConfiguration config = new DatabaseConfiguration(datasource, TABLE, COL_KEY, COL_VALUE);
351 assertEquals("No error listener registered", 1, config.getErrorListeners().size());
352 }
353
354 /***
355 * Tests handling of errors in getProperty().
356 */
357 public void testGetPropertyError()
358 {
359 setUpErrorConfig().getProperty("key1");
360 checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key1", null);
361 }
362
363 /***
364 * Tests handling of errors in addPropertyDirect().
365 */
366 public void testAddPropertyError()
367 {
368 setUpErrorConfig().addProperty("key1", "value");
369 checkErrorListener(AbstractConfiguration.EVENT_ADD_PROPERTY, "key1", "value");
370 }
371
372 /***
373 * Tests handling of errors in isEmpty().
374 */
375 public void testIsEmptyError()
376 {
377 assertTrue("Wrong return value for failure", setUpErrorConfig().isEmpty());
378 checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null, null);
379 }
380
381 /***
382 * Tests handling of errors in containsKey().
383 */
384 public void testContainsKeyError()
385 {
386 assertFalse("Wrong return value for failure", setUpErrorConfig().containsKey("key1"));
387 checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key1", null);
388 }
389
390 /***
391 * Tests handling of errors in clearProperty().
392 */
393 public void testClearPropertyError()
394 {
395 setUpErrorConfig().clearProperty("key1");
396 checkErrorListener(AbstractConfiguration.EVENT_CLEAR_PROPERTY, "key1", null);
397 }
398
399 /***
400 * Tests handling of errors in clear().
401 */
402 public void testClearError()
403 {
404 setUpErrorConfig().clear();
405 checkErrorListener(AbstractConfiguration.EVENT_CLEAR, null, null);
406 }
407
408 /***
409 * Tests handling of errors in getKeys().
410 */
411 public void testGetKeysError()
412 {
413 Iterator it = setUpErrorConfig().getKeys();
414 checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null, null);
415 assertFalse("Iteration is not empty", it.hasNext());
416 }
417
418 /***
419 * Tests obtaining a property as list whose value contains the list
420 * delimiter. Multiple values should be returned.
421 */
422 public void testGetListWithDelimiter()
423 {
424 DatabaseConfiguration config = setUpConfig();
425 config.setListDelimiter(';');
426 List values = config.getList("keyMulti");
427 assertEquals("Wrong number of list elements", 3, values.size());
428 assertEquals("Wrong list element 0", "a", values.get(0));
429 assertEquals("Wrong list element 2", "c", values.get(2));
430 }
431
432 /***
433 * Tests obtaining a property whose value contains the list delimiter when
434 * delimiter parsing is disabled.
435 */
436 public void testGetListWithDelimiterParsingDisabled()
437 {
438 DatabaseConfiguration config = setUpConfig();
439 config.setListDelimiter(';');
440 config.setDelimiterParsingDisabled(true);
441 assertEquals("Wrong value of property", "a;b;c", config.getString("keyMulti"));
442 }
443
444 /***
445 * Tests adding a property containing the list delimiter. When this property
446 * is queried multiple values should be returned.
447 */
448 public void testAddWithDelimiter()
449 {
450 DatabaseConfiguration config = setUpConfig();
451 config.setListDelimiter(';');
452 config.addProperty("keyList", "1;2;3");
453 String[] values = config.getStringArray("keyList");
454 assertEquals("Wrong number of property values", 3, values.length);
455 assertEquals("Wrong value at index 1", "2", values[1]);
456 }
457
458 /***
459 * A specialized database configuration implementation that can be
460 * configured to throw an exception when obtaining a connection. This way
461 * database exceptions can be simulated.
462 */
463 static class PotentialErrorDatabaseConfiguration extends DatabaseConfiguration
464 {
465 /*** A flag whether a getConnection() call should fail. */
466 boolean failOnConnect;
467
468 public PotentialErrorDatabaseConfiguration(DataSource datasource,
469 String table, String keyColumn, String valueColumn)
470 {
471 super(datasource, table, keyColumn, valueColumn);
472 }
473
474 protected Connection getConnection() throws SQLException
475 {
476 if (failOnConnect)
477 {
478 throw new SQLException("Simulated DB error");
479 }
480 return super.getConnection();
481 }
482 }
483 }