/// <summary> /// This method resolves the client and processes the message. /// </summary> /// <param name="payload">The payload to transmit.</param> public virtual async Task SenderTransmit(TransmissionPayload payload) { int? start = null; ClientHolder sender = null; try { sender = SenderClientResolve(payload.Message.ChannelPriority); start = sender.StatisticsInternal.ActiveIncrement(); await sender.Transmit(payload); payload.TraceWrite($"Sent: {sender.Name}", "MessagingSenderBase/ProcessMessage"); } catch (Exception ex) { LogExceptionLocation($"{nameof(SenderTransmit)} (Unhandled)", ex); //OK, not sure what happened here, so we need to throw the exception. payload.TraceWrite($"Exception: {ex.Message}", "MessagingSenderBase/ProcessMessage"); if (sender != null) { sender.StatisticsInternal.ErrorIncrement(); } throw; } finally { if (sender != null && start.HasValue) { sender.StatisticsInternal.ActiveDecrement(start.Value); } } }
/// <summary> /// This method resolves the client and processes the message. /// </summary> /// <param name="payload">The payload to transmit.</param> public virtual async Task ProcessMessage(TransmissionPayload payload) { int?start = null; H client = null; try { client = ClientResolve(payload.Message.ChannelPriority); start = client.Statistics.ActiveIncrement(); await client.Transmit(payload); payload.TraceWrite($"Sent: {client.Name}", "MessagingSenderBase/ProcessMessage"); } catch (Exception ex) { LogExceptionLocation("ProcessMessage (Unhandled)", ex); //OK, not sure what happened here, so we need to throw the exception. payload.TraceWrite($"Exception: {ex.Message}", "MessagingSenderBase/ProcessMessage"); if (client != null) { client.Statistics.ErrorIncrement(); } throw; } finally { if (client != null && start.HasValue) { client.Statistics.ActiveDecrement(start.Value); } } }
/// <summary> /// This method processes an individual payload returned from a client. /// </summary> /// <param name="clientId">The originating client.</param> /// <param name="payload">The payload.</param> private void PayloadSubmit(Guid clientId, TransmissionPayload payload) { try { payload.TraceConfigure(mPolicy.TransmissionPayloadTraceEnabled); payload.TraceWrite("Incoming", "CommunicationContainer/PayloadSubmit"); //Ensure the priority cannot spoof the internal priority of -1 if (payload.Message.ChannelPriority < 0) { payload.Message.ChannelPriority = 0; } mClientCollection.QueueTimeLog(clientId, payload.Message.EnqueuedTimeUTC); //Verify the incoming payload with the security container. PayloadIncomingSecurity(payload); //Do we need to redirect the payload based on the redirect/rewrite rules. PayloadIncomingRedirectCheck(payload); //Create the tracker to process the incoming TaskTracker tracker = TaskManager.TrackerCreateFromPayload(payload, payload.Source); //Set the function that executes when the payload completes. tracker.ExecuteComplete = (tr, failed, ex) => { var contextPayload = tr.ToTransmissionPayload(); try { mClientCollection.ActiveDecrement(clientId, tr.TickCount); if (failed) { mClientCollection.ErrorIncrement(clientId); } contextPayload.Signal(!failed); contextPayload.TraceWrite(failed?"Failed":"Success", "CommunicationContainer/PayloadSubmit -> ExecuteComplete"); } catch (Exception exin) { Collector?.LogException($"Payload completion error-{payload} after {(tr.Context as TransmissionPayload)?.Message?.FabricDeliveryCount} delivery attempts", exin); contextPayload.TraceWrite($"Exception: {ex.Message}", "CommunicationContainer/PayloadSubmit -> ExecuteComplete"); } }; //Submit the tracker to the task manager. payload.TraceWrite("Outgoing", "CommunicationContainer/PayloadSubmit"); TaskSubmit(tracker); } catch (Exception ex) { Collector?.LogException($"ProcessClientPayload: unhandled error {payload.Source}/{payload.Message.CorrelationKey}-{payload} after {payload.Message?.FabricDeliveryCount} delivery attempts", ex); payload.TraceWrite($"Exception: {ex.Message}", "CommunicationContainer/PayloadSubmit"); payload.SignalFail(); } }
/// <summary> /// This method injects a payload to be picked up by the polling algorithm. /// </summary> /// <param name="payload">The payload to inject.</param> public void Inject(TransmissionPayload payload) { try { mPending.Enqueue(payload); payload.TraceWrite("Enqueued", "ManualChannelClientHolder/Inject"); } catch (Exception ex) { payload.TraceWrite($"Failed: {ex.Message}", "ManualChannelClientHolder/Inject"); } }
/// <summary> /// This method transmits the messages to the relevant senders. /// </summary> /// <param name="payload">The payload messages to externalOnly</param> public virtual async Task <bool> Send(TransmissionPayload payload) { payload.TraceConfigure(mPolicy.TransmissionPayloadTraceEnabled); payload.TraceWrite("Outgoing", "CommunicationContainer/Send"); try { //Set outgoing routing information to lower case. This is important as messaging protocols such as //Service Bus can be case sensitive when running subscription filters. if (mPolicy.ServiceMessageHeaderConvertToLowercase) { payload.Message.ConvertMessageHeadersToLowercase(); } Channel channel = PayloadOutgoingRedirectChecks(payload); PayloadOutgoingSecurity(payload); //No, we want to send the message externally. List <ISender> messageSenders = null; //Get the supported message handler if (channel != null && !mMessageSenderMap.TryGetValue(channel.Id, out messageSenders)) { messageSenders = MessageSenderResolve(payload); } //If there are no supported senders for the particular channelId then throw an exception if (messageSenders == null || messageSenders.Count == 0) { Collector?.LogMessage(LoggingLevel.Warning, string.Format("Unable to resolve sender for message {0}", payload != null ? payload.Message : null), "Communication"); payload.TraceWrite("Senders Unresolved", "CommunicationContainer/Send"); return(false); } //Set the outgoing originator if not set. if (string.IsNullOrEmpty(payload.Message.OriginatorServiceId)) { payload.Message.OriginatorServiceId = OriginatorId.ExternalServiceId; } //Send the message to the supported senders. await Task.WhenAll(messageSenders.Select(s => s.ProcessMessage(payload))); } catch (Exception ex) { Collector?.LogException(string.Format("Unable to send message {0}", payload != null ? payload.Message : null), ex); payload.TraceWrite($"Exception: {ex.Message}", "CommunicationContainer/Send"); return(false); } return(true); }
/// <summary> /// Purges any remaining messages when the service shuts down. /// </summary> public void Purge() { TransmissionPayload payload = null; while (mPending?.TryDequeue(out payload) ?? false) { payload.TraceWrite("Purged", "ManualChannelClientHolder/Purge"); payload.SignalFail(); } }
/// <summary> /// This method separates the payloads so that they are different objects. /// </summary> /// <param name="inPayload">The incoming payload.</param> /// <param name="signal">The optional signal action.</param> /// <param name="traceEnabled">Specifies whether trace is enabled. If omitted or set to null, the value inherits from the incoming payload.</param> /// <returns>Returns a new cloned payload.</returns> public static TransmissionPayload Clone(this TransmissionPayload inPayload, Action <bool, Guid> signal, bool?traceEnabled = null) { traceEnabled = traceEnabled ?? inPayload.TraceEnabled; //First clone the service message. var cloned = new TransmissionPayload(inPayload.Message.Clone(), release: signal, traceEnabled: traceEnabled.Value); cloned.TraceWrite("Cloned", "ManualCommunicationBridgeAgent/PayloadCopy"); return(cloned); }
/// <summary> /// This method validates the payload with the security container. /// </summary> /// <param name="payload">The incoming payload.</param> protected virtual void PayloadPack(TransmissionPayload payload) { //Try and resolve the channel. Channel channel = null; TryGet(payload.Message.ChannelId, ChannelDirection.Outgoing, out channel); //Secure the outgoing payload. Secure(channel, payload); payload.TraceWrite("Secured", "CommunicationContainer/PayloadOutgoingSecurity"); }
/// <summary> /// This method validates the payload with the security container. /// </summary> /// <param name="payload">The incoming payload.</param> protected virtual void PayloadUnpack(TransmissionPayload payload) { //Try and resolve the channel. Channel channel = null; TryGet(payload.Message.ChannelId, ChannelDirection.Incoming, out channel); //Decrypt and verify the incoming message. Verify(channel, payload); payload.TraceWrite("Verified", "CommunicationContainer/PayloadIncomingSecurity"); }
/// <summary> /// This method injects a service message manually in to the Microservice. /// </summary> /// <param name="payload">The message payload.</param> /// <param name="priority">The optional priority. The default is 1.</param> public void Inject(TransmissionPayload payload, int?priority = null) { if (this.Status != ServiceStatus.Running) { payload.SignalSuccess(); payload.TraceWrite($"Failed: {Status}", "ManualChannelListener/Inject"); return; } try { var client = ClientResolve(priority ?? mDefaultPriority ?? 1); client.Inject(payload); payload.TraceWrite($"Success: {client.Name}", "ManualChannelListener/Inject"); } catch (Exception ex) { payload.TraceWrite($"Error: {ex.Message}", "ManualChannelListener/Inject"); } }
/// <summary> /// This method injects a payload in to the execution path and bypasses the listener infrastructure. /// </summary> /// <param name="payload">The transmission payload to execute.</param> public virtual void Process(TransmissionPayload payload) { ValidateServiceStarted(); if (mTransmissionPayloadTraceEnabled) { payload.TraceEnabled = true; payload.TraceWrite($"{Name} received."); } ExecuteOrEnqueue(payload, $"{Name} method request"); }
/// <summary> /// This method injects a payload in to the execution path and bypasses the listener infrastructure. /// </summary> /// <param name="payload">The transmission payload to execute.</param> public void Process(TransmissionPayload payload) { ValidateServiceStarted(); if (mTransmissionPayloadTraceEnabled) { payload.TraceEnabled = true; payload.TraceWrite("howdy"); } ExecuteOrEnqueue(payload, "Incoming Process method request"); }
/// <summary> /// Processes the request if self generated. This is used to accommodate state change. /// </summary> /// <param name="rq">The incoming request.</param> /// <returns>Returns true if the request is from this command.</returns> protected virtual bool ProcessRequestIfSelfGenerated(TransmissionPayload rq) { if (!string.Equals(rq.Message.OriginatorServiceId, OriginatorId.ExternalServiceId, StringComparison.InvariantCultureIgnoreCase)) { return(false); } rq.TraceWrite("Processing", "Command/ProcessSelfSentMessage"); switch (mMasterJobContext.State) { case MasterJobState.VerifyingComms: //We can now say that the masterjob channel is working, so we can now enable the job for negotiation. mMasterJobContext.State = MasterJobState.Starting; break; case MasterJobState.Inactive: if (mMasterJobContext.MasterPollAttemptsExceeded()) { mMasterJobContext.State = MasterJobState.Starting; } break; case MasterJobState.Starting: if (IsMatch(rq, MasterJobStates.WhoIsMaster) && mMasterJobContext.MasterPollAttemptsExceeded()) { mMasterJobContext.State = MasterJobState.Requesting1; } break; case MasterJobState.Requesting1: if (IsMatch(rq, MasterJobStates.RequestingControl1) && mMasterJobContext.MasterPollAttemptsExceeded()) { mMasterJobContext.State = MasterJobState.Requesting2; } break; case MasterJobState.Requesting2: if (IsMatch(rq, MasterJobStates.RequestingControl2) && mMasterJobContext.MasterPollAttemptsExceeded()) { mMasterJobContext.State = MasterJobState.TakingControl; } break; case MasterJobState.TakingControl: if (IsMatch(rq, MasterJobStates.TakingControl)) { MasterJobStart(); } break; } return(true); }
/// <summary> /// This method validates any rewrite rules for the incoming payload. /// </summary> /// <param name="payload">The incoming payload.</param> protected virtual void PayloadIncomingRedirectCheck(TransmissionPayload payload) { var channelId = payload.Message.ChannelId; //Rewrite rule validate, and rewrite for incoming message. Channel channel; if (TryGet(channelId, ChannelDirection.Incoming, out channel)) { if (channel.CouldRedirect) { channel.Redirect(payload); payload.TraceWrite("Redirected", "CommunicationContainer/PayloadIncomingRedirectCheck"); } } }
/// <summary> /// This method injects a payload in to the execution path and bypasses the listener infrastructure. /// </summary> /// <param name="payload">The transmission payload to execute.</param> public override void Process(TransmissionPayload payload) { ValidateServiceStarted(); if (payload.Message.OriginatorServiceId == null && DefaultOriginatorServiceId != null) { payload.Message.OriginatorServiceId = DefaultOriginatorServiceId; } if (mTransmissionPayloadTraceEnabled) { payload.TraceEnabled = true; payload.TraceWrite($"{Name} received."); } ExecuteOrEnqueue(payload, $"{Name} method request"); }
/// <summary> /// This helper method turns round an incoming payload request in to its corresponding response payload. /// </summary> /// <param name="incoming">The incoming payload.</param> /// <returns></returns> public static TransmissionPayload ToResponse(this TransmissionPayload incoming) { var m = incoming.Message; var rsMessage = m.ToResponse(); rsMessage.ChannelId = m.ResponseChannelId; rsMessage.ChannelPriority = m.ResponseChannelPriority; rsMessage.MessageType = m.ResponseMessageType; rsMessage.ActionType = m.ResponseActionType; var outgoing = new TransmissionPayload(rsMessage, traceEnabled: incoming.TraceEnabled); if (incoming.TraceEnabled) { outgoing.TraceWrite(new TransmissionPayloadTraceEventArgs(outgoing.TickCount, "Created from request", "ToResponse")); } return(outgoing); }
/// <summary> /// This method checks for any redirect rules for the outgoing payload. /// </summary> /// <param name="payload">The outgoing payload.</param> /// <returns>The outgoing channel.</returns> protected virtual Channel PayloadOutgoingRedirectChecks(TransmissionPayload payload) { //Rewrite rule validate, and rewrite for outgoing message. var channelId = payload.Message.ChannelId; Channel channel; if (TryGet(channelId, ChannelDirection.Outgoing, out channel)) { if (channel.CouldRedirect) { channel.Redirect(payload); payload.TraceWrite("Redirected", "CommunicationContainer/PayloadOutgoingRedirectChecks"); //Get the new outgoing channel. TryGet(payload.Message.ChannelId, ChannelDirection.Outgoing, out channel); } } return(channel); }
/// <summary> /// This method rewrites the rule. /// </summary> /// <param name="payload">The payload to adjust the incoming header information.</param> public void Redirect(TransmissionPayload payload) { var header = payload.Message.ToServiceMessageHeader(); Guid?id = null; if (!mRedirectCache.ContainsKey(header)) { id = RedirectBuildCacheEntry(header, payload); } else { id = mRedirectCache[header]; } //There is an entry, but this may be null if there isn't a match. if (id.HasValue) { mRedirectRules[id.Value].Redirect(payload); payload.TraceWrite("Rule matched: {id.Value}", "Channel/Redirect"); } return; }
/// <summary> /// This method processes state notifications from other instances of the MasterJob. /// </summary> /// <param name="rq">The request.</param> /// <param name="rs">The responses - this is not used.</param> protected virtual async Task MasterJobStateNotificationIncoming(TransmissionPayload rq, List <TransmissionPayload> rs) { mMasterJobContext.NegotiationPollLastIn = DateTime.UtcNow; rq.TraceWrite("Received", "Command/MasterJobStateNotificationIncoming"); //If we are not active then do nothing. if (mMasterJobContext.State == MasterJobState.Disabled) { return; } //Filter out the messages sent from this service. //We will use these messages to signal a transition to the next state. if (ProcessRequestIfSelfGenerated(rq)) { return; } rq.TraceWrite("Processing", "Command/MasterJobStateNotificationIncoming"); //Raise an event for the incoming communication. FireAndDecorateEventArgs(OnMasterJobCommunication, () => new MasterJobCommunicationEventArgs( MasterJobCommunicationDirection.Incoming , mMasterJobContext.State , rq.Message.ActionType , mMasterJobContext.StateChangeCounter , rq.Message.OriginatorServiceId)); if (IsMatch(rq, MasterJobStates.IAmStandby)) { mMasterJobContext.PartnerSet(rq.Message.OriginatorServiceId, true); } else if (IsMatch(rq, MasterJobStates.IAmMaster)) { if (mMasterJobContext.State == MasterJobState.Active) { MasterJobStop(); } mMasterJobContext.PartnerSet(rq.Message.OriginatorServiceId, false); mMasterJobContext.State = MasterJobState.Inactive; await NegotiationTransmit(MasterJobStates.IAmStandby); } else if (IsMatch(rq, MasterJobStates.ResyncMaster)) { mMasterJobContext.PartnerMasterClear(); if (mMasterJobContext.State == MasterJobState.Inactive) { mMasterJobContext.State = MasterJobState.Starting; } } else if (IsMatch(rq, MasterJobStates.WhoIsMaster)) { mMasterJobContext.PartnerSet(rq.Message.OriginatorServiceId, true); if (mMasterJobContext.State == MasterJobState.Active) { await MasterJobSyncIAmMaster(); } } else if (IsMatch(rq, MasterJobStates.RequestingControl1)) { await InformOrResetState(MasterJobState.Requesting1); } else if (IsMatch(rq, MasterJobStates.RequestingControl2)) { await InformOrResetState(MasterJobState.Requesting2); } else if (IsMatch(rq, MasterJobStates.TakingControl)) { await InformOrResetState(MasterJobState.TakingControl); } else { Collector?.LogMessage(LoggingLevel.Warning, $"{rq?.Message?.ActionType??"NULL"} is not a valid negotiating action type for master job {FriendlyName}", "MasterJob"); rq.TraceWrite("Unhandled", "Command/MasterJobStateNotificationIncoming"); return; } rq.TraceWrite("Complete", "Command/MasterJobStateNotificationIncoming"); }