Author: oheger
Date: Mon Apr  8 19:57:35 2013
New Revision: 1465754

URL: http://svn.apache.org/r1465754
Log:
Reworked DatabaseConfiguration.

A template approach is now used for executing SQL operations. This
reduces code duplication.

Modified:
    
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java
    
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDatabaseConfiguration.java

Modified: 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java?rev=1465754&r1=1465753&r2=1465754&view=diff
==============================================================================
--- 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java
 (original)
+++ 
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java
 Mon Apr  8 19:57:35 2013
@@ -87,6 +87,21 @@ import org.apache.commons.logging.LogFac
  */
 public class DatabaseConfiguration extends AbstractConfiguration
 {
+    /** Constant for the statement used by getProperty.*/
+    private static final String SQL_GET_PROPERTY = "SELECT * FROM %s WHERE %s 
=?";
+
+    /** Constant for the statement used by isEmpty.*/
+    private static final String SQL_IS_EMPTY = "SELECT count(*) FROM %s WHERE 
1 = 1";
+
+    /** Constant for the statement used by clearProperty.*/
+    private static final String SQL_CLEAR_PROPERTY = "DELETE FROM %s WHERE %s 
=?";
+
+    /** Constant for the statement used by clear.*/
+    private static final String SQL_CLEAR = "DELETE FROM %s WHERE 1 = 1";
+
+    /** Constant for the statement used by getKeys.*/
+    private static final String SQL_GET_KEYS = "SELECT DISTINCT %s FROM %s 
WHERE 1 = 1";
+
     /** The datasource to connect to the database. */
     private final DataSource datasource;
 
@@ -204,71 +219,47 @@ public class DatabaseConfiguration exten
      * @param key the key of the desired property
      * @return the value of this property
      */
-    public Object getProperty(String key)
+    public Object getProperty(final String key)
     {
-        Object result = null;
-
-        // build the query
-        StringBuilder query = new StringBuilder("SELECT * FROM ");
-        query.append(table).append(" WHERE ");
-        query.append(keyColumn).append("=?");
-        if (nameColumn != null)
+        JdbcOperation<Object> op = new 
JdbcOperation<Object>(EVENT_READ_PROPERTY, key, null)
         {
-            query.append(" AND " + nameColumn + "=?");
-        }
-
-        Connection conn = null;
-        PreparedStatement pstmt = null;
-        ResultSet rs = null;
-
-        try
-        {
-            conn = getConnection();
-
-            // bind the parameters
-            pstmt = conn.prepareStatement(query.toString());
-            pstmt.setString(1, key);
-            if (nameColumn != null)
+            @Override
+            protected Object performOperation() throws SQLException
             {
-                pstmt.setString(2, name);
-            }
+                ResultSet rs = openResultSet(String.format(
+                        SQL_GET_PROPERTY, table, keyColumn), true, key);
 
-            rs = pstmt.executeQuery();
+                List<Object> results = new ArrayList<Object>();
+                while (rs.next())
+                {
+                    Object value = rs.getObject(valueColumn);
+                    if (isDelimiterParsingDisabled())
+                    {
+                        results.add(value);
+                    }
+                    else
+                    {
+                        // Split value if it contains the list delimiter
+                        Iterator<?> it = PropertyConverter.toIterator(value, 
getListDelimiter());
+                        while (it.hasNext())
+                        {
+                            results.add(it.next());
+                        }
+                    }
+                }
 
-            List<Object> results = new ArrayList<Object>();
-            while (rs.next())
-            {
-                Object value = rs.getObject(valueColumn);
-                if (isDelimiterParsingDisabled())
+                if (!results.isEmpty())
                 {
-                    results.add(value);
+                    return (results.size() > 1) ? results : results.get(0);
                 }
                 else
                 {
-                    // Split value if it contains the list delimiter
-                    Iterator<?> it = PropertyConverter.toIterator(value, 
getListDelimiter());
-                    while (it.hasNext())
-                    {
-                        results.add(it.next());
-                    }
+                    return null;
                 }
             }
+        };
 
-            if (!results.isEmpty())
-            {
-                result = (results.size() > 1) ? results : results.get(0);
-            }
-        }
-        catch (SQLException e)
-        {
-            fireError(EVENT_READ_PROPERTY, key, null, e);
-        }
-        finally
-        {
-            close(conn, pstmt, rs);
-        }
-
-        return result;
+        return op.execute();
     }
 
     /**
@@ -282,48 +273,39 @@ public class DatabaseConfiguration exten
      * @param obj the value of the property to add
      */
     @Override
