Example #1
0
        /*
         * public void Put(string resourceKind, SyncDigestInfo info)
         * {
         *  if (null == resourceKind)
         *      throw new ArgumentNullException("resourceKind");
         *  if (resourceKind == String.Empty)
         *      throw new ArgumentException("Parameter value is empty.", "resourceKind");
         *
         *  if (null == info)
         *      throw new ArgumentNullException("info");
         *
         *  // TODO: Validate entry property values
         *
         *  // We set the priority on 'update'.
         *  lock (lockObj)
         *  {
         *      //try
         *      //{
         *      //    _provider.Update(resourceKind, info);
         *      //}
         *      //catch (StoreException)
         *      //{
         *      //    _provider.Add(resourceKind, info);
         *      //}
         *
         #warning WORKAROUND for bug in Update Method
         *      // WORKAROUND
         *      // Update method does not throw any exception if entry did not exist.
         *      try
         *      {
         *          _provider.Add(resourceKind, info);
         *      }
         *      catch (StoreException)
         *      {
         *          _provider.Update(resourceKind, info);
         *      }
         *  }
         * }
         * */

        //public void PersistNewer(string resourceKind, SyncState syncState)
        //{
        //    if (null == resourceKind)
        //        throw new ArgumentNullException("resourceKind");
        //    if (resourceKind == String.Empty)
        //        throw new ArgumentException("Parameter value is empty.", "resourceKind");
        //    if (null == syncState)
        //        throw new ArgumentNullException("syncState");
        //    lock (lockObj)
        //    {
        //        SyncDigestEntryInfo entry = _provider.Get(resourceKind, syncState.Endpoint);
        //        if (entry != null)
        //        {
        //            if (entry.Tick < syncState.Tick+1)
        //            {
        //                entry.Tick = syncState.Tick+1;
        //                _provider.Update(resourceKind, entry);
        //            }
        //        }
        //        else
        //        {
        //            entry = new SyncDigestEntryInfo(syncState.Endpoint, syncState.Tick+1, 0, DateTime.Now);
        //            _provider.Add(resourceKind, entry);
        //        }
        //    }
        //}


        public void PersistNewer(string resourceKind, ResSyncInfo resSyncInfo)
        {
            if (null == resourceKind)
            {
                throw new ArgumentNullException("resourceKind");
            }
            if (resourceKind == String.Empty)
            {
                throw new ArgumentException("Parameter value is empty.", "resourceKind");
            }
            if (null == resSyncInfo)
            {
                throw new ArgumentNullException("resSyncInfo");
            }

            lock (lockObj)
            {
                SyncDigestEntryInfo entry = _provider.Get(resourceKind, resSyncInfo.Endpoint);
                if (entry != null)
                {
                    if (entry.Tick < resSyncInfo.Tick + 1)
                    {
                        entry.Tick = resSyncInfo.Tick + 1;
                        _provider.Update(resourceKind, entry);
                    }
                }
                else
                {
                    entry = new SyncDigestEntryInfo(resSyncInfo.Endpoint, resSyncInfo.Tick + 1, 0, DateTime.Now);
                    _provider.Add(resourceKind, entry);
                }
            }
        }
        private Guid GetUuid(string localId, string uuidString, SupportedResourceKinds resKind)
        {
            if (String.IsNullOrEmpty(localId))
            {
                return(Guid.Empty);
            }

            CorrelatedResSyncInfo[] results = _correlatedResSyncInfoStore.GetByLocalId(resKind.ToString(),
                                                                                       new string[] { localId });
            if (results.Length > 0)
            {
                return(results[0].ResSyncInfo.Uuid);
            }
            Guid result;

            if (string.IsNullOrEmpty(uuidString))
            {
                result = Guid.NewGuid();
            }
            else
            {
                try
                {
                    GuidConverter converter = new GuidConverter();
                    result = (Guid)converter.ConvertFromString(uuidString);
                    if (Guid.Empty.Equals(result))
                    {
                        result = Guid.NewGuid();
                    }
                }
                catch (Exception)
                {
                    result = Guid.NewGuid();
                }
            }

            ResSyncInfo           newResSyncInfo = new ResSyncInfo(result, _context.DatasetLink + resKind.ToString(), 0, string.Empty, DateTime.Now);
            CorrelatedResSyncInfo newCorrelation = new CorrelatedResSyncInfo(localId, newResSyncInfo);

            _correlatedResSyncInfoStore.Put(resKind.ToString(), newCorrelation);
            return(result);
        }
        protected Guid GetUuid(string localId, string uuidString)
        {
            if (String.IsNullOrEmpty(localId))
            {
                return(Guid.Empty);
            }

            CorrelatedResSyncInfo[] results = _correlatedResSyncInfoStore.GetByLocalId(_resourceKindString, new string[] { localId });
            if (results.Length > 0)
            {
                return(results[0].ResSyncInfo.Uuid);
            }
            Guid result;

            if (string.IsNullOrEmpty(uuidString))
            {
                result = Guid.NewGuid();
            }
            else
            {
                try
                {
                    GuidConverter converter = new GuidConverter();
                    result = (Guid)converter.ConvertFromString(uuidString);
                    if (Guid.Empty.Equals(result))
                    {
                        result = Guid.NewGuid();
                    }
                }
                catch (Exception)
                {
                    result = Guid.NewGuid();
                }
            }

            ResSyncInfo           newResSyncInfo = new ResSyncInfo(result, _originApplication, 0, string.Empty, DateTime.Now);
            CorrelatedResSyncInfo newCorrelation = new CorrelatedResSyncInfo(localId, newResSyncInfo);

            _correlatedResSyncInfoStore.Put(_resourceKindString, newCorrelation);
            return(result);
        }
        private void PersistRelations(List <TransactionResult> transactions)
        {
            GuidConverter guidConverter = new GuidConverter();

            foreach (TransactionResult transaction in transactions)
            {
                if (transaction.Status == TransactionStatus.Success)
                {
                    SupportedResourceKinds resourceKind = GetSupportedResourceKind(transaction.EntityName);
                    if (resourceKind == SupportedResourceKinds.None)
                    {
                        continue;
                    }

                    string uuidString = transaction.CRMID;
                    Guid   uuid       = Guid.Empty;
                    try
                    {
                        uuid = (Guid)guidConverter.ConvertFromString(uuidString);
                    }
                    catch
                    {
                        continue;
                    }
                    if (uuid == Guid.Empty)
                    {
                        continue;
                    }

                    string      localId     = transaction.ID;
                    ResSyncInfo resSyncInfo = new ResSyncInfo(uuid, _context.DatasetLink + resourceKind.ToString(), -1, "", DateTime.Now);

                    CorrelatedResSyncInfo correlatedResSyncInfo = new CorrelatedResSyncInfo(localId, resSyncInfo);

                    // store the new correlation to the sync store
                    _correlatedResSyncInfoStore.Put(resourceKind.ToString(), correlatedResSyncInfo);
                }
            }
        }
