Beispiel #1
0
 private void HandleErrorOnUpdate(UpdateDto dto)
 {
     if (IsNotOwnUpdate(dto))
     {
         _editor.ReloadDocument(dto.DocumentId);
     }
 }
 public void UpdateRequest(string host, UpdateDto updateDto)
 {
     using (var cf = GetChannelFactory(host))
     {
        cf.CreateChannel().UpdateRequest(updateDto);
     }
 }
Beispiel #3
0
        private bool MergePendingUpdate(Document document, UpdateDto updateDto, Tuple <string, bool[]> resultAppliedGivenUpdate)
        {
            var everythingOk  = true;
            var pendingUpdate = document.PendingUpdate;

            if (pendingUpdate != null)
            {
                //will the pending update be applied after the given update?
                if (MemberOfFirstUpdateIsNotOwnerAndHigherMember(pendingUpdate, updateDto))
                {
                    everythingOk = MergePendingUpdateAfterGivenUpdate(updateDto, pendingUpdate);
                }
                else if (MemberOfFirstUpdateIsOwnerOrLowerMember(pendingUpdate, updateDto))
                {
                    everythingOk = MergePendingUpdateBeforeGivenUpdate(document, updateDto, pendingUpdate,
                                                                       resultAppliedGivenUpdate);
                }
            }
            else
            {
                //no mergin needed, only update the editor
                _editor.UpdateText(updateDto.DocumentId, resultAppliedGivenUpdate.Item1);
            }

            return(everythingOk);
        }
Beispiel #4
0
        private void MergeUpdate(Document document, UpdateDto updateDto)
        {
            var resultAppliedGivenUpdate = _diffMatchPatch.patch_apply(updateDto.Patch, document.Content);

            if (CheckResultIsValidOtherwiseReOpen(resultAppliedGivenUpdate, updateDto.DocumentId))
            {
                if (MergePendingUpdate(document, updateDto, resultAppliedGivenUpdate))
                {
                    UpdateDocument(document, updateDto, resultAppliedGivenUpdate);
                }
            }

            //check whether we have an out of sync update which is based on the given update (so we could apply it as well)
            if (document.OutOfSyncUpdate != null && IsFirstPreviousOfSecond(updateDto, document.OutOfSyncUpdate))
            {
                var outOfSynUpdate = document.OutOfSyncUpdate;
                document.OutOfSyncUpdate = null;
                MergeUpdate(document, outOfSynUpdate);
            }

            var outOfSyncAcknowledge = document.OutOfSyncAcknowledge;

            if (outOfSyncAcknowledge != null && IsFirstPreviousOfSecond(updateDto, document.PendingUpdate))
            {
                ConfirmPendingUpdate(document, outOfSyncAcknowledge);
                document.OutOfSyncAcknowledge = null;
            }
        }
Beispiel #5
0
        private bool MergePendingUpdateBeforeGivenUpdate(Document document, UpdateDto updateDto, UpdateDto pendingUpdate, Tuple <string, bool[]> resultAppliedGivenUpdate)
        {
            // merging happens as follows, first apply pending patch on document, then given update = new content
            // yet, since server has already applied given update we need to set pending's hash to the has of the update
            // and we need to recompute the patch of the pending patch in order that we get the same text when the ACK follows
            pendingUpdate.PreviousHash = updateDto.NewHash;

            var  documentId = updateDto.DocumentId;
            var  resultAppliedPendingUpdate = _diffMatchPatch.patch_apply(pendingUpdate.Patch, document.Content);
            bool everythingOk = CheckResultIsValidOtherwiseReOpen(resultAppliedPendingUpdate, documentId);

            if (everythingOk)
            {
                var resultAfterPatches = _diffMatchPatch.patch_apply(updateDto.Patch, resultAppliedPendingUpdate.Item1);
                everythingOk = CheckResultIsValidOtherwiseReOpen(resultAfterPatches, documentId);
                if (everythingOk)
                {
                    //compute new patch for pendin gupdate
                    pendingUpdate.Patch = _diffMatchPatch.patch_make(resultAppliedGivenUpdate.Item1, resultAfterPatches.Item1);

                    // Merge screen and update -> screen is built up on pending update, thus create patch pending -> screen
                    // then apply patch to new content
                    var mergePatch  = _diffMatchPatch.patch_make(resultAppliedPendingUpdate.Item1, _editor.GetText(documentId));
                    var resultMerge = _diffMatchPatch.patch_apply(mergePatch, resultAfterPatches.Item1);
                    everythingOk = CheckResultIsValidOtherwiseReOpen(resultMerge, documentId);
                    if (everythingOk)
                    {
                        _editor.UpdateText(documentId, resultMerge.Item1);
                    }
                }
            }
            return(everythingOk);
        }
Beispiel #6
0
 public void UpdateRequest(string host, UpdateDto updateDto)
 {
     using (var cf = GetChannelFactory(host))
     {
         cf.CreateChannel().UpdateRequest(updateDto);
     }
 }
Beispiel #7
0
 private void SendUpdateToDocumentOwner(Document document, UpdateDto dto)
 {
     try
     {
         _communication.UpdateRequest(document.OwnerHost, dto);
     }
     catch (EndpointNotFoundException)
     {
         _editor.ServerUnreachable(document.Id);
     }
 }
Beispiel #8
0
 private void UpdateDocument(Document document, UpdateDto updateDto, Tuple <string, bool[]> resultAppliedGivenUpdate)
 {
     document.Content           = resultAppliedGivenUpdate.Item1;
     document.CurrentRevisionId = updateDto.NewRevisionId;
     document.CurrentHash       = GetHash(document.Content);
     if (!document.CurrentHash.SequenceEqual(updateDto.NewHash))
     {
         //oho... should be the same, something went terribly wrong
         ReOpenDocument(document.Id);
     }
 }
Beispiel #9
0
        private UpdateDto CreateUpdateDto(Document document, string content)
        {
            var updateDto = new UpdateDto
            {
                DocumentId         = document.Id,
                PreviousRevisionId = document.CurrentRevisionId,
                PreviousHash       = document.CurrentHash,
                Patch      = _diffMatchPatch.patch_make(document.Content, content),
                MemberName = _memberName,
                MemberHost = _serverHost
            };

            return(updateDto);
        }
Beispiel #10
0
        private bool MergePendingUpdateAfterGivenUpdate(UpdateDto updateDto, UpdateDto pendingUpdate)
        {
            // it's enough to set the previous hash to the hash of the given update
            pendingUpdate.PreviousHash = updateDto.NewHash;

            // Merge screen and update
            var documentId   = updateDto.DocumentId;
            var result       = _diffMatchPatch.patch_apply(updateDto.Patch, _editor.GetText(documentId));
            var everythingOk = CheckResultIsValidOtherwiseReOpen(result, documentId);

            if (everythingOk)
            {
                _editor.UpdateText(documentId, result.Item1);
            }
            return(everythingOk);
        }
Beispiel #11
0
 private void ApplyUpdate(Document document, UpdateDto dto)
 {
     if (document.CurrentRevisionId == dto.PreviousRevisionId && document.CurrentHash.SequenceEqual(dto.PreviousHash))
     {
         MergeUpdate(document, dto);
     }
     else if (document.OutOfSyncUpdate == null)
     {
         //update out of sync. Got an update which is not based on previous revision
         document.OutOfSyncUpdate = dto;
     }
     else
     {
         //too many out of sync updates, need to re-open the document
         ReOpenDocument(dto.DocumentId);
     }
 }
