예제 #1
0
        public virtual void ReceiveEvent(IEvent <TAuthenticationToken> @event)
        {
            switch (@event.Framework)
            {
            case FrameworkType.Akka:
                Logger.LogInfo(string.Format("An event arrived of the type '{0}' but was marked as coming from the '{1}' framework, so it was dropped.", @event.GetType().FullName, @event.Framework));
                return;
            }

            CorrelationIdHelper.SetCorrelationId(@event.CorrelationId);
            AuthenticationTokenHelper.SetAuthenticationToken(@event.AuthenticationToken);

            Type eventType  = @event.GetType();
            bool isRequired = BusHelper.IsEventRequired(eventType);

            IEnumerable <Action <IMessage> > handlers = Routes.GetHandlers(@event, isRequired).Select(x => x.Delegate).ToList();

            // This check doesn't require an isRequired check as there will be an exception raised above and handled below.
            if (!handlers.Any())
            {
                Logger.LogDebug(string.Format("The event handler for '{0}' is not required.", eventType.FullName));
            }

            foreach (Action <IMessage> handler in handlers)
            {
                handler(@event);
            }
        }
예제 #2
0
        /// <summary>
        /// The default event handler that
        /// check if the <see cref="IEvent{TAuthenticationToken}"/> has already been processed by this framework,
        /// checks if the <see cref="IEvent{TAuthenticationToken}"/> is required,
        /// finds the handler from the provided <paramref name="routeManager"/>.
        /// </summary>
        /// <param name="event">The <see cref="IEvent{TAuthenticationToken}"/> to process.</param>
        /// <param name="routeManager">The <see cref="RouteManager"/> to get the <see cref="IEventHandler{TAuthenticationToken,TCommand}"/> from.</param>
        /// <param name="framework">The current framework.</param>
        /// <returns>
        /// True indicates the <paramref name="event"/> was successfully handled by a handler.
        /// False indicates the <paramref name="event"/> wasn't handled, but didn't throw an error, so by convention, that means it was skipped.
        /// Null indicates the <paramref name="event"/> wasn't handled as it was already handled.
        /// </returns>
        public virtual bool?DefaultReceiveEvent(IEvent <TAuthenticationToken> @event, RouteManager routeManager, string framework)
        {
            Type eventType = @event.GetType();

            if (@event.Frameworks != null && @event.Frameworks.Contains(framework))
            {
                // if this is the only framework in the list, then it's fine to handle as it's just pre-stamped, if there is more than one framework, then exit.
                if (@event.Frameworks.Count() != 1)
                {
                    Logger.LogInfo("The provided event has already been processed in Azure.", string.Format("{0}\\DefaultReceiveEvent({1})", GetType().FullName, eventType.FullName));
                    return(null);
                }
            }

            CorrelationIdHelper.SetCorrelationId(@event.CorrelationId);
            AuthenticationTokenHelper.SetAuthenticationToken(@event.AuthenticationToken);

            bool isRequired = BusHelper.IsEventRequired(eventType);

            IEnumerable <Action <IMessage> > handlers = routeManager.GetHandlers(@event, isRequired).Select(x => x.Delegate).ToList();

            // This check doesn't require an isRequired check as there will be an exception raised above and handled below.
            if (!handlers.Any())
            {
                Logger.LogDebug(string.Format("The event handler for '{0}' is not required.", eventType.FullName));
                return(false);
            }

            foreach (Action <IMessage> handler in handlers)
            {
                handler(@event);
            }
            return(true);
        }
예제 #3
0
        public virtual void DefaultReceiveCommand(ICommand <TAuthenticationToken> command, RouteManager routeManager, string framework)
        {
            Type commandType = command.GetType();

            if (command.Frameworks != null && command.Frameworks.Contains(framework))
            {
                Logger.LogInfo("The provided command has already been processed in Azure.", string.Format("{0}\\DefaultReceiveCommand({1})", GetType().FullName, commandType.FullName));
                return;
            }

            CorrelationIdHelper.SetCorrelationId(command.CorrelationId);
            AuthenticationTokenHelper.SetAuthenticationToken(command.AuthenticationToken);

            bool isRequired = BusHelper.IsEventRequired(commandType);

            RouteHandlerDelegate commandHandler = routeManager.GetSingleHandler(command, isRequired);

            // This check doesn't require an isRequired check as there will be an exception raised above and handled below.
            if (commandHandler == null)
            {
                Logger.LogDebug(string.Format("The command handler for '{0}' is not required.", commandType.FullName));
                return;
            }

            Action <IMessage> handler = commandHandler.Delegate;

            handler(command);
        }
