/// <summary> /// Provides the observer with new data. This method is invoked by an observable. /// </summary> /// <param name="topic">The topic.</param> /// <param name="message">The current notification information.</param> /// <param name="entityId">The entity id of the observable.</param> /// <param name="observers">A list of observers in the same cluster node. This field is optional. /// When the list if not null or empty, the observer will forward the message to each observer which EntityId is in the list.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task NotifyObserverAsync(string topic, Message message, EntityId entityId, IEnumerable <EntityId> observers) { EntityId id = null; for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { id = await this.GetEntityIdAsync(); break; } catch (FabricTransientException ex) { ServiceEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw new TimeoutException(Constants.RetryTimeoutExhausted); } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } throw; } catch (Exception ex) { ServiceEventSource.Current.Error(ex); throw; } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } try { ServiceEventSource.Current.Message( $"Message Received.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Message]: Topic=[{topic}] Body=[{message?.Body ?? "NULL"}]"); if (observers != null) { IList <EntityId> observerList = observers as IList <EntityId> ?? observers.ToList(); if (observerList.Any()) { StringBuilder builder = new StringBuilder($"Observer Proxy:\r\n[From]: {id}"); foreach (EntityId item in observerList) { builder.Append($"\r\n[To]: {item}"); } ServiceEventSource.Current.Message(builder.ToString()); List <Task> taskList = new List <Task>(); taskList.AddRange( observerList.Select( observer => ProcessingHelper.NotifyObserverAsync( topic, message, observer, entityId, null))); await Task.WhenAll(taskList.ToArray()); } } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } throw; } catch (Exception ex) { ServiceEventSource.Current.Error(ex); throw; } if (this.NotificationMessageReceived == null) { return; } try { Delegate[] invocationList = this.NotificationMessageReceived.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; NotificationEventArgs <Message> args = new NotificationEventArgs <Message>(topic, message, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <NotificationEventArgs <Message>, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } } catch (Exception ex) { ServiceEventSource.Current.Error(ex); } }
/// <summary> /// Provides the observer with new data. This method is invoked by an observable. /// </summary> /// <param name="topic">The topic.</param> /// <param name="message">The current notification information.</param> /// <param name="entityId">The entity id of the observable.</param> /// <param name="observers"> /// A list of observers in the same cluster node. This field is optional. /// When the list if not null or empty, the observer will forward the message to each observer which EntityId is in the /// list. /// </param> /// <returns>The asynchronous result of the operation.</returns> public virtual async Task NotifyObserverAsync(string topic, Message message, EntityId entityId, IEnumerable <EntityId> observers) { try { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (entityId == null) { entityId = new EntityId { ActorId = ActorId.CreateRandom(), ServiceUri = this.ServiceUri } } ; ActorEventSource.Current.Message( $"Message Received.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Message]: Topic=[{topic}] Body=[{message?.Body ?? "NULL"}]"); if (observers != null) { IList <EntityId> observerList = observers as IList <EntityId> ?? observers.ToList(); if (observerList.Any()) { StringBuilder builder = new StringBuilder($"Observer Proxy:\r\n[From]: {id}"); foreach (EntityId item in observerList) { builder.Append($"\r\n[To]: {item}"); } ActorEventSource.Current.Message(builder.ToString()); List <Task> taskList = new List <Task>(); taskList.AddRange( observerList.Select( observer => ProcessingHelper.NotifyObserverAsync( topic, message, observer, entityId, null))); await Task.WhenAll(taskList.ToArray()); } } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } if (this.NotificationMessageReceived == null) { return; } try { Delegate[] invocationList = this.NotificationMessageReceived.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; NotificationEventArgs <Message> args = new NotificationEventArgs <Message>(topic, message, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <NotificationEventArgs <Message>, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } } catch (Exception ex) { ActorEventSource.Current.Error(ex); } }
/// <summary> /// Sends data to observers for a given topic. /// </summary> /// <param name="topic">The topic.</param> /// <param name="message">The current notification information.</param> /// <param name="useObserverAsProxy">Observable uses one observer for each cluster node as a proxy when true, /// it directly sends the message to all observers otherwise.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task NotifyObserversAsync(string topic, Message message, bool useObserverAsProxy) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic); if (!topicState.HasValue) { throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]"); } Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value; if (string.IsNullOrWhiteSpace(message?.Body)) { throw new ArgumentException($"The {nameof(message)} parameter cannot be null.", nameof(message)); } try { List <Task> taskList = new List <Task>(); if (useObserverAsProxy) { if (JsonSerializerHelper.IsJson(message.Body)) { // Create the list of observers: an observer is added to the list only at least of of its // filter predicates is satisified by the message. JObject jObject = JsonSerializerHelper.Deserialize(message.Body); IEnumerable <EntityId> observerEnumerable = (from subscriptionInfo in observerDictionary. Where(kvp => kvp.Value.Predicates.Any()). Select(observer => observer.Value) let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject)) where ok select subscriptionInfo.EntityId); // observers are grouped by NodeName taskList.AddRange( observerEnumerable. GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2))); } else { // observers are grouped by NodeName taskList.AddRange( observerDictionary. Select(kvp => kvp.Value.EntityId). GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2))); } } else { if (JsonSerializerHelper.IsJson(message.Body)) { JObject jObject = JsonSerializerHelper.Deserialize(message.Body); taskList.AddRange( (from subscriptionInfo in observerDictionary. Where(kvp => kvp.Value.Predicates.Any()). Select(observer => observer.Value) let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject)) where ok select subscriptionInfo.EntityId). Select( entityId => ProcessingHelper.NotifyObserverAsync( topic, message, entityId, id, null))); } else { taskList.AddRange( observerDictionary.Select( observer => ProcessingHelper.NotifyObserverAsync( topic, message, observer.Value.EntityId, id, null))); } } await Task.WhenAll(taskList.ToArray()); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } }