Skip to content

Commit

Permalink
Improved error reporting for reflection based handling rules
Browse files Browse the repository at this point in the history
  • Loading branch information
sweetlandj committed Feb 9, 2018
1 parent 0df2773 commit 7d98fd9
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 39 deletions.
20 changes: 15 additions & 5 deletions Publish.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -163,7 +160,7 @@ private async Task WhenHandlerResolutionErrorsOccur()
{
try
{
await AssertRulesAdded();
await TryHandleMessages();
}
catch (Exception e)
{
Expand All @@ -174,15 +171,15 @@ private async Task WhenHandlerResolutionErrorsOccur()
private void AssertDiagnosticEventEmitted()
{
Assert.NotNull(Exception);
MockDiagnosticService.Verify(x => x.Emit(It.Is<DiagnosticEvent>(e => IsHandlerResolutionError(e))), Times.AtLeastOnce);
MockDiagnosticService.Verify(x => x.Emit(It.Is<DiagnosticEvent>(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;
}

Expand Down Expand Up @@ -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<IMessageContext>();
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();
Expand Down
46 changes: 46 additions & 0 deletions Source/Platibus/Config/HandlerActivation.cs
Original file line number Diff line number Diff line change
@@ -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<object> _handlerFactory;
private readonly MethodInfo _method;

public HandlerActivation(IDiagnosticService diagnosticService, Type handlerType, Func<object> 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;
}
}
}
}
33 changes: 9 additions & 24 deletions Source/Platibus/Config/PlatibusConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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);
}
}

Expand Down
5 changes: 5 additions & 0 deletions Source/Platibus/Diagnostics/DiagnosticEventType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ public class DiagnosticEventType
/// </summary>
public static readonly DiagnosticEventType SignatureVerificationFailure = new DiagnosticEventType("SignatureVerificationFailure", DiagnosticEventLevel.Warn);

/// <summary>
/// Emitted when a handler cannot be activated (factory method returns null)
/// </summary>
public static readonly DiagnosticEventType HandlerActivationError = new DiagnosticEventType("HandlerActivationError", DiagnosticEventLevel.Error);

/// <summary>
/// The name of the diagnostic event type
/// </summary>
Expand Down

0 comments on commit 7d98fd9

Please sign in to comment.