Skip to content

Commit e03b3ad

Browse files
authored
Merge pull request #33 from smdn/introduce-transaction-callback
Introduce ITransactionCallback, replacement for INodeSessionCallback
2 parents 4bd95f2 + 2eee06b commit e03b3ad

File tree

22 files changed

+780
-24
lines changed

22 files changed

+780
-24
lines changed

src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.DependencyInjection/IMuninNodeBuilderExtensions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,22 @@ Func<IServiceProvider, INodeSessionCallback> buildSessionCallback
123123
buildSessionCallback: buildSessionCallback ?? throw new ArgumentNullException(nameof(buildSessionCallback))
124124
);
125125

126+
#pragma warning disable CS0419
127+
/// <remarks>
128+
/// If <see cref="UsePluginProvider"/> is called, the configurations made by this method will be overridden.
129+
/// </remarks>
130+
public static IMuninNodeBuilder UseTransactionCallback(
131+
this IMuninNodeBuilder builder,
132+
Func<CancellationToken, ValueTask>? onStartTransactionAsyncFunc,
133+
Func<CancellationToken, ValueTask>? onEndTransactionAsyncFunc
134+
)
135+
#pragma warning restore CS0419
136+
=> MuninNodeBuilderExtensions.UseTransactionCallback(
137+
builder: ThrowIfBuilderTypeIsNotSupported(builder ?? throw new ArgumentNullException(nameof(builder))),
138+
onStartTransactionAsyncFunc: onStartTransactionAsyncFunc,
139+
onEndTransactionAsyncFunc: onEndTransactionAsyncFunc
140+
);
141+
126142
public static IMuninNodeBuilder UseListenerFactory(
127143
this IMuninNodeBuilder builder,
128144
IMuninNodeListenerFactory listenerFactory

src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.DependencyInjection/MuninNodeBuilder.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7+
using System.Threading;
8+
using System.Threading.Tasks;
79

810
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.Extensions.Logging;
@@ -23,7 +25,9 @@ public class MuninNodeBuilder : IMuninNodeBuilder {
2325
#pragma warning restore CS0618
2426
private readonly List<Func<IServiceProvider, IPlugin>> pluginFactories = new(capacity: 4);
2527
private Func<IServiceProvider, IPluginProvider>? buildPluginProvider;
26-
private Func<IServiceProvider, INodeSessionCallback>? buildSessionCallback;
28+
[Obsolete] private Func<IServiceProvider, INodeSessionCallback>? buildSessionCallback;
29+
private Func<CancellationToken, ValueTask>? onStartTransactionAsyncFunc;
30+
private Func<CancellationToken, ValueTask>? onEndTransactionAsyncFunc;
2731
private Func<IServiceProvider, IMuninNodeListenerFactory>? buildListenerFactory;
2832

2933
/// <summary>
@@ -64,6 +68,7 @@ Func<IServiceProvider, IPluginProvider> buildPluginProvider
6468
this.buildPluginProvider = buildPluginProvider;
6569
}
6670

71+
[Obsolete]
6772
internal void SetSessionCallbackFactory(
6873
Func<IServiceProvider, INodeSessionCallback> buildSessionCallback
6974
)
@@ -74,6 +79,15 @@ Func<IServiceProvider, INodeSessionCallback> buildSessionCallback
7479
this.buildSessionCallback = buildSessionCallback;
7580
}
7681

