Author: oheger Date: Tue Apr 6 20:29:51 2010 New Revision: 931307 URL: http://svn.apache.org/viewvc?rev=931307&view=rev Log: CONFIGURATION-412: DatabaseConfiguration can now be instructed to perform commits after database updates.
Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/DatabaseConfigurationTestHelper.java commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java commons/proper/configuration/trunk/xdocs/changes.xml Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java?rev=931307&r1=931306&r2=931307&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java (original) +++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DatabaseConfiguration.java Tue Apr 6 20:29:51 2010 @@ -74,6 +74,12 @@ import org.apache.commons.logging.LogFac * Configuration config2 = new DatabaseConfiguration(datasource, "myconfigs", "name", "key", "value", "config2"); * String value2 = conf.getString("key2"); * </pre> + * The configuration can be instructed to perform commits after database updates. + * This is achieved by setting the <code>commits</code> parameter of the + * constructors to <b>true</b>. If commits should not be performed (which is the + * default behavior), it should be ensured that the connections returned by the + * <code>DataSource</code> are in auto-commit mode. + * * <h1>Note: Like JDBC itself, protection against SQL injection is left to the user.</h1> * @since 1.0 * @@ -100,8 +106,12 @@ public class DatabaseConfiguration exten /** The name of the configuration. */ private String name; + /** A flag whether commits should be performed by this configuration. */ + private final boolean doCommits; + /** * Build a configuration from a table containing multiple configurations. + * No commits are performed by the new configuration instance. * * @param datasource the datasource to connect to the database * @param table the name of the table containing the configurations @@ -113,18 +123,39 @@ public class DatabaseConfiguration exten public DatabaseConfiguration(DataSource datasource, String table, String nameColumn, String keyColumn, String valueColumn, String name) { + this(datasource, table, nameColumn, keyColumn, valueColumn, name, false); + } + + /** + * Creates a new instance of <code>DatabaseConfiguration</code> that operates on + * a database table containing multiple configurations. + * + * @param datasource the <code>DataSource</code> to connect to the database + * @param table the name of the table containing the configurations + * @param nameColumn the column containing the name of the configuration + * @param keyColumn the column containing the keys of the configuration + * @param valueColumn the column containing the values of the configuration + * @param name the name of the configuration + * @param commits a flag whether the configuration should perform a commit + * after a database update + */ + public DatabaseConfiguration(DataSource datasource, String table, + String nameColumn, String keyColumn, String valueColumn, + String name, boolean commits) + { this.datasource = datasource; this.table = table; this.nameColumn = nameColumn; this.keyColumn = keyColumn; this.valueColumn = valueColumn; this.name = name; + doCommits = commits; setLogger(LogFactory.getLog(getClass())); addErrorLogListener(); // log errors per default } /** - * Build a configuration from a table.- + * Build a configuration from a table. * * @param datasource the datasource to connect to the database * @param table the name of the table containing the configurations @@ -137,6 +168,35 @@ public class DatabaseConfiguration exten } /** + * Creates a new instance of <code>DatabaseConfiguration</code> that + * operates on a database table containing a single configuration only. + * + * @param datasource the <code>DataSource</code> to connect to the database + * @param table the name of the table containing the configurations + * @param keyColumn the column containing the keys of the configuration + * @param valueColumn the column containing the values of the configuration + * @param name the name of the configuration + * @param commits a flag whether the configuration should perform a commit + * after a database update + */ + public DatabaseConfiguration(DataSource datasource, String table, + String keyColumn, String valueColumn, boolean commits) + { + this(datasource, table, null, keyColumn, valueColumn, null, commits); + } + + /** + * Returns a flag whether this configuration performs commits after database + * updates. + * + * @return a flag whether commits are performed + */ + public boolean isDoCommits() + { + return doCommits; + } + + /** * 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 @@ -249,6 +309,7 @@ public class DatabaseConfiguration exten pstmt.setString(index++, String.valueOf(obj)); pstmt.executeUpdate(); + commitIfRequired(conn); } catch (SQLException e) { @@ -431,6 +492,7 @@ public class DatabaseConfiguration exten } pstmt.executeUpdate(); + commitIfRequired(conn); } catch (SQLException e) { @@ -475,6 +537,7 @@ public class DatabaseConfiguration exten } pstmt.executeUpdate(); + commitIfRequired(conn); } catch (SQLException e) { @@ -603,4 +666,20 @@ public class DatabaseConfiguration exten getLogger().error("An error occured on closing the connection", e); } } + + /** + * Performs a commit if needed. This method is called after updates of the + * managed database table. If the configuration should perform commits, it + * does so now. + * + * @param conn the active connection + * @throws SQLException if an error occurs + */ + private void commitIfRequired(Connection conn) throws SQLException + { + if (isDoCommits()) + { + conn.commit(); + } + } } Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/DatabaseConfigurationTestHelper.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/DatabaseConfigurationTestHelper.java?rev=931307&r1=931306&r2=931307&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/DatabaseConfigurationTestHelper.java (original) +++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/DatabaseConfigurationTestHelper.java Tue Apr 6 20:29:51 2010 @@ -17,6 +17,7 @@ package org.apache.commons.configuration; import java.io.FileInputStream; +import java.sql.Connection; import javax.sql.DataSource; @@ -69,48 +70,49 @@ public class DatabaseConfigurationTestHe public static final String CONFIG_NAME = "test"; /** Stores the in-process database. */ - private static HsqlDB hsqlDB = null; + private HsqlDB hsqlDB; /** The data source. */ private DataSource datasource; /** + * The auto-commit mode for the connections created by the managed data + * source. + */ + private boolean autoCommit = true; + + /** + * Returns the auto-commit mode of the connections created by the managed + * data source. + * + * @return the auto-commit mode + */ + public boolean isAutoCommit() + { + return autoCommit; + } + + /** + * Sets the auto-commit mode of the connections created by the managed data + * source. + * + * @param autoCommit the auto-commit mode + */ + public void setAutoCommit(boolean autoCommit) + { + this.autoCommit = autoCommit; + } + + /** * Initializes this helper object. This method can be called from a * <code>setUp()</code> method of a unit test class. It creates the database - * instance if necessary and populates it with test data. + * instance if necessary. * * @throws Exception if an error occurs */ public void setUp() throws Exception { - if (hsqlDB == null) - { - hsqlDB = new HsqlDB(DATABASE_URL, DATABASE_DRIVER, - "conf/testdb.script"); - } - - BasicDataSource datasource = new BasicDataSource(); - datasource.setDriverClassName(DATABASE_DRIVER); - datasource.setUrl(DATABASE_URL); - datasource.setUsername(DATABASE_USERNAME); - datasource.setPassword(DATABASE_PASSWORD); - - this.datasource = datasource; - - // prepare the database - IDatabaseConnection connection = new DatabaseConnection(datasource - .getConnection()); - IDataSet dataSet = new XmlDataSet(new FileInputStream( - "conf/dataset.xml")); - - try - { - DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet); - } - finally - { - connection.close(); - } + hsqlDB = new HsqlDB(DATABASE_URL, DATABASE_DRIVER, "conf/testdb.script"); } /** @@ -121,8 +123,11 @@ public class DatabaseConfigurationTestHe */ public void tearDown() throws Exception { - datasource.getConnection().commit(); - datasource.getConnection().close(); + if (datasource != null) + { + datasource.getConnection().close(); + } + hsqlDB.close(); } /** @@ -132,7 +137,8 @@ public class DatabaseConfigurationTestHe */ public DatabaseConfiguration setUpConfig() { - return new DatabaseConfiguration(datasource, TABLE, COL_KEY, COL_VALUE); + return new DatabaseConfiguration(getDatasource(), TABLE, COL_KEY, + COL_VALUE, !isAutoCommit()); } /** @@ -143,17 +149,80 @@ public class DatabaseConfigurationTestHe */ public DatabaseConfiguration setUpMultiConfig() { - return new DatabaseConfiguration(datasource, TABLE_MULTI, COL_NAME, - COL_KEY, COL_VALUE, CONFIG_NAME); + return setUpMultiConfig(CONFIG_NAME); + } + + /** + * Creates a database configuration that supports multiple configurations in + * a table and sets the specified configuration name. + * + * @param configName the name of the configuration + * @return the configuration + */ + public DatabaseConfiguration setUpMultiConfig(String configName) + { + return new DatabaseConfiguration(getDatasource(), TABLE_MULTI, + COL_NAME, COL_KEY, COL_VALUE, configName, !isAutoCommit()); } /** - * Returns the <code>DataSource</code> managed by this class. + * Returns the <code>DataSource</code> managed by this class. The data + * source is created on first access. * * @return the <code>DataSource</code> */ public DataSource getDatasource() { + if (datasource == null) + { + try + { + datasource = setUpDataSource(); + } + catch (Exception ex) + { + throw new ConfigurationRuntimeException( + "Could not create data source", ex); + } + } return datasource; } + + /** + * Creates the internal data source. This method also initializes the + * database. + * + * @return the data source + * @throws Exception if an error occurs + */ + private DataSource setUpDataSource() throws Exception + { + BasicDataSource ds = new BasicDataSource(); + ds.setDriverClassName(DATABASE_DRIVER); + ds.setUrl(DATABASE_URL); + ds.setUsername(DATABASE_USERNAME); + ds.setPassword(DATABASE_PASSWORD); + ds.setDefaultAutoCommit(isAutoCommit()); + + // prepare the database + Connection conn = ds.getConnection(); + IDatabaseConnection connection = new DatabaseConnection(conn); + IDataSet dataSet = new XmlDataSet(new FileInputStream( + "conf/dataset.xml")); + + try + { + DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet); + } + finally + { + if (!isAutoCommit()) + { + conn.commit(); + } + connection.close(); + } + + return ds; + } } Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java?rev=931307&r1=931306&r2=931307&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java (original) +++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDatabaseConfiguration.java Tue Apr 6 20:29:51 2010 @@ -37,6 +37,9 @@ import org.apache.commons.configuration. */ public class TestDatabaseConfiguration extends TestCase { + /** Constant for another configuration name. */ + private static final String CONFIG_NAME2 = "anotherTestConfig"; + /** An error listener for testing whether internal errors occurred.*/ private ConfigurationErrorListenerImpl listener; @@ -83,17 +86,6 @@ public class TestDatabaseConfiguration e } /** - * Creates a database configuration that supports multiple configurations in - * a table with default values. - * - * @return the configuration - */ - private DatabaseConfiguration setUpMultiConfig() - { - return helper.setUpMultiConfig(); - } - - /** * Creates an error listener and adds it to the specified configuration. * * @param config the configuration @@ -139,17 +131,55 @@ public class TestDatabaseConfiguration e listener = null; // mark as checked } + /** + * Tests the default value of the doCommits property. + */ + public void testDoCommitsDefault() + { + DatabaseConfiguration config = new DatabaseConfiguration(helper + .getDatasource(), DatabaseConfigurationTestHelper.TABLE, + DatabaseConfigurationTestHelper.COL_KEY, + DatabaseConfigurationTestHelper.COL_VALUE); + assertFalse("Wrong commits flag", config.isDoCommits()); + } + + /** + * Tests the default value of the doCommits property for multiple + * configurations in a table. + */ + public void testDoCommitsDefaultMulti() + { + DatabaseConfiguration config = new DatabaseConfiguration(helper + .getDatasource(), DatabaseConfigurationTestHelper.TABLE, + DatabaseConfigurationTestHelper.COL_NAME, + DatabaseConfigurationTestHelper.COL_KEY, + DatabaseConfigurationTestHelper.COL_VALUE, + DatabaseConfigurationTestHelper.CONFIG_NAME); + assertFalse("Wrong commits flag", config.isDoCommits()); + } + public void testAddPropertyDirectSingle() { - DatabaseConfiguration config = setUpConfig(); + 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. + */ + public void testAddPropertyDirectCommit() + { + helper.setAutoCommit(false); + DatabaseConfiguration config = helper.setUpConfig(); + config.addPropertyDirect("key", "value"); + assertTrue("missing property", config.containsKey("key")); + } + public void testAddPropertyDirectMultiple() { - DatabaseConfiguration config = setUpMultiConfig(); + DatabaseConfiguration config = helper.setUpMultiConfig(); config.addPropertyDirect("key", "value"); assertTrue("missing property", config.containsKey("key")); @@ -157,7 +187,7 @@ public class TestDatabaseConfiguration e public void testAddNonStringProperty() { - DatabaseConfiguration config = setUpConfig(); + DatabaseConfiguration config = helper.setUpConfig(); config.addPropertyDirect("boolean", Boolean.TRUE); assertTrue("missing property", config.containsKey("boolean")); @@ -174,7 +204,7 @@ public class TestDatabaseConfiguration e public void testGetPropertyDirectMultiple() { - Configuration config = setUpMultiConfig(); + Configuration config = helper.setUpMultiConfig(); assertEquals("property1", "value1", config.getProperty("key1")); assertEquals("property2", "value2", config.getProperty("key2")); @@ -183,23 +213,49 @@ public class TestDatabaseConfiguration e public void testClearPropertySingle() { - Configuration config = setUpConfig(); - config.clearProperty("key"); + Configuration config = helper.setUpConfig(); + config.clearProperty("key1"); - assertFalse("property not cleared", config.containsKey("key")); + assertFalse("property not cleared", config.containsKey("key1")); } public void testClearPropertyMultiple() { - Configuration config = setUpMultiConfig(); - config.clearProperty("key"); + Configuration config = helper.setUpMultiConfig(); + config.clearProperty("key1"); + + assertFalse("property not cleared", config.containsKey("key1")); + } + + /** + * Tests that another configuration is not affected when clearing + * properties. + */ + public void testClearPropertyMultipleOtherConfig() + { + DatabaseConfiguration config = helper.setUpMultiConfig(); + DatabaseConfiguration config2 = helper.setUpMultiConfig(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")); + } - assertFalse("property not cleared", config.containsKey("key")); + /** + * Tests whether a commit is performed after a property was cleared. + */ + public void testClearPropertyCommit() + { + helper.setAutoCommit(false); + Configuration config = helper.setUpConfig(); + config.clearProperty("key1"); + assertFalse("property not cleared", config.containsKey("key1")); } public void testClearSingle() { - Configuration config = setUpConfig(); + Configuration config = helper.setUpConfig(); config.clear(); assertTrue("configuration is not cleared", config.isEmpty()); @@ -207,12 +263,23 @@ public class TestDatabaseConfiguration e public void testClearMultiple() { - Configuration config = setUpMultiConfig(); + Configuration config = helper.setUpMultiConfig(); config.clear(); assertTrue("configuration is not cleared", config.isEmpty()); } + /** + * Tests whether a commit is performed after a clear operation. + */ + public void testClearCommit() + { + helper.setAutoCommit(false); + Configuration config = helper.setUpConfig(); + config.clear(); + assertTrue("configuration is not cleared", config.isEmpty()); + } + public void testGetKeysSingle() { Configuration config = setUpConfig(); @@ -224,7 +291,7 @@ public class TestDatabaseConfiguration e public void testGetKeysMultiple() { - Configuration config = setUpMultiConfig(); + Configuration config = helper.setUpMultiConfig(); Iterator it = config.getKeys(); assertEquals("1st key", "key1", it.next()); @@ -240,7 +307,7 @@ public class TestDatabaseConfiguration e public void testContainsKeyMultiple() { - Configuration config = setUpMultiConfig(); + Configuration config = helper.setUpMultiConfig(); assertTrue("missing key1", config.containsKey("key1")); assertTrue("missing key2", config.containsKey("key2")); } @@ -253,7 +320,7 @@ public class TestDatabaseConfiguration e public void testIsEmptyMultiple() { - Configuration config1 = setUpMultiConfig(); + Configuration config1 = helper.setUpMultiConfig(); assertFalse("The configuration named 'test' is empty", config1.isEmpty()); Configuration config2 = new DatabaseConfiguration(helper.getDatasource(), DatabaseConfigurationTestHelper.TABLE_MULTI, DatabaseConfigurationTestHelper.COL_NAME, DatabaseConfigurationTestHelper.COL_KEY, DatabaseConfigurationTestHelper.COL_VALUE, "testIsEmpty"); @@ -407,7 +474,7 @@ public class TestDatabaseConfiguration e */ public void testSetPropertyWithDelimiter() { - DatabaseConfiguration config = setUpMultiConfig(); + DatabaseConfiguration config = helper.setUpMultiConfig(); config.setListDelimiter(';'); config.setProperty("keyList", "1;2;3"); String[] values = config.getStringArray("keyList"); Modified: commons/proper/configuration/trunk/xdocs/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/xdocs/changes.xml?rev=931307&r1=931306&r2=931307&view=diff ============================================================================== --- commons/proper/configuration/trunk/xdocs/changes.xml (original) +++ commons/proper/configuration/trunk/xdocs/changes.xml Tue Apr 6 20:29:51 2010 @@ -26,6 +26,11 @@ <action dev="oheger" type="fix" issue="CONFIGURATION-413" due-to="Alexander Prishchepov"> SubsetConfiguration now produces correct events. </action> + <action dev="oheger" type="add" issue="CONFIGURATION-412"> + DatabaseConfiguration can now be instructed to perform a commit after an + update of the managed database table. This makes it usable in + environments where the connections do not use auto-commit mode. + </action> <action dev="oheger" type="fix" issue="CONFIGURATION-409"> HierarchicalINIConfiguration now correctly saves sections whose name contains delimiter characters.