View Javadoc
1   /*
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
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
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.
16   */
17  
18  package org.apache.commons.configuration2;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertNotSame;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertSame;
26  import static org.junit.jupiter.api.Assertions.assertThrows;
27  import static org.junit.jupiter.api.Assertions.assertTrue;
28  import static org.mockito.Mockito.mock;
29  
30  import java.util.ArrayList;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  
35  import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
36  import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
37  import org.apache.commons.configuration2.event.ConfigurationErrorEvent;
38  import org.apache.commons.configuration2.event.ConfigurationEvent;
39  import org.apache.commons.configuration2.event.EventListener;
40  import org.apache.commons.configuration2.event.EventSource;
41  import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
42  import org.apache.commons.configuration2.sync.NoOpSynchronizer;
43  import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
44  import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols;
45  import org.apache.commons.configuration2.tree.ExpressionEngine;
46  import org.apache.commons.configuration2.tree.ImmutableNode;
47  import org.junit.jupiter.api.AfterEach;
48  import org.junit.jupiter.api.BeforeEach;
49  import org.junit.jupiter.api.Test;
50  
51  /**
52   * Tests the ConfigurationUtils class
53   */
54  public class TestConfigurationUtils {
55      /**
56       * A test Synchronizer implementation which can be cloned.
57       */
58      private static final class CloneableSynchronizer extends NonCloneableSynchronizer implements Cloneable {
59          /** A flag whether clone() was called. */
60          private final boolean cloned;
61  
62          /**
63           * Creates a new instance of {@code CloneableSynchronizer} and sets the clone flag.
64           *
65           * @param clone the clone flag
66           */
67          public CloneableSynchronizer(final boolean clone) {
68              cloned = clone;
69          }
70  
71          @Override
72          public Object clone() {
73              return new CloneableSynchronizer(true);
74          }
75  
76          /**
77           * Returns a flag whether this object was cloned.
78           *
79           * @return the clone flag
80           */
81          public boolean isCloned() {
82              return cloned;
83          }
84      }
85  
86      /**
87       * A test Synchronizer implementation which cannot be cloned.
88       */
89      private static class NonCloneableSynchronizer extends SynchronizerTestImpl {
90      }
91  
92      /** Constant for the name of a class to be loaded. */
93      private static final String CLS_NAME = "org.apache.commons.configuration2.PropertiesConfiguration";
94  
95      /** Stores the CCL. */
96      private ClassLoader ccl;
97  
98      @BeforeEach
99      public void setUp() throws Exception {
100         ccl = Thread.currentThread().getContextClassLoader();
101     }
102 
103     @AfterEach
104     public void tearDown() throws Exception {
105         Thread.currentThread().setContextClassLoader(ccl);
106     }
107 
108     @Test
109     public void testAppend() {
110         // create the source configuration
111         final Configuration conf1 = new BaseConfiguration();
112         conf1.addProperty("key1", "value1");
113         conf1.addProperty("key2", "value2");
114 
115         // create the target configuration
116         final Configuration conf2 = new BaseConfiguration();
117         conf2.addProperty("key1", "value3");
118         conf2.addProperty("key2", "value4");
119 
120         // append the source configuration to the target configuration
121         ConfigurationUtils.append(conf1, conf2);
122 
123         List<Object> expected = new ArrayList<>();
124         expected.add("value3");
125         expected.add("value1");
126         assertEquals(expected, conf2.getList("key1"));
127 
128         expected = new ArrayList<>();
129         expected.add("value4");
130         expected.add("value2");
131         assertEquals(expected, conf2.getList("key2"));
132     }
133 
134     /**
135      * Tests asEventSource() if an exception is expected.
136      */
137     @Test
138     public void testAsEventSourceNonSupportedEx() {
139         assertThrows(ConfigurationRuntimeException.class, () -> ConfigurationUtils.asEventSource(this, false));
140     }
141 
142     /**
143      * Tests asEventSource() if the passed in object implements this interface.
144      */
145     @Test
146     public void testAsEventSourceSupported() {
147         final XMLConfiguration src = new XMLConfiguration();
148         assertSame(src, ConfigurationUtils.asEventSource(src, true));
149     }
150 
151     /**
152      * Tests asEventSource() if a mock object has to be returned.
153      */
154     @Test
155     public void testAsEventSourceUnsupportedMock() {
156         @SuppressWarnings("unchecked")
157         final EventListener<ConfigurationEvent> cl = mock(EventListener.class);
158         final EventSource source = ConfigurationUtils.asEventSource(this, true);
159         source.addEventListener(ConfigurationEvent.ANY, cl);
160         assertFalse(source.removeEventListener(ConfigurationEvent.ANY, cl));
161         source.addEventListener(ConfigurationEvent.ANY, null);
162     }
163 
164     /**
165      * Tests cloning a configuration that supports this operation.
166      */
167     @Test
168     public void testCloneConfiguration() {
169         final BaseHierarchicalConfiguration conf = new BaseHierarchicalConfiguration();
170         conf.addProperty("test", "yes");
171         final BaseHierarchicalConfiguration copy = (BaseHierarchicalConfiguration) ConfigurationUtils.cloneConfiguration(conf);
172         assertNotSame(conf, copy);
173         assertEquals("yes", copy.getString("test"));
174     }
175 
176     /**
177      * Tests cloning a configuration that does not support this operation. This should cause an exception.
178      */
179     @Test
180     public void testCloneConfigurationNotSupported() {
181         final Configuration myNonCloneableConfig = new NonCloneableConfiguration();
182         assertThrows(ConfigurationRuntimeException.class, () -> ConfigurationUtils.cloneConfiguration(myNonCloneableConfig));
183     }
184 
185     /**
186      * Tests cloning a <b>null</b> configuration.
187      */
188     @Test
189     public void testCloneConfigurationNull() {
190         assertNull(ConfigurationUtils.cloneConfiguration(null));
191     }
192 
193     /**
194      * Tests whether errors are handled correctly by cloneIfPossible().
195      */
196     @Test
197     public void testCloneIfPossibleError() {
198         final XMLBuilderParametersImpl params = new XMLBuilderParametersImpl() {
199             @Override
200             public XMLBuilderParametersImpl clone() {
201                 throw new ConfigurationRuntimeException();
202             }
203         };
204         assertSame(params, ConfigurationUtils.cloneIfPossible(params));
205     }
206 
207     /**
208      * Tests cloneIfPossible() if the passed in object does not support cloning.
209      */
210     @Test
211     public void testCloneIfPossibleNotSupported() {
212         final Long value = 20130116221714L;
213         assertSame(value, ConfigurationUtils.cloneIfPossible(value));
214     }
215 
216     /**
217      * Tests whether cloneIfPossible() can handle null parameters.
218      */
219     @Test
220     public void testCloneIfPossibleNull() {
221         assertNull(ConfigurationUtils.cloneIfPossible(null));
222     }
223 
224     /**
225      * Tests whether an object can be cloned which supports cloning.
226      */
227     @Test
228     public void testCloneIfPossibleSupported() {
229         final XMLBuilderParametersImpl params = new XMLBuilderParametersImpl();
230         params.setPublicID("testID");
231         params.setSchemaValidation(true);
232         final XMLBuilderParametersImpl clone = (XMLBuilderParametersImpl) ConfigurationUtils.cloneIfPossible(params);
233         assertNotSame(params, clone);
234         final Map<String, Object> map = clone.getParameters();
235         for (final Map.Entry<String, Object> e : params.getParameters().entrySet()) {
236             if (!e.getKey().startsWith("config-")) {
237                 assertEquals(e.getValue(), map.get(e.getKey()), "Wrong value for field " + e.getKey());
238             }
239         }
240     }
241 
242     /**
243      * Tests whether a Synchronizer can be cloned using its clone() method.
244      */
245     @Test
246     public void testCloneSynchronizerClone() {
247         final CloneableSynchronizer sync = new CloneableSynchronizer(false);
248         final CloneableSynchronizer sync2 = (CloneableSynchronizer) ConfigurationUtils.cloneSynchronizer(sync);
249         assertTrue(sync2.isCloned());
250     }
251 
252     /**
253      * Tests cloneSynchronizer() if the argument cannot be cloned.
254      */
255     @Test
256     public void testCloneSynchronizerFailed() {
257         final NonCloneableSynchronizer synchronizer = new NonCloneableSynchronizer();
258         assertThrows(ConfigurationRuntimeException.class, () -> ConfigurationUtils.cloneSynchronizer(synchronizer));
259     }
260 
261     /**
262      * Tests whether a new Synchronizer can be created using reflection.
263      */
264     @Test
265     public void testCloneSynchronizerNewInstance() {
266         final SynchronizerTestImpl sync = new SynchronizerTestImpl();
267         final SynchronizerTestImpl sync2 = (SynchronizerTestImpl) ConfigurationUtils.cloneSynchronizer(sync);
268         assertNotNull(sync2);
269         assertNotSame(sync, sync2);
270     }
271 
272     /**
273      * Tests whether the NoOpSyhnchronizer can be cloned.
274      */
275     @Test
276     public void testCloneSynchronizerNoOp() {
277         assertSame(NoOpSynchronizer.INSTANCE, ConfigurationUtils.cloneSynchronizer(NoOpSynchronizer.INSTANCE));
278     }
279 
280     /**
281      * Tries to clone a null Synchronizer.
282      */
283     @Test
284     public void testCloneSynchronizerNull() {
285         assertThrows(IllegalArgumentException.class, () -> ConfigurationUtils.cloneSynchronizer(null));
286     }
287 
288     /**
289      * Tests converting a configuration into a hierarchical one that is already hierarchical.
290      */
291     @Test
292     public void testConvertHierarchicalToHierarchical() {
293         final Configuration conf = new BaseHierarchicalConfiguration();
294         conf.addProperty("test", "yes");
295         assertSame(conf, ConfigurationUtils.convertToHierarchical(conf));
296     }
297 
298     /**
299      * Tests converting an already hierarchical configuration using an expression engine. The new engine should be set.
300      */
301     @Test
302     public void testConvertHierarchicalToHierarchicalEngine() {
303         final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
304         final ExpressionEngine engine = new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS);
305         assertSame(hc, ConfigurationUtils.convertToHierarchical(hc, engine));
306         assertSame(engine, hc.getExpressionEngine());
307     }
308 
309     /**
310      * Tests converting an already hierarchical configuration using a null expression engine. In this case the expression
311      * engine of the configuration should not be touched.
312      */
313     @Test
314     public void testConvertHierarchicalToHierarchicalNullEngine() {
315         final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
316         final ExpressionEngine engine = new DefaultExpressionEngine(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS);
317         hc.setExpressionEngine(engine);
318         assertSame(hc, ConfigurationUtils.convertToHierarchical(hc, null));
319         assertSame(engine, hc.getExpressionEngine());
320     }
321 
322     /**
323      * Tests converting a null configuration to a hierarchical one. The result should be null, too.
324      */
325     @Test
326     public void testConvertNullToHierarchical() {
327         assertNull(ConfigurationUtils.convertToHierarchical(null));
328     }
329 
330     /**
331      * Tests converting a configuration into a hierarchical one.
332      */
333     @Test
334     public void testConvertToHierarchical() {
335         final Configuration conf = new BaseConfiguration();
336         for (int i = 0; i < 10; i++) {
337             conf.addProperty("test" + i, "value" + i);
338             conf.addProperty("test.list", "item" + i);
339         }
340 
341         final BaseHierarchicalConfiguration hc = (BaseHierarchicalConfiguration) ConfigurationUtils.convertToHierarchical(conf);
342         for (final Iterator<String> it = conf.getKeys(); it.hasNext();) {
343             final String key = it.next();
344             assertEquals(conf.getProperty(key), hc.getProperty(key), "Wrong value for key " + key);
345         }
346     }
347 
348     /**
349      * Tests converting a configuration into a hierarchical one if some of its properties contain escaped list delimiter
350      * characters.
351      */
352     @Test
353     public void testConvertToHierarchicalDelimiters() {
354         final BaseConfiguration conf = new BaseConfiguration();
355         conf.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
356         conf.addProperty("test.key", "1\\,2\\,3");
357         assertEquals("1,2,3", conf.getString("test.key"));
358         final HierarchicalConfiguration<?> hc = ConfigurationUtils.convertToHierarchical(conf);
359         assertEquals("1,2,3", hc.getString("test.key"));
360     }
361 
362     /**
363      * Tests converting a configuration to a hierarchical one using a specific expression engine.
364      */
365     @Test
366     public void testConvertToHierarchicalEngine() {
367         final Configuration conf = new BaseConfiguration();
368         conf.addProperty("test(a)", Boolean.TRUE);
369         conf.addProperty("test(b)", Boolean.FALSE);
370         final DefaultExpressionEngine engine = new DefaultExpressionEngine(
371             new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS).setIndexStart("[").setIndexEnd("]").create());
372         final HierarchicalConfiguration<?> hc = ConfigurationUtils.convertToHierarchical(conf, engine);
373         assertTrue(hc.getBoolean("test(a)"));
374         assertFalse(hc.getBoolean("test(b)"));
375     }
376 
377     /**
378      * Tests converting a configuration to a hierarchical one that contains a property with multiple values. This test is
379      * related to CONFIGURATION-346.
380      */
381     @Test
382     public void testConvertToHierarchicalMultiValues() {
383         final BaseConfiguration config = new BaseConfiguration();
384         config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
385         config.addProperty("test", "1,2,3");
386         final HierarchicalConfiguration<?> hc = ConfigurationUtils.convertToHierarchical(config);
387         assertEquals(1, hc.getInt("test(0)"));
388         assertEquals(2, hc.getInt("test(1)"));
389         assertEquals(3, hc.getInt("test(2)"));
390     }
391 
392     /**
393      * Tests that the structure of the resulting hierarchical configuration does not depend on the order of properties in
394      * the source configuration. This test is related to CONFIGURATION-604.
395      */
396     @Test
397     public void testConvertToHierarchicalOrderOfProperties() {
398         final PropertiesConfiguration config = new PropertiesConfiguration();
399         config.addProperty("x.y.z", true);
400         config.addProperty("x.y", true);
401         @SuppressWarnings("unchecked")
402         final HierarchicalConfiguration<ImmutableNode> hc = (HierarchicalConfiguration<ImmutableNode>) ConfigurationUtils.convertToHierarchical(config);
403         final ImmutableNode rootNode = hc.getNodeModel().getNodeHandler().getRootNode();
404         final ImmutableNode nodeX = rootNode.getChildren().get(0);
405         assertEquals(1, nodeX.getChildren().size());
406     }
407 
408     @Test
409     public void testCopy() {
410         // create the source configuration
411         final Configuration conf1 = new BaseConfiguration();
412         conf1.addProperty("key1", "value1");
413         conf1.addProperty("key2", "value2");
414 
415         // create the target configuration
416         final Configuration conf2 = new BaseConfiguration();
417         conf2.addProperty("key1", "value3");
418         conf2.addProperty("key2", "value4");
419 
420         // copy the source configuration into the target configuration
421         ConfigurationUtils.copy(conf1, conf2);
422 
423         assertEquals("value1", conf2.getProperty("key1"));
424         assertEquals("value2", conf2.getProperty("key2"));
425     }
426 
427     /**
428      * Tests whether runtime exceptions can be enabled.
429      */
430     @Test
431     public void testEnableRuntimeExceptions() {
432         final PropertiesConfiguration config = new PropertiesConfiguration() {
433             @Override
434             protected void addPropertyDirect(final String key, final Object value) {
435                 // always simulate an exception
436                 fireError(ConfigurationErrorEvent.WRITE, ConfigurationEvent.ADD_PROPERTY, key, value, new RuntimeException("A faked exception!"));
437             }
438         };
439         config.clearErrorListeners();
440         ConfigurationUtils.enableRuntimeExceptions(config);
441         assertThrows(ConfigurationRuntimeException.class, () -> config.addProperty("test", "testValue"));
442     }
443 
444     /**
445      * Tries to enable runtime exceptions for a configuration that does not inherit from EventSource. This should cause an
446      * exception.
447      */
448     @Test
449     public void testEnableRuntimeExceptionsInvalid() {
450         final Configuration c = mock(Configuration.class);
451         assertThrows(IllegalArgumentException.class, () -> ConfigurationUtils.enableRuntimeExceptions(c));
452     }
453 
454     /**
455      * Tries to enable runtime exceptions for a null configuration. This should cause an exception.
456      */
457     @Test
458     public void testEnableRuntimeExceptionsNull() {
459         assertThrows(IllegalArgumentException.class, () -> ConfigurationUtils.enableRuntimeExceptions(null));
460     }
461 
462     /**
463      * Tests whether a class can be loaded if it is not found by the CCL.
464      */
465     @Test
466     public void testLoadClassCCLNotFound() throws ClassNotFoundException {
467         Thread.currentThread().setContextClassLoader(new ClassLoader() {
468             @Override
469             public Class<?> loadClass(final String name) throws ClassNotFoundException {
470                 throw new ClassNotFoundException(name);
471             }
472         });
473         assertEquals(CLS_NAME, ConfigurationUtils.loadClass(CLS_NAME).getName());
474     }
475 
476     /**
477      * Tests whether a class can be loaded if there is no CCL.
478      */
479     @Test
480     public void testLoadClassCCLNull() throws ClassNotFoundException {
481         Thread.currentThread().setContextClassLoader(null);
482         assertEquals(CLS_NAME, ConfigurationUtils.loadClass(CLS_NAME).getName());
483     }
484 
485     /**
486      * Tests whether a class can be loaded from CCL.
487      */
488     @Test
489     public void testLoadClassFromCCL() throws ClassNotFoundException {
490         Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
491         assertEquals(CLS_NAME, ConfigurationUtils.loadClass(CLS_NAME).getName());
492     }
493 
494     /**
495      * Tests loadClassNoEx() if the class can be resolved.
496      */
497     @Test
498     public void testLoadClassNoExFound() {
499         assertEquals(CLS_NAME, ConfigurationUtils.loadClassNoEx(CLS_NAME).getName());
500     }
501 
502     /**
503      * Tests loadClassNoEx() if the class cannot be resolved.
504      */
505     @Test
506     public void testLoadClassNoExNotFound() {
507         assertThrows(ConfigurationRuntimeException.class, () -> ConfigurationUtils.loadClassNoEx("a non existing class!"));
508     }
509 
510     /**
511      * Tests the behavior of loadClass() for a non-existing class.
512      */
513     @Test
514     public void testLoadClassNotFound() {
515         assertThrows(ClassNotFoundException.class, () -> ConfigurationUtils.loadClass("a non existing class!"));
516     }
517 
518     @Test
519     public void testToString() {
520         final Configuration config = new BaseConfiguration();
521         final String lineSeparator = System.lineSeparator();
522 
523         assertEquals("", ConfigurationUtils.toString(config));
524 
525         config.setProperty("one", "1");
526         assertEquals("one=1", ConfigurationUtils.toString(config));
527 
528         config.setProperty("two", "2");
529         assertEquals("one=1" + lineSeparator + "two=2", ConfigurationUtils.toString(config));
530 
531         config.clearProperty("one");
532         assertEquals("two=2", ConfigurationUtils.toString(config));
533 
534         config.setProperty("one", "1");
535         assertEquals("two=2" + lineSeparator + "one=1", ConfigurationUtils.toString(config));
536     }
537 }