82+
internal void SetTransactionCallback(
83+
Func<CancellationToken, ValueTask>? onStartTransactionAsyncFunc,
84+
Func<CancellationToken, ValueTask>? onEndTransactionAsyncFunc
85+
)
86+
{
87+
this.onStartTransactionAsyncFunc = onStartTransactionAsyncFunc;
88+
this.onEndTransactionAsyncFunc = onEndTransactionAsyncFunc;
89+
}
90+
7791
internal void SetListenerFactory(
7892
Func<IServiceProvider, IMuninNodeListenerFactory> buildListenerFactory
7993
)
@@ -100,26 +114,51 @@ public IMuninNode Build(IServiceProvider serviceProvider)
100114
pluginProvider: buildPluginProvider is null
101115
? new PluginProvider(
102116
plugins: pluginFactories.Select(factory => factory(serviceProvider)).ToList(),
103-
sessionCallback: buildSessionCallback?.Invoke(serviceProvider)
117+
#pragma warning disable CS0612
118+
sessionCallback: buildSessionCallback?.Invoke(serviceProvider),
119+
#pragma warning restore CS0612
120+
onStartTransactionAsyncFunc: onStartTransactionAsyncFunc,
121+
onEndTransactionAsyncFunc: onEndTransactionAsyncFunc
104122
)
105123
: buildPluginProvider.Invoke(serviceProvider),
106124
listenerFactory: buildListenerFactory?.Invoke(serviceProvider),
107125
serviceProvider: serviceProvider
108126
);
109127
}
110128

