/// <summary>
        /// Invokes the routing function and performs some action depending on the returned <see cref="ForwardAction"/> result
        /// </summary>
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transportMessage = context.Load <TransportMessage>();
            var routingResult    = (await _routingFunction(transportMessage)) ?? ForwardAction.None;
            var actionType       = routingResult.ActionType;

            switch (actionType)
            {
            case ActionType.Forward:
                var destinationAddresses = routingResult.DestinationAddresses;
                var transactionContext   = context.Load <ITransactionContext>();

                _log.Debug("Forwarding {0} to {1}", transportMessage.GetMessageLabel(), string.Join(", ", destinationAddresses));

                await Task.WhenAll(
                    destinationAddresses
                    .Select(address => _transport.Send(address, transportMessage, transactionContext))
                    );

                break;

            case ActionType.None:
                await next();

                break;

            default:
                await next();

                break;
            }
        }
예제 #2
0
        /// <summary>
        /// Processes the message
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var invokers = context.Load<HandlerInvokers>();
            var didInvokeHandler = false;

            foreach (var invoker in invokers)
            {
                await invoker.Invoke();
                didInvokeHandler = true;
            }

            if (!didInvokeHandler)
            {
                var message = context.Load<Message>();
                
                var messageId = message.GetMessageId();
                var messageType = message.GetMessageType();

                var text = string.Format("Message with ID {0} and type {1} could not be dispatched to any handlers",
                    messageId, messageType);

                throw new RebusApplicationException(text);
            }

            await next();
        }
예제 #3
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            await next();

            var message         = context.Load <Message>();
            var handlerInvokers = context.Load <HandlerInvokers>();

            var createdAndUpdatedSagaData = handlerInvokers
                                            .Where(i => i.HasSaga)
                                            .Select(i => new
            {
                i.Handler,
                SagaData = i.GetSagaData()
            })
                                            .Where(a => a.SagaData != null)
                                            .ToList();

            var saveTasks = createdAndUpdatedSagaData
                            .Select(sagaData =>
            {
                var metadata = GetMetadata(sagaData.SagaData, sagaData.Handler, message);

                return(_sagaSnapshotStorage.Save(sagaData.SagaData, metadata));
            });

            await Task.WhenAll(saveTasks);
        }
