private bool SubmitBatchedAddOrDeleteLinkChanges( List <XmlDocument> updateDocuments, Dictionary <int, LinkChangeAction> updateDocIndexToLinkChangeActionMap, ITranslationService translationService, ConfigurationService configService, ConflictManager conflictManager) { bool succeeded = true; UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updateDocuments.ToArray()); if (results.Length != updateDocuments.Count) { throw new SynchronizationEngineException("Wrong number of link update results."); } // Collect list of successful LinkChangeActions (for LinkTypes with GetsActionsFromLinkChangeHistory true) to pass to SetServerLinkChangeIds() List <LinkChangeAction> actionsNeedingServerLinkIdSet = new List <LinkChangeAction>(); for (int i = 0; i < results.Length; ++i) { if (results[i] == null) { continue; } UpdateResult rslt = results[i]; if (rslt.Exception != null) { MigrationConflict conflict = null; ConflictResolutionResult resolutionRslt = null; List <MigrationAction> actions; bool createWitGeneralConflict = false; System.Web.Services.Protocols.SoapException soapException = rslt.Exception as System.Web.Services.Protocols.SoapException; if (soapException != null) { int?tfsErrorNumber = GetTfsErrorNumberFromSoapException(soapException); if (tfsErrorNumber.HasValue) { LinkChangeAction linkChangeAction = updateDocIndexToLinkChangeActionMap[i]; switch (tfsErrorNumber) { case TfsConstants.TfsError_AddLink_LinkExists: case TfsConstants.TfsError_DeleteLink_LinkNotFound: // it is ok to eat these exception and skip the action // mark the change action completed so it is not retried later linkChangeAction.Status = LinkChangeAction.LinkChangeActionStatus.Skipped; if (tfsErrorNumber == TfsConstants.TfsError_AddLink_LinkExists) { TraceManager.TraceInformation("Tried to add a link that already exists so skipping it: " + GetLinkChangeActionDescription(linkChangeAction)); } else { TraceManager.TraceInformation("Tried to delete a link that does not exist so skipping it: " + GetLinkChangeActionDescription(linkChangeAction)); } if (soapException.Detail != null) { TraceManager.TraceVerbose("SoapException.Detail.InnerXml for ignored exception: " + soapException.Detail.InnerXml); } break; case TfsConstants.TfsError_AddLink_TooManyParents: if (linkChangeAction.Group.IsForcedSync) { if (DeleteExistingParentLinkToForceSyncAddLink(linkChangeAction, updateDocuments[i])) { break; } } conflict = TFSMulitpleParentLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } break; case TfsConstants.TfsError_AddLink_Circular: case TfsConstants.TfsError_AddLink_ChildIsAncestor: ILinkProvider linkProvider = ServiceContainer.GetService(typeof(ILinkProvider)) as ILinkProvider; Debug.Assert(null != linkProvider, "linkProvider is NULL"); LinkChangeAction conflictedAction = updateDocIndexToLinkChangeActionMap[i]; NonCyclicReferenceClosure linkRefClosure = linkProvider.CreateNonCyclicLinkReferenceClosure(conflictedAction.Link.LinkType, conflictedAction.Link.SourceArtifact); conflict = TFSCyclicLinkConflictType.CreateConflict(conflictedAction, rslt.Exception, linkRefClosure); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } break; case TfsConstants.TfsError_LinkAuthorizationFailedLinkLocked: conflict = TFSModifyLockedWorkItemLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; break; case TfsConstants.TfsError_LinkAuthorizationFailed: case TfsConstants.TfsError_LinkAuthorizationFailedNotServiceAccount: conflict = TFSLinkAccessViolationConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; break; default: // TFS error doesn't match any that we explicitly handle TraceManager.TraceError("SubmitBatchedAddOrDeleteLinkChanges:TFS error number in SoapException not explictly handled: {0}", tfsErrorNumber); createWitGeneralConflict = true; break; } } else { TraceManager.TraceError("SubmitBatchedAddOrDeleteLinkChanges: Unable to get TFS error number from SoapException: {0}", soapException.ToString()); createWitGeneralConflict = true; } } else // Exception is not SoapException { TraceManager.TraceError("SubmitBatchedAddOrDeleteLinkChanges: Exception returned is not SoapException: {0}", rslt.Exception.ToString()); createWitGeneralConflict = true; } if (createWitGeneralConflict) { conflict = WitGeneralConflictType.CreateConflict(rslt.Exception); resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } } else // rslt.Exception == null { LinkChangeAction successfulAction = updateDocIndexToLinkChangeActionMap[i]; MarkLinkChangeActionCompleted(successfulAction); TraceManager.TraceVerbose("Successful " + GetLinkChangeActionDescription(successfulAction)); List <LinkChangeAction> updatedActions = new List <LinkChangeAction>(); updatedActions.Add(successfulAction); if (successfulAction.Link.LinkType.GetsActionsFromLinkChangeHistory) { actionsNeedingServerLinkIdSet.Add(successfulAction); } UpdateLinkConversionHistory(configService, translationService, rslt, updatedActions); } } SetServerLinkChangeIds(actionsNeedingServerLinkIdSet); return(succeeded); }
private bool SubmitBatchedAddOrDeleteLinkChanges( List <XmlDocument> updateDocuments, Dictionary <int, LinkChangeAction> updateDocIndexToLinkChangeActionMap, ITranslationService translationService, ConfigurationService configService, ConflictManager conflictManager) { bool succeeded = true; UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updateDocuments.ToArray()); if (results.Length != updateDocuments.Count) { throw new SynchronizationEngineException("Wrong number of link update results."); } // Collect list of successful LinkChangeActions (for LinkTypes with GetsActionsFromLinkChangeHistory true) to pass to SetServerLinkChangeIds() List <LinkChangeAction> actionsNeedingServerLinkIdSet = new List <LinkChangeAction>(); for (int i = 0; i < results.Length; ++i) { UpdateResult rslt = results[i]; if (rslt.Exception != null) { if (rslt.Exception.Message.Contains("The specified link already exists")) { // it is ok to eat this exception // mark the change action completed so it is not retried later LinkChangeAction action = updateDocIndexToLinkChangeActionMap[i]; action.Status = LinkChangeAction.LinkChangeActionStatus.Skipped; TraceManager.TraceInformation("Tried to add a link that already exists so skipping it: " + GetLinkChangeActionDescription(action)); System.Web.Services.Protocols.SoapException soapException = rslt.Exception as System.Web.Services.Protocols.SoapException; if (soapException != null) { TraceManager.TraceVerbose("SoapException.Detail.InnerXml for ignored exception: " + soapException.Detail.InnerXml); } } else if (updateDocIndexToLinkChangeActionMap[i].ChangeActionId == WellKnownChangeActionId.Delete && rslt.Exception.Message.Contains("This specified link does not exist")) { // it is ok to eat this exception and skip the action // mark the change action completed so it is not retried later LinkChangeAction action = updateDocIndexToLinkChangeActionMap[i]; action.Status = LinkChangeAction.LinkChangeActionStatus.Skipped; TraceManager.TraceInformation("Tried to delete a link that does not exist so skipping it: " + GetLinkChangeActionDescription(action)); System.Web.Services.Protocols.SoapException soapException = rslt.Exception as System.Web.Services.Protocols.SoapException; if (soapException != null) { TraceManager.TraceVerbose("SoapException.Detail.InnerXml for ignored exception: " + soapException.Detail.InnerXml); } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSMulitpleParentLinkConflictType.SingleParentViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSMulitpleParentLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSCyclicLinkConflictType.CircularityLinkHierarchyViolationMessage, StringComparison.OrdinalIgnoreCase)) { ILinkProvider linkProvider = ServiceContainer.GetService(typeof(ILinkProvider)) as ILinkProvider; Debug.Assert(null != linkProvider, "linkProvider is NULL"); LinkChangeAction conflictedAction = updateDocIndexToLinkChangeActionMap[i]; NonCyclicReferenceClosure linkRefClosure = linkProvider.CreateNonCyclicLinkReferenceClosure(conflictedAction.Link.LinkType, conflictedAction.Link.SourceArtifact); MigrationConflict conflict = TFSCyclicLinkConflictType.CreateConflict(conflictedAction, rslt.Exception, linkRefClosure); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSModifyLockedWorkItemLinkConflictType.ModifyLockedWorkItemLinkViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSModifyLockedWorkItemLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && (rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage1, StringComparison.OrdinalIgnoreCase) || rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage2, StringComparison.OrdinalIgnoreCase))) { MigrationConflict conflict = TFSLinkAccessViolationConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else { LinkChangeAction action = updateDocIndexToLinkChangeActionMap[i]; // TODO // Try resolve conflict and push to backlog if resolution fails action.IsConflicted = true; TraceManager.TraceError(String.Format(CultureInfo.InvariantCulture, "Exception processing {0}: {1}", GetLinkChangeActionDescription(action), rslt.Exception.ToString())); succeeded = false; } } else // rslt.Exception == null { LinkChangeAction successfulAction = updateDocIndexToLinkChangeActionMap[i]; MarkLinkChangeActionCompleted(successfulAction); TraceManager.TraceVerbose("Successful " + GetLinkChangeActionDescription(successfulAction)); List <LinkChangeAction> updatedActions = new List <LinkChangeAction>(); updatedActions.Add(successfulAction); if (successfulAction.Link.LinkType.GetsActionsFromLinkChangeHistory) { actionsNeedingServerLinkIdSet.Add(successfulAction); } UpdateLinkConversionHistory(configService, translationService, rslt, updatedActions); } } SetServerLinkChangeIds(actionsNeedingServerLinkIdSet); return(succeeded); }
private bool SubmitBatchedAddOrDeleteLinkChanges( List <XmlDocument> updateDocuments, Dictionary <int, LinkChangeAction> updateDocIndexToLinkChangeActionMap, ITranslationService translationService, ConfigurationService configService, ConflictManager conflictManager) { bool succeeded = true; UpdateResult[] results = TfsBatchUpdateHelper.Submit(Core, WorkItemServer, updateDocuments.ToArray()); if (results.Length != updateDocuments.Count) { throw new SynchronizationEngineException("Wrong number of link update results."); } for (int i = 0; i < results.Length; ++i) { UpdateResult rslt = results[i]; if (rslt.Exception != null) { if (rslt.Exception.Message.Contains("The specified link already exists")) { // it is ok to eat this exception } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSMulitpleParentLinkConflictType.SingleParentViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSMulitpleParentLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSCyclicLinkConflictType.CircularityLinkHierarchyViolationMessage, StringComparison.OrdinalIgnoreCase)) { ILinkProvider linkProvider = ServiceContainer.GetService(typeof(ILinkProvider)) as ILinkProvider; Debug.Assert(null != linkProvider, "linkProvider is NULL"); LinkChangeAction conflictedAction = updateDocIndexToLinkChangeActionMap[i]; NonCyclicReferenceClosure linkRefClosure = linkProvider.CreateNonCyclicLinkReferenceClosure(conflictedAction.Link.LinkType, conflictedAction.Link.SourceArtifact); MigrationConflict conflict = TFSCyclicLinkConflictType.CreateConflict(conflictedAction, rslt.Exception, linkRefClosure); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; succeeded = false; } } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && rslt.Exception.Message.StartsWith( TFSModifyLockedWorkItemLinkConflictType.ModifyLockedWorkItemLinkViolationMessage, StringComparison.OrdinalIgnoreCase)) { MigrationConflict conflict = TFSModifyLockedWorkItemLinkConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else if (rslt.Exception is System.Web.Services.Protocols.SoapException && null != rslt.Exception.Message && (rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage1, StringComparison.OrdinalIgnoreCase) || rslt.Exception.Message.StartsWith(TFSLinkAccessViolationConflictType.LinkAccessViolationMessage2, StringComparison.OrdinalIgnoreCase))) { MigrationConflict conflict = TFSLinkAccessViolationConflictType.CreateConflict( updateDocIndexToLinkChangeActionMap[i], rslt.Exception); List <MigrationAction> actions; var resolutionRslt = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!resolutionRslt.Resolved) { updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } // returning "not succeeded" so that the caller keeps this change group in "ReadyForMigration" status succeeded = false; } else { TraceManager.TraceError(rslt.Exception.ToString()); succeeded = false; // TODO // Try resolve conflict and push to backlog if resolution fails updateDocIndexToLinkChangeActionMap[i].IsConflicted = true; } } else { foreach (LinkChangeAction action in updateDocIndexToLinkChangeActionMap.Values) { MarkLinkChangeActionCompleted(action); } List <LinkChangeAction> updatedActions = new List <LinkChangeAction>(updateDocIndexToLinkChangeActionMap.Values); if (rslt.Exception == null) { UpdateLinkConversionHistory(configService, translationService, rslt, updatedActions); } else if (rslt.Exception.Message.Contains("The specified link already exists")) { WorkItemLinkStore relatedArtifactsStore = new WorkItemLinkStore(configService.SourceId); relatedArtifactsStore.UpdateSyncedLinks(updatedActions); } } } return(succeeded); }