2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
17
package org.apache.commons.configuration;
20
import java.util.ArrayList;
21
import java.util.HashSet;
22
import java.util.Iterator;
23
import java.util.List;
24
import java.util.NoSuchElementException;
27
import junit.framework.TestCase;
29
import org.apache.commons.collections.CollectionUtils;
30
import org.apache.commons.configuration.event.ConfigurationEvent;
31
import org.apache.commons.configuration.event.ConfigurationListener;
32
import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
33
import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
34
import org.apache.commons.configuration.tree.ConfigurationNode;
35
import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
36
import org.apache.commons.lang.text.StrLookup;
39
* Test case for SubnodeConfiguration.
41
* @author Oliver Heger
42
* @version $Id: TestSubnodeConfiguration.java 757114 2009-03-22 00:55:25Z joehni $
44
public class TestSubnodeConfiguration extends TestCase
46
/** An array with names of tables (test data). */
47
private static final String[] TABLE_NAMES =
48
{ "documents", "users" };
50
/** An array with the fields of the test tables (test data). */
51
private static final String[][] TABLE_FIELDS =
53
{ "docid", "docname", "author", "dateOfCreation", "version", "size" },
54
{ "userid", "uname", "firstName", "lastName" } };
56
/** Constant for a test output file.*/
57
private static final File TEST_FILE = new File("target/test.xml");
59
/** Constant for an updated table name.*/
60
private static final String NEW_TABLE_NAME = "newTable";
62
/** The parent configuration. */
63
HierarchicalConfiguration parent;
65
/** The subnode configuration to be tested. */
66
SubnodeConfiguration config;
68
/** Stores a counter for the created nodes. */
71
protected void setUp() throws Exception
74
parent = setUpParentConfig();
78
protected void tearDown() throws Exception
80
// remove the test output file if necessary
81
if (TEST_FILE.exists())
83
assertTrue("Could not remove test file", TEST_FILE.delete());
88
* Tests creation of a subnode config.
90
public void testInitSubNodeConfig()
93
assertSame("Wrong root node in subnode", getSubnodeRoot(parent), config
95
assertSame("Wrong parent config", parent, config.getParent());
99
* Tests constructing a subnode configuration with a null parent. This
100
* should cause an exception.
102
public void testInitSubNodeConfigWithNullParent()
106
config = new SubnodeConfiguration(null, getSubnodeRoot(parent));
107
fail("Could set a null parent config!");
109
catch (IllegalArgumentException iex)
116
* Tests constructing a subnode configuration with a null root node. This
117
* should cause an exception.
119
public void testInitSubNodeConfigWithNullNode()
123
config = new SubnodeConfiguration(parent, null);
124
fail("Could set a null root node!");
126
catch (IllegalArgumentException iex)
133
* Tests if properties of the sub node can be accessed.
135
public void testGetProperties()
137
setUpSubnodeConfig();
138
assertEquals("Wrong table name", TABLE_NAMES[0], config
140
List fields = config.getList("fields.field.name");
141
assertEquals("Wrong number of fields", TABLE_FIELDS[0].length, fields
143
for (int i = 0; i < TABLE_FIELDS[0].length; i++)
145
assertEquals("Wrong field at position " + i, TABLE_FIELDS[0][i],
151
* Tests setting of properties in both the parent and the subnode
152
* configuration and whether the changes are visible to each other.
154
public void testSetProperty()
156
setUpSubnodeConfig();
157
config.setProperty(null, "testTable");
158
config.setProperty("name", TABLE_NAMES[0] + "_tested");
159
assertEquals("Root value was not set", "testTable", parent
160
.getString("tables.table(0)"));
161
assertEquals("Table name was not changed", TABLE_NAMES[0] + "_tested",
162
parent.getString("tables.table(0).name"));
164
parent.setProperty("tables.table(0).fields.field(1).name", "testField");
165
assertEquals("Field name was not changed", "testField", config
166
.getString("fields.field(1).name"));
170
* Tests adding of properties.
172
public void testAddProperty()
174
setUpSubnodeConfig();
175
config.addProperty("[@table-type]", "test");
176
assertEquals("parent.createNode() was not called", 1, nodeCounter);
177
assertEquals("Attribute not set", "test", parent
178
.getString("tables.table(0)[@table-type]"));
180
parent.addProperty("tables.table(0).fields.field(-1).name", "newField");
181
List fields = config.getList("fields.field.name");
182
assertEquals("New field was not added", TABLE_FIELDS[0].length + 1,
184
assertEquals("Wrong last field", "newField", fields
185
.get(fields.size() - 1));
189
* Tests listing the defined keys.
191
public void testGetKeys()
193
setUpSubnodeConfig();
194
Set keys = new HashSet();
195
CollectionUtils.addAll(keys, config.getKeys());
196
assertEquals("Incorrect number of keys", 2, keys.size());
197
assertTrue("Key 1 not contained", keys.contains("name"));
198
assertTrue("Key 2 not contained", keys.contains("fields.field.name"));
202
* Tests setting the exception on missing flag. The subnode config obtains
203
* this flag from its parent.
205
public void testSetThrowExceptionOnMissing()
207
parent.setThrowExceptionOnMissing(true);
208
setUpSubnodeConfig();
209
assertTrue("Exception flag not fetchted from parent", config
210
.isThrowExceptionOnMissing());
213
config.getString("non existing key");
214
fail("Could fetch non existing key!");
216
catch (NoSuchElementException nex)
221
config.setThrowExceptionOnMissing(false);
222
assertTrue("Exception flag reset on parent", parent
223
.isThrowExceptionOnMissing());
227
* Tests handling of the delimiter parsing disabled flag. This is shared
228
* with the parent, too.
230
public void testSetDelimiterParsingDisabled()
232
parent.setDelimiterParsingDisabled(true);
233
setUpSubnodeConfig();
234
parent.setDelimiterParsingDisabled(false);
235
assertTrue("Delimiter parsing flag was not received from parent",
236
config.isDelimiterParsingDisabled());
237
config.addProperty("newProp", "test1,test2,test3");
238
assertEquals("New property was splitted", "test1,test2,test3", parent
239
.getString("tables.table(0).newProp"));
240
parent.setDelimiterParsingDisabled(true);
241
config.setDelimiterParsingDisabled(false);
242
assertTrue("Delimiter parsing flag was reset on parent", parent
243
.isDelimiterParsingDisabled());
247
* Tests manipulating the list delimiter. This piece of data is derived from
250
public void testSetListDelimiter()
252
parent.setListDelimiter('/');
253
setUpSubnodeConfig();
254
parent.setListDelimiter(';');
255
assertEquals("List delimiter not obtained from parent", '/', config
256
.getListDelimiter());
257
config.addProperty("newProp", "test1,test2/test3");
258
assertEquals("List was incorrectly splitted", "test1,test2", parent
259
.getString("tables.table(0).newProp"));
260
config.setListDelimiter(',');
261
assertEquals("List delimiter changed on parent", ';', parent
262
.getListDelimiter());
266
* Tests changing the expression engine.
268
public void testSetExpressionEngine()
270
parent.setExpressionEngine(new XPathExpressionEngine());
271
setUpSubnodeConfig();
272
assertEquals("Wrong field name", TABLE_FIELDS[0][1], config
273
.getString("fields/field[2]/name"));
274
Set keys = new HashSet();
275
CollectionUtils.addAll(keys, config.getKeys());
276
assertEquals("Wrong number of keys", 2, keys.size());
277
assertTrue("Key 1 not contained", keys.contains("name"));
278
assertTrue("Key 2 not contained", keys.contains("fields/field/name"));
279
config.setExpressionEngine(null);
280
assertTrue("Expression engine reset on parent", parent
281
.getExpressionEngine() instanceof XPathExpressionEngine);
285
* Tests the configurationAt() method.
287
public void testConfiguarationAt()
289
setUpSubnodeConfig();
290
SubnodeConfiguration sub2 = (SubnodeConfiguration) config
291
.configurationAt("fields.field(1)");
292
assertEquals("Wrong value of property", TABLE_FIELDS[0][1], sub2
294
assertEquals("Wrong parent", config.getParent(), sub2.getParent());
298
* Tests interpolation features. The subnode config should use its parent
301
public void testInterpolation()
303
parent.addProperty("tablespaces.tablespace.name", "default");
304
parent.addProperty("tablespaces.tablespace(-1).name", "test");
305
parent.addProperty("tables.table(0).tablespace",
306
"${tablespaces.tablespace(0).name}");
307
assertEquals("Wrong interpolated tablespace", "default", parent
308
.getString("tables.table(0).tablespace"));
310
setUpSubnodeConfig();
311
assertEquals("Wrong interpolated tablespace in subnode", "default",
312
config.getString("tablespace"));
316
* An additional test for interpolation when the configurationAt() method is
319
public void testInterpolationFromConfigurationAt()
321
parent.addProperty("base.dir", "/home/foo");
322
parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1");
323
parent.addProperty("test.absolute.dir.dir2", "${base.dir}/path2");
324
parent.addProperty("test.absolute.dir.dir3", "${base.dir}/path3");
326
Configuration sub = parent.configurationAt("test.absolute.dir");
327
for (int i = 1; i < 4; i++)
329
assertEquals("Wrong interpolation in parent", "/home/foo/path" + i,
330
parent.getString("test.absolute.dir.dir" + i));
331
assertEquals("Wrong interpolation in subnode",
332
"/home/foo/path" + i, sub.getString("dir" + i));
337
* An additional test for interpolation when the configurationAt() method is
338
* involved for a local interpolation.
340
public void testLocalInterpolationFromConfigurationAt()
342
parent.addProperty("base.dir", "/home/foo");
343
parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1");
344
parent.addProperty("test.absolute.dir.dir2", "${dir1}");
346
Configuration sub = parent.configurationAt("test.absolute.dir");
347
assertEquals("Wrong interpolation in subnode",
348
"/home/foo/path1", sub.getString("dir1"));
349
assertEquals("Wrong local interpolation in subnode",
350
"/home/foo/path1", sub.getString("dir2"));
354
* Tests manipulating the interpolator.
356
public void testInterpolator()
358
parent.addProperty("tablespaces.tablespace.name", "default");
359
parent.addProperty("tablespaces.tablespace(-1).name", "test");
361
setUpSubnodeConfig();
362
InterpolationTestHelper.testGetInterpolator(config);
365
public void testLocalLookupsInInterpolatorAreInherited() {
366
parent.addProperty("tablespaces.tablespace.name", "default");
367
parent.addProperty("tablespaces.tablespace(-1).name", "test");
368
parent.addProperty("tables.table(0).var", "${brackets:x}");
370
ConfigurationInterpolator interpolator = parent.getInterpolator();
371
interpolator.registerLookup("brackets", new StrLookup(){
373
public String lookup(String key) {
374
return "(" + key +")";
378
setUpSubnodeConfig();
379
assertEquals("Local lookup was not inherited", "(x)", config.getString("var", ""));
383
* Tests a reload operation for the parent configuration when the subnode
384
* configuration does not support reloads. Then the new value should not be
387
public void testParentReloadNotSupported() throws ConfigurationException
389
Configuration c = setUpReloadTest(false);
390
assertEquals("Name changed in sub config", TABLE_NAMES[1], config
392
assertEquals("Name not changed in parent", NEW_TABLE_NAME, c
393
.getString("tables.table(1).name"));
397
* Tests a reload operation for the parent configuration when the subnode
398
* configuration does support reloads. The new value should be returned.
400
public void testParentReloadSupported() throws ConfigurationException
402
Configuration c = setUpReloadTest(true);
403
assertEquals("Name not changed in sub config", NEW_TABLE_NAME, config
405
assertEquals("Name not changed in parent", NEW_TABLE_NAME, c
406
.getString("tables.table(1).name"));
410
* Tests whether events are fired if a change of the parent is detected.
412
public void testParentReloadEvents() throws ConfigurationException
414
setUpReloadTest(true);
415
ConfigurationListenerTestImpl l = new ConfigurationListenerTestImpl();
416
config.addConfigurationListener(l);
417
config.getString("name");
418
assertEquals("Wrong number of events", 2, l.events.size());
419
boolean before = true;
420
for (Iterator it = l.events.iterator(); it.hasNext();)
422
ConfigurationEvent e = (ConfigurationEvent) it.next();
423
assertEquals("Wrong configuration", config, e.getSource());
424
assertEquals("Wrong event type",
425
HierarchicalConfiguration.EVENT_SUBNODE_CHANGED, e
427
assertNull("Got a property name", e.getPropertyName());
428
assertNull("Got a property value", e.getPropertyValue());
429
assertEquals("Wrong before flag", before, e.isBeforeUpdate());
435
* Tests a reload operation for the parent configuration when the subnode
436
* configuration is aware of reloads, and the parent configuration is
437
* accessed first. The new value should be returned.
439
public void testParentReloadSupportAccessParent()
440
throws ConfigurationException
442
Configuration c = setUpReloadTest(true);
443
assertEquals("Name not changed in parent", NEW_TABLE_NAME, c
444
.getString("tables.table(1).name"));
445
assertEquals("Name not changed in sub config", NEW_TABLE_NAME, config
450
* Tests whether reloads work with sub subnode configurations.
452
public void testParentReloadSubSubnode() throws ConfigurationException
454
setUpReloadTest(true);
455
SubnodeConfiguration sub = config.configurationAt("fields", true);
456
assertEquals("Wrong subnode key", "tables.table(1).fields", sub
458
assertEquals("Changed field not detected", "newField", sub
459
.getString("field(0).name"));
463
* Tests creating a sub sub config when the sub config is not aware of
464
* changes. Then the sub sub config shouldn't be either.
466
public void testParentReloadSubSubnodeNoChangeSupport()
467
throws ConfigurationException
469
setUpReloadTest(false);
470
SubnodeConfiguration sub = config.configurationAt("fields", true);
471
assertNull("Sub sub config is attached to parent", sub.getSubnodeKey());
472
assertEquals("Changed field name returned", TABLE_FIELDS[1][0], sub
473
.getString("field(0).name"));
477
* Prepares a test for a reload operation.
479
* @param supportReload a flag whether the subnode configuration should
480
* support reload operations
481
* @return the parent configuration that can be used for testing
482
* @throws ConfigurationException if an error occurs
484
private XMLConfiguration setUpReloadTest(boolean supportReload)
485
throws ConfigurationException
487
XMLConfiguration xmlConf = new XMLConfiguration(parent);
488
xmlConf.setFile(TEST_FILE);
490
config = xmlConf.configurationAt("tables.table(1)", supportReload);
491
assertEquals("Wrong table name", TABLE_NAMES[1], config
493
xmlConf.setReloadingStrategy(new FileAlwaysReloadingStrategy());
494
// Now change the configuration file
495
XMLConfiguration confUpdate = new XMLConfiguration(TEST_FILE);
496
confUpdate.setProperty("tables.table(1).name", NEW_TABLE_NAME);
497
confUpdate.setProperty("tables.table(1).fields.field(0).name",
504
* Tests a manipulation of the parent configuration that causes the subnode
505
* configuration to become invalid. In this case the sub config should be
506
* detached and keep its old values.
508
public void testParentChangeDetach()
510
final String key = "tables.table(1)";
511
config = parent.configurationAt(key, true);
512
assertEquals("Wrong subnode key", key, config.getSubnodeKey());
513
assertEquals("Wrong table name", TABLE_NAMES[1], config
515
parent.clearTree(key);
516
assertEquals("Wrong table name after change", TABLE_NAMES[1], config
518
assertNull("Sub config was not detached", config.getSubnodeKey());
522
* Tests detaching a subnode configuration when an exception is thrown
523
* during reconstruction. This can happen e.g. if the expression engine is
524
* changed for the parent.
526
public void testParentChangeDetatchException()
528
config = parent.configurationAt("tables.table(1)", true);
529
parent.setExpressionEngine(new XPathExpressionEngine());
530
assertEquals("Wrong name of table", TABLE_NAMES[1], config
532
assertNull("Sub config was not detached", config.getSubnodeKey());
536
* Initializes the parent configuration. This method creates the typical
537
* structure of tables and fields nodes.
539
* @return the parent configuration
541
protected HierarchicalConfiguration setUpParentConfig()
543
HierarchicalConfiguration conf = new HierarchicalConfiguration()
545
// Provide a special implementation of createNode() to check
546
// if it is called by the subnode config
547
protected Node createNode(String name)
550
return super.createNode(name);
553
for (int i = 0; i < TABLE_NAMES.length; i++)
555
conf.addProperty("tables.table(-1).name", TABLE_NAMES[i]);
556
for (int j = 0; j < TABLE_FIELDS[i].length; j++)
558
conf.addProperty("tables.table.fields.field(-1).name",
566
* Returns the root node for the subnode config. This method returns the
569
* @param conf the parent config
570
* @return the root node for the subnode config
572
protected ConfigurationNode getSubnodeRoot(HierarchicalConfiguration conf)
574
ConfigurationNode root = conf.getRoot();
575
return root.getChild(0).getChild(0);
579
* Performs a standard initialization of the subnode config to test.
581
protected void setUpSubnodeConfig()
583
config = new SubnodeConfiguration(parent, getSubnodeRoot(parent));
587
* A specialized configuration listener for testing whether the expected
590
private static class ConfigurationListenerTestImpl implements ConfigurationListener
592
/** Stores the events received.*/
593
final List events = new ArrayList();
595
public void configurationChanged(ConfigurationEvent event)