예제 #4
0
        /// <summary>
        /// Checks to see if the incoming message has the <see cref="Headers.DeferredUntil"/> header. If that is the case, the message is either stored for later delivery
        /// or forwarded to the configured external timeout manager. If not, the message will be passed on down the pipeline.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transportMessage = context.Load <TransportMessage>();

            var headers = transportMessage.Headers;

            if (!headers.TryGetValue(Headers.DeferredUntil, out var deferredUntil))
            {
                await next();

                //return;don't return here! for some reason it is faster to have an "else"
            }
            else
            {
                if (!headers.ContainsKey(Headers.DeferredRecipient))
                {
                    throw new RebusApplicationException(
                              $"Received message {headers[Headers.MessageId]} with the '{Headers.DeferredUntil}' header" +
                              $" set to '{headers[Headers.DeferredUntil]}', but the message had no" +
                              $" '{Headers.DeferredRecipient}' header!");
                }

                if (UsingExternalTimeoutManager)
                {
                    var transactionContext = context.Load <ITransactionContext>();

                    await ForwardMessageToExternalTimeoutManager(transportMessage, transactionContext);
                }
                else
                {
                    await StoreMessageUntilDue(deferredUntil, headers, transportMessage);
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Checks to see if the incoming message has the <see cref="Headers.DeferredUntil"/> header. If that is the case, the message is either stored for later delivery
        /// or forwarded to the configured external timeout manager. If not, the message will be passed on down the pipeline.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transportMessage = context.Load <TransportMessage>();

            string deferredUntil;

            var headers = transportMessage.Headers;

            if (!headers.TryGetValue(Headers.DeferredUntil, out deferredUntil))
            {
                await next();

                return;
            }

            if (!headers.ContainsKey(Headers.ReturnAddress))
            {
                throw new ApplicationException(string.Format("Received message {0} with the '{1}' header set to '{2}', but the message had no '{3}' header!",
                                                             headers[Headers.MessageId],
                                                             Headers.DeferredUntil,
                                                             headers[Headers.DeferredUntil],
                                                             Headers.ReturnAddress));
            }

            if (UsingExternalTimeoutManager)
            {
                var transactionContext = context.Load <ITransactionContext>();

                await ForwardMessageToExternalTimeoutManager(transportMessage, transactionContext);
            }
            else
            {
                await StoreMessageUntilDue(deferredUntil, headers, transportMessage);
            }
        }
예제 #6
0
            public async Task Process(IncomingStepContext context, Func <Task> next)
            {
                var serviceProvider = context.Load <IServiceProvider>();

                SomethingDisposable somethingDisposable;

                using (var scope = serviceProvider.CreateScope())
                {
                    context.Save(scope);

                    somethingDisposable = scope.ServiceProvider.GetRequiredService <SomethingDisposable>();

                    Assert.That(somethingDisposable.HasBeenDisposed, Is.False);

                    var somethingDisposableResolvedAgain = scope.ServiceProvider.GetRequiredService <SomethingDisposable>();

                    Assert.That(somethingDisposableResolvedAgain, Is.SameAs(somethingDisposable));

                    await next();

                    var somethingDisposableInjectedIntoMessageHandler = context.Load <SomethingDisposable>();

                    Assert.That(somethingDisposableInjectedIntoMessageHandler, Is.SameAs(somethingDisposable));

                    Assert.That(somethingDisposable.HasBeenDisposed, Is.False);
                }

                Assert.That(somethingDisposable.HasBeenDisposed, Is.True);
            }
예제 #7
0
    public Task Process(IncomingStepContext context, Func <Task> next)
    {
        var message         = context.Load <Message>();
        var handlerInvokers = context.Load <HandlerInvokers>().ToList();

        handlerInvokers.RemoveAll(x => x.Handler.GetType() == typeof(RebusDistributedEventHandlerAdapter <object>));
        context.Save(new HandlerInvokers(message, handlerInvokers));

        return(next());
    }
            public async Task Process(IncomingStepContext context, Func <Task> next)
            {
                // this is the global service provider - we can load stuff from it
                var serviceProvider = context.Load <IServiceProvider>();

                // or we can create a scope and load stuff from that
                var serviceScope = context.Load <IServiceScope>();

                await next();
            }
예제 #9
0
        /// <summary>
        /// Executes the entire message processing pipeline in an exception handler, tracking the number of failed delivery attempts.
        /// Forwards the message to the error queue when the max number of delivery attempts has been exceeded.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transportMessage   = context.Load <TransportMessage>();
            var transactionContext = context.Load <ITransactionContext>();
            var messageId          = transportMessage.Headers.GetValueOrNull(Headers.MessageId);

            if (string.IsNullOrWhiteSpace(messageId))
            {
                await MoveMessageToErrorQueue("<no message ID>", transportMessage,
                                              transactionContext, string.Format("Received message with empty or absent '{0}' header! All messages must be" +
                                                                                " supplied with an ID . If no ID is present, the message cannot be tracked" +
                                                                                " between delivery attempts, and other stuff would also be much harder to" +
                                                                                " do - therefore, it is a requirement that messages be supplied with an ID.",
                                                                                Headers.MessageId),
                                              shortErrorDescription : string.Format("Received message with empty or absent '{0}' header", Headers.MessageId));

                return;
            }

            if (_errorTracker.HasFailedTooManyTimes(messageId))
            {
                // if we don't have 2nd level retries, just get the message out of the way
                if (!_simpleRetryStrategySettings.SecondLevelRetriesEnabled)
                {
                    await
                    MoveMessageToErrorQueue(messageId, transportMessage, transactionContext,
                                            GetErrorDescriptionFor(messageId), GetErrorDescriptionFor(messageId, brief : true));

                    _errorTracker.CleanUp(messageId);
                    return;
                }

                // change the identifier to track by to perform this 2nd level of delivery attempts
                var secondLevelMessageId = messageId + "-2nd-level";

                if (_errorTracker.HasFailedTooManyTimes(secondLevelMessageId))
                {
                    await
                    MoveMessageToErrorQueue(messageId, transportMessage, transactionContext,
                                            GetErrorDescriptionFor(messageId), GetErrorDescriptionFor(messageId, brief : true));

                    _errorTracker.CleanUp(messageId);
                    _errorTracker.CleanUp(secondLevelMessageId);
                    return;
                }

                context.Save(DispatchAsFailedMessageKey, true);

                await DispatchWithTrackerIdentifier(next, secondLevelMessageId, transactionContext);

                return;
            }

            await DispatchWithTrackerIdentifier(next, messageId, transactionContext);
        }
