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  package org.apache.commons.configuration2.builder.combined;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertNotSame;
23  import static org.junit.jupiter.api.Assertions.assertSame;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  import static org.mockito.Mockito.mock;
27  
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  
32  import org.apache.commons.configuration2.ConfigurationLookup;
33  import org.apache.commons.configuration2.DynamicCombinedConfiguration;
34  import org.apache.commons.configuration2.HierarchicalConfiguration;
35  import org.apache.commons.configuration2.XMLConfiguration;
36  import org.apache.commons.configuration2.builder.BasicBuilderParameters;
37  import org.apache.commons.configuration2.builder.BuilderConfigurationWrapperFactory;
38  import org.apache.commons.configuration2.builder.BuilderEventListenerImpl;
39  import org.apache.commons.configuration2.builder.BuilderParameters;
40  import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent;
41  import org.apache.commons.configuration2.builder.ConfigurationBuilderResultCreatedEvent;
42  import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
43  import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
44  import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
45  import org.apache.commons.configuration2.event.ConfigurationEvent;
46  import org.apache.commons.configuration2.event.Event;
47  import org.apache.commons.configuration2.event.EventListener;
48  import org.apache.commons.configuration2.event.EventListenerTestImpl;
49  import org.apache.commons.configuration2.ex.ConfigurationException;
50  import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
51  import org.apache.commons.configuration2.interpol.DefaultLookups;
52  import org.apache.commons.configuration2.tree.ExpressionEngine;
53  import org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine;
54  import org.junit.jupiter.api.Test;
55  import org.xml.sax.SAXParseException;
56  
57  /**
58   * Test class for {@code MultiFileConfigurationBuilder}.
59   */
60  public class TestMultiFileConfigurationBuilder extends AbstractMultiFileConfigurationBuilderTest {
61      /**
62       * Creates a test builder instance which allows access to the managed builders created by it. The returned builder
63       * instance overrides the method for creating managed builders. It stores newly created builders in the passed in
64       * collection.
65       *
66       * @param managedBuilders a collection in which to store managed builders
67       * @return the test builder instance
68       */
69      private static MultiFileConfigurationBuilder<XMLConfiguration> createBuilderWithAccessToManagedBuilders(
70          final Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders) {
71          final MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder<XMLConfiguration>(XMLConfiguration.class) {
72              @Override
73              protected FileBasedConfigurationBuilder<XMLConfiguration> createInitializedManagedBuilder(final String fileName,
74                  final java.util.Map<String, Object> params) throws ConfigurationException {
75                  final FileBasedConfigurationBuilder<XMLConfiguration> result = super.createInitializedManagedBuilder(fileName, params);
76                  managedBuilders.add(result);
77                  return result;
78              }
79          };
80          builder.configure(createTestBuilderParameters(null));
81          return builder;
82      }
83  
84      /**
85       * Creates a test builder object with default settings.
86       *
87       * @param managedParams the parameters for managed configurations
88       * @return the test instance
89       */
90      private static MultiFileConfigurationBuilder<XMLConfiguration> createTestBuilder(final BuilderParameters managedParams) {
91          return new MultiFileConfigurationBuilder<>(XMLConfiguration.class).configure(createTestBuilderParameters(managedParams));
92      }
93  
94      /**
95       * Tests whether configuration listeners are handled correctly.
96       */
97      @Test
98      public void testAddConfigurationListener() throws ConfigurationException {
99          final EventListener<ConfigurationEvent> l1 = new EventListenerTestImpl(null);
100         @SuppressWarnings("unchecked")
101         final EventListener<Event> l2 = mock(EventListener.class);
102         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createTestBuilder(null);
103         builder.addEventListener(ConfigurationEvent.ANY, l1);
104         switchToConfig(1);
105         final XMLConfiguration config = builder.getConfiguration();
106         assertTrue(config.getEventListeners(ConfigurationEvent.ANY).contains(l1));
107         builder.addEventListener(Event.ANY, l2);
108         assertTrue(config.getEventListeners(Event.ANY).contains(l2));
109         assertTrue(builder.removeEventListener(Event.ANY, l2));
110         assertFalse(builder.removeEventListener(Event.ANY, l2));
111         assertFalse(config.getEventListeners(Event.ANY).contains(l2));
112         switchToConfig(2);
113         final XMLConfiguration config2 = builder.getConfiguration();
114         assertFalse(config2.getEventListeners(Event.ANY).contains(l2));
115     }
116 
117     /**
118      * Tests whether builder events of other types can be received.
119      */
120     @Test
121     public void testBuilderListenerOtherTypes() throws ConfigurationException {
122         final BuilderEventListenerImpl listener = new BuilderEventListenerImpl();
123         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createTestBuilder(null);
124         builder.addEventListener(ConfigurationBuilderEvent.ANY, listener);
125         switchToConfig(1);
126         builder.getConfiguration();
127         final ConfigurationBuilderEvent event = listener.nextEvent(ConfigurationBuilderEvent.CONFIGURATION_REQUEST);
128         assertEquals(builder, event.getSource());
129         final ConfigurationBuilderResultCreatedEvent createdEvent = listener.nextEvent(ConfigurationBuilderResultCreatedEvent.RESULT_CREATED);
130         assertEquals(builder, createdEvent.getSource());
131         listener.assertNoMoreEvents();
132     }
133 
134     /**
135      * Tests whether builder reset events are handled correctly.
136      */
137     @Test
138     public void testBuilderListenerReset() throws ConfigurationException {
139         final BuilderEventListenerImpl listener = new BuilderEventListenerImpl();
140         final Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders = new ArrayList<>();
141         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createBuilderWithAccessToManagedBuilders(managedBuilders);
142         switchToConfig(1);
143         builder.addEventListener(ConfigurationBuilderEvent.RESET, listener);
144         final XMLConfiguration configuration = builder.getConfiguration();
145         managedBuilders.iterator().next().resetResult();
146         final ConfigurationBuilderEvent event = listener.nextEvent(ConfigurationBuilderEvent.RESET);
147         assertSame(builder, event.getSource());
148         assertNotSame(configuration, builder.getConfiguration());
149     }
150 
151     /**
152      * Tests whether managed builders are cached.
153      */
154     @Test
155     public void testCaching() throws ConfigurationException {
156         final Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders = new ArrayList<>();
157         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createBuilderWithAccessToManagedBuilders(managedBuilders);
158         switchToConfig(1);
159         builder.getConfiguration();
160         assertEquals(1, managedBuilders.size());
161         builder.getConfiguration();
162         assertEquals(1, managedBuilders.size());
163         switchToConfig(2);
164         builder.getConfiguration();
165         assertEquals(2, managedBuilders.size());
166     }
167 
168     /**
169      * Tests whether a reset of the builder configuration also flushes the cache.
170      */
171     @Test
172     public void testCachingWithReset() throws ConfigurationException {
173         final Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders = new ArrayList<>();
174         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createBuilderWithAccessToManagedBuilders(managedBuilders);
175         switchToConfig(1);
176         builder.getConfiguration();
177         builder.resetParameters();
178         builder.configure(createTestBuilderParameters(null));
179         builder.getConfiguration();
180         assertEquals(2, managedBuilders.size());
181     }
182 
183     /**
184      * Tests the behavior if a configuration is accessed which cannot be located.
185      */
186     @Test
187     public void testFileNotFound() {
188         switchToConfig("unknown configuration ID");
189         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createTestBuilder(null);
190         assertThrows(ConfigurationException.class, builder::getConfiguration);
191     }
192 
193     /**
194      * Tests whether exceptions when creating configurations can be suppressed.
195      */
196     @Test
197     public void testFileNotFoundAllowFailOnInit() throws ConfigurationException {
198         final BasicBuilderParameters params = createTestBuilderParameters(null);
199         final MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder<>(XMLConfiguration.class, params.getParameters(),
200             true);
201         switchToConfig("unknown configuration ID");
202         final XMLConfiguration config = builder.getConfiguration();
203         assertTrue(config.isEmpty());
204     }
205 
206     /**
207      * Tests whether access to multiple configurations works.
208      */
209     @Test
210     public void testGetConfiguration() throws ConfigurationException {
211         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createTestBuilder(null);
212         final String key = "rowsPerPage";
213         switchToConfig(1);
214         assertEquals(15, builder.getConfiguration().getInt(key));
215         switchToConfig(2);
216         assertEquals(25, builder.getConfiguration().getInt(key));
217         switchToConfig(3);
218         assertEquals(35, builder.getConfiguration().getInt(key));
219     }
220 
221     /**
222      * Tests whether initialization parameters of managed builders are cloned before they are applied.
223      */
224     @Test
225     public void testGetManagedBuilderClonedParameters() throws ConfigurationException {
226         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createTestBuilder(new XMLBuilderParametersImpl());
227         switchToConfig(1);
228         final FileBasedConfigurationBuilder<XMLConfiguration> managedBuilder1 = builder.getManagedBuilder();
229         switchToConfig(2);
230         final FileBasedConfigurationBuilder<XMLConfiguration> managedBuilder2 = builder.getManagedBuilder();
231         assertNotSame(managedBuilder1.getFileHandler(), managedBuilder2.getFileHandler());
232     }
233 
234     /**
235      * Tests whether a {@code ConfigurationInterpolator} is created from properties defined in the parameters object if
236      * necessary.
237      */
238     @Test
239     public void testInterpolatorFromParameters() throws ConfigurationException {
240         final BasicBuilderParameters params = new MultiFileBuilderParametersImpl().setFilePattern(PATTERN)
241             .setPrefixLookups(Collections.singletonMap(DefaultLookups.SYSTEM_PROPERTIES.getPrefix(), DefaultLookups.SYSTEM_PROPERTIES.getLookup()));
242         final MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder<>(XMLConfiguration.class);
243         builder.configure(params);
244         switchToConfig(1);
245         assertEquals(15, builder.getConfiguration().getInt("rowsPerPage"));
246     }
247 
248     /**
249      * Tests whether the ConfigurationInterpolator is reset, too.
250      */
251     @Test
252     public void testInterpolatorReset() {
253         final BasicBuilderParameters params = new MultiFileBuilderParametersImpl().setFilePattern(PATTERN);
254         final MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder<>(XMLConfiguration.class);
255         builder.configure(params);
256         final ConfigurationInterpolator interpolator = builder.getInterpolator();
257         assertNotNull(interpolator);
258         builder.resetParameters();
259         assertNotSame(interpolator, builder.getInterpolator());
260     }
261 
262     /**
263      * Tests whether a managed configuration is properly initialized.
264      */
265     @Test
266     public void testManagedConfigurationSettings() throws ConfigurationException {
267         final MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder<>(XMLConfiguration.class);
268         final ExpressionEngine engine = new XPathExpressionEngine();
269         final BuilderParameters xmlParams = new XMLBuilderParametersImpl().setExpressionEngine(engine)
270             .setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
271         final MultiFileBuilderParametersImpl params = new MultiFileBuilderParametersImpl().setFilePattern(PATTERN).setManagedBuilderParameters(xmlParams);
272         final ConfigurationInterpolator ci = createInterpolator();
273         params.setInterpolator(ci).setListDelimiterHandler(new DefaultListDelimiterHandler('#'));
274         builder.configure(params);
275         switchToConfig(1);
276         final XMLConfiguration config = builder.getConfiguration();
277         assertSame(engine, config.getExpressionEngine());
278         final DefaultListDelimiterHandler listHandler = (DefaultListDelimiterHandler) config.getListDelimiterHandler();
279         assertEquals(';', listHandler.getDelimiter());
280         assertNotSame(ci, config.getInterpolator());
281     }
282 
283     /**
284      * Tests whether a missing file name pattern causes an exception.
285      */
286     @Test
287     public void testNoPattern() {
288         final BasicBuilderParameters params = new MultiFileBuilderParametersImpl().setInterpolator(createInterpolator());
289         final MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder<>(XMLConfiguration.class, params.getParameters(),
290             true);
291         switchToConfig(1);
292         assertThrows(ConfigurationException.class, builder::getConfiguration);
293     }
294 
295     /**
296      * Tests whether infinite loops on constructing the file name using interpolation can be handled. This can happen if a
297      * pattern cannot be resolved and the {@code ConfigurationInterpolator} causes again a lookup of the builder's
298      * configuration.
299      */
300     @Test
301     public void testRecursiveInterpolation() {
302         final DynamicCombinedConfiguration config = new DynamicCombinedConfiguration();
303         config.setKeyPattern(PATTERN_VAR);
304         final BasicBuilderParameters params = createTestBuilderParameters(null);
305         final ConfigurationInterpolator ci = new ConfigurationInterpolator();
306         ci.addDefaultLookup(new ConfigurationLookup(config));
307         params.setInterpolator(ci);
308         final MultiFileConfigurationBuilder<XMLConfiguration> builder = new MultiFileConfigurationBuilder<>(XMLConfiguration.class, null, true);
309         builder.configure(params);
310         final BuilderConfigurationWrapperFactory wrapFactory = new BuilderConfigurationWrapperFactory();
311         config.addConfiguration(wrapFactory.createBuilderConfigurationWrapper(HierarchicalConfiguration.class, builder), "Multi");
312         assertTrue(config.isEmpty());
313     }
314 
315     /**
316      * Tests whether listeners at managed builders are removed when the cache is cleared.
317      */
318     @Test
319     public void testRemoveBuilderListenerOnReset() throws ConfigurationException {
320         final BuilderEventListenerImpl listener = new BuilderEventListenerImpl();
321         final Collection<FileBasedConfigurationBuilder<XMLConfiguration>> managedBuilders = new ArrayList<>();
322         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createBuilderWithAccessToManagedBuilders(managedBuilders);
323         switchToConfig(1);
324         builder.addEventListener(ConfigurationBuilderEvent.RESET, listener);
325         builder.getConfiguration();
326         builder.resetParameters();
327         managedBuilders.iterator().next().resetResult();
328         listener.assertNoMoreEvents();
329     }
330 
331     /**
332      * Tests whether XML schema validation can be enabled.
333      */
334     @Test
335     public void testSchemaValidationError() {
336         final MultiFileConfigurationBuilder<XMLConfiguration> builder = createTestBuilder(
337             new XMLBuilderParametersImpl().setValidating(true).setSchemaValidation(true));
338         switchToConfig("2001");
339         final ConfigurationException ex = assertThrows(ConfigurationException.class, builder::getConfiguration);
340         Throwable cause = ex.getCause();
341         while (cause != null && !(cause instanceof SAXParseException)) {
342             cause = cause.getCause();
343         }
344         assertNotNull(cause);
345     }
346 }