-    protected void addPropertyDirect(String key, Object obj)
+    protected void addPropertyDirect(final String key, final Object obj)
     {
-        // build the query
-        StringBuilder query = new StringBuilder("INSERT INTO " + table);
-        if (nameColumn != null)
-        {
-            query.append(" (" + nameColumn + ", " + keyColumn + ", " + 
valueColumn + ") VALUES (?, ?, ?)");
-        }
-        else
+        new JdbcOperation<Void>(EVENT_ADD_PROPERTY, key, obj)
         {
-            query.append(" (" + keyColumn + ", " + valueColumn + ") VALUES (?, 
?)");
-        }
-
-        Connection conn = null;
-        PreparedStatement pstmt = null;
+            @Override
+            protected Void performOperation() throws SQLException
+            {
+                StringBuilder query = new StringBuilder("INSERT INTO ");
+                query.append(table).append(" (");
+                query.append(keyColumn).append(", ");
+                query.append(valueColumn);
+                if (nameColumn != null)
+                {
+                    query.append(", ").append(nameColumn);
+                }
+                query.append(") VALUES (?, ?");
+                if (nameColumn != null)
+                {
+                    query.append(", ?");
+                }
+                query.append(")");
 
-        try
-        {
-            conn = getConnection();
+                PreparedStatement pstmt = initStatement(query.toString(),
+                        false, key, String.valueOf(obj));
+                if (nameColumn != null)
+                {
+                    pstmt.setString(3, name);
+                }
 
-            // bind the parameters
-            pstmt = conn.prepareStatement(query.toString());
-            int index = 1;
-            if (nameColumn != null)
-            {
-                pstmt.setString(index++, name);
+                pstmt.executeUpdate();
+                return null;
             }
-            pstmt.setString(index++, key);
-            pstmt.setString(index++, String.valueOf(obj));
-
-            pstmt.executeUpdate();
-            commitIfRequired(conn);
-        }
-        catch (SQLException e)
-        {
-            fireError(EVENT_ADD_PROPERTY, key, obj, e);
-        }
-        finally
-        {
-            // clean up
-            close(conn, pstmt, null);
-        }
+        }.execute();
     }
 
     /**
@@ -367,48 +349,21 @@ public class DatabaseConfiguration exten
      */
     public boolean isEmpty()
     {
-        boolean empty = true;
 
-        // build the query
-        StringBuilder query = new StringBuilder("SELECT count(*) FROM " + 
table);
-        if (nameColumn != null)
+        JdbcOperation<Integer> op = new 
JdbcOperation<Integer>(EVENT_READ_PROPERTY, null, null)
         {
-            query.append(" WHERE " + nameColumn + "=?");
-        }
-
-        Connection conn = null;
-        PreparedStatement pstmt = null;
-        ResultSet rs = null;
-
-        try
-        {
-            conn = getConnection();
-
-            // bind the parameters
-            pstmt = conn.prepareStatement(query.toString());
-            if (nameColumn != null)
+            @Override
+            protected Integer performOperation() throws SQLException
             {
-                pstmt.setString(1, name);
-            }
-
-            rs = pstmt.executeQuery();
+                ResultSet rs = openResultSet(String.format(
+                        SQL_IS_EMPTY, table), true);
 
-            if (rs.next())
-            {
-                empty = rs.getInt(1) == 0;
+                return rs.next() ? Integer.valueOf(rs.getInt(1)) : null;
             }
-        }
-        catch (SQLException e)
-        {
-            fireError(EVENT_READ_PROPERTY, null, null, e);
-        }
-        finally
-        {
-            // clean up
-            close(conn, pstmt, rs);
-        }
+        };
 
-        return empty;
+        Integer count = op.execute();
+        return count == null || count.intValue() == 0;
     }
 
     /**
@@ -421,48 +376,23 @@ public class DatabaseConfiguration exten
      * @param key the key to be checked
      * @return a flag whether this key is defined
      */
-    public boolean containsKey(String key)
+    public boolean containsKey(final String key)
     {
-        boolean found = false;
-
-        // build the query
-        StringBuilder query = new StringBuilder("SELECT * FROM " + table + " 
WHERE " + keyColumn + "=?");
-        if (nameColumn != null)
+        JdbcOperation<Boolean> op = new 
JdbcOperation<Boolean>(EVENT_READ_PROPERTY, key, null)
         {
-            query.append(" AND " + nameColumn + "=?");
-        }
-
-        Connection conn = null;
-        PreparedStatement pstmt = null;
-        ResultSet rs = null;
-
-        try
-        {
-            conn = getConnection();
-
-            // bind the parameters
-            pstmt = conn.prepareStatement(query.toString());
-            pstmt.setString(1, key);
-            if (nameColumn != null)
+            @Override
+            protected Boolean performOperation() throws SQLException
             {
-                pstmt.setString(2, name);
-            }
-
-            rs = pstmt.executeQuery();
+                PreparedStatement pstmt = initStatement(String.format(
+                        SQL_GET_PROPERTY, table, keyColumn), true, key);
+                ResultSet rs = pstmt.executeQuery();
 
-            found = rs.next();
-        }
-        catch (SQLException e)
-        {
-            fireError(EVENT_READ_PROPERTY, key, null, e);
-        }
-        finally
-        {
-            // clean up
-            close(conn, pstmt, rs);
-        }
+                return rs.next();
+            }
+        };
 
-        return found;
+        Boolean result = op.execute();
+        return result != null && result.booleanValue();
     }
 
     /**
@@ -475,42 +405,19 @@ public class DatabaseConfiguration exten
      * @param key the key of the property to be removed
      */
     @Override
-    protected void clearPropertyDirect(String key)
+    protected void clearPropertyDirect(final String key)
     {
-        // build the query
-        StringBuilder query = new StringBuilder("DELETE FROM " + table + " 
WHERE " + keyColumn + "=?");
-        if (nameColumn != null)
+        new JdbcOperation<Void>(EVENT_CLEAR_PROPERTY, key, null)
         {
-            query.append(" AND " + nameColumn + "=?");
-        }
-
-        Connection conn = null;
-        PreparedStatement pstmt = null;
-
-        try
-        {
-            conn = getConnection();
-
-            // bind the parameters
-            pstmt = conn.prepareStatement(query.toString());
-            pstmt.setString(1, key);
-            if (nameColumn != null)
+            @Override
+            protected Void performOperation() throws SQLException
             {
-                pstmt.setString(2, name);
+                PreparedStatement ps = initStatement(String.format(
+                        SQL_CLEAR_PROPERTY, table, keyColumn), true, key);
+                ps.executeUpdate();
+                return null;
             }
-
-            pstmt.executeUpdate();
-            commitIfRequired(conn);
-        }
-        catch (SQLException e)
-        {
-            fireError(EVENT_CLEAR_PROPERTY, key, null, e);
-        }
-        finally
-        {
-            // clean up
-            close(conn, pstmt, null);
-        }
+        }.execute();
     }
 
     /**
@@ -524,39 +431,16 @@ public class DatabaseConfiguration exten
     public void clear()
     {
         fireEvent(EVENT_CLEAR, null, null, true);
-        // build the query
-        StringBuilder query = new StringBuilder("DELETE FROM " + table);
-        if (nameColumn != null)
+        new JdbcOperation<Void>(EVENT_CLEAR, null, null)
         {
-            query.append(" WHERE " + nameColumn + "=?");
-        }
-
-        Connection conn = null;
-        PreparedStatement pstmt = null;
-
-        try
-        {
-            conn = getConnection();
-
-            // bind the parameters
-            pstmt = conn.prepareStatement(query.toString());
-            if (nameColumn != null)
+            @Override
+            protected Void performOperation() throws SQLException
             {
-                pstmt.setString(1, name);
+                initStatement(String.format(SQL_CLEAR,
+                        table), true).executeUpdate();
+                return null;
             }
-
-            pstmt.executeUpdate();
-            commitIfRequired(conn);
-        }
-        catch (SQLException e)
-        {
-            fireError(EVENT_CLEAR, null, null, e);
-        }
-        finally
-        {
-            // clean up
-            close(conn, pstmt, null);
-        }
+        }.execute();
         fireEvent(EVENT_CLEAR, null, null, false);
     }
 
@@ -572,46 +456,22 @@ public class DatabaseConfiguration exten
      */
     public Iterator<String> getKeys()
     {
-        Collection<String> keys = new ArrayList<String>();
-
-        // build the query
-        StringBuilder query = new StringBuilder("SELECT DISTINCT " + keyColumn 
+ " FROM " + table);
-        if (nameColumn != null)
+        final Collection<String> keys = new ArrayList<String>();
+        new JdbcOperation<Collection<String>>(EVENT_READ_PROPERTY, null, null)
         {
-            query.append(" WHERE " + nameColumn + "=?");
-        }
-
-        Connection conn = null;
-        PreparedStatement pstmt = null;
-        ResultSet rs = null;
-
-        try
-        {
-            conn = getConnection();
-
-            // bind the parameters
-            pstmt = conn.prepareStatement(query.toString());
-            if (nameColumn != null)
+            @Override
+            protected Collection<String> performOperation() throws SQLException
             {
-                pstmt.setString(1, name);
-            }
-
-            rs = pstmt.executeQuery();
+                ResultSet rs = openResultSet(String.format(
+                        SQL_GET_KEYS, keyColumn, table), true);
 
-            while (rs.next())
-            {
-                keys.add(rs.getString(1));
+                while (rs.next())
+                {
+                    keys.add(rs.getString(1));
+                }
+                return keys;
             }
-        }
-        catch (SQLException e)
-        {
-            fireError(EVENT_READ_PROPERTY, null, null, e);
-        }
-        finally
-        {
-            // clean up
-            close(conn, pstmt, rs);
-        }
+        }.execute();
 
         return keys.iterator();
     }