예제 #10
0
        /// <summary>
        /// For each <see cref="HandlerInvoker"/> found in the current <see cref="IncomingStepContext"/>'s <see cref="HandlerInvokers"/>,
        /// this step will see if the invoker's handler is actually a <see cref="Saga"/>. If that is the case, the saga's correlation properties
        /// are used to see if a piece of existing saga data can be retrieved and mounted on the <see cref="Saga{TSagaData}.Data"/> property.
        /// If no existing instance was found, but the saga implements <see cref="IAmInitiatedBy{TMessage}"/> for the current message,
        /// a new saga data instance will be created (and mounted). Otherwise, the message is ignored.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            // first we get the relevant handler invokers
            var handlerInvokersForSagas = context.Load <HandlerInvokers>()
                                          .Where(l => l.HasSaga)
                                          .ToList();

            // maybe short-circuit? this makes it slightly faster
            if (!handlerInvokersForSagas.Any())
            {
                await next();

                return;
            }

            var message            = context.Load <Message>();
            var label              = message.GetMessageLabel();
            var transactionContext = context.Load <ITransactionContext>();

            var body = message.Body;

            // keep track of saga data instances in these two lists
            var loadedSagaData       = new List <RelevantSagaInfo>();
            var newlyCreatedSagaData = new List <RelevantSagaInfo>();

            // and then we process them
            foreach (var sagaInvoker in handlerInvokersForSagas)
            {
                await TryMountSagaDataOnInvoker(sagaInvoker, body, label, loadedSagaData, newlyCreatedSagaData, transactionContext);
            }

            // invoke the rest of the pipeline (most likely also dispatching the incoming message to the now-ready saga handlers)
            await next();

            // everything went well - let's divide saga data instances into those to insert, update, and delete
            var newlyCreatedSagaDataToSave = newlyCreatedSagaData.Where(s => !s.Saga.WasMarkedAsComplete && !s.Saga.WasMarkedAsUnchanged);
            var loadedSagaDataToUpdate     = loadedSagaData.Where(s => !s.Saga.WasMarkedAsComplete && !s.Saga.WasMarkedAsUnchanged);
            var loadedSagaDataToDelete     = loadedSagaData.Where(s => s.Saga.WasMarkedAsComplete);

            foreach (var sagaDataToInsert in newlyCreatedSagaDataToSave)
            {
                await SaveSagaData(sagaDataToInsert, insert : true);
            }

            foreach (var sagaDataToUpdate in loadedSagaDataToUpdate)
            {
                await SaveSagaData(sagaDataToUpdate, insert : false);
            }

            foreach (var sagaDataToUpdate in loadedSagaDataToDelete)
            {
                await _sagaStorage.Delete(sagaDataToUpdate.SagaData);
            }
        }
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var scope            = context.Load <IServiceScope>(SafeRebusContextTags.ScopeContextTag);
            var outboxBus        = scope.ServiceProvider.GetService <IOutboxBus>();
            var transportMessage = context.Load <TransportMessage>();
            await outboxBus.BeginTransaction(transportMessage);

            await next();

            await outboxBus.Commit();
        }
예제 #12
0
    /// <summary>
    /// Executes the entire message processing pipeline in an exception handler, tracking the number of failed delivery attempts.
    /// Forwards the message to the error queue when the max number of delivery attempts has been exceeded.
    /// </summary>
    public async Task Process(IncomingStepContext context, Func <Task> next)
    {
        var transportMessage   = context.Load <TransportMessage>() ?? throw new RebusApplicationException("Could not find a transport message in the current incoming step context");
        var transactionContext = context.Load <ITransactionContext>() ?? throw new RebusApplicationException("Could not find a transaction context in the current incoming step context");

        var messageId = transportMessage.Headers.GetValueOrNull(Headers.MessageId);

        if (string.IsNullOrWhiteSpace(messageId))
        {
            await MoveMessageToErrorQueue(context, transactionContext,
                                          new RebusApplicationException($"Received message with empty or absent '{Headers.MessageId}' header! All messages must be" +
                                                                        " supplied with an ID . If no ID is present, the message cannot be tracked" +
                                                                        " between delivery attempts, and other stuff would also be much harder to" +
                                                                        " do - therefore, it is a requirement that messages be supplied with an ID."));

            return;
        }

        if (_errorTracker.HasFailedTooManyTimes(messageId))
        {
            // if we don't have 2nd level retries, just get the message out of the way
            if (!_simpleRetryStrategySettings.SecondLevelRetriesEnabled)
            {
                var aggregateException = GetAggregateException(messageId);
                await MoveMessageToErrorQueue(context, transactionContext, aggregateException);

                _errorTracker.CleanUp(messageId);
                return;
            }

            // change the identifier to track by to perform this 2nd level of delivery attempts
            var secondLevelMessageId = GetSecondLevelMessageId(messageId);

            if (_errorTracker.HasFailedTooManyTimes(secondLevelMessageId))
            {
                var aggregateException = GetAggregateException(messageId, secondLevelMessageId);
                await MoveMessageToErrorQueue(context, transactionContext, aggregateException);

                _errorTracker.CleanUp(messageId);
                _errorTracker.CleanUp(secondLevelMessageId);
                return;
            }

            context.Save(DispatchAsFailedMessageKey, true);

            await DispatchWithTrackerIdentifier(next, secondLevelMessageId, transactionContext, messageId, secondLevelMessageId);

            return;
        }

        await DispatchWithTrackerIdentifier(next, messageId, transactionContext, messageId);
    }
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var scope = context.Load <IServiceScope>(SafeRebusContextTags.ScopeContextTag);
            var outboxDuplicationFilterRepository = scope.ServiceProvider.GetService <IOutboxDuplicationFilterRepository>();
            var transportMessage = context.Load <TransportMessage>();
            var messageId        = transportMessage.Headers[Headers.MessageId];
            var messageGuidId    = Guid.Parse(messageId);

            if (await outboxDuplicationFilterRepository.TryInsertMessageId(messageGuidId))
            {
                await next();
            }
        }
