001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.configuration;
018
019 import static org.junit.Assert.assertEquals;
020 import static org.junit.Assert.assertFalse;
021 import static org.junit.Assert.assertTrue;
022
023 import java.util.ArrayList;
024 import java.util.Arrays;
025 import java.util.Collection;
026 import java.util.HashMap;
027 import java.util.Iterator;
028 import java.util.List;
029 import java.util.Map;
030
031 import org.apache.commons.collections.CollectionUtils;
032 import org.apache.commons.configuration.event.ConfigurationEvent;
033 import org.apache.commons.configuration.event.ConfigurationListener;
034 import org.junit.Test;
035
036 /**
037 * A test class for some of the basic functionality implemented by
038 * AbstractConfiguration.
039 *
040 * @version $Id: TestAbstractConfigurationBasicFeatures.java 1222823 2011-12-23 20:03:10Z oheger $
041 */
042 public class TestAbstractConfigurationBasicFeatures
043 {
044 /** Constant for the prefix of test keys.*/
045 private static final String KEY_PREFIX = "key";
046
047 /** Constant for the number of properties in tests for copy operations.*/
048 private static final int PROP_COUNT = 12;
049
050 /**
051 * Tests the clear() implementation of AbstractConfiguration if the iterator
052 * returned by getKeys() does not support the remove() operation.
053 */
054 @Test
055 public void testClearIteratorNoRemove()
056 {
057 AbstractConfiguration config = new TestConfigurationImpl(
058 new BaseConfiguration())
059 {
060 // return an iterator that does not support remove operations
061 @Override
062 public Iterator<String> getKeys()
063 {
064 Collection<String> keyCol = new ArrayList<String>();
065 CollectionUtils.addAll(keyCol, getUnderlyingConfiguration()
066 .getKeys());
067 String[] keys = keyCol.toArray(new String[keyCol.size()]);
068 return Arrays.asList(keys).iterator();
069 }
070 };
071 for (int i = 0; i < 20; i++)
072 {
073 config.addProperty("key" + i, "value" + i);
074 }
075 config.clear();
076 assertTrue("Configuration not empty", config.isEmpty());
077 }
078
079 /**
080 * Tests escaping the variable marker, so that no interpolation will be
081 * performed.
082 */
083 @Test
084 public void testInterpolateEscape()
085 {
086 AbstractConfiguration config = new TestConfigurationImpl(
087 new PropertiesConfiguration());
088 config
089 .addProperty(
090 "mypath",
091 "$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar\\,$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar");
092 assertEquals(
093 "Wrong interpolated value",
094 "${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar,${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar",
095 config.getString("mypath"));
096 }
097
098 /**
099 * Tests adding list properties. The single elements of the list should be
100 * added.
101 */
102 @Test
103 public void testAddPropertyList()
104 {
105 checkAddListProperty(new TestConfigurationImpl(
106 new PropertiesConfiguration()));
107 }
108
109 /**
110 * Tests adding list properties when delimiter parsing is disabled.
111 */
112 @Test
113 public void testAddPropertyListNoDelimiterParsing()
114 {
115 AbstractConfiguration config = new TestConfigurationImpl(
116 new PropertiesConfiguration());
117 config.setDelimiterParsingDisabled(true);
118 checkAddListProperty(config);
119 }
120
121 /**
122 * Helper method for adding properties with multiple values.
123 *
124 * @param config the configuration to be used for testing
125 */
126 private void checkAddListProperty(AbstractConfiguration config)
127 {
128 config.addProperty("test", "value1");
129 Object[] lstValues1 = new Object[]
130 { "value2", "value3" };
131 Object[] lstValues2 = new Object[]
132 { "value4", "value5", "value6" };
133 config.addProperty("test", lstValues1);
134 config.addProperty("test", Arrays.asList(lstValues2));
135 List<Object> lst = config.getList("test");
136 assertEquals("Wrong number of list elements", 6, lst.size());
137 for (int i = 0; i < lst.size(); i++)
138 {
139 assertEquals("Wrong list element at " + i, "value" + (i + 1), lst
140 .get(i));
141 }
142 }
143
144 /**
145 * Tests the copy() method.
146 */
147 @Test
148 public void testCopy()
149 {
150 AbstractConfiguration config = setUpDestConfig();
151 Configuration srcConfig = setUpSourceConfig();
152 config.copy(srcConfig);
153 for (int i = 0; i < PROP_COUNT; i++)
154 {
155 String key = KEY_PREFIX + i;
156 if (srcConfig.containsKey(key))
157 {
158 assertEquals("Value not replaced: " + key, srcConfig
159 .getProperty(key), config.getProperty(key));
160 }
161 else
162 {
163 assertEquals("Value modified: " + key, "value" + i, config
164 .getProperty(key));
165 }
166 }
167 }
168
169 /**
170 * Tests the copy() method when properties with multiple values and escaped
171 * list delimiters are involved.
172 */
173 @Test
174 public void testCopyWithLists()
175 {
176 Configuration srcConfig = setUpSourceConfig();
177 AbstractConfiguration config = setUpDestConfig();
178 config.copy(srcConfig);
179 checkListProperties(config);
180 }
181
182 /**
183 * Tests the events generated by a copy() operation.
184 */
185 @Test
186 public void testCopyEvents()
187 {
188 AbstractConfiguration config = setUpDestConfig();
189 Configuration srcConfig = setUpSourceConfig();
190 CollectingConfigurationListener l = new CollectingConfigurationListener();
191 config.addConfigurationListener(l);
192 config.copy(srcConfig);
193 checkCopyEvents(l, srcConfig, AbstractConfiguration.EVENT_SET_PROPERTY);
194 }
195
196 /**
197 * Tests copying a null configuration. This should be a noop.
198 */
199 @Test
200 public void testCopyNull()
201 {
202 AbstractConfiguration config = setUpDestConfig();
203 config.copy(null);
204 ConfigurationAssert.assertEquals(setUpDestConfig(), config);
205 }
206
207 /**
208 * Tests the append() method.
209 */
210 @Test
211 public void testAppend()
212 {
213 AbstractConfiguration config = setUpDestConfig();
214 Configuration srcConfig = setUpSourceConfig();
215 config.append(srcConfig);
216 for (int i = 0; i < PROP_COUNT; i++)
217 {
218 String key = KEY_PREFIX + i;
219 if (srcConfig.containsKey(key))
220 {
221 List<Object> values = config.getList(key);
222 assertEquals("Value not added: " + key, 2, values.size());
223 assertEquals("Wrong value 1 for " + key, "value" + i, values
224 .get(0));
225 assertEquals("Wrong value 2 for " + key, "src" + i, values
226 .get(1));
227 }
228 else
229 {
230 assertEquals("Value modified: " + key, "value" + i, config
231 .getProperty(key));
232 }
233 }
234 }
235
236 /**
237 * Tests the append() method when properties with multiple values and
238 * escaped list delimiters are involved.
239 */
240 @Test
241 public void testAppendWithLists()
242 {
243 AbstractConfiguration config = setUpDestConfig();
244 config.append(setUpSourceConfig());
245 checkListProperties(config);
246 }
247
248 /**
249 * Tests the events generated by an append() operation.
250 */
251 @Test
252 public void testAppendEvents()
253 {
254 AbstractConfiguration config = setUpDestConfig();
255 Configuration srcConfig = setUpSourceConfig();
256 CollectingConfigurationListener l = new CollectingConfigurationListener();
257 config.addConfigurationListener(l);
258 config.append(srcConfig);
259 checkCopyEvents(l, srcConfig, AbstractConfiguration.EVENT_ADD_PROPERTY);
260 }
261
262 /**
263 * Tests appending a null configuration. This should be a noop.
264 */
265 @Test
266 public void testAppendNull()
267 {
268 AbstractConfiguration config = setUpDestConfig();
269 config.append(null);
270 ConfigurationAssert.assertEquals(setUpDestConfig(), config);
271 }
272
273 /**
274 * Tests whether environment variables can be interpolated.
275 */
276 @Test
277 public void testInterpolateEnvironmentVariables()
278 {
279 AbstractConfiguration config = new TestConfigurationImpl(
280 new PropertiesConfiguration());
281 EnvironmentConfiguration envConfig = new EnvironmentConfiguration();
282 Map<String, Object> env = new HashMap<String, Object>();
283 for (Iterator<String> it = envConfig.getKeys(); it.hasNext();)
284 {
285 String key = it.next();
286 String propKey = "envtest." + key;
287 env.put(propKey, envConfig.getString(key));
288 config.addProperty(propKey, "${env:" + key + "}");
289 }
290 assertFalse("No environment properties", env.isEmpty());
291 for (Map.Entry<String, Object> e : env.entrySet())
292 {
293 assertEquals("Wrong value for " + e.getKey(), e.getValue(), config
294 .getString(e.getKey()));
295 }
296 }
297
298 /**
299 * Tests getList() for single non-string values.
300 */
301 @Test
302 public void testGetListNonString()
303 {
304 checkGetListScalar(Integer.valueOf(42));
305 checkGetListScalar(Long.valueOf(42));
306 checkGetListScalar(Short.valueOf((short) 42));
307 checkGetListScalar(Byte.valueOf((byte) 42));
308 checkGetListScalar(Float.valueOf(42));
309 checkGetListScalar(Double.valueOf(42));
310 checkGetListScalar(Boolean.TRUE);
311 }
312
313 /**
314 * Tests getStringArray() for single son-string values.
315 */
316 @Test
317 public void testGetStringArrayNonString()
318 {
319 checkGetStringArrayScalar(Integer.valueOf(42));
320 checkGetStringArrayScalar(Long.valueOf(42));
321 checkGetStringArrayScalar(Short.valueOf((short) 42));
322 checkGetStringArrayScalar(Byte.valueOf((byte) 42));
323 checkGetStringArrayScalar(Float.valueOf(42));
324 checkGetStringArrayScalar(Double.valueOf(42));
325 checkGetStringArrayScalar(Boolean.TRUE);
326 }
327
328 /**
329 * Helper method for checking getList() if the property value is a scalar.
330 * @param value the value of the property
331 */
332 private void checkGetListScalar(Object value)
333 {
334 BaseConfiguration config = new BaseConfiguration();
335 config.addProperty(KEY_PREFIX, value);
336 List<Object> lst = config.getList(KEY_PREFIX);
337 assertEquals("Wrong number of values", 1, lst.size());
338 assertEquals("Wrong value", value.toString(), lst.get(0));
339 }
340
341 /**
342 * Helper method for checking getStringArray() if the property value is a
343 * scalar.
344 *
345 * @param value the value of the property
346 */
347 private void checkGetStringArrayScalar(Object value)
348 {
349 BaseConfiguration config = new BaseConfiguration();
350 config.addProperty(KEY_PREFIX, value);
351 String[] array = config.getStringArray(KEY_PREFIX);
352 assertEquals("Weong number of elements", 1, array.length);
353 assertEquals("Wrong value", value.toString(), array[0]);
354 }
355
356 /**
357 * Tests whether interpolation works in variable names.
358 */
359 @Test
360 public void testNestedVariableInterpolation()
361 {
362 BaseConfiguration config = new BaseConfiguration();
363 config.getSubstitutor().setEnableSubstitutionInVariables(true);
364 config.addProperty("java.version", "1.4");
365 config.addProperty("jre-1.4", "C:\\java\\1.4");
366 config.addProperty("jre.path", "${jre-${java.version}}");
367 assertEquals("Wrong path", "C:\\java\\1.4",
368 config.getString("jre.path"));
369 }
370
371 /**
372 * Creates the source configuration for testing the copy() and append()
373 * methods. This configuration contains keys with an odd index and values
374 * starting with the prefix "src". There are also some list properties.
375 *
376 * @return the source configuration for copy operations
377 */
378 private Configuration setUpSourceConfig()
379 {
380 BaseConfiguration config = new BaseConfiguration();
381 for (int i = 1; i < PROP_COUNT; i += 2)
382 {
383 config.addProperty(KEY_PREFIX + i, "src" + i);
384 }
385 config.addProperty("list1", "1,2,3");
386 config.addProperty("list2", "3\\,1415,9\\,81");
387 return config;
388 }
389
390 /**
391 * Creates the destination configuration for testing the copy() and append()
392 * methods. This configuration contains keys with a running index and
393 * corresponding values starting with the prefix "value".
394 *
395 * @return the destination configuration for copy operations
396 */
397 private AbstractConfiguration setUpDestConfig()
398 {
399 AbstractConfiguration config = new TestConfigurationImpl(
400 new PropertiesConfiguration());
401 for (int i = 0; i < PROP_COUNT; i++)
402 {
403 config.addProperty(KEY_PREFIX + i, "value" + i);
404 }
405 return config;
406 }
407
408 /**
409 * Tests the values of list properties after a copy operation.
410 *
411 * @param config the configuration to test
412 */
413 private void checkListProperties(Configuration config)
414 {
415 List<Object> values = config.getList("list1");
416 assertEquals("Wrong number of elements in list 1", 3, values.size());
417 values = config.getList("list2");
418 assertEquals("Wrong number of elements in list 2", 2, values.size());
419 assertEquals("Wrong value 1", "3,1415", values.get(0));
420 assertEquals("Wrong value 2", "9,81", values.get(1));
421 }
422
423 /**
424 * Tests whether the correct events are received for a copy operation.
425 *
426 * @param l the event listener
427 * @param src the configuration that was copied
428 * @param eventType the expected event type
429 */
430 private void checkCopyEvents(CollectingConfigurationListener l,
431 Configuration src, int eventType)
432 {
433 Map<String, ConfigurationEvent> events = new HashMap<String, ConfigurationEvent>();
434 for (ConfigurationEvent e : l.events)
435 {
436 assertEquals("Wrong event type", eventType, e.getType());
437 assertTrue("Unknown property: " + e.getPropertyName(), src
438 .containsKey(e.getPropertyName()));
439 assertEquals("Wrong property value for " + e.getPropertyName(), e
440 .getPropertyValue(), src.getProperty(e.getPropertyName()));
441 if (!e.isBeforeUpdate())
442 {
443 assertTrue("After event without before event", events
444 .containsKey(e.getPropertyName()));
445 }
446 else
447 {
448 events.put(e.getPropertyName(), e);
449 }
450 }
451
452 for (Iterator<String> it = src.getKeys(); it.hasNext();)
453 {
454 String key = it.next();
455 assertTrue("No event received for key " + key, events
456 .containsKey(key));
457 }
458 }
459
460 /**
461 * A test configuration implementation. This implementation inherits
462 * directly from AbstractConfiguration. For implementing the required
463 * functionality another implementation of AbstractConfiguration is used;
464 * all methods that need to be implemented delegate to this wrapped
465 * configuration.
466 */
467 static class TestConfigurationImpl extends AbstractConfiguration
468 {
469 /** Stores the underlying configuration. */
470 private AbstractConfiguration config;
471
472 public AbstractConfiguration getUnderlyingConfiguration()
473 {
474 return config;
475 }
476
477 public TestConfigurationImpl(AbstractConfiguration wrappedConfig)
478 {
479 config = wrappedConfig;
480 }
481
482 @Override
483 protected void addPropertyDirect(String key, Object value)
484 {
485 config.addPropertyDirect(key, value);
486 }
487
488 public boolean containsKey(String key)
489 {
490 return config.containsKey(key);
491 }
492
493 public Iterator<String> getKeys()
494 {
495 return config.getKeys();
496 }
497
498 public Object getProperty(String key)
499 {
500 return config.getProperty(key);
501 }
502
503 public boolean isEmpty()
504 {
505 return config.isEmpty();
506 }
507
508 @Override
509 protected void clearPropertyDirect(String key)
510 {
511 config.clearPropertyDirect(key);
512 }
513 }
514
515 /**
516 * An event listener implementation that simply collects all received
517 * configuration events.
518 */
519 static class CollectingConfigurationListener implements
520 ConfigurationListener
521 {
522 List<ConfigurationEvent> events = new ArrayList<ConfigurationEvent>();
523
524 public void configurationChanged(ConfigurationEvent event)
525 {
526 events.add(event);
527 }
528 }
529 }