Example #5
0
        public void DoWork(IRequest request)
        {
            // only atom entry supported!!!
            if (request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry)
            {
                throw new RequestException("Atom entry content type expected");
            }

            // deserialize the request stream to a SyncFeedEntry
            SyncFeedEntry entry  = new SyncFeedEntry();
            XmlReader     reader = XmlReader.Create(request.Stream);

            reader.MoveToContent();
            entry.ReadXml(reader, typeof(SyncDigestPayload));

            if (null == entry.Linked)
            {
                throw new RequestException("Invalid content: element 'linked' missing");
            }

            // We check for equality of the urls of the request url and
            // in the linked element up to the resourceKind.

            string         requestEndpointUrl            = _requestContext.OriginEndPoint;
            RequestContext entryResourceAsRequestContext = new RequestContext(new Sage.Common.Syndication.SDataUri(entry.Linked.Resource));    // TODO: not really nice here.
            string         linkedResourceUrl             = entryResourceAsRequestContext.OriginEndPoint;

            if (!string.Equals(requestEndpointUrl, linkedResourceUrl, StringComparison.InvariantCultureIgnoreCase))
            {
                throw new RequestException("Request url and linked entry resource not matching.");
            }

            string resourceKindName = _requestContext.ResourceKind.ToString();

            Guid   currentUuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(_requestContext.ResourceKey);
            Guid   newUuid     = entry.Linked.Uuid;
            string newLocalId  = entryResourceAsRequestContext.SdataUri.CollectionPredicate;

            // update the correlation entry in the sync store.
            // if the uuid should be updated we have to remove the current correlation and then add a new one.
            // otherwise we update the current correlation.
            ICorrelatedResSyncInfoStore correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext);
            // create the target correlation
            ResSyncInfo           targetResSyncInfo = new ResSyncInfo(newUuid, requestEndpointUrl, 0, string.Empty, DateTime.Now);
            CorrelatedResSyncInfo targetInfo        = new CorrelatedResSyncInfo(newLocalId, targetResSyncInfo);

            if (currentUuid == newUuid)
            {
                correlatedResSyncInfoStore.Update(resourceKindName, targetInfo);
            }
            else
            {
                correlatedResSyncInfoStore.Delete(resourceKindName, currentUuid);
                correlatedResSyncInfoStore.Add(resourceKindName, targetInfo);
            }

            // create response
            SyncFeed feed = new SyncFeed();

            feed.FeedType = FeedType.LinkedSingle;

            // create entry
            SyncFeedEntry responseEntry = new SyncFeedEntry();

            responseEntry.Id    = FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, targetInfo.ResSyncInfo.Uuid);
            responseEntry.Title = FeedMetadataHelpers.BuildLinkedEntryTitle(_requestContext, targetInfo.ResSyncInfo.Uuid);

            LinkedElement linkedElement = new LinkedElement();

            linkedElement.Resource = FeedMetadataHelpers.BuildEntryResourceUrl(_requestContext, targetInfo.LocalId);
            linkedElement.Uuid     = targetInfo.ResSyncInfo.Uuid;
            responseEntry.Linked   = linkedElement;

            feed.Entries.Add(responseEntry);

            request.Response.Serializer  = new SyncFeedSerializer();
            request.Response.Feed        = (IFeed)feed;
            request.Response.ContentType = MediaType.AtomEntry;
        }
            // Asynchronous called method
            private void Execute(NorthwindConfig config, SyncFeedDigest syncDigestInfo)
            {
                #region Declaration

                SdataContext                sdataContext;
                SupportedResourceKinds      resource;
                IAppBookmarkInfoStore       appBookmarkInfoStore;
                ICorrelatedResSyncInfoStore correlatedResSyncInfoStore;
                ISyncSyncDigestInfoStore    syncDigestStore;
                ISyncTickProvider           tickProvider;
                string         resourceKind;
                string         endpoint;
                int            nextTick = 0;
                Token          lastToken;
                Token          nextToken;
                Identity[]     changedIdentites;
                IEntityWrapper wrapper;

                #endregion

                #region init

                sdataContext               = _parentPerformer._requestContext.SdataContext;
                resource                   = _parentPerformer._requestContext.ResourceKind;
                resourceKind               = resource.ToString();
                endpoint                   = _parentPerformer._requestContext.DatasetLink + resourceKind;
                appBookmarkInfoStore       = RequestReceiver.NorthwindAdapter.StoreLocator.GetAppBookmarkStore(sdataContext);
                correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(sdataContext);
                syncDigestStore            = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext);
                tickProvider               = RequestReceiver.NorthwindAdapter.StoreLocator.GetTickProvider(sdataContext);

                wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext);

                #endregion

                #region get last token or create a new one

                if (!appBookmarkInfoStore.Get <Token>(resourceKind, out lastToken))
                {
                    lastToken             = new Token();
                    lastToken.InitRequest = true;
                }

                #endregion


                #region Get local identities of changed entries since last synchronisation

                changedIdentites = wrapper.Entity.GetLastChanges(lastToken, config, out nextToken);

                #endregion

                if (resource == SupportedResourceKinds.phoneNumbers)
                {
                    #region workaround for phones
                    for (int index = 0; index < changedIdentites.Length; index++)
                    {
                        string phoneid = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.PhoneIdPostfix;
                        string faxId   = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.FaxIdPostfix;


                        // receive the feed entry for local identity
                        SyncFeedEntry phoneentry = wrapper.GetFeedEntry(phoneid);
                        SyncFeedEntry faxentry   = wrapper.GetFeedEntry(faxId);
                        if (phoneentry == null && faxentry == null)
                        {
                            continue;
                        }
                        // receive the correlation for the local identity


                        if (phoneentry != null)
                        {
                            CorrelatedResSyncInfo[] correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { phoneid });



                            string etag = EtagServices.ComputeEtag(phoneentry.Payload, true);   // new etag
                            if (correlatedResSyncInfos.Length == 0)
                            {
                                nextTick = tickProvider.CreateNextTick(resourceKind); // create next tick

                                ResSyncInfo           resyncInfo = new ResSyncInfo(Guid.NewGuid(), endpoint, nextTick, etag, DateTime.Now);
                                CorrelatedResSyncInfo info       = new CorrelatedResSyncInfo(phoneid, resyncInfo);

                                correlatedResSyncInfoStore.Put(resourceKind, info);

                                syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo);
                            }
                            else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag))
                            {
                                nextTick = tickProvider.CreateNextTick(resourceKind);
                                correlatedResSyncInfos[0].ResSyncInfo.Etag          = etag;
                                correlatedResSyncInfos[0].ResSyncInfo.Tick          = nextTick;
                                correlatedResSyncInfos[0].ResSyncInfo.Endpoint      = endpoint;
                                correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now;
                                correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]);

                                syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo);
                            }
                        }
                        if (faxentry != null)
                        {
                            CorrelatedResSyncInfo[] correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { faxId });

                            string etag = EtagServices.ComputeEtag(faxentry.Payload, true);   // new etag
                            if (correlatedResSyncInfos.Length == 0)
                            {
                                nextTick = tickProvider.CreateNextTick(resourceKind); // create next tick

                                ResSyncInfo           resyncInfo = new ResSyncInfo(Guid.NewGuid(), endpoint, nextTick, etag, DateTime.Now);
                                CorrelatedResSyncInfo info       = new CorrelatedResSyncInfo(faxId, resyncInfo);

                                correlatedResSyncInfoStore.Put(resourceKind, info);

                                syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo);
                            }
                            else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag))
                            {
                                nextTick = tickProvider.CreateNextTick(resourceKind);
                                correlatedResSyncInfos[0].ResSyncInfo.Etag          = etag;
                                correlatedResSyncInfos[0].ResSyncInfo.Tick          = nextTick;
                                correlatedResSyncInfos[0].ResSyncInfo.Endpoint      = endpoint;
                                correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now;
                                correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]);

                                syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo);
                            }
                        }
                    }
                    #endregion
                }
                else
                {
                    for (int index = 0; index < changedIdentites.Length; index++)
                    {
                        string id = changedIdentites[index].Id;
                        // receive the feed entry for local identity
                        SyncFeedEntry entry = wrapper.GetFeedEntry(id);
                        if (entry == null)
                        {
                            continue;
                        }
                        // receive the correlation for the local identity



                        CorrelatedResSyncInfo[] correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { id });



                        string etag = EtagServices.ComputeEtag(entry.Payload, true);   // new etag
                        if (correlatedResSyncInfos.Length == 0)
                        {
                            nextTick = tickProvider.CreateNextTick(resourceKind); // create next tick

                            ResSyncInfo           resyncInfo = new ResSyncInfo(Guid.NewGuid(), endpoint, nextTick, etag, DateTime.Now);
                            CorrelatedResSyncInfo info       = new CorrelatedResSyncInfo(id, resyncInfo);

                            correlatedResSyncInfoStore.Put(resourceKind, info);

                            syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo);
                        }
                        else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag))
                        {
                            nextTick = tickProvider.CreateNextTick(resourceKind);
                            correlatedResSyncInfos[0].ResSyncInfo.Etag          = etag;
                            correlatedResSyncInfos[0].ResSyncInfo.Tick          = nextTick;
                            correlatedResSyncInfos[0].ResSyncInfo.Endpoint      = endpoint;
                            correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now;
                            correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]);

                            syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo);
                        }
                    }
                }

                #region store next token

                appBookmarkInfoStore.Put(resourceKind, nextToken);

                #endregion

                // set tracking phase
                lock (_parentPerformer._asyncStateObj)
                {
                    _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.GETCHANGESBYTICK;
                }


                // Receive syncDigestInfo
                if (null != syncDigestInfo)
                {
                    ICorrelatedResSyncInfoEnumerator enumerator;
                    List <string> endpoints = new List <string>();

                    foreach (SyncFeedDigestEntry digestEntry in syncDigestInfo.Entries)
                    {
                        endpoints.Add(digestEntry.Endpoint);
                        enumerator = correlatedResSyncInfoStore.GetSinceTick(resourceKind, digestEntry.Endpoint, digestEntry.Tick - 2);
                        while (enumerator.MoveNext())
                        {
                            // No lock needed, as we expect that CorrelatedResSyncInfos list is
                            // only acceeded anywhere else when Tracking phase is 'finish'.
                            //lock(_parentPerformer._asyncStateObj)
                            //{
                            _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current);
                            //}
                        }
                    }

                    SyncDigestInfo sourceSyncDigestInfo = syncDigestStore.Get(resourceKind);
                    foreach (SyncDigestEntryInfo digestEntry in sourceSyncDigestInfo)
                    {
                        if (endpoints.Contains(digestEntry.Endpoint))
                        {
                            continue;
                        }
                        endpoints.Add(digestEntry.Endpoint);
                        enumerator = correlatedResSyncInfoStore.GetSinceTick(resourceKind, digestEntry.Endpoint, -1);
                        while (enumerator.MoveNext())
                        {
                            // No lock needed, as we expect that CorrelatedResSyncInfos list is
                            // only acceeded anywhere else when Tracking phase is 'finish'.
                            //lock(_parentPerformer._asyncStateObj)
                            //{
                            _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current);
                            //}
                        }
                    }
                    if (!endpoints.Contains(endpoint))
                    {
                        enumerator = correlatedResSyncInfoStore.GetSinceTick(resourceKind, endpoint, -1);
                        while (enumerator.MoveNext())
                        {
                            // No lock needed, as we expect that CorrelatedResSyncInfos list is
                            // only acceeded anywhere else when Tracking phase is 'finish'.
                            //lock(_parentPerformer._asyncStateObj)
                            //{
                            _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current);
                            //}
                        }
                    }
                }



                // Set tracking phase
                lock (_parentPerformer._asyncStateObj.Tracking)
                {
                    _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.FINISH;
                }
            }
        public void Create(FeedEntry entry)
        {
            // only atom entry supported!!!

            if (_request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry)
            {
                throw new RequestException("Atom entry content type expected");
            }

            string         requestEndPointUrl;
            RequestContext entryResourceAsRequestContext;

            string url;             // The url that references an existing resource
            string localId;         // will be parsed from urlAttrValue and set to the key attribute of the result resource payload
            Guid   uuid;            // the new uuid

            if (null == entry)
            {
                throw new RequestException("sdata payload element missing");
            }

            // the consumer MUST provide an sdata:url attribute that references an existing resource.
            url = entry.Uri;
            if (string.IsNullOrEmpty(url))
            {
                throw new RequestException("sdata url attribute missing for resource payload element.");
            }


            // Parse the url of thew url attribute to get the local id.
            // Additionally we check for equality of the urls of the request url and
            // in the linked element up to the resourceKind.
            requestEndPointUrl            = _requestContext.OriginEndPoint;
            entryResourceAsRequestContext = new RequestContext(new Sage.Common.Syndication.SDataUri(url));    // TODO: not really nice here.
            string linkedResourceUrl = entryResourceAsRequestContext.OriginEndPoint;

            if (!string.Equals(requestEndPointUrl, linkedResourceUrl, StringComparison.InvariantCultureIgnoreCase))
            {
                throw new RequestException("Request url and linked entry resource not matching.");
            }

            string resourceKindName = _requestContext.ResourceKind.ToString();

            localId = entryResourceAsRequestContext.ResourceKey;

            ICorrelatedResSyncInfoStore correlatedResSyncInfoStore = NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext);

            CheckExisting(correlatedResSyncInfoStore, localId);

            // try to get the new uuid from uuid attribute
            // if this attribute is not set a new one is created
            if (null == entry.UUID || entry.UUID == Guid.Empty)
            {
                uuid = Guid.NewGuid();
            }
            else
            {
                uuid = entry.UUID;
            }


            // store a new correlation entry to the sync store
            ResSyncInfo           newResSyncInfo = new ResSyncInfo(uuid, requestEndPointUrl, 0, string.Empty, DateTime.Now);
            CorrelatedResSyncInfo newInfo        = new CorrelatedResSyncInfo(localId, newResSyncInfo);

            correlatedResSyncInfoStore.Add(resourceKindName, newInfo);



            // If the service consumer only needs to retrieve the URL, not the actual payload,
            // it may do so by adding an empty select parameter to its request:
            // a) select parameter not exist -> return deep resource payload
            // b) select exists and empty -> return no resource payload
            // c) select exists and not empty -> return deep resource payload as we do not yet support payload filtering
            //    with select parameter.
            string tmpValue;
            // ?select
            bool includeResourcePayloads = true;    // default value, but check for select parameter now

            if (_requestContext.SdataUri.QueryArgs.TryGetValue("select", out tmpValue))
            {
                if (string.IsNullOrEmpty(_requestContext.SdataUri.QueryArgs["select"]))
                {
                    includeResourcePayloads = false;
                }
            }


            // Create an entity wrapper if resource data should be requested. Otherwise
            // leave wrapper null.
            IFeedEntryEntityWrapper wrapper = null;

            //if (includeResourcePayloads) //TODO: Comment this in as soon as there is a possibility to send empty payload objects
            wrapper = FeedEntryWrapperFactory.Create(_requestContext.ResourceKind, _requestContext);

            /* Create the response entry */
            _request.Response.FeedEntry   = (FeedEntry)this.BuildFeedEntryForCorrelation(newInfo, wrapper);
            _request.Response.ContentType = MediaType.AtomEntry;

            _request.Response.StatusCode = System.Net.HttpStatusCode.Created;
            _request.Response.Protocol.SendUnknownResponseHeader("location", FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, uuid));
        }
        public void DoWork(IRequest request)
        {
            // only atom entry supported!!!
            if (request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry)
            {
                throw new RequestException("Atom entry content type expected");
            }

            // deserialize the request stream to a SyncFeedEntry
            SyncFeedEntry entry  = new SyncFeedEntry();
            XmlReader     reader = XmlReader.Create(request.Stream);

            reader.MoveToContent();
            entry.ReadXml(reader, typeof(SyncDigestPayload));

            if (null == entry.Linked)
            {
                throw new RequestException("Invalid content: element 'linked' missing");
            }

            // Parse the resource url to get the local id.
            // Additionally we check for equality of the urls of the request url and
            // in the linked element up to the resourceKind.

            string         requestEndpointUrl            = _requestContext.OriginEndPoint;
            RequestContext entryResourceAsRequestContext = new RequestContext(new Sage.Common.Syndication.SDataUri(entry.Linked.Resource));    // TODO: not really nice here.
            string         linkedResourceUrl             = entryResourceAsRequestContext.OriginEndPoint;

            if (!string.Equals(requestEndpointUrl, linkedResourceUrl, StringComparison.InvariantCultureIgnoreCase))
            {
                throw new RequestException("Request url and linked entry resource not matching.");
            }

            string resourceKindName = _requestContext.ResourceKind.ToString();
            string localId          = entryResourceAsRequestContext.SdataUri.CollectionPredicate;
            Guid   uuid             = entry.Linked.Uuid;

            // store a new correlation entry to the sync store
            ICorrelatedResSyncInfoStore correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext);

            ResSyncInfo           newResSyncInfo = new ResSyncInfo(uuid, requestEndpointUrl, 0, string.Empty, DateTime.Now);
            CorrelatedResSyncInfo newInfo        = new CorrelatedResSyncInfo(localId, newResSyncInfo);

            correlatedResSyncInfoStore.Add(resourceKindName, newInfo);

            // Set the response values
            SyncFeed feed = new SyncFeed();

            feed.FeedType = FeedType.LinkedSingle;

            SyncFeedEntry feedEntry = new SyncFeedEntry();

            feedEntry.Title     = FeedMetadataHelpers.BuildLinkedEntryTitle(_requestContext, uuid);
            feedEntry.Id        = FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, uuid);
            feedEntry.Updated   = newInfo.ResSyncInfo.ModifiedStamp;
            feedEntry.Published = newInfo.ResSyncInfo.ModifiedStamp;
            feedEntry.Linked    = entry.Linked;

            feed.Entries.Add(feedEntry);


            request.Response.Serializer  = new SyncFeedSerializer();
            request.Response.Feed        = (IFeed)feed;
            request.Response.ContentType = MediaType.AtomEntry;

            request.Response.StatusCode = System.Net.HttpStatusCode.Created;
            request.Response.Protocol.SendUnknownResponseHeader("location", FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, uuid));
        }
            // Asynchronous called method
            private void Execute(NorthwindConfig config, Digest targetDigest)
            {
                #region Declaration

                SdataContext                sdataContext;
                SupportedResourceKinds      resource;
                IAppBookmarkInfoStore       appBookmarkInfoStore;
                ICorrelatedResSyncInfoStore correlatedResSyncInfoStore;
                ISyncSyncDigestInfoStore    syncDigestStore;
                ISynctickProvider           tickProvider;
                string     resourceKind;
                string     EndPoint;
                int        nexttick = 1;
                Token      lastToken;
                Token      nextToken;
                Identity[] changedIdentites;
                IFeedEntryEntityWrapper wrapper;

                #endregion

                #region init

                sdataContext               = _parentPerformer._requestContext.SdataContext;
                resource                   = _parentPerformer._requestContext.ResourceKind;
                resourceKind               = resource.ToString();
                EndPoint                   = _parentPerformer._requestContext.DatasetLink + resourceKind;
                appBookmarkInfoStore       = NorthwindAdapter.StoreLocator.GetAppBookmarkStore(sdataContext);
                correlatedResSyncInfoStore = NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(sdataContext);
                syncDigestStore            = NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext);
                tickProvider               = NorthwindAdapter.StoreLocator.GettickProvider(sdataContext);

                wrapper = FeedEntryWrapperFactory.Create(resource, _parentPerformer._requestContext);

                #endregion

                #region get last token or create a new one

                if (!appBookmarkInfoStore.Get <Token>(resourceKind, out lastToken))
                {
                    lastToken             = new Token();
                    lastToken.InitRequest = true;
                }

                #endregion


                #region Get local identities of changed entries since last synchronisation

                changedIdentites = wrapper.Entity.GetLastChanges(lastToken, config, out nextToken);

                #endregion

                if (resource == SupportedResourceKinds.phoneNumbers)
                {
                    #region workaround for phones
                    for (int index = 0; index < changedIdentites.Length; index++)
                    {
                        string phoneid = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.PhoneIdPostfix;
                        string faxId   = changedIdentites[index].Id + Sage.Integration.Northwind.Application.API.Constants.FaxIdPostfix;


                        // receive the resource payloads for the phone id and for the fax id
                        // so that we can calculate their current etags.
                        FeedEntry phoneResourcePayloadContainer = wrapper.GetFeedEntry(phoneid);
                        FeedEntry faxResourcePayloadConatainer  = wrapper.GetFeedEntry(faxId);

                        string etag;
                        CorrelatedResSyncInfo[] correlatedResSyncInfos;

                        #region Phone
                        if (phoneResourcePayloadContainer != null)
                        {
                            // calculate etag of the resource
                            etag = EtagServices.ComputeEtag(phoneResourcePayloadContainer, true);   // new etag

                            // retrieve correlations for the current identity from synch storage
                            correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { phoneid });

                            // if no correlation exists AND resource has been deleted:
                            // -> continue with next id
                            // else if no correlation exists:
                            // -> get next tick, create new correlation and add correlation to synch store
                            // otherwise and if the etag stored in synch store is different than etag previously calculated:
                            // -> get next tick, modify existing correlation and update it in synch store.
                            if (correlatedResSyncInfos.Length == 0 && phoneResourcePayloadContainer.IsDeleted)
                            {
                                continue;
                            }
                            else if (correlatedResSyncInfos.Length == 0)
                            {
                                nexttick = tickProvider.CreateNexttick(resourceKind); // create next tick

                                ResSyncInfo           resyncInfo = new ResSyncInfo(Guid.NewGuid(), EndPoint, nexttick, etag, DateTime.Now);
                                CorrelatedResSyncInfo info       = new CorrelatedResSyncInfo(phoneid, resyncInfo);

                                correlatedResSyncInfoStore.Put(resourceKind, info);

                                syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo);
                            }
                            else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag))
                            {
                                nexttick = tickProvider.CreateNexttick(resourceKind);
                                correlatedResSyncInfos[0].ResSyncInfo.Etag          = etag;
                                correlatedResSyncInfos[0].ResSyncInfo.Tick          = nexttick;
                                correlatedResSyncInfos[0].ResSyncInfo.EndPoint      = EndPoint;
                                correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now;
                                correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]);

                                syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo);
                            }
                        }

                        #endregion

                        #region Fax
                        if (faxResourcePayloadConatainer != null)
                        {
                            // calculate etag of the resource
                            etag = EtagServices.ComputeEtag(faxResourcePayloadConatainer, true);   // new etag

                            // retrieve correlations for the current identity from synch storage
                            correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { faxId });

                            // if no correlation exists AND resource has been deleted:
                            // -> continue with next id
                            // else if no correlation exists:
                            // -> get next tick, create new correlation and add correlation to synch store
                            // otherwise and if the etag stored in synch store is different than etag previously calculated:
                            // -> get next tick, modify existing correlation and update it in synch store.
                            if (correlatedResSyncInfos.Length == 0 && faxResourcePayloadConatainer.IsDeleted)
                            {
                                continue;
                            }
                            else if (correlatedResSyncInfos.Length == 0)
                            {
                                nexttick = tickProvider.CreateNexttick(resourceKind); // create next tick

                                ResSyncInfo           resyncInfo = new ResSyncInfo(Guid.NewGuid(), EndPoint, nexttick, etag, DateTime.Now);
                                CorrelatedResSyncInfo info       = new CorrelatedResSyncInfo(faxId, resyncInfo);

                                correlatedResSyncInfoStore.Put(resourceKind, info);

                                syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo);
                            }
                            else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag))
                            {
                                nexttick = tickProvider.CreateNexttick(resourceKind);
                                correlatedResSyncInfos[0].ResSyncInfo.Etag          = etag;
                                correlatedResSyncInfos[0].ResSyncInfo.Tick          = nexttick;
                                correlatedResSyncInfos[0].ResSyncInfo.EndPoint      = EndPoint;
                                correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now;
                                correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]);

                                syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo);
                            }
                        }

                        #endregion
                    }
                    #endregion
                }
                else
                {
                    string    id;
                    FeedEntry resourcePayloadContainer;
                    CorrelatedResSyncInfo[] correlatedResSyncInfos;
                    string etag;

                    // iterate through the collection of ids of all changed resources.
                    for (int index = 0; index < changedIdentites.Length; index++)
                    {
                        // current id from iterated collection
                        id = changedIdentites[index].Id;

                        // get resource payload container for the current identity so that we can calculated the
                        // etag.
                        // continue with next identity if no resource was found.
                        resourcePayloadContainer = wrapper.GetFeedEntry(id);

                        // calculate etag of the current resource payload
                        etag = EtagServices.ComputeEtag(resourcePayloadContainer, true);


                        // retrieve correlations for the current identity from synch storage
                        correlatedResSyncInfos = correlatedResSyncInfoStore.GetByLocalId(resourceKind, new string[] { id });


                        // if no correlation exists AND resource has been deleted:
                        // -> continue with next id
                        // else if no correlation exists:
                        // -> get next tick, create new correlation and add correlation to synch store
                        // otherwise and if the etag stored in synch store is different than etag previously calculated:
                        // -> get next tick, modify existing correlation and update it in synch store.
                        if (resourcePayloadContainer == null || (correlatedResSyncInfos.Length == 0 && resourcePayloadContainer.IsDeleted))
                        {
                            continue;
                        }
                        else if (correlatedResSyncInfos.Length == 0)
                        {
                            nexttick = tickProvider.CreateNexttick(resourceKind); // create next tick

                            ResSyncInfo           resyncInfo = new ResSyncInfo(Guid.NewGuid(), EndPoint, nexttick, etag, DateTime.Now);
                            CorrelatedResSyncInfo info       = new CorrelatedResSyncInfo(id, resyncInfo);

                            correlatedResSyncInfoStore.Put(resourceKind, info);

                            syncDigestStore.PersistNewer(resourceKind, info.ResSyncInfo);
                        }
                        else if (!correlatedResSyncInfos[0].ResSyncInfo.Etag.Equals(etag))
                        {
                            nexttick = tickProvider.CreateNexttick(resourceKind);
                            correlatedResSyncInfos[0].ResSyncInfo.Etag          = etag;
                            correlatedResSyncInfos[0].ResSyncInfo.Tick          = nexttick;
                            correlatedResSyncInfos[0].ResSyncInfo.EndPoint      = EndPoint;
                            correlatedResSyncInfos[0].ResSyncInfo.ModifiedStamp = DateTime.Now;
                            correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfos[0]);

                            syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfos[0].ResSyncInfo);
                        }
                    }
                }

                #region store next token

                appBookmarkInfoStore.Put(resourceKind, nextToken);

                #endregion

                // set tracking phase
                lock (_parentPerformer._asyncStateObj)
                {
                    _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.GETCHANGESBYtick;
                }



                if (null != targetDigest)
                {
                    ICorrelatedResSyncInfoEnumerator enumerator;
                    List <string> EndPoints = new List <string>();
#warning remove this workaround
                    if (targetDigest.Entries != null)
                    {
                        foreach (DigestEntry targetDigestEntry in targetDigest.Entries)
                        {
                            EndPoints.Add(targetDigestEntry.EndPoint);
                            enumerator = correlatedResSyncInfoStore.GetSincetick(resourceKind, targetDigestEntry.EndPoint, ((int)targetDigestEntry.Tick) - 1);
                            while (enumerator.MoveNext())
                            {
                                // No lock needed, as we expect that CorrelatedResSyncInfos list is
                                // only acceeded anywhere else when Tracking phase is 'finish'.
                                //lock(_parentPerformer._asyncStateObj)
                                //{
                                _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current);
                                //}
                            }
                        }
                    }

                    SyncDigestInfo sourceSyncDigestInfo = syncDigestStore.Get(resourceKind);
                    foreach (SyncDigestEntryInfo digestEntry in sourceSyncDigestInfo)
                    {
                        if (EndPoints.Contains(digestEntry.EndPoint))
                        {
                            continue;
                        }
                        EndPoints.Add(digestEntry.EndPoint);
                        enumerator = correlatedResSyncInfoStore.GetSincetick(resourceKind, digestEntry.EndPoint, -1);
                        while (enumerator.MoveNext())
                        {
                            // No lock needed, as we expect that CorrelatedResSyncInfos list is
                            // only acceeded anywhere else when Tracking phase is 'finish'.
                            //lock(_parentPerformer._asyncStateObj)
                            //{
                            _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current);
                            //}
                        }
                    }
                    if (!EndPoints.Contains(EndPoint))
                    {
                        enumerator = correlatedResSyncInfoStore.GetSincetick(resourceKind, EndPoint, -1);
                        while (enumerator.MoveNext())
                        {
                            // No lock needed, as we expect that CorrelatedResSyncInfos list is
                            // only acceeded anywhere else when Tracking phase is 'finish'.
                            //lock(_parentPerformer._asyncStateObj)
                            //{
                            _parentPerformer._asyncStateObj.CorrelatedResSyncInfos.Add(enumerator.Current);
                            //}
                        }
                    }
                }

                // Set tracking phase to FINISH
                lock (_parentPerformer._asyncStateObj.Tracking)
                {
                    _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.FINISH;
                }
            }
            // Asynchronous called method
            private void Execute(NorthwindConfig config, SyncFeed feed)
            {
                #region Declarations

                SdataContext                sdataContext;
                SupportedResourceKinds      resource;
                IAppBookmarkInfoStore       appBookmarkInfoStore;
                ICorrelatedResSyncInfoStore correlatedResSyncInfoStore;
                ISyncSyncDigestInfoStore    syncDigestStore;

                GuidConverter  guidConverter = new GuidConverter();
                string         resourceKind;
                string         endpoint;
                SyncFeedDigest sourceDigest;
                SyncDigestInfo targetDigest;

                IEntityWrapper wrapper;

                #endregion

                #region init

                sdataContext               = _parentPerformer._requestContext.SdataContext;
                resource                   = _parentPerformer._requestContext.ResourceKind;
                resourceKind               = resource.ToString();
                endpoint                   = _parentPerformer._requestContext.DatasetLink + resourceKind;
                appBookmarkInfoStore       = RequestReceiver.NorthwindAdapter.StoreLocator.GetAppBookmarkStore(sdataContext);
                correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(sdataContext);
                syncDigestStore            = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext);
                sourceDigest               = feed.Digest;
                targetDigest               = syncDigestStore.Get(resourceKind);
                wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext);

                #endregion

                #region process entries

                bool sourceIsDeleteMode;
                SdataTransactionResult sdTrResult;
                SyncState sourceState;
                SyncState targetState;

                foreach (SyncFeedEntry entry in feed.Entries)
                {
                    sdTrResult = null;

                    try
                    {
                        // Check whether the source entry had been deleted.
                        // if not we expect a payload!
                        // The variable 'sourceIsDeleteMode' holds the result of this check for later calls.
                        if (entry.HttpMethod.Equals("DELETE", StringComparison.InvariantCultureIgnoreCase))
                        {
                            sourceIsDeleteMode = true;
                        }
                        else if (null == entry.Payload)
                        {
                            throw new Exception("Payload missing.");
                        }
                        else
                        {
                            sourceIsDeleteMode = false;
                        }


                        // set the uuid to the payload.SyncUuid property
                        if (!sourceIsDeleteMode)
                        {
                            entry.Payload.SyncUuid = entry.Uuid;
                        }

                        // get the source syncstate
                        sourceState = entry.SyncState;

                        // Look into the store to get all correlations of the uuid received from source
                        CorrelatedResSyncInfo[] corelations = correlatedResSyncInfoStore.GetByUuid(resourceKind, new Guid[] { entry.Uuid });

                        if (corelations.Length == 0)
                        {
                            if (sourceIsDeleteMode)
                            {
                                // the source entry had been deleted and no correlation exists for this
                                // entry in the target.
                                // So we do nothing here.
                                sdTrResult              = new SdataTransactionResult();
                                sdTrResult.HttpStatus   = HttpStatusCode.OK;
                                sdTrResult.HttpMessage  = "OK";
                                sdTrResult.HttpMethod   = "DELETE";
                                sdTrResult.ResourceKind = resource;
                                sdTrResult.Uuid         = entry.Uuid;
                            }
                            else
                            {
                                // no target entry exists for the updated or added source entry.
                                // So we add the source entry to the application and add a new correlation to the sync store.
                                // If this operation fails we will have to set the result state to conflict.
                                try
                                {
                                    // add the entry to application
                                    sdTrResult = wrapper.Add(entry.Payload, entry.SyncLinks);

                                    if ((sdTrResult != null) && ((sdTrResult.HttpStatus == HttpStatusCode.OK) || (sdTrResult.HttpStatus == HttpStatusCode.Created)))
                                    {
                                        string etag = EtagServices.ComputeEtag(entry.Payload, true);

                                        ResSyncInfo resSyncInfo =
                                            new ResSyncInfo(entry.Uuid, entry.SyncState.Endpoint,
                                                            entry.SyncState.Tick, etag, entry.SyncState.Stamp);

                                        CorrelatedResSyncInfo correlatedResSyncInfo = new CorrelatedResSyncInfo(sdTrResult.LocalId, resSyncInfo);

                                        // store the new correlation to the sync store
                                        correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfo);
                                        // update the sync digest entry if the tick of the source entry is newer than the actual tick of the target.
                                        syncDigestStore.PersistNewer(resourceKind, correlatedResSyncInfo.ResSyncInfo);
                                    }
                                }
                                catch (Exception e)
                                {
                                    // in case of an unexpected error while adding a new entry to target
                                    // we create a transaction result with the state 'Conflict'
                                    sdTrResult              = new SdataTransactionResult();
                                    sdTrResult.HttpStatus   = HttpStatusCode.Conflict;
                                    sdTrResult.HttpMethod   = "POST";
                                    sdTrResult.HttpMessage  = e.ToString();
                                    sdTrResult.ResourceKind = resource;
                                    sdTrResult.Uuid         = entry.Uuid;
                                }
                            }
                        }
                        else
                        {
                            // a correlation was found for the source entry.

                            #region update or delete

                            try
                            {
                                bool doUpdate = false;
                                //bool doDelete = false;
                                bool isConflict = true;

                                // set the LocalID from correlation to the entry.Payload.LocalID property
                                // only if source had not been deleted.
                                if (!sourceIsDeleteMode)
                                {
                                    entry.Payload.LocalID = corelations[0].LocalId;
                                }

                                targetState = Helper.GetSyncState(corelations[0]);


                                //If sourceState.endpoint = targetState.endpoint,
                                //there is no conflict and the update must be applied
                                //if sourceState.tick > targetState.tick.
                                if (targetState.Endpoint.Equals(sourceState.Endpoint, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    isConflict = false;
                                    if (sourceState.Tick > targetState.Tick)
                                    {
                                        //if (!sourceIsDeleteMode)
                                        doUpdate = true;
                                        //else
                                        //    doDelete = true;
                                    }
                                }
                                else
                                {
                                    SyncState sourceDigestSyncState = Helper.GetSyncState(sourceDigest, targetState.Endpoint);;
                                    SyncState targetDigestSyncState = Helper.GetSyncState(targetDigest, sourceState.Endpoint);

                                    //If targetState is contained in sourceDigest,
                                    //i.e. if sourceDigest has a digest entry E such that E.endpoint = targetState.endpoint
                                    //and E.tick >= targetState.tick, there is no conflict and the update must be applied.
                                    if (sourceDigestSyncState != null)
                                    {
                                        if (sourceDigestSyncState.Tick > targetState.Tick)
                                        {
                                            doUpdate   = true;
                                            isConflict = false;
                                        }
                                    }


                                    //If sourceState is contained in targetDigest,
                                    //i.e. if targetDigest has a digest entry E such that E.endpoint = sourceState.endpoint
                                    //and E.tick >= sourceState.tick, there is no conflict and the update must be ignored
                                    //(target has the most recent version).
                                    if (targetDigestSyncState != null)
                                    {
                                        if (targetDigestSyncState.Tick > sourceState.Tick)
                                        {
                                            doUpdate   = false;
                                            isConflict = false;
                                        }
                                    }



                                    //Otherwise (targetState not contained in sourceDigest, sourceState not contained in targetDigest),
                                    //there is a conflict.
                                    if ((sourceDigestSyncState == null) && (targetDigestSyncState == null))
                                    {
                                        isConflict = true;
                                    }
                                }

                                //****************** Conflict ****************
                                //In case of conflict, the target endpoint uses the following algorithm to resolve the conflict:
                                //Let sourceEntry be the sourceDigest digest entry such that sourceEntry.endpoint = sourceState.endpoint.
                                //Let targetEntry be the targetDigest digest entry such that targetEntry.endpoint = targetState.endpoint.
                                //If sourceEntry .conflictPriority <> targetEntry .conflictPriority, the side with lowest priority wins.
                                if (isConflict)
                                {
                                    int sourceConflictPriority = Helper.GetConflictPriority(sourceDigest, sourceState.Endpoint);
                                    int targetConflictPriority = Helper.GetConflictPriority(targetDigest, targetState.Endpoint);

                                    if (sourceConflictPriority > targetConflictPriority)
                                    {
                                        doUpdate = true;
                                    }
                                    else if (sourceConflictPriority < targetConflictPriority)
                                    {
                                        doUpdate = false;
                                    }
                                    else
                                    {
                                        //Otherwise (sourceEntry .conflictPriority = targetEntry .conflictPriority),
                                        //then sourceState.stamp and targetState.stamp are compared
                                        //and the entry with the most recent timestamp wins.
                                        if (sourceState.Stamp > targetState.Stamp)
                                        {
                                            doUpdate = true;
                                        }
                                    }
                                }
                                ResSyncInfo resSyncInfo =
                                    new ResSyncInfo(entry.Uuid, entry.SyncState.Endpoint,
                                                    entry.SyncState.Tick, "", entry.SyncState.Stamp);


                                if (doUpdate && !sourceIsDeleteMode)
                                {
                                    // update the entry in the application and update the sync store
                                    sdTrResult = wrapper.Update(entry.Payload, entry.SyncLinks);

                                    if ((sdTrResult != null) && (sdTrResult.HttpStatus == HttpStatusCode.OK))
                                    {
                                        string etag = EtagServices.ComputeEtag(entry.Payload, true);

                                        resSyncInfo =
                                            new ResSyncInfo(entry.Uuid, entry.SyncState.Endpoint,
                                                            entry.SyncState.Tick, etag, entry.SyncState.Stamp);

                                        CorrelatedResSyncInfo correlatedResSyncInfo = new CorrelatedResSyncInfo(sdTrResult.LocalId, resSyncInfo);

                                        correlatedResSyncInfoStore.Put(resourceKind, correlatedResSyncInfo);
                                    }
                                }
                                else if (!doUpdate && !sourceIsDeleteMode)
                                {
                                    sdTrResult              = new SdataTransactionResult();
                                    sdTrResult.HttpStatus   = HttpStatusCode.Conflict;
                                    sdTrResult.HttpMethod   = "PUT";
                                    sdTrResult.HttpMessage  = "";
                                    sdTrResult.ResourceKind = resource;
                                    sdTrResult.Uuid         = entry.Uuid;
                                }
                                else if (doUpdate && sourceIsDeleteMode)
                                {
                                    // delete the entry in the application and update the sync store
                                    // we do not support nested deletion. So only TradingAccounts and SalesOrders can be deleted.
                                    if (resource == SupportedResourceKinds.tradingAccounts ||
                                        resource == SupportedResourceKinds.salesOrders)
                                    {
                                        sdTrResult = wrapper.Delete(corelations[0].LocalId);

                                        if ((sdTrResult != null) && (sdTrResult.HttpStatus == HttpStatusCode.OK))
                                        {
                                            correlatedResSyncInfoStore.Delete(resource.ToString(), corelations[0].ResSyncInfo.Uuid);
                                        }
                                    }
                                    else
                                    {
                                        // this  does not correspond to real fact, what we did at target side!
                                        sdTrResult              = new SdataTransactionResult();
                                        sdTrResult.HttpStatus   = HttpStatusCode.OK;
                                        sdTrResult.HttpMessage  = "OK";
                                        sdTrResult.HttpMethod   = "DELETE";
                                        sdTrResult.ResourceKind = resource;
                                        sdTrResult.Uuid         = entry.Uuid;
                                    }
                                }
                                else
                                {
                                    sdTrResult              = new SdataTransactionResult();
                                    sdTrResult.HttpStatus   = HttpStatusCode.Conflict;
                                    sdTrResult.HttpMessage  = "";
                                    sdTrResult.HttpMethod   = "DELETE";
                                    sdTrResult.ResourceKind = resource;
                                    sdTrResult.Uuid         = entry.Uuid;
                                }

                                syncDigestStore.PersistNewer(resourceKind, resSyncInfo);
                            }
                            catch (Exception e)
                            {
                                sdTrResult              = new SdataTransactionResult();
                                sdTrResult.HttpStatus   = HttpStatusCode.Conflict;
                                sdTrResult.HttpMethod   = "PUT";
                                sdTrResult.HttpMessage  = e.ToString();
                                sdTrResult.ResourceKind = resource;
                                sdTrResult.Uuid         = entry.Uuid;
                            }
                            #endregion
                        }
                    }
                    catch (Exception e)
                    {
                        sdTrResult              = new SdataTransactionResult();
                        sdTrResult.HttpStatus   = HttpStatusCode.Conflict;
                        sdTrResult.HttpMessage  = e.ToString();
                        sdTrResult.ResourceKind = resource;
                        sdTrResult.Uuid         = entry.Uuid;
                    }

                    #region store transaction result

                    if (sdTrResult != null)
                    {
                        lock (_parentPerformer._asyncStateObj)
                        {
                            this._parentPerformer._asyncStateObj.TransactionResults.Add(sdTrResult);
                        }
                    }

                    #endregion
                }
                #endregion


                // Set tracking phase
                lock (_parentPerformer._asyncStateObj.Tracking)
                {
                    _parentPerformer._asyncStateObj.Tracking.Phase = TrackingPhase.FINISH;
                }
            }