From 21670732faa7e172b9041f9569f142c0bb4b80a2 Mon Sep 17 00:00:00 2001 From: Will Abson Date: Wed, 25 Jun 2014 15:31:17 +0000 Subject: [PATCH] Merged HEAD-BUG-FIX (5.0/Cloud) to HEAD (4.3/Cloud) 71604: Merged V4.2-BUG-FIX (4.2.3) to HEAD-BUG-FIX (4.3/Cloud) 70359: Merged V4.1-BUG-FIX (4.1.9) to V4.2-BUG-FIX (4.2.3) 70323: Merged DEV to V4.1-BUG-FIX (4.1.9) 70317: MNT-9899 : Change default value for db.pool.max to 275 Converted CannotCreateTransactionException to the new ConnectionPoolException when the DB connection pool is depleted. Implemented test. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@74696 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/repository.properties | 2 +- .../org/alfresco/Repository01TestSuite.java | 1 + .../ConnectionPoolOverloadTest.java | 193 ++++++++++++++++++ 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 source/test-java/org/alfresco/repo/transaction/ConnectionPoolOverloadTest.java diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 9836398964..d272bd933c 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -367,7 +367,7 @@ db.pool.statements.enable=true db.pool.statements.max=40 db.pool.min=0 db.pool.idle=-1 -db.pool.wait.max=-1 +db.pool.wait.max=100 db.pool.validate.query= db.pool.evict.interval=-1 diff --git a/source/test-java/org/alfresco/Repository01TestSuite.java b/source/test-java/org/alfresco/Repository01TestSuite.java index 7a46863505..1f9361e198 100644 --- a/source/test-java/org/alfresco/Repository01TestSuite.java +++ b/source/test-java/org/alfresco/Repository01TestSuite.java @@ -376,6 +376,7 @@ public class Repository01TestSuite extends TestSuite suite.addTestSuite(org.alfresco.repo.transaction.RetryingTransactionHelperTest.class); suite.addTestSuite(org.alfresco.repo.transaction.TransactionAwareSingletonTest.class); suite.addTestSuite(org.alfresco.repo.transaction.TransactionServiceImplTest.class); + suite.addTestSuite(org.alfresco.repo.transaction.ConnectionPoolOverloadTest.class); suite.addTestSuite(org.alfresco.repo.transfer.NodeCrawlerTest.class); suite.addTestSuite(org.alfresco.repo.transfer.RepoTransferReceiverImplTest.class); suite.addTestSuite(org.alfresco.repo.transfer.TransferServiceCallbackTest.class); diff --git a/source/test-java/org/alfresco/repo/transaction/ConnectionPoolOverloadTest.java b/source/test-java/org/alfresco/repo/transaction/ConnectionPoolOverloadTest.java new file mode 100644 index 0000000000..a9379bdc08 --- /dev/null +++ b/source/test-java/org/alfresco/repo/transaction/ConnectionPoolOverloadTest.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.transaction; + +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +import javax.transaction.UserTransaction; + +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.transaction.ConnectionPoolException; +import org.apache.commons.lang.mutable.MutableInt; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Test; +import org.springframework.context.ApplicationContext; + +import com.sun.star.auth.InvalidArgumentException; + +import junit.framework.TestCase; + +/** + * A test designed to catch ConnectionPoolException + * + * @author alex.mukha + * @since 4.1.9 + */ + +public class ConnectionPoolOverloadTest extends TestCase +{ + private static Log logger = LogFactory.getLog(ConnectionPoolOverloadTest.class); + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private static MutableInt failCount; + + private TransactionService transactionService; + private Properties properties; + + private int dbPoolMax; + + @Override + public void setUp() throws Exception + { + failCount = new MutableInt(0); + transactionService = ctx.getBean("transactionComponent", TransactionService.class); + properties = ctx.getBean("global-properties", Properties.class); + + String dbPoolMaxProp = properties.getProperty("db.pool.max"); + if (PropertyCheck.isValidPropertyString(dbPoolMaxProp)) + { + dbPoolMax = Integer.parseInt(dbPoolMaxProp); + } + else + { + throw new InvalidArgumentException("The db.pool.max property is not valid."); + } + } + + @Test + public void testOverload() throws Exception + { + List threads = new LinkedList(); + int numThreads = dbPoolMax + 1; + int i = 0; + try + { + for (i = 0; i < numThreads; i++) + { + Thread thread = new TxnThread("Thread-" + i); + thread.start(); + threads.add(thread); + } + } + finally + { + try + { + for (Thread thread : threads) + { + if (thread != null) + { + try + { + thread.join(); + } + catch (Exception e) + { + fail("The " + thread.getName() + " failed to join."); + } + } + } + } + finally + { + for (Thread thread : threads) + { + if (thread != null) + { + thread.interrupt(); + } + } + } + assertTrue("The number of failed threads should not be 0.", failCount.intValue() > 0); + assertTrue("The number of open transactions should not be more that the db pool maximum." + + "(Maybe a configuration of DB connection limit is less then db.pool.max)" + + " db.pool.max is " + dbPoolMax + + ", number of threads is " + numThreads + + ", number of failed threads is" + failCount.intValue(), + dbPoolMax >= numThreads - failCount.intValue()); + } + } + + private class TxnThread extends Thread + { + private ThreadLocal txnTL = new ThreadLocal(); + + public TxnThread(String name) + { + super(name); + this.setDaemon(true); + } + + @Override + public void run() + { + if (logger.isDebugEnabled()) + { + logger.debug("Start " + this.getName()); + } + UserTransaction txn = transactionService.getUserTransaction(); + txnTL.set(txn); + try + { + txn.begin(); + } + catch (ConnectionPoolException e) + { + if (logger.isDebugEnabled()) + { + logger.debug("The " + this.getName() + " failed with ConnectionPoolException."); + } + failCount.increment(); + interrupt(); + } + catch (Exception e) + { + if (logger.isDebugEnabled()) + { + logger.debug("The " + this.getName() + " failed with not expected exception."); + } + e.printStackTrace(); + failCount.increment(); + interrupt(); + fail("Thread should fail with ConnectionPoolException."); + } + } + + @Override + public void interrupt() + { + if (txnTL.get() != null) + { + try + { + txnTL.get().rollback(); + } + catch (Exception e) + { + } + } + super.interrupt(); + } + } +}