Skip to content

Commit 7c440a3

Browse files
authored
Merge pull request #4 from Particular/develop
Release 1.0
2 parents 3aee04d + cc96620 commit 7c440a3

13 files changed

+436
-0
lines changed

GitVersion.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
assembly-versioning-scheme: Major
2+
next-version: 1.0.0
3+
commit-message-incrementing: Disabled
4+
branches:
5+
develop:
6+
tag: alpha
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
3+
<metadata xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
4+
<id>NServiceBus.Metrics.ServiceControl.Msmq</id>
5+
<title>ServiceControl Monitoring Msmq plugin</title>
6+
<version>$version$</version>
7+
<authors>$authors$</authors>
8+
<owners>$owners$</owners>
9+
<licenseUrl>$licenseUrl$</licenseUrl>
10+
<projectUrl>$projectUrl$</projectUrl>
11+
<iconUrl>$iconUrl$</iconUrl>
12+
<description>Plugin for native MSMQ queue length data to ServiceControl Monitoring instance</description>
13+
<copyright>$copyright$</copyright>
14+
<tags>$tags$</tags>
15+
<dependencies>
16+
<dependency id="NServiceBus.Metrics.ServiceControl" version="[1.3.0-rc0002,2.0.0)" />
17+
<dependency id="NServiceBus" version="[5.2,6)" />
18+
</dependencies>
19+
</metadata>
20+
<files>
21+
<file src="..\..\binaries\net452\NServiceBus.Metrics.ServiceControl.Msmq.???" target="lib\net452" />
22+
</files>
23+
</package>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Runtime.CompilerServices;
6+
using ApprovalTests;
7+
using ApprovalTests.Core;
8+
using NUnit.Framework;
9+
using PublicApiGenerator;
10+
11+
[TestFixture]
12+
public class APIApprovals
13+
{
14+
[Test]
15+
[MethodImpl(MethodImplOptions.NoInlining)]
16+
public void Approve()
17+
{
18+
var combine = Path.Combine(TestContext.CurrentContext.TestDirectory, "NServiceBus.Metrics.ServiceControl.Msmq.dll");
19+
var assembly = Assembly.LoadFile(combine);
20+
var publicApi = Filter(ApiGenerator.GeneratePublicApi(assembly));
21+
Approvals.Verify(BuildWriter(publicApi));
22+
}
23+
24+
static IApprovalWriter BuildWriter(string api,[CallerFilePath] string path = null)
25+
{
26+
var directory = Path.GetDirectoryName(path);
27+
return new LocalApprovalTextWriter(api, "cs", directory);
28+
}
29+
30+
string Filter(string text)
31+
{
32+
return string.Join(Environment.NewLine, text.Split(new[]
33+
{
34+
Environment.NewLine
35+
}, StringSplitOptions.RemoveEmptyEntries)
36+
.Where(l => !l.StartsWith("[assembly: System.Runtime.Versioning"))
37+
.Where(l => !string.IsNullOrWhiteSpace(l))
38+
);
39+
}
40+
41+
class LocalApprovalTextWriter : ApprovalTextWriter
42+
{
43+
readonly string directory;
44+
45+
public LocalApprovalTextWriter(string data, string extensionWithoutDot, string directory)
46+
: base(data, extensionWithoutDot)
47+
{
48+
this.directory = directory;
49+
}
50+
51+
public override string GetApprovalFilename(string basename) => Path.Combine(directory, base.GetApprovalFilename(basename));
52+
public override string GetReceivedFilename(string basename) => Path.Combine(directory, base.GetReceivedFilename(basename));
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using ApprovalTests.Reporters;
2+
3+
[assembly: UseReporter(typeof(DiffReporter), typeof(AllFailingTestsClipboardReporter))]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net452</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Compile Remove="APIApprovals.Approve.approved.cs" />
9+
<Compile Remove="APIApprovals.Approve.received.cs" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="NUnit" Version="3.8.1" />
14+
<PackageReference Include="ApprovalTests" Version="3.*" />
15+
<PackageReference Include="ApprovalUtilities" Version="3.*" />
16+
<PackageReference Include="PublicApiGenerator" Version="6.1.0-*" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\NServiceBus.Metrics.ServiceControl.Msmq\NServiceBus.Metrics.ServiceControl.Msmq.csproj" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<Reference Include="Microsoft.CSharp">
25+
<HintPath>..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Microsoft.CSharp.dll</HintPath>
26+
</Reference>
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
31+
</ItemGroup>
32+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.26730.16
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NServiceBus.Metrics.ServiceControl.Msmq", "NServiceBus.Metrics.ServiceControl.Msmq\NServiceBus.Metrics.ServiceControl.Msmq.csproj", "{17FAA608-F5E1-4A48-BFEE-C2071BDB0238}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NServiceBus.Metrics.ServiceControl.Msmq.Tests", "NServiceBus.Metrics.ServiceControl..Msmq.Tests\NServiceBus.Metrics.ServiceControl.Msmq.Tests.csproj", "{09E52424-06AF-4948-92D8-560491309181}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleEndpoint", "SampleEndpoint\SampleEndpoint.csproj", "{76FADEEE-3788-4F59-902A-A970167CE11F}"
11+
EndProject
12+
Global
13+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
14+
Debug|Any CPU = Debug|Any CPU
15+
Release|Any CPU = Release|Any CPU
16+
EndGlobalSection
17+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
18+
{17FAA608-F5E1-4A48-BFEE-C2071BDB0238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19+
{17FAA608-F5E1-4A48-BFEE-C2071BDB0238}.Debug|Any CPU.Build.0 = Debug|Any CPU
20+
{17FAA608-F5E1-4A48-BFEE-C2071BDB0238}.Release|Any CPU.ActiveCfg = Release|Any CPU
21+
{17FAA608-F5E1-4A48-BFEE-C2071BDB0238}.Release|Any CPU.Build.0 = Release|Any CPU
22+
{09E52424-06AF-4948-92D8-560491309181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23+
{09E52424-06AF-4948-92D8-560491309181}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{09E52424-06AF-4948-92D8-560491309181}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{09E52424-06AF-4948-92D8-560491309181}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{76FADEEE-3788-4F59-902A-A970167CE11F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{76FADEEE-3788-4F59-902A-A970167CE11F}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{76FADEEE-3788-4F59-902A-A970167CE11F}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{76FADEEE-3788-4F59-902A-A970167CE11F}.Release|Any CPU.Build.0 = Release|Any CPU
30+
EndGlobalSection
31+
GlobalSection(SolutionProperties) = preSolution
32+
HideSolutionNode = FALSE
33+
EndGlobalSection
34+
GlobalSection(ExtensibilityGlobals) = postSolution
35+
SolutionGuid = {936FE755-95F0-481E-8D52-C1BB5F0A8E0F}
36+
EndGlobalSection
37+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System;
2+
using System.Messaging;
3+
using System.Runtime.InteropServices;
4+
5+
static class MsmqExtensions
6+
{
7+
/// <remarks>
8+
/// Source: http://functionalflow.co.uk/blog/2008/08/27/counting-the-number-of-messages-in-a-message-queue-in/
9+
/// </remarks>
10+
public static int GetCount(this MessageQueue self)
11+
{
12+
var props = new Win32.MQMGMTPROPS {cProp = 1};
13+
try
14+
{
15+
props.aPropID = Marshal.AllocHGlobal(sizeof(int));
16+
Marshal.WriteInt32(props.aPropID, Win32.PROPID_MGMT_QUEUE_MESSAGE_COUNT);
17+
18+
props.aPropVar = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.MQPROPVariant)));
19+
Marshal.StructureToPtr(new Win32.MQPROPVariant {vt = Win32.VT_NULL}, props.aPropVar, false);
20+
21+
props.status = Marshal.AllocHGlobal(sizeof(int));
22+
Marshal.WriteInt32(props.status, 0);
23+
24+
var result = Win32.MQMgmtGetInfo(null, "queue=" + self.FormatName, ref props);
25+
if (result != 0)
26+
{
27+
throw new InvalidOperationException($"Unable to retrieve queue information (error: {result:x8}");
28+
}
29+
30+
if (Marshal.ReadInt32(props.status) != 0)
31+
{
32+
return -1;
33+
}
34+
35+
var propVar = (Win32.MQPROPVariant)Marshal.PtrToStructure(props.aPropVar, typeof(Win32.MQPROPVariant));
36+
37+
return propVar.vt != Win32.VT_UI4
38+
? 0
39+
: Convert.ToInt32(propVar.ulVal);
40+
}
41+
finally
42+
{
43+
Marshal.FreeHGlobal(props.aPropID);
44+
Marshal.FreeHGlobal(props.aPropVar);
45+
Marshal.FreeHGlobal(props.status);
46+
}
47+
}
48+
49+
static class Win32
50+
{
51+
[DllImport("mqrt.dll")]
52+
internal static extern uint MQMgmtGetInfo([MarshalAs(UnmanagedType.BStr)] string computerName, [MarshalAs(UnmanagedType.BStr)] string objectName, ref MQMGMTPROPS mgmtProps);
53+
54+
public const byte VT_NULL = 1;
55+
public const byte VT_UI4 = 19;
56+
public const int PROPID_MGMT_QUEUE_MESSAGE_COUNT = 7;
57+
58+
//size must be 16
59+
[StructLayout(LayoutKind.Sequential)]
60+
internal struct MQPROPVariant
61+
{
62+
public byte vt; //0
63+
public byte spacer; //1
64+
public short spacer2; //2
65+
public int spacer3; //4
66+
public uint ulVal; //8
67+
public int spacer4; //12
68+
}
69+
70+
//size must be 16 in x86 and 28 in x64
71+
[StructLayout(LayoutKind.Sequential)]
72+
internal struct MQMGMTPROPS
73+
{
74+
public uint cProp;
75+
public IntPtr aPropID;
76+
public IntPtr aPropVar;
77+
public IntPtr status;
78+
}
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Messaging;
2+
using NServiceBus;
3+
using NServiceBus.Metrics.ServiceControl;
4+
5+
class MsmqNativeQueueLengthReporter
6+
{
7+
IReportNativeQueueLength nativeQueueLengthReporter;
8+
readonly Configure configure;
9+
string localAddress;
10+
MessageQueue messageQueue;
11+
12+
public MsmqNativeQueueLengthReporter(IReportNativeQueueLength nativeQueueLengthReporter, Configure configure)
13+
{
14+
this.nativeQueueLengthReporter = nativeQueueLengthReporter;
15+
this.configure = configure;
16+
}
17+
18+
public void Warmup()
19+
{
20+
localAddress = configure.LocalAddress.ToString();
21+
messageQueue = new MessageQueue($@".\private$\{configure.LocalAddress.Queue}", QueueAccessMode.Peek);
22+
}
23+
24+
public void ReportNativeQueueLength()
25+
{
26+
nativeQueueLengthReporter.ReportQueueLength(localAddress, messageQueue.GetCount());
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net452</TargetFramework>
5+
<OutputPath>..\..\binaries\</OutputPath>
6+
<DocumentationFile>..\..\binaries\net452\NServiceBus.Metrics.ServiceControl.Msmq.xml</DocumentationFile>
7+
<LangVersion>7</LangVersion>
8+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
9+
<DebugType>full</DebugType>
10+
<DebugSymbols>true</DebugSymbols>
11+
</PropertyGroup>
12+
<ItemGroup>
13+
<PackageReference Include="NServiceBus.Metrics.ServiceControl" Version="1.3.0-rc0002" />
14+
<PackageReference Include="NServiceBus" Version="5.0.0" />
15+
<PackageReference Include="GitVersionTask" Version="3.6.5" />
16+
<PackageReference Include="NuGetPackager" Version="0.6.5" />
17+
</ItemGroup>
18+
<ItemGroup>
19+
<Reference Include="System.Messaging">
20+
<HintPath>..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Messaging.dll</HintPath>
21+
</Reference>
22+
</ItemGroup>
23+
24+
25+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using NServiceBus;
5+
using NServiceBus.Features;
6+
using NServiceBus.Logging;
7+
using NServiceBus.Transports;
8+
9+
class ReportMsmqNativeQueueLength : Feature
10+
{
11+
public ReportMsmqNativeQueueLength()
12+
{
13+
EnableByDefault();
14+
DependsOn("ServiceControlMonitoring");
15+
Prerequisite(ctx => ctx.Settings.Get<TransportDefinition>() is MsmqTransport, "MSMQ Transport not configured");
16+
}
17+
18+
protected override void Setup(FeatureConfigurationContext context)
19+
{
20+
context.Container.ConfigureComponent<MsmqNativeQueueLengthReporter>(DependencyLifecycle.SingleInstance);
21+
context.Container.ConfigureComponent<PeriodicallyReportQueueLength>(DependencyLifecycle.SingleInstance);
22+
23+
RegisterStartupTask<PeriodicallyReportQueueLength>();
24+
}
25+
26+
class PeriodicallyReportQueueLength : FeatureStartupTask
27+
{
28+
TimeSpan delayBetweenReports = TimeSpan.FromSeconds(1);
29+
CancellationTokenSource cancellationTokenSource;
30+
Task task;
31+
MsmqNativeQueueLengthReporter reporter;
32+
33+
public PeriodicallyReportQueueLength(MsmqNativeQueueLengthReporter reporter)
34+
{
35+
this.reporter = reporter;
36+
}
37+
38+
protected override void OnStart()
39+
{
40+
cancellationTokenSource = new CancellationTokenSource();
41+
task = Task.Run(async () =>
42+
{
43+
reporter.Warmup();
44+
while (!cancellationTokenSource.IsCancellationRequested)
45+
{
46+
try
47+
{
48+
await Task.Delay(delayBetweenReports, cancellationTokenSource.Token).ConfigureAwait(false);
49+
reporter.ReportNativeQueueLength();
50+
}
51+
catch (TaskCanceledException)
52+
{
53+
// Ignore cancellation. It means we are shutting down
54+
}
55+
catch (Exception ex)
56+
{
57+
Log.Warn("Error reporting MSMQ native queue length", ex);
58+
}
59+
}
60+
});
61+
}
62+
63+
protected override void OnStop()
64+
{
65+
cancellationTokenSource.Cancel();
66+
task.GetAwaiter().GetResult();
67+
}
68+
69+
static ILog Log = LogManager.GetLogger<PeriodicallyReportQueueLength>();
70+
}
71+
}

0 commit comments

Comments
 (0)