private void BuildResponseFeed(ref IRequest request, computePriceResponsetype computePriceResponse) { ComputePriceResponsePayload computePriceResponsePayload = new ComputePriceResponsePayload(); computePriceResponsePayload.ComputePriceResponse = computePriceResponse; SyncFeedEntry entry = new SyncFeedEntry(); entry.Payload = computePriceResponsePayload; entry.Id = _requestContext.SdataUri.Uri.ToString(); entry.Title = "computePrice"; entry.Updated = DateTime.Now; //entry.Links //entry.Categories SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(entry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; }
public void DoWork(IRequest request) { string resourceKind = _requestContext.ResourceKind.ToString(); string endpoint = _requestContext.DatasetLink + resourceKind; SdataContext sdataContext = _requestContext.SdataContext; ISyncSyncDigestInfoStore syncDigestStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext); SyncDigestInfo syncDigestInfo = syncDigestStore.Get(resourceKind); SyncDigestPayload syncDigestPayload = new SyncDigestPayload(); syncDigestPayload.Digest.Origin = endpoint; // create a new initial syncDigest and store it. if ((null == syncDigestInfo) || (syncDigestInfo.Count == 0)) { SyncFeedDigestEntry entry = new SyncFeedDigestEntry(); entry.ConflictPriority = 0; entry.Endpoint = endpoint; entry.Stamp = DateTime.Now; entry.Tick = -1; syncDigestPayload.Digest.Entries.Add(entry); } else { foreach (SyncDigestEntryInfo digestEntry in syncDigestInfo) { SyncFeedDigestEntry entry = new SyncFeedDigestEntry(); entry.ConflictPriority = digestEntry.ConflictPriority; entry.Endpoint = digestEntry.Endpoint; //entry.Stamp = digestEntry.; entry.Tick = digestEntry.Tick; syncDigestPayload.Digest.Entries.Add(entry); } } SyncFeed feed = new SyncFeed(); string url = endpoint + "/$syncDigest"; FeedLink link = new FeedLink(url, LinkType.Self, MediaType.AtomEntry); feed.Links = new FeedLinkCollection(); feed.Links.Add(link); feed.Id = url; feed.FeedType = FeedType.ResourceEntry; SyncFeedEntry syncFeedEntry = new SyncFeedEntry(); syncFeedEntry.Payload = syncDigestPayload; syncFeedEntry.Title = "Synchronization digest"; syncFeedEntry.Id = url; syncFeedEntry.Links.Add(link); feed.Entries.Add(syncFeedEntry); feed.Id = url; request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = MediaType.AtomEntry; }
public void DoWork(IRequest request) { // If an asyncState object already exists, an exception is thrown as the performer only accepts // one call after each other. The Request receiver has to manage a queued execution. // Before calling the performers execution implementation method a new AsyncState object is created // and set to an initial tracking state. lock (_asyncStateObj) { if (null != _asyncStateObj.Tracking) { throw new InvalidOperationException("The performer cannot be executed because it is already running."); } SyncTracking tracking = new SyncTracking(); tracking.ElapsedSeconds = 1; tracking.Phase = TrackingPhase.INIT; tracking.PhaseDetail = "Tracking Id was: " + _requestContext.TrackingId.ToString(); tracking.PollingMillis = 100; tracking.RemainingSeconds = 100; _asyncStateObj.Tracking = tracking; } // *** Initialization for the async execution *** // - Read Feed from request stream. // - Read trackingId from request URL // convert tracking ID from request to type Guid string strTrackingId = request.Uri.TrackingID; if (String.IsNullOrEmpty(strTrackingId)) { throw new RequestException("TrackingId is missing"); } GuidConverter converter = new GuidConverter(); this.TrackingId = (Guid)converter.ConvertFrom(strTrackingId); //read feed SyncFeed feed = new SyncFeed(); XmlReader reader = XmlReader.Create(request.Stream); feed.ReadXml(reader, ResourceKindHelpers.GetPayloadType(_requestContext.ResourceKind)); // *** Do work asynchronously *** _asyncPerformer = new InternalAsyncPerformer(this); _asyncPerformer.DoWork(_requestContext.Config, feed); // *** set the tracking to the request response *** this.GetTrackingState(request); }
public void DoWork(IRequest request) { if (request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry) { throw new RequestException("Atom entry content type expected"); } if (!String.IsNullOrEmpty(_requestContext.ResourceKey)) { throw new RequestException("Please use resource url"); } // read entry from stream SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, ResourceKindHelpers.GetPayloadType(_requestContext.ResourceKind)); IEntityWrapper wrapper = EntityWrapperFactory.Create(_requestContext.ResourceKind, _requestContext); Document document = wrapper.GetTransformedDocument(entry.Payload, entry.SyncLinks); if (!String.IsNullOrEmpty(document.Id)) { throw new RequestException("Entity alredy exists"); } // Add Document SdataTransactionResult sdTrResult = wrapper.Add(entry.Payload, entry.SyncLinks); if (sdTrResult == null) { throw new RequestException("Entity does not exists"); //????? } if ((sdTrResult.HttpStatus == System.Net.HttpStatusCode.OK) || (sdTrResult.HttpStatus == System.Net.HttpStatusCode.Created)) { // store correlation SyncFeedEntry responseEntry = wrapper.GetFeedEntry(sdTrResult.LocalId); SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(responseEntry); request.Response.StatusCode = System.Net.HttpStatusCode.Created; request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.Protocol.SendUnknownResponseHeader("Location", responseEntry.Id); request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; } else { throw new RequestException(sdTrResult.HttpMessage); } }
public void DoWork(NorthwindConfig config, SyncFeed feed) { ExecuteDelegate worker = new ExecuteDelegate(Execute); AsyncCallback completedCallback = new AsyncCallback(ExecuteCompletedCallback); AsyncOperation async = AsyncOperationManager.CreateOperation(null); // Begin asynchronous method call worker.BeginInvoke(config, feed, completedCallback, async); }
public void DoWork(IRequest request) { if (request.ContentType != Sage.Common.Syndication.MediaType.Atom) { throw new RequestException("Atom content type expected"); } //read feed string resourceKindName = _requestContext.ResourceKind.ToString(); SyncFeed feed = new SyncFeed(); XmlReader reader = XmlReader.Create(request.Stream); feed.ReadXml(reader, ResourceKindHelpers.GetPayloadType(_requestContext.ResourceKind)); /* iterate through all entries and store result information */ ISyncResultInfoStore syncResultInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncResultStore(_requestContext.SdataContext); int noOfEntries = feed.Entries.Count; string endpoint = string.Empty; try { endpoint = new RequestContext(new Sage.Common.Syndication.SDataUri(feed.Id)).OriginEndPoint; } catch { } SyncResultEntryInfo[] syncResultEntries = new SyncResultEntryInfo[noOfEntries]; for (int i = 0; i < noOfEntries; i++) { SyncFeedEntry entry = (SyncFeedEntry)feed.Entries[i]; string httpMethod = entry.HttpMethod; int httpStatus = -1; if (Enum.IsDefined(typeof(HttpStatusCode), entry.HttpStatusCode.ToString())) { httpStatus = (int)Enum.Parse(typeof(HttpStatusCode), entry.HttpStatusCode.ToString(), true); } string httpLocation = entry.HttpLocation; string httpMessage = entry.HttpMessage; string diagnosisXml = XmlSerializationHelpers.SerializeObjectToXml(entry.Diagnoses); string payloadXml = XmlSerializationHelpers.SerializeObjectToXml(entry.Payload); syncResultEntries[i] = new SyncResultEntryInfo(httpMethod, httpStatus, httpLocation, httpMessage, diagnosisXml, payloadXml, DateTime.Now, endpoint); } syncResultInfoStore.Add(resourceKindName, syncResultEntries); }
public void DoWork(IRequest request) { IEntityWrapper wrapper = EntityWrapperFactory.Create(_requestContext.ResourceKind, _requestContext); SyncFeed syncFeed = wrapper.GetFeed(); request.Response.Serializer = new SyncFeedSerializer(); if (String.IsNullOrEmpty(_requestContext.ResourceKey)) { syncFeed.FeedType = FeedType.Resource; request.Response.ContentType = Sage.Common.Syndication.MediaType.Atom; } else { syncFeed.FeedType = FeedType.ResourceEntry; request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; } request.Response.Feed = syncFeed; }
public void GetTrackingState(IRequest request) { lock (_asyncStateObj) { if (null == _asyncStateObj.Tracking) { throw new InvalidOperationException("Performer has not been started."); } if (_asyncStateObj.Tracking.Phase == TrackingPhase.FINISH) { request.Response.ContentType = MediaType.Atom; int startindex = Convert.ToInt32(request.Uri.StartIndex); int count = Convert.ToInt32(request.Uri.Count); SdataContext sdataContext = _requestContext.SdataContext; ICorrelatedResSyncInfoStore correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(sdataContext); request.Response.Serializer = new SyncFeedSerializer(); SyncFeed syncFeed = _asyncPerformer.GetFeed(_requestContext.Config, startindex, count); syncFeed.FeedType = FeedType.SyncSource; request.Response.Feed = syncFeed; } else if (_asyncStateObj.Tracking.Phase == TrackingPhase.ERROR) { request.Response.Xml = XmlSerializationHelpers.SerializeObjectToXml(_asyncStateObj.Tracking); request.Response.ContentType = MediaType.Xml; request.Response.StatusCode = HttpStatusCode.InternalServerError; request.Response.Serializer = new XmlSerializer(); request.Response.Protocol.SendUnknownResponseHeader("location", String.Format("{0}{1}/$syncSource('{2}')", _requestContext.DatasetLink, _requestContext.ResourceKind.ToString(), _requestContext.TrackingId)); } else { request.Response.Xml = XmlSerializationHelpers.SerializeObjectToXml(_asyncStateObj.Tracking); request.Response.ContentType = MediaType.Xml; request.Response.StatusCode = HttpStatusCode.Accepted; request.Response.Serializer = new XmlSerializer(); request.Response.Protocol.SendUnknownResponseHeader("location", String.Format("{0}{1}/$syncSource('{2}')", _requestContext.DatasetLink, _requestContext.ResourceKind.ToString(), _requestContext.TrackingId)); } } }
public override SyncFeed GetFeed() { bool includeUuid; string whereClause = string.Empty; OleDbParameter[] oleDbParameters = null; if (this is IEntityQueryWrapper) { QueryFilterBuilder queryFilterBuilder = new QueryFilterBuilder((IEntityQueryWrapper)this); queryFilterBuilder.BuildSqlStatement(_context, out whereClause, out oleDbParameters); } SyncFeed feed = new SyncFeed(); feed.Title = _resourceKind.ToString() + ": " + DateTime.Now.ToString(); Token emptyToken = new Token(); List <Identity> identities = new List <Identity>(); if (String.IsNullOrEmpty(_context.ResourceKey)) { identities = _entity.GetAll(_context.Config, whereClause, oleDbParameters); } else { identities.Add(new Identity(_entity.EntityName, _context.ResourceKey)); } int totalResult = identities.Count; #region PAGING & OPENSEARCH /* PAGING */ feed.Links = FeedMetadataHelpers.CreatePageFeedLinks(_context, totalResult, FeedMetadataHelpers.RequestKeywordType.none); /* OPENSEARCH */ PageController pageController = FeedMetadataHelpers.GetPageLinkBuilder(_context, totalResult, FeedMetadataHelpers.RequestKeywordType.none); feed.Opensearch_ItemsPerPageElement = pageController.GetOpensearch_ItemsPerPageElement(); feed.Opensearch_StartIndexElement = pageController.GetOpensearch_StartIndexElement(); feed.Opensearch_TotalResultsElement = pageController.GetOpensearch_TotalResultsElement(); #endregion feed.Id = _context.SdataUri.ToString(); string tmpValue; // ?includeUuid includeUuid = false; // default value, but check for settings now if (_context.SdataUri.QueryArgs.TryGetValue("includeUuid", out tmpValue)) { includeUuid = System.Xml.XmlConvert.ToBoolean(tmpValue); } ICorrelatedResSyncInfoStore correlatedResSyncStore = null; if (includeUuid) { // get store to request the correlations correlatedResSyncStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_context.SdataContext); } for (int pageIndex = pageController.StartIndex; pageIndex <= pageController.LastIndex; pageIndex++) //for (int index = startIndex; index < startIndex + count; index++) { int zeroBasedIndex = pageIndex - 1; Identity identity = identities[zeroBasedIndex]; AccountDocument accountDocument = (AccountDocument)_entity.GetDocument(identity, emptyToken, _context.Config); if (accountDocument.LogState == LogState.Deleted) { continue; } SyncFeedEntry entry = new SyncFeedEntry(); if (accountDocument.addresses.documents.Count == 0) { return(null); } entry.Id = String.Format("{0}{1}('{2}')", _context.DatasetLink, _resourceKind.ToString(), identity.Id); entry.Title = String.Format("{0}: {1}", _resourceKind.ToString(), identity.Id); entry.Updated = DateTime.Now; if (_context.SdataUri.Precedence == null) { List <SyncFeedEntryLink> links; Document document = accountDocument.addresses.documents[0]; entry.Payload = GetTransformedPayload(document, out links); string taUuid = GetTradingAccountUuid(accountDocument.Id); if (!String.IsNullOrEmpty(taUuid)) { SyncFeedEntryLink tradingAccountLink = SyncFeedEntryLink.CreateRelatedLink( String.Format("{0}{1}('{2}')", _context.DatasetLink, SupportedResourceKinds.tradingAccounts.ToString(), accountDocument.Id), SupportedResourceKinds.tradingAccounts.ToString(), _tradingAccountUuidPayloadPath, taUuid); links.Add(tradingAccountLink); } entry.SyncLinks = links; } if (includeUuid) { CorrelatedResSyncInfo[] infos = correlatedResSyncStore.GetByLocalId(_context.ResourceKind.ToString(), new string[] { identity.Id }); entry.Uuid = (infos.Length > 0) ? infos[0].ResSyncInfo.Uuid : Guid.Empty; } if (entry != null) { feed.Entries.Add(entry); } } return(feed); }
public void DoWork(IRequest request) { EntryRequestType entryRequestType; string resourceKindName; ICorrelatedResSyncInfoStore correlatedResSyncStore; bool includeDataPayloads; //?includePayloads value CorrelatedResSyncInfo[] correlatedResSyncInfos; int totalResult; #region initialization // multi or single entry request? entryRequestType = (String.IsNullOrEmpty(_requestContext.ResourceKey)) ? EntryRequestType.Multi : EntryRequestType.Single; resourceKindName = _requestContext.ResourceKind.ToString(); string tmpValue; // ?includePayloads includeDataPayloads = false; // default value, but check for settings now if (_requestContext.SdataUri.QueryArgs.TryGetValue("includePayload", out tmpValue)) { includeDataPayloads = System.Xml.XmlConvert.ToBoolean(tmpValue); } // get store to request the correlations correlatedResSyncStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext); #endregion // receive correlated resource synchronization entries if (entryRequestType == EntryRequestType.Multi) { int pageNumber = FeedMetadataHelpers.GetPageNumber(_requestContext); correlatedResSyncInfos = correlatedResSyncStore.GetPaged(resourceKindName, pageNumber, FeedMetadataHelpers.DEFAULT_ITEMS_PER_PAGE, out totalResult); } else { Guid uuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(_requestContext.ResourceKey); correlatedResSyncInfos = correlatedResSyncStore.GetByUuid(resourceKindName, new Guid[] { uuid }); totalResult = correlatedResSyncInfos.Length; } // Create the feed SyncFeed feed = new SyncFeed(); // initialize the feed feed.Id = FeedMetadataHelpers.BuildBaseUrl(_requestContext, FeedMetadataHelpers.RequestKeywordType.linked); feed.Title = string.Format("{0} Linking Feed", resourceKindName); #region PAGING & OPENSEARCH /* PAGING */ feed.Links = FeedMetadataHelpers.CreatePageFeedLinks(_requestContext, totalResult, FeedMetadataHelpers.RequestKeywordType.linked); /* OPENSEARCH */ PageController pageLinkBuilder = FeedMetadataHelpers.GetPageLinkBuilder(_requestContext, totalResult, FeedMetadataHelpers.RequestKeywordType.linked); feed.Opensearch_ItemsPerPageElement = pageLinkBuilder.GetOpensearch_ItemsPerPageElement(); feed.Opensearch_StartIndexElement = pageLinkBuilder.GetOpensearch_StartIndexElement(); feed.Opensearch_TotalResultsElement = pageLinkBuilder.GetOpensearch_TotalResultsElement(); #endregion feed.FeedType = (entryRequestType == EntryRequestType.Multi) ? FeedType.Linked : FeedType.LinkedSingle; IEntityWrapper wrapper = null; if (includeDataPayloads) { wrapper = EntityWrapperFactory.Create(_requestContext.ResourceKind, _requestContext); } for (int i = 0; i < correlatedResSyncInfos.Length; i++) { SyncFeedEntry entry = null; if (includeDataPayloads) { entry = wrapper.GetFeedEntry(correlatedResSyncInfos[i].LocalId); } else { entry = new SyncFeedEntry(); } entry.Id = FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, correlatedResSyncInfos[i].ResSyncInfo.Uuid); entry.Title = FeedMetadataHelpers.BuildLinkedEntryTitle(_requestContext, correlatedResSyncInfos[i].ResSyncInfo.Uuid); #region LINKED ELEMENT LinkedElement linkedElement = new LinkedElement(); linkedElement.Resource = FeedMetadataHelpers.BuildEntryResourceUrl(_requestContext, correlatedResSyncInfos[i].LocalId); linkedElement.Uuid = correlatedResSyncInfos[i].ResSyncInfo.Uuid; entry.Linked = linkedElement; #endregion feed.Entries.Add(entry); } request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = (IFeed)feed; request.Response.ContentType = (entryRequestType == EntryRequestType.Multi) ? MediaType.Atom : 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"); } // 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; }
/// <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); }
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 DoWork(IRequest request) { if (request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry) { throw new RequestException("Atom entry content type expected"); } if (String.IsNullOrEmpty(_requestContext.ResourceKey)) { throw new RequestException("Please use single resource url"); } //SupportedResourceKinds resKind = _requestContext.ResourceKind; //switch (resKind) //{ // case SupportedResourceKinds.tradingAccounts: // case SupportedResourceKinds.contacts: // case SupportedResourceKinds.phoneNumbers: // case SupportedResourceKinds.postalAddresses: // break; // default: // throw new RequestException("Put is not Supported for requested resource"); //} // read entry from stream SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, ResourceKindHelpers.GetPayloadType(_requestContext.ResourceKind)); IEntityWrapper wrapper = EntityWrapperFactory.Create(_requestContext.ResourceKind, _requestContext); Token emptyToken = new Token(); Identity identity = wrapper.GetIdentity(_requestContext.ResourceKey); Document originalDocument = wrapper.Entity.GetDocument(identity, emptyToken, _requestContext.Config); if (originalDocument.LogState == LogState.Deleted) { throw new RequestException("Entity does not exists"); } entry.Payload.LocalID = _requestContext.ResourceKey; SdataTransactionResult sdTrResult = wrapper.Update(entry.Payload, entry.SyncLinks); if (sdTrResult == null) { SyncFeedEntry responseEntry = wrapper.GetFeedEntry(_requestContext.ResourceKey); SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(responseEntry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; } else if (sdTrResult.HttpStatus == System.Net.HttpStatusCode.OK) { SyncFeedEntry responseEntry = wrapper.GetFeedEntry(_requestContext.ResourceKey); SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(responseEntry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; } else { throw new RequestException(sdTrResult.HttpMessage); } }
// 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; } }
/// <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 <SdataTransactionResult> transactinResults; sdataContext = _parentPerformer._requestContext.SdataContext; resource = _parentPerformer._requestContext.ResourceKind; resourceKind = resource.ToString(); transactinResults = _parentPerformer._asyncStateObj.TransactionResults; endpoint = _parentPerformer._requestContext.DatasetLink + resourceKind;; trackingId = _parentPerformer._requestContext.TrackingId; if (count == 0) { count = 10; } feed = new SyncFeed(); Token emptyToken = new Token(); IEntityWrapper wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext); for (int index = startIndex; index < ((startIndex + count > transactinResults.Count) ? transactinResults.Count : startIndex + count); index++) { SdataTransactionResult transactionResult = (SdataTransactionResult)transactinResults[index]; SyncFeedEntry entry = wrapper.GetFeedEntry(transactionResult); if (entry != null) { feed.Entries.Add(entry); } else { entry = new SyncFeedEntry(); entry.Uuid = transactionResult.Uuid; entry.HttpStatusCode = transactionResult.HttpStatus; entry.HttpMessage = transactionResult.HttpMessage;; entry.HttpMethod = transactionResult.HttpMethod; entry.HttpLocation = transactionResult.Location; entry.HttpETag = transactionResult.Etag; feed.Entries.Add(entry); } } // initialize the feed string url = string.Format("{0}/$syncTarget('{1}')", endpoint, trackingId); feed.Title = resourceKind; feed.Id = url; #region PAGING & OPENSEARCH int totalResults = transactinResults.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 //if (startIndex + count < transactinResults.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); }