/**
* Copyright 2014 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.config;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.event.ConfigurationEvent;
import org.apache.commons.configuration.event.ConfigurationListener;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class ConcurrentMapConfigurationTest {
@BeforeClass
public static void init() {
//Set the system property to default value
System.setProperty(ConcurrentMapConfiguration.DISABLE_DELIMITER_PARSING, "false");
}
@AfterClass
public static void cleanUp() {
//Set the system property to default value
System.setProperty(ConcurrentMapConfiguration.DISABLE_DELIMITER_PARSING, "false");
}
@Test
public void testSetGet() {
ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
conf.addProperty("key1", "xyz");
assertEquals("xyz", conf.getProperty("key1"));
conf.setProperty("key1", "newProp");
assertEquals("newProp", conf.getProperty("key1"));
conf.setProperty("listProperty", "0,1,2,3");
assertTrue(conf.getProperty("listProperty") instanceof List);
List<String> props = (List<String>) conf.getProperty("listProperty");
for (int i = 0; i < 4; i++) {
assertEquals(i, Integer.parseInt(props.get(i)));
}
conf.addProperty("listProperty", "4");
conf.addProperty("listProperty", Arrays.asList((new String[] {"5", "6"})));
props = (List<String>) conf.getProperty("listProperty");
for (int i = 0; i < 7; i++) {
assertEquals(i, Integer.parseInt(props.get(i)));
}
Date date = new Date();
conf.setProperty("listProperty", date);
assertEquals(date, conf.getProperty("listProperty"));
}
@Test
public void testDelimiterParsingDisabled() {
ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
conf.setDelimiterParsingDisabled(true);
conf.setProperty("listProperty", "0,1,2,3");
assertEquals("0,1,2,3", conf.getProperty("listProperty"));
conf.addProperty("listProperty2", "0,1,2,3");
assertEquals("0,1,2,3", conf.getProperty("listProperty2"));
conf.setDelimiterParsingDisabled(false);
assertEquals("0,1,2,3", conf.getProperty("listProperty"));
conf.setProperty("anotherList", "0,1,2,3");
List<String> props = (List<String>) conf.getProperty("anotherList");
for (int i = 0; i < 4; i++) {
assertEquals(i, Integer.parseInt(props.get(i)));
}
}
@Test
public void testConcurrency() {
final ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
ExecutorService exectuor = Executors.newFixedThreadPool(20);
final CountDownLatch doneSignal = new CountDownLatch(1000);
for (int i = 0; i < 1000; i++) {
final Integer index = i;
exectuor.submit(new Runnable() {
public void run() {
conf.addProperty("key", index);
conf.addProperty("key", "stringValue");
doneSignal.countDown();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
});
}
try {
doneSignal.await();
} catch (InterruptedException e) {
}
List prop = (List) conf.getProperty("key");
assertEquals(2000, prop.size());
}
@Test
public void testListeners() {
ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
final AtomicReference<ConfigurationEvent> eventRef = new AtomicReference<ConfigurationEvent>();
conf.addConfigurationListener(new ConfigurationListener() {
@Override
public void configurationChanged(ConfigurationEvent arg0) {
eventRef.set(arg0);
}
});
conf.addProperty("key", "1");
assertEquals(1, conf.getInt("key"));
ConfigurationEvent event = eventRef.get();
assertEquals("key", event.getPropertyName());
assertEquals("1", event.getPropertyValue());
assertTrue(conf == event.getSource());
assertEquals(AbstractConfiguration.EVENT_ADD_PROPERTY, event.getType());
conf.setProperty("key", "2");
event = eventRef.get();
assertEquals("key", event.getPropertyName());
assertEquals("2", event.getPropertyValue());
assertTrue(conf == event.getSource());
assertEquals(AbstractConfiguration.EVENT_SET_PROPERTY, event.getType());
conf.clearProperty("key");
event = eventRef.get();
assertEquals("key", event.getPropertyName());
assertNull(event.getPropertyValue());
assertTrue(conf == event.getSource());
assertEquals(AbstractConfiguration.EVENT_CLEAR_PROPERTY, event.getType());
conf.clear();
assertFalse(conf.getKeys().hasNext());
event = eventRef.get();
assertTrue(conf == event.getSource());
assertEquals(AbstractConfiguration.EVENT_CLEAR, event.getType());
}
@Test
public void testInterpolate() {
ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
conf.setProperty("key1", "value1");
conf.setProperty("key2", "${key1}");
assertEquals("${key1}", conf.getProperty("key2"));
assertEquals("value1", conf.getString("key2"));
}
@Test
public void testSubset() {
Map<String, String> properties = new HashMap<String, String>();
properties.put("a", "x.y.a");
properties.put("b", "x.y.b");
properties.put("c", "x.y.c");
ConcurrentMapConfiguration config = new ConcurrentMapConfiguration();
for (String prop: properties.values()) {
config.addProperty(prop, prop);
}
Configuration subconfig = config.subset("x.y");
Set<String> subsetKeys = new HashSet<String>();
for (Iterator<String> i = subconfig.getKeys(); i.hasNext();) {
String key = i.next();
subsetKeys.add(key);
assertEquals(properties.get(key), subconfig.getString(key));
}
assertEquals(properties.keySet(), subsetKeys);
}
static class MyListener implements ConfigurationListener {
AtomicLong counter = new AtomicLong();
@Override
public void configurationChanged(ConfigurationEvent arg0) {
counter.incrementAndGet();
}
public void resetCounter() {
counter.set(0);
}
public long getCount() {
return counter.get();
}
}
private void testConfigurationSet(Configuration conf) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
conf.setProperty("key" + + (i % 100), "value");
}
long duration = System.currentTimeMillis() - start;
System.out.println("Set property for " + conf + " took " + duration + " ms");
}
private void testConfigurationAdd(Configuration conf) {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
conf.addProperty("add-key" + i, "value");
}
long duration = System.currentTimeMillis() - start;
System.out.println("Add property for " + conf + " took " + duration + " ms");
}
private void testConfigurationGet(Configuration conf) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
conf.getProperty("key" + (i % 100));
}
long duration = System.currentTimeMillis() - start;
System.out.println("get property for " + conf + " took " + duration + " ms");
}
@Test
public void testPerformance() {
MyListener listener = new MyListener();
BaseConfiguration baseConfig = new BaseConfiguration();
baseConfig.addConfigurationListener(listener);
HierarchicalConfiguration hConfig = new HierarchicalConfiguration();
hConfig.addConfigurationListener(listener);
ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
conf.addConfigurationListener(listener);
testConfigurationSet(baseConfig);
testConfigurationSet(hConfig);
testConfigurationSet(conf);
testConfigurationAdd(baseConfig);
testConfigurationAdd(hConfig);
testConfigurationAdd(conf);
testConfigurationGet(baseConfig);
testConfigurationGet(hConfig);
testConfigurationGet(conf);
}
@Test
public void testNullValue() {
ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
try {
conf.setProperty("xyz", null);
} catch (NullPointerException e) {
assertNotNull(e);
}
try {
conf.addProperty("xyz", null);
} catch (NullPointerException e) {
assertNotNull(e);
}
}
@Test
public void testDelimiterParsingSystemProperty() {
//Default behavior of enabling Delimiter parsing
ConcurrentMapConfiguration conf = new ConcurrentMapConfiguration();
conf.setProperty("anotherList", "0,1,2,3");
List<String> props = (List<String>) conf.getProperty("anotherList");
for (int i = 0; i < 4; i++) {
assertEquals(i, Integer.parseInt(props.get(i)));
}
//Set the system property to turn off delimiter parsing
System.setProperty(ConcurrentMapConfiguration.DISABLE_DELIMITER_PARSING, "true");
ConcurrentMapConfiguration conf_delimiter_parsing_off = new ConcurrentMapConfiguration();
conf_delimiter_parsing_off.setProperty("listProperty", "0,1,2,3");
assertEquals("0,1,2,3", conf_delimiter_parsing_off.getProperty("listProperty"));
conf_delimiter_parsing_off.addProperty("listProperty2", "0,1,2,3");
assertEquals("0,1,2,3", conf_delimiter_parsing_off.getProperty("listProperty2"));
conf_delimiter_parsing_off.setDelimiterParsingDisabled(false);
conf_delimiter_parsing_off.setProperty("listProperty3", "0,1,2,3");
List<String> newProps = (List<String>) conf_delimiter_parsing_off.getProperty("listProperty3");
for (int i = 0; i < 4; i++) {
assertEquals(i, Integer.parseInt(newProps.get(i)));
}
//Restore the system property to turn off delimiter parsing
System.setProperty(ConcurrentMapConfiguration.DISABLE_DELIMITER_PARSING, "false");
}
}