예제 #4
0
        public virtual bool PrepareAndValidateEvent <TEvent>(TEvent @event, string framework, out IEnumerable <RouteHandlerDelegate> handlers)
            where TEvent : IEvent <TAuthenticationToken>
        {
            Type eventType = @event.GetType();

            if (@event.Frameworks != null && @event.Frameworks.Contains(framework))
            {
                Logger.LogInfo("The provided event has already been processed in Akka.", string.Format("{0}\\PrepareAndValidateEvent({1})", GetType().FullName, eventType.FullName));
                handlers = Enumerable.Empty <RouteHandlerDelegate>();
                return(false);
            }

            PrepareEvent(@event, framework);


            bool isRequired = BusHelper.IsEventRequired(eventType);

            handlers = Routes.GetHandlers(@event, isRequired);
            // This check doesn't require an isRequired check as there will be an exception raised above and handled below.
            if (handlers == null || !handlers.Any())
            {
                Logger.LogDebug(string.Format("An event handler for '{0}' is not required.", eventType.FullName));
            }

            return(true);
        }
예제 #5
0
        /// <summary>
        /// The default command handler that
        /// check if the <see cref="ICommand{TAuthenticationToken}"/> has already been processed by this framework,
        /// checks if the <see cref="ICommand{TAuthenticationToken}"/> is required,
        /// finds the handler from the provided <paramref name="routeManager"/>.
        /// </summary>
        /// <param name="command">The <see cref="ICommand{TAuthenticationToken}"/> to process.</param>
        /// <param name="routeManager">The <see cref="RouteManager"/> to get the <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> from.</param>
        /// <param name="framework">The current framework.</param>
        /// <returns>
        /// True indicates the <paramref name="command"/> was successfully handled by a handler.
        /// False indicates the <paramref name="command"/> wasn't handled, but didn't throw an error, so by convention, that means it was skipped.
        /// Null indicates the command<paramref name="command"/> wasn't handled as it was already handled.
        /// </returns>
        public virtual bool?DefaultReceiveCommand(ICommand <TAuthenticationToken> command, RouteManager routeManager, string framework)
        {
            Type commandType = command.GetType();

            if (command.Frameworks != null && command.Frameworks.Contains(framework))
            {
                // if this is the only framework in the list, then it's fine to handle as it's just pre-stamped, if there is more than one framework, then exit.
                if (command.Frameworks.Count() != 1)
                {
                    Logger.LogInfo("The provided command has already been processed in Azure.", string.Format("{0}\\DefaultReceiveCommand({1})", GetType().FullName, commandType.FullName));
                    return(null);
                }
            }

            CorrelationIdHelper.SetCorrelationId(command.CorrelationId);
            AuthenticationTokenHelper.SetAuthenticationToken(command.AuthenticationToken);

            bool isRequired = BusHelper.IsEventRequired(commandType);

            RouteHandlerDelegate commandHandler = routeManager.GetSingleHandler(command, isRequired);

            // This check doesn't require an isRequired check as there will be an exception raised above and handled below.
            if (commandHandler == null)
            {
                Logger.LogDebug(string.Format("The command handler for '{0}' is not required.", commandType.FullName));
                return(false);
            }

            Action <IMessage> handler = commandHandler.Delegate;

            handler(command);
            return(true);
        }
예제 #6
0
        protected virtual bool PrepareAndValidateCommand <TCommand>(TCommand command, out RouteHandlerDelegate commandHandler)
            where TCommand : ICommand <TAuthenticationToken>
        {
            Type commandType = command.GetType();

            if (command.Frameworks != null && command.Frameworks.Contains("Akka"))
            {
                // if this is the only framework in the list, then it's fine to handle as it's just pre-stamped, if there is more than one framework, then exit.
                if (command.Frameworks.Count() != 1)
                {
                    Logger.LogInfo("The provided command has already been processed in Akka.", string.Format("{0}\\PrepareAndValidateEvent({1})", GetType().FullName, commandType.FullName));
                    commandHandler = null;
                    return(false);
                }
            }

            ICommandValidator <TAuthenticationToken, TCommand> commandValidator = null;

            try
            {
                commandValidator = DependencyResolver.Resolve <ICommandValidator <TAuthenticationToken, TCommand> >();
            }
            catch (Exception exception)
            {
                Logger.LogDebug("Locating an ICommandValidator failed.", string.Format("{0}\\Handle({1})", GetType().FullName, commandType.FullName), exception);
            }

            if (commandValidator != null && !commandValidator.IsCommandValid(command))
            {
                Logger.LogInfo("The provided command is not valid.", string.Format("{0}\\Handle({1})", GetType().FullName, commandType.FullName));
                commandHandler = null;
                return(false);
            }

            PrepareCommand(command);

            bool isRequired = BusHelper.IsEventRequired(commandType);

            commandHandler = Routes.GetSingleHandler(command, isRequired);
            // This check doesn't require an isRequired check as there will be an exception raised above and handled below.
            if (commandHandler == null)
            {
                Logger.LogDebug(string.Format("The command handler for '{0}' is not required.", commandType.FullName));
            }

            return(true);
        }