예제 #14
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transactionScope = context.Load <ITransactionContext>();
            var scope            = transactionScope.GetOrNull <ILifetimeScope>("current-autofac-lifetime-scope");
            var message          = context.Load <Message>();
            var sessionStorage   = scope.Resolve <ISessionStorage>();

            var headers = MessageContext.Current.Headers.Select(x => (x.Key, (new[] { x.Value }).AsEnumerable())).ToArray();

            sessionStorage.SetHeaders(headers);

            await next();
        }
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transactionContext = context.Load <ITransactionContext>();
            var transportMessage   = context.Load <TransportMessage>();

            var clone = transportMessage.Clone();

            clone.Headers[Headers.AuditTime] = RebusTime.Now.ToString("O");

            await _transport.Send(_auditQueue, clone, transactionContext);

            await next();
        }
예제 #16
0
        /// <summary>
        /// Processes the message
        /// </summary>
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var invokers        = context.Load <HandlerInvokers>();
            var handlersInvoked = 0;
            var message         = invokers.Message;

            var messageId   = message.GetMessageId();
            var messageType = message.GetMessageType();

            // if dispatch has already been aborted (e.g. in a transport message filter or something else that
            // was run before us....) bail out here:
            if (context.Load <bool>(AbortDispatchContextKey))
            {
                _log.Debug("Skipping dispatch of message {messageType} {messageId}", messageType, messageId);
                await next().ConfigureAwait(false);

                return;
            }

            var stopwatch = Stopwatch.StartNew();

            for (var index = 0; index < invokers.Count; index++)
            {
                var invoker = invokers[index];

                await invoker.Invoke().ConfigureAwait(false);

                handlersInvoked++;

                // if dispatch was aborted at this point, bail out
                if (context.Load <bool>(AbortDispatchContextKey))
                {
                    _log.Debug("Skipping further dispatch of message {messageType} {messageId}", messageType, messageId);
                    break;
                }
            }

            // throw error if we should have executed a handler but we didn't
            if (handlersInvoked == 0)
            {
                var text = $"Message with ID {messageId} and type {messageType} could not be dispatched to any handlers";

                throw new RebusApplicationException(text);
            }

            _log.Debug("Dispatching {messageType} {messageId} to {count} handlers took {elapsedMs:0} ms",
                       messageType, messageId, handlersInvoked, stopwatch.ElapsedMilliseconds);

            await next().ConfigureAwait(false);
        }
예제 #17
0
        async Task ProcessDeadletterCommand(IncomingStepContext context, ManualDeadletterCommand deadletterCommand)
        {
            var originalTransportMessage = context.Load <OriginalTransportMessage>() ?? throw new RebusApplicationException("Could not find the original transport message in the current incoming step context");

            var transportMessage = originalTransportMessage.TransportMessage.Clone();
            var errorDetails     = deadletterCommand.ErrorDetails ?? "Manually dead-lettered";

            transportMessage.Headers[Headers.ErrorDetails] = errorDetails;
            transportMessage.Headers[Headers.SourceQueue]  = _transport.Address;

            var transactionContext = context.Load <ITransactionContext>();

            await _errorHandler.HandlePoisonMessage(transportMessage, transactionContext, new RebusApplicationException(errorDetails));
        }
예제 #18
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transactionContext = context.Load <ITransactionContext>(SafeRebusContextTags.TransactionContextTag);
            var scope            = transactionContext.GetOrAdd(SafeRebusContextTags.ScopeContextTag, () => ServiceProvider.CreateScope());
            var adapter          = scope.ServiceProvider.GetService <ISafeStandardAdapter>();
            var transportMessage = context.Load <TransportMessage>();

            if (adapter.IsUsableOnIncoming(transportMessage))
            {
                transportMessage = adapter.ConvertIncomingTransportMessage(transportMessage);
                context.Save(transportMessage);
            }

            await next();
        }
예제 #19
0
        /// <summary>
        /// Looks up handlers for the incoming message and saves the handlers (without invoking them) to the context as a <see cref="HandlerInvokers"/>
        /// </summary>
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var transactionContext = context.Load <ITransactionContext>();
            var message            = context.Load <Message>();
            var body           = message.Body;
            var messageType    = body.GetType();
            var methodToInvoke = _dispatchMethods
                                 .GetOrAdd(messageType, type => GetDispatchMethod(messageType));

            var handlerInvokers = await((Task <HandlerInvokers>)methodToInvoke.Invoke(this, new[] { body, transactionContext, message }));

            context.Save(handlerInvokers);

            await next();
        }
