/// <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; } }
/// <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(); }
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); }
/// <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); } } }
/// <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); } }
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); }
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(); }
/// <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); }
/// <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(); }
/// <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(); } }
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(); }
/// <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); }
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)); }
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(); }
/// <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(); }
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); } }
/// <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); } }
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); }
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(); }
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(); }
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>()); }
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); }
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)); }
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(); } }
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(); }
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; }
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); }
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); } }
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); } }
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); }
/// <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); } } }