예제 #7
0
        protected virtual void ReceiveEvent(BrokeredMessage message)
        {
            var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource();

            try
            {
                Logger.LogDebug(string.Format("An event message arrived with the id '{0}'.", message.MessageId));
                string messageBody = message.GetBody <string>();
                IEvent <TAuthenticationToken> @event;

                try
                {
                    @event = MessageSerialiser.DeserialiseEvent(messageBody);
                }
                catch (JsonSerializationException exception)
                {
                    JsonSerializationException checkException = exception;
                    bool safeToExit = false;
                    do
                    {
                        if (checkException.Message.StartsWith("Could not load assembly"))
                        {
                            safeToExit = true;
                            break;
                        }
                    } while ((checkException = checkException.InnerException as JsonSerializationException) != null);
                    if (safeToExit)
                    {
                        const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')";
                        Match        match   = new Regex(pattern).Match(exception.Message);
                        if (match.Success)
                        {
                            string[] typeParts = match.Value.Split(',');
                            if (typeParts.Length == 2)
                            {
                                string classType  = typeParts[0];
                                bool   isRequired = BusHelper.IsEventRequired(string.Format("{0}.IsRequired", classType));

                                if (!isRequired)
                                {
                                    // Remove message from queue
                                    message.Complete();
                                    Logger.LogDebug(string.Format("An event message arrived with the id '{0}' but processing was skipped due to event settings.", message.MessageId));
                                    return;
                                }
                            }
                        }
                    }
                    throw;
                }

                CorrelationIdHelper.SetCorrelationId(@event.CorrelationId);
                Logger.LogInfo(string.Format("An event message arrived with the id '{0}' was of type {1}.", message.MessageId, @event.GetType().FullName));

                bool canRefresh;
                if (!ConfigurationManager.TryGetSetting(string.Format("{0}.ShouldRefresh", @event.GetType().FullName), out canRefresh))
                {
                    canRefresh = false;
                }

                if (canRefresh)
                {
                    Task.Factory.StartNew(() =>
                    {
                        while (!brokeredMessageRenewCancellationTokenSource.Token.IsCancellationRequested)
                        {
                            //Based on LockedUntilUtc property to determine if the lock expires soon
                            if (DateTime.UtcNow > message.LockedUntilUtc.AddSeconds(-10))
                            {
                                // If so, repeat the message
                                message.RenewLock();
                            }

                            Thread.Sleep(500);
                        }
                    }, brokeredMessageRenewCancellationTokenSource.Token);
                }

                ReceiveEvent(@event);

                // Remove message from queue
                message.Complete();
                Logger.LogDebug(string.Format("An event message arrived and was processed with the id '{0}'.", message.MessageId));

                IList <IEvent <TAuthenticationToken> > events;
                if (EventWaits.TryGetValue(@event.CorrelationId, out events))
                {
                    events.Add(@event);
                }
            }
            catch (Exception exception)
            {
                // Indicates a problem, unlock message in queue
                Logger.LogError(string.Format("An event message arrived with the id '{0}' but failed to be process.", message.MessageId), exception: exception);
                message.Abandon();
            }
            finally
            {
                // Cancel the lock of renewing the task
                brokeredMessageRenewCancellationTokenSource.Cancel();
            }
        }
예제 #8
0
        public virtual IEvent <TAuthenticationToken> ReceiveEvent(string messageBody, Action <IEvent <TAuthenticationToken> > receiveEventHandler, string messageId, Action skippedAction = null, Action lockRefreshAction = null)
        {
            IEvent <TAuthenticationToken> @event;

            try
            {
                @event = MessageSerialiser.DeserialiseEvent(messageBody);
            }
            catch (JsonSerializationException exception)
            {
                JsonSerializationException checkException = exception;
                bool safeToExit = false;
                do
                {
                    if (checkException.Message.StartsWith("Could not load assembly"))
                    {
                        safeToExit = true;
                        break;
                    }
                } while ((checkException = checkException.InnerException as JsonSerializationException) != null);
                if (safeToExit)
                {
                    const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')";
                    Match        match   = new Regex(pattern).Match(exception.Message);
                    if (match.Success)
                    {
                        string[] typeParts = match.Value.Split(',');
                        if (typeParts.Length == 2)
                        {
                            string classType  = typeParts[0];
                            bool   isRequired = BusHelper.IsEventRequired(string.Format("{0}.IsRequired", classType));

                            if (!isRequired)
                            {
                                if (skippedAction != null)
                                {
                                    skippedAction();
                                }
                                return(null);
                            }
                        }
                    }
                }
                throw;
            }

            string eventTypeName = @event.GetType().FullName;

            CorrelationIdHelper.SetCorrelationId(@event.CorrelationId);
            Logger.LogInfo(string.Format("An event message arrived with the {0} was of type {1}.", messageId, eventTypeName));

            bool canRefresh;

            if (!ConfigurationManager.TryGetSetting(string.Format("{0}.ShouldRefresh", eventTypeName), out canRefresh))
            {
                canRefresh = false;
            }

            if (canRefresh)
            {
                if (lockRefreshAction == null)
                {
                    Logger.LogWarning(string.Format("An event message arrived with the {0} was of type {1} and was destined to support lock renewal, but no action was provided.", messageId, eventTypeName));
                }
                else
                {
                    lockRefreshAction();
                }
            }

            receiveEventHandler(@event);

            return(@event);
        }