예제 #20
0
    public Task Process(IncomingStepContext context, Func <Task> next)
    {
        var message         = context.Load <Message>();
        var handlerInvokers = context.Load <HandlerInvokers>().ToList();

        if (handlerInvokers.All(x => x.Handler is IRebusDistributedEventHandlerAdapter))
        {
            handlerInvokers = new List <HandlerInvoker> {
                handlerInvokers.Last()
            };
            context.Save(new HandlerInvokers(message, handlerInvokers));
        }

        return(next());
    }
    public async Task Process(IncomingStepContext context, Func <Task> next)
    {
        var handlerInvokersForSagas = context.Load <HandlerInvokers>()
                                      .Where(l => l.HasSaga)
                                      .ToList();

        if (!handlerInvokersForSagas.Any())
        {
            await next();

            return;
        }

        var message            = context.Load <Message>();
        var transactionContext = context.Load <ITransactionContext>();
        var messageContext     = new MessageContext(transactionContext);

        var messageBody = message.Body;

        var correlationProperties = handlerInvokersForSagas
                                    .Select(h => h.Saga)
                                    .SelectMany(saga => _sagaHelper.GetCorrelationProperties(messageBody, saga).ForMessage(messageBody)
                                                .Select(correlationProperty => new { saga, correlationProperty }))
                                    .ToList();

        var locksToObtain = correlationProperties
                            .Select(a => new
        {
            SagaDataType             = a.saga.GetSagaDataType().FullName,
            CorrelationPropertyName  = a.correlationProperty.PropertyName,
            CorrelationPropertyValue = a.correlationProperty.ValueFromMessage(messageContext, messageBody)
        })
                            .Select(a => a.ToString())
                            .Select(lockId => Math.Abs(lockId.GetHashCode()) % _maxLockBuckets)
                            .Distinct()          // avoid accidentally acquiring the same lock twice, because a bucket got hit more than once
                            .OrderBy(str => str) // enforce consistent ordering to avoid deadlocks
                            .ToArray();

        try
        {
            await WaitForLocks(locksToObtain);
            await next();
        }
        finally
        {
            await ReleaseLocks(locksToObtain);
        }
    }
예제 #22
0
        /// <summary>
        /// Check if the current message handling is 'done' and flag MessageId as FINAL.
        /// Support also 2nd-level retries and FailFast.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="beforeTry">true if the check is prior to handling, to bailout before event try.</param>
        /// <remarks>
        /// At the start of the Handling we want to bailout if the current DeliveryCount is greater than the threashold.
        /// DeliveryCount is natively 1-based: first try has count=1 before trying.
        /// When this is the last try we need first to execute.
        /// </remarks>
        private void _checkFinal(IncomingStepContext context, bool beforeTry = false)
        {
            var transportMessage = context.Load <TransportMessage>();
            var messageId        = transportMessage.Headers.GetValue(Headers.MessageId);

            int?transportDeliveryCount = null;

            if (transportMessage.Headers.TryGetValue(DeliveryCountHeader, out var dch) &&
                int.TryParse(dch, out var dc))
            {
                transportDeliveryCount = dc;
            }

            _checkFinal(messageId, transportDeliveryCount, beforeTry);

            if (_arkRetryStrategySettings.SecondLevelRetriesEnabled)
            {
                var secondLevelMessageId = GetSecondLevelMessageId(messageId);

                if (transportDeliveryCount != null)
                {
                    // can happen that we have fail-fasted the first-level and we're failing the 2nd-level
                    transportDeliveryCount -= _arkRetryStrategySettings.MaxDeliveryAttempts;
                    if (transportDeliveryCount <= 0)
                    {
                        transportDeliveryCount = 1;
                    }
                }

                _checkFinal(secondLevelMessageId, transportDeliveryCount, beforeTry);
            }
        }
예제 #23
0
        async Task HandleRoutingSlip(IncomingStepContext context, Message message)
        {
            var transactionContext = context.Load <ITransactionContext>();
            var transportMessage   = await _serialier.Serialize(message).ConfigureAwait(false);

            var headers = transportMessage.Headers;

            var itinerary       = GetDestinations(headers, Headers.RoutingSlipItinerary);
            var nextDestination = itinerary.FirstOrDefault();

            if (nextDestination == null)
            {
                // no more destinations - stop forwarding it now
                return;
            }

            var remainingDestinations = itinerary.Skip(1);

            transportMessage.Headers[Headers.RoutingSlipItinerary] = string.Join(";", remainingDestinations);

            var travelogue = GetDestinations(transportMessage.Headers, Headers.RoutingSlipTravelogue);

            travelogue.Add(_transport.Address);

            transportMessage.Headers[Headers.RoutingSlipTravelogue] = string.Join(";", travelogue);
            transportMessage.Headers[Headers.CorrelationSequence]   = GetNextSequenceNumber(transportMessage.Headers);

            await _transport.Send(nextDestination, transportMessage, transactionContext).ConfigureAwait(false);
        }
예제 #24
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var message = context.Load <Message>();

            var hasInReplyToHeader = message.Headers.TryGetValue(Headers.InReplyTo, out var inReplyToMessageId);

            if (hasInReplyToHeader)
            {
                var isRequestReplyCorrelationId = inReplyToMessageId.StartsWith(SpecialMessageIdPrefix);

                if (isRequestReplyCorrelationId)
                {
                    // if we could successfully remove the task completion source for the request message ID,
                    // we can complete it here
                    if (_messages.TryRemove(inReplyToMessageId, out var taskCompletionSource))
                    {
                        taskCompletionSource.SetResult(message);

                        // abort anything else in the pipeline
                        return;
                    }

                    _log.Warn("Received message with message ID {messageId}, which was determined to be a reply to be handled as an inline request-reply reply (because of the {prefix} prefix), BUT the dictionary of task completion sources did NOT contain an entry for that ID",
                              inReplyToMessageId, SpecialMessageIdPrefix);
                }
            }

            await next();
        }
