001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.configuration;
019    
020    import static org.junit.Assert.assertEquals;
021    import static org.junit.Assert.assertFalse;
022    import static org.junit.Assert.assertTrue;
023    
024    import java.sql.Connection;
025    import java.sql.SQLException;
026    import java.util.Iterator;
027    import java.util.List;
028    
029    import javax.sql.DataSource;
030    
031    import org.junit.After;
032    import org.junit.Before;
033    import org.junit.Test;
034    
035    /**
036     * Test for database stored configurations.  Note, when running this Unit
037     * Test in Eclipse it sometimes takes a couple tries. Otherwise you may get
038     * database is already in use by another process errors.
039     *
040     * @version $Id: TestDatabaseConfiguration.java 1223016 2011-12-24 20:56:52Z oheger $
041     */
042    public class TestDatabaseConfiguration
043    {
044        /** Constant for another configuration name. */
045        private static final String CONFIG_NAME2 = "anotherTestConfig";
046    
047        /** An error listener for testing whether internal errors occurred.*/
048        private ConfigurationErrorListenerImpl listener;
049    
050        /** The test helper. */
051        private DatabaseConfigurationTestHelper helper;
052    
053        @Before
054        public void setUp() throws Exception
055        {
056            /*
057             * Thread.sleep may or may not help with the database is already in
058             * use exception.
059             */
060            //Thread.sleep(1000);
061    
062            // set up the datasource
063    
064            helper = new DatabaseConfigurationTestHelper();
065            helper.setUp();
066        }
067    
068        @After
069        public void tearDown() throws Exception
070        {
071            // if an error listener is defined, we check whether an error occurred
072            if(listener != null)
073            {
074                assertEquals("An internal error occurred", 0, listener.getErrorCount());
075            }
076            helper.tearDown();
077        }
078    
079        /**
080         * Creates a database configuration with default values.
081         *
082         * @return the configuration
083         */
084        private PotentialErrorDatabaseConfiguration setUpConfig()
085        {
086            return new PotentialErrorDatabaseConfiguration(helper.getDatasource(),
087                    DatabaseConfigurationTestHelper.TABLE,
088                    DatabaseConfigurationTestHelper.COL_KEY,
089                    DatabaseConfigurationTestHelper.COL_VALUE);
090        }
091    
092        /**
093         * Creates an error listener and adds it to the specified configuration.
094         *
095         * @param config the configuration
096         */
097        private void setUpErrorListener(PotentialErrorDatabaseConfiguration config)
098        {
099            // remove log listener to avoid exception longs
100            config.removeErrorListener(config.getErrorListeners().iterator().next());
101            listener = new ConfigurationErrorListenerImpl();
102            config.addErrorListener(listener);
103            config.failOnConnect = true;
104        }
105    
106        /**
107         * Prepares a test for a database error. Sets up a config and registers an
108         * error listener.
109         *
110         * @return the initialized configuration
111         */
112        private PotentialErrorDatabaseConfiguration setUpErrorConfig()
113        {
114            PotentialErrorDatabaseConfiguration config = setUpConfig();
115            setUpErrorListener(config);
116            return config;
117        }
118    
119        /**
120         * Checks the error listener for an expected error. The properties of the
121         * error event will be compared with the expected values.
122         *
123         * @param type the expected type of the error event
124         * @param key the expected property key
125         * @param value the expected property value
126         */
127        private void checkErrorListener(int type, String key, Object value)
128        {
129            listener.verify(type, key, value);
130            assertTrue(
131                    "Wrong event source",
132                    listener.getLastEvent().getSource() instanceof DatabaseConfiguration);
133            assertTrue("Wrong exception",
134                    listener.getLastEvent().getCause() instanceof SQLException);
135            listener = null; // mark as checked
136        }
137    
138        /**
139         * Tests the default value of the doCommits property.
140         */
141        @Test
142        public void testDoCommitsDefault()
143        {
144            DatabaseConfiguration config = new DatabaseConfiguration(helper
145                    .getDatasource(), DatabaseConfigurationTestHelper.TABLE,
146                    DatabaseConfigurationTestHelper.COL_KEY,
147                    DatabaseConfigurationTestHelper.COL_VALUE);
148            assertFalse("Wrong commits flag", config.isDoCommits());
149        }
150    
151        /**
152         * Tests the default value of the doCommits property for multiple
153         * configurations in a table.
154         */
155        @Test
156        public void testDoCommitsDefaultMulti()
157        {
158            DatabaseConfiguration config = new DatabaseConfiguration(helper
159                    .getDatasource(), DatabaseConfigurationTestHelper.TABLE,
160                    DatabaseConfigurationTestHelper.COL_NAME,
161                    DatabaseConfigurationTestHelper.COL_KEY,
162                    DatabaseConfigurationTestHelper.COL_VALUE,
163                    DatabaseConfigurationTestHelper.CONFIG_NAME);
164            assertFalse("Wrong commits flag", config.isDoCommits());
165        }
166    
167        @Test
168        public void testAddPropertyDirectSingle()
169        {
170            DatabaseConfiguration config = helper.setUpConfig();
171            config.addPropertyDirect("key", "value");
172    
173            assertTrue("missing property", config.containsKey("key"));
174        }
175    
176        /**
177         * Tests whether a commit is performed after a property was added.
178         */
179        @Test
180        public void testAddPropertyDirectCommit()
181        {
182            helper.setAutoCommit(false);
183            DatabaseConfiguration config = helper.setUpConfig();
184            config.addPropertyDirect("key", "value");
185            assertTrue("missing property", config.containsKey("key"));
186        }
187    
188        @Test
189        public void testAddPropertyDirectMultiple()
190        {
191            DatabaseConfiguration config = helper.setUpMultiConfig();
192            config.addPropertyDirect("key", "value");
193    
194            assertTrue("missing property", config.containsKey("key"));
195        }
196    
197        @Test
198        public void testAddNonStringProperty()
199        {
200            DatabaseConfiguration config = helper.setUpConfig();
201            config.addPropertyDirect("boolean", Boolean.TRUE);
202    
203            assertTrue("missing property", config.containsKey("boolean"));
204        }
205    
206        @Test
207        public void testGetPropertyDirectSingle()
208        {
209            Configuration config = setUpConfig();
210    
211            assertEquals("property1", "value1", config.getProperty("key1"));
212            assertEquals("property2", "value2", config.getProperty("key2"));
213            assertEquals("unknown property", null, config.getProperty("key3"));
214        }
215    
216        @Test
217        public void testGetPropertyDirectMultiple()
218        {
219            Configuration config = helper.setUpMultiConfig();
220    
221            assertEquals("property1", "value1", config.getProperty("key1"));
222            assertEquals("property2", "value2", config.getProperty("key2"));
223            assertEquals("unknown property", null, config.getProperty("key3"));
224        }
225    
226        @Test
227        public void testClearPropertySingle()
228        {
229            Configuration config = helper.setUpConfig();
230            config.clearProperty("key1");
231    
232            assertFalse("property not cleared", config.containsKey("key1"));
233        }
234    
235        @Test
236        public void testClearPropertyMultiple()
237        {
238            Configuration config = helper.setUpMultiConfig();
239            config.clearProperty("key1");
240    
241            assertFalse("property not cleared", config.containsKey("key1"));
242        }
243    
244        /**
245         * Tests that another configuration is not affected when clearing
246         * properties.
247         */
248        @Test
249        public void testClearPropertyMultipleOtherConfig()
250        {
251            DatabaseConfiguration config = helper.setUpMultiConfig();
252            DatabaseConfiguration config2 = helper.setUpMultiConfig(CONFIG_NAME2);
253            config2.addProperty("key1", "some test");
254            config.clearProperty("key1");
255            assertFalse("property not cleared", config.containsKey("key1"));
256            assertTrue("Property cleared in other config", config2
257                    .containsKey("key1"));
258        }
259    
260        /**
261         * Tests whether a commit is performed after a property was cleared.
262         */
263        @Test
264        public void testClearPropertyCommit()
265        {
266            helper.setAutoCommit(false);
267            Configuration config = helper.setUpConfig();
268            config.clearProperty("key1");
269            assertFalse("property not cleared", config.containsKey("key1"));
270        }
271    
272        @Test
273        public void testClearSingle()
274        {
275            Configuration config = helper.setUpConfig();
276            config.clear();
277    
278            assertTrue("configuration is not cleared", config.isEmpty());
279        }
280    
281        @Test
282        public void testClearMultiple()
283        {
284            Configuration config = helper.setUpMultiConfig();
285            config.clear();
286    
287            assertTrue("configuration is not cleared", config.isEmpty());
288        }
289    
290        /**
291         * Tests whether a commit is performed after a clear operation.
292         */
293        @Test
294        public void testClearCommit()
295        {
296            helper.setAutoCommit(false);
297            Configuration config = helper.setUpConfig();
298            config.clear();
299            assertTrue("configuration is not cleared", config.isEmpty());
300        }
301    
302        @Test
303        public void testGetKeysSingle()
304        {
305            Configuration config = setUpConfig();
306            Iterator<String> it = config.getKeys();
307    
308            assertEquals("1st key", "key1", it.next());
309            assertEquals("2nd key", "key2", it.next());
310        }
311    
312        @Test
313        public void testGetKeysMultiple()
314        {
315            Configuration config = helper.setUpMultiConfig();
316            Iterator<String> it = config.getKeys();
317    
318            assertEquals("1st key", "key1", it.next());
319            assertEquals("2nd key", "key2", it.next());
320        }
321    
322        @Test
323        public void testContainsKeySingle()
324        {
325            Configuration config = setUpConfig();
326            assertTrue("missing key1", config.containsKey("key1"));
327            assertTrue("missing key2", config.containsKey("key2"));
328        }
329    
330        @Test
331        public void testContainsKeyMultiple()
332        {
333            Configuration config = helper.setUpMultiConfig();
334            assertTrue("missing key1", config.containsKey("key1"));
335            assertTrue("missing key2", config.containsKey("key2"));
336        }
337    
338        @Test
339        public void testIsEmptySingle()
340        {
341            Configuration config1 = setUpConfig();
342            assertFalse("The configuration is empty", config1.isEmpty());
343        }
344    
345        @Test
346        public void testIsEmptyMultiple()
347        {
348            Configuration config1 = helper.setUpMultiConfig();
349            assertFalse("The configuration named 'test' is empty", config1.isEmpty());
350    
351            Configuration config2 = new DatabaseConfiguration(helper.getDatasource(), DatabaseConfigurationTestHelper.TABLE_MULTI, DatabaseConfigurationTestHelper.COL_NAME, DatabaseConfigurationTestHelper.COL_KEY, DatabaseConfigurationTestHelper.COL_VALUE, "testIsEmpty");
352            assertTrue("The configuration named 'testIsEmpty' is not empty", config2.isEmpty());
353        }
354    
355        @Test
356        public void testGetList()
357        {
358            Configuration config1 = new DatabaseConfiguration(helper.getDatasource(), "configurationList", DatabaseConfigurationTestHelper.COL_KEY, DatabaseConfigurationTestHelper.COL_VALUE);
359            List<Object> list = config1.getList("key3");
360            assertEquals(3,list.size());
361        }
362    
363        @Test
364        public void testGetKeys()
365        {
366            Configuration config1 = new DatabaseConfiguration(helper.getDatasource(), "configurationList", DatabaseConfigurationTestHelper.COL_KEY, DatabaseConfigurationTestHelper.COL_VALUE);
367            Iterator<String> i = config1.getKeys();
368            assertTrue(i.hasNext());
369            Object key = i.next();
370            assertEquals("key3",key.toString());
371            assertFalse(i.hasNext());
372        }
373    
374        @Test
375        public void testClearSubset()
376        {
377            Configuration config = setUpConfig();
378    
379            Configuration subset = config.subset("key1");
380            subset.clear();
381    
382            assertTrue("the subset is not empty", subset.isEmpty());
383            assertFalse("the parent configuration is empty", config.isEmpty());
384        }
385    
386        /**
387         * Tests whether the configuration has already an error listener registered
388         * that is used for logging.
389         */
390        @Test
391        public void testLogErrorListener()
392        {
393            DatabaseConfiguration config = new DatabaseConfiguration(helper.getDatasource(), DatabaseConfigurationTestHelper.TABLE, DatabaseConfigurationTestHelper.COL_KEY, DatabaseConfigurationTestHelper.COL_VALUE);
394            assertEquals("No error listener registered", 1, config.getErrorListeners().size());
395        }
396    
397        /**
398         * Tests handling of errors in getProperty().
399         */
400        @Test
401        public void testGetPropertyError()
402        {
403            setUpErrorConfig().getProperty("key1");
404            checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key1", null);
405        }
406    
407        /**
408         * Tests handling of errors in addPropertyDirect().
409         */
410        @Test
411        public void testAddPropertyError()
412        {
413            setUpErrorConfig().addProperty("key1", "value");
414            checkErrorListener(AbstractConfiguration.EVENT_ADD_PROPERTY, "key1", "value");
415        }
416    
417        /**
418         * Tests handling of errors in isEmpty().
419         */
420        @Test
421        public void testIsEmptyError()
422        {
423            assertTrue("Wrong return value for failure", setUpErrorConfig().isEmpty());
424            checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null, null);
425        }
426    
427        /**
428         * Tests handling of errors in containsKey().
429         */
430        @Test
431        public void testContainsKeyError()
432        {
433            assertFalse("Wrong return value for failure", setUpErrorConfig().containsKey("key1"));
434            checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key1", null);
435        }
436    
437        /**
438         * Tests handling of errors in clearProperty().
439         */
440        @Test
441        public void testClearPropertyError()
442        {
443            setUpErrorConfig().clearProperty("key1");
444            checkErrorListener(AbstractConfiguration.EVENT_CLEAR_PROPERTY, "key1", null);
445        }
446    
447        /**
448         * Tests handling of errors in clear().
449         */
450        @Test
451        public void testClearError()
452        {
453            setUpErrorConfig().clear();
454            checkErrorListener(AbstractConfiguration.EVENT_CLEAR, null, null);
455        }
456    
457        /**
458         * Tests handling of errors in getKeys().
459         */
460        @Test
461        public void testGetKeysError()
462        {
463            Iterator<String> it = setUpErrorConfig().getKeys();
464            checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null, null);
465            assertFalse("Iteration is not empty", it.hasNext());
466        }
467    
468        /**
469         * Tests obtaining a property as list whose value contains the list
470         * delimiter. Multiple values should be returned.
471         */
472        @Test
473        public void testGetListWithDelimiter()
474        {
475            DatabaseConfiguration config = setUpConfig();
476            config.setListDelimiter(';');
477            List<Object> values = config.getList("keyMulti");
478            assertEquals("Wrong number of list elements", 3, values.size());
479            assertEquals("Wrong list element 0", "a", values.get(0));
480            assertEquals("Wrong list element 2", "c", values.get(2));
481        }
482    
483        /**
484         * Tests obtaining a property whose value contains the list delimiter when
485         * delimiter parsing is disabled.
486         */
487        @Test
488        public void testGetListWithDelimiterParsingDisabled()
489        {
490            DatabaseConfiguration config = setUpConfig();
491            config.setListDelimiter(';');
492            config.setDelimiterParsingDisabled(true);
493            assertEquals("Wrong value of property", "a;b;c", config.getString("keyMulti"));
494        }
495    
496        /**
497         * Tests adding a property containing the list delimiter. When this property
498         * is queried multiple values should be returned.
499         */
500        @Test
501        public void testAddWithDelimiter()
502        {
503            DatabaseConfiguration config = setUpConfig();
504            config.setListDelimiter(';');
505            config.addProperty("keyList", "1;2;3");
506            String[] values = config.getStringArray("keyList");
507            assertEquals("Wrong number of property values", 3, values.length);
508            assertEquals("Wrong value at index 1", "2", values[1]);
509        }
510    
511        /**
512         * Tests setProperty() if the property value contains the list delimiter.
513         */
514        @Test
515        public void testSetPropertyWithDelimiter()
516        {
517            DatabaseConfiguration config = helper.setUpMultiConfig();
518            config.setListDelimiter(';');
519            config.setProperty("keyList", "1;2;3");
520            String[] values = config.getStringArray("keyList");
521            assertEquals("Wrong number of property values", 3, values.length);
522            assertEquals("Wrong value at index 1", "2", values[1]);
523        }
524    
525        /**
526         * A specialized database configuration implementation that can be
527         * configured to throw an exception when obtaining a connection. This way
528         * database exceptions can be simulated.
529         */
530        static class PotentialErrorDatabaseConfiguration extends DatabaseConfiguration
531        {
532            /** A flag whether a getConnection() call should fail. */
533            boolean failOnConnect;
534    
535            public PotentialErrorDatabaseConfiguration(DataSource datasource,
536                    String table, String keyColumn, String valueColumn)
537            {
538                super(datasource, table, keyColumn, valueColumn);
539            }
540    
541            @Override
542            protected Connection getConnection() throws SQLException
543            {
544                if (failOnConnect)
545                {
546                    throw new SQLException("Simulated DB error");
547                }
548                return super.getConnection();
549            }
550        }
551    }