1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration;
18
19 import java.io.File;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.NoSuchElementException;
23 import java.util.Set;
24
25 import org.apache.commons.collections.CollectionUtils;
26 import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
27 import org.apache.commons.configuration.tree.ConfigurationNode;
28 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
29
30 import junit.framework.TestCase;
31
32 /***
33 * Test case for SubnodeConfiguration.
34 *
35 * @author Oliver Heger
36 * @version $Id: TestSubnodeConfiguration.java 531038 2007-04-21 14:31:58Z oheger $
37 */
38 public class TestSubnodeConfiguration extends TestCase
39 {
40 /*** An array with names of tables (test data). */
41 private static final String[] TABLE_NAMES =
42 { "documents", "users" };
43
44 /*** An array with the fields of the test tables (test data). */
45 private static final String[][] TABLE_FIELDS =
46 {
47 { "docid", "docname", "author", "dateOfCreation", "version", "size" },
48 { "userid", "uname", "firstName", "lastName" } };
49
50 /*** Constant for a test output file.*/
51 private static final File TEST_FILE = new File("target/test.xml");
52
53 /*** Constant for an updated table name.*/
54 private static final String NEW_TABLE_NAME = "newTable";
55
56 /*** The parent configuration. */
57 HierarchicalConfiguration parent;
58
59 /*** The subnode configuration to be tested. */
60 SubnodeConfiguration config;
61
62 /*** Stores the root node of the subnode config. */
63 ConfigurationNode subnode;
64
65 /*** Stores a counter for the created nodes. */
66 int nodeCounter;
67
68 protected void setUp() throws Exception
69 {
70 super.setUp();
71 parent = setUpParentConfig();
72 nodeCounter = 0;
73 }
74
75 protected void tearDown() throws Exception
76 {
77
78 if (TEST_FILE.exists())
79 {
80 TEST_FILE.delete();
81 }
82 }
83
84 /***
85 * Tests creation of a subnode config.
86 */
87 public void testInitSubNodeConfig()
88 {
89 setUpSubnodeConfig();
90 assertSame("Wrong root node in subnode", getSubnodeRoot(parent), config
91 .getRoot());
92 assertSame("Wrong parent config", parent, config.getParent());
93 }
94
95 /***
96 * Tests constructing a subnode configuration with a null parent. This
97 * should cause an exception.
98 */
99 public void testInitSubNodeConfigWithNullParent()
100 {
101 try
102 {
103 config = new SubnodeConfiguration(null, getSubnodeRoot(parent));
104 fail("Could set a null parent config!");
105 }
106 catch (IllegalArgumentException iex)
107 {
108
109 }
110 }
111
112 /***
113 * Tests constructing a subnode configuration with a null root node. This
114 * should cause an exception.
115 */
116 public void testInitSubNodeConfigWithNullNode()
117 {
118 try
119 {
120 config = new SubnodeConfiguration(parent, null);
121 fail("Could set a null root node!");
122 }
123 catch (IllegalArgumentException iex)
124 {
125
126 }
127 }
128
129 /***
130 * Tests if properties of the sub node can be accessed.
131 */
132 public void testGetProperties()
133 {
134 setUpSubnodeConfig();
135 assertEquals("Wrong table name", TABLE_NAMES[0], config
136 .getString("name"));
137 List fields = config.getList("fields.field.name");
138 assertEquals("Wrong number of fields", TABLE_FIELDS[0].length, fields
139 .size());
140 for (int i = 0; i < TABLE_FIELDS[0].length; i++)
141 {
142 assertEquals("Wrong field at position " + i, TABLE_FIELDS[0][i],
143 fields.get(i));
144 }
145 }
146
147 /***
148 * Tests setting of properties in both the parent and the subnode
149 * configuration and whether the changes are visible to each other.
150 */
151 public void testSetProperty()
152 {
153 setUpSubnodeConfig();
154 config.setProperty(null, "testTable");
155 config.setProperty("name", TABLE_NAMES[0] + "_tested");
156 assertEquals("Root value was not set", "testTable", parent
157 .getString("tables.table(0)"));
158 assertEquals("Table name was not changed", TABLE_NAMES[0] + "_tested",
159 parent.getString("tables.table(0).name"));
160
161 parent.setProperty("tables.table(0).fields.field(1).name", "testField");
162 assertEquals("Field name was not changed", "testField", config
163 .getString("fields.field(1).name"));
164 }
165
166 /***
167 * Tests adding of properties.
168 */
169 public void testAddProperty()
170 {
171 setUpSubnodeConfig();
172 config.addProperty("[@table-type]", "test");
173 assertEquals("parent.createNode() was not called", 1, nodeCounter);
174 assertEquals("Attribute not set", "test", parent
175 .getString("tables.table(0)[@table-type]"));
176
177 parent.addProperty("tables.table(0).fields.field(-1).name", "newField");
178 List fields = config.getList("fields.field.name");
179 assertEquals("New field was not added", TABLE_FIELDS[0].length + 1,
180 fields.size());
181 assertEquals("Wrong last field", "newField", fields
182 .get(fields.size() - 1));
183 }
184
185 /***
186 * Tests listing the defined keys.
187 */
188 public void testGetKeys()
189 {
190 setUpSubnodeConfig();
191 Set keys = new HashSet();
192 CollectionUtils.addAll(keys, config.getKeys());
193 assertEquals("Incorrect number of keys", 2, keys.size());
194 assertTrue("Key 1 not contained", keys.contains("name"));
195 assertTrue("Key 2 not contained", keys.contains("fields.field.name"));
196 }
197
198 /***
199 * Tests setting the exception on missing flag. The subnode config obtains
200 * this flag from its parent.
201 */
202 public void testSetThrowExceptionOnMissing()
203 {
204 parent.setThrowExceptionOnMissing(true);
205 setUpSubnodeConfig();
206 assertTrue("Exception flag not fetchted from parent", config
207 .isThrowExceptionOnMissing());
208 try
209 {
210 config.getString("non existing key");
211 fail("Could fetch non existing key!");
212 }
213 catch (NoSuchElementException nex)
214 {
215
216 }
217
218 config.setThrowExceptionOnMissing(false);
219 assertTrue("Exception flag reset on parent", parent
220 .isThrowExceptionOnMissing());
221 }
222
223 /***
224 * Tests handling of the delimiter parsing disabled flag. This is shared
225 * with the parent, too.
226 */
227 public void testSetDelimiterParsingDisabled()
228 {
229 parent.setDelimiterParsingDisabled(true);
230 setUpSubnodeConfig();
231 parent.setDelimiterParsingDisabled(false);
232 assertTrue("Delimiter parsing flag was not received from parent",
233 config.isDelimiterParsingDisabled());
234 config.addProperty("newProp", "test1,test2,test3");
235 assertEquals("New property was splitted", "test1,test2,test3", parent
236 .getString("tables.table(0).newProp"));
237 parent.setDelimiterParsingDisabled(true);
238 config.setDelimiterParsingDisabled(false);
239 assertTrue("Delimiter parsing flag was reset on parent", parent
240 .isDelimiterParsingDisabled());
241 }
242
243 /***
244 * Tests manipulating the list delimiter. This piece of data is derived from
245 * the parent.
246 */
247 public void testSetListDelimiter()
248 {
249 parent.setListDelimiter('/');
250 setUpSubnodeConfig();
251 parent.setListDelimiter(';');
252 assertEquals("List delimiter not obtained from parent", '/', config
253 .getListDelimiter());
254 config.addProperty("newProp", "test1,test2/test3");
255 assertEquals("List was incorrectly splitted", "test1,test2", parent
256 .getString("tables.table(0).newProp"));
257 config.setListDelimiter(',');
258 assertEquals("List delimiter changed on parent", ';', parent
259 .getListDelimiter());
260 }
261
262 /***
263 * Tests changing the expression engine.
264 */
265 public void testSetExpressionEngine()
266 {
267 parent.setExpressionEngine(new XPathExpressionEngine());
268 setUpSubnodeConfig();
269 assertEquals("Wrong field name", TABLE_FIELDS[0][1], config
270 .getString("fields/field[2]/name"));
271 Set keys = new HashSet();
272 CollectionUtils.addAll(keys, config.getKeys());
273 assertEquals("Wrong number of keys", 2, keys.size());
274 assertTrue("Key 1 not contained", keys.contains("name"));
275 assertTrue("Key 2 not contained", keys.contains("fields/field/name"));
276 config.setExpressionEngine(null);
277 assertTrue("Expression engine reset on parent", parent
278 .getExpressionEngine() instanceof XPathExpressionEngine);
279 }
280
281 /***
282 * Tests the configurationAt() method.
283 */
284 public void testConfiguarationAt()
285 {
286 setUpSubnodeConfig();
287 SubnodeConfiguration sub2 = (SubnodeConfiguration) config
288 .configurationAt("fields.field(1)");
289 assertEquals("Wrong value of property", TABLE_FIELDS[0][1], sub2
290 .getString("name"));
291 assertEquals("Wrong parent", config.getParent(), sub2.getParent());
292 }
293
294 /***
295 * Tests interpolation features. The subnode config should use its parent
296 * for interpolation.
297 */
298 public void testInterpolation()
299 {
300 parent.addProperty("tablespaces.tablespace.name", "default");
301 parent.addProperty("tablespaces.tablespace(-1).name", "test");
302 parent.addProperty("tables.table(0).tablespace",
303 "${tablespaces.tablespace(0).name}");
304 assertEquals("Wrong interpolated tablespace", "default", parent
305 .getString("tables.table(0).tablespace"));
306
307 setUpSubnodeConfig();
308 assertEquals("Wrong interpolated tablespace in subnode", "default",
309 config.getString("tablespace"));
310 }
311
312 /***
313 * An additional test for interpolation when the configurationAt() method is
314 * involved.
315 */
316 public void testInterpolationFromConfigurationAt()
317 {
318 parent.addProperty("base.dir", "/home/foo");
319 parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1");
320 parent.addProperty("test.absolute.dir.dir2", "${base.dir}/path2");
321 parent.addProperty("test.absolute.dir.dir3", "${base.dir}/path3");
322
323 Configuration sub = parent.configurationAt("test.absolute.dir");
324 for (int i = 1; i < 4; i++)
325 {
326 assertEquals("Wrong interpolation in parent", "/home/foo/path" + i,
327 parent.getString("test.absolute.dir.dir" + i));
328 assertEquals("Wrong interpolation in subnode",
329 "/home/foo/path" + i, sub.getString("dir" + i));
330 }
331 }
332
333 /***
334 * Tests a reload operation for the parent configuration when the subnode
335 * configuration does not support reloads. Then the new value should not be
336 * detected.
337 */
338 public void testParentReloadNotSupported() throws ConfigurationException
339 {
340 Configuration c = setUpReloadTest(false);
341 assertEquals("Name changed in sub config", TABLE_NAMES[1], config
342 .getString("name"));
343 assertEquals("Name not changed in parent", NEW_TABLE_NAME, c
344 .getString("tables.table(1).name"));
345 }
346
347 /***
348 * Tests a reload operation for the parent configuration when the subnode
349 * configuration does support reloads. The new value should be returned.
350 */
351 public void testParentReloadSupported() throws ConfigurationException
352 {
353 Configuration c = setUpReloadTest(true);
354 assertEquals("Name not changed in sub config", NEW_TABLE_NAME, config
355 .getString("name"));
356 assertEquals("Name not changed in parent", NEW_TABLE_NAME, c
357 .getString("tables.table(1).name"));
358 }
359
360 /***
361 * Tests a reload operation for the parent configuration when the subnode
362 * configuration is aware of reloads, and the parent configuration is
363 * accessed first. The new value should be returned.
364 */
365 public void testParentReloadSupportAccessParent()
366 throws ConfigurationException
367 {
368 Configuration c = setUpReloadTest(true);
369 assertEquals("Name not changed in parent", NEW_TABLE_NAME, c
370 .getString("tables.table(1).name"));
371 assertEquals("Name not changed in sub config", NEW_TABLE_NAME, config
372 .getString("name"));
373 }
374
375 /***
376 * Tests whether reloads work with sub subnode configurations.
377 */
378 public void testParentReloadSubSubnode() throws ConfigurationException
379 {
380 setUpReloadTest(true);
381 SubnodeConfiguration sub = config.configurationAt("fields", true);
382 assertEquals("Wrong subnode key", "tables.table(1).fields", sub
383 .getSubnodeKey());
384 assertEquals("Changed field not detected", "newField", sub
385 .getString("field(0).name"));
386 }
387
388 /***
389 * Tests creating a sub sub config when the sub config is not aware of
390 * changes. Then the sub sub config shouldn't be either.
391 */
392 public void testParentReloadSubSubnodeNoChangeSupport()
393 throws ConfigurationException
394 {
395 setUpReloadTest(false);
396 SubnodeConfiguration sub = config.configurationAt("fields", true);
397 assertNull("Sub sub config is attached to parent", sub.getSubnodeKey());
398 assertEquals("Changed field name returned", TABLE_FIELDS[1][0], sub
399 .getString("field(0).name"));
400 }
401
402 /***
403 * Prepares a test for a reload operation.
404 *
405 * @param supportReload a flag whether the subnode configuration should
406 * support reload operations
407 * @return the parent configuration that can be used for testing
408 * @throws ConfigurationException if an error occurs
409 */
410 private XMLConfiguration setUpReloadTest(boolean supportReload)
411 throws ConfigurationException
412 {
413 XMLConfiguration xmlConf = new XMLConfiguration(parent);
414 xmlConf.setFile(TEST_FILE);
415 xmlConf.save();
416 config = xmlConf.configurationAt("tables.table(1)", supportReload);
417 assertEquals("Wrong table name", TABLE_NAMES[1], config
418 .getString("name"));
419 xmlConf.setReloadingStrategy(new FileAlwaysReloadingStrategy());
420
421 XMLConfiguration confUpdate = new XMLConfiguration(TEST_FILE);
422 confUpdate.setProperty("tables.table(1).name", NEW_TABLE_NAME);
423 confUpdate.setProperty("tables.table(1).fields.field(0).name",
424 "newField");
425 confUpdate.save();
426 return xmlConf;
427 }
428
429 /***
430 * Tests a manipulation of the parent configuration that causes the subnode
431 * configuration to become invalid. In this case the sub config should be
432 * detached and keep its old values.
433 */
434 public void testParentChangeDetach()
435 {
436 final String key = "tables.table(1)";
437 config = parent.configurationAt(key, true);
438 assertEquals("Wrong subnode key", key, config.getSubnodeKey());
439 assertEquals("Wrong table name", TABLE_NAMES[1], config
440 .getString("name"));
441 parent.clearTree(key);
442 assertEquals("Wrong table name after change", TABLE_NAMES[1], config
443 .getString("name"));
444 assertNull("Sub config was not detached", config.getSubnodeKey());
445 }
446
447 /***
448 * Tests detaching a subnode configuration when an exception is thrown
449 * during reconstruction. This can happen e.g. if the expression engine is
450 * changed for the parent.
451 */
452 public void testParentChangeDetatchException()
453 {
454 config = parent.configurationAt("tables.table(1)", true);
455 parent.setExpressionEngine(new XPathExpressionEngine());
456 assertEquals("Wrong name of table", TABLE_NAMES[1], config
457 .getString("name"));
458 assertNull("Sub config was not detached", config.getSubnodeKey());
459 }
460
461 /***
462 * Initializes the parent configuration. This method creates the typical
463 * structure of tables and fields nodes.
464 *
465 * @return the parent configuration
466 */
467 protected HierarchicalConfiguration setUpParentConfig()
468 {
469 HierarchicalConfiguration conf = new HierarchicalConfiguration()
470 {
471
472
473 protected Node createNode(String name)
474 {
475 nodeCounter++;
476 return super.createNode(name);
477 }
478 };
479 for (int i = 0; i < TABLE_NAMES.length; i++)
480 {
481 conf.addProperty("tables.table(-1).name", TABLE_NAMES[i]);
482 for (int j = 0; j < TABLE_FIELDS[i].length; j++)
483 {
484 conf.addProperty("tables.table.fields.field(-1).name",
485 TABLE_FIELDS[i][j]);
486 }
487 }
488 return conf;
489 }
490
491 /***
492 * Returns the root node for the subnode config. This method returns the
493 * first table node.
494 *
495 * @param conf the parent config
496 * @return the root node for the subnode config
497 */
498 protected ConfigurationNode getSubnodeRoot(HierarchicalConfiguration conf)
499 {
500 ConfigurationNode root = conf.getRoot();
501 return root.getChild(0).getChild(0);
502 }
503
504 /***
505 * Performs a standard initialization of the subnode config to test.
506 */
507 protected void setUpSubnodeConfig()
508 {
509 config = new SubnodeConfiguration(parent, getSubnodeRoot(parent));
510 }
511 }