diff --git a/.editorconfig b/.editorconfig index 586b27c..20a49b6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,11 +1,54 @@ -# EditorConfig is awesome: http://EditorConfig.org - -# top-most EditorConfig file +# This file is the top-most EditorConfig file root = true -# Windows-style newlines with a newline ending every file +# All Files [*] +charset = utf-8 end_of_line = crlf +indent_style = space +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true + +# .NET Code files +[*.{cs,csx,cake,vb}] indent_style = tab +tab_width = 4 insert_final_newline = true + +# Visual Studio Solution Files +[*.sln] +indent_style = tab tab_width = 4 + +# Visual Studio XML Project Files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# Various XML Configuration Files +[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}] +indent_size = 2 + +# JSON Files +[*.{json,json5}] +indent_size = 2 + +# YAML Files +[*.{yml,yaml}] +indent_size = 2 + +# Markdown Files +[*.md] +trim_trailing_whitespace = false + +# Web Files +[*.{htm,html,js,ts,tsx,css,sass,scss,less,svg,vue}] +indent_size = 2 +insert_final_newline = true + +# Batch Files +[*.{cmd,bat}] + +# Bash Files +[*.sh] +end_of_line = lf diff --git a/.gitignore b/.gitignore index 6bbb609..d6ffc67 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -210,6 +213,8 @@ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored @@ -235,8 +240,6 @@ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ -# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true -**/wwwroot/lib/ # RIA/Silverlight projects Generated_Code/ @@ -261,6 +264,7 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser +*- Backup*.rdl # Microsoft Fakes FakesAssemblies/ @@ -296,10 +300,6 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - # CodeRush personal settings .cr/personal @@ -344,6 +344,9 @@ ASALocalRun/ # BeatPulse healthcheck temp database healthchecksdb +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + # WinMerge *.bak diff --git a/LICENSE b/LICENSE deleted file mode 100644 index a8fc851..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 62871b8..de41423 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Coverage Status](https://coveralls.io/repos/github/Jericho/Picton.Messaging/badge.svg?branch=master)](https://coveralls.io/github/Jericho/Picton.Messaging?branch=master) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FJericho%2FPicton.Messaging.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FJericho%2FPicton.Messaging?ref=badge_shield) + ## About Picton.Messaging is a C# library containing a high performance message processor (also known as a message "pump") designed to process messages from an Azure storage queue as efficiently as possible. diff --git a/Source/Picton.Messaging.IntegrationTests/ColoredConsoleLogProvider.cs b/Source/Picton.Messaging.IntegrationTests/ColoredConsoleLogProvider.cs index 8993280..b57132c 100644 --- a/Source/Picton.Messaging.IntegrationTests/ColoredConsoleLogProvider.cs +++ b/Source/Picton.Messaging.IntegrationTests/ColoredConsoleLogProvider.cs @@ -1,11 +1,11 @@ -namespace Picton.Messaging.IntegrationTests +namespace Picton.Messaging.IntegrationTests { using Logging; - using Picton.Messaging.Logging.LogProviders; using System; using System.Collections.Generic; using System.Globalization; + // Inspired by: https://github.com/damianh/LibLog/blob/master/src/LibLog.Example.ColoredConsoleLogProvider/ColoredConsoleLogProvider.cs public class ColoredConsoleLogProvider : ILogProvider { private static readonly Dictionary Colors = new Dictionary @@ -40,7 +40,7 @@ public Logger GetLogger(string name) { // Please note: locking is important to ensure that multiple threads // don't attempt to change the foreground color at the same time - lock (this) + lock (Console.Out) { if (Colors.TryGetValue(logLevel, out ConsoleColor consoleColor)) { @@ -75,7 +75,7 @@ public IDisposable OpenNestedContext(string message) { return NullDisposable.Instance; } - + /// /// Opens a mapped diagnostics context. Not supported in EntLib logging. /// diff --git a/Source/Picton.Messaging.IntegrationTests/ConsoleUtils.cs b/Source/Picton.Messaging.IntegrationTests/ConsoleUtils.cs new file mode 100644 index 0000000..61f37a4 --- /dev/null +++ b/Source/Picton.Messaging.IntegrationTests/ConsoleUtils.cs @@ -0,0 +1,32 @@ +using System; + +namespace Picton.Messaging.IntegrationTests +{ + public static class ConsoleUtils + { + public static void CenterConsole() + { + var hWin = NativeMethods.GetConsoleWindow(); + if (hWin == IntPtr.Zero) return; + + var monitor = NativeMethods.MonitorFromWindow(hWin, NativeMethods.MONITOR_DEFAULT_TO_NEAREST); + if (monitor == IntPtr.Zero) return; + + var monitorInfo = new NativeMethods.NativeMonitorInfo(); + NativeMethods.GetMonitorInfo(monitor, monitorInfo); + + NativeMethods.GetWindowRect(hWin, out NativeMethods.NativeRectangle consoleInfo); + + var monitorWidth = monitorInfo.Monitor.Right - monitorInfo.Monitor.Left; + var monitorHeight = monitorInfo.Monitor.Bottom - monitorInfo.Monitor.Top; + + var consoleWidth = consoleInfo.Right - consoleInfo.Left; + var consoleHeight = consoleInfo.Bottom - consoleInfo.Top; + + var left = monitorInfo.Monitor.Left + (monitorWidth - consoleWidth) / 2; + var top = monitorInfo.Monitor.Top + (monitorHeight - consoleHeight) / 2; + + NativeMethods.MoveWindow(hWin, left, top, consoleWidth, consoleHeight, false); + } + } +} diff --git a/Source/Picton.Messaging.IntegrationTests/NativeMethods.cs b/Source/Picton.Messaging.IntegrationTests/NativeMethods.cs new file mode 100644 index 0000000..2a582e8 --- /dev/null +++ b/Source/Picton.Messaging.IntegrationTests/NativeMethods.cs @@ -0,0 +1,52 @@ +using System; +using System.Runtime.InteropServices; + +namespace Picton.Messaging.IntegrationTests +{ + public static class NativeMethods + { + public const Int32 MONITOR_DEFAULT_TO_PRIMARY = 0x00000001; + public const Int32 MONITOR_DEFAULT_TO_NEAREST = 0x00000002; + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GetConsoleWindow(); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool GetWindowRect(IntPtr hWnd, out NativeRectangle rc); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint); + + [DllImport("user32.dll")] + public static extern IntPtr MonitorFromWindow(IntPtr handle, Int32 flags); + + [DllImport("user32.dll")] + public static extern Boolean GetMonitorInfo(IntPtr hMonitor, NativeMonitorInfo lpmi); + + [Serializable, StructLayout(LayoutKind.Sequential)] + public struct NativeRectangle + { + public Int32 Left; + public Int32 Top; + public Int32 Right; + public Int32 Bottom; + + public NativeRectangle(Int32 left, Int32 top, Int32 right, Int32 bottom) + { + this.Left = left; + this.Top = top; + this.Right = right; + this.Bottom = bottom; + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public sealed class NativeMonitorInfo + { + public Int32 Size = Marshal.SizeOf(typeof(NativeMonitorInfo)); + public NativeRectangle Monitor; + public NativeRectangle Work; + public Int32 Flags; + } + } +} diff --git a/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj b/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj index ed873e8..67ed383 100644 --- a/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj +++ b/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj @@ -12,7 +12,7 @@ - + diff --git a/Source/Picton.Messaging.IntegrationTests/Program.cs b/Source/Picton.Messaging.IntegrationTests/Program.cs index d895e42..3b3be8d 100644 --- a/Source/Picton.Messaging.IntegrationTests/Program.cs +++ b/Source/Picton.Messaging.IntegrationTests/Program.cs @@ -1,7 +1,7 @@ -using App.Metrics; +using App.Metrics; using App.Metrics.Scheduling; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Queue; +using Microsoft.Azure.Storage; +using Microsoft.Azure.Storage.Queue; using Picton.Managers; using Picton.Messaging.IntegrationTests.Datadog; using Picton.Messaging.Logging; @@ -26,51 +26,54 @@ static void Main() var logger = logProvider.GetLogger("Main"); LogProvider.SetCurrentLogProvider(logProvider); - // Ensure the Console is tall enough + // Ensure the Console is tall enough and centered on the screen Console.WindowHeight = Math.Min(60, Console.LargestWindowHeight); + ConsoleUtils.CenterConsole(); - // Configure where metrics are published to + // Configure where metrics are published to. By default, don't publish metrics + var metrics = (IMetricsRoot)null; + + // In this example, I'm publishing metrics to a DataDog account var datadogApiKey = Environment.GetEnvironmentVariable("DATADOG_APIKEY"); - var metrics = new MetricsBuilder() - .Report.OverHttp(o => - { - o.HttpSettings.RequestUri = new Uri($"https://app.datadoghq.com/api/v1/series?api_key={datadogApiKey}"); - o.MetricsOutputFormatter = new DatadogFormatter(new DatadogFormatterOptions { Hostname = Environment.MachineName }); - o.FlushInterval = TimeSpan.FromSeconds(2); - }) - .Build(); - - // Send metrics to Datadog - var sendMetricsJob = new AppMetricsTaskScheduler( - TimeSpan.FromSeconds(2), - async () => - { - await Task.WhenAll(metrics.ReportRunner.RunAllAsync()); - }); - sendMetricsJob.Start(); + if (!string.IsNullOrEmpty(datadogApiKey)) + { + metrics = new MetricsBuilder() + .Report.OverHttp(o => + { + o.HttpSettings.RequestUri = new Uri($"https://app.datadoghq.com/api/v1/series?api_key={datadogApiKey}"); + o.MetricsOutputFormatter = new DatadogFormatter(new DatadogFormatterOptions { Hostname = Environment.MachineName }); + o.FlushInterval = TimeSpan.FromSeconds(2); + }) + .Build(); + + // Send metrics to Datadog + var sendMetricsJob = new AppMetricsTaskScheduler( + TimeSpan.FromSeconds(2), + async () => + { + await Task.WhenAll(metrics.ReportRunner.RunAllAsync()); + }); + sendMetricsJob.Start(); + } // Setup the message queue in Azure storage emulator var storageAccount = CloudStorageAccount.DevelopmentStorageAccount; var queueName = "myqueue"; + var numberOfMessages = 25; logger(Logging.LogLevel.Info, () => "Begin integration tests..."); - var numberOfMessages = 25; - - logger(Logging.LogLevel.Info, () => $"Adding {numberOfMessages} string messages to the queue..."); - AddStringMessagesToQueue(numberOfMessages, queueName, storageAccount, logProvider).Wait(); - logger(Logging.LogLevel.Info, () => "Processing the messages in the queue..."); - ProcessSimpleMessages(queueName, storageAccount, logProvider, metrics); + var stringMessagesLogger = logProvider.GetLogger("StringMessages"); + AddStringMessagesToQueue(numberOfMessages, queueName, storageAccount, stringMessagesLogger).Wait(); + ProcessSimpleMessages(queueName, storageAccount, stringMessagesLogger, metrics); - logger(Logging.LogLevel.Info, () => $"Adding {numberOfMessages} simple messages to the queue..."); - AddSimpleMessagesToQueue(numberOfMessages, queueName, storageAccount, logProvider).Wait(); - logger(Logging.LogLevel.Info, () => "Processing the messages in the queue..."); - ProcessSimpleMessages(queueName, storageAccount, logProvider, metrics); + var simpleMessagesLogger = logProvider.GetLogger("SimpleMessages"); + AddSimpleMessagesToQueue(numberOfMessages, queueName, storageAccount, simpleMessagesLogger).Wait(); + ProcessSimpleMessages(queueName, storageAccount, simpleMessagesLogger, metrics); - logger(Logging.LogLevel.Info, () => $"Adding {numberOfMessages} messages with handlers to the queue..."); - AddMessagesWithHandlerToQueue(numberOfMessages, queueName, storageAccount, logProvider).Wait(); - logger(Logging.LogLevel.Info, () => "Processing the messages in the queue..."); - ProcessMessagesWithHandlers(queueName, storageAccount, logProvider, metrics); + var messagesWithHandlerLogger = logProvider.GetLogger("MessagesWithHandler"); + AddMessagesWithHandlerToQueue(numberOfMessages, queueName, storageAccount, messagesWithHandlerLogger).Wait(); + ProcessMessagesWithHandlers(queueName, storageAccount, messagesWithHandlerLogger, metrics); // Flush the console key buffer while (Console.KeyAvailable) Console.ReadKey(true); @@ -80,8 +83,10 @@ static void Main() Console.ReadKey(); } - public static async Task AddStringMessagesToQueue(int numberOfMessages, string queueName, CloudStorageAccount storageAccount, ILogProvider logProvider) + public static async Task AddStringMessagesToQueue(int numberOfMessages, string queueName, CloudStorageAccount storageAccount, Logger logger) { + logger(Logging.LogLevel.Info, () => $"Adding {numberOfMessages} string messages to the queue..."); + var cloudQueueClient = storageAccount.CreateCloudQueueClient(); var cloudQueue = cloudQueueClient.GetQueueReference(queueName); await cloudQueue.CreateIfNotExistsAsync().ConfigureAwait(false); @@ -92,8 +97,10 @@ public static async Task AddStringMessagesToQueue(int numberOfMessages, string q } } - public static async Task AddSimpleMessagesToQueue(int numberOfMessages, string queueName, CloudStorageAccount storageAccount, ILogProvider logProvider) + public static async Task AddSimpleMessagesToQueue(int numberOfMessages, string queueName, CloudStorageAccount storageAccount, Logger logger) { + logger(Logging.LogLevel.Info, () => $"Adding {numberOfMessages} simple messages to the queue..."); + var queueManager = new QueueManager(queueName, storageAccount); await queueManager.CreateIfNotExistsAsync().ConfigureAwait(false); await queueManager.ClearAsync().ConfigureAwait(false); @@ -103,9 +110,8 @@ public static async Task AddSimpleMessagesToQueue(int numberOfMessages, string q } } - public static void ProcessSimpleMessages(string queueName, CloudStorageAccount storageAccount, ILogProvider logProvider, IMetrics metrics) + public static void ProcessSimpleMessages(string queueName, CloudStorageAccount storageAccount, Logger logger, IMetrics metrics) { - var logger = logProvider.GetLogger("ProcessSimpleMessages"); Stopwatch sw = null; // Configure the message pump @@ -138,8 +144,10 @@ public static void ProcessSimpleMessages(string queueName, CloudStorageAccount s logger(Logging.LogLevel.Info, () => $"\tDone in {sw.Elapsed.ToDurationString()}"); } - public static async Task AddMessagesWithHandlerToQueue(int numberOfMessages, string queueName, CloudStorageAccount storageAccount, ILogProvider logProvider) + public static async Task AddMessagesWithHandlerToQueue(int numberOfMessages, string queueName, CloudStorageAccount storageAccount, Logger logger) { + logger(Logging.LogLevel.Info, () => $"Adding {numberOfMessages} messages with handlers to the queue..."); + var queueManager = new QueueManager(queueName, storageAccount); await queueManager.CreateIfNotExistsAsync().ConfigureAwait(false); await queueManager.ClearAsync().ConfigureAwait(false); @@ -149,10 +157,8 @@ public static async Task AddMessagesWithHandlerToQueue(int numberOfMessages, str } } - public static void ProcessMessagesWithHandlers(string queueName, CloudStorageAccount storageAccount, ILogProvider logProvider, IMetrics metrics) + public static void ProcessMessagesWithHandlers(string queueName, CloudStorageAccount storageAccount, Logger logger, IMetrics metrics) { - var logger = logProvider.GetLogger("ProcessMessagesWithHandlers"); - Stopwatch sw = null; // Configure the message pump diff --git a/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs b/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs index 447c880..21ce1e4 100644 --- a/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs +++ b/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs @@ -1,13 +1,12 @@ -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Auth; -using Microsoft.WindowsAzure.Storage.Blob; -using Microsoft.WindowsAzure.Storage.Queue; +using Microsoft.Azure.Storage; +using Microsoft.Azure.Storage.Auth; +using Microsoft.Azure.Storage.Blob; +using Microsoft.Azure.Storage.Queue; using Moq; using Shouldly; using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj b/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj index 3b18450..45cf163 100644 --- a/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj +++ b/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj @@ -1,13 +1,13 @@ - + - net452;netcoreapp2.0 + net461;net472;netcoreapp2.0 Picton.Messaging.UnitTests Picton.Messaging.UnitTests - + diff --git a/Source/Picton.Messaging.sln b/Source/Picton.Messaging.sln index 704ef72..e4b66d7 100644 --- a/Source/Picton.Messaging.sln +++ b/Source/Picton.Messaging.sln @@ -1,12 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28803.452 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{53D250B0-3F11-4CD4-AF30-5F636B405D87}" ProjectSection(SolutionItems) = preProject - ..\appveyor.yml = ..\appveyor.yml - ..\build.cake = ..\build.cake ..\README.md = ..\README.md EndProjectSection EndProject diff --git a/Source/Picton.Messaging/AsyncMessagePump.cs b/Source/Picton.Messaging/AsyncMessagePump.cs index a73b761..378aac0 100644 --- a/Source/Picton.Messaging/AsyncMessagePump.cs +++ b/Source/Picton.Messaging/AsyncMessagePump.cs @@ -1,7 +1,7 @@ -using App.Metrics; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Blob; -using Microsoft.WindowsAzure.Storage.Queue; +using App.Metrics; +using Microsoft.Azure.Storage; +using Microsoft.Azure.Storage.Blob; +using Microsoft.Azure.Storage.Queue; using Picton.Interfaces; using Picton.Managers; using Picton.Messaging.Logging; @@ -202,7 +202,7 @@ private void InitMessagePump() OnError = (message, exception, isPoison) => _logger.ErrorException("An error occured when processing a message", exception); } - private async Task ProcessMessages(TimeSpan? visibilityTimeout = null, CancellationToken cancellationToken = default(CancellationToken)) + private async Task ProcessMessages(TimeSpan? visibilityTimeout = null, CancellationToken cancellationToken = default) { var runningTasks = new ConcurrentDictionary(); var semaphore = new SemaphoreSlim(_concurrentTasks, _concurrentTasks); diff --git a/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs b/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs index 8517494..8b7e7e1 100644 --- a/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs +++ b/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs @@ -1,6 +1,6 @@ -using App.Metrics; +using App.Metrics; +using Microsoft.Azure.Storage; using Microsoft.Extensions.DependencyModel; -using Microsoft.WindowsAzure.Storage; using Picton.Messaging.Logging; using Picton.Messaging.Messages; using System; @@ -125,8 +125,15 @@ public void Stop() private static IDictionary GetMessageHandlers() { + _logger.Trace("Discovering message handlers."); + var assemblies = GetLocalAssemblies(); + var assembliesCount = assemblies.Length; + if (assembliesCount == 0) _logger.Trace($"Did not find any local assembly."); + else if (assembliesCount == 1) _logger.Trace("Found 1 local assembly."); + else _logger.Trace($"Found {assemblies.Count()} local assemblies."); + var typesWithMessageHandlerInterfaces = assemblies .SelectMany(x => x.GetTypes()) .Where(t => !GetTypeInfo(t).IsInterface) @@ -142,17 +149,25 @@ private static IDictionary GetMessageHandlers() .Where(t => t.MessageTypes != null && t.MessageTypes.Any()) .ToArray(); + var classesCount = typesWithMessageHandlerInterfaces.Length; + if (classesCount == 0) _logger.Trace($"Did not find any class implementing the 'IMessageHandler' interface."); + else if (classesCount == 1) _logger.Trace("Found 1 class implementing the 'IMessageHandler' interface."); + else _logger.Trace($"Found {typesWithMessageHandlerInterfaces.Count()} classes implementing the 'IMessageHandler' interface."); + var oneTypePerMessageHandler = typesWithMessageHandlerInterfaces .SelectMany(t => t.MessageTypes, (t, messageType) => new { t.Type, MessageType = messageType - }).ToArray(); + }) + .ToArray(); var messageHandlers = oneTypePerMessageHandler .GroupBy(h => h.MessageType) - .ToDictionary(group => group.Key, group => group.Select(t => t.Type).ToArray()); + .ToDictionary(group => group.Key, group => group.Select(t => t.Type) + .ToArray()); + return messageHandlers; } @@ -162,13 +177,14 @@ private static Type GetTypeInfo(Type type) return type; } - private static IEnumerable GetLocalAssemblies() + private static Assembly[] GetLocalAssemblies() { var callingAssembly = Assembly.GetCallingAssembly(); var path = new Uri(System.IO.Path.GetDirectoryName(callingAssembly.Location)).AbsolutePath; return AppDomain.CurrentDomain.GetAssemblies() - .Where(x => !x.IsDynamic && new Uri(x.CodeBase).AbsolutePath.Contains(path)).ToList(); + .Where(x => !x.IsDynamic && new Uri(x.CodeBase).AbsolutePath.Contains(path)) + .ToArray(); } #else private static TypeInfo GetTypeInfo(Type type) @@ -176,7 +192,7 @@ private static TypeInfo GetTypeInfo(Type type) return type.GetTypeInfo(); } - private static IEnumerable GetLocalAssemblies() + private static Assembly[] GetLocalAssemblies() { var dependencies = DependencyContext.Default.RuntimeLibraries; @@ -190,7 +206,7 @@ private static IEnumerable GetLocalAssemblies() } } - return assemblies; + return assemblies.ToArray(); } private static bool IsCandidateLibrary(RuntimeLibrary library) diff --git a/Source/Picton.Messaging/Picton.Messaging.csproj b/Source/Picton.Messaging/Picton.Messaging.csproj index 01f6258..f16a8fb 100644 --- a/Source/Picton.Messaging/Picton.Messaging.csproj +++ b/Source/Picton.Messaging/Picton.Messaging.csproj @@ -1,12 +1,18 @@ - + - net452;netstandard2.0 + net461;net472;netstandard2.0 anycpu true Library $(SemVer) - full + portable + + + + true + true + snupkg @@ -17,7 +23,7 @@ High performance message processor (also known as a message "pump") designed to process messages from an Azure storage queue as efficiently as possible Copyright © Jeremie Desautels 2016 - Present - http://jericho.mit-license.org + MIT https://github.com/Jericho/Picton.Messaging https://github.com/identicons/jericho.png false @@ -26,33 +32,32 @@ - + all runtime; build; native; contentfiles; analyzers - - - All - + + + - - + + - + - - $(DefineConstants);NETFULL;LIBLOG_EXCLUDE_CODE_COVERAGE + + $(DefineConstants);NETFULL - - $(DefineConstants);NETSTANDARD;LIBLOG_EXCLUDE_CODE_COVERAGE + + $(DefineConstants);NETSTANDARD diff --git a/appveyor.yml b/appveyor.yml index 7ce10ec..82c4365 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ init: # Build script build_script: - - ps: .\build.ps1 -Target "AppVeyor" + - ps: .\build.ps1 -Target AppVeyor # Tests test: off diff --git a/build.cake b/build.cake index cb47f5c..c488278 100644 --- a/build.cake +++ b/build.cake @@ -2,10 +2,10 @@ #addin nuget:?package=Cake.Coveralls&version=0.9.0 // Install tools. -#tool nuget:?package=GitVersion.CommandLine&version=4.0.1-beta1-58 +#tool nuget:?package=GitVersion.CommandLine&version=5.0.0-beta2-97 #tool nuget:?package=GitReleaseManager&version=0.8.0 -#tool nuget:?package=OpenCover&version=4.7.870-rc -#tool nuget:?package=ReportGenerator&version=4.0.5 +#tool nuget:?package=OpenCover&version=4.7.922 +#tool nuget:?package=ReportGenerator&version=4.1.5 #tool nuget:?package=coveralls.io&version=1.4.2 #tool nuget:?package=xunit.runner.console&version=2.4.1 @@ -229,13 +229,14 @@ Task("Create-NuGet-Package") { Configuration = configuration, IncludeSource = false, - IncludeSymbols = false, + IncludeSymbols = true, NoBuild = true, NoDependencies = true, OutputDirectory = outputDir, ArgumentCustomization = (args) => { return args + .Append("/p:SymbolPackageFormat=snupkg") .Append("/p:Version={0}", versionInfo.LegacySemVerPadded) .Append("/p:AssemblyVersion={0}", versionInfo.MajorMinorPatch) .Append("/p:FileVersion={0}", versionInfo.MajorMinorPatch) diff --git a/build.ps1 b/build.ps1 index c6c91b2..7f1f813 100644 --- a/build.ps1 +++ b/build.ps1 @@ -59,7 +59,10 @@ try { # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is # installed (.NET 4.5 is an in-place upgrade). - [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + # PowerShell Core already has support for TLS 1.2 so we can skip this if running in that. + if (-not $IsCoreCLR) { + [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + } } catch { Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' } @@ -118,7 +121,7 @@ $MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" # Make sure tools folder exists if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { Write-Verbose -Message "Creating tools directory..." - New-Item -Path $TOOLS_DIR -Type directory | out-null + New-Item -Path $TOOLS_DIR -Type Directory | Out-Null } # Make sure that packages.config exist. @@ -155,7 +158,12 @@ if (!(Test-Path $NUGET_EXE)) { } # Save nuget.exe path to environment to be available to child processed -$ENV:NUGET_EXE = $NUGET_EXE +$env:NUGET_EXE = $NUGET_EXE +$env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { + "mono `"$NUGET_EXE`"" +} else { + "`"$NUGET_EXE`"" +} # Restore tools from NuGet? if(-Not $SkipToolPackageRestore.IsPresent) { @@ -163,16 +171,17 @@ if(-Not $SkipToolPackageRestore.IsPresent) { Set-Location $TOOLS_DIR # Check for changes in packages.config and remove installed tools if true. - [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) + [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or - ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { + ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { Write-Verbose -Message "Missing or changed package.config hash..." Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | Remove-Item -Recurse } Write-Verbose -Message "Restoring tools from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" if ($LASTEXITCODE -ne 0) { Throw "An error occurred while restoring NuGet tools." @@ -181,7 +190,7 @@ if(-Not $SkipToolPackageRestore.IsPresent) { { $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" } - Write-Verbose -Message ($NuGetOutput | out-string) + Write-Verbose -Message ($NuGetOutput | Out-String) Pop-Location } @@ -192,13 +201,13 @@ if (Test-Path $ADDINS_PACKAGES_CONFIG) { Set-Location $ADDINS_DIR Write-Verbose -Message "Restoring addins from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" if ($LASTEXITCODE -ne 0) { Throw "An error occurred while restoring NuGet addins." } - Write-Verbose -Message ($NuGetOutput | out-string) + Write-Verbose -Message ($NuGetOutput | Out-String) Pop-Location } @@ -209,13 +218,13 @@ if (Test-Path $MODULES_PACKAGES_CONFIG) { Set-Location $MODULES_DIR Write-Verbose -Message "Restoring modules from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" if ($LASTEXITCODE -ne 0) { Throw "An error occurred while restoring NuGet modules." } - Write-Verbose -Message ($NuGetOutput | out-string) + Write-Verbose -Message ($NuGetOutput | Out-String) Pop-Location } @@ -225,6 +234,11 @@ if (!(Test-Path $CAKE_EXE)) { Throw "Could not find Cake.exe at $CAKE_EXE" } +$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { + "mono `"$CAKE_EXE`"" +} else { + "`"$CAKE_EXE`"" +} # Build Cake arguments @@ -238,5 +252,5 @@ $cakeArguments += $ScriptArgs # Start Cake Write-Host "Running build script..." -&$CAKE_EXE $cakeArguments +Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")" exit $LASTEXITCODE