fixed bug 39601: can't execute DB updates after killing a connection
authorAsier Lostalé <asier.lostale@openbravo.com>
Thu, 08 Nov 2018 10:19:51 +0100
changeset 34947 e834e74a3c1a
parent 34946 0b6bb996e29d
child 34948 2ef2c5233cab
fixed bug 39601: can't execute DB updates after killing a connection

Audit Trail infrastructure (ad_context_info in PG) is initialized each time a
new connection is created. We incorrectly assumed a PooledConnection is always
associated with a single Connection instance, which is not true in case Connection
gets corrupted or killed, in this case Tomcat pool transparently re-associates
a new connection with existent PooledConnection.

We are now caching in PooledConnection actual Connection to detect this case
so it can be initialized again.
modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/ConnectionInitializerInterceptor.java
--- a/modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/ConnectionInitializerInterceptor.java	Fri Nov 02 12:54:48 2018 -0400
+++ b/modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/ConnectionInitializerInterceptor.java	Thu Nov 08 10:19:51 2018 +0100
@@ -18,6 +18,7 @@
  */
 package org.openbravo.apachejdbcconnectionpool;
 
+import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.util.HashMap;
@@ -40,6 +41,7 @@
 
   private static final String SESSION_CONFIG_APPLIED = "OB_INITIALIZED";
   private static final String SESSION_INFO_APPLIED = "SESSION_INFO_INITIALIZED";
+  private static final String CACHED_CONNECTION = "CACHED_CONNECTION";
 
   String rbdms = (String) OBPropertiesProvider.getInstance().getOpenbravoProperties()
       .get("bbdd.rdbms");
@@ -51,9 +53,11 @@
   @Override
   public void reset(ConnectionPool parent, PooledConnection con) {
     if (con != null) {
+      boolean physicalConnectionChanged = hasPhysicalConnectionChanged(con);
+
       HashMap<Object, Object> attributes = con.getAttributes();
       Boolean sessionInfoApplied = (Boolean) attributes.get(SESSION_CONFIG_APPLIED);
-      if (sessionInfoApplied == null || !sessionInfoApplied) {
+      if (physicalConnectionChanged || sessionInfoApplied == null || !sessionInfoApplied) {
         PreparedStatement pstmt = null;
         try {
           final Properties props = OBPropertiesProvider.getInstance().getOpenbravoProperties();
@@ -75,7 +79,7 @@
       }
 
       Boolean sessionInfoInitialized = (Boolean) attributes.get(SESSION_INFO_APPLIED);
-      if (sessionInfoInitialized == null || !sessionInfoInitialized) {
+      if (physicalConnectionChanged || sessionInfoInitialized == null || !sessionInfoInitialized) {
         boolean initialized = false;
         if (isReadOnlyPool(parent)) {
           initialized = true;
@@ -90,6 +94,34 @@
         }
         attributes.put(SESSION_INFO_APPLIED, initialized);
       }
+
+      cachePhysicalConnection(con);
+    }
+  }
+
+  /**
+   * The physical database {@code Connection} associated with a {@code PooledConnection} can change
+   * in case it was invalid when trying to borrow it. We need to track it in order to properly
+   * initialize connections also in this case.
+   */
+  private boolean hasPhysicalConnectionChanged(PooledConnection con) {
+    Connection physicalConn = con.getConnection();
+    if (physicalConn == null) {
+      return false;
+    }
+
+    Connection storedConn = (Connection) con.getAttributes().get(CACHED_CONNECTION);
+    if (storedConn == null) {
+      return false;
+    }
+
+    return physicalConn != storedConn;
+  }
+
+  private void cachePhysicalConnection(PooledConnection con) {
+    Connection storedConn = (Connection) con.getAttributes().get(CACHED_CONNECTION);
+    if (storedConn == null || hasPhysicalConnectionChanged(con)) {
+      con.getAttributes().put(CACHED_CONNECTION, con.getConnection());
     }
   }