You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2017/06/26 13:37:42 UTC

svn commit: r1799917 - /velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java

Author: cbrisson
Date: Mon Jun 26 13:37:42 2017
New Revision: 1799917

URL: http://svn.apache.org/viewvc?rev=1799917&view=rev
Log:
[engine] Review DataSourceResourceLoader connection, statement and resultset lifecycle

Modified:
    velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java

Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java?rev=1799917&r1=1799916&r2=1799917&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java Mon Jun 26 13:37:42 2017
@@ -29,6 +29,7 @@ import org.apache.commons.lang3.StringUt
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
 import javax.sql.DataSource;
+import java.io.FilterReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
@@ -137,6 +138,44 @@ public class DataSourceResourceLoader ex
     private InitialContext ctx;
     private DataSource dataSource;
 
+    /*
+        Keep connection and prepared statements open. It's not just an optimization:
+        For several engines, the connection, and/or the statement, and/or the result set
+        must be kept open for the reader to be valid.
+     */
+    private Connection connection = null;
+    private PreparedStatement templatePrepStatement = null;
+    private PreparedStatement timestampPrepStatement = null;
+
+    private static class SelfCleaningReader extends FilterReader
+    {
+        private ResultSet resultSet;
+
+        public SelfCleaningReader(Reader reader, ResultSet resultSet)
+        {
+            super(reader);
+            this.resultSet = resultSet;
+        }
+
+        @Override
+        public void close() throws IOException
+        {
+            super.close();
+            try
+            {
+                resultSet.close();
+            }
+            catch (RuntimeException re)
+            {
+                throw re;
+            }
+            catch (Exception e)
+            {
+                // ignore
+            }
+        }
+    }
+
     /**
      * @see ResourceLoader#init(org.apache.velocity.util.ExtProperties)
      */
@@ -215,14 +254,11 @@ public class DataSourceResourceLoader ex
             throw new ResourceNotFoundException("DataSourceResourceLoader: Template name was empty or null");
         }
 
-        Connection conn = null;
         ResultSet rs = null;
-        PreparedStatement ps = null;
         try
         {
-            conn = openDbConnection();
-            ps = getStatement(conn, templateColumn, tableName, keyColumn, name);
-            rs = ps.executeQuery();
+            checkDBConnection();
+            rs = fetchResult(templatePrepStatement, name);
 
             if (rs.next())
             {
@@ -233,7 +269,7 @@ public class DataSourceResourceLoader ex
                             + "template column for '"
                             + name + "' is null");
                 }
-                return reader;
+                return new SelfCleaningReader(reader, rs);
             }
             else
             {
@@ -250,11 +286,6 @@ public class DataSourceResourceLoader ex
 
             log.error(msg, sqle);
             throw new ResourceNotFoundException(msg);
-        } finally
-        {
-            closeResultSet(rs);
-            closeStatement(ps);
-            closeDbConnection(conn);
         }
     }
 