Beispiel #12
0
        public void UpdateRequest(UpdateDto dto)
        {
            //do we know such a document?
            if (_documents.ContainsKey(dto.DocumentId))
            {
                var document = _documents[dto.DocumentId];


                //I am the owner/server?
                if (document.Owner == _memberName)
                {
                    _editor.UpdateNumberOfEditors(document.Id, document.EditorCount);
                    CreatePatchForUpdate(document, dto);
                }
                else
                {
                    _editor.UpdateNumberOfEditors(document.Id, dto.EditorCount);
                    ApplyUpdate(document, dto);
                }
            }
        }
 private UpdateDto CreateUpdateDto(Document document, string content)
 {
     var updateDto = new UpdateDto
     {
         DocumentId = document.Id,
         PreviousRevisionId = document.CurrentRevisionId,
         PreviousHash = document.CurrentHash,
         Patch = _diffMatchPatch.patch_make(document.Content, content),
         MemberName = _memberName,
         MemberHost = _serverHost
     };
     return updateDto;
 }
        private bool MergePendingUpdate(Document document, UpdateDto updateDto, Tuple<string, bool[]> resultAppliedGivenUpdate)
        {
            var everythingOk = true;
            var pendingUpdate = document.PendingUpdate;

            if (pendingUpdate != null)
            {
                //will the pending update be applied after the given update?
                if (MemberOfFirstUpdateIsNotOwnerAndHigherMember(pendingUpdate, updateDto))
                {
                    everythingOk = MergePendingUpdateAfterGivenUpdate(updateDto, pendingUpdate);
                }
                else if (MemberOfFirstUpdateIsOwnerOrLowerMember(pendingUpdate, updateDto))
                {
                    everythingOk = MergePendingUpdateBeforeGivenUpdate(document, updateDto, pendingUpdate,
                        resultAppliedGivenUpdate);
                }
            }
            else
            {
                //no mergin needed, only update the editor
                _editor.UpdateText(updateDto.DocumentId, resultAppliedGivenUpdate.Item1);
            }

            return everythingOk;
        }
 private bool IsFirstPreviousOfSecond(UpdateDto lastUpdate, UpdateDto updateDto)
 {
     return lastUpdate.NewRevisionId == updateDto.PreviousRevisionId &&
            lastUpdate.NewHash.SequenceEqual(updateDto.PreviousHash);
 }
        private bool MergePendingUpdateAfterGivenUpdate(UpdateDto updateDto, UpdateDto pendingUpdate)
        {
            // it's enough to set the previous hash to the hash of the given update
            pendingUpdate.PreviousHash = updateDto.NewHash;

            // Merge screen and update
            var documentId = updateDto.DocumentId;
            var result = _diffMatchPatch.patch_apply(updateDto.Patch, _editor.GetText(documentId));
            var everythingOk = CheckResultIsValidOtherwiseReOpen(result, documentId);
            if (everythingOk)
            {
                _editor.UpdateText(documentId, result.Item1);
            }
            return everythingOk;
        }
 private bool IsNotOwnUpdate(UpdateDto updateDto)
 {
     return updateDto.MemberName != _memberName;
 }
Beispiel #18
0
 private bool MemberOfFirstUpdateIsOwnerOrLowerMember(UpdateDto firstUpdateDto, UpdateDto secondUpdateDto)
 {
     return(!IsNotOwnUpdate(firstUpdateDto) || string.Compare(firstUpdateDto.MemberName, secondUpdateDto.MemberName) < 0);
 }
Beispiel #19
0
 private bool MemberOfFirstUpdateIsNotOwnerAndHigherMember(UpdateDto firstUpdateDto, UpdateDto secondUpdateDto)
 {
     return(IsNotOwnUpdate(firstUpdateDto) && string.Compare(firstUpdateDto.MemberName, secondUpdateDto.MemberName) > 0);
 }
 private bool MemberOfFirstUpdateIsOwnerOrLowerMember(UpdateDto firstUpdateDto, UpdateDto secondUpdateDto)
 {
     return !IsNotOwnUpdate(firstUpdateDto) || string.Compare(firstUpdateDto.MemberName, secondUpdateDto.MemberName) < 0;
 }
