From 7d98fd91139866874481246300eeca054ffec8de Mon Sep 17 00:00:00 2001 From: Jesse Sweetland Date: Fri, 9 Feb 2018 14:13:15 -0600 Subject: [PATCH] Improved error reporting for reflection based handling rules --- Publish.cmd | 20 ++++++-- .../PlatibusConfigurationExtensionsTests.cs | 37 +++++++++++---- Source/Platibus/Config/HandlerActivation.cs | 46 +++++++++++++++++++ .../Config/PlatibusConfigurationExtensions.cs | 33 ++++--------- .../Diagnostics/DiagnosticEventType.cs | 5 ++ 5 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 Source/Platibus/Config/HandlerActivation.cs diff --git a/Publish.cmd b/Publish.cmd index d24fb42b..27cf40a8 100644 --- a/Publish.cmd +++ b/Publish.cmd @@ -62,20 +62,30 @@ goto :eof goto :eof :push - set apiKey=%~1 - for %%p in (%outputDir%\*.nupkg) do ( + for %%p in (%outputDir%\*%packageVersion%.nupkg) do ( call :pushPackage %%p %apikey% ) + for %%s in (%outputDir%\*%packageVersion%.symbols.nupkg) do ( + call :pushSymbolsPackage %%s %apikey% + ) goto :eof :pushPackage set package=%~f1 - set apiKey=%~2 - set pushArgs=%package% + set pushArgs=%package% -s https://api.nuget.org/v3/index.json if "%apiKey%" neq "" ( set pushArgs=%pushArgs% -k %apiKey% ) - dotnet push %pushArgs% + dotnet nuget push %pushArgs% + goto :eof + +:pushSymbolsPackage + set symbolsPackage=%~f1 + set pushArgs=%symbolsPackage% -ss https://nuget.smbsrc.net/ + if "%apiKey%" neq "" ( + set pushArgs=%pushArgs% -sk %apiKey% + ) + dotnet nuget push %pushArgs% goto :eof :joinPath diff --git a/Source/Platibus.UnitTests/Config/PlatibusConfigurationExtensionsTests.cs b/Source/Platibus.UnitTests/Config/PlatibusConfigurationExtensionsTests.cs index e194b8a0..c95ef8c9 100644 --- a/Source/Platibus.UnitTests/Config/PlatibusConfigurationExtensionsTests.cs +++ b/Source/Platibus.UnitTests/Config/PlatibusConfigurationExtensionsTests.cs @@ -148,12 +148,9 @@ public async Task HandlingRulesAddedForMessageHandlerInterfaceImplementationsOfT } [Fact] - public async Task HandlerResolutionErrorsEmitDiagnosticEvents() + public async Task UncaughtHandlerErrorsEmitDiagnosticEvents() { - Configuration.AddHandlingRulesForType(typeof(H), () => - { - throw new Exception(); - }); + Configuration.AddHandlingRulesForType(typeof(H), () => throw new Exception()); await WhenHandlerResolutionErrorsOccur(); AssertDiagnosticEventEmitted(); @@ -163,7 +160,7 @@ private async Task WhenHandlerResolutionErrorsOccur() { try { - await AssertRulesAdded(); + await TryHandleMessages(); } catch (Exception e) { @@ -174,15 +171,15 @@ private async Task WhenHandlerResolutionErrorsOccur() private void AssertDiagnosticEventEmitted() { Assert.NotNull(Exception); - MockDiagnosticService.Verify(x => x.Emit(It.Is(e => IsHandlerResolutionError(e))), Times.AtLeastOnce); + MockDiagnosticService.Verify(x => x.Emit(It.Is(e => IsHandlerActivationError(e))), Times.AtLeastOnce); } - private bool IsHandlerResolutionError(DiagnosticEvent e) + private bool IsHandlerActivationError(DiagnosticEvent e) { Assert.NotNull(e); - Assert.Equal(DiagnosticEventType.ConfigurationError, e.Type); + Assert.Equal(DiagnosticEventType.HandlerActivationError, e.Type); Assert.Equal(Exception, e.Exception); - Assert.StartsWith("Error activiting instance of message handler ", e.Detail); + Assert.StartsWith("Error activating instance of message handler ", e.Detail); return true; } @@ -210,6 +207,26 @@ private async Task AssertRulesAdded() mockContext.Verify(); } + private async Task TryHandleMessages() + { + var handlingRules = Configuration.HandlingRules.ToList(); + Assert.Equal(2, handlingRules.Count); + + var expectedASpec = new MessageNamePatternSpecification(@"^A$"); + var aRules = handlingRules.Where(r => expectedASpec.Equals(r.Specification)).ToList(); + Assert.Single(aRules); + + var expectedBSpec = new MessageNamePatternSpecification(@"^B$"); + var bRules = handlingRules.Where(r => expectedBSpec.Equals(r.Specification)).ToList(); + Assert.Single(bRules); + + var mockContext = new Mock(); + var cancellationToken = default(CancellationToken); + + await aRules[0].MessageHandler.HandleMessage(new A(), mockContext.Object, cancellationToken); + await bRules[0].MessageHandler.HandleMessage(new B(), mockContext.Object, cancellationToken); + } + private void AssertExplicitQueueNames() { var handlingRules = Configuration.HandlingRules.ToList(); diff --git a/Source/Platibus/Config/HandlerActivation.cs b/Source/Platibus/Config/HandlerActivation.cs new file mode 100644 index 00000000..bfbf4944 --- /dev/null +++ b/Source/Platibus/Config/HandlerActivation.cs @@ -0,0 +1,46 @@ +using Platibus.Diagnostics; +using System; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace Platibus.Config +{ + internal class HandlerActivation : IMessageHandler + { + private readonly IDiagnosticService _diagnosticService; + private readonly Type _handlerType; + private readonly Func _handlerFactory; + private readonly MethodInfo _method; + + public HandlerActivation(IDiagnosticService diagnosticService, Type handlerType, Func handlerFactory, MethodInfo method) + { + _diagnosticService = diagnosticService; + _handlerType = handlerType; + _handlerFactory = handlerFactory; + _method = method; + } + + public async Task HandleMessage(object content, IMessageContext messageContext, CancellationToken cancellationToken) + { + try + { + var handler = _handlerFactory(); + if (handler == null && !_method.IsStatic) + { + throw new NullReferenceException("Handler factory returned null handler instance"); + } + await (Task)_method.Invoke(handler, new[] { content, messageContext, cancellationToken }); + } + catch (Exception e) + { + _diagnosticService.Emit(new DiagnosticEventBuilder(null, DiagnosticEventType.HandlerActivationError) + { + Detail = $"Error activating instance of message handler {_handlerType}: {e.Message}", + Exception = e + }.Build()); + throw; + } + } + } +} diff --git a/Source/Platibus/Config/PlatibusConfigurationExtensions.cs b/Source/Platibus/Config/PlatibusConfigurationExtensions.cs index 049be16e..778e0232 100644 --- a/Source/Platibus/Config/PlatibusConfigurationExtensions.cs +++ b/Source/Platibus/Config/PlatibusConfigurationExtensions.cs @@ -24,9 +24,9 @@ using System.Linq; using System.Security.Cryptography; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Platibus.Diagnostics; namespace Platibus.Config { @@ -403,32 +403,17 @@ public static void AddHandlingRulesForType(this PlatibusConfiguration configurat { var messageType = autoBindInterface.GetGenericArguments().First(); var messageName = configuration.MessageNamingService.GetNameForType(messageType); - var specification = new MessageNamePatternSpecification("^" + messageName + "$"); + var specification = new MessageNamePatternSpecification("^" + Regex.Escape(messageName) + "$"); var queueName = queueNameFactory(handlerType, messageType); - var method = autoBindInterface.GetMethod("HandleMessage", - new[] { messageType, typeof(IMessageContext), typeof(CancellationToken) }); + var args = new[] {messageType, typeof(IMessageContext), typeof(CancellationToken)}; + var method = autoBindInterface.GetMethod("HandleMessage", args); - if (method != null) - { - configuration.AddHandlingRule(specification, (object msg, IMessageContext ctx, CancellationToken ct) => - { - try - { - var handler = handlerFactory(); - return (Task)method.Invoke(handler, new[] { msg, ctx, ct }); - } - catch (Exception e) - { - diagnosticService.Emit(new DiagnosticEventBuilder(null, DiagnosticEventType.ConfigurationError) - { - Detail = "Error activiting instance of message handler " + handlerType, - Exception = e - }.Build()); - throw; - } - }, queueName, queueOptions); - } + if (method == null) continue; + + var handlerActivation = new HandlerActivation(diagnosticService, handlerType, handlerFactory, method); + var rule = new HandlingRule(specification, handlerActivation, queueName, queueOptions); + configuration.AddHandlingRule(rule); } } diff --git a/Source/Platibus/Diagnostics/DiagnosticEventType.cs b/Source/Platibus/Diagnostics/DiagnosticEventType.cs index 8a78c6cb..ace12933 100644 --- a/Source/Platibus/Diagnostics/DiagnosticEventType.cs +++ b/Source/Platibus/Diagnostics/DiagnosticEventType.cs @@ -226,6 +226,11 @@ public class DiagnosticEventType /// public static readonly DiagnosticEventType SignatureVerificationFailure = new DiagnosticEventType("SignatureVerificationFailure", DiagnosticEventLevel.Warn); + /// + /// Emitted when a handler cannot be activated (factory method returns null) + /// + public static readonly DiagnosticEventType HandlerActivationError = new DiagnosticEventType("HandlerActivationError", DiagnosticEventLevel.Error); + /// /// The name of the diagnostic event type ///