예제 #25
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var message = context.Load <Message>();

            string correlationId;

            var hasCorrelationId = message.Headers.TryGetValue(Headers.CorrelationId, out correlationId);

            if (hasCorrelationId)
            {
                var isRequestReplyCorrelationId = correlationId.StartsWith(SpecialCorrelationIdPrefix);
                if (isRequestReplyCorrelationId)
                {
                    var isRequest = message.Headers.ContainsKey(SpecialRequestTag);
                    if (!isRequest)
                    {
                        // it's the reply!
                        _messages[correlationId] = new TimedMessage(message);
                        return;
                    }
                }
            }

            await next();
        }
예제 #26
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var message = context.Load <Message>();
            var headers = message.Headers;

            if (headers.ContainsKey(MapLegacyHeadersIncomingStep.LegacyMessageHeader))
            {
                var body  = message.Body;
                var array = body as object[];

                if (array == null)
                {
                    throw new FormatException(
                              $"Incoming message has the '{MapLegacyHeadersIncomingStep.LegacyMessageHeader}' header, but the message body {body} is not an object[] as expected");
                }

                foreach (var bodyToDispatch in array)
                {
                    var messageBodyToDispatch = PossiblyConvertBody(bodyToDispatch, headers);

                    context.Save(new Message(headers, messageBodyToDispatch));

                    await next();
                }

                return;
            }

            await next();
        }
        public async Task WrapsInvokersSoTheyCanRunInsideAnActivity()
        {
            var step = new IncomingDiagnosticsHandlerInvokerWrapper();

            var headers = new Dictionary <string, string>
            {
                { Headers.Type, "MyType" }
            };
            var transportMessage = new TransportMessage(headers, Array.Empty <byte>());
            var message          = new Message(headers, Array.Empty <byte>());

            var innerInvoker = new TestInvoker(() => { });

            var handlerInvokers = new HandlerInvokers(message, new[] { innerInvoker });

            var scope   = new RebusTransactionScope();
            var context = new IncomingStepContext(transportMessage, scope.TransactionContext);

            context.Save(handlerInvokers);

            var callbackWasInvoked = false;
            await step.Process(context, () =>
            {
                callbackWasInvoked = true;
                return(Task.CompletedTask);
            });

            Assert.That(callbackWasInvoked);

            var updatedInvokers = context.Load <HandlerInvokers>();

            Assert.That(updatedInvokers, Is.Not.SameAs(handlerInvokers));
            Assert.That(updatedInvokers, Has.Exactly(1).Items.And.All.TypeOf <HandlerInvokerWrapper>());
        }
예제 #28
0
        private static Activity StartActivity(IncomingStepContext context)
        {
            var message = context.Load <Message>();
            var headers = message.Headers;

            var activity = new Activity(TracingConstants.ConsumerActivityName);

            if (headers.TryGetValue(TracingConstants.TraceParentHeaderName, out var requestId) == false)
            {
                headers.TryGetValue(TracingConstants.RequestIdHeaderName, out requestId);
            }

            if (string.IsNullOrEmpty(requestId) == false)
            {
                // This is the magic
                activity.SetParentId(requestId);

                if (headers.TryGetValue(TracingConstants.TraceStateHeaderName, out var traceState))
                {
                    activity.TraceStateString = traceState;
                }
            }

            // The current activity gets an ID with the W3C format
            activity.Start();

            return(activity);
        }
예제 #29
0
        void PossiblyDecompressTransportMessage(IncomingStepContext context)
        {
            var transportMessage = context.Load <TransportMessage>();

            string contentEncoding;

            if (!transportMessage.Headers.TryGetValue(Headers.ContentEncoding, out contentEncoding))
            {
                return;
            }

            if (contentEncoding != ZipMessagesOutgoingStep.GzipEncodingHeader)
            {
                var message = $"The message {transportMessage.GetMessageLabel()} has a '{Headers.ContentEncoding}' with the" +
                              $" value '{contentEncoding}', but this middleware only knows how to decompress" +
                              $" '{ZipMessagesOutgoingStep.GzipEncodingHeader}'";

                throw new ArgumentException(message);
            }

            var headers        = transportMessage.Headers.Clone();
            var compressedBody = transportMessage.Body;

            headers.Remove(Headers.ContentEncoding);

            var body = _zipper.Unzip(compressedBody);

            context.Save(new TransportMessage(headers, body));
        }
예제 #30
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var items = context.Load <ITransactionContext>().Items;

            if (!items.TryGetValue(CurrentTransactionContextKey, out var temp))
            {
                await next();

                return;
            }

            if (!(temp is Transaction transaction))
            {
                await next();

                return;
            }

            using (var scope = new TransactionScope(transaction, TransactionScopeAsyncFlowOption.Enabled))
            {
                await next();

                scope.Complete();
            }
        }