Beispiel #21
0
 private bool IsNotOwnUpdate(UpdateDto updateDto)
 {
     return(updateDto.MemberName != _memberName);
 }
 private void HandleErrorOnUpdate(UpdateDto dto)
 {
     if (IsNotOwnUpdate(dto))
     {
         _editor.ReloadDocument(dto.DocumentId);
     }
 }
        private bool MergePendingUpdateBeforeGivenUpdate(Document document, UpdateDto updateDto, UpdateDto pendingUpdate, Tuple<string, bool[]> resultAppliedGivenUpdate)
        {
            // merging happens as follows, first apply pending patch on document, then given update = new content
            // yet, since server has already applied given update we need to set pending's hash to the has of the update
            // and we need to recompute the patch of the pending patch in order that we get the same text when the ACK follows
            pendingUpdate.PreviousHash = updateDto.NewHash;

            var documentId = updateDto.DocumentId;
            var resultAppliedPendingUpdate = _diffMatchPatch.patch_apply(pendingUpdate.Patch, document.Content);
            bool everythingOk = CheckResultIsValidOtherwiseReOpen(resultAppliedPendingUpdate, documentId);
            if (everythingOk)
            {
                var resultAfterPatches = _diffMatchPatch.patch_apply(updateDto.Patch, resultAppliedPendingUpdate.Item1);
                everythingOk = CheckResultIsValidOtherwiseReOpen(resultAfterPatches, documentId);
                if (everythingOk)
                {
                    //compute new patch for pendin gupdate
                    pendingUpdate.Patch = _diffMatchPatch.patch_make(resultAppliedGivenUpdate.Item1, resultAfterPatches.Item1);

                    // Merge screen and update -> screen is built up on pending update, thus create patch pending -> screen
                    // then apply patch to new content
                    var mergePatch = _diffMatchPatch.patch_make(resultAppliedPendingUpdate.Item1, _editor.GetText(documentId));
                    var resultMerge = _diffMatchPatch.patch_apply(mergePatch, resultAfterPatches.Item1);
                    everythingOk = CheckResultIsValidOtherwiseReOpen(resultMerge, documentId);
                    if (everythingOk)
                    {
                        _editor.UpdateText(documentId, resultMerge.Item1);
                    }
                }
            }
            return everythingOk;
        }
 private void UpdateDocument(Document document, UpdateDto updateDto, Tuple<string, bool[]> resultAppliedGivenUpdate)
 {
     document.Content = resultAppliedGivenUpdate.Item1;
     document.CurrentRevisionId = updateDto.NewRevisionId;
     document.CurrentHash = GetHash(document.Content);
     if (!document.CurrentHash.SequenceEqual(updateDto.NewHash))
     {
         //oho... should be the same, something went terribly wrong
         ReOpenDocument(document.Id);
     }
 }
 private void SendUpdateToDocumentOwner(Document document, UpdateDto dto)
 {
     try
     {
         _communication.UpdateRequest(document.OwnerHost, dto);
     }
     catch (EndpointNotFoundException)
     {
         _editor.ServerUnreachable(document.Id);
     }
 }
        private void MergeUpdate(Document document, UpdateDto updateDto)
        {
            var resultAppliedGivenUpdate = _diffMatchPatch.patch_apply(updateDto.Patch, document.Content);
            if (CheckResultIsValidOtherwiseReOpen(resultAppliedGivenUpdate, updateDto.DocumentId))
            {
                if (MergePendingUpdate(document, updateDto, resultAppliedGivenUpdate))
                {
                    UpdateDocument(document, updateDto, resultAppliedGivenUpdate);
                }
            }

            //check whether we have an out of sync update which is based on the given update (so we could apply it as well)
            if (document.OutOfSyncUpdate != null && IsFirstPreviousOfSecond(updateDto, document.OutOfSyncUpdate))
            {
                var outOfSynUpdate = document.OutOfSyncUpdate;
                document.OutOfSyncUpdate = null;
                MergeUpdate(document, outOfSynUpdate);
            }

            var outOfSyncAcknowledge = document.OutOfSyncAcknowledge;
            if (outOfSyncAcknowledge != null && IsFirstPreviousOfSecond(updateDto, document.PendingUpdate))
            {
                ConfirmPendingUpdate(document, outOfSyncAcknowledge);
                document.OutOfSyncAcknowledge = null;
            }
        }