예제 #9
0
        /// <summary>
        /// Deserialises and processes the <paramref name="messageBody"/> received from the network through the provided <paramref name="receiveCommandHandler"/>.
        /// </summary>
        /// <param name="messageBody">A serialised <see cref="IMessage"/>.</param>
        /// <param name="receiveCommandHandler">The handler method that will process the <see cref="ICommand{TAuthenticationToken}"/>.</param>
        /// <param name="messageId">The network id of the <see cref="IMessage"/>.</param>
        /// <param name="skippedAction">The <see cref="Action"/> to call when the <see cref="ICommand{TAuthenticationToken}"/> is being skipped.</param>
        /// <param name="lockRefreshAction">The <see cref="Action"/> to call to refresh the network lock.</param>
        /// <returns>The <see cref="ICommand{TAuthenticationToken}"/> that was processed.</returns>
        public virtual ICommand <TAuthenticationToken> ReceiveCommand(string messageBody, Func <ICommand <TAuthenticationToken>, bool?> receiveCommandHandler, string messageId, Action skippedAction = null, Action lockRefreshAction = null)
        {
            ICommand <TAuthenticationToken> command;

            try
            {
                command = MessageSerialiser.DeserialiseCommand(messageBody);
            }
            catch (JsonSerializationException exception)
            {
                JsonSerializationException checkException = exception;
                bool safeToExit = false;
                do
                {
                    if (checkException.Message.StartsWith("Could not load assembly"))
                    {
                        safeToExit = true;
                        break;
                    }
                } while ((checkException = checkException.InnerException as JsonSerializationException) != null);
                if (safeToExit)
                {
                    const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')";
                    Match        match   = new Regex(pattern).Match(exception.Message);
                    if (match.Success)
                    {
                        string[] typeParts = match.Value.Split(',');
                        if (typeParts.Length == 2)
                        {
                            string classType  = typeParts[0];
                            bool   isRequired = BusHelper.IsEventRequired(string.Format("{0}.IsRequired", classType));

                            if (!isRequired)
                            {
                                if (skippedAction != null)
                                {
                                    skippedAction();
                                }
                                return(null);
                            }
                        }
                    }
                }
                throw;
            }

            string commandTypeName = command.GetType().FullName;

            CorrelationIdHelper.SetCorrelationId(command.CorrelationId);
            AuthenticationTokenHelper.SetAuthenticationToken(command.AuthenticationToken);
            Logger.LogInfo(string.Format("A command message arrived with the {0} was of type {1}.", messageId, commandTypeName));

            bool canRefresh;

            if (!ConfigurationManager.TryGetSetting(string.Format("{0}.ShouldRefresh", commandTypeName), out canRefresh))
            {
                canRefresh = false;
            }

            if (canRefresh)
            {
                if (lockRefreshAction == null)
                {
                    Logger.LogWarning(string.Format("A command message arrived with the {0} was of type {1} and was destined to support lock renewal, but no action was provided.", messageId, commandTypeName));
                }
                else
                {
                    lockRefreshAction();
                }
            }

            // a false response means the action wasn't handled, but didn't throw an error, so we assume, by convention, that this means it was skipped.
            bool?result = receiveCommandHandler(command);

            if (result != null && !result.Value)
            {
                if (skippedAction != null)
                {
                    skippedAction();
                }
            }

            return(command);
        }