예제 #31
0
        public async Task Process(IncomingStepContext context, Func <Task> next)
        {
            var message = context.Load <Message>();
            var args    = new MessageHandledEventHandlerArgs();
            var bus     = _bus.Value;

            foreach (var e in _beforeMessageHandled)
            {
                e(bus, message.Headers, message.Body, context, args);
            }

            try
            {
                await next();

                foreach (var e in _afterMessageHandled)
                {
                    e(bus, message.Headers, message.Body, context, args);
                }
            }
            catch (Exception exception)
            {
                context.Save(exception);

                foreach (var e in _afterMessageHandled)
                {
                    e(bus, message.Headers, message.Body, context, args);
                }

                if (!args.IgnoreException)
                {
                    throw;
                }
            }
        }
 /// <summary>
 /// Deserializes the incoming message by invoking the currently configured <see cref="ISerializer"/> on the <see cref="TransportMessage"/> found in the context,
 /// storing the result as the <see cref="Message"/> returned by the serializer
 /// </summary>
 public async Task Process(IncomingStepContext context, Func<Task> next)
 {
     var transportMessage = context.Load<TransportMessage>();
     var message = await _serializer.Deserialize(transportMessage);
     context.Save(message);
     await next();
 }
        /// <summary>
        /// Processes the message
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var invokers = context.Load<HandlerInvokers>();
            var didInvokeHandler = false;

            // if dispatch has already been aborted (e.g. in a transport message filter or something else that
            // was run before us....) bail out here:
            if (context.Load<bool>(AbortDispatchContextKey))
            {
                await next();
                return;
            }

            foreach (var invoker in invokers)
            {
                await invoker.Invoke();
                didInvokeHandler = true;

                // if dispatch was aborted at this point, bail out
                if (context.Load<bool>(AbortDispatchContextKey)) break;
            }

            // throw error if we should have executed a handler but we didn't
            if (!didInvokeHandler)
            {
                var message = context.Load<Message>();
                
                var messageId = message.GetMessageId();
                var messageType = message.GetMessageType();

                var text = string.Format("Message with ID {0} and type {1} could not be dispatched to any handlers",
                    messageId, messageType);

                throw new RebusApplicationException(text);
            }

            await next();
        }
예제 #34
0
        static string GetCorrelationIdToAssign(IncomingStepContext incomingStepContext, Message outgoingMessage)
        {
            // if we're handling an incoming message right now, let either current correlation ID or the message ID flow
            if (incomingStepContext == null)
            {
                return outgoingMessage.Headers.GetValue(Headers.MessageId);
            }
            
            var incomingMessage = incomingStepContext.Load<Message>();

            var correlationId = incomingMessage.Headers.GetValueOrNull(Headers.CorrelationId)
                                ?? incomingMessage.Headers.GetValue(Headers.MessageId);

            return correlationId;
        }
예제 #35
0
        static CorrelationInfo GetCorrelationIdToAssign(IncomingStepContext incomingStepContext, Message outgoingMessage)
        {
            // if we're handling an incoming message right now, let either current correlation ID or the message ID flow
            if (incomingStepContext == null)
            {
                var messageId = outgoingMessage.Headers.GetValue(Headers.MessageId);

                return new CorrelationInfo(messageId, 0);
            }

            var incomingMessage = incomingStepContext.Load<Message>();

            var correlationId = incomingMessage.Headers.GetValueOrNull(Headers.CorrelationId)
                                ?? incomingMessage.Headers.GetValue(Headers.MessageId);

            var correlationSequenceHeader = incomingMessage.Headers.GetValueOrNull(Headers.CorrelationSequence) ?? "0";
            var currentCorrelationSequence = ParseOrZero(correlationSequenceHeader);
            var nextCorrelationSequence = currentCorrelationSequence + 1;

            return new CorrelationInfo(correlationId, nextCorrelationSequence);
        }
예제 #36
0
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();

            string deferredUntil;

            var headers = transportMessage.Headers;

            if (!headers.TryGetValue(Headers.DeferredUntil, out deferredUntil))
            {
                await next();
                return;
            }

            if (!headers.ContainsKey(Headers.ReturnAddress))
            {
                throw new ApplicationException(string.Format("Received message {0} with the '{1}' header set to '{2}', but the message had no '{3}' header!",
                    headers[Headers.MessageId],
                    Headers.DeferredUntil,
                    headers[Headers.DeferredUntil],
                    Headers.ReturnAddress));
            }

            if (UsingExternalTimeoutManager)
            {
                var transactionContext = context.Load<ITransactionContext>();

                await ForwardMessageToExternalTimeoutManager(transportMessage, transactionContext);
            }
            else
            {
                await StoreMessageUntilDue(deferredUntil, headers, transportMessage);
            }
        }
