# HG changeset patch
# User Asier Lostalé <asier.lostale@openbravo.com>
# Date 1538114588 -7200
#      Fri Sep 28 08:03:08 2018 +0200
# Node ID c3e2feab90493085ab475e40568cae425a0faeda
# Parent  fabe6e99f181df1112da8843ec3c7e54f21273f2
fixed issue 39362: can't create tickets if one failed due to pool without conns

  After a ticket failed to be imported due to the pool is out of connections, no
  new tickets can be created from that terminal until all import entries are set
  again in Initial status.

  Now in this case instead of setting the import entry as Error preventing new
  tickets from the same terminal to be processed, it's kept in Initial status,
  in this way it will be tried to be processed in next cycle, not exposing the
  problem to users.

diff -r fabe6e99f181 -r c3e2feab9049 modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/JdbcExternalConnectionPool.java
--- a/modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/JdbcExternalConnectionPool.java	Tue Sep 25 18:57:02 2018 +0000
+++ b/modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/JdbcExternalConnectionPool.java	Fri Sep 28 08:03:08 2018 +0200
@@ -19,6 +19,7 @@
 package org.openbravo.apachejdbcconnectionpool;
 
 import java.sql.Connection;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -30,6 +31,7 @@
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.tomcat.jdbc.pool.DataSource;
+import org.apache.tomcat.jdbc.pool.PoolExhaustedException;
 import org.apache.tomcat.jdbc.pool.PoolProperties;
 import org.openbravo.base.exception.OBException;
 import org.openbravo.base.session.OBPropertiesProvider;
@@ -47,12 +49,14 @@
  * connection from a pool, close the different pools and other actions.
  */
 public class JdbcExternalConnectionPool extends ExternalConnectionPool {
-
   final static private Logger log = LoggerFactory.getLogger(JdbcExternalConnectionPool.class);
 
   private Map<String, DataSource> availableDataSources = null;
   private DataSource defaultDataSource = null;
 
+  private static final List<Class<? extends Exception>> EXHAUSTED_EXCEPTION = Arrays
+      .asList(PoolExhaustedException.class);
+
   /**
    * This method loads all the interceptors of Apache JDBC Connection Pool injected with weld.
    */
@@ -349,4 +353,10 @@
     }
     super.closePool();
   }
+
+  @Override
+  protected List<Class<? extends Exception>> getExhaustedExceptions() {
+    return EXHAUSTED_EXCEPTION;
+  }
+
 }
diff -r fabe6e99f181 -r c3e2feab9049 src-core/src/org/openbravo/database/ExternalConnectionPool.java
--- a/src-core/src/org/openbravo/database/ExternalConnectionPool.java	Tue Sep 25 18:57:02 2018 +0000
+++ b/src-core/src/org/openbravo/database/ExternalConnectionPool.java	Fri Sep 28 08:03:08 2018 +0200
@@ -18,6 +18,8 @@
 package org.openbravo.database;
 
 import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.log4j.Logger;
@@ -34,6 +36,9 @@
 
   private static ExternalConnectionPool instance;
 
+  private static final String PG_TOO_MANY_CONNECTIONS = "53300";
+  private static final String ORA_CONNECTION_REFUSED = "66000";
+
   /**
    * 
    * @param externalConnectionPoolClassName
@@ -93,4 +98,28 @@
     return getConnection();
   }
 
+  /** {@code Exception}s thrown when trying to create a new connection and pool is exhausted. */
+  protected List<Class<? extends Exception>> getExhaustedExceptions() {
+    return Collections.emptyList();
+  }
+
+  /** Checks if {@code Throwable} was caused by pool not having more connections. */
+  public boolean hasNoConnections(Throwable t) {
+    if (t == null) {
+      return false;
+    }
+
+    boolean isOutOfPhysicalConns;
+    if (t instanceof SQLException) {
+      String state = ((SQLException) t).getSQLState();
+      isOutOfPhysicalConns = PG_TOO_MANY_CONNECTIONS.equals(state)
+          || ORA_CONNECTION_REFUSED.equals(state);
+    } else {
+      isOutOfPhysicalConns = false;
+    }
+
+    return isOutOfPhysicalConns
+        || getExhaustedExceptions().stream().anyMatch(e -> e.isAssignableFrom(t.getClass()))
+        || hasNoConnections(t.getCause());
+  }
 }
diff -r fabe6e99f181 -r c3e2feab9049 src/org/openbravo/service/importprocess/ImportEntryProcessor.java
--- a/src/org/openbravo/service/importprocess/ImportEntryProcessor.java	Tue Sep 25 18:57:02 2018 +0000
+++ b/src/org/openbravo/service/importprocess/ImportEntryProcessor.java	Fri Sep 28 08:03:08 2018 +0200
@@ -38,6 +38,7 @@
 import org.openbravo.dal.core.SessionHandler;
 import org.openbravo.dal.core.TriggerHandler;
 import org.openbravo.dal.service.OBDal;
+import org.openbravo.database.ExternalConnectionPool;
 import org.openbravo.database.SessionInfo;
 import org.openbravo.model.common.enterprise.Organization;
 
@@ -421,11 +422,18 @@
           } catch (Exception ignored) {
           }
 
-          // store the error
-          try {
-            importEntryManager.setImportEntryErrorIndependent(queuedImportEntry.importEntryId, t);
-          } catch (Throwable ignore) {
-            ImportProcessUtils.logError(logger, ignore);
+          ExternalConnectionPool pool = ExternalConnectionPool.getInstance();
+          if (pool != null && pool.hasNoConnections(t)) {
+            // If the exception was caused by not having connections in pool, import entry will be
+            // kept in Initial status to be processed in next cycle if there are connections. We
+            // also break the loop to stop trying to process any other pending entry in this cycle.
+            break;
+          } else {
+            try {
+              importEntryManager.setImportEntryErrorIndependent(queuedImportEntry.importEntryId, t);
+            } catch (Throwable ignore) {
+              ImportProcessUtils.logError(logger, ignore);
+            }
           }
         } finally {
           cleanUpThreadForNextCycle();
