Skip to content

Commit 371bfdc

Browse files
authored
TypeLoader: implement Try methods (#1358)
* TypeLoader: implement Try methods * fix
1 parent 5c5e104 commit 371bfdc

File tree

10 files changed

+191
-82
lines changed

10 files changed

+191
-82
lines changed

src/WireMock.Net.MimePart/Util/MimeKitUtils.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright © WireMock.Net
22

33
using System;
4-
using System.Collections.Generic;
54
using System.Diagnostics.CodeAnalysis;
65
using System.IO;
76
using System.Linq;
@@ -33,16 +32,25 @@ public bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)
3332
StartsWithMultiPart(contentTypeHeader)
3433
)
3534
{
36-
var bytes = requestMessage.BodyData?.DetectedBodyType switch
35+
byte[] bytes;
36+
37+
switch (requestMessage.BodyData?.DetectedBodyType)
3738
{
3839
// If the body is bytes, use the BodyAsBytes to match on.
39-
BodyType.Bytes => requestMessage.BodyData.BodyAsBytes!,
40+
case BodyType.Bytes:
41+
bytes = requestMessage.BodyData.BodyAsBytes!;
42+
break;
4043

4144
// If the body is a String or MultiPart, use the BodyAsString to match on.
42-
BodyType.String or BodyType.MultiPart => Encoding.UTF8.GetBytes(requestMessage.BodyData.BodyAsString!),
43-
44-
_ => throw new NotSupportedException()
45-
};
45+
case BodyType.String or BodyType.MultiPart:
46+
bytes = Encoding.UTF8.GetBytes(requestMessage.BodyData.BodyAsString!);
47+
break;
48+
49+
// Else return false.
50+
default:
51+
mimeMessageData = null;
52+
return false;
53+
}
4654

4755
var fixedBytes = FixBytes(bytes, contentTypeHeader[0]);
4856

src/WireMock.Net.Minimal/Matchers/Request/RequestMessageMultiPartMatcher.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace WireMock.Matchers.Request;
1212
/// </summary>
1313
public class RequestMessageMultiPartMatcher : IRequestMatcher
1414
{
15-
private static readonly IMimeKitUtils MimeKitUtils = TypeLoader.LoadStaticInstance<IMimeKitUtils>();
15+
private readonly IMimeKitUtils _mimeKitUtils = LoadMimeKitUtils();
1616

1717
/// <summary>
1818
/// The matchers.
@@ -62,7 +62,7 @@ public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResu
6262
return requestMatchResult.AddScore(GetType(), score, null);
6363
}
6464

65-
if (!MimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
65+
if (!_mimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
6666
{
6767
return requestMatchResult.AddScore(GetType(), score, null);
6868
}
@@ -96,4 +96,14 @@ public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResu
9696

9797
return requestMatchResult.AddScore(GetType(), score, exception);
9898
}
99+
100+
private static IMimeKitUtils LoadMimeKitUtils()
101+
{
102+
if (TypeLoader.TryLoadStaticInstance<IMimeKitUtils>(out var mimeKitUtils))
103+
{
104+
return mimeKitUtils;
105+
}
106+
107+
throw new InvalidOperationException("MimeKit is required for RequestMessageMultiPartMatcher. Please install the WireMock.Net.MimePart package.");
108+
}
99109
}

src/WireMock.Net.Minimal/Owin/Mappers/OwinResponseMapper.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,12 @@ private bool IsFault(IResponseMessage responseMessage)
178178
return (bodyData.Encoding ?? _utf8NoBom).GetBytes(jsonBody);
179179

180180
case BodyType.ProtoBuf:
181-
var protoDefinitions = bodyData.ProtoDefinition?.Invoke().Texts;
182-
var protoBufUtils = TypeLoader.LoadStaticInstance<IProtoBufUtils>();
183-
return await protoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, bodyData.ProtoBufMessageType, bodyData.BodyAsJson).ConfigureAwait(false);
181+
if (TypeLoader.TryLoadStaticInstance<IProtoBufUtils>(out var protoBufUtils))
182+
{
183+
var protoDefinitions = bodyData.ProtoDefinition?.Invoke().Texts;
184+
return await protoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, bodyData.ProtoBufMessageType, bodyData.BodyAsJson).ConfigureAwait(false);
185+
}
186+
break;
184187

185188
case BodyType.Bytes:
186189
return bodyData.BodyAsBytes;