111-
private sealed class PluginProvider : IPluginProvider {
129+
private sealed class PluginProvider : IPluginProvider, ITransactionCallback {
112130
public IReadOnlyCollection<IPlugin> Plugins { get; }
131+
132+
[Obsolete]
113133
public INodeSessionCallback? SessionCallback { get; }
114134

135+
private readonly Func<CancellationToken, ValueTask>? onStartTransactionAsyncFunc;
136+
private readonly Func<CancellationToken, ValueTask>? onEndTransactionAsyncFunc;
137+
115138
public PluginProvider(
116139
IReadOnlyCollection<IPlugin> plugins,
117-
INodeSessionCallback? sessionCallback
140+
#pragma warning disable CS0618
141+
INodeSessionCallback? sessionCallback,
142+
#pragma warning restore CS0618
143+
Func<CancellationToken, ValueTask>? onStartTransactionAsyncFunc,
144+
Func<CancellationToken, ValueTask>? onEndTransactionAsyncFunc
118145
)
119146
{
120147
Plugins = plugins ?? throw new ArgumentNullException(nameof(plugins));
148+
149+
#pragma warning disable CS0612
121150
SessionCallback = sessionCallback;
151+
#pragma warning restore CS0612
152+
153+
this.onStartTransactionAsyncFunc = onStartTransactionAsyncFunc;
154+
this.onEndTransactionAsyncFunc = onEndTransactionAsyncFunc;
122155
}
156+
157+
public ValueTask StartTransactionAsync(CancellationToken cancellationToken)
158+
=> onStartTransactionAsyncFunc?.Invoke(cancellationToken) ?? default;
159+
160+
public ValueTask EndTransactionAsync(CancellationToken cancellationToken)
161+
=> onEndTransactionAsyncFunc?.Invoke(cancellationToken) ?? default;
123162
}
124163

125164
protected virtual IMuninNode Build(

src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.DependencyInjection/MuninNodeBuilderExtensions.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,17 @@ Func<IServiceProvider, IPluginProvider> buildPluginProvider
100100
return builder;
101101
}
102102

103+
#pragma warning disable CS0618
104+
private const string ObsoleteMessageForUseSessionCallback =
105+
$"{nameof(INodeSessionCallback)} is deprecated and will be removed in the next major version release. " +
106+
$"Use ${nameof(UseTransactionCallback)} instead of {nameof(UseSessionCallback)}.";
107+
#pragma warning restore CS0618
108+
103109
#pragma warning disable CS0419
104110
/// <remarks>
105111
/// If <see cref="UsePluginProvider"/> is called, the configurations made by this method will be overridden.
106112
/// </remarks>
113+
[Obsolete(ObsoleteMessageForUseSessionCallback)]
107114
public static TMuninNodeBuilder UseSessionCallback<TMuninNodeBuilder>(
108115
this TMuninNodeBuilder builder,
109116
INodeSessionCallback sessionCallback
@@ -126,6 +133,7 @@ INodeSessionCallback sessionCallback
126133
/// <remarks>
127134
/// If <see cref="UsePluginProvider"/> is called, the configurations made by this method will be overridden.
128135
/// </remarks>
136+
[Obsolete(ObsoleteMessageForUseSessionCallback)]
129137
public static TMuninNodeBuilder UseSessionCallback<TMuninNodeBuilder>(
130138
this TMuninNodeBuilder builder,
131139
Func<string, CancellationToken, ValueTask>? reportSessionStartedAsyncFunc,
@@ -141,6 +149,7 @@ public static TMuninNodeBuilder UseSessionCallback<TMuninNodeBuilder>(
141149
)
142150
);
143151

152+
[Obsolete]
144153
private sealed class SessionCallbackFuncWrapper(
145154
Func<string, CancellationToken, ValueTask>? reportSessionStartedAsyncFunc,
146155
Func<string, CancellationToken, ValueTask>? reportSessionClosedAsyncFunc
@@ -160,6 +169,7 @@ public ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken ca
160169
/// <remarks>
161170
/// If <see cref="UsePluginProvider"/> is called, the configurations made by this method will be overridden.
162171
/// </remarks>
172+
[Obsolete(ObsoleteMessageForUseSessionCallback)]
163173
public static TMuninNodeBuilder UseSessionCallback<TMuninNodeBuilder>(
164174
this TMuninNodeBuilder builder,
165175
Func<IServiceProvider, INodeSessionCallback> buildSessionCallback
@@ -177,6 +187,29 @@ Func<IServiceProvider, INodeSessionCallback> buildSessionCallback
177187
return builder;
178188
}
179189

190+
#pragma warning disable CS0419
191+
/// <remarks>
192+
/// If <see cref="UsePluginProvider"/> is called, the configurations made by this method will be overridden.
193+
/// </remarks>
194+
public static TMuninNodeBuilder UseTransactionCallback<TMuninNodeBuilder>(
195+
this TMuninNodeBuilder builder,
196+
Func<CancellationToken, ValueTask>? onStartTransactionAsyncFunc,
197+
Func<CancellationToken, ValueTask>? onEndTransactionAsyncFunc
198+
)
199+
where TMuninNodeBuilder : MuninNodeBuilder
200+
#pragma warning restore CS0419
201+
{
202+
if (builder is null)
203+
throw new ArgumentNullException(nameof(builder));
204+
205+
builder.SetTransactionCallback(
206+
onStartTransactionAsyncFunc,
207+
onEndTransactionAsyncFunc
208+
);
209+
210+
return builder;
211+
}
212+
180213
public static TMuninNodeBuilder UseListenerFactory<TMuninNodeBuilder>(
181214
this TMuninNodeBuilder builder,
182215
IMuninNodeListenerFactory listenerFactory

src/Smdn.Net.MuninNode/Smdn.Net.MuninNode.Protocol/MuninProtocolHandler.cs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,28 @@ public ValueTask HandleTransactionStartAsync(
147147
}
148148

149149
/// <remarks>
150-
/// In the default implementation, a banner response is sent back to the client.
150+
/// In the default implementation, <see cref="ITransactionCallback.StartTransactionAsync"/> is
151+
/// called for <see cref="IPluginProvider"/> and <see cref="IPlugin"/>s,
152+
/// and then a banner response is sent back to the client.
151153
/// </remarks>
152-
protected virtual ValueTask HandleTransactionStartAsyncCore(
154+
protected virtual async ValueTask HandleTransactionStartAsyncCore(
153155
IMuninNodeClient client,
154156
CancellationToken cancellationToken
155157
)
156-
=> SendResponseAsync(
158+
{
159+
if (profile.PluginProvider is ITransactionCallback providerTransactionCallback)
160+
await providerTransactionCallback.StartTransactionAsync(cancellationToken).ConfigureAwait(false);
161+
162+
foreach (var pluginTransactionCallback in plugins.Values.OfType<ITransactionCallback>()) {
163+
await pluginTransactionCallback.StartTransactionAsync(cancellationToken).ConfigureAwait(false);
164+
}
165+
166+
await SendResponseAsync(
157167
client,
158168
banner,
159169
cancellationToken
160-
);
170+
).ConfigureAwait(false);
171+
}
161172

162173
/// <inheritdoc cref="IMuninProtocolHandler.HandleTransactionEndAsync"/>
163174
public ValueTask HandleTransactionEndAsync(
@@ -178,11 +189,22 @@ public ValueTask HandleTransactionEndAsync(
178189
return HandleTransactionEndAsyncCore(client, cancellationToken);
179190
}
180191

181-
protected virtual ValueTask HandleTransactionEndAsyncCore(
192+
/// <remarks>
193+
/// In the default implementation, <see cref="ITransactionCallback.EndTransactionAsync"/> is
194+
/// called for <see cref="IPluginProvider"/> and <see cref="IPlugin"/>s.
195+
/// </remarks>
196+
protected virtual async ValueTask HandleTransactionEndAsyncCore(
182197
IMuninNodeClient client,
183198
CancellationToken cancellationToken
184199
)
185-
=> default; // do nothing in this class
200+
{
201+
foreach (var pluginTransactionCallback in plugins.Values.OfType<ITransactionCallback>()) {
202+
await pluginTransactionCallback.EndTransactionAsync(cancellationToken).ConfigureAwait(false);
203+
}
204+
205+
if (profile.PluginProvider is ITransactionCallback providerTransactionCallback)
206+
await providerTransactionCallback.EndTransactionAsync(cancellationToken).ConfigureAwait(false);
207+
}
186208

187209
private static bool ExpectCommand(
188210
ReadOnlySequence<byte> commandLine,

src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.Create.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ partial class LocalNode {
2020
#pragma warning restore IDE0040
2121
private class ReadOnlyCollectionPluginProvider : IPluginProvider {
2222
public IReadOnlyCollection<IPlugin> Plugins { get; }
23+
24+
[Obsolete]
2325
public INodeSessionCallback? SessionCallback => null;
2426

2527
public ReadOnlyCollectionPluginProvider(IReadOnlyCollection<IPlugin> plugins)

src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/NodeBase.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,13 +562,14 @@ CancellationToken cancellationToken
562562
LogSessionStarted(Logger, null);
563563

564564
try {
565-
// TODO: rename INodeSessionCallback to ITransactionCallback
565+
#pragma warning disable CS0612,CS0618
566566
if (PluginProvider.SessionCallback is INodeSessionCallback pluginProviderSessionCallback)
567567
await pluginProviderSessionCallback.ReportSessionStartedAsync(sessionId, cancellationToken).ConfigureAwait(false);
568568

569569
foreach (var pluginSessionCallback in EnumerateSessionCallbackForPlugins(PluginProvider)) {
570570
await pluginSessionCallback.ReportSessionStartedAsync(sessionId, cancellationToken).ConfigureAwait(false);
571571
}
572+
#pragma warning restore CS0612,CS0618
572573

573574
// https://docs.microsoft.com/ja-jp/dotnet/standard/io/pipelines
574575
var pipe = new Pipe();
@@ -582,17 +583,22 @@ await Task.WhenAll(
582583
LogSessionClosed(Logger, null);
583584
}
584585
finally {
586+
#pragma warning disable CS0612,CS0618
585587
foreach (var pluginSessionCallback in EnumerateSessionCallbackForPlugins(PluginProvider)) {
586588
await pluginSessionCallback.ReportSessionClosedAsync(sessionId, cancellationToken).ConfigureAwait(false);
587589
}
588590

589591
if (PluginProvider.SessionCallback is INodeSessionCallback pluginProviderSessionCallback)
590592
await pluginProviderSessionCallback.ReportSessionClosedAsync(sessionId, cancellationToken).ConfigureAwait(false);
593+
#pragma warning restore CS0612,CS0618
591594

592595
await protocolHandler.HandleTransactionEndAsync(client, cancellationToken).ConfigureAwait(false);
593596
}
594597

598+
[Obsolete]
599+
#pragma warning disable CS0618
595600
static IEnumerable<INodeSessionCallback> EnumerateSessionCallbackForPlugins(IPluginProvider pluginProvider)
601+
#pragma warning restore CS0618
596602
{
597603
foreach (var plugin in pluginProvider.EnumeratePlugins(flattenMultigraphPlugins: true)) {
598604
if (plugin.SessionCallback is INodeSessionCallback pluginSessionCallback)

src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/AggregatePluginProvider.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ namespace Smdn.Net.MuninPlugin;
1717
#pragma warning disable IDE0055
1818
public sealed class AggregatePluginProvider :
1919
ReadOnlyCollection<IPluginProvider>,
20+
#pragma warning disable CS0618
2021
INodeSessionCallback,
21-
IPluginProvider
22+
#pragma warning restore CS0618
23+
IPluginProvider,
24+
ITransactionCallback
2225
{
2326
#pragma warning restore IDE0055
2427
/*
2528
* IPluginProvider
2629
*/
2730
public IReadOnlyCollection<IPlugin> Plugins { get; }
2831

32+
[Obsolete]
2933
INodeSessionCallback? IPluginProvider.SessionCallback => this;
3034

3135
/*
@@ -37,6 +41,7 @@ public AggregatePluginProvider(IList<IPluginProvider> pluginProviders)
3741
Plugins = Items.SelectMany(static provider => provider.Plugins).ToList();
3842
}
3943

44+
#pragma warning disable CS0618
4045
/*
4146
* INodeSessionCallback
4247
*/
@@ -59,4 +64,26 @@ async ValueTask INodeSessionCallback.ReportSessionClosedAsync(string sessionId,
5964
await sessionCallback.ReportSessionClosedAsync(sessionId, cancellationToken).ConfigureAwait(false);
6065
}
6166
}
67+
#pragma warning restore CS0618
68+
69+
/*
70+
* ITransactionCallback
71+
*/
72+
async ValueTask ITransactionCallback.StartTransactionAsync(CancellationToken cancellationToken)
73+
{
74+
foreach (var transactionCallback in Items.OfType<ITransactionCallback>()) {
75+
cancellationToken.ThrowIfCancellationRequested();
76+
77+
await transactionCallback.StartTransactionAsync(cancellationToken).ConfigureAwait(false);
78+
}
79+
}
80+
81+
async ValueTask ITransactionCallback.EndTransactionAsync(CancellationToken cancellationToken)
82+
{
83+
foreach (var transactionCallback in Items.OfType<ITransactionCallback>()) {
84+
cancellationToken.ThrowIfCancellationRequested();
85+
86+
await transactionCallback.EndTransactionAsync(cancellationToken).ConfigureAwait(false);
87+
}
88+
}
6289
}

src/Smdn.Net.MuninNode/Smdn.Net.MuninPlugin/INodeSessionCallback.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
22
// SPDX-License-Identifier: MIT
33

4+
using System;
45
using System.Threading;
56
using System.Threading.Tasks;
67

@@ -9,6 +10,7 @@ namespace Smdn.Net.MuninPlugin;
910
/// <summary>
1011
/// Defines the callbacks when a request session from the <c>munin-update</c> starts or ends.
1112
/// </summary>
13+
[Obsolete(message: ObsoleteMessage.TypeReference)]
1214
public interface INodeSessionCallback {
1315
/// <summary>
1416
/// Implements a callback to be called when <c>munin-update</c> starts a session.
@@ -25,4 +27,15 @@ public interface INodeSessionCallback {
2527
/// <param name="sessionId">A unique ID that <see cref="MuninNode.NodeBase"/> associates with the session.</param>
2628
/// <param name="cancellationToken">The <see cref="CancellationToken" /> to monitor for cancellation requests.</param>
2729
ValueTask ReportSessionClosedAsync(string sessionId, CancellationToken cancellationToken);
30+
31+
internal static class ObsoleteMessage {
32+
public const string TypeReference =
33+
$"{nameof(INodeSessionCallback)} is deprecated and will be removed in the next major version release. " +
34+
$"Use ${nameof(ITransactionCallback)} interface instead.";
35+
36+
public const string SessionCallbackProperty =
37+
$"{nameof(INodeSessionCallback)} is deprecated and will be removed in the next major version release. " +
38+
$"Instead of setting an object that implements ${nameof(INodeSessionCallback)} to the property, " +
39+
$"implement the ${nameof(ITransactionCallback)} interface to the type itself.";
40+
}
2841
}

0 commit comments

Comments
 (0)