details: https://code.openbravo.com/erp/devel/pi/rev/d8c618c02470 changeset: 34832:d8c618c02470 user: Asier Lostalé <asier.lostale <at> openbravo.com> date: Fri Sep 28 08:03:08 2018 +0200 summary: 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. details: https://code.openbravo.com/erp/devel/pi/rev/ba9ef09516cf changeset: 34833:ba9ef09516cf user: Asier Lostalé <asier.lostale <at> openbravo.com> date: Tue Oct 02 09:43:11 2018 +0200 summary: related to issue 39362: added test cases covering no DB conns detection diffstat: modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/JdbcExternalConnectionPool.java | 12 +- src-core/src/org/openbravo/database/ExternalConnectionPool.java | 29 +++ src-test/src/org/openbravo/test/AllAntTaskTests.java | 2 + src-test/src/org/openbravo/test/db/pool/PoolHasNoConnectionsDetection.java | 73 ++++++++++ src/org/openbravo/service/importprocess/ImportEntryProcessor.java | 18 +- 5 files changed, 128 insertions(+), 6 deletions(-) diffs (228 lines): diff -r 6ce7922086d7 -r ba9ef09516cf modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/JdbcExternalConnectionPool.java --- a/modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/JdbcExternalConnectionPool.java Wed Oct 03 17:18:12 2018 +0200 +++ b/modules/org.openbravo.apachejdbcconnectionpool/src/org/openbravo/apachejdbcconnectionpool/JdbcExternalConnectionPool.java Tue Oct 02 09:43:11 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 6ce7922086d7 -r ba9ef09516cf src-core/src/org/openbravo/database/ExternalConnectionPool.java --- a/src-core/src/org/openbravo/database/ExternalConnectionPool.java Wed Oct 03 17:18:12 2018 +0200 +++ b/src-core/src/org/openbravo/database/ExternalConnectionPool.java Tue Oct 02 09:43:11 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 6ce7922086d7 -r ba9ef09516cf src-test/src/org/openbravo/test/AllAntTaskTests.java --- a/src-test/src/org/openbravo/test/AllAntTaskTests.java Wed Oct 03 17:18:12 2018 +0200 +++ b/src-test/src/org/openbravo/test/AllAntTaskTests.java Tue Oct 02 09:43:11 2018 +0200 @@ -68,6 +68,7 @@ import org.openbravo.test.db.model.functions.ADOrgTreeTest; import org.openbravo.test.db.model.functions.Ad_isorgincludedTest; import org.openbravo.test.db.model.functions.SqlCallableStatement; +import org.openbravo.test.db.pool.PoolHasNoConnectionsDetection; import org.openbravo.test.expression.EvaluationTest; import org.openbravo.test.expression.OBBindingsTest; import org.openbravo.test.generalsetup.enterprise.organization.ADOrgPersistInfoTestSuite; @@ -274,6 +275,7 @@ // db SqlCallableStatement.class, // + PoolHasNoConnectionsDetection.class, // // grid configuration ViewGenerationWithDifferentConfigLevelTest.class, // diff -r 6ce7922086d7 -r ba9ef09516cf src-test/src/org/openbravo/test/db/pool/PoolHasNoConnectionsDetection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src-test/src/org/openbravo/test/db/pool/PoolHasNoConnectionsDetection.java Tue Oct 02 09:43:11 2018 +0200 @@ -0,0 +1,73 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2018 Openbravo SLU + * All Rights Reserved. + ************************************************************************ + */ + +package org.openbravo.test.db.pool; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.sql.SQLException; + +import org.apache.tomcat.jdbc.pool.PoolExhaustedException; +import org.junit.Test; +import org.openbravo.apachejdbcconnectionpool.JdbcExternalConnectionPool; +import org.openbravo.base.exception.OBException; + +/** Test cases covering checking of Exceptions thrown when DB pool gets out of connections */ +public class PoolHasNoConnectionsDetection { + private JdbcExternalConnectionPool pool = new JdbcExternalConnectionPool(); + + @Test + public void tomcatPoolExhaustedShouldBeDetected() { + assertThat("PoolExhaustedException should be detected as no connections exception", + pool.hasNoConnections(new PoolExhaustedException()), is(true)); + } + + @Test + public void tomcatPoolNestedExhaustedShouldBeDetected() { + Exception nestedException = new OBException(new PoolExhaustedException()); + assertThat("Nested PoolExhaustedException should be detected as no connections exception", + pool.hasNoConnections(nestedException), is(true)); + + Exception nested2LevelException = new Exception(nestedException); + assertThat( + "Nested 2 levels PoolExhaustedException should be detected as no connections exception", + pool.hasNoConnections(nested2LevelException), is(true)); + } + + @Test + public void pgOutOfConnectionsShouldBeDetected() { + SQLException se = new SQLException("PG cannot create DB connection", "53300"); + assertThat("Cannot create DB connections should be detected as no connections exception", + pool.hasNoConnections(se), is(true)); + } + + @Test + public void oraOutOfConnectionsShouldBeDetected() { + SQLException se = new SQLException("ORA cannot create DB connection", "66000"); + assertThat("Cannot create DB connections should be detected as no connections exception", + pool.hasNoConnections(se), is(true)); + } + + @Test + public void otherExceptionsShoulNotBeDetected() { + assertThat("Other Exception detected as no connections", + pool.hasNoConnections(new SQLException()), is(false)); + } + +} diff -r 6ce7922086d7 -r ba9ef09516cf src/org/openbravo/service/importprocess/ImportEntryProcessor.java --- a/src/org/openbravo/service/importprocess/ImportEntryProcessor.java Wed Oct 03 17:18:12 2018 +0200 +++ b/src/org/openbravo/service/importprocess/ImportEntryProcessor.java Tue Oct 02 09:43:11 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(); _______________________________________________ Openbravo-commits mailing list Openbravo-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openbravo-commits