-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Expand file tree
/
Copy pathDefaultStreamNamespacePredicateProvider.cs
More file actions
131 lines (116 loc) · 4.93 KB
/
DefaultStreamNamespacePredicateProvider.cs
File metadata and controls
131 lines (116 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
using System;
using System.Collections.Generic;
using Orleans.Serialization.TypeSystem;
namespace Orleans.Streams
{
/// <summary>
/// Default implementation of <see cref="IStreamNamespacePredicateProvider"/> for internally supported stream predicates.
/// </summary>
public class DefaultStreamNamespacePredicateProvider : IStreamNamespacePredicateProvider
{
/// <inheritdoc/>
public bool TryGetPredicate(string predicatePattern, out IStreamNamespacePredicate predicate)
{
switch (predicatePattern)
{
case "*":
predicate = new AllStreamNamespacesPredicate();
return true;
case var regex when regex.StartsWith(RegexStreamNamespacePredicate.Prefix, StringComparison.Ordinal):
predicate = new RegexStreamNamespacePredicate(regex[RegexStreamNamespacePredicate.Prefix.Length..]);
return true;
case var ns when ns.StartsWith(ExactMatchStreamNamespacePredicate.Prefix, StringComparison.Ordinal):
predicate = new ExactMatchStreamNamespacePredicate(ns[ExactMatchStreamNamespacePredicate.Prefix.Length..]);
return true;
}
predicate = null;
return false;
}
}
/// <summary>
/// Stream namespace predicate provider which supports objects which can be constructed and optionally accept a string as a constructor argument.
/// </summary>
public class ConstructorStreamNamespacePredicateProvider : IStreamNamespacePredicateProvider
{
#if NET9_0_OR_GREATER
private readonly Lock _lock = new();
#else
private readonly object _lock = new();
#endif
private readonly Dictionary<string, bool> _allowedPredicateTypes = new(StringComparer.Ordinal);
/// <summary>
/// The prefix used to identify this predicate provider.
/// </summary>
public const string Prefix = "ctor";
/// <summary>
/// Registers a predicate type as allowed for construction.
/// </summary>
/// <param name="predicateType">The predicate type to register.</param>
public void RegisterPredicateType(Type predicateType)
{
ArgumentNullException.ThrowIfNull(predicateType);
var typeName = RuntimeTypeNameFormatter.Format(predicateType);
lock (_lock)
{
_allowedPredicateTypes[typeName] = true;
}
}
/// <summary>
/// Formats a stream namespace predicate which indicates a concrete <see cref="IStreamNamespacePredicate"/> type to be constructed, along with an optional argument.
/// </summary>
public static string FormatPattern(Type predicateType, string constructorArgument)
{
if (constructorArgument is null)
{
return $"{Prefix}:{RuntimeTypeNameFormatter.Format(predicateType)}";
}
return $"{Prefix}:{RuntimeTypeNameFormatter.Format(predicateType)}:{constructorArgument}";
}
/// <inheritdoc/>
public bool TryGetPredicate(string predicatePattern, out IStreamNamespacePredicate predicate)
{
if (!predicatePattern.StartsWith(Prefix, StringComparison.Ordinal))
{
predicate = null;
return false;
}
var start = Prefix.Length + 1;
string typeName;
string arg;
var index = predicatePattern.IndexOf(':', start);
if (index < 0)
{
typeName = predicatePattern[start..];
arg = null;
}
else
{
typeName = predicatePattern[start..index];
arg = predicatePattern[(index + 1)..];
}
bool allowed;
lock (_lock)
{
allowed = _allowedPredicateTypes.ContainsKey(typeName);
}
if (!allowed)
{
throw new InvalidOperationException($"Type \"{typeName}\" is not a registered stream namespace predicate. Ensure the grain interface assembly is loaded and the predicate type is used in an [{nameof(ImplicitStreamSubscriptionAttribute)}].");
}
var type = Type.GetType(typeName, throwOnError: true);
if (!typeof(IStreamNamespacePredicate).IsAssignableFrom(type))
{
throw new InvalidOperationException($"Type \"{type}\" is not a valid stream namespace predicate because it does not implement {nameof(IStreamNamespacePredicate)}.");
}
if (string.IsNullOrEmpty(arg))
{
predicate = (IStreamNamespacePredicate)Activator.CreateInstance(type);
}
else
{
predicate = (IStreamNamespacePredicate)Activator.CreateInstance(type, arg);
}
return true;
}
}
}