예제 #10
0
        /// <summary>
        /// Using a <see cref="Task"/>, clears all dead letters from the topic and subscription of the
        /// provided <paramref name="topicName"/> and <paramref name="topicSubscriptionName"/>.
        /// </summary>
        /// <param name="topicName">The name of the topic.</param>
        /// <param name="topicSubscriptionName">The name of the subscription.</param>
        /// <returns></returns>
        protected virtual CancellationTokenSource CleanUpDeadLetters(string topicName, string topicSubscriptionName)
        {
            var brokeredMessageRenewCancellationTokenSource  = new CancellationTokenSource();
            IDictionary <string, string> telemetryProperties = new Dictionary <string, string> {
                { "Type", "Azure/Servicebus" }
            };
            int lockIssues = 0;

            Action <BrokeredMessage, IMessage> leaveDeadlLetterInQueue = (deadLetterBrokeredMessage, deadLetterMessage) =>
            {
                // Remove message from queue
                try
                {
                    deadLetterBrokeredMessage.Abandon();
                    lockIssues = 0;
                }
                catch (MessageLockLostException)
                {
                    lockIssues++;
                    Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId));
                }
                Logger.LogDebug(string.Format("A dead-letter message of type {0} arrived with the id '{1}' but left in the queue due to settings.", deadLetterMessage.GetType().FullName, deadLetterBrokeredMessage.MessageId));
            };
            Action <BrokeredMessage> removeDeadlLetterFromQueue = deadLetterBrokeredMessage =>
            {
                // Remove message from queue
                try
                {
                    deadLetterBrokeredMessage.Complete();
                    lockIssues = 0;
                }
                catch (MessageLockLostException)
                {
                    lockIssues++;
                    Logger.LogWarning(string.Format("The lock supplied for complete for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId));
                }
                Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}' but was removed as processing was skipped due to settings.", deadLetterBrokeredMessage.MessageId));
            };

            Task.Factory.StartNewSafely(() =>
            {
                int loop = 0;
                while (!brokeredMessageRenewCancellationTokenSource.Token.IsCancellationRequested)
                {
                    lockIssues = 0;
                    MessagingFactory factory = MessagingFactory.CreateFromConnectionString(ConnectionString);
                    string deadLetterPath    = SubscriptionClient.FormatDeadLetterPath(topicName, topicSubscriptionName);
                    MessageReceiver client   = factory.CreateMessageReceiver(deadLetterPath, ReceiveMode.PeekLock);

                    IEnumerable <BrokeredMessage> brokeredMessages = client.ReceiveBatch(1000);

                    foreach (BrokeredMessage brokeredMessage in brokeredMessages)
                    {
                        if (lockIssues > 10)
                        {
                            break;
                        }
                        try
                        {
                            Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}'.", brokeredMessage.MessageId));
                            string messageBody = brokeredMessage.GetBody <string>();

                            // Closure protection
                            BrokeredMessage message = brokeredMessage;
                            try
                            {
                                AzureBusHelper.ReceiveEvent
                                (
                                    messageBody,
                                    @event =>
                                {
                                    bool isRequired = BusHelper.IsEventRequired(@event.GetType());
                                    if (!isRequired)
                                    {
                                        removeDeadlLetterFromQueue(message);
                                    }
                                    else
                                    {
                                        leaveDeadlLetterInQueue(message, @event);
                                    }
                                    return(true);
                                },
                                    string.Format("id '{0}'", brokeredMessage.MessageId),
                                    () =>
                                {
                                    removeDeadlLetterFromQueue(message);
                                },
                                    () => { }
                                );
                            }
                            catch
                            {
                                AzureBusHelper.ReceiveCommand
                                (
                                    messageBody,
                                    command =>
                                {
                                    bool isRequired = BusHelper.IsEventRequired(command.GetType());
                                    if (!isRequired)
                                    {
                                        removeDeadlLetterFromQueue(message);
                                    }
                                    else
                                    {
                                        leaveDeadlLetterInQueue(message, command);
                                    }
                                    return(true);
                                },
                                    string.Format("id '{0}'", brokeredMessage.MessageId),
                                    () =>
                                {
                                    removeDeadlLetterFromQueue(message);
                                },
                                    () => { }
                                );
                            }
                        }
                        catch (Exception exception)
                        {
                            TelemetryHelper.TrackException(exception, null, telemetryProperties);
                            // Indicates a problem, unlock message in queue
                            Logger.LogError(string.Format("A dead-letter message arrived with the id '{0}' but failed to be process.", brokeredMessage.MessageId), exception: exception);
                            try
                            {
                                brokeredMessage.Abandon();
                            }
                            catch (MessageLockLostException)
                            {
                                lockIssues++;
                                Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", brokeredMessage.MessageId));
                            }
                        }
                    }

                    client.Close();

                    if (loop++ % 5 == 0)
                    {
                        loop = 0;
                        Thread.Yield();
                    }
                    else
                    {
                        Thread.Sleep(500);
                    }
                }
                try
                {
                    brokeredMessageRenewCancellationTokenSource.Dispose();
                }
                catch (ObjectDisposedException) { }
            }, brokeredMessageRenewCancellationTokenSource.Token);

            return(brokeredMessageRenewCancellationTokenSource);
        }
