blob: a6587d5656a82e6fef63df81c06d4f7d2ec3b324 [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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import javax.sql.DataSource;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.configuration2.builder.fluent.DatabaseBuilderParameters;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
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.EventType;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Test for database stored configurations. Note, when running this Unit
* Test in Eclipse it sometimes takes a couple tries. Otherwise you may get
* database is already in use by another process errors.
*
* @version $Id$
*/
public class TestDatabaseConfiguration
{
/** Constant for another configuration name. */
private static final String CONFIG_NAME2 = "anotherTestConfig";
/** An error listener for testing whether internal errors occurred.*/
private ErrorListenerTestImpl listener;
/** The test helper. */
private DatabaseConfigurationTestHelper helper;
@Before
public void setUp() throws Exception
{
/*
* Thread.sleep may or may not help with the database is already in
* use exception.
*/
//Thread.sleep(1000);
// set up the datasource
helper = new DatabaseConfigurationTestHelper();
helper.setUp();
}
@After
public void tearDown() throws Exception
{
// if an error listener is defined, we check whether an error occurred
if(listener != null)
{
listener.done();
}
helper.tearDown();
}
/**
* Creates a database configuration with default values.
*
* @return the configuration
* @throws ConfigurationException if an error occurs
*/
private PotentialErrorDatabaseConfiguration setUpConfig()
throws ConfigurationException
{
return helper.setUpConfig(PotentialErrorDatabaseConfiguration.class);
}
/**
* Creates an error listener and adds it to the specified configuration.
*
* @param config the configuration
*/
private void setUpErrorListener(PotentialErrorDatabaseConfiguration config)
{
// remove log listener to avoid exception longs
config.clearErrorListeners();
listener = new ErrorListenerTestImpl(config);
config.addEventListener(ConfigurationErrorEvent.ANY, listener);
config.failOnConnect = true;
}
/**
* Prepares a test for a database error. Sets up a config and registers an
* error listener.
*
* @return the initialized configuration
* @throws ConfigurationException if an error occurs
*/
private PotentialErrorDatabaseConfiguration setUpErrorConfig()
throws ConfigurationException
{
PotentialErrorDatabaseConfiguration config = setUpConfig();
setUpErrorListener(config);
return config;
}
/**
* Checks the error listener for an expected error. The properties of the
* error event will be compared with the expected values.
*
* @param type the expected type of the error event
* @param opType the expected operation type
* @param key the expected property key
* @param value the expected property value
*/
private void checkErrorListener(
EventType<? extends ConfigurationErrorEvent> type,
EventType<?> opType, String key, Object value)
{
Throwable exception = listener.checkEvent(type, opType, key, value);
assertTrue("Wrong exception", exception instanceof SQLException);
listener = null; // mark as checked
}
@Test
public void testAddPropertyDirectSingle() throws ConfigurationException
{
DatabaseConfiguration config = helper.setUpConfig();
config.addPropertyDirect("key", "value");
assertTrue("missing property", config.containsKey("key"));
}
/**
* Tests whether a commit is performed after a property was added.
*/
@Test
public void testAddPropertyDirectCommit() throws ConfigurationException
{
helper.setAutoCommit(false);
DatabaseConfiguration config = helper.setUpConfig();
config.addPropertyDirect("key", "value");
assertTrue("missing property", config.containsKey("key"));
}
@Test
public void testAddPropertyDirectMultiple() throws ConfigurationException
{
DatabaseConfiguration config = helper.setUpMultiConfig();
config.addPropertyDirect("key", "value");
assertTrue("missing property", config.containsKey("key"));
}
@Test
public void testAddNonStringProperty() throws ConfigurationException
{
DatabaseConfiguration config = helper.setUpConfig();
config.addPropertyDirect("boolean", Boolean.TRUE);
assertTrue("missing property", config.containsKey("boolean"));
}
@Test
public void testGetPropertyDirectSingle() throws ConfigurationException
{
Configuration config = setUpConfig();
assertEquals("property1", "value1", config.getProperty("key1"));
assertEquals("property2", "value2", config.getProperty("key2"));
assertEquals("unknown property", null, config.getProperty("key3"));
}
@Test
public void testGetPropertyDirectMultiple() throws ConfigurationException
{
Configuration config = helper.setUpMultiConfig();
assertEquals("property1", "value1", config.getProperty("key1"));
assertEquals("property2", "value2", config.getProperty("key2"));
assertEquals("unknown property", null, config.getProperty("key3"));
}
@Test
public void testClearPropertySingle() throws ConfigurationException
{
Configuration config = helper.setUpConfig();
config.clearProperty("key1");
assertFalse("property not cleared", config.containsKey("key1"));
}
@Test
public void testClearPropertyMultiple() throws ConfigurationException
{
Configuration config = helper.setUpMultiConfig();
config.clearProperty("key1");
assertFalse("property not cleared", config.containsKey("key1"));
}
/**
* Tests that another configuration is not affected when clearing
* properties.
*/
@Test
public void testClearPropertyMultipleOtherConfig() throws ConfigurationException
{
DatabaseConfiguration config = helper.setUpMultiConfig();
DatabaseConfiguration config2 =
helper.setUpMultiConfig(DatabaseConfiguration.class,
CONFIG_NAME2);
config2.addProperty("key1", "some test");
config.clearProperty("key1");
assertFalse("property not cleared", config.containsKey("key1"));
assertTrue("Property cleared in other config", config2
.containsKey("key1"));
}
/**
* Tests whether a commit is performed after a property was cleared.
*/
@Test
public void testClearPropertyCommit() throws ConfigurationException
{
helper.setAutoCommit(false);
Configuration config = helper.setUpConfig();
config.clearProperty("key1");
assertFalse("property not cleared", config.containsKey("key1"));
}
@Test
public void testClearSingle() throws ConfigurationException
{
Configuration config = helper.setUpConfig();
config.clear();
assertTrue("configuration is not cleared", config.isEmpty());
}
@Test
public void testClearMultiple() throws ConfigurationException
{
Configuration config = helper.setUpMultiConfig();
config.clear();
assertTrue("configuration is not cleared", config.isEmpty());
}
/**
* Tests whether a commit is performed after a clear operation.
*/
@Test
public void testClearCommit() throws ConfigurationException
{
helper.setAutoCommit(false);
Configuration config = helper.setUpConfig();
config.clear();
assertTrue("configuration is not cleared", config.isEmpty());
}
@Test
public void testGetKeysSingle() throws ConfigurationException
{
Configuration config = setUpConfig();
Iterator<String> it = config.getKeys();
assertEquals("1st key", "key1", it.next());
assertEquals("2nd key", "key2", it.next());
}
@Test
public void testGetKeysMultiple() throws ConfigurationException
{
Configuration config = helper.setUpMultiConfig();
Iterator<String> it = config.getKeys();
assertEquals("1st key", "key1", it.next());
assertEquals("2nd key", "key2", it.next());
}
@Test
public void testContainsKeySingle() throws ConfigurationException
{
Configuration config = setUpConfig();
assertTrue("missing key1", config.containsKey("key1"));
assertTrue("missing key2", config.containsKey("key2"));
}
@Test
public void testContainsKeyMultiple() throws ConfigurationException
{
Configuration config = helper.setUpMultiConfig();
assertTrue("missing key1", config.containsKey("key1"));
assertTrue("missing key2", config.containsKey("key2"));
}
@Test
public void testIsEmptySingle() throws ConfigurationException
{
Configuration config1 = setUpConfig();
assertFalse("The configuration is empty", config1.isEmpty());
}
@Test
public void testIsEmptyMultiple() throws ConfigurationException
{
Configuration config1 = helper.setUpMultiConfig();
assertFalse("The configuration named 'test' is empty", config1.isEmpty());
Configuration config2 = helper.setUpMultiConfig(DatabaseConfiguration.class, "testIsEmpty");
assertTrue("The configuration named 'testIsEmpty' is not empty", config2.isEmpty());
}
@Test
public void testGetList() throws ConfigurationException
{
DatabaseBuilderParameters params = helper.setUpDefaultParameters().setTable("configurationList");
Configuration config1 = helper.createConfig(DatabaseConfiguration.class, params);
List<Object> list = config1.getList("key3");
assertEquals(3,list.size());
}
@Test
public void testGetKeys() throws ConfigurationException
{
DatabaseBuilderParameters params = helper.setUpDefaultParameters().setTable("configurationList");
Configuration config1 = helper.createConfig(DatabaseConfiguration.class, params);
Iterator<String> i = config1.getKeys();
assertTrue(i.hasNext());
Object key = i.next();
assertEquals("key3",key.toString());
assertFalse(i.hasNext());
}
@Test
public void testClearSubset() throws ConfigurationException
{
Configuration config = setUpConfig();
Configuration subset = config.subset("key1");
subset.clear();
assertTrue("the subset is not empty", subset.isEmpty());
assertFalse("the parent configuration is empty", config.isEmpty());
}
/**
* Tests whether the configuration has already an error listener registered
* that is used for logging.
*/
@Test
public void testLogErrorListener() throws ConfigurationException
{
DatabaseConfiguration config = helper.setUpConfig();
assertEquals("No error listener registered", 1, config
.getEventListeners(ConfigurationErrorEvent.ANY).size());
}
/**
* Tests handling of errors in getProperty().
*/
@Test
public void testGetPropertyError() throws ConfigurationException
{
setUpErrorConfig().getProperty("key1");
checkErrorListener(ConfigurationErrorEvent.READ,
ConfigurationErrorEvent.READ, "key1", null);
}
/**
* Tests handling of errors in addPropertyDirect().
*/
@Test
public void testAddPropertyError() throws ConfigurationException
{
setUpErrorConfig().addProperty("key1", "value");
checkErrorListener(ConfigurationErrorEvent.WRITE,
ConfigurationEvent.ADD_PROPERTY, "key1", "value");
}
/**
* Tests handling of errors in isEmpty().
*/
@Test
public void testIsEmptyError() throws ConfigurationException
{
assertTrue("Wrong return value for failure", setUpErrorConfig().isEmpty());
checkErrorListener(ConfigurationErrorEvent.READ,
ConfigurationErrorEvent.READ, null, null);
}
/**
* Tests handling of errors in containsKey().
*/
@Test
public void testContainsKeyError() throws ConfigurationException
{
assertFalse("Wrong return value for failure", setUpErrorConfig().containsKey("key1"));
checkErrorListener(ConfigurationErrorEvent.READ,
ConfigurationErrorEvent.READ, "key1", null);
}
/**
* Tests handling of errors in clearProperty().
*/
@Test
public void testClearPropertyError() throws ConfigurationException
{
setUpErrorConfig().clearProperty("key1");
checkErrorListener(ConfigurationErrorEvent.WRITE,
ConfigurationEvent.CLEAR_PROPERTY, "key1", null);
}
/**
* Tests handling of errors in clear().
*/
@Test
public void testClearError() throws ConfigurationException
{
setUpErrorConfig().clear();
checkErrorListener(ConfigurationErrorEvent.WRITE,
ConfigurationEvent.CLEAR, null, null);
}
/**
* Tests handling of errors in getKeys().
*/
@Test
public void testGetKeysError() throws ConfigurationException
{
Iterator<String> it = setUpErrorConfig().getKeys();
checkErrorListener(ConfigurationErrorEvent.READ,
ConfigurationErrorEvent.READ, null, null);
assertFalse("Iteration is not empty", it.hasNext());
}
/**
* Tests obtaining a property as list whose value contains the list
* delimiter. Multiple values should be returned.
*/
@Test
public void testGetListWithDelimiter() throws ConfigurationException
{
DatabaseConfiguration config = setUpConfig();
config.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
List<Object> values = config.getList("keyMulti");
assertEquals("Wrong number of list elements", 3, values.size());
assertEquals("Wrong list element 0", "a", values.get(0));
assertEquals("Wrong list element 2", "c", values.get(2));
}
/**
* Tests obtaining a property whose value contains the list delimiter when
* delimiter parsing is disabled.
*/
@Test
public void testGetListWithDelimiterParsingDisabled() throws ConfigurationException
{
DatabaseConfiguration config = setUpConfig();
assertEquals("Wrong value of property", "a;b;c", config.getString("keyMulti"));
}
/**
* Tests adding a property containing the list delimiter. When this property
* is queried multiple values should be returned.
*/
@Test
public void testAddWithDelimiter() throws ConfigurationException
{
DatabaseConfiguration config = setUpConfig();
config.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
config.addProperty("keyList", "1;2;3");
String[] values = config.getStringArray("keyList");
assertEquals("Wrong number of property values", 3, values.length);
assertEquals("Wrong value at index 1", "2", values[1]);
}
/**
* Tests setProperty() if the property value contains the list delimiter.
*/
@Test
public void testSetPropertyWithDelimiter() throws ConfigurationException
{
DatabaseConfiguration config = helper.setUpMultiConfig();
config.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
config.setProperty("keyList", "1;2;3");
String[] values = config.getStringArray("keyList");
assertEquals("Wrong number of property values", 3, values.length);
assertEquals("Wrong value at index 1", "2", values[1]);
}
/**
* Tests whether a CLOB as a property value is handled correctly.
*/
@Test
public void testExtractPropertyValueCLOB() throws ConfigurationException,
SQLException
{
ResultSet rs = EasyMock.createMock(ResultSet.class);
Clob clob = EasyMock.createMock(Clob.class);
final String content = "This is the content of the test CLOB!";
EasyMock.expect(rs.getObject(DatabaseConfigurationTestHelper.COL_VALUE))
.andReturn(clob);
EasyMock.expect(clob.length())
.andReturn(Long.valueOf(content.length()));
EasyMock.expect(clob.getSubString(1, content.length())).andReturn(
content);
EasyMock.replay(rs, clob);
DatabaseConfiguration config = helper.setUpConfig();
assertEquals("Wrong extracted value", content,
config.extractPropertyValue(rs));
EasyMock.verify(rs, clob);
}
/**
* Tests whether an empty CLOB is correctly handled by
* extractPropertyValue().
*/
@Test
public void testExtractPropertyValueCLOBEmpty()
throws ConfigurationException, SQLException
{
ResultSet rs = EasyMock.createMock(ResultSet.class);
Clob clob = EasyMock.createMock(Clob.class);
EasyMock.expect(rs.getObject(DatabaseConfigurationTestHelper.COL_VALUE))
.andReturn(clob);
EasyMock.expect(clob.length()).andReturn(0L);
EasyMock.replay(rs, clob);
DatabaseConfiguration config = helper.setUpConfig();
assertEquals("Wrong extracted value", "",
config.extractPropertyValue(rs));
EasyMock.verify(rs, clob);
}
/**
* A specialized database configuration implementation that can be
* configured to throw an exception when obtaining a connection. This way
* database exceptions can be simulated.
*/
public static class PotentialErrorDatabaseConfiguration extends DatabaseConfiguration
{
/** A flag whether a getConnection() call should fail. */
boolean failOnConnect;
@Override
public DataSource getDatasource()
{
if (failOnConnect)
{
DataSource ds = EasyMock.createMock(DataSource.class);
try
{
EasyMock.expect(ds.getConnection()).andThrow(
new SQLException("Simulated DB error"));
}
catch (SQLException e)
{
// should not happen
throw new AssertionError("Unexpected exception!");
}
EasyMock.replay(ds);
return ds;
}
return super.getDatasource();
}
}
}