Beispiel #1
0
        public override System.Collections.ObjectModel.ReadOnlyCollection <LinkChangeGroup> GenerateNextLinkDeltaSlice(
            LinkService linkService,
            int maxDeltaSliceSize)
        {
            try
            {
                var linkChangeGroups = new List <LinkChangeGroup>();

                if (null == ExtractLinkChangeActionsCallback)
                {
                    return(linkChangeGroups.AsReadOnly());
                }

                // load main Highwater Mark
                m_hwmLink.Reload();
                DateTime hwmLinkValue = m_hwmLink.Value;
                // search back 60 seconds to deal with potential WIT race condition
                if (!hwmLinkValue.Equals(default(DateTime)))
                {
                    hwmLinkValue = hwmLinkValue.AddSeconds(-60);
                }
                string hwmLinkValueStr = hwmLinkValue.ToString(CultureInfo.InvariantCulture);

                // load Work Items for extracting links
                string sourceId  = m_migrationSource.UniqueId;
                string storeName = m_migrationSource.WorkItemStore.StoreName;

                // Get items based on primary Highwater Mark
                TraceManager.TraceInformation(TfsWITAdapterResources.GettingModifiedItems, sourceId, storeName);
                IEnumerable <TfsMigrationWorkItem> items = m_migrationSource.WorkItemStore.GetItems(ref hwmLinkValueStr);
                TraceManager.TraceInformation(TfsWITAdapterResources.ReceivedModifiedItems, sourceId, storeName);

                // Record the updated HWM value
                DateTime wiqlExecutionTime = Convert.ToDateTime(hwmLinkValueStr, CultureInfo.InvariantCulture);

                // store to be used to analyze deleted links
                WorkItemLinkStore store = new WorkItemLinkStore(new Guid(sourceId));

                // extract links
                DateTime lastWorkITemUpdateTime = DateTime.MinValue;
                var      inMaxDeltaSliceSize    = maxDeltaSliceSize;
                foreach (TfsMigrationWorkItem tfsMigrationWorkItem in items)
                {
                    if (tfsMigrationWorkItem.WorkItem == null)
                    {
                        continue;
                    }

                    TraceManager.TraceInformation("Generating linking delta for Work Item: {0}", tfsMigrationWorkItem.WorkItem.Id.ToString());
                    var detectedLinkChangeGroups = new List <LinkChangeGroup>();
                    ExtractLinkChangeActionsCallback(tfsMigrationWorkItem, detectedLinkChangeGroups, store);

                    if (detectedLinkChangeGroups.Count == 0)
                    {
                        TraceManager.TraceInformation("Number of links: {0}", 0);
                        continue;
                    }

                    Dictionary <string, LinkChangeGroup> perWorkItemConsolidatedLinkChangeGroup = new Dictionary <string, LinkChangeGroup>();
                    for (int i = 0; i < detectedLinkChangeGroups.Count; ++i)
                    {
                        foreach (LinkChangeAction action in detectedLinkChangeGroups[i].Actions)
                        {
                            if (!perWorkItemConsolidatedLinkChangeGroup.ContainsKey(action.Link.SourceArtifact.Uri))
                            {
                                var linkChangeGroup = new LinkChangeGroup(
                                    action.Link.SourceArtifactId, LinkChangeGroup.LinkChangeGroupStatus.Created, false);
                                perWorkItemConsolidatedLinkChangeGroup.Add(action.Link.SourceArtifact.Uri, linkChangeGroup);
                            }
                            perWorkItemConsolidatedLinkChangeGroup[action.Link.SourceArtifact.Uri].AddChangeAction(action);
                        }
                    }

                    // always make sure that the currently analyzed work item has a link change group to represent it
                    // even though the group can be empty
                    if (!perWorkItemConsolidatedLinkChangeGroup.ContainsKey(tfsMigrationWorkItem.Uri))
                    {
                        perWorkItemConsolidatedLinkChangeGroup.Add(
                            tfsMigrationWorkItem.Uri,
                            new LinkChangeGroup(TfsWorkItemHandler.IdFromUri(tfsMigrationWorkItem.Uri), LinkChangeGroup.LinkChangeGroupStatus.Created, false));
                    }


                    foreach (var workItemLinkGroup in perWorkItemConsolidatedLinkChangeGroup)
                    {
                        string workItemIdStr = TfsWorkItemHandler.IdFromUri(workItemLinkGroup.Key);
                        TraceManager.TraceInformation("Detected {0} links for Work Item '{1}'",
                                                      workItemLinkGroup.Value.Actions.Count, workItemIdStr);

                        if (workItemLinkGroup.Key.Equals(tfsMigrationWorkItem.Uri, StringComparison.OrdinalIgnoreCase))
                        {
                            // VERY IMPORTANT: use the RelatedArtifactsStore to detect link deletion
                            store.UpdatePerItemLinkChangeGroupsByCheckingRelatedItemRecords(
                                workItemLinkGroup.Key, workItemLinkGroup.Value, this);
                        }
                        else
                        {
                            store.UpdatePerItemLinkChangeGroupsByCheckingRelatedItemRecordsWithoutImplicitDelete(
                                workItemLinkGroup.Key, workItemLinkGroup.Value, this);
                        }

                        if (workItemLinkGroup.Value.Actions.Count > 0)
                        {
                            linkChangeGroups.Add(workItemLinkGroup.Value);
                        }
                        maxDeltaSliceSize -= workItemLinkGroup.Value.Actions.Count;

                        if (maxDeltaSliceSize <= 0)
                        {
                            // size limit reached - persist groups to DB
                            linkService.AddChangeGroups(linkChangeGroups);
                            linkChangeGroups.Clear();
                            maxDeltaSliceSize = inMaxDeltaSliceSize;
                        }
                    }

                    DateTime lastRevChangedDate = tfsMigrationWorkItem.WorkItem.ChangedDate;

                    if (lastWorkITemUpdateTime.CompareTo(lastRevChangedDate) <= 0)
                    {
                        lastWorkITemUpdateTime = lastRevChangedDate;
                    }
                }

                // persist remaining groups to DB
                linkService.AddChangeGroups(linkChangeGroups);

                // clean up the returned link change group collection
                // when the caller (toolkit) receives an empty collection, it understands there is no more
                // delta to generate for the moment, and proceeds to next phase
                linkChangeGroups.Clear();

                // update primary Highwater Mark
                //m_hwmLink.Update(newHwmLinkValue);

                string newHwmValueStr = hwmLinkValueStr;
                if (lastWorkITemUpdateTime.Equals(DateTime.MinValue))
                {
                    // no changes in this sync cycle, record the wiql query execution time
                    m_hwmLink.Update(wiqlExecutionTime);
                }
                else
                {
                    // hwm is recorded in UTC, so does the WIQL query asof time
                    lastWorkITemUpdateTime = lastWorkITemUpdateTime.ToUniversalTime();

                    if (lastWorkITemUpdateTime.CompareTo(wiqlExecutionTime) <= 0)
                    {
                        // last work item rev time is earlier than wiql query execution time, use it as hwm
                        m_hwmLink.Update(lastWorkITemUpdateTime);
                        newHwmValueStr = lastWorkITemUpdateTime.ToString();
                    }
                    else
                    {
                        m_hwmLink.Update(wiqlExecutionTime);
                    }
                }
                TraceManager.TraceInformation("Persisted WIT linking HWM: {0}", Toolkit.Constants.HwmDeltaLink);
                TraceManager.TraceInformation(TfsWITAdapterResources.UpdatedHighWatermark, newHwmValueStr);

                return(linkChangeGroups.AsReadOnly());
            }
            catch (Exception exception)
            {
                MigrationConflict genericeConflict = WitGeneralConflictType.CreateConflict(exception);
                var conflictManager = m_conflictManager.GetService(typeof(ConflictManager)) as ConflictManager;
                Debug.Assert(null != conflictManager);
                List <MigrationAction>   resolutionActions;
                ConflictResolutionResult resolveRslt =
                    conflictManager.TryResolveNewConflict(conflictManager.SourceId, genericeConflict, out resolutionActions);
                Debug.Assert(!resolveRslt.Resolved);
                return(new List <LinkChangeGroup>().AsReadOnly());
            }
        }
Beispiel #2
0
        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);
        }