예제 #11
0
        protected virtual void ReceiveCommand(BrokeredMessage message)
        {
            try
            {
                Logger.LogDebug(string.Format("A command message arrived with the id '{0}'.", message.MessageId));
                string messageBody = message.GetBody <string>();
                ICommand <TAuthenticationToken> command;
                try
                {
                    command = MessageSerialiser.DeserialiseCommand(messageBody);
                }
                catch (JsonSerializationException exception)
                {
                    JsonSerializationException checkException = exception;
                    bool safeToExit = false;
                    do
                    {
                        if (checkException.Message.StartsWith("Could not load assembly"))
                        {
                            safeToExit = true;
                            break;
                        }
                    } while ((checkException = checkException.InnerException as JsonSerializationException) != null);
                    if (safeToExit)
                    {
                        const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')";
                        Match        match   = new Regex(pattern).Match(exception.Message);
                        if (match.Success)
                        {
                            string[] typeParts = match.Value.Split(',');
                            if (typeParts.Length == 2)
                            {
                                string classType  = typeParts[0];
                                bool   isRequired = BusHelper.IsEventRequired(string.Format("{0}.IsRequired", classType));

                                if (!isRequired)
                                {
                                    // Remove message from queue
                                    message.Complete();
                                    Logger.LogDebug(string.Format("A command message arrived with the id '{0}' but processing was skipped due to command settings.", message.MessageId));
                                    return;
                                }
                            }
                        }
                    }
                    throw;
                }

                CorrelationIdHelper.SetCorrelationId(command.CorrelationId);
                Logger.LogInfo(string.Format("A command message arrived with the id '{0}' was of type {1}.", message.MessageId, command.GetType().FullName));

                ReceiveCommand(command);

                // Remove message from queue
                message.Complete();
                Logger.LogDebug(string.Format("A command message arrived and was processed with the id '{0}'.", message.MessageId));
            }
            catch (Exception exception)
            {
                // Indicates a problem, unlock message in queue
                Logger.LogError(string.Format("A command message arrived with the id '{0}' but failed to be process.", message.MessageId), exception: exception);
                message.Abandon();
            }
        }
예제 #12
0
        /// <summary>
        /// Using a <see cref="Task"/>, clears all dead letters from the topic and subscription of the
        /// provided <paramref name="topicName"/> and <paramref name="topicSubscriptionName"/>.
        /// </summary>
        /// <param name="topicName">The name of the topic.</param>
        /// <param name="topicSubscriptionName">The name of the subscription.</param>
        /// <returns></returns>
        protected virtual CancellationTokenSource CleanUpDeadLetters(string topicName, string topicSubscriptionName)
        {
            var brokeredMessageRenewCancellationTokenSource  = new CancellationTokenSource();
            IDictionary <string, string> telemetryProperties = new Dictionary <string, string> {
                { "Type", "Azure/Servicebus" }
            };
            int lockIssues = 0;

#if NET452
            Action <BrokeredMessage, IMessage> leaveDeadlLetterInQueue = (deadLetterBrokeredMessage, deadLetterMessage) =>
#endif
#if NETSTANDARD2_0
            Action <IMessageReceiver, BrokeredMessage, IMessage> leaveDeadlLetterInQueue = (client, deadLetterBrokeredMessage, deadLetterMessage) =>
#endif
            {
                // Remove message from queue
                try
                {
#if NET452
                    deadLetterBrokeredMessage.Abandon();
#endif
#if NETSTANDARD2_0
                    client.AbandonAsync(deadLetterBrokeredMessage.SystemProperties.LockToken).Wait(1500);
#endif
                    lockIssues = 0;
                }
                catch (MessageLockLostException)
                {
                    lockIssues++;
                    Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId));
                }
                Logger.LogDebug(string.Format("A dead-letter message of type {0} arrived with the id '{1}' but left in the queue due to settings.", deadLetterMessage.GetType().FullName, deadLetterBrokeredMessage.MessageId));
            };
#if NET452
            Action <BrokeredMessage> removeDeadlLetterFromQueue = (deadLetterBrokeredMessage) =>
#endif
#if NETSTANDARD2_0
            Action <IMessageReceiver, BrokeredMessage> removeDeadlLetterFromQueue = (client, deadLetterBrokeredMessage) =>
