blob: 7d000ba1a5fe0cbd7dcad3802a6dd616d3ba348b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.configuration2.builder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.commons.configuration2.BaseConfiguration;
import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.Initializable;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.beanutils.BeanCreationContext;
import org.apache.commons.configuration2.beanutils.BeanDeclaration;
import org.apache.commons.configuration2.beanutils.BeanFactory;
import org.apache.commons.configuration2.beanutils.BeanHelper;
import org.apache.commons.configuration2.beanutils.DefaultBeanFactory;
import org.apache.commons.configuration2.beanutils.XMLBeanDeclaration;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
import org.apache.commons.configuration2.convert.ListDelimiterHandler;
import org.apache.commons.configuration2.event.ConfigurationErrorEvent;
import org.apache.commons.configuration2.event.ConfigurationEvent;
import org.apache.commons.configuration2.event.ErrorListenerTestImpl;
import org.apache.commons.configuration2.event.EventListener;
import org.apache.commons.configuration2.event.EventListenerRegistrationData;
import org.apache.commons.configuration2.event.EventListenerTestImpl;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
import org.apache.commons.configuration2.reloading.ReloadingController;
import org.apache.commons.configuration2.reloading.ReloadingDetector;
import org.easymock.EasyMock;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Test class for {@code BasicConfigurationBuilder}.
*
*/
public class TestBasicConfigurationBuilder
{
/** A test list delimiter handler. */
private static ListDelimiterHandler listHandler;
@BeforeClass
public static void setUpBeforeClass() throws Exception
{
listHandler = new DefaultListDelimiterHandler(';');
}
/**
* Tries to create an instance without a result class.
*/
@Test(expected = IllegalArgumentException.class)
public void testInitNoClass()
{
new BasicConfigurationBuilder<Configuration>(null);
}
/**
* Creates a map with test initialization parameters.
*
* @return the map with parameters
*/
private static Map<String, Object> createTestParameters()
{
final Map<String, Object> params = new HashMap<>();
params.put("throwExceptionOnMissing", Boolean.TRUE);
params.put("listDelimiterHandler", listHandler);
return params;
}
/**
* Tests whether initialization parameters can be passed to the constructor.
*/
@Test
public void testInitWithParameters()
{
final Map<String, Object> params = createTestParameters();
final BasicConfigurationBuilder<Configuration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, params);
final Map<String, Object> params2 =
new HashMap<>(builder.getParameters());
assertEquals("Wrong parameters", createTestParameters(), params2);
}
/**
* Tests whether a copy of the passed in parameters is created.
*/
@Test
public void testInitWithParametersDefensiveCopy()
{
final Map<String, Object> params = createTestParameters();
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, params);
params.put("anotherParameter", "value");
final Map<String, Object> params2 =
new HashMap<>(builder.getParameters());
assertEquals("Wrong parameters", createTestParameters(), params2);
}
/**
* Tests whether null parameters are handled correctly.
*/
@Test
public void testInitWithParametersNull()
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, null);
assertTrue("Got parameters", builder.getParameters().isEmpty());
}
/**
* Tests that the map with parameters cannot be modified.
*/
@Test(expected = UnsupportedOperationException.class)
public void testGetParametersModify()
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, createTestParameters());
builder.getParameters().clear();
}
/**
* Tests whether parameters can be set using the configure() method.
*/
@Test
public void testConfigure()
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class)
.configure(new BasicBuilderParameters()
.setListDelimiterHandler(listHandler)
.setThrowExceptionOnMissing(true));
final Map<String, Object> params2 =
new HashMap<>(builder.getParameters());
assertEquals("Wrong parameters", createTestParameters(), params2);
}
/**
* Tests whether new parameters can be set to replace existing ones.
*/
@Test
public void testSetParameters()
{
final Map<String, Object> params1 = new HashMap<>();
params1.put("someParameter", "value");
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, params1);
assertSame("Wrong result", builder,
builder.setParameters(createTestParameters()));
final Map<String, Object> params2 =
new HashMap<>(builder.getParameters());
assertEquals("Wrong parameters", createTestParameters(), params2);
}
/**
* Tests whether additional parameters can be added.
*/
@Test
public void testAddParameters()
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, createTestParameters());
final Map<String, Object> params = createTestParameters();
params.put("anotherParameter", "value");
assertSame("Wrong result", builder, builder.addParameters(params));
final Map<String, Object> params2 = builder.getParameters();
assertTrue("No original parameters",
params2.keySet().containsAll(createTestParameters().keySet()));
assertEquals("Additional parameter not found", "value",
params2.get("anotherParameter"));
}
/**
* Tests whether null parameters are handled correctly by addParameters().
*/
@Test
public void testAddParametersNull()
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, createTestParameters());
final Map<String, Object> params = builder.getParameters();
builder.addParameters(null);
assertEquals("Parameters changed", params, builder.getParameters());
}
/**
* Tests whether all parameters can be reset.
*/
@Test
public void testResetParameters()
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, createTestParameters());
builder.resetParameters();
assertTrue("Still got parameters", builder.getParameters().isEmpty());
}
/**
* Tests whether the builder can create a correctly initialized
* configuration object.
*/
@Test
public void testGetConfiguration() throws ConfigurationException
{
final PropertiesConfiguration config =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class).configure(
new BasicBuilderParameters().setListDelimiterHandler(
listHandler).setThrowExceptionOnMissing(true))
.getConfiguration();
assertTrue("Wrong exception flag", config.isThrowExceptionOnMissing());
assertEquals("Wrong list delimiter handler", listHandler,
config.getListDelimiterHandler());
}
/**
* Tests whether the builder can be accessed by multiple threads and that
* only a single result object is produced.
*/
@Test
public void testGetConfigurationConcurrently() throws Exception
{
final int threadCount = 32;
final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch endLatch = new CountDownLatch(threadCount);
final ConfigurationBuilder<?> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class);
final AccessBuilderThread[] threads = new AccessBuilderThread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new AccessBuilderThread(startLatch, endLatch, builder);
threads[i].start();
}
startLatch.countDown();
assertTrue("Timeout", endLatch.await(5, TimeUnit.SECONDS));
final Set<Object> results = new HashSet<>();
for (final AccessBuilderThread t : threads)
{
results.add(t.result);
}
assertEquals("Wrong number of result objects", 1, results.size());
}
/**
* Tests whether a reset of the result object can be performed.
*/
@Test
public void testResetResult() throws ConfigurationException
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, createTestParameters());
final PropertiesConfiguration config = builder.getConfiguration();
builder.resetResult();
final PropertiesConfiguration config2 = builder.getConfiguration();
assertNotSame("No new result", config, config2);
assertTrue("Wrong property", config2.isThrowExceptionOnMissing());
}
/**
* Tests a full reset of the builder.
*/
@Test
public void testReset() throws ConfigurationException
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, createTestParameters());
final PropertiesConfiguration config = builder.getConfiguration();
builder.reset();
final PropertiesConfiguration config2 = builder.getConfiguration();
assertNotSame("No new result", config, config2);
assertFalse("Parameters not reset", config2.isThrowExceptionOnMissing());
}
/**
* Tests whether a check for the correct bean class is made.
*/
@Test(expected = ConfigurationRuntimeException.class)
public void testGetResultDeclarationInvalidBeanClass()
throws ConfigurationException
{
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<PropertiesConfiguration>(
PropertiesConfiguration.class, createTestParameters())
{
@Override
protected BeanDeclaration createResultDeclaration(
final Map<String, Object> params)
{
return new XMLBeanDeclaration(
new BaseHierarchicalConfiguration(), "bean",
true, Object.class.getName());
}
};
builder.getConfiguration();
}
/**
* Creates a mock for an event listener.
*
* @return the event listener mock
*/
private static EventListener<ConfigurationEvent> createEventListener()
{
@SuppressWarnings("unchecked")
final
EventListener<ConfigurationEvent> listener =
EasyMock.createMock(EventListener.class);
return listener;
}
/**
* Tests whether configuration listeners can be added.
*/
@Test
public void testAddConfigurationListener() throws ConfigurationException
{
final EventListener<ConfigurationEvent> l1 = createEventListener();
final EventListener<ConfigurationEvent> l2 = createEventListener();
EasyMock.replay(l1, l2);
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class);
builder.addEventListener(ConfigurationEvent.ANY, l1);
final PropertiesConfiguration config = builder.getConfiguration();
builder.addEventListener(ConfigurationEvent.ANY, l2);
final Collection<EventListener<? super ConfigurationEvent>> listeners =
config.getEventListeners(ConfigurationEvent.ANY);
assertTrue("Listener 1 not registered", listeners.contains(l1));
assertTrue("Listener 2 not registered", listeners.contains(l2));
}
/**
* Tests whether configuration listeners can be removed.
*/
@Test
public void testRemoveConfigurationListener() throws ConfigurationException
{
final EventListener<ConfigurationEvent> l1 = createEventListener();
final EventListener<ConfigurationEvent> l2 = createEventListener();
EasyMock.replay(l1, l2);
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class);
builder.addEventListener(ConfigurationEvent.ANY_HIERARCHICAL,
l1);
builder.addEventListener(ConfigurationEvent.ANY, l2);
assertTrue("Wrong result",
builder.removeEventListener(ConfigurationEvent.ANY, l2));
final PropertiesConfiguration config = builder.getConfiguration();
assertFalse("Removed listener was registered", config
.getEventListeners(ConfigurationEvent.ANY).contains(l2));
assertTrue("Listener not registered",
config.getEventListeners(ConfigurationEvent.ANY_HIERARCHICAL)
.contains(l1));
builder.removeEventListener(
ConfigurationEvent.ANY_HIERARCHICAL, l1);
assertFalse("Listener still registered",
config.getEventListeners(ConfigurationEvent.ANY_HIERARCHICAL)
.contains(l1));
}
/**
* Tests whether event listeners can be copied to another builder.
*/
@Test
public void testCopyEventListeners() throws ConfigurationException
{
final EventListener<ConfigurationEvent> l1 = createEventListener();
final EventListener<ConfigurationEvent> l2 = createEventListener();
final EventListener<ConfigurationErrorEvent> l3 = new ErrorListenerTestImpl(null);
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class);
builder.addEventListener(ConfigurationEvent.ANY, l1);
builder.addEventListener(ConfigurationEvent.ANY_HIERARCHICAL, l2);
builder.addEventListener(ConfigurationErrorEvent.ANY, l3);
final BasicConfigurationBuilder<XMLConfiguration> builder2 =
new BasicConfigurationBuilder<>(
XMLConfiguration.class);
builder.copyEventListeners(builder2);
final XMLConfiguration config = builder2.getConfiguration();
Collection<EventListener<? super ConfigurationEvent>> listeners =
config.getEventListeners(ConfigurationEvent.ANY);
assertEquals("Wrong number of listeners", 1, listeners.size());
assertTrue("Wrong listener", listeners.contains(l1));
listeners =
config.getEventListeners(ConfigurationEvent.ANY_HIERARCHICAL);
assertEquals("Wrong number of listeners for hierarchical", 2,
listeners.size());
assertTrue("Listener 1 not found", listeners.contains(l1));
assertTrue("Listener 2 not found", listeners.contains(l2));
final Collection<EventListener<? super ConfigurationErrorEvent>> errListeners =
config.getEventListeners(ConfigurationErrorEvent.ANY);
assertEquals("Wrong number of error listeners", 1, errListeners.size());
assertTrue("Wrong error listener", errListeners.contains(l3));
}
/**
* Tests whether configuration listeners can be defined via the configure()
* method.
*/
@Test
public void testEventListenerConfiguration() throws ConfigurationException
{
final EventListenerTestImpl listener1 = new EventListenerTestImpl(null);
final EventListenerRegistrationData<ConfigurationErrorEvent> regData =
new EventListenerRegistrationData<>(
ConfigurationErrorEvent.WRITE,
new ErrorListenerTestImpl(null));
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class)
.configure(new EventListenerParameters()
.addEventListener(ConfigurationEvent.ANY,
listener1).addEventListener(regData));
final PropertiesConfiguration config = builder.getConfiguration();
assertTrue("Configuration listener not found", config
.getEventListeners(ConfigurationEvent.ANY).contains(listener1));
assertTrue(
"Error listener not found",
config.getEventListeners(regData.getEventType()).contains(
regData.getListener()));
}
/**
* Tests whether configuration listeners are removed from the managed
* configuration when the builder's result object is reset.
*/
@Test
public void testRemoveConfigurationListenersOnReset()
throws ConfigurationException
{
final EventListenerTestImpl listener = new EventListenerTestImpl(null);
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class)
.configure(new EventListenerParameters()
.addEventListener(ConfigurationEvent.ANY,
listener));
final PropertiesConfiguration config = builder.getConfiguration();
builder.resetResult();
config.addProperty("foo", "bar");
listener.done();
}
/**
* Tests whether parameters starting with a reserved prefix are filtered out
* before result objects are initialized.
*/
@Test
public void testReservedParameter() throws ConfigurationException
{
final Map<String, Object> params = new HashMap<>();
params.put("throwExceptionOnMissing", Boolean.TRUE);
params.put("config-test", "a test");
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class, params);
final PropertiesConfiguration config = builder.getConfiguration();
assertTrue("Flag not set", config.isThrowExceptionOnMissing());
}
/**
* Tests an exception during configuration initialization if the
* allowFailOnInit flag is false.
*/
@Test(expected = ConfigurationException.class)
public void testInitializationErrorNotAllowed()
throws ConfigurationException
{
final BasicConfigurationBuilderInitFailImpl builder =
new BasicConfigurationBuilderInitFailImpl(false);
builder.getConfiguration();
}
/**
* Tests an exception during configuration initialization if the
* allowFailOnInit flag is true.
*/
@Test
public void testInitializationErrorAllowed() throws ConfigurationException
{
final BasicConfigurationBuilderInitFailImpl builder =
new BasicConfigurationBuilderInitFailImpl(true);
final PropertiesConfiguration config = builder.getConfiguration();
assertTrue("Got data", config.isEmpty());
}
/**
* Tests whether a configuration implementing {@code Initializable} is
* correctly handled.
*/
@Test
public void testInitializableCalled() throws ConfigurationException
{
final BasicConfigurationBuilder<InitializableConfiguration> builder =
new BasicConfigurationBuilder<>(
InitializableConfiguration.class);
builder.configure(new BasicBuilderParameters()
.setThrowExceptionOnMissing(true));
final InitializableConfiguration config = builder.getConfiguration();
assertEquals("Property not correctly initialized",
"Initialized with flag true", config.getInitProperty());
}
/**
* Tests whether a configured BeanHelper is used for result creation.
*/
@Test
public void testBeanHelperInConfiguration() throws ConfigurationException
{
final Set<Class<?>> classesPassedToFactory = new HashSet<>();
final BeanFactory factory = new DefaultBeanFactory()
{
@Override
public Object createBean(final BeanCreationContext bcc) throws Exception
{
classesPassedToFactory.add(bcc.getBeanClass());
return super.createBean(bcc);
}
};
final BeanHelper helper = new BeanHelper(factory);
final BasicConfigurationBuilder<PropertiesConfiguration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class);
builder.configure(new BasicBuilderParameters().setBeanHelper(helper));
final PropertiesConfiguration config = builder.getConfiguration();
assertTrue("BeanFactory was not used correctly",
classesPassedToFactory.contains(config.getClass()));
}
/**
* Tests whether a builder can be connected to a reloading controller.
*/
@Test
public void testConnectToReloadingController()
throws ConfigurationException
{
final ReloadingDetector detector =
EasyMock.createNiceMock(ReloadingDetector.class);
EasyMock.expect(detector.isReloadingRequired()).andReturn(Boolean.TRUE);
EasyMock.replay(detector);
final ReloadingController controller = new ReloadingController(detector);
final BasicConfigurationBuilder<Configuration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class);
final Configuration configuration = builder.getConfiguration();
builder.connectToReloadingController(controller);
controller.checkForReloading(null);
assertTrue("Not in reloading state", controller.isInReloadingState());
assertNotSame("No new configuration created", configuration,
builder.getConfiguration());
assertFalse("Still in reloading state", controller.isInReloadingState());
}
/**
* Tries to connect to a null reloading controller.
*/
@Test(expected = IllegalArgumentException.class)
public void testConnectToReloadingControllerNull()
{
final BasicConfigurationBuilder<Configuration> builder =
new BasicConfigurationBuilder<>(
PropertiesConfiguration.class);
builder.connectToReloadingController(null);
}
/**
* A test thread class for testing whether the builder's result object can
* be requested concurrently.
*/
private static class AccessBuilderThread extends Thread
{
/** A latch for controlling the start of the thread. */
private final CountDownLatch startLatch;
/** A latch for controlling the end of the thread. */
private final CountDownLatch endLatch;
/** The builder to be accessed. */
private final ConfigurationBuilder<?> builder;
/** The result object obtained from the builder. */
private volatile Object result;
/**
* Creates a new instance of {@code AccessBuilderThread}.
*
* @param lstart the latch for controlling the thread start
* @param lend the latch for controlling the thread end
* @param bldr the builder to be tested
*/
public AccessBuilderThread(final CountDownLatch lstart, final CountDownLatch lend,
final ConfigurationBuilder<?> bldr)
{
startLatch = lstart;
endLatch = lend;
builder = bldr;
}
@Override
public void run()
{
try
{
startLatch.await();
result = builder.getConfiguration();
}
catch (final Exception ex)
{
result = ex;
}
finally
{
endLatch.countDown();
}
}
}
/**
* A builder test implementation which allows checking exception handling
* when creating new configuration objects.
*/
private static class BasicConfigurationBuilderInitFailImpl extends
BasicConfigurationBuilder<PropertiesConfiguration>
{
public BasicConfigurationBuilderInitFailImpl(final boolean allowFailOnInit)
{
super(PropertiesConfiguration.class, null, allowFailOnInit);
}
/**
* {@inheritDoc} This implementation only throws an exception.
*/
@Override
protected void initResultInstance(final PropertiesConfiguration obj)
throws ConfigurationException
{
throw new ConfigurationException("Initialization test exception!");
}
}
/**
* A test configuration implementation which also implements Initializable.
*/
public static class InitializableConfiguration extends BaseConfiguration
implements Initializable
{
/** A property which is initialized if the builder works as expected. */
private String initProperty;
/**
* Sets the value of the initProperty member based on other flag values.
* This tests whether the method is called after other properties have
* been set.
*/
@Override
public void initialize()
{
initProperty =
"Initialized with flag " + isThrowExceptionOnMissing();
}
public String getInitProperty()
{
return initProperty;
}
}
}