Merged 5.2.N (5.2.1) to HEAD (5.2)

125039 cturlica: Merged 5.1.N (5.1.2) to 5.2.N (5.2.1)
      124939 arebegea: Merged 5.0.N (5.0.4) to 5.1.N (5.1.2)
         124889 jvonka: Merged 50N-NDB (5.0.4) to 5.0.N (5.0.4)
            124745: MNT-15211: Follow-on (note: only affects NDB) - tweaks for further repo test fixes, including:
            - FileFolderServiceImplTest.testMove


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@127774 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2016-06-03 16:08:16 +00:00
parent 0cb9bc6dc9
commit f83f196211
2 changed files with 228 additions and 174 deletions

View File

@@ -1535,60 +1535,16 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
}
final Long newChildNodeId = newChildNode.getId();
// Now update the primary parent assoc
RetryingCallback<Integer> callback = new RetryingCallback<Integer>()
{
public Integer execute() throws Throwable
{
// Because we are retrying in-transaction i.e. absorbing exceptions, we need a Savepoint
Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException");
// We use the child node's UUID if there is no cm:name
String childNodeNameToUse = childNodeName == null ? childNode.getUuid() : childNodeName;
try
{
int updated = updatePrimaryParentAssocs(
newChildNodeId,
newParentNodeId,
assocTypeQName,
assocQName,
childNodeNameToUse);
controlDAO.releaseSavepoint(savepoint);
// Ensure we invalidate the name cache (the child version key might not have been 'bumped' by the last
// 'touch')
if (updated > 0 && primaryParentAssoc != null)
{
Pair<Long, QName> oldTypeQnamePair = qnameDAO.getQName(
primaryParentAssoc.getTypeQNameId());
if (oldTypeQnamePair != null)
{
childByNameCache.remove(new ChildByNameKey(oldParentNodeId, oldTypeQnamePair.getSecond(),
primaryParentAssoc.getChildNodeName()));
}
}
return updated;
}
catch (Throwable e)
{
controlDAO.rollbackToSavepoint(savepoint);
// DuplicateChildNodeNameException implements DoNotRetryException.
// There are some cases - FK violations, specifically - where we DO actually want to retry.
// Detecting this is done by looking for the related FK names, 'fk_alf_cass_*' in the error message
String lowerMsg = e.getMessage().toLowerCase();
if (lowerMsg.contains("fk_alf_cass_"))
{
throw new ConcurrencyFailureException("FK violation updating primary parent association for " + childNodeId, e);
}
// We assume that this is from the child cm:name constraint violation
throw new DuplicateChildNodeNameException(
newParentNode.getNodeRef(),
assocTypeQName,
childNodeName,
e);
}
}
};
childAssocRetryingHelper.doWithRetry(callback);
updatePrimaryParentAssocs(primaryParentAssoc,
newParentNode,
childNode,
newChildNodeId,
childNodeName,
oldParentNodeId,
assocTypeQName,
assocQName);
// Optimize for rename case
if (!EqualsHelper.nullSafeEquals(newParentNodeId, oldParentNodeId))
@@ -1615,6 +1571,94 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
return new Pair<Pair<Long, ChildAssociationRef>, Pair<Long, NodeRef>>(assocPair, nodePair);
}
protected void updatePrimaryParentAssocs(
final ChildAssocEntity primaryParentAssoc,
final Node newParentNode,
final Node childNode,
final Long newChildNodeId,
final String childNodeName,
final Long oldParentNodeId,
final QName assocTypeQName,
final QName assocQName)
{
// Because we are retrying in-transaction i.e. absorbing exceptions, we need partial rollback &/or via savepoint if needed (eg. PostgreSQL)
RetryingCallback<Integer> callback = new RetryingCallback<Integer>()
{
public Integer execute() throws Throwable
{
return updatePrimaryParentAssocsImpl(primaryParentAssoc,
newParentNode,
childNode,
newChildNodeId,
childNodeName,
oldParentNodeId,
assocTypeQName,
assocQName);
}
};
childAssocRetryingHelper.doWithRetry(callback);
}
protected int updatePrimaryParentAssocsImpl(
ChildAssocEntity primaryParentAssoc,
Node newParentNode,
Node childNode,
Long newChildNodeId,
String childNodeName,
Long oldParentNodeId,
QName assocTypeQName,
QName assocQName)
{
Long newParentNodeId = newParentNode.getId();
Long childNodeId = childNode.getId();
Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException");
// We use the child node's UUID if there is no cm:name
String childNodeNameToUse = childNodeName == null ? childNode.getUuid() : childNodeName;
try
{
int updated = updatePrimaryParentAssocs(
newChildNodeId,
newParentNodeId,
assocTypeQName,
assocQName,
childNodeNameToUse);
controlDAO.releaseSavepoint(savepoint);
// Ensure we invalidate the name cache (the child version key might not have been 'bumped' by the last
// 'touch')
if (updated > 0 && primaryParentAssoc != null)
{
Pair<Long, QName> oldTypeQnamePair = qnameDAO.getQName(
primaryParentAssoc.getTypeQNameId());
if (oldTypeQnamePair != null)
{
childByNameCache.remove(new ChildByNameKey(oldParentNodeId, oldTypeQnamePair.getSecond(),
primaryParentAssoc.getChildNodeName()));
}
}
return updated;
}
catch (Throwable e)
{
controlDAO.rollbackToSavepoint(savepoint);
// DuplicateChildNodeNameException implements DoNotRetryException.
// There are some cases - FK violations, specifically - where we DO actually want to retry.
// Detecting this is done by looking for the related FK names, 'fk_alf_cass_*' in the error message
String lowerMsg = e.getMessage().toLowerCase();
if (lowerMsg.contains("fk_alf_cass_"))
{
throw new ConcurrencyFailureException("FK violation updating primary parent association for " + childNodeId, e);
}
// We assume that this is from the child cm:name constraint violation
throw new DuplicateChildNodeNameException(
newParentNode.getNodeRef(),
assocTypeQName,
childNodeName,
e);
}
}
@Override
public boolean updateNode(Long nodeId, QName nodeTypeQName, Locale nodeLocale)
{
@@ -3143,7 +3187,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Index
assoc.setAssocIndex(-1);
Long assocId = newChildAssocImplInsert(assoc, assocTypeQName, childNodeName);
Long assocId = newChildAssocInsert(assoc, assocTypeQName, childNodeName);
// Persist it
assoc.setId(assocId);
@@ -3163,50 +3207,56 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
return assoc;
}
protected Long newChildAssocImplInsert(final ChildAssocEntity assoc, final QName assocTypeQName, final String childNodeName)
protected Long newChildAssocInsert(final ChildAssocEntity assoc, final QName assocTypeQName, final String childNodeName)
{
// Because we are retrying in-transaction i.e. absorbing exceptions, we need partial rollback &/or via savepoint if needed (eg. PostgreSQL)
RetryingCallback<Long> callback = new RetryingCallback<Long>()
{
public Long execute() throws Throwable
{
Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException");
try
{
Long id = insertChildAssoc(assoc);
controlDAO.releaseSavepoint(savepoint);
return id;
}
catch (Throwable e)
{
controlDAO.rollbackToSavepoint(savepoint);
// DuplicateChildNodeNameException implements DoNotRetryException.
// Allow real DB concurrency issues (e.g. DeadlockLoserDataAccessException) straight through for a retry
if (e instanceof ConcurrencyFailureException)
{
throw e;
}
// There are some cases - FK violations, specifically - where we DO actually want to retry.
// Detecting this is done by looking for the related FK names, 'fk_alf_cass_*' in the error message
String lowerMsg = e.getMessage().toLowerCase();
if (lowerMsg.contains("fk_alf_cass_"))
{
throw new ConcurrencyFailureException("FK violation updating primary parent association:" + assoc, e);
}
// We assume that this is from the child cm:name constraint violation
throw new DuplicateChildNodeNameException(
assoc.getParentNode().getNodeRef(),
assocTypeQName,
childNodeName,
e);
}
return newChildAssocInsertImpl(assoc, assocTypeQName, childNodeName);
}
};
Long assocId = childAssocRetryingHelper.doWithRetry(callback);
return assocId;
}
protected Long newChildAssocInsertImpl(final ChildAssocEntity assoc, final QName assocTypeQName, final String childNodeName)
{
Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException");
try
{
Long id = insertChildAssoc(assoc);
controlDAO.releaseSavepoint(savepoint);
return id;
}
catch (Throwable e)
{
controlDAO.rollbackToSavepoint(savepoint);
// DuplicateChildNodeNameException implements DoNotRetryException.
// Allow real DB concurrency issues (e.g. DeadlockLoserDataAccessException) straight through for a retry
if (e instanceof ConcurrencyFailureException)
{
throw e;
}
// There are some cases - FK violations, specifically - where we DO actually want to retry.
// Detecting this is done by looking for the related FK names, 'fk_alf_cass_*' in the error message
String lowerMsg = e.getMessage().toLowerCase();
if (lowerMsg.contains("fk_alf_cass_"))
{
throw new ConcurrencyFailureException("FK violation updating primary parent association:" + assoc, e);
}
// We assume that this is from the child cm:name constraint violation
throw new DuplicateChildNodeNameException(
assoc.getParentNode().getNodeRef(),
assocTypeQName,
childNodeName,
e);
}
}
@Override
public Pair<Long, ChildAssociationRef> newChildAssoc(
@@ -3275,53 +3325,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
* TODO: See about pulling automatic cm:name update logic into this DAO
*/
@Override
public void setChildAssocsUniqueName(final Long childNodeId, final String childName)
public void setChildAssocsUniqueName(Long childNodeId, String childName)
{
RetryingCallback<Integer> callback = new RetryingCallback<Integer>()
{
public Integer execute() throws Throwable
{
int total = 0;
Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException");
try
{
for (ChildAssocEntity parentAssoc : getParentAssocsCached(childNodeId).getParentAssocs().values())
{
// Subtlety: We only update those associations for which name uniqueness checking is enforced.
// Such associations have a positive CRC
if (parentAssoc.getChildNodeNameCrc() <= 0)
{
continue;
}
Pair<Long, QName> oldTypeQnamePair = qnameDAO.getQName(parentAssoc.getTypeQNameId());
// Ensure we invalidate the name cache (the child version key might not be 'bumped' by the next
// 'touch')
if (oldTypeQnamePair != null)
{
childByNameCache.remove(new ChildByNameKey(parentAssoc.getParentNode().getId(),
oldTypeQnamePair.getSecond(), parentAssoc.getChildNodeName()));
}
int count = updateChildAssocUniqueName(parentAssoc.getId(), childName);
if (count <= 0)
{
// Should not be attempting to delete a deleted node
throw new ConcurrencyFailureException("Failed to update an existing parent association "
+ parentAssoc.getId());
}
total += count;
}
controlDAO.releaseSavepoint(savepoint);
return total;
}
catch (Throwable e)
{
controlDAO.rollbackToSavepoint(savepoint);
// We assume that this is from the child cm:name constraint violation
throw new DuplicateChildNodeNameException(null, null, childName, e);
}
}
};
Integer count = childAssocRetryingHelper.doWithRetry(callback);
Integer count = setChildAssocsUniqueNameImpl(childNodeId, childName);
if (count > 0)
{
// Touch the node; parent assocs are out of sync
@@ -3337,6 +3344,61 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
" Updated: " + count);
}
}
protected int setChildAssocsUniqueNameImpl(final Long childNodeId, final String childName)
{
// Because we are retrying in-transaction i.e. absorbing exceptions, we need partial rollback &/or via savepoint if needed (eg. PostgreSQL)
RetryingCallback<Integer> callback = new RetryingCallback<Integer>()
{
public Integer execute() throws Throwable
{
return updateChildAssocUniqueNameImpl(childNodeId, childName);
}
};
return childAssocRetryingHelper.doWithRetry(callback);
}
protected int updateChildAssocUniqueNameImpl(final Long childNodeId, final String childName)
{
int total = 0;
Savepoint savepoint = controlDAO.createSavepoint("DuplicateChildNodeNameException");
try
{
for (ChildAssocEntity parentAssoc : getParentAssocsCached(childNodeId).getParentAssocs().values())
{
// Subtlety: We only update those associations for which name uniqueness checking is enforced.
// Such associations have a positive CRC
if (parentAssoc.getChildNodeNameCrc() <= 0)
{
continue;
}
Pair<Long, QName> oldTypeQnamePair = qnameDAO.getQName(parentAssoc.getTypeQNameId());
// Ensure we invalidate the name cache (the child version key might not be 'bumped' by the next
// 'touch')
if (oldTypeQnamePair != null)
{
childByNameCache.remove(new ChildByNameKey(parentAssoc.getParentNode().getId(),
oldTypeQnamePair.getSecond(), parentAssoc.getChildNodeName()));
}
int count = updateChildAssocUniqueName(parentAssoc.getId(), childName);
if (count <= 0)
{
// Should not be attempting to delete a deleted node
throw new ConcurrencyFailureException("Failed to update an existing parent association "
+ parentAssoc.getId());
}
total += count;
}
controlDAO.releaseSavepoint(savepoint);
return total;
}
catch (Throwable e)
{
controlDAO.rollbackToSavepoint(savepoint);
// We assume that this is from the child cm:name constraint violation
throw new DuplicateChildNodeNameException(null, null, childName, e);
}
}
@Override
public Pair<Long, ChildAssociationRef> getChildAssoc(Long assocId)