#endif
            {
                // Remove message from queue
                try
                {
#if NET452
                    deadLetterBrokeredMessage.Complete();
#endif
#if NETSTANDARD2_0
                    client.CompleteAsync(deadLetterBrokeredMessage.SystemProperties.LockToken).Wait(1500);
#endif
                    lockIssues = 0;
                }
                catch (MessageLockLostException)
                {
                    lockIssues++;
                    Logger.LogWarning(string.Format("The lock supplied for complete for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId));
                }
                Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}' but was removed as processing was skipped due to settings.", deadLetterBrokeredMessage.MessageId));
            };

            Task.Factory.StartNewSafely(() =>
            {
                int loop = 0;
                while (!brokeredMessageRenewCancellationTokenSource.Token.IsCancellationRequested)
                {
                    lockIssues = 0;
                    IEnumerable <BrokeredMessage> brokeredMessages;

#if NET452
                    MessagingFactory factory = MessagingFactory.CreateFromConnectionString(ConnectionString);
                    string deadLetterPath    = SubscriptionClient.FormatDeadLetterPath(topicName, topicSubscriptionName);
                    MessageReceiver client   = factory.CreateMessageReceiver(deadLetterPath, ReceiveMode.PeekLock);
                    brokeredMessages         = client.ReceiveBatch(1000);
#endif
#if NETSTANDARD2_0
                    string deadLetterPath  = EntityNameHelper.FormatDeadLetterPath(EntityNameHelper.FormatSubscriptionPath(topicName, topicSubscriptionName));
                    MessageReceiver client = new MessageReceiver(ConnectionString, deadLetterPath, ReceiveMode.PeekLock);
                    Task <IList <BrokeredMessage> > receiveTask = client.ReceiveAsync(1000);
                    receiveTask.Wait(10000);
                    if (receiveTask.IsCompleted && receiveTask.Result != null)
                    {
                        brokeredMessages = receiveTask.Result;
                    }
                    else
                    {
                        brokeredMessages = Enumerable.Empty <BrokeredMessage>();
                    }
#endif

                    foreach (BrokeredMessage brokeredMessage in brokeredMessages)
                    {
                        if (lockIssues > 10)
                        {
                            break;
                        }
                        try
                        {
                            Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}'.", brokeredMessage.MessageId));
                            string messageBody = brokeredMessage.GetBodyAsString();

                            // Closure protection
                            BrokeredMessage message = brokeredMessage;
                            try
                            {
                                AzureBusHelper.ReceiveEvent
                                (
                                    messageBody,
                                    @event =>
                                {
                                    bool isRequired = BusHelper.IsEventRequired(@event.GetType());
                                    if (!isRequired)
                                    {
#if NET452
                                        removeDeadlLetterFromQueue(message);
#endif
#if NETSTANDARD2_0
                                        removeDeadlLetterFromQueue(client, message);
#endif
                                    }
                                    else
                                    {
#if NET452
                                        leaveDeadlLetterInQueue(message, @event);
#endif
#if NETSTANDARD2_0
                                        leaveDeadlLetterInQueue(client, message, @event);
#endif
                                    }
                                    return(true);
                                },
                                    string.Format("id '{0}'", brokeredMessage.MessageId),
                                    ExtractSignature(message),
                                    SigningTokenConfigurationKey,
                                    () =>
                                {
#if NET452
                                    removeDeadlLetterFromQueue(message);
#endif
#if NETSTANDARD2_0
                                    removeDeadlLetterFromQueue(client, message);
#endif
                                },
                                    () => { }
                                );
                            }
                            catch
                            {
                                AzureBusHelper.ReceiveCommand
                                (
                                    messageBody,
                                    command =>
                                {
                                    bool isRequired = BusHelper.IsEventRequired(command.GetType());
                                    if (!isRequired)
                                    {
#if NET452
                                        removeDeadlLetterFromQueue(message);
#endif
#if NETSTANDARD2_0
                                        removeDeadlLetterFromQueue(client, message);
#endif
                                    }
                                    else
                                    {
#if NET452
                                        leaveDeadlLetterInQueue(message, command);
#endif
#if NETSTANDARD2_0
                                        leaveDeadlLetterInQueue(client, message, command);
#endif
                                    }
                                    return(true);
                                },
                                    string.Format("id '{0}'", brokeredMessage.MessageId),
                                    ExtractSignature(message),
                                    SigningTokenConfigurationKey,
                                    () =>
                                {
#if NET452
                                    removeDeadlLetterFromQueue(message);
#endif
#if NETSTANDARD2_0
                                    removeDeadlLetterFromQueue(client, message);
#endif
                                },
                                    () => { }
                                );
                            }
                        }
                        catch (Exception exception)
                        {
                            TelemetryHelper.TrackException(exception, null, telemetryProperties);
                            // Indicates a problem, unlock message in queue
                            Logger.LogError(string.Format("A dead-letter message arrived with the id '{0}' but failed to be process.", brokeredMessage.MessageId), exception: exception);
                            try
                            {
#if NET452
                                brokeredMessage.Abandon();
#endif
#if NETSTANDARD2_0
                                client.AbandonAsync(brokeredMessage.SystemProperties.LockToken).Wait(1500);
#endif
                            }
                            catch (MessageLockLostException)
                            {
                                lockIssues++;
                                Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", brokeredMessage.MessageId));
                            }
                        }
                    }
#if NET452
                    client.Close();
#endif
#if NETSTANDARD2_0
                    client.CloseAsync().Wait(1500);
#endif

                    if (loop++ % 5 == 0)
                    {
                        loop = 0;
                        Thread.Yield();
                    }
                    else
                    {
                        Thread.Sleep(500);
                    }
                }
                try
                {
                    brokeredMessageRenewCancellationTokenSource.Dispose();
                }
                catch (ObjectDisposedException) { }
            }, brokeredMessageRenewCancellationTokenSource.Token);

            return(brokeredMessageRenewCancellationTokenSource);
        }
