You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by oh...@apache.org on 2007/01/15 22:23:59 UTC
svn commit: r496498 - in /jakarta/commons/proper/configuration/trunk/src:
java/org/apache/commons/configuration/DatabaseConfiguration.java
test/org/apache/commons/configuration/TestDatabaseConfiguration.java
Author: oheger
Date: Mon Jan 15 13:23:58 2007
New Revision: 496498
URL: http://svn.apache.org/viewvc?view=rev&rev=496498
Log:
CONFIGURATION-245: DatabaseConfiguration will now generate error events in case on an internal error.
Modified:
jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java
jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java
Modified: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java?view=diff&rev=496498&r1=496497&r2=496498
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java Mon Jan 15 13:23:58 2007
@@ -79,6 +79,7 @@
this.valueColumn = valueColumn;
this.name = name;
setLogger(LogFactory.getLog(getClass()));
+ addErrorLogListener(); // log errors per default
}
/**
@@ -95,7 +96,14 @@
}
/**
- * {@inheritDoc}
+ * Returns the value of the specified property. If this causes a database
+ * error, an error event will be generated of type
+ * <code>EVENT_READ_PROPERTY</code> with the causing exception. The
+ * event's <code>propertyName</code> is set to the passed in property key,
+ * the <code>propertyValue</code> is undefined.
+ *
+ * @param key the key of the desired property
+ * @return the value of this property
*/
public Object getProperty(String key)
{
@@ -113,7 +121,7 @@
try
{
- conn = datasource.getConnection();
+ conn = getConnection();
// bind the parameters
pstmt = conn.prepareStatement(query.toString());
@@ -145,7 +153,7 @@
}
catch (SQLException e)
{
- getLogger().error(e.getMessage(), e);
+ fireError(EVENT_READ_PROPERTY, key, null, e);
}
finally
{
@@ -156,7 +164,14 @@
}
/**
- * {@inheritDoc}
+ * Adds a property to this configuration. If this causes a database error,
+ * an error event will be generated of type <code>EVENT_ADD_PROPERTY</code>
+ * with the causing exception. The event's <code>propertyName</code> is
+ * set to the passed in property key, the <code>propertyValue</code>
+ * points to the passed in value.
+ *
+ * @param key the property key
+ * @param obj the value of the property to add
*/
protected void addPropertyDirect(String key, Object obj)
{
@@ -176,7 +191,7 @@
try
{
- conn = datasource.getConnection();
+ conn = getConnection();
// bind the parameters
pstmt = conn.prepareStatement(query.toString());
@@ -192,7 +207,7 @@
}
catch (SQLException e)
{
- getLogger().error(e.getMessage(), e);
+ fireError(EVENT_ADD_PROPERTY, key, obj, e);
}
finally
{
@@ -202,7 +217,12 @@
}
/**
- * {@inheritDoc}
+ * Checks if this configuration is empty. If this causes a database error,
+ * an error event will be generated of type <code>EVENT_READ_PROPERTY</code>
+ * with the causing exception. Both the event's <code>propertyName</code>
+ * and <code>propertyValue</code> will be undefined.
+ *
+ * @return a flag whether this configuration is empty.
*/
public boolean isEmpty()
{
@@ -220,7 +240,7 @@
try
{
- conn = datasource.getConnection();
+ conn = getConnection();
// bind the parameters
pstmt = conn.prepareStatement(query.toString());
@@ -238,7 +258,7 @@
}
catch (SQLException e)
{
- getLogger().error(e.getMessage(), e);
+ fireError(EVENT_READ_PROPERTY, null, null, e);
}
finally
{
@@ -250,7 +270,14 @@
}
/**
- * {@inheritDoc}
+ * Checks whether this configuration contains the specified key. If this
+ * causes a database error, an error event will be generated of type
+ * <code>EVENT_READ_PROPERTY</code> with the causing exception. The
+ * event's <code>propertyName</code> will be set to the passed in key, the
+ * <code>propertyValue</code> will be undefined.
+ *
+ * @param key the key to be checked
+ * @return a flag whether this key is defined
*/
public boolean containsKey(String key)
{
@@ -268,7 +295,7 @@
try
{
- conn = datasource.getConnection();
+ conn = getConnection();
// bind the parameters
pstmt = conn.prepareStatement(query.toString());
@@ -284,7 +311,7 @@
}
catch (SQLException e)
{
- getLogger().error(e.getMessage(), e);
+ fireError(EVENT_READ_PROPERTY, key, null, e);
}
finally
{
@@ -296,7 +323,13 @@
}
/**
- * {@inheritDoc}
+ * Removes the specified value from this configuration. If this causes a
+ * database error, an error event will be generated of type
+ * <code>EVENT_CLEAR_PROPERTY</code> with the causing exception. The
+ * event's <code>propertyName</code> will be set to the passed in key, the
+ * <code>propertyValue</code> will be undefined.
+ *
+ * @param key the key of the property to be removed
*/
public void clearProperty(String key)
{
@@ -312,7 +345,7 @@
try
{
- conn = datasource.getConnection();
+ conn = getConnection();
// bind the parameters
pstmt = conn.prepareStatement(query.toString());
@@ -326,7 +359,7 @@
}
catch (SQLException e)
{
- getLogger().error(e.getMessage(), e);
+ fireError(EVENT_CLEAR_PROPERTY, key, null, e);
}
finally
{
@@ -336,7 +369,11 @@
}
/**
- * {@inheritDoc}
+ * Removes all entries from this configuration. If this causes a database
+ * error, an error event will be generated of type
+ * <code>EVENT_CLEAR</code> with the causing exception. Both the
+ * event's <code>propertyName</code> and the <code>propertyValue</code>
+ * will be undefined.
*/
public void clear()
{
@@ -352,7 +389,7 @@
try
{
- conn = datasource.getConnection();
+ conn = getConnection();
// bind the parameters
pstmt = conn.prepareStatement(query.toString());
@@ -365,7 +402,7 @@
}
catch (SQLException e)
{
- getLogger().error(e.getMessage(), e);
+ fireError(EVENT_CLEAR, null, null, e);
}
finally
{
@@ -375,7 +412,14 @@
}
/**
- * {@inheritDoc}
+ * Returns an iterator with the names of all properties contained in this
+ * configuration. If this causes a database
+ * error, an error event will be generated of type
+ * <code>EVENT_READ_PROPERTY</code> with the causing exception. Both the
+ * event's <code>propertyName</code> and the <code>propertyValue</code>
+ * will be undefined.
+ * @return an iterator with the contained keys (an empty iterator in case
+ * of an error)
*/
public Iterator getKeys()
{
@@ -393,7 +437,7 @@
try
{
- conn = datasource.getConnection();
+ conn = getConnection();
// bind the parameters
pstmt = conn.prepareStatement(query.toString());
@@ -411,7 +455,7 @@
}
catch (SQLException e)
{
- getLogger().error(e.getMessage(), e);
+ fireError(EVENT_READ_PROPERTY, null, null, e);
}
finally
{
@@ -423,6 +467,31 @@
}
/**
+ * Returns the used <code>DataSource</code> object.
+ *
+ * @return the data source
+ * @since 1.4
+ */
+ public DataSource getDatasource()
+ {
+ return datasource;
+ }
+
+ /**
+ * Returns a <code>Connection</code> object. This method is called when
+ * ever the database is to be accessed. This implementation returns a
+ * connection from the current <code>DataSource</code>.
+ *
+ * @return the <code>Connection</code> object to be used
+ * @throws SQLException if an error occurs
+ * @since 1.4
+ */
+ protected Connection getConnection() throws SQLException
+ {
+ return getDatasource().getConnection();
+ }
+
+ /**
* Close a <code>Connection</code> and, <code>Statement</code>.
* Avoid closing if null and hide any SQLExceptions that occur.
*
@@ -447,4 +516,4 @@
getLogger().error(e.getMessage(), e);
}
}
-}
+}
\ No newline at end of file
Modified: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java?view=diff&rev=496498&r1=496497&r2=496498
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java (original)
+++ jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java Mon Jan 15 13:23:58 2007
@@ -18,6 +18,7 @@
package org.apache.commons.configuration;
import java.io.FileInputStream;
+import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
@@ -26,6 +27,8 @@
import junit.framework.TestCase;
+import org.apache.commons.configuration.event.ConfigurationErrorEvent;
+import org.apache.commons.configuration.event.ConfigurationErrorListener;
import org.apache.commons.configuration.test.HsqlDB;
import org.apache.commons.dbcp.BasicDataSource;
import org.dbunit.database.DatabaseConnection;
@@ -35,7 +38,7 @@
import org.dbunit.operation.DatabaseOperation;
/**
- * Test for database stored configurations. Note, when running this Unit
+ * 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.
*
@@ -48,10 +51,31 @@
public final String DATABASE_USERNAME = "sa";
public final String DATABASE_PASSWORD = "";
+ /** Constant for the configuration table.*/
+ private static final String TABLE = "configuration";
+
+ /** Constant for the multi configuration table.*/
+ private static final String TABLE_MULTI = "configurations";
+
+ /** Constant for the column with the keys.*/
+ private static final String COL_KEY = "key";
+
+ /** Constant for the column with the values.*/
+ private static final String COL_VALUE = "value";
+
+ /** Constant for the column with the configuration name.*/
+ private static final String COL_NAME = "name";
+
+ /** Constant for the name of the test configuration.*/
+ private static final String CONFIG_NAME = "test";
+
private static HsqlDB hsqlDB = null;
private DataSource datasource;
+ /** An error listener for testing whether internal errors occurred.*/
+ private TestErrorListener listener;
+
protected void setUp() throws Exception
{
/*
@@ -59,9 +83,9 @@
* use exception.
*/
//Thread.sleep(1000);
-
+
// set up the datasource
-
+
if (hsqlDB == null)
{
hsqlDB = new HsqlDB(DATABASE_URL, DATABASE_DRIVER, "conf/testdb.script");
@@ -74,7 +98,7 @@
datasource.setPassword(DATABASE_PASSWORD);
this.datasource = datasource;
-
+
// prepare the database
IDatabaseConnection connection = new DatabaseConnection(datasource.getConnection());
@@ -89,15 +113,98 @@
connection.close();
}
}
-
- protected void tearDown() throws SQLException{
+
+ protected void tearDown() throws Exception{
datasource.getConnection().commit();
datasource.getConnection().close();
+
+ // if an error listener is defined, we check whether an error occurred
+ if(listener != null)
+ {
+ assertEquals("An internal error occurred", 0, listener.errorCount);
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Creates a database configuration with default values.
+ *
+ * @return the configuration
+ */
+ private PotentialErrorDatabaseConfiguration setUpConfig()
+ {
+ return new PotentialErrorDatabaseConfiguration(datasource, TABLE,
+ COL_KEY, COL_VALUE);
+ }
+
+ /**
+ * Creates a database configuration that supports multiple configurations in
+ * a table with default values.
+ *
+ * @return the configuration
+ */
+ private DatabaseConfiguration setUpMultiConfig()
+ {
+ return new DatabaseConfiguration(datasource, TABLE_MULTI, COL_NAME,
+ COL_KEY, COL_VALUE, CONFIG_NAME);
+ }
+
+ /**
+ * 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.removeErrorListener((ConfigurationErrorListener) config
+ .getErrorListeners().iterator().next());
+ listener = new TestErrorListener();
+ config.addErrorListener(listener);
+ config.failOnConnect = true;
+ }
+
+ /**
+ * Prepares a test for a database error. Sets up a config and registers an
+ * error listener.
+ *
+ * @return the initialized configuration
+ */
+ private PotentialErrorDatabaseConfiguration setUpErrorConfig()
+ {
+ 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 key the expected property key
+ * @param value the expected property value
+ */
+ private void checkErrorListener(int type, String key, Object value)
+ {
+ assertEquals("Wrong number of errors", 1, listener.errorCount);
+ assertEquals("Wrong event type", type, listener.event.getType());
+ assertTrue("Wrong event source",
+ listener.event.getSource() instanceof DatabaseConfiguration);
+ assertTrue("Wrong exception",
+ listener.event.getCause() instanceof SQLException);
+ assertTrue("Wrong property key", (key == null) ? listener.event
+ .getPropertyName() == null : key.equals(listener.event
+ .getPropertyName()));
+ assertTrue("Wrong property value", (value == null) ? listener.event
+ .getPropertyValue() == null : value.equals(listener.event
+ .getPropertyValue()));
+ listener = null; // mark as checked
}
public void testAddPropertyDirectSingle()
{
- DatabaseConfiguration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ DatabaseConfiguration config = setUpConfig();
config.addPropertyDirect("key", "value");
assertTrue("missing property", config.containsKey("key"));
@@ -105,7 +212,7 @@
public void testAddPropertyDirectMultiple()
{
- DatabaseConfiguration config = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "test");
+ DatabaseConfiguration config = setUpMultiConfig();
config.addPropertyDirect("key", "value");
assertTrue("missing property", config.containsKey("key"));
@@ -113,7 +220,7 @@
public void testAddNonStringProperty()
{
- DatabaseConfiguration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ DatabaseConfiguration config = setUpConfig();
config.addPropertyDirect("boolean", Boolean.TRUE);
assertTrue("missing property", config.containsKey("boolean"));
@@ -121,7 +228,7 @@
public void testGetPropertyDirectSingle()
{
- Configuration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ Configuration config = setUpConfig();
assertEquals("property1", "value1", config.getProperty("key1"));
assertEquals("property2", "value2", config.getProperty("key2"));
@@ -130,7 +237,7 @@
public void testGetPropertyDirectMultiple()
{
- Configuration config = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "test");
+ Configuration config = setUpMultiConfig();
assertEquals("property1", "value1", config.getProperty("key1"));
assertEquals("property2", "value2", config.getProperty("key2"));
@@ -139,7 +246,7 @@
public void testClearPropertySingle()
{
- Configuration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ Configuration config = setUpConfig();
config.clearProperty("key");
assertFalse("property not cleared", config.containsKey("key"));
@@ -147,7 +254,7 @@
public void testClearPropertyMultiple()
{
- Configuration config = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "test");
+ Configuration config = setUpMultiConfig();
config.clearProperty("key");
assertFalse("property not cleared", config.containsKey("key"));
@@ -155,7 +262,7 @@
public void testClearSingle()
{
- Configuration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ Configuration config = setUpConfig();
config.clear();
assertTrue("configuration is not cleared", config.isEmpty());
@@ -163,7 +270,7 @@
public void testClearMultiple()
{
- Configuration config = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "test");
+ Configuration config = setUpMultiConfig();
config.clear();
assertTrue("configuration is not cleared", config.isEmpty());
@@ -171,7 +278,7 @@
public void testGetKeysSingle()
{
- Configuration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ Configuration config = setUpConfig();
Iterator it = config.getKeys();
assertEquals("1st key", "key1", it.next());
@@ -180,7 +287,7 @@
public void testGetKeysMultiple()
{
- Configuration config = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "test");
+ Configuration config = setUpMultiConfig();
Iterator it = config.getKeys();
assertEquals("1st key", "key1", it.next());
@@ -189,43 +296,43 @@
public void testContainsKeySingle()
{
- Configuration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ Configuration config = setUpConfig();
assertTrue("missing key1", config.containsKey("key1"));
assertTrue("missing key2", config.containsKey("key2"));
}
public void testContainsKeyMultiple()
{
- Configuration config = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "test");
+ Configuration config = setUpMultiConfig();
assertTrue("missing key1", config.containsKey("key1"));
assertTrue("missing key2", config.containsKey("key2"));
}
public void testIsEmptySingle()
{
- Configuration config1 = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ Configuration config1 = setUpConfig();
assertFalse("The configuration is empty", config1.isEmpty());
}
public void testIsEmptyMultiple()
{
- Configuration config1 = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "test");
+ Configuration config1 = setUpMultiConfig();
assertFalse("The configuration named 'test' is empty", config1.isEmpty());
- Configuration config2 = new DatabaseConfiguration(datasource, "configurations", "name", "key", "value", "testIsEmpty");
+ Configuration config2 = new DatabaseConfiguration(datasource, TABLE_MULTI, COL_NAME, COL_KEY, COL_VALUE, "testIsEmpty");
assertTrue("The configuration named 'testIsEmpty' is not empty", config2.isEmpty());
}
-
+
public void testGetList()
{
- Configuration config1 = new DatabaseConfiguration(datasource, "configurationList", "key", "value");
+ Configuration config1 = new DatabaseConfiguration(datasource, "configurationList", COL_KEY, COL_VALUE);
List list = config1.getList("key3");
assertEquals(3,list.size());
- }
-
+ }
+
public void testGetKeys()
{
- Configuration config1 = new DatabaseConfiguration(datasource, "configurationList", "key", "value");
+ Configuration config1 = new DatabaseConfiguration(datasource, "configurationList", COL_KEY, COL_VALUE);
Iterator i = config1.getKeys();
assertTrue(i.hasNext());
Object key = i.next();
@@ -235,7 +342,7 @@
public void testClearSubset()
{
- Configuration config = new DatabaseConfiguration(datasource, "configuration", "key", "value");
+ Configuration config = setUpConfig();
Configuration subset = config.subset("key1");
subset.clear();
@@ -244,4 +351,133 @@
assertFalse("the parent configuration is empty", config.isEmpty());
}
+ /**
+ * Tests whether the configuration has already an error listener registered
+ * that is used for logging.
+ */
+ public void testLogErrorListener()
+ {
+ DatabaseConfiguration config = new DatabaseConfiguration(datasource,
+ TABLE, COL_KEY, COL_VALUE);
+ assertEquals("No error listener registered", 1, config
+ .getErrorListeners().size());
+ }
+
+ /**
+ * Tests handling of errors in getProperty().
+ */
+ public void testGetPropertyError()
+ {
+ setUpErrorConfig().getProperty("key1");
+ checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key1",
+ null);
+ }
+
+ /**
+ * Tests handling of errors in addPropertyDirect().
+ */
+ public void testAddPropertyError()
+ {
+ setUpErrorConfig().addProperty("key1", "value");
+ checkErrorListener(AbstractConfiguration.EVENT_ADD_PROPERTY, "key1",
+ "value");
+ }
+
+ /**
+ * Tests handling of errors in isEmpty().
+ */
+ public void testIsEmptyError()
+ {
+ assertTrue("Wrong return value for failure", setUpErrorConfig()
+ .isEmpty());
+ checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null,
+ null);
+ }
+
+ /**
+ * Tests handling of errors in containsKey().
+ */
+ public void testContainsKeyError()
+ {
+ assertFalse("Wrong return value for failure", setUpErrorConfig()
+ .containsKey("key1"));
+ checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, "key1",
+ null);
+ }
+
+ /**
+ * Tests handling of errors in clearProperty().
+ */
+ public void testClearPropertyError()
+ {
+ setUpErrorConfig().clearProperty("key1");
+ checkErrorListener(AbstractConfiguration.EVENT_CLEAR_PROPERTY, "key1",
+ null);
+ }
+
+ /**
+ * Tests handling of errors in clear().
+ */
+ public void testClearError()
+ {
+ setUpErrorConfig().clear();
+ checkErrorListener(AbstractConfiguration.EVENT_CLEAR, null, null);
+ }
+
+ /**
+ * Tests handling of errors in getKeys().
+ */
+ public void testGetKeysError()
+ {
+ Iterator it = setUpErrorConfig().getKeys();
+ checkErrorListener(AbstractConfiguration.EVENT_READ_PROPERTY, null,
+ null);
+ assertFalse("Iteration is not empty", it.hasNext());
+ }
+
+ /**
+ * A specialized database configuration implementation that can be
+ * configured to throw an exception when obtaining a connection. This way
+ * database exceptions can be simulated.
+ */
+ static class PotentialErrorDatabaseConfiguration extends
+ DatabaseConfiguration
+ {
+ /** A flag whether a getConnection() call should fail. */
+ boolean failOnConnect;
+
+ public PotentialErrorDatabaseConfiguration(DataSource datasource,
+ String table, String keyColumn, String valueColumn)
+ {
+ super(datasource, table, keyColumn, valueColumn);
+ }
+
+ protected Connection getConnection() throws SQLException
+ {
+ if (failOnConnect)
+ {
+ throw new SQLException("Simulated DB error");
+ }
+ return super.getConnection();
+ }
+ }
+
+ /**
+ * A test error listener implementation that is used for finding out whether
+ * error events are correctly triggered.
+ */
+ static class TestErrorListener implements ConfigurationErrorListener
+ {
+ /** Stores the number of calls. */
+ int errorCount;
+
+ /** Stores the last error event. */
+ ConfigurationErrorEvent event;
+
+ public void configurationError(ConfigurationErrorEvent event)
+ {
+ errorCount++;
+ this.event = event;
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org