src/WireMock.Net.Minimal/RequestMessage.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,9 @@ internal RequestMessage(
183183
#endif
184184

185185
#if MIMEKIT
186-
try
186+
if (TypeLoader.TryLoadStaticInstance<IMimeKitUtils>(out var mimeKitUtils) && mimeKitUtils.TryGetMimeMessage(this, out var mimeMessage))
187187
{
188-
if (TypeLoader.LoadStaticInstance<IMimeKitUtils>().TryGetMimeMessage(this, out var mimeMessage))
189-
{
190-
BodyAsMimeMessage = mimeMessage;
191-
}
192-
}
193-
catch
194-
{
195-
// Ignore exception from MimeMessage.Load
188+
BodyAsMimeMessage = mimeMessage;
196189
}
197190
#endif
198191
}

src/WireMock.Net.Minimal/Serialization/MatcherMapper.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ public MatcherMapper(WireMockServerSettings settings)
5555
case "CSharpCodeMatcher":
5656
if (_settings.AllowCSharpCodeMatcher == true)
5757
{
58-
return TypeLoader.LoadNewInstance<ICSharpCodeMatcher>(matchBehaviour, matchOperator, stringPatterns);
58+
if (TypeLoader.TryLoadNewInstance<ICSharpCodeMatcher>(out var csharpCodeMatcher, matchBehaviour, matchOperator, stringPatterns))
59+
{
60+
return csharpCodeMatcher;
61+
}
62+
63+
throw new InvalidOperationException("The 'CSharpCodeMatcher' cannot be loaded. Please install the WireMock.Net.Matchers.CSharpCode package.");
5964
}
6065

6166
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because WireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");
@@ -75,7 +80,12 @@ public MatcherMapper(WireMockServerSettings settings)
7580
case "GraphQLMatcher":
7681
var patternAsString = stringPatterns[0].GetPattern();
7782
var schema = new AnyOf<string, StringPattern, ISchemaData>(patternAsString);
78-
return TypeLoader.LoadNewInstance<IGraphQLMatcher>(schema, matcherModel.CustomScalars, matchBehaviour, matchOperator);
83+
if (TypeLoader.TryLoadNewInstance<IGraphQLMatcher>(out var graphQLMatcher, schema, matcherModel.CustomScalars, matchBehaviour, matchOperator))
84+
{
85+
return graphQLMatcher;
86+
}
87+
88+
throw new InvalidOperationException("The 'GraphQLMatcher' cannot be loaded. Please install the WireMock.Net.GraphQL package.");
7989

8090
case "MimePartMatcher":
8191
return CreateMimePartMatcher(matchBehaviour, matcherModel);
@@ -282,18 +292,34 @@ private IMimePartMatcher CreateMimePartMatcher(MatchBehaviour matchBehaviour, Ma
282292
var contentTransferEncodingMatcher = Map(matcher.ContentTransferEncodingMatcher) as IStringMatcher;
283293
var contentMatcher = Map(matcher.ContentMatcher);
284294

285-
return TypeLoader.LoadNewInstance<IMimePartMatcher>(matchBehaviour, contentTypeMatcher, contentDispositionMatcher, contentTransferEncodingMatcher, contentMatcher);
295+
if (TypeLoader.TryLoadNewInstance<IMimePartMatcher>(
296+
out var mimePartMatcher,
297+
matchBehaviour,
298+
contentTypeMatcher,
299+
contentDispositionMatcher,
300+
contentTransferEncodingMatcher,
301+
contentMatcher))
302+
{
303+
return mimePartMatcher;
304+
}
305+
306+
throw new InvalidOperationException("The 'MimePartMatcher' cannot be loaded. Please install the WireMock.Net.MimePart package.");
286307
}
287308

288309
private IProtoBufMatcher CreateProtoBufMatcher(MatchBehaviour? matchBehaviour, IReadOnlyList<string> protoDefinitions, MatcherModel matcher)
289310
{
290311
var objectMatcher = Map(matcher.ContentMatcher) as IObjectMatcher;
291312

292-
return TypeLoader.LoadNewInstance<IProtoBufMatcher>(
313+
if (TypeLoader.TryLoadNewInstance<IProtoBufMatcher>(
314+
out var protobufMatcher,
293315
() => ProtoDefinitionUtils.GetIdOrTexts(_settings, protoDefinitions.ToArray()),
294316
matcher.ProtoBufMessageType!,
295317
matchBehaviour ?? MatchBehaviour.AcceptOnMatch,
296-
objectMatcher
297-
);
318+
objectMatcher))
319+
{
320+
return protobufMatcher;
321+
}
322+
323+
throw new InvalidOperationException("The 'ProtoBufMatcher' cannot be loaded. Please install the WireMock.Net.ProtoBuf package.");
298324
}
299325
}