Beispiel #27
0
 private bool IsFirstPreviousOfSecond(UpdateDto lastUpdate, UpdateDto updateDto)
 {
     return(lastUpdate.NewRevisionId == updateDto.PreviousRevisionId &&
            lastUpdate.NewHash.SequenceEqual(updateDto.PreviousHash));
 }
 private bool MemberOfFirstUpdateIsNotOwnerAndHigherMember(UpdateDto firstUpdateDto, UpdateDto secondUpdateDto)
 {
     return IsNotOwnUpdate(firstUpdateDto) && string.Compare(firstUpdateDto.MemberName, secondUpdateDto.MemberName) > 0;
 }
 private void ApplyUpdate(Document document, UpdateDto dto)
 {
     if (document.CurrentRevisionId == dto.PreviousRevisionId && document.CurrentHash.SequenceEqual(dto.PreviousHash))
     {
         MergeUpdate(document, dto);
     }
     else if (document.OutOfSyncUpdate == null)
     {
         //update out of sync. Got an update which is not based on previous revision
         document.OutOfSyncUpdate = dto;
     }
     else
     {
         //too many out of sync updates, need to re-open the document
         ReOpenDocument(dto.DocumentId);
     }
 }
        private void CreatePatchForUpdate(Document document, UpdateDto updateDto)
        {
            var currentRevision = document.GetCurrentRevision();
            var lastUpdate = currentRevision.UpdateDto;
            //non existing revision - used as null object
            var secondLastUpdate = new UpdateDto {NewRevisionId = -1};
            if (document.CurrentRevisionId > FIRST_VALID_REVISON_ID)
            {
                secondLastUpdate=document.GetRevision(document.CurrentRevisionId - 1).UpdateDto;
            }

            bool creationSucessfull = false;

            //update is either based on current version
            //or on previous version where
            //   - the member which initialised the previous version was the owner itself
            //   - or a member with a lower member name (and thus the given update will be applied afterwards)
            //)
            if (IsFirstPreviousOfSecond(lastUpdate, updateDto) ||
                (IsFirstPreviousOfSecond(secondLastUpdate, updateDto)
                   && MemberOfFirstUpdateIsOwnerOrLowerMember(lastUpdate, updateDto)
                )
            )
            {
                var result = _diffMatchPatch.patch_apply(updateDto.Patch, document.Content);
                if (result.Item2.All(x => x))
                {
                    document.Content = result.Item1;
                    creationSucessfull = true;
                }else
                {
                   HandleErrorOnUpdate(updateDto);
                }
            }
            else
            {
                var revision = document.GetRevision(updateDto.PreviousRevisionId);
                if (revision.Id + SUPPORTED_NUM_OF_REACTIVE_UPDATES >= currentRevision.Id)
                {
                    var nextRevision = document.GetRevision(revision.Id + 1);
                    //move to next revision as long as
                    while (
                        nextRevision.UpdateDto.PreviousHash.SequenceEqual(updateDto.PreviousHash)
                        && MemberOfFirstUpdateIsNotOwnerAndHigherMember(updateDto, nextRevision.UpdateDto)
                        && nextRevision.Id < currentRevision.Id)
                    {
                        revision = nextRevision;
                        nextRevision = document.GetRevision(nextRevision.Id + 1);
                    }

                    var content = revision.Content;
                    var tmpRevision = revision;
                    var patch = updateDto.Patch;

                    //apply all patches on top of the found revision
                    while (tmpRevision.Id <= currentRevision.Id)
                    {
                        var result = _diffMatchPatch.patch_apply(patch, content);
                        if (result.Item2.All(x => x))
                        {
                            content = result.Item1;
                            if (tmpRevision.Id == currentRevision.Id)
                            {
                                break;
                            }
                            tmpRevision = document.GetRevision(tmpRevision.Id + 1);
                            patch = tmpRevision.UpdateDto.Patch;
                        }
                        else
                        {
                            HandleErrorOnUpdate(updateDto);
                        }
                    }
                    document.Content = content;
                    updateDto.Patch = _diffMatchPatch.patch_make(currentRevision.Content, content);
                    creationSucessfull = true;
                }
            }

            if (creationSucessfull)
            {

                document.CurrentRevisionId = currentRevision.Id + 1;
                document.CurrentHash = GetHash(document.Content);
                updateDto.NewRevisionId = document.CurrentRevisionId;
                updateDto.NewHash = document.CurrentHash;
                document.AddRevision(new Revision
                {
                    Id = document.CurrentRevisionId,
                    Content = document.Content,
                    UpdateDto = updateDto
                });

                if (IsNotOwnUpdate(updateDto))
                {
                    _editor.UpdateText(updateDto.DocumentId, document.Content);

                    var acknowledgeDto = new AcknowledgeDto
                    {
                        PreviousRevisionId = updateDto.PreviousRevisionId,
                        PreviousHash = updateDto.PreviousHash,
                        NewRevisionId = updateDto.NewRevisionId,
                        NewHash = updateDto.NewHash,
                        DocumentId = document.Id
                    };

                    _communication.AckRequest(updateDto.MemberHost, acknowledgeDto);
                }

                var newUpdateDto = new UpdateDto
                {
                    DocumentId = document.Id,
                    MemberName = updateDto.MemberName,
                    MemberHost = _serverHost,
                    PreviousRevisionId = updateDto.PreviousRevisionId,
                    PreviousHash = updateDto.PreviousHash,
                    NewRevisionId = updateDto.NewRevisionId,
                    NewHash = updateDto.NewHash,
                    Patch = updateDto.Patch,
                    EditorCount = document.EditorCount
                };

                foreach (var editorHost in document.Editors().Values)
                {
                    if (updateDto.MemberHost != editorHost)
                    {
                        try
                        {
                            _communication.UpdateRequest(editorHost, newUpdateDto);
                        }
                        catch (EndpointNotFoundException)
                        {
                            document.Editors().Remove(editorHost);
                        }
                    }
                }
            }
            else if (IsNotOwnUpdate(updateDto))
            {
                HandleErrorOnUpdate(updateDto);
            }
        }