예제 #37
0
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var handlerInvokersForSagas = context.Load<HandlerInvokers>()
                .Where(l => l.HasSaga)
                .ToList();

            var message = context.Load<Message>();
            var label = message.GetMessageLabel();

            var body = message.Body;
            var loadedSagaData = new List<RelevantSagaInfo>();
            var newlyCreatedSagaData = new List<RelevantSagaInfo>();

            foreach (var sagaInvoker in handlerInvokersForSagas)
            {
                var foundExistingSagaData = false;

                var correlationProperties = _sagaHelper.GetCorrelationProperties(body, sagaInvoker.Saga);
                var correlationPropertiesRelevantForMessage = correlationProperties.ForMessage(body);

                foreach (var correlationProperty in correlationPropertiesRelevantForMessage)
                {
                    var valueFromMessage = correlationProperty.ValueFromMessage(body);
                    var sagaData = await _sagaStorage.Find(sagaInvoker.Saga.GetSagaDataType(), correlationProperty.PropertyName, valueFromMessage);

                    if (sagaData == null) continue;

                    sagaInvoker.SetSagaData(sagaData);
                    foundExistingSagaData = true;
                    loadedSagaData.Add(new RelevantSagaInfo(sagaData, correlationProperties, sagaInvoker.Saga));

                    _log.Debug("Found existing saga data with ID {0} for message {1}", sagaData.Id, label);
                    break;
                }

                if (!foundExistingSagaData)
                {
                    var messageType = body.GetType();
                    var canBeInitiatedByThisMessageType = sagaInvoker.CanBeInitiatedBy(messageType);

                    if (canBeInitiatedByThisMessageType)
                    {
                        var newSagaData = _sagaHelper.CreateNewSagaData(sagaInvoker.Saga);
                        sagaInvoker.SetSagaData(newSagaData);
                        _log.Debug("Created new saga data with ID {0} for message {1}", newSagaData.Id, label);
                        newlyCreatedSagaData.Add(new RelevantSagaInfo(newSagaData, correlationProperties, sagaInvoker.Saga));
                    }
                    else
                    {
                        _log.Debug("Could not find existing saga data for message {0}", label);
                        sagaInvoker.SkipInvocation();
                    }
                }
            }

            await next();

            var newlyCreatedSagaDataToSave = newlyCreatedSagaData.Where(s => !s.Saga.WasMarkedAsComplete);
            var loadedSagaDataToUpdate = loadedSagaData.Where(s => !s.Saga.WasMarkedAsComplete);
            var loadedSagaDataToDelete = loadedSagaData.Where(s => s.Saga.WasMarkedAsComplete);

            foreach (var sagaDataToInsert in newlyCreatedSagaDataToSave)
            {
                await _sagaStorage.Insert(sagaDataToInsert.SagaData, sagaDataToInsert.CorrelationProperties);
            }

            foreach (var sagaDataToUpdate in loadedSagaDataToUpdate)
            {
                await _sagaStorage.Update(sagaDataToUpdate.SagaData, sagaDataToUpdate.CorrelationProperties);
            }

            foreach (var sagaDataToUpdate in loadedSagaDataToDelete)
            {
                await _sagaStorage.Delete(sagaDataToUpdate.SagaData);
            }
        }
예제 #38
0
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();

            string deferredUntil;

            var headers = transportMessage.Headers;

            if (!headers.TryGetValue(Headers.DeferredUntil, out deferredUntil))
            {
                await next();
                return;
            }

            if (!headers.ContainsKey(Headers.ReturnAddress))
            {
                throw new ApplicationException(string.Format("Received message {0} with the '{1}' header set to '{2}', but the message had no '{3}' header!",
                    headers[Headers.MessageId],
                    Headers.DeferredUntil,
                    headers[Headers.DeferredUntil],
                    Headers.ReturnAddress));
            }

            DateTimeOffset approximateDueTime;
            if (!DateTimeOffset.TryParseExact(deferredUntil, DateTimeOffsetFormat, CultureInfo.InvariantCulture,
                    DateTimeStyles.RoundtripKind, out approximateDueTime))
            {
                throw new FormatException(string.Format("Could not parse the '{0}' header value '{1}' into a valid DateTimeOffset!",
                    Headers.DeferredUntil, deferredUntil));
            }

            _log.Info("Deferring message {0} until {1}", headers[Headers.MessageId], approximateDueTime);

            headers.Remove(Headers.DeferredUntil);

            await _timeoutManager.Defer(approximateDueTime, headers, transportMessage.Body);
        }
예제 #39
0
        /// <summary>
        /// Checks to see if the incoming message has the <see cref="Headers.DeferredUntil"/> header. If that is the case, the message is either stored for later delivery
        /// or forwarded to the configured external timeout manager. If not, the message will be passed on down the pipeline.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();

            string deferredUntil;

            var headers = transportMessage.Headers;

            if (!headers.TryGetValue(Headers.DeferredUntil, out deferredUntil))
            {
                await next();
                //return;don't return here! for some reason it is faster to have an "else"
            }
            else
            {
                if (!headers.ContainsKey(Headers.DeferredRecipient))
                {
                    throw new ApplicationException(
                        $"Received message {headers[Headers.MessageId]} with the '{Headers.DeferredUntil}' header" +
                        $" set to '{headers[Headers.DeferredUntil]}', but the message had no" +
                        $" '{Headers.DeferredRecipient}' header!");
                }

                if (UsingExternalTimeoutManager)
                {
                    var transactionContext = context.Load<ITransactionContext>();

                    await ForwardMessageToExternalTimeoutManager(transportMessage, transactionContext);
                }
                else
                {
                    await StoreMessageUntilDue(deferredUntil, headers, transportMessage);
                }
            }
        }