From 1ad8293a8c47e1fc7e254630aef12b49591beaa5 Mon Sep 17 00:00:00 2001 From: Tatyana Valkevych Date: Fri, 10 Apr 2015 21:05:36 +0000 Subject: [PATCH] Merged HEAD-BUG-FIX (5.1/Cloud) to HEAD (5.1/Cloud) 101651: Merged 5.0.N (5.0.2) to HEAD-BUG-FIX (5.1/Cloud) 101403: Merged V4.2-BUG-FIX (4.2.5) to 5.0.N (5.0.2) 101245: Merged V4.1-BUG-FIX (4.1.11) to V4.2-BUG-FIX (4.2.5) 101157: Merged V4.1.10 (4.1.10) to V4.1-BUG-FIX (4.1.11) 101099: MNT-13607: Merged DEV to PATCHES/V4.1.10 101029: MNT-13607: Sending two emails to inbound server at the same time & same subject causes error - Rethrow DuplicateChildNodeNameException which occurred during message processing as ConcurrencyFailureException to force retry. Add unit test for case. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@101698 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../email/server/EmailServiceImpl.java | 13 +++- .../email/server/EmailServiceImplTest.java | 66 ++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/source/java/org/alfresco/email/server/EmailServiceImpl.java b/source/java/org/alfresco/email/server/EmailServiceImpl.java index 081204bd14..d53ab2119a 100644 --- a/source/java/org/alfresco/email/server/EmailServiceImpl.java +++ b/source/java/org/alfresco/email/server/EmailServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -39,6 +39,7 @@ import org.alfresco.service.cmr.email.EmailDelivery; import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailService; +import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -52,6 +53,7 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.dao.ConcurrencyFailureException; import org.springframework.extensions.surf.util.ParameterCheck; /** @@ -293,7 +295,14 @@ public class EmailServiceImpl implements EmailService targetNodeRef = nodeRef; } EmailMessageHandler messageHandler = getMessageHandler(targetNodeRef); - messageHandler.processMessage(targetNodeRef, message); + try + { + messageHandler.processMessage(targetNodeRef, message); + } + catch (DuplicateChildNodeNameException e) + { + throw new ConcurrencyFailureException(e.getMessage()); + } return null; } }; diff --git a/source/test-java/org/alfresco/email/server/EmailServiceImplTest.java b/source/test-java/org/alfresco/email/server/EmailServiceImplTest.java index e3343f4b0f..e01eb9f4e9 100644 --- a/source/test-java/org/alfresco/email/server/EmailServiceImplTest.java +++ b/source/test-java/org/alfresco/email/server/EmailServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2013 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.Vector; +import java.util.concurrent.CountDownLatch; import javax.mail.Message; import javax.mail.Session; @@ -41,6 +42,7 @@ import org.alfresco.model.ForumModel; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transfer.TransferModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.email.EmailDelivery; import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailService; @@ -86,6 +88,7 @@ public class EmailServiceImplTest extends TestCase private SearchService searchService; private NamespaceService namespaceService; private FolderEmailMessageHandler folderEmailMessageHandler; + private RetryingTransactionHelper transactionHelper; String TEST_USER="EmailServiceImplTestUser"; @@ -110,6 +113,8 @@ public class EmailServiceImplTest extends TestCase assertNotNull("searchService", searchService); folderEmailMessageHandler = (FolderEmailMessageHandler) emailCtx.getBean("folderEmailMessageHandler"); assertNotNull("folderEmailMessageHandler", folderEmailMessageHandler); + transactionHelper = (RetryingTransactionHelper) emailCtx.getBean("retryingTransactionHelper"); + assertNotNull("transactionHelper", transactionHelper); } public void tearDown() throws Exception @@ -752,6 +757,65 @@ public class EmailServiceImplTest extends TestCase assertTrue("Blob(2).xls not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob(2).xls"))); assertTrue(TEST_SUBJECT + "not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT))); assertTrue(TEST_SUBJECT+"(1) not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Practical Bee Keeping(1)"))); + + /** + * Check concurrent deliver of the same message. Reuse message from the previous test. + */ + logger.debug("Step 5: turn off Overwite Duplicates and check concurrent deliver of the same message"); + folderEmailMessageHandler.setOverwriteDuplicates(false); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + int numBeforeConcurrentDeliver = assocs.size(); + deliverConcurrently(delivery, m); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + int numAfterConcurrentDeliver = assocs.size(); + assertEquals("Two messages must be added", numBeforeConcurrentDeliver + 2, numAfterConcurrentDeliver); + } + + private void deliverConcurrently(final EmailDelivery delivery, final SubethaEmailMessage m) throws Exception + { + final CountDownLatch cdl = new CountDownLatch(1); + class ConcurrentMessageImporter implements Runnable + { + private Throwable throwable; + @Override + public void run() + { + try + { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + cdl.countDown(); + emailService.importMessage(delivery, m); + return null; + } + }, false, true); + } + catch (Throwable t) + { + throwable = t; + } + } + } + ConcurrentMessageImporter messageImporter = new ConcurrentMessageImporter(); + final Thread messageImporterThread = new Thread(messageImporter); + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + emailService.importMessage(delivery, m); + messageImporterThread.start(); + // wait until concurrent transaction has started + cdl.await(); + return null; + } + }, false, true); + messageImporterThread.join(); + if (null != messageImporter.throwable) + { + fail(messageImporter.throwable.getMessage()); + } } /**