/// <summary>Creates service definition that can be registered with a server</summary>
 /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
 public static grpc::ServerServiceDefinition BindService(OrderCommandBase serviceImpl)
 {
     return(grpc::ServerServiceDefinition.CreateBuilder()
            .AddMethod(__Method_SubmitOrder, serviceImpl.SubmitOrder)
            .AddMethod(__Method_CancelOrder, serviceImpl.CancelOrder).Build());
 }
Esempio n. 2
0
        /// <summary>
        ///   Performs the tasks needed to process a dead letter message observed on one of the
        ///   command queues.
        /// </summary>
        ///
        /// <param name="deadLetterLocation">The name of the location where the dead letter message was observed.  For example, the process-order queue.</param>
        /// <param name="deadLetterMessage">The brokered message containing the command to process.</param>
        ///
        protected internal virtual async Task ProcessDeadLetterMessage(string deadLetterLocation,
                                                                       BrokeredMessage deadLetterMessage)
        {
            if (deadLetterMessage == null)
            {
                this.Log.Error("The {CommandType} brokered message was null.", "DeadLetter");
                throw new ArgumentNullException(nameof(deadLetterMessage));
            }

            ILogger          log     = null;
            OrderCommandBase command = null;

            try
            {
                // Attempt to retrieve the command from the brokered message.

                using (var bodyStream = deadLetterMessage.GetBody <Stream>())
                    using (var reader = new StreamReader(bodyStream))
                        using (var jsonReader = new JsonTextReader(reader))
                        {
                            command = this.jsonSerializer.Deserialize <OrderCommandBase>(jsonReader);

                            jsonReader.Close();
                            reader.Close();
                            bodyStream.Close();
                        };

                // If the body was not a proper OrderCommandBase command, an empty command will be
                // returned.  Verify that the Id and OrderId are not in their default states.

                if ((command.Id == Guid.Empty) && (command.OrderId == null))
                {
                    throw new MissingDependencyException("The command could not be extracted from the brokered message");
                }

                // Process the order identified by the command.

                var correlationId = command.CorrelationId ?? deadLetterMessage.CorrelationId;

                log = this.Log.WithCorrelationId(correlationId);
                log.Information("A {CommandType} command was found on the dead letter {DeadLetterLocation} and notification is being sent.  {Command}", nameof(OrderCommandBase), deadLetterLocation, command);

                var result = await this.notifier.NotifyDeadLetterMessageAsync(deadLetterLocation, command.PartnerCode, command.OrderId, correlationId);

                // Consider the result to complete handling.  If processing was successful, then the message should be completed to
                // ensure that it is not retried.

                if (result?.Outcome == Outcome.Success)
                {
                    await this.CompleteMessageAsync(deadLetterMessage);

                    var sentEvent = (command?.CreateNewOrderEvent <NotificationSent>() ?? new NotificationSent
                    {
                        Id = Guid.NewGuid(),
                        CorrelationId = correlationId,
                        OccurredTimeUtc = this.clock.GetCurrentInstant().ToDateTimeUtc()
                    });

                    this.eventPublisher.TryPublishAsync(sentEvent).FireAndForget();
                    log.Information("Notification was successfully sent for the {CommandType} found on the dead letter {DeadLetterLocation}.  The fatal notification was sent for order {Partner}//{Order}.", nameof(NotifyOfFatalFailure), deadLetterLocation, command.PartnerCode, command.OrderId);
                }
                else
                {
                    // Submission was not successful.  Throw an exception to signal failure to the WebJob infrastructure
                    // so that the queue message is retried.
                    //
                    // We should be applying better retry logic and requeuing the message on a delay for a backoff.  This is a future
                    // enhancement.

                    log.Warning("Notification was not sent for the {CommandType} found on the dead letter {DeadLetterLocation}.  The message was handled successfully, but the sending failed", nameof(NotifyOfFatalFailure), deadLetterLocation);
                    throw new FailedtoHandleCommandException();
                }
            }

            catch (Exception ex) when(!(ex is FailedtoHandleCommandException))
            {
                (log ?? this.Log).Error(ex, "An exception occurred while sending notification for the {CommandType} found on the dead letter {DeadLetterLocation}.", nameof(NotifyOfFatalFailure), deadLetterLocation);

                var failedEvent = (command?.CreateNewOrderEvent <NotificationFailed>() ?? new NotificationFailed
                {
                    Id = Guid.NewGuid(),
                    CorrelationId = deadLetterMessage?.CorrelationId,
                    OccurredTimeUtc = this.clock.GetCurrentInstant().ToDateTimeUtc()
                });

                this.eventPublisher.TryPublishAsync(failedEvent).FireAndForget();

                // The exception must bubble in order for the WebJob to mark the failure and perform the actions needed
                // to fail the message so that it retries.

                throw;
            }
        }