@@ -280,15 +311,12 @@ public class DataSourceResourceLoader ex
         }
         else
         {
-            Connection conn = null;
             ResultSet rs = null;
-            PreparedStatement ps = null;
 
             try
             {
-                conn = openDbConnection();
-                ps = getStatement(conn, timestampColumn, tableName, keyColumn, name);
-                rs = ps.executeQuery();
+                checkDBConnection();
+                rs = fetchResult(timestampPrepStatement, name);
 
                 if (rs.next())
                 {
@@ -314,8 +342,6 @@ public class DataSourceResourceLoader ex
             finally
             {
                 closeResultSet(rs);
-                closeStatement(ps);
-                closeDbConnection(conn);
             }
         }
         return timeStamp;
@@ -325,83 +351,126 @@ public class DataSourceResourceLoader ex
      * Gets connection to the datasource specified through the configuration
      * parameters.
      *
-     * @return connection
      */
-    private Connection openDbConnection() throws NamingException, SQLException
+    private void openDBConnection() throws NamingException, SQLException
     {
-         if (dataSource != null)
-         {
-            return dataSource.getConnection();
-         }
+        if (dataSource == null)
+        {
+            if (ctx == null)
+            {
+                ctx = new InitialContext();
+            }
 
-         if (ctx == null)
-         {
-            ctx = new InitialContext();
-         }
+            dataSource = (DataSource) ctx.lookup(dataSourceName);
+        }
 
-         dataSource = (DataSource) ctx.lookup(dataSourceName);
+        if (connection != null)
+        {
+            closeDBConnection();
+        }
+
+        connection = dataSource.getConnection();
+        templatePrepStatement = prepareStatement(connection, templateColumn, tableName, keyColumn);
+        timestampPrepStatement = prepareStatement(connection, timestampColumn, tableName, keyColumn);
+    }
 
-         return dataSource.getConnection();
-     }
+    /**
+     * Checks the connection is valid
+     *
+     */
+    private void checkDBConnection() throws NamingException, SQLException
+    {
+        if (connection == null || !connection.isValid(0))
+        {
+            openDBConnection();
+        }
+    }
+
+    /**
+     * Close DB connection on finalization
+     *
+     * @throws Throwable
+     */
+    protected void finalize()
+        throws Throwable
+    {
+        closeDBConnection();
+    }
 
     /**
-     * Closes connection to the datasource
+     * Closes the prepared statements and the connection to the datasource
      */
-    private void closeDbConnection(final Connection conn)
+    private void closeDBConnection()
     {
-        if (conn != null)
+        if (templatePrepStatement != null)
         {
             try
             {
-                conn.close();
+                templatePrepStatement.close();
             }
             catch (RuntimeException re)
             {
                 throw re;
             }
-            catch (Exception e)
+            catch (SQLException e)
+            {
+                // ignore
+            }
+            finally
             {
-                String msg = "DataSourceResourceLoader: problem when closing connection";
-                log.error(msg, e);
-                throw new VelocityException(msg, e);
+                templatePrepStatement = null;
             }
         }
-    }
-
-    /**
-     * Closes the result set.
-     */
-    private void closeResultSet(final ResultSet rs)
-    {
-        if (rs != null)
+        if (timestampPrepStatement != null)
         {
             try
             {
-                rs.close();
+                timestampPrepStatement.close();
             }
             catch (RuntimeException re)
             {
                 throw re;
             }
-            catch (Exception e)
+            catch (SQLException e)
+            {
+                // ignore
+            }
+            finally
+            {
+                timestampPrepStatement = null;
+            }
+        }
+        if (connection != null)
+        {
+            try
+            {
+                connection.close();
+            }
+            catch (RuntimeException re)
             {
-                String msg = "DataSourceResourceLoader: problem when closing result set";
-                log.error(msg, e);
-                throw new VelocityException(msg, e);
+                throw re;
+            }
+            catch (SQLException e)
+            {
+                // ignore
+            }
+            finally
+            {
+                connection = null;
             }
         }
     }
 
     /**
-     * Closes the PreparedStatement.
+     * Closes the result set.
      */
-    private void closeStatement(PreparedStatement ps)
+    private void closeResultSet(final ResultSet rs)
     {
-        if (ps != null)
+        if (rs != null)
         {
             try
             {
-                ps.close();
+//                rs.close();
             }
             catch (RuntimeException re)
             {
@@ -409,14 +478,11 @@ public class DataSourceResourceLoader ex
             }
             catch (Exception e)
             {
-                String msg = "DataSourceResourceLoader: problem when closing PreparedStatement ";
-                log.error(msg, e);
-                throw new VelocityException(msg, e);
+                // ignore
             }
         }
     }
 
-
     /**
      * Creates the following PreparedStatement query :
      * <br>
@@ -429,18 +495,35 @@ public class DataSourceResourceLoader ex
      * @param columnNames columns to fetch from datasource
      * @param tableName table to fetch from
      * @param keyColumn column whose value should match templateName
-     * @param templateName name of template to fetch
      * @return PreparedStatement
      */
-    protected PreparedStatement getStatement(final Connection conn,
-                               final String columnNames,
-                               final String tableName,
-                               final String keyColumn,
-                               final String templateName) throws SQLException
+    protected PreparedStatement prepareStatement(
+        final Connection conn,
+        final String columnNames,
+        final String tableName,
+        final String keyColumn
+    ) throws SQLException
     {
         PreparedStatement ps = conn.prepareStatement("SELECT " + columnNames + " FROM "+ tableName + " WHERE " + keyColumn + " = ?");
-        ps.setString(1, templateName);
         return ps;
     }
 
+    /**
+     * Fetches the result for a given template name.
+     * Inherit this method if there is any calculation to perform on the template name.
+     *
+     * @param ps target prepared statement
+     * @param templateName input template name
+     * @return result set
+     * @throws SQLException
+     */
+    protected ResultSet fetchResult(
+        final PreparedStatement ps,
+        final String templateName
+    ) throws SQLException
+    {
+        ps.setString(1, templateName);
+        return ps.executeQuery();
+    }
+
 }