@@ -628,23 +488,6 @@ public class DatabaseConfiguration exten
     }
 
     /**
-     * Returns a {@code Connection} object. This method is called when
-     * ever the database is to be accessed. This implementation returns a
-     * connection from the current {@code DataSource}.
-     *
-     * @return the {@code Connection} object to be used
-     * @throws SQLException if an error occurs
-     * @since 1.4
-     * @deprecated Use a custom data source to change the connection used by 
the
-     * class. To be removed in Commons Configuration 2.0
-     */
-    @Deprecated
-    protected Connection getConnection() throws SQLException
-    {
-        return getDatasource().getConnection();
-    }
-
-    /**
      * Close the specified database objects.
      * Avoid closing if null and hide any SQLExceptions that occur.
      *
@@ -692,18 +535,180 @@ public class DatabaseConfiguration exten
     }
 
     /**
-     * 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())
+     * An internally used helper class for simplifying database access through
+     * plain JDBC. This class provides a simple framework for creating and
+     * executing a JDBC statement. It especially takes care of proper handling
+     * of JDBC resources even in case of an error.
+     * @param <T> the type of the results produced by a JDBC operation
+     */
+    private abstract class JdbcOperation<T>
+    {
+        /** Stores the connection. */
+        private Connection conn;
+
+        /** Stores the statement. */
+        private PreparedStatement pstmt;
+
+        /** Stores the result set. */
+        private ResultSet resultSet;
+
+        /** The type of the event to send in case of an error. */
+        private final int errorEventType;
+
+        /** The property name for an error event. */
+        private final String errorPropertyName;
+
+        /** The property value for an error event. */
+        private final Object errorPropertyValue;
+
+        /**
+         * Creates a new instance of {@code JdbcOperation} and initializes
+         * the properties related to the error event.
+         *
+         * @param errEvType the type of the error event
+         * @param errPropName the property name for the error event
+         * @param errPropVal the property value for the error event
+         */
+        protected JdbcOperation(int errEvType, String errPropName,
+                Object errPropVal)
+        {
+            errorEventType = errEvType;
+            errorPropertyName = errPropName;
+            errorPropertyValue = errPropVal;
+        }
+
+        /**
+         * Executes this operation. This method obtains a database connection
+         * and then delegates to {@code performOperation()}. Afterwards it
+         * performs the necessary clean up. Exceptions that are thrown during
+         * the JDBC operation are caught and transformed into configuration
+         * error events.
+         *
+         * @return the result of the operation
+         */
+        public T execute()
+        {
+            T result = null;
+
+            try
+            {
+                conn = getDatasource().getConnection();
+                result = performOperation();
+
+                if (isDoCommits())
+                {
+                    conn.commit();
+                }
+            }
+            catch (SQLException e)
+            {
+                fireError(errorEventType, errorPropertyName,
+                        errorPropertyValue, e);
+            }
+            finally
+            {
+                close(conn, pstmt, resultSet);
+            }
+
+            return result;
+        }
+
+        /**
+         * Returns the current connection. This method can be called while
+         * {@code execute()} is running. It returns <b>null</b> otherwise.
+         *
+         * @return the current connection
+         */
+        protected Connection getConnection()
+        {
+            return conn;
+        }
+
+        /**
+         * Creates a {@code PreparedStatement} object for executing the
+         * specified SQL statement.
+         *
+         * @param sql the statement to be executed
+         * @param nameCol a flag whether the name column should be taken into
+         *        account
+         * @return the prepared statement object
+         * @throws SQLException if an SQL error occurs
+         */
+        protected PreparedStatement createStatement(String sql, boolean 
nameCol)
+                throws SQLException
+        {
+            String statement;
+            if (nameCol && nameColumn != null)
+            {
+                StringBuilder buf = new StringBuilder(sql);
+                buf.append(" AND ").append(nameColumn).append("=?");
+                statement = buf.toString();
+            }
+            else
+            {
+                statement = sql;
+            }
+
+            pstmt = getConnection().prepareStatement(statement);
+            return pstmt;
+        }
+
+        /**
+         * Creates an initializes a {@code PreparedStatement} object for
+         * executing an SQL statement. This method first calls
+         * {@code createStatement()} for creating the statement and then
+         * initializes the statement's parameters.
+         *
+         * @param sql the statement to be executed
+         * @param nameCol a flag whether the name column should be taken into
+         *        account
+         * @param params the parameters for the statement
+         * @return the initialized statement object
+         * @throws SQLException if an SQL error occurs
+         */
+        protected PreparedStatement initStatement(String sql, boolean nameCol,
+                Object... params) throws SQLException
         {
-            conn.commit();
+            PreparedStatement ps = createStatement(sql, nameCol);
+
+            int idx = 1;
+            for (Object param : params)
+            {
+                ps.setObject(idx++, param);
+            }
+            if (nameCol && nameColumn != null)
+            {
+                ps.setString(idx, name);
+            }
+
+            return ps;
         }
+
+        /**
+         * Creates a {@code PreparedStatement} for a query, initializes it and
+         * executes it. The resulting {@code ResultSet} is returned.
+         *
+         * @param sql the statement to be executed
+         * @param nameCol a flag whether the name column should be taken into
+         *        account
+         * @param params the parameters for the statement
+         * @return the {@code ResultSet} produced by the query
+         * @throws SQLException if an SQL error occurs
+         */
+        protected ResultSet openResultSet(String sql, boolean nameCol,
+                Object... params) throws SQLException
+        {
+            return initStatement(sql, nameCol, params).executeQuery();
+        }
+
+        /**
+         * Performs the JDBC operation. This method is called by
+         * {@code execute()} after this object has been fully initialized.
+         * Here the actual JDBC logic has to be placed.
+         *
+         * @return the result of the operation
+         * @throws SQLException if an SQL error occurs
+         */
+        protected abstract T performOperation() throws SQLException;
     }
 }

Modified: 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDatabaseConfiguration.java
URL: 
http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDatabaseConfiguration.java?rev=1465754&r1=1465753&r2=1465754&view=diff
==============================================================================
--- 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDatabaseConfiguration.java
 (original)
+++ 
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDatabaseConfiguration.java
 Mon Apr  8 19:57:35 2013
@@ -21,13 +21,13 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.Iterator;
 import java.util.List;
 
 import javax.sql.DataSource;
 
+import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -539,13 +539,25 @@ public class TestDatabaseConfiguration
         }
 
         @Override
-        protected Connection getConnection() throws SQLException
+        public DataSource getDatasource()
         {
             if (failOnConnect)
             {
-                throw new SQLException("Simulated DB error");
+                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.getConnection();
+            return super.getDatasource();
         }
     }
 }


Reply via email to