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.


Reply via email to