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.ByteArrayInputStream;
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.io.FileWriter;
23 import java.io.IOException;
24 import java.io.PrintWriter;
25 import java.io.StringReader;
26 import java.io.StringWriter;
27 import java.net.URL;
28 import java.util.Iterator;
29 import java.util.List;
30
31 import javax.xml.parsers.DocumentBuilder;
32 import javax.xml.parsers.DocumentBuilderFactory;
33
34 import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
35 import org.apache.commons.configuration.reloading.InvariantReloadingStrategy;
36 import org.xml.sax.SAXException;
37 import org.xml.sax.SAXParseException;
38 import org.xml.sax.helpers.DefaultHandler;
39
40 import junit.framework.TestCase;
41
42 /***
43 * test for loading and saving xml properties files
44 *
45 * @version $Id: TestXMLConfiguration.java 330630 2005-11-03 20:53:13Z oheger $
46 */
47 public class TestXMLConfiguration extends TestCase
48 {
49 /*** Constant for the used encoding.*/
50 static final String ENCODING = "ISO-8859-1";
51
52 /*** The File that we test with */
53 private String testProperties = new File("conf/test.xml").getAbsolutePath();
54 private String testProperties2 = new File("conf/testDigesterConfigurationInclude1.xml").getAbsolutePath();
55 private String testBasePath = new File("conf").getAbsolutePath();
56 private File testSaveConf = new File("target/testsave.xml");
57
58 private XMLConfiguration conf;
59
60 protected void setUp() throws Exception
61 {
62 conf = new XMLConfiguration();
63 conf.setFile(new File(testProperties));
64 conf.load();
65 removeTestFile();
66 }
67
68 public void testGetProperty()
69 {
70 assertEquals("value", conf.getProperty("element"));
71 }
72
73 public void testGetCommentedProperty()
74 {
75 assertEquals("", conf.getProperty("test.comment"));
76 }
77
78 public void testGetPropertyWithXMLEntity()
79 {
80 assertEquals("1<2", conf.getProperty("test.entity"));
81 }
82
83 public void testClearProperty() throws ConfigurationException, IOException
84 {
85
86 String key = "clearly";
87 conf.clearProperty(key);
88 assertNull(key, conf.getProperty(key));
89 assertNull(key, conf.getProperty(key));
90
91
92 conf.load();
93 key = "clear.element";
94 conf.clearProperty(key);
95 assertNull(key, conf.getProperty(key));
96 assertNull(key, conf.getProperty(key));
97
98
99 conf.load();
100 key = "clear.element2";
101 conf.clearProperty(key);
102 assertNull(key, conf.getProperty(key));
103 assertNull(key, conf.getProperty(key));
104 key = "clear.element2[@id]";
105 assertNotNull(key, conf.getProperty(key));
106 assertNotNull(key, conf.getProperty(key));
107
108
109 conf.load();
110 key = "clear.comment";
111 conf.clearProperty(key);
112 assertNull(key, conf.getProperty(key));
113 assertNull(key, conf.getProperty(key));
114
115
116 conf.load();
117 key = "clear.cdata";
118 conf.clearProperty(key);
119 assertNull(key, conf.getProperty(key));
120 assertNull(key, conf.getProperty(key));
121
122
123 conf.load();
124 key = "clear.list.item";
125 conf.clearProperty(key);
126 assertNull(key, conf.getProperty(key));
127 assertNull(key, conf.getProperty(key));
128 key = "clear.list.item[@id]";
129 assertNotNull(key, conf.getProperty(key));
130 assertNotNull(key, conf.getProperty(key));
131
132
133 conf.load();
134 key = "list.item";
135 conf.clearProperty(key);
136 assertNull(key, conf.getProperty(key));
137 assertNull(key, conf.getProperty(key));
138 }
139
140 public void testgetProperty() {
141
142 Object property = conf.getProperty("clear");
143 assertNull(property);
144
145
146 property = conf.getProperty("e");
147 assertNull(property);
148
149
150 property = conf.getProperty("element3[@n]");
151 assertNull(property);
152
153
154 property = conf.getProperty("element");
155 assertNotNull(property);
156 assertTrue(property instanceof String);
157 assertEquals("value", property);
158
159
160 property = conf.getProperty("element3[@name]");
161 assertNotNull(property);
162 assertTrue(property instanceof String);
163 assertEquals("foo", property);
164
165
166 property = conf.getProperty("test.comment");
167 assertEquals("", property);
168
169
170 property = conf.getProperty("test.cdata");
171 assertNotNull(property);
172 assertTrue(property instanceof String);
173 assertEquals("<cdata value>", property);
174
175
176 property = conf.getProperty("list.sublist.item");
177 assertNotNull(property);
178 assertTrue(property instanceof List);
179 List list = (List)property;
180 assertEquals(2, list.size());
181 assertEquals("five", list.get(0));
182 assertEquals("six", list.get(1));
183
184
185 property = conf.getProperty("list.item");
186 assertNotNull(property);
187 assertTrue(property instanceof List);
188 list = (List)property;
189 assertEquals(4, list.size());
190 assertEquals("one", list.get(0));
191 assertEquals("two", list.get(1));
192 assertEquals("three", list.get(2));
193 assertEquals("four", list.get(3));
194
195
196 property = conf.getProperty("list.item[@name]");
197 assertNotNull(property);
198 assertTrue(property instanceof List);
199 list = (List)property;
200 assertEquals(2, list.size());
201 assertEquals("one", list.get(0));
202 assertEquals("three", list.get(1));
203 }
204
205 public void testGetAttribute()
206 {
207 assertEquals("element3[@name]", "foo", conf.getProperty("element3[@name]"));
208 }
209
210 public void testClearAttribute() throws Exception
211 {
212
213 String key = "clear[@id]";
214 conf.clearProperty(key);
215 assertNull(key, conf.getProperty(key));
216 assertNull(key, conf.getProperty(key));
217
218
219 conf.load();
220 key = "clear.element2[@id]";
221 conf.clearProperty(key);
222 assertNull(key, conf.getProperty(key));
223 assertNull(key, conf.getProperty(key));
224 key = "clear.element2";
225 assertNotNull(key, conf.getProperty(key));
226 assertNotNull(key, conf.getProperty(key));
227
228
229 conf.load();
230 key = "clear.list.item[@id]";
231 conf.clearProperty(key);
232 assertNull(key, conf.getProperty(key));
233 assertNull(key, conf.getProperty(key));
234 key = "clear.list.item";
235 assertNotNull(key, conf.getProperty(key));
236 assertNotNull(key, conf.getProperty(key));
237 }
238
239 public void testSetAttribute()
240 {
241
242 conf.setProperty("element3[@name]", "bar");
243 assertEquals("element3[@name]", "bar", conf.getProperty("element3[@name]"));
244
245
246 conf.setProperty("foo[@bar]", "value");
247 assertEquals("foo[@bar]", "value", conf.getProperty("foo[@bar]"));
248
249 conf.setProperty("name1","value1");
250 assertEquals("value1",conf.getProperty("name1"));
251 }
252
253 public void testAddAttribute()
254 {
255 conf.addProperty("element3[@name]", "bar");
256
257 List list = conf.getList("element3[@name]");
258 assertNotNull("null list", list);
259 assertTrue("'foo' element missing", list.contains("foo"));
260 assertTrue("'bar' element missing", list.contains("bar"));
261 assertEquals("list size", 2, list.size());
262 }
263
264 public void testAddObjectAttribute()
265 {
266 conf.addProperty("test.boolean[@value]", Boolean.TRUE);
267 assertTrue("test.boolean[@value]", conf.getBoolean("test.boolean[@value]"));
268 }
269
270 public void testAddList()
271 {
272 conf.addProperty("test.array", "value1");
273 conf.addProperty("test.array", "value2");
274
275 List list = conf.getList("test.array");
276 assertNotNull("null list", list);
277 assertTrue("'value1' element missing", list.contains("value1"));
278 assertTrue("'value2' element missing", list.contains("value2"));
279 assertEquals("list size", 2, list.size());
280 }
281
282 public void testGetComplexProperty()
283 {
284 assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
285 }
286
287 public void testSettingFileNames()
288 {
289 conf = new XMLConfiguration();
290 conf.setFileName(testProperties);
291 assertEquals(testProperties.toString(), conf.getFileName());
292
293 conf.setBasePath(testBasePath);
294 conf.setFileName("hello.xml");
295 assertEquals("hello.xml", conf.getFileName());
296 assertEquals(testBasePath.toString(), conf.getBasePath());
297 assertEquals(new File(testBasePath, "hello.xml"), conf.getFile());
298
299 conf.setBasePath(testBasePath);
300 conf.setFileName("subdir/hello.xml");
301 assertEquals("subdir/hello.xml", conf.getFileName());
302 assertEquals(testBasePath.toString(), conf.getBasePath());
303 assertEquals(new File(testBasePath, "subdir/hello.xml"), conf.getFile());
304 }
305
306 public void testLoad() throws Exception
307 {
308 conf = new XMLConfiguration();
309 conf.setFileName(testProperties);
310 conf.load();
311
312 assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
313 }
314
315 public void testLoadWithBasePath() throws Exception
316 {
317 conf = new XMLConfiguration();
318
319 conf.setFileName("test.xml");
320 conf.setBasePath(testBasePath);
321 conf.load();
322
323 assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
324 }
325
326 /***
327 * Tests constructing an XMLConfiguration from a non existing file and
328 * later saving to this file.
329 */
330 public void testLoadAndSaveFromFile() throws Exception
331 {
332
333 conf = new XMLConfiguration(testSaveConf);
334 assertTrue(conf.isEmpty());
335 conf.addProperty("test", "yes");
336 conf.save();
337
338 conf = new XMLConfiguration(testSaveConf);
339 assertEquals("yes", conf.getString("test"));
340 }
341
342 /***
343 * Tests loading a configuration from a URL.
344 */
345 public void testLoadFromURL() throws Exception
346 {
347 URL url = new File(testProperties).toURL();
348 conf = new XMLConfiguration(url);
349 assertEquals("value", conf.getProperty("element"));
350 assertEquals(url, conf.getURL());
351 }
352
353 /***
354 * Tests loading from a stream.
355 */
356 public void testLoadFromStream() throws Exception
357 {
358 String xml = "<?xml version=\"1.0\"?><config><test>1</test></config>";
359 conf = new XMLConfiguration();
360 conf.load(new ByteArrayInputStream(xml.getBytes()));
361 assertEquals(1, conf.getInt("test"));
362
363 conf = new XMLConfiguration();
364 conf.load(new ByteArrayInputStream(xml.getBytes()), "UTF8");
365 assertEquals(1, conf.getInt("test"));
366 }
367
368 /***
369 * Tests loading a non well formed XML from a string.
370 */
371 public void testLoadInvalidXML() throws Exception
372 {
373 String xml = "<?xml version=\"1.0\"?><config><test>1</rest></config>";
374 conf = new XMLConfiguration();
375 try
376 {
377 conf.load(new StringReader(xml));
378 fail("Could load invalid XML!");
379 }
380 catch(ConfigurationException cex)
381 {
382
383 }
384 }
385
386 public void testSetProperty() throws Exception
387 {
388 conf.setProperty("element.string", "hello");
389
390 assertEquals("'element.string'", "hello", conf.getString("element.string"));
391 assertEquals("XML value of element.string", "hello", conf.getProperty("element.string"));
392 }
393
394 public void testAddProperty()
395 {
396
397 XMLConfiguration config = new XMLConfiguration();
398 config.addProperty("test.string", "hello");
399
400 assertEquals("'test.string'", "hello", config.getString("test.string"));
401 }
402
403 public void testAddObjectProperty()
404 {
405
406 conf.addProperty("test.boolean", Boolean.TRUE);
407 assertTrue("'test.boolean'", conf.getBoolean("test.boolean"));
408 }
409
410 public void testSave() throws Exception
411 {
412
413 conf.addProperty("string", "value1");
414 for (int i = 1; i < 5; i++)
415 {
416 conf.addProperty("test.array", "value" + i);
417 }
418
419
420 for (int i = 1; i < 5; i++)
421 {
422 conf.addProperty("test.attribute[@array]", "value" + i);
423 }
424
425
426 conf.addProperty("split.list5", "a//,b//,c");
427 conf.setProperty("element3", "value//,value1//,value2");
428 conf.setProperty("element3[@name]", "foo//,bar");
429
430
431 conf.save(testSaveConf.getAbsolutePath());
432
433
434 XMLConfiguration checkConfig = new XMLConfiguration();
435 checkConfig.setFileName(testSaveConf.getAbsolutePath());
436 checkSavedConfig(checkConfig);
437 }
438
439 /***
440 * Tests saving to a URL.
441 */
442 public void testSaveToURL() throws Exception
443 {
444 conf.save(testSaveConf.toURL());
445 XMLConfiguration checkConfig = new XMLConfiguration();
446 checkConfig.setFile(testSaveConf);
447 checkSavedConfig(checkConfig);
448 }
449
450 /***
451 * Tests saving to a stream.
452 */
453 public void testSaveToStream() throws Exception
454 {
455 assertNull(conf.getEncoding());
456 conf.setEncoding("UTF8");
457 FileOutputStream out = null;
458 try
459 {
460 out = new FileOutputStream(testSaveConf);
461 conf.save(out);
462 }
463 finally
464 {
465 if(out != null)
466 {
467 out.close();
468 }
469 }
470
471 XMLConfiguration checkConfig = new XMLConfiguration();
472 checkConfig.setFile(testSaveConf);
473 checkSavedConfig(checkConfig);
474
475 try
476 {
477 out = new FileOutputStream(testSaveConf);
478 conf.save(out, "UTF8");
479 }
480 finally
481 {
482 if(out != null)
483 {
484 out.close();
485 }
486 }
487
488 checkConfig.clear();
489 checkSavedConfig(checkConfig);
490 }
491
492 public void testAutoSave() throws Exception
493 {
494 conf.setFile(new File("target/testsave.xml"));
495 assertFalse(conf.isAutoSave());
496 conf.setAutoSave(true);
497 assertTrue(conf.isAutoSave());
498 conf.setProperty("autosave", "ok");
499
500
501 XMLConfiguration conf2 = new XMLConfiguration(conf.getFile());
502 assertEquals("'autosave' property", "ok", conf2.getString("autosave"));
503
504 conf.clearTree("clear");
505 conf2 = new XMLConfiguration(conf.getFile());
506 Configuration sub = conf2.subset("clear");
507 assertTrue(sub.isEmpty());
508 }
509
510 /***
511 * Tests if a second file can be appended to a first.
512 */
513 public void testAppend() throws Exception
514 {
515 conf = new XMLConfiguration();
516 conf.setFileName(testProperties);
517 conf.load();
518 conf.load(testProperties2);
519 assertEquals("value", conf.getString("element"));
520 assertEquals("tasks", conf.getString("table.name"));
521
522 conf.save(testSaveConf);
523 conf = new XMLConfiguration(testSaveConf);
524 assertEquals("value", conf.getString("element"));
525 assertEquals("tasks", conf.getString("table.name"));
526 assertEquals("application", conf.getString("table[@tableType]"));
527 }
528
529 /***
530 * Tests saving attributes (related to issue 34442).
531 */
532 public void testSaveAttributes() throws Exception
533 {
534 conf.clear();
535 conf.load();
536 conf.save(testSaveConf);
537 conf = new XMLConfiguration();
538 conf.load(testSaveConf);
539 assertEquals("foo", conf.getString("element3[@name]"));
540 }
541
542 /***
543 * Tests collaboration between XMLConfiguration and a reloading strategy.
544 */
545 public void testReloading() throws Exception
546 {
547 assertNotNull(conf.getReloadingStrategy());
548 assertTrue(conf.getReloadingStrategy() instanceof InvariantReloadingStrategy);
549 PrintWriter out = null;
550
551 try
552 {
553 out = new PrintWriter(new FileWriter(testSaveConf));
554 out.println("<?xml version=\"1.0\"?><config><test>1</test></config>");
555 out.close();
556 out = null;
557 conf.setFile(testSaveConf);
558 FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();
559 strategy.setRefreshDelay(100);
560 conf.setReloadingStrategy(strategy);
561 assertEquals(strategy, conf.getReloadingStrategy());
562 conf.load();
563 assertEquals(1, conf.getInt("test"));
564 Thread.sleep(1000);
565
566 out = new PrintWriter(new FileWriter(testSaveConf));
567 out.println("<?xml version=\"1.0\"?><config><test>2</test></config>");
568 out.close();
569 out = null;
570
571 int trial = 0, value;
572
573 do
574 {
575 Thread.sleep(1000);
576 value = conf.getInt("test");
577 } while (value != 2 && ++trial <= 10);
578 assertEquals(2, value);
579 }
580 finally
581 {
582 if (out != null)
583 {
584 out.close();
585 }
586 }
587 }
588
589 /***
590 * Tests access to tag names with delimiter characters.
591 */
592 public void testComplexNames()
593 {
594 assertEquals("Name with dot", conf.getString("complexNames.my..elem"));
595 assertEquals("Another dot", conf.getString("complexNames.my..elem.sub..elem"));
596 }
597
598 /***
599 * Tests setting a custom document builder.
600 */
601 public void testCustomDocBuilder() throws Exception
602 {
603
604
605 conf = new XMLConfiguration();
606 conf.load(new File("conf/testValidateInvalid.xml"));
607 assertEquals("customers", conf.getString("table.name"));
608 assertFalse(conf.containsKey("table.fields.field(1).type"));
609
610
611 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
612 factory.setValidating(true);
613 DocumentBuilder builder = factory.newDocumentBuilder();
614 builder.setErrorHandler(new DefaultHandler() {
615 public void error(SAXParseException ex) throws SAXException
616 {
617 throw ex;
618 }
619 });
620 conf = new XMLConfiguration();
621 conf.setDocumentBuilder(builder);
622 try
623 {
624 conf.load(new File("conf/testValidateInvalid.xml"));
625 fail("Could load invalid file with validating set to true!");
626 }
627 catch(ConfigurationException ex)
628 {
629
630 }
631
632
633 conf = new XMLConfiguration();
634 conf.setDocumentBuilder(builder);
635 conf.load(new File("conf/testValidateValid.xml"));
636 assertTrue(conf.containsKey("table.fields.field(1).type"));
637 }
638
639 /***
640 * Tests the clone() method.
641 */
642 public void testClone()
643 {
644 Configuration c = (Configuration) conf.clone();
645 assertTrue(c instanceof XMLConfiguration);
646 XMLConfiguration copy = (XMLConfiguration) c;
647 assertNotNull(conf.getDocument());
648 assertNull(copy.getDocument());
649 assertNotNull(conf.getFileName());
650 assertNull(copy.getFileName());
651
652 copy.setProperty("element3", "clonedValue");
653 assertEquals("value", conf.getString("element3"));
654 conf.setProperty("element3[@name]", "originalFoo");
655 assertEquals("foo", copy.getString("element3[@name]"));
656 }
657
658 /***
659 * Tests the subset() method. There was a bug that calling subset() had
660 * undesired side effects.
661 */
662 public void testSubset() throws ConfigurationException
663 {
664 conf = new XMLConfiguration();
665 conf.load(new File("conf/testHierarchicalXMLConfiguration.xml"));
666 conf.subset("tables.table(0)");
667 conf.save(testSaveConf);
668
669 conf = new XMLConfiguration(testSaveConf);
670 assertEquals("users", conf.getString("tables.table(0).name"));
671 }
672
673 /***
674 * Tests string properties with list delimiters and escaped delimiters.
675 */
676 public void testSplitLists()
677 {
678 assertEquals("a", conf.getString("split.list3[@values]"));
679 assertEquals(2, conf.getMaxIndex("split.list3[@values]"));
680 assertEquals("a,b,c", conf.getString("split.list4[@values]"));
681 assertEquals("a", conf.getString("split.list1"));
682 assertEquals(2, conf.getMaxIndex("split.list1"));
683 assertEquals("a,b,c", conf.getString("split.list2"));
684 }
685
686 /***
687 * Tests whether a DTD can be accessed.
688 */
689 public void testDtd() throws ConfigurationException
690 {
691 conf = new XMLConfiguration("testDtd.xml");
692 assertEquals("value1", conf.getString("entry(0)"));
693 assertEquals("test2", conf.getString("entry(1)[@key]"));
694 }
695
696 /***
697 * Tests DTD validation using the setValidating() method.
698 */
699 public void testValidating() throws ConfigurationException
700 {
701 File nonValidFile = new File("conf/testValidateInvalid.xml");
702 conf = new XMLConfiguration();
703 assertFalse(conf.isValidating());
704
705
706 conf.load(nonValidFile);
707 assertEquals("customers", conf.getString("table.name"));
708 assertFalse(conf.containsKey("table.fields.field(1).type"));
709
710
711 conf.setValidating(true);
712 try
713 {
714 conf.load(nonValidFile);
715 fail("Validation was not performed!");
716 }
717 catch(ConfigurationException cex)
718 {
719
720 }
721 }
722
723 /***
724 * Tests handling of empty elements.
725 */
726 public void testEmptyElements() throws ConfigurationException
727 {
728 assertTrue(conf.containsKey("empty"));
729 assertEquals("", conf.getString("empty"));
730 conf.addProperty("empty2", "");
731 conf.setProperty("empty", "no more empty");
732 conf.save(testSaveConf);
733
734 conf = new XMLConfiguration(testSaveConf);
735 assertEquals("no more empty", conf.getString("empty"));
736 assertEquals("", conf.getProperty("empty2"));
737 }
738
739 /***
740 * Tests whether the encoding is correctly detected by the XML parser. This
741 * is done by loading an XML file with the encoding "UTF-16". If this
742 * encoding is not detected correctly, an exception will be thrown that
743 * "Content is not allowed in prolog". This test case is related to issue
744 * 34204.
745 */
746 public void testLoadWithEncoding() throws ConfigurationException
747 {
748 File file = new File("conf/testEncoding.xml");
749 conf = new XMLConfiguration();
750 conf.load(file);
751 assertEquals("test3_yoge", conf.getString("yoge"));
752 }
753
754 /***
755 * Tests whether the encoding is written to the generated XML file.
756 */
757 public void testSaveWithEncoding() throws ConfigurationException
758 {
759 conf = new XMLConfiguration();
760 conf.setProperty("test", "a value");
761 conf.setEncoding(ENCODING);
762
763 StringWriter out = new StringWriter();
764 conf.save(out);
765 assertTrue("Encoding was not written to file", out.toString().indexOf(
766 "encoding=\"" + ENCODING + "\"") >= 0);
767 }
768
769 /***
770 * Tests whether a default encoding is used if no specific encoding is set.
771 * According to the XSLT specification (http://www.w3.org/TR/xslt#output)
772 * this should be either UTF-8 or UTF-16.
773 */
774 public void testSaveWithNullEncoding() throws ConfigurationException
775 {
776 conf = new XMLConfiguration();
777 conf.setProperty("testNoEncoding", "yes");
778 conf.setEncoding(null);
779
780 StringWriter out = new StringWriter();
781 conf.save(out);
782 assertTrue("Encoding was written to file", out.toString().indexOf(
783 "encoding=\"UTF-") >= 0);
784 }
785
786 /***
787 * Removes the test output file if it exists.
788 */
789 private void removeTestFile()
790 {
791 if (testSaveConf.exists())
792 {
793 assertTrue(testSaveConf.delete());
794 }
795 }
796
797 /***
798 * Helper method for checking if a save operation was successful. Loads a
799 * saved configuration and then tests against a reference configuration.
800 * @param checkConfig the configuration to check
801 * @throws ConfigurationException if an error occurs
802 */
803 private void checkSavedConfig(FileConfiguration checkConfig) throws ConfigurationException
804 {
805 checkConfig.load();
806
807 for (Iterator i = conf.getKeys(); i.hasNext();)
808 {
809 String key = (String) i.next();
810 assertTrue("The saved configuration doesn't contain the key '" + key + "'", checkConfig.containsKey(key));
811 assertEquals("Value of the '" + key + "' property", conf.getProperty(key), checkConfig.getProperty(key));
812 }
813 }
814 }