src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,8 @@ private static IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
366366
}
367367
else if (responseModel.BodyAsJson != null)
368368
{
369-
if (responseModel.ProtoBufMessageType != null)
369+
if (responseModel.ProtoBufMessageType != null && TypeLoader.TryLoadStaticInstance<IProtoBufUtils>(out var protoBufUtils))
370370
{
371-
var protoBufUtils = TypeLoader.LoadStaticInstance<IProtoBufUtils>();
372-
373371
if (responseModel.ProtoDefinition != null)
374372
{
375373
responseBuilder = protoBufUtils.UpdateResponseBuilder(responseBuilder, responseModel.ProtoBufMessageType, responseModel.BodyAsJson, responseModel.ProtoDefinition);

src/WireMock.Net.Shared/Matchers/Request/RequestMessageGraphQLMatcher.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,10 @@ private static IMatcher[] CreateMatcherArray(
100100
IDictionary<string, Type>? customScalars
101101
)
102102
{
103-
var graphQLMatcher = TypeLoader.LoadNewInstance<IGraphQLMatcher>(schema, customScalars, matchBehaviour, MatchOperator.Or);
104-
return [graphQLMatcher];
103+
if (TypeLoader.TryLoadNewInstance<IGraphQLMatcher>(out var graphQLMatcher, schema, customScalars, matchBehaviour, MatchOperator.Or))
104+
{
105+
return [graphQLMatcher];
106+
}
107+
return [];
105108
}
106109
}

src/WireMock.Net.Shared/Matchers/Request/RequestMessageProtoBufMatcher.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ public class RequestMessageProtoBufMatcher : IRequestMatcher
2525
/// <param name="matcher">The optional matcher to use to match the ProtoBuf as (json) object.</param>
2626
public RequestMessageProtoBufMatcher(MatchBehaviour matchBehaviour, Func<IdOrTexts> protoDefinition, string messageType, IObjectMatcher? matcher = null)
2727
{
28-
Matcher = TypeLoader.LoadNewInstance<IProtoBufMatcher>(protoDefinition, messageType, matchBehaviour, matcher);
28+
if (TypeLoader.TryLoadNewInstance<IProtoBufMatcher>(out var protoBufMatcher, protoDefinition, messageType, matchBehaviour, matcher))
29+
{
30+
Matcher = protoBufMatcher;
31+
}
2932
}
3033

3134
/// <inheritdoc />

src/WireMock.Net.Shared/Util/TypeLoader.cs

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,68 +14,130 @@ internal static class TypeLoader
1414
{
1515
private static readonly ConcurrentDictionary<string, Type> Assemblies = new();
1616
private static readonly ConcurrentDictionary<Type, object> Instances = new();
17+
private static readonly ConcurrentBag<(string FullName, Type Type)> InstancesWhichCannotBeFoundByFullName = [];
18+
private static readonly ConcurrentBag<(string FullName, Type Type)> StaticInstancesWhichCannotBeFoundByFullName = [];
19+
private static readonly ConcurrentBag<Type> InstancesWhichCannotBeFound = [];
20+
private static readonly ConcurrentBag<Type> StaticInstancesWhichCannotBeFound = [];
1721

18-
public static TInterface LoadNewInstance<TInterface>(params object?[] args) where TInterface : class
22+
public static bool TryLoadNewInstance<TInterface>([NotNullWhen(true)] out TInterface? instance, params object?[] args) where TInterface : class
1923
{
20-
var pluginType = GetPluginType<TInterface>();
24+
var type = typeof(TInterface);
25+
if (InstancesWhichCannotBeFound.Contains(type))
26+
{
27+
instance = null;
28+
return false;
29+
}
30+
31+
if (TryGetPluginType<TInterface>(out var pluginType))
32+
{
33+
instance = (TInterface)Activator.CreateInstance(pluginType, args)!;
34+
return true;
35+
}
2136

22-
return (TInterface)Activator.CreateInstance(pluginType, args)!;
37+
InstancesWhichCannotBeFound.Add(type);
38+
instance = null;
39+
return false;
2340
}
2441

25-
public static TInterface LoadStaticInstance<TInterface>(params object?[] args) where TInterface : class
42+
public static bool TryLoadStaticInstance<TInterface>([NotNullWhen(true)] out TInterface? staticInstance, params object?[] args) where TInterface : class
2643
{
27-
var pluginType = GetPluginType<TInterface>();
44+
var type = typeof(TInterface);
45+
if (StaticInstancesWhichCannotBeFound.Contains(type))
46+
{
47+
staticInstance = null;
48+
return false;
49+
}
2850

29-
return (TInterface)Instances.GetOrAdd(pluginType, key => Activator.CreateInstance(key, args)!);
51+
if (TryGetPluginType<TInterface>(out var pluginType))
52+
{
53+
staticInstance = (TInterface)Instances.GetOrAdd(pluginType, key => Activator.CreateInstance(key, args)!);
54+
return true;
55+
}
56+
57+
StaticInstancesWhichCannotBeFound.Add(type);
58+
staticInstance = null;
59+
return false;
3060
}
3161

32-
public static TInterface LoadNewInstanceByFullName<TInterface>(string implementationTypeFullName, params object?[] args) where TInterface : class
62+
public static bool TryLoadNewInstanceByFullName<TInterface>([NotNullWhen(true)] out TInterface? instance, string implementationTypeFullName, params object?[] args) where TInterface : class
3363
{
3464
Guard.NotNullOrEmpty(implementationTypeFullName);
3565

36-
var pluginType = GetPluginTypeByFullName<TInterface>(implementationTypeFullName);
66+
var type = typeof(TInterface);
67+
if (InstancesWhichCannotBeFoundByFullName.Contains((implementationTypeFullName, type)))
68+
{
69+
instance = null;
70+
return false;
71+
}
72+
73+
if (TryGetPluginTypeByFullName<TInterface>(implementationTypeFullName, out var pluginType))
74+
{
75+
instance = (TInterface)Activator.CreateInstance(pluginType, args)!;
76+
return true;
77+
}
3778

38-
return (TInterface)Activator.CreateInstance(pluginType, args)!;
79+
InstancesWhichCannotBeFoundByFullName.Add((implementationTypeFullName, type));
80+
instance = null;
81+
return false;
3982
}
4083

41-
public static TInterface LoadStaticInstanceByFullName<TInterface>(string implementationTypeFullName, params object?[] args) where TInterface : class
84+
public static bool TryLoadStaticInstanceByFullName<TInterface>([NotNullWhen(true)] out TInterface? staticInstance, string implementationTypeFullName, params object?[] args) where TInterface : class
4285
{
4386
Guard.NotNullOrEmpty(implementationTypeFullName);
4487

45-
var pluginType = GetPluginTypeByFullName<TInterface>(implementationTypeFullName);
88+
var type = typeof(TInterface);
89+
if (StaticInstancesWhichCannotBeFoundByFullName.Contains((implementationTypeFullName, type)))
90+
{
91+
staticInstance = null;
92+
return false;
93+
}
94+
95+
if (TryGetPluginTypeByFullName<TInterface>(implementationTypeFullName, out var pluginType))
96+
{
97+
staticInstance = (TInterface)Instances.GetOrAdd(pluginType, key => Activator.CreateInstance(key, args)!);
98+
return true;
99+
}
46100

47-
return (TInterface)Instances.GetOrAdd(pluginType, key => Activator.CreateInstance(key, args)!);
101+
StaticInstancesWhichCannotBeFoundByFullName.Add((implementationTypeFullName, type));
102+
staticInstance = null;
103+
return false;
48104
}
49105

50-
private static Type GetPluginType<TInterface>() where TInterface : class
106+
private static bool TryGetPluginType<TInterface>([NotNullWhen(true)] out Type? foundType) where TInterface : class
51107
{
52108
var key = typeof(TInterface).FullName!;
53109

54-
return Assemblies.GetOrAdd(key, _ =>
110+
if (Assemblies.TryGetValue(key, out foundType))
55111
{
56-
if (TryFindTypeInDlls<TInterface>(null, out var foundType))
57-
{
58-
return foundType;
59-
}
112+
return true;
113+
}
60114

61-
throw new DllNotFoundException($"No dll found which implements interface '{key}'.");
62-
});
115+
if (TryFindTypeInDlls<TInterface>(null, out foundType))
116+
{
117+
Assemblies.TryAdd(key, foundType);
118+
return true;
119+
}
120+
121+
return false;
63122
}
64123

65-
private static Type GetPluginTypeByFullName<TInterface>(string implementationTypeFullName) where TInterface : class
124+
private static bool TryGetPluginTypeByFullName<TInterface>(string implementationTypeFullName, [NotNullWhen(true)] out Type? foundType) where TInterface : class
66125
{
67126
var @interface = typeof(TInterface).FullName;
68127
var key = $"{@interface}_{implementationTypeFullName}";
69128

70-
return Assemblies.GetOrAdd(key, _ =>
129+
if (Assemblies.TryGetValue(key, out foundType))
71130
{
72-
if (TryFindTypeInDlls<TInterface>(implementationTypeFullName, out var foundType))
73-
{
74-
return foundType;
75-
}
131+
return true;
132+
}
76133

77-
throw new DllNotFoundException($"No dll found which implements Interface '{@interface}' and has FullName '{implementationTypeFullName}'.");
78-
});
134+
if (TryFindTypeInDlls<TInterface>(implementationTypeFullName, out foundType))
135+
{
136+
Assemblies.TryAdd(key, foundType);
137+
return true;
138+
}
139+
140+
return false;
79141
}
80142

81143
private static bool TryFindTypeInDlls<TInterface>(string? implementationTypeFullName, [NotNullWhen(true)] out Type? pluginType) where TInterface : class

0 commit comments

Comments
 (0)