Beispiel #31
0
        private void CreatePatchForUpdate(Document document, UpdateDto updateDto)
        {
            var currentRevision = document.GetCurrentRevision();
            var lastUpdate      = currentRevision.UpdateDto;
            //non existing revision - used as null object
            var secondLastUpdate = new UpdateDto {
                NewRevisionId = -1
            };

            if (document.CurrentRevisionId > FIRST_VALID_REVISON_ID)
            {
                secondLastUpdate = document.GetRevision(document.CurrentRevisionId - 1).UpdateDto;
            }

            bool creationSucessfull = false;

            //update is either based on current version
            //or on previous version where
            //   - the member which initialised the previous version was the owner itself
            //   - or a member with a lower member name (and thus the given update will be applied afterwards)
            //)
            if (IsFirstPreviousOfSecond(lastUpdate, updateDto) ||
                (IsFirstPreviousOfSecond(secondLastUpdate, updateDto) &&
                 MemberOfFirstUpdateIsOwnerOrLowerMember(lastUpdate, updateDto)
                )
                )
            {
                var result = _diffMatchPatch.patch_apply(updateDto.Patch, document.Content);
                if (result.Item2.All(x => x))
                {
                    document.Content   = result.Item1;
                    creationSucessfull = true;
                }
                else
                {
                    HandleErrorOnUpdate(updateDto);
                }
            }
            else
            {
                var revision = document.GetRevision(updateDto.PreviousRevisionId);
                if (revision.Id + SUPPORTED_NUM_OF_REACTIVE_UPDATES >= currentRevision.Id)
                {
                    var nextRevision = document.GetRevision(revision.Id + 1);
                    //move to next revision as long as
                    while (
                        nextRevision.UpdateDto.PreviousHash.SequenceEqual(updateDto.PreviousHash) &&
                        MemberOfFirstUpdateIsNotOwnerAndHigherMember(updateDto, nextRevision.UpdateDto) &&
                        nextRevision.Id < currentRevision.Id)
                    {
                        revision     = nextRevision;
                        nextRevision = document.GetRevision(nextRevision.Id + 1);
                    }

                    var content     = revision.Content;
                    var tmpRevision = revision;
                    var patch       = updateDto.Patch;

                    //apply all patches on top of the found revision
                    while (tmpRevision.Id <= currentRevision.Id)
                    {
                        var result = _diffMatchPatch.patch_apply(patch, content);
                        if (result.Item2.All(x => x))
                        {
                            content = result.Item1;
                            if (tmpRevision.Id == currentRevision.Id)
                            {
                                break;
                            }
                            tmpRevision = document.GetRevision(tmpRevision.Id + 1);
                            patch       = tmpRevision.UpdateDto.Patch;
                        }
                        else
                        {
                            HandleErrorOnUpdate(updateDto);
                        }
                    }
                    document.Content   = content;
                    updateDto.Patch    = _diffMatchPatch.patch_make(currentRevision.Content, content);
                    creationSucessfull = true;
                }
            }

            if (creationSucessfull)
            {
                document.CurrentRevisionId = currentRevision.Id + 1;
                document.CurrentHash       = GetHash(document.Content);
                updateDto.NewRevisionId    = document.CurrentRevisionId;
                updateDto.NewHash          = document.CurrentHash;
                document.AddRevision(new Revision
                {
                    Id        = document.CurrentRevisionId,
                    Content   = document.Content,
                    UpdateDto = updateDto
                });

                if (IsNotOwnUpdate(updateDto))
                {
                    _editor.UpdateText(updateDto.DocumentId, document.Content);

                    var acknowledgeDto = new AcknowledgeDto
                    {
                        PreviousRevisionId = updateDto.PreviousRevisionId,
                        PreviousHash       = updateDto.PreviousHash,
                        NewRevisionId      = updateDto.NewRevisionId,
                        NewHash            = updateDto.NewHash,
                        DocumentId         = document.Id
                    };

                    _communication.AckRequest(updateDto.MemberHost, acknowledgeDto);
                }

                var newUpdateDto = new UpdateDto
                {
                    DocumentId         = document.Id,
                    MemberName         = updateDto.MemberName,
                    MemberHost         = _serverHost,
                    PreviousRevisionId = updateDto.PreviousRevisionId,
                    PreviousHash       = updateDto.PreviousHash,
                    NewRevisionId      = updateDto.NewRevisionId,
                    NewHash            = updateDto.NewHash,
                    Patch       = updateDto.Patch,
                    EditorCount = document.EditorCount
                };

                foreach (var editorHost in document.Editors().Values)
                {
                    if (updateDto.MemberHost != editorHost)
                    {
                        try
                        {
                            _communication.UpdateRequest(editorHost, newUpdateDto);
                        }
                        catch (EndpointNotFoundException)
                        {
                            document.Editors().Remove(editorHost);
                        }
                    }
                }
            }
            else if (IsNotOwnUpdate(updateDto))
            {
                HandleErrorOnUpdate(updateDto);
            }
        }
        public void UpdateRequest(UpdateDto dto)
        {
            //do we know such a document?
            if (_documents.ContainsKey(dto.DocumentId))
            {
                var document = _documents[dto.DocumentId];

                //I am the owner/server?
                if (document.Owner == _memberName)
                {
                    _editor.UpdateNumberOfEditors(document.Id, document.EditorCount);
                    CreatePatchForUpdate(document, dto);
                }
                else
                {
                    _editor.UpdateNumberOfEditors(document.Id, dto.EditorCount);
                    ApplyUpdate(document, dto);
                }
            }
        }