예제 #13
0
        /// <summary>
        /// Deserialises and processes the <paramref name="messageBody"/> received from the network through the provided <paramref name="receiveEventHandler"/>.
        /// </summary>
        /// <param name="messageBody">A serialised <see cref="IMessage"/>.</param>
        /// <param name="receiveEventHandler">The handler method that will process the <see cref="IEvent{TAuthenticationToken}"/>.</param>
        /// <param name="messageId">The network id of the <see cref="IMessage"/>.</param>
        /// <param name="signature">The signature of the <see cref="IMessage"/>.</param>
        /// <param name="signingTokenConfigurationKey">The configuration key for the signing token as used by <see cref="IConfigurationManager"/>.</param>
        /// <param name="skippedAction">The <see cref="Action"/> to call when the <see cref="IEvent{TAuthenticationToken}"/> is being skipped.</param>
        /// <param name="lockRefreshAction">The <see cref="Action"/> to call to refresh the network lock.</param>
        /// <returns>The <see cref="IEvent{TAuthenticationToken}"/> that was processed.</returns>
        public virtual IEvent <TAuthenticationToken> ReceiveEvent(string messageBody, Func <IEvent <TAuthenticationToken>, bool?> receiveEventHandler, string messageId, string signature, string signingTokenConfigurationKey, Action skippedAction = null, Action lockRefreshAction = null)
        {
            IEvent <TAuthenticationToken> @event;

            try
            {
                @event = MessageSerialiser.DeserialiseEvent(messageBody);
            }
            catch (JsonSerializationException exception)
            {
                JsonSerializationException checkException = exception;
                bool safeToExit = false;
                do
                {
                    if (checkException.Message.StartsWith("Could not load assembly"))
                    {
                        safeToExit = true;
                        break;
                    }
                } while ((checkException = checkException.InnerException as JsonSerializationException) != null);
                if (safeToExit)
                {
                    const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')";
                    Match        match   = new Regex(pattern).Match(exception.Message);
                    if (match.Success)
                    {
                        string[] typeParts = match.Value.Split(',');
                        if (typeParts.Length == 2)
                        {
                            string classType  = typeParts[0];
                            bool   isRequired = BusHelper.IsEventRequired(string.Format("{0}.IsRequired", classType));

                            if (!isRequired)
                            {
                                if (skippedAction != null)
                                {
                                    skippedAction();
                                }
                                return(null);
                            }
                        }
                    }
                }
                throw;
            }

            string eventTypeName = @event.GetType().FullName;

            CorrelationIdHelper.SetCorrelationId(@event.CorrelationId);
            AuthenticationTokenHelper.SetAuthenticationToken(@event.AuthenticationToken);
            object identifyMessage = null;
            var    identifiedEvent = @event as IEventWithIdentity <TAuthenticationToken>;

            if (identifiedEvent != null)
            {
                identifyMessage = string.Format(" for aggregate {0}", identifiedEvent.Rsn);
            }
            Logger.LogInfo(string.Format("An event message arrived with the {0} was of type {1}{2}.", messageId, eventTypeName, identifyMessage));

            VerifySignature(signingTokenConfigurationKey, signature, "An event", messageId, eventTypeName, identifyMessage, messageBody);
            bool canRefresh;

            if (!ConfigurationManager.TryGetSetting(string.Format("{0}.ShouldRefresh", eventTypeName), out canRefresh))
            {
                if (!ConfigurationManager.TryGetSetting(DefaultMessagesShouldRefreshConfigurationKey, out canRefresh))
                {
                    canRefresh = false;
                }
            }

            if (canRefresh)
            {
                if (lockRefreshAction == null)
                {
                    Logger.LogWarning(string.Format("An event message arrived with the {0} was of type {1} and was destined to support lock renewal, but no action was provided.", messageId, eventTypeName));
                }
                else
                {
                    lockRefreshAction();
                }
            }

            // a false response means the action wasn't handled, but didn't throw an error, so we assume, by convention, that this means it was skipped.
            bool?result = receiveEventHandler(@event);

            if (result != null && !result.Value)
            {
                if (skippedAction != null)
                {
                    skippedAction();
                }
            }

            return(@event);
        }