Skip to content

Commit ce833c1

Browse files
authored
Extend TypeLoader (#1069)
* Extend PluginLoader * rename
1 parent 2364866 commit ce833c1

File tree

6 files changed

+156
-95
lines changed

6 files changed

+156
-95
lines changed

WireMock.Net Solution.sln.DotSettings

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WWW/@EntryIndexedValue">WWW</s:String>
2424
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XMS/@EntryIndexedValue">XMS</s:String>
2525
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XUA/@EntryIndexedValue">XUA</s:String>
26+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dlls/@EntryIndexedValue">True</s:Boolean>
2627
<s:Boolean x:Key="/Default/UserDictionary/Words/=Flurl/@EntryIndexedValue">True</s:Boolean>
2728
<s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean>
2829
<s:Boolean x:Key="/Default/UserDictionary/Words/=Grpc/@EntryIndexedValue">True</s:Boolean>

src/WireMock.Net/Plugin/PluginLoader.cs

-56
This file was deleted.

src/WireMock.Net/Serialization/MatcherMapper.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using WireMock.Extensions;
99
using WireMock.Matchers;
1010
using WireMock.Models;
11-
using WireMock.Plugin;
1211
using WireMock.Settings;
1312
using WireMock.Util;
1413

@@ -53,7 +52,7 @@ public MatcherMapper(WireMockServerSettings settings)
5352
case "CSharpCodeMatcher":
5453
if (_settings.AllowCSharpCodeMatcher == true)
5554
{
56-
return PluginLoader.Load<ICSharpCodeMatcher>(matchBehaviour, matchOperator, stringPatterns);
55+
return TypeLoader.Load<ICSharpCodeMatcher>(matchBehaviour, matchOperator, stringPatterns);
5756
}
5857

5958
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because WireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");

src/WireMock.Net/Util/TypeLoader.cs

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Reflection;
7+
using Stef.Validation;
8+
9+
namespace WireMock.Util;
10+
11+
internal static class TypeLoader
12+
{
13+
private static readonly ConcurrentDictionary<string, Type> Assemblies = new();
14+
15+
public static TInterface Load<TInterface>(params object[] args) where TInterface : class
16+
{
17+
var key = typeof(TInterface).FullName!;
18+
19+
var pluginType = Assemblies.GetOrAdd(key, _ =>
20+
{
21+
if (TryFindTypeInDlls<TInterface>(null, out var foundType))
22+
{
23+
return foundType;
24+
}
25+
26+
throw new DllNotFoundException($"No dll found which implements Interface '{key}'.");
27+
});
28+
29+
return (TInterface)Activator.CreateInstance(pluginType, args)!;
30+
}
31+
32+
public static TInterface LoadByFullName<TInterface>(string implementationTypeFullName, params object[] args) where TInterface : class
33+
{
34+
Guard.NotNullOrEmpty(implementationTypeFullName);
35+
36+
var @interface = typeof(TInterface).FullName;
37+
var key = $"{@interface}_{implementationTypeFullName}";
38+
39+
var pluginType = Assemblies.GetOrAdd(key, _ =>
40+
{
41+
if (TryFindTypeInDlls<TInterface>(implementationTypeFullName, out var foundType))
42+
{
43+
return foundType;
44+
}
45+
46+
throw new DllNotFoundException($"No dll found which implements Interface '{@interface}' and has FullName '{implementationTypeFullName}'.");
47+
});
48+
49+
return (TInterface)Activator.CreateInstance(pluginType, args)!;
50+
}
51+
52+
private static bool TryFindTypeInDlls<TInterface>(string? implementationTypeFullName, [NotNullWhen(true)] out Type? pluginType) where TInterface : class
53+
{
54+
foreach (var file in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll"))
55+
{
56+
try
57+
{
58+
var assembly = Assembly.Load(new AssemblyName
59+
{
60+
Name = Path.GetFileNameWithoutExtension(file)
61+
});
62+
63+
if (TryGetImplementationTypeByInterfaceAndOptionalFullName<TInterface>(assembly, implementationTypeFullName, out pluginType))
64+
{
65+
return true;
66+
}
67+
}
68+
catch
69+
{
70+
// no-op: just try next .dll
71+
}
72+
}
73+
74+
pluginType = null;
75+
return false;
76+
}
77+
78+
private static bool TryGetImplementationTypeByInterfaceAndOptionalFullName<T>(Assembly assembly, string? implementationTypeFullName, [NotNullWhen(true)] out Type? type)
79+
{
80+
type = assembly
81+
.GetTypes()
82+
.FirstOrDefault(t =>
83+
typeof(T).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface &&
84+
(implementationTypeFullName == null || string.Equals(t.FullName, implementationTypeFullName, StringComparison.OrdinalIgnoreCase))
85+
);
86+
87+
return type != null;
88+
}
89+
}

test/WireMock.Net.Tests/Plugin/PluginLoaderTests.cs

-37
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using AnyOfTypes;
3+
using FluentAssertions;
4+
using WireMock.Matchers;
5+
using WireMock.Models;
6+
using WireMock.Util;
7+
using Xunit;
8+
9+
namespace WireMock.Net.Tests.Util;
10+
11+
public class TypeLoaderTests
12+
{
13+
public interface IDummyInterfaceNoImplementation
14+
{
15+
}
16+
17+
public interface IDummyInterfaceWithImplementation
18+
{
19+
}
20+
21+
public class DummyClass : IDummyInterfaceWithImplementation
22+
{
23+
}
24+
25+
[Fact]
26+
public void Load_ByInterface()
27+
{
28+
// Act
29+
AnyOf<string, StringPattern> pattern = "x";
30+
var result = TypeLoader.Load<ICSharpCodeMatcher>(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, pattern);
31+
32+
// Assert
33+
result.Should().NotBeNull();
34+
}
35+
36+
[Fact]
37+
public void Load_ByInterfaceAndFullName()
38+
{
39+
// Act
40+
var result = TypeLoader.LoadByFullName<IDummyInterfaceWithImplementation>(typeof(DummyClass).FullName!);
41+
42+
// Assert
43+
result.Should().BeOfType<DummyClass>();
44+
}
45+
46+
[Fact]
47+
public void Load_ByInterface_ButNoImplementationFoundForInterface_ThrowsException()
48+
{
49+
// Act
50+
Action a = () => TypeLoader.Load<IDummyInterfaceNoImplementation>();
51+
52+
// Assert
53+
a.Should().Throw<DllNotFoundException>().WithMessage("No dll found which implements Interface 'WireMock.Net.Tests.Util.TypeLoaderTests+IDummyInterfaceNoImplementation'.");
54+
}
55+
56+
[Fact]
57+
public void Load_ByInterfaceAndFullName_ButNoImplementationFoundForInterface_ThrowsException()
58+
{
59+
// Act
60+
Action a = () => TypeLoader.LoadByFullName<IDummyInterfaceWithImplementation>("xyz");
61+
62+
// Assert
63+
a.Should().Throw<DllNotFoundException>().WithMessage("No dll found which implements Interface 'WireMock.Net.Tests.Util.TypeLoaderTests+IDummyInterfaceWithImplementation' and has FullName 'xyz'.");
64+
}
65+
}

0 commit comments

Comments
 (0)