public bool Update(CorrelatedResSyncInfo info, IJetTransaction jetTransaction) { IEndPointTableAdapter EndPointTableAdapter = StoreEnvironment.Resolve <IEndPointTableAdapter>(this.Context); EndPointInfo EndPointInfo = EndPointTableAdapter.GetOrCreate(info.ResSyncInfo.EndPoint, jetTransaction); OleDbCommand oleDbCommand = jetTransaction.CreateOleCommand(); string sqlQuery = string.Empty; // store only correlation data if tick is set to -1 if (info.ResSyncInfo.Tick == -1) { sqlQuery = "UPDATE [{0}] SET [LocalId]=@LocalId WHERE (Uuid=@Uuid);"; oleDbCommand.CommandText = string.Format(sqlQuery, _correlatedResSyncTable.TableName); oleDbCommand.Parameters.AddWithValue("@LocalId", info.LocalId); oleDbCommand.Parameters.AddWithValue("@Uuid", info.ResSyncInfo.Uuid); } else { sqlQuery = "UPDATE [{0}] SET [tick]=@tick, [ModifiedStamp]=@ModifiedStamp, [Etag]=@Etag, [LocalId]=@LocalId , [FKEndPointId]=@EndPointId WHERE (Uuid=@Uuid);"; oleDbCommand.CommandText = string.Format(sqlQuery, _correlatedResSyncTable.TableName); oleDbCommand.Parameters.AddWithValue("@tick", info.ResSyncInfo.Tick); oleDbCommand.Parameters.AddWithValue("@ModifiedStamp", info.ResSyncInfo.ModifiedStamp.ToString()); oleDbCommand.Parameters.AddWithValue("@Etag", info.ResSyncInfo.Etag); oleDbCommand.Parameters.AddWithValue("@LocalId", info.LocalId); oleDbCommand.Parameters.AddWithValue("@EndPointId", EndPointInfo.Id); oleDbCommand.Parameters.AddWithValue("@Uuid", info.ResSyncInfo.Uuid); } return(0 != oleDbCommand.ExecuteNonQuery()); }
private FeedEntry BuildFeedEntry(CorrelatedResSyncInfo correlation, IFeedEntryEntityWrapper wrapper) { // create a new empty Feed Entry //ISyncSourceResourceFeedEntry feedEntry = FeedComponentFactory.Create<ISyncSourceResourceFeedEntry>(); // get resource payload container from data store FeedEntry feedEntry = wrapper.GetSyncSourceFeedEntry(correlation); // create and set SyncState feedEntry.SyncState = new SyncState(); feedEntry.SyncState.EndPoint = correlation.ResSyncInfo.EndPoint; feedEntry.SyncState.Tick = (correlation.ResSyncInfo.Tick > 0) ? correlation.ResSyncInfo.Tick : 1; feedEntry.SyncState.Stamp = correlation.ResSyncInfo.ModifiedStamp; // set the id tag feedEntry.Id = feedEntry.Uri; // set the title tag feedEntry.Title = String.Format("{0}: {1}", _parentPerformer._requestContext.ResourceKind.ToString(), feedEntry.Key); // set the updated tag feedEntry.Updated = correlation.ResSyncInfo.ModifiedStamp.ToLocalTime(); // set resource dependent links (self, edit, schema, template, post, service) feedEntry.Links.AddRange(LinkFactory.CreateEntryLinks(_parentPerformer._requestContext, feedEntry)); return(feedEntry); }
public void Put(string resourceKind, CorrelatedResSyncInfo 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"); } // We set the priority on 'update'. lock (lockObj) { try { _provider.Update(resourceKind, info); } catch (StoreException) { _provider.Add(resourceKind, info); } } }
public static SyncState GetSyncState(CorrelatedResSyncInfo corelation) { SyncState result = new SyncState(); result.Endpoint = corelation.ResSyncInfo.Endpoint; result.Stamp = corelation.ResSyncInfo.ModifiedStamp; result.Tick = corelation.ResSyncInfo.Tick; return(result); }
protected void SetCommonProperties(Sage.Integration.Northwind.Application.Base.Document document, string descriptor, Sage.Common.Syndication.FeedEntry entry) { entry.Id = GetSDataId(document.Id); entry.Key = document.Id; CorrelatedResSyncInfo syncInfo = GetUuidAsGuid(document.Id, document.CrmId); entry.UUID = syncInfo.ResSyncInfo.Uuid; entry.Updated = syncInfo.ResSyncInfo.ModifiedStamp.ToLocalTime(); entry.Descriptor = descriptor; }
public void Update(string resourceKind, CorrelatedResSyncInfo info) { ICorrelatedResSyncTableAdapter correlatedResSyncTableAdapter = this.GetAdapter(resourceKind); using (IJetTransaction jetTransaction = _jetConnectionProvider.GetTransaction(false)) { if (!correlatedResSyncTableAdapter.Update(info, jetTransaction)) { throw new StoreException(string.Format("No correlated ResSync exists for the resource kind '{0}' that can be updated.", resourceKind)); } jetTransaction.Commit(); } }
public virtual FeedEntry GetSyncSourceFeedEntry(CorrelatedResSyncInfo resSyncInfo) { FeedEntry result = GetFeedEntry(resSyncInfo.LocalId); if (result == null) { return(null); } result.UUID = resSyncInfo.ResSyncInfo.Uuid; result.Key = resSyncInfo.LocalId; return(result); }
public CorrelatedResSyncInfo[] GetPaged(string resourceKind, int pageNumber, int itemsPerPage, out int totalResult) { CorrelatedResSyncInfo[] resultInfos; ICorrelatedResSyncTableAdapter correlatedResSyncTableAdapter = this.GetAdapter(resourceKind); using (IJetTransaction jetTransaction = _jetConnectionProvider.GetTransaction(false)) { resultInfos = correlatedResSyncTableAdapter.GetAll(jetTransaction); jetTransaction.Commit(); } // TODO: TO BE REVIEWED!!! totalResult = resultInfos.Length; if (totalResult == 0) { return(new CorrelatedResSyncInfo[0]); } int startIndex = ((pageNumber - 1) * itemsPerPage) + 1; if (totalResult < startIndex) { return(new CorrelatedResSyncInfo[0]); } int realItemsPerPage; if ((totalResult - startIndex) > itemsPerPage) { realItemsPerPage = itemsPerPage; } else { realItemsPerPage = totalResult - startIndex; } CorrelatedResSyncInfo[] destinationArray = new CorrelatedResSyncInfo[realItemsPerPage]; Array.Copy(resultInfos, startIndex - 1, destinationArray, 0, realItemsPerPage); return(destinationArray); }
public virtual SyncFeedEntry GetFeedEntry(CorrelatedResSyncInfo resSyncInfo) { SyncFeedEntry result = GetFeedEntry(resSyncInfo.LocalId); if (result == null) { return(null); } result.Uuid = resSyncInfo.ResSyncInfo.Uuid; result.HttpETag = resSyncInfo.ResSyncInfo.Etag; result.HttpMethod = "PUT"; result.SyncState.Endpoint = resSyncInfo.ResSyncInfo.Endpoint; result.SyncState.Tick = resSyncInfo.ResSyncInfo.Tick; result.SyncState.Stamp = resSyncInfo.ResSyncInfo.ModifiedStamp; return(result); }
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); }
public void Update(string resourceKind, CorrelatedResSyncInfo updateInfo) { if (null == resourceKind) { throw new ArgumentNullException("resourceKind"); } if (resourceKind == String.Empty) { throw new ArgumentException("Parameter value is empty.", "resourceKind"); } if (null == updateInfo) { throw new ArgumentNullException("updateInfo"); } lock (lockObj) { _provider.Update(resourceKind, updateInfo); } }
protected FeedEntry BuildFeedEntryForCorrelation(CorrelatedResSyncInfo corrResSyncInfo, IFeedEntryEntityWrapper wrapper) { FeedEntry feedEntry; #region Payload if (null != wrapper) { // Get resource data feedEntry = wrapper.GetFeedEntry(corrResSyncInfo.LocalId); } else { // Create an empty payload container feedEntry = new FeedEntry(); feedEntry.IsDeleted = false; feedEntry.Key = corrResSyncInfo.LocalId; } if (feedEntry != null) { // modify url and set uuid as we are requesting linked resources here. feedEntry.Uri = string.Format("{0}{1}('{2}')", _requestContext.DatasetLink, _requestContext.ResourceKind.ToString(), corrResSyncInfo.LocalId); feedEntry.UUID = corrResSyncInfo.ResSyncInfo.Uuid; #endregion // set id tag feedEntry.Id = feedEntry.Uri; // set title tag feedEntry.Title = string.Format("{0}('{1}') : {2}", _requestContext.ResourceKind.ToString(), corrResSyncInfo.LocalId, corrResSyncInfo.ResSyncInfo.Uuid); feedEntry.Updated = corrResSyncInfo.ResSyncInfo.ModifiedStamp.ToLocalTime(); // set resource dependent links (self, edit, schema, template, post, service) feedEntry.Links.AddRange(LinkFactory.CreateEntryLinks(_requestContext, feedEntry)); } return(feedEntry); }
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); } } }
public void Insert(CorrelatedResSyncInfo info, IJetTransaction jetTransaction) { IEndPointTableAdapter EndPointTableAdapter = StoreEnvironment.Resolve <IEndPointTableAdapter>(this.Context); EndPointInfo EndPointInfo = EndPointTableAdapter.GetOrCreate(info.ResSyncInfo.EndPoint, jetTransaction); OleDbCommand oleDbCommand = jetTransaction.CreateOleCommand(); string sqlQuery = string.Empty; sqlQuery = "INSERT INTO {0} ([Uuid], [tick], [ModifiedStamp], [Etag], [LocalId], [FKEndPointId], [FKResourceKindId]) VALUES (@Uuid, @tick, @ModifiedStamp, @Etag, @LocalId, @EndPointId, @ResourceKindId);"; oleDbCommand.CommandText = string.Format(sqlQuery, _correlatedResSyncTable.TableName); oleDbCommand.Parameters.AddWithValue("@Uuid", info.ResSyncInfo.Uuid); oleDbCommand.Parameters.AddWithValue("@tick", info.ResSyncInfo.Tick); oleDbCommand.Parameters.AddWithValue("@ModifiedStamp", info.ResSyncInfo.ModifiedStamp.ToString()); oleDbCommand.Parameters.AddWithValue("@Etag", info.ResSyncInfo.Etag); oleDbCommand.Parameters.AddWithValue("@LocalId", info.LocalId); oleDbCommand.Parameters.AddWithValue("@EndPointId", EndPointInfo.Id); oleDbCommand.Parameters.AddWithValue("@ResourceKindId", _correlatedResSyncTable.ResourceKindId); oleDbCommand.ExecuteNonQuery(); }
public void Add(string resourceKind, CorrelatedResSyncInfo info) { ICorrelatedResSyncTableAdapter correlatedResSyncTableAdapter = this.GetAdapter(resourceKind); using (IJetTransaction jetTransaction = _jetConnectionProvider.GetTransaction(false)) { try { correlatedResSyncTableAdapter.Insert(info, jetTransaction); } catch (OleDbException exception) { if (exception.Errors.Count == 1 && exception.Errors[0].SQLState == "3022") { throw new StoreException(string.Format("An error occured while adding a new correlated ResSync. A correlated ResSync already exists for the uuid '{0}' and local id '{1}.", info.ResSyncInfo.Uuid, info.LocalId), exception); } throw; } jetTransaction.Commit(); } }
// 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; } }
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; }
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)); }
public void Update(FeedEntry entry) { // only atom entry supported!!! if (_request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry) { throw new RequestException("Atom entry content type expected"); } // resource key must exist in url if (string.IsNullOrEmpty(_requestContext.ResourceKey)) { throw new RequestException("ResourceKey missing in requested url."); } 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 // retrieve the feed entry //entry = FeedComponentFactory.Create<ILinkingResourceFeedEntry>(request.Stream); // Get the uuid from query uuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(_requestContext.ResourceKey); 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; // The correlation store ICorrelatedResSyncInfoStore correlatedResSyncInfoStore = NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext); // update the correlation entry in the sync store. // if uuid is not in use -> throw exception // if resource is already linked -> remove existing link, reassign link // if resource is not yet linked -> reassign link CorrelatedResSyncInfo[] correlations; CorrelatedResSyncInfo correlationToModify = null; // retrieve the correlation we want to reassign by given uuid correlations = correlatedResSyncInfoStore.GetByUuid(resourceKindName, new Guid[] { uuid }); if (correlations.Length > 0) { correlationToModify = correlations[0]; } else { throw new RequestException("Uuid not in use"); } //remember the old Correlation for the new localId, will be deleted after the update. CorrelatedResSyncInfo[] oldCorrelations = correlatedResSyncInfoStore.GetByLocalId(resourceKindName, new string[] { localId }); // change the local ID to link to the new resource. // change the modification stamp and reset the tick correlationToModify.LocalId = localId; correlationToModify.ResSyncInfo.ModifiedStamp = DateTime.Now; correlationToModify.ResSyncInfo.Tick = 1; // update the correlation correlatedResSyncInfoStore.Update(resourceKindName, correlationToModify); //If updating went OK, delete the old correlation for the new localId (should be only 1, for-loop just to be sure to catch em all) foreach (CorrelatedResSyncInfo oldCorrelation in oldCorrelations) { correlatedResSyncInfoStore.Delete(resourceKindName, oldCorrelation.ResSyncInfo.Uuid); } // 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(correlationToModify, wrapper); _request.Response.ContentType = MediaType.AtomEntry; }
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)); }
// 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; } }
/// <summary> /// /// </summary> /// <param name="config"></param> /// <returns></returns> /// <remarks>This method is not threadsafe as the performer must be finished when calling this method.</remarks> public SyncFeed GetFeed(NorthwindConfig config, int startIndex, int count) { SyncFeed feed; SdataContext sdataContext; SupportedResourceKinds resource; string resourceKind; string endpoint; Guid trackingId; List <CorrelatedResSyncInfo> correlatedResSyncInfos; sdataContext = _parentPerformer._requestContext.SdataContext; resource = _parentPerformer._requestContext.ResourceKind; resourceKind = resource.ToString(); correlatedResSyncInfos = _parentPerformer._asyncStateObj.CorrelatedResSyncInfos; endpoint = _parentPerformer._requestContext.DatasetLink + resourceKind; trackingId = _parentPerformer._requestContext.TrackingId; ISyncSyncDigestInfoStore syncDigestStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext); SyncDigestInfo syncDigestInfo = syncDigestStore.Get(resourceKind); if (count == 0) { count = 10; } feed = new SyncFeed(); Token emptyToken = new Token(); feed.Digest = new SyncFeedDigest(); feed.Digest.Origin = _parentPerformer._requestContext.OriginEndPoint; feed.Digest.Entries = new List <SyncFeedDigestEntry>(); if (syncDigestInfo != null) { foreach (SyncDigestEntryInfo entryinfo in syncDigestInfo) { SyncFeedDigestEntry entry = new SyncFeedDigestEntry(); entry.ConflictPriority = entryinfo.ConflictPriority; entry.Endpoint = entryinfo.Endpoint; entry.Tick = entryinfo.Tick; entry.Stamp = DateTime.Now; feed.Digest.Entries.Add(entry); } } IEntityWrapper wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext); for (int index = startIndex; index < ((startIndex + count > correlatedResSyncInfos.Count) ? correlatedResSyncInfos.Count : startIndex + count); index++) { CorrelatedResSyncInfo resSyncInfo = (CorrelatedResSyncInfo)correlatedResSyncInfos[index]; SyncFeedEntry entry = wrapper.GetFeedEntry(resSyncInfo); if (entry != null) { feed.Entries.Add(entry); } else { entry = new SyncFeedEntry(); entry.HttpMethod = "DELETE"; entry.Uuid = resSyncInfo.ResSyncInfo.Uuid; feed.Entries.Add(entry); } } // initialize the feed string url = string.Format("{0}/$syncSource('{1}')", endpoint, trackingId); feed.Id = url; feed.Title = resourceKind; #region PAGING & OPENSEARCH int totalResults = correlatedResSyncInfos.Count; PageController pageController = new PageController(startIndex + 1, FeedMetadataHelpers.DEFAULT_ITEMS_PER_PAGE, totalResults, count, url); /* PAGING */ FeedLinkCollection feedLinks = new FeedLinkCollection(); feedLinks.Add(new FeedLink(pageController.GetLinkSelf(), LinkType.Self, MediaType.Atom, "Current Page")); feedLinks.Add(new FeedLink(pageController.GetLinkFirst(), LinkType.First, MediaType.Atom, "First Page")); feedLinks.Add(new FeedLink(pageController.GetLinkLast(), LinkType.Last, MediaType.Atom, "Last Page")); string linkUrl; if (pageController.GetLinkNext(out linkUrl)) { feedLinks.Add(new FeedLink(linkUrl, LinkType.Next, MediaType.Atom, "Next Page")); } if (pageController.GetLinkPrevious(out linkUrl)) { feedLinks.Add(new FeedLink(linkUrl, LinkType.Previous, MediaType.Atom, "Previous Page")); } feed.Links = feedLinks; /* OPENSEARCH */ feed.Opensearch_ItemsPerPageElement = pageController.GetOpensearch_ItemsPerPageElement(); feed.Opensearch_StartIndexElement = pageController.GetOpensearch_StartIndexElement(); feed.Opensearch_TotalResultsElement = pageController.GetOpensearch_TotalResultsElement(); #endregion //feed.Id = url; //if (startIndex + count < correlatedResSyncInfos.Count) //{ // FeedLink linkNext = new FeedLink(string.Format("{0}?startIndex={1}&count=10", url, startIndex + count), LinkType.Next); // feed.Links.Add(linkNext); //} //FeedLink linkFirst = new FeedLink(String.Format("{0}?startIndex=0&count=10", url), LinkType.First); //feed.Links.Add(linkFirst); //FeedLink linkSelf = new FeedLink(String.Format("{0}?startIndex={1}&count=10", url, startIndex), LinkType.Self); //feed.Links.Add(linkSelf); return(feed); }
// 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; } }