From e40265f1a135f1cbad4e3b051299415cf5f2be8c Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 13 Sep 2011 12:40:21 +0000 Subject: [PATCH] =?UTF-8?q?Fixed=20ALF-10280:=20Slow=20to=20report=20?= =?UTF-8?q?=C2=AB=20Duplicate=20entry=20=C2=BB=20in=20database.=20=20-=20D?= =?UTF-8?q?uplicateChildNodeNameException=20implements=20DoNotRetryExcepti?= =?UTF-8?q?on=20=20-=20Must=20be=20merged=20back=20to=20V3.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@30468 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/repo/node/NodeServiceTest.java | 42 ++++++++++++++++++- .../repo/transaction/DoNotRetryException.java | 30 +++++++++++++ .../RetryingTransactionHelper.java | 8 +++- .../DuplicateChildNodeNameException.java | 6 ++- 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 source/java/org/alfresco/repo/transaction/DoNotRetryException.java diff --git a/source/java/org/alfresco/repo/node/NodeServiceTest.java b/source/java/org/alfresco/repo/node/NodeServiceTest.java index c7461045b7..c863635103 100644 --- a/source/java/org/alfresco/repo/node/NodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/NodeServiceTest.java @@ -20,6 +20,7 @@ package org.alfresco.repo.node; import java.io.Serializable; import java.util.Collections; +import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -30,6 +31,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef.Status; @@ -211,11 +213,14 @@ public class NodeServiceTest extends TestCase ContentModel.TYPE_FOLDER).getChildRef(); for (int i = 1; i < liveNodeRefs.length; i++) { + Map props = new HashMap(3); + props.put(ContentModel.PROP_NAME, "depth-" + i); liveNodeRefs[i] = nodeService.createNode( liveNodeRefs[i-1], ContentModel.ASSOC_CONTAINS, QName.createQName(NAMESPACE, "depth-" + i), - ContentModel.TYPE_FOLDER).getChildRef(); + ContentModel.TYPE_FOLDER, + props).getChildRef(); } return null; } @@ -383,4 +388,39 @@ public class NodeServiceTest extends TestCase AssociationRef assocRef = nodeService.getAssoc(Long.MAX_VALUE); assertNull("Should get null for missing ID of association. ", assocRef); } + + public void testDuplicateChildNodeName() + { + final NodeRef[] liveNodeRefs = new NodeRef[3]; + final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + buildNodeHierarchy(workspaceRootNodeRef, liveNodeRefs); + + // Get the name of the last node + final String lastName = (String) nodeService.getProperty(liveNodeRefs[2], ContentModel.PROP_NAME); + // Now create a node with the same name + RetryingTransactionCallback newNodeCallback = new RetryingTransactionCallback() + { + @Override + public NodeRef execute() throws Throwable + { + Map props = new HashMap(3); + props.put(ContentModel.PROP_NAME, lastName); + return nodeService.createNode( + liveNodeRefs[1], + ContentModel.ASSOC_CONTAINS, + QName.createQName(NAMESPACE, "duplicate"), + ContentModel.TYPE_FOLDER, + props).getChildRef(); + } + }; + try + { + txnService.getRetryingTransactionHelper().doInTransaction(newNodeCallback); + fail("Duplicate child node name not detected."); + } + catch (DuplicateChildNodeNameException e) + { + // Expected + } + } } diff --git a/source/java/org/alfresco/repo/transaction/DoNotRetryException.java b/source/java/org/alfresco/repo/transaction/DoNotRetryException.java new file mode 100644 index 0000000000..428a568a21 --- /dev/null +++ b/source/java/org/alfresco/repo/transaction/DoNotRetryException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2010 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; + +/** + * Marker interface for the exceptions that should not trigger retries, regardless of + * the contained causal exceptions. + * + * @author Derek Hulley + * @since 3.4.6 + */ +public interface DoNotRetryException +{ +} diff --git a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java index a29668a19b..49197b2bbf 100644 --- a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java +++ b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java @@ -541,7 +541,8 @@ public class RetryingTransactionHelper } /** - * Sometimes, the exception means retry and sometimes not. + * Sometimes, the exception means retry and sometimes not. The stack of exceptions is also checked + * for any occurence of {@link DoNotRetryException} and, if found, nothing is returned. * * @param cause the cause to examine * @return Returns the original cause if it is a valid retry cause, otherwise null @@ -554,6 +555,11 @@ public class RetryingTransactionHelper { return null; } + else if (ExceptionStackUtil.getCause(cause, DoNotRetryException.class) != null) + { + // Someone decided that the txn should NOT retry + return null; + } else if (retryCause instanceof SQLGrammarException && ((SQLGrammarException) retryCause).getErrorCode() != 3960) { diff --git a/source/java/org/alfresco/service/cmr/repository/DuplicateChildNodeNameException.java b/source/java/org/alfresco/service/cmr/repository/DuplicateChildNodeNameException.java index b50feeada8..8617c64493 100644 --- a/source/java/org/alfresco/service/cmr/repository/DuplicateChildNodeNameException.java +++ b/source/java/org/alfresco/service/cmr/repository/DuplicateChildNodeNameException.java @@ -19,16 +19,20 @@ package org.alfresco.service.cmr.repository; import org.springframework.extensions.surf.util.I18NUtil; +import org.alfresco.repo.transaction.DoNotRetryException; import org.alfresco.service.namespace.QName; /** * Thrown when a child node cm:name property violates the data dictionary * duplicate child association constraint. + *

+ * Note that this exception may be triggered by database constraints but must + * still NOT trigger transaction retries. * * @author Derek Hulley */ -public class DuplicateChildNodeNameException extends RuntimeException +public class DuplicateChildNodeNameException extends RuntimeException implements DoNotRetryException { private static final long serialVersionUID = 5143099335847200453L;