From 45f5ca7e0824aa1f41cf1c04c7dbbbc4d74aa3a1 Mon Sep 17 00:00:00 2001 From: Luke Bakken Date: Wed, 18 Dec 2024 15:15:40 -0800 Subject: [PATCH 1/2] `IEnvironment` XML doc * Minor refactor to remove ability to get all connections from `IEnvironment`, since that's just used for tests. --- RabbitMQ.AMQP.Client/IEnvironment.cs | 30 ++++++++------- RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs | 7 ++-- RabbitMQ.AMQP.Client/PublicAPI.Unshipped.txt | 2 - Tests/ClusterTests.cs | 6 ++- Tests/EnvironmentTests.cs | 40 ++++++++++++-------- 5 files changed, 49 insertions(+), 36 deletions(-) diff --git a/RabbitMQ.AMQP.Client/IEnvironment.cs b/RabbitMQ.AMQP.Client/IEnvironment.cs index 6993868..258b9be 100644 --- a/RabbitMQ.AMQP.Client/IEnvironment.cs +++ b/RabbitMQ.AMQP.Client/IEnvironment.cs @@ -2,38 +2,42 @@ // and the Mozilla Public License, version 2.0. // Copyright (c) 2017-2024 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. -using System.Collections.ObjectModel; using System.Threading.Tasks; namespace RabbitMQ.AMQP.Client { /// - /// Interface to create IConnections and manage them. + /// + /// The is the main entry point to a node or a cluster of nodes. + /// + /// + /// The method allows creating instances. + /// An application is expected to maintain a single instance and to close that instance + /// upon application exit. + /// + /// + /// instances are expected to be thread-safe. + /// /// public interface IEnvironment { /// - /// Create a new connection with the given connection settings. + /// Create a new with the given connection settings. /// /// - /// IConnection + /// instance. public Task CreateConnectionAsync(ConnectionSettings connectionSettings); /// - /// Create a new connection with the default connection settings. + /// Create a new with the default connection settings. /// - /// IConnection + /// instance. public Task CreateConnectionAsync(); /// - /// Get all connections. + /// Close this environment and its resources. /// - public ReadOnlyCollection GetConnections(); - - /// - /// Close all connections. - /// - /// + /// // TODO cancellation token Task CloseAsync(); } diff --git a/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs b/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs index c3c3e72..5059f06 100644 --- a/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs +++ b/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs @@ -3,7 +3,7 @@ // Copyright (c) 2017-2024 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. using System.Collections.Concurrent; -using System.Collections.ObjectModel; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -59,13 +59,12 @@ public Task CreateConnectionAsync() return CreateConnectionAsync(ConnectionSettings); } - public ReadOnlyCollection GetConnections() => - new(_connections.Values.ToList()); - // TODO cancellation token public Task CloseAsync() { return Task.WhenAll(_connections.Values.Select(c => c.CloseAsync())); } + + internal IList Connections => _connections.Values.ToList(); } } diff --git a/RabbitMQ.AMQP.Client/PublicAPI.Unshipped.txt b/RabbitMQ.AMQP.Client/PublicAPI.Unshipped.txt index 7b38b66..51477cf 100644 --- a/RabbitMQ.AMQP.Client/PublicAPI.Unshipped.txt +++ b/RabbitMQ.AMQP.Client/PublicAPI.Unshipped.txt @@ -214,7 +214,6 @@ RabbitMQ.AMQP.Client.IEnvironment RabbitMQ.AMQP.Client.IEnvironment.CloseAsync() -> System.Threading.Tasks.Task! RabbitMQ.AMQP.Client.IEnvironment.CreateConnectionAsync() -> System.Threading.Tasks.Task! RabbitMQ.AMQP.Client.IEnvironment.CreateConnectionAsync(RabbitMQ.AMQP.Client.ConnectionSettings! connectionSettings) -> System.Threading.Tasks.Task! -RabbitMQ.AMQP.Client.IEnvironment.GetConnections() -> System.Collections.ObjectModel.ReadOnlyCollection! RabbitMQ.AMQP.Client.IExchangeSpecification RabbitMQ.AMQP.Client.IExchangeSpecification.Argument(string! key, object! value) -> RabbitMQ.AMQP.Client.IExchangeSpecification! RabbitMQ.AMQP.Client.IExchangeSpecification.Arguments(System.Collections.Generic.Dictionary! arguments) -> RabbitMQ.AMQP.Client.IExchangeSpecification! @@ -361,7 +360,6 @@ RabbitMQ.AMQP.Client.Impl.AmqpEnvironment RabbitMQ.AMQP.Client.Impl.AmqpEnvironment.CloseAsync() -> System.Threading.Tasks.Task! RabbitMQ.AMQP.Client.Impl.AmqpEnvironment.CreateConnectionAsync() -> System.Threading.Tasks.Task! RabbitMQ.AMQP.Client.Impl.AmqpEnvironment.CreateConnectionAsync(RabbitMQ.AMQP.Client.ConnectionSettings! connectionSettings) -> System.Threading.Tasks.Task! -RabbitMQ.AMQP.Client.Impl.AmqpEnvironment.GetConnections() -> System.Collections.ObjectModel.ReadOnlyCollection! RabbitMQ.AMQP.Client.Impl.AmqpExchangeSpecification RabbitMQ.AMQP.Client.Impl.AmqpExchangeSpecification.AmqpExchangeSpecification(RabbitMQ.AMQP.Client.Impl.AmqpManagement! management) -> void RabbitMQ.AMQP.Client.Impl.AmqpExchangeSpecification.Argument(string! key, object! value) -> RabbitMQ.AMQP.Client.IExchangeSpecification! diff --git a/Tests/ClusterTests.cs b/Tests/ClusterTests.cs index 1a073a3..8513e6e 100644 --- a/Tests/ClusterTests.cs +++ b/Tests/ClusterTests.cs @@ -33,15 +33,17 @@ public async Task CreateConnectionWithEnvironmentAndMultipleUris() ConnectionSettings connectionSettings = connectionSettingBuilder.Build(); IEnvironment env = AmqpEnvironment.Create(connectionSettings); + var amqpEnv = (AmqpEnvironment)env; // Note: by using _connection, the test will dispose the object on teardown _connection = await env.CreateConnectionAsync(); Assert.NotNull(_connection); - Assert.NotEmpty(env.GetConnections()); + + Assert.NotEmpty(amqpEnv.Connections); await env.CloseAsync(); Assert.Equal(State.Closed, _connection.State); - Assert.Empty(env.GetConnections()); + Assert.Empty(amqpEnv.Connections); } } diff --git a/Tests/EnvironmentTests.cs b/Tests/EnvironmentTests.cs index 786da93..5f10beb 100644 --- a/Tests/EnvironmentTests.cs +++ b/Tests/EnvironmentTests.cs @@ -18,82 +18,92 @@ public class EnvironmentTests(ITestOutputHelper testOutputHelper) public async Task CreateAConnectionWithEnvironment() { IEnvironment env = AmqpEnvironment.Create(ConnectionSettingsBuilder.Create().Build()); + var amqpEnv = (AmqpEnvironment)env; + IConnection connection = await env.CreateConnectionAsync(); + Assert.NotNull(connection); - Assert.NotEmpty(env.GetConnections()); + Assert.NotEmpty(amqpEnv.Connections); await env.CloseAsync(); Assert.Equal(State.Closed, connection.State); - Assert.Empty(env.GetConnections()); + Assert.Empty(amqpEnv.Connections); } [Fact] public async Task CreateMoreConnectionsWithDifferentParametersEnvironment() { string envConnectionName = "EnvironmentConnection_" + Guid.NewGuid(); + IEnvironment env = AmqpEnvironment.Create( ConnectionSettingsBuilder.Create().ContainerId(envConnectionName).Build()); + var amqpEnv = (AmqpEnvironment)env; IConnection connection = await env.CreateConnectionAsync(); + Assert.NotNull(connection); await WaitUntilConnectionIsOpen(envConnectionName); - Assert.NotEmpty(env.GetConnections()); - Assert.Single(env.GetConnections()); + Assert.NotEmpty(amqpEnv.Connections); + Assert.Single(amqpEnv.Connections); string envConnectionName2 = "EnvironmentConnection2_" + Guid.NewGuid(); IConnection connection2 = await env.CreateConnectionAsync( ConnectionSettingsBuilder.Create().ContainerId(envConnectionName2).Build()); Assert.NotNull(connection2); - Assert.Equal(2, env.GetConnections().Count); + Assert.Equal(2, amqpEnv.Connections.Count); await WaitUntilConnectionIsOpen(envConnectionName2); await env.CloseAsync(); Assert.Equal(State.Closed, connection.State); Assert.Equal(State.Closed, connection2.State); - Assert.Empty(env.GetConnections()); + Assert.Empty(amqpEnv.Connections); } [Fact] public async Task CloseConnectionsIndividually() { string envConnectionName = "EnvironmentConnection_" + Guid.NewGuid(); + IEnvironment env = AmqpEnvironment.Create( ConnectionSettingsBuilder.Create().ContainerId(envConnectionName).Build()); + var amqpEnv = (AmqpEnvironment)env; + IConnection connection = await env.CreateConnectionAsync(); + await WaitUntilConnectionIsOpen(envConnectionName); - Assert.Single(env.GetConnections()); - Assert.Equal(1, env.GetConnections()[0].Id); + Assert.Single(amqpEnv.Connections); + Assert.Equal(1, amqpEnv.Connections[0].Id); string envConnectionName2 = "EnvironmentConnection2_" + Guid.NewGuid().ToString(); IConnection connection2 = await env.CreateConnectionAsync( ConnectionSettingsBuilder.Create().ContainerId(envConnectionName2).Build()); - Assert.Equal(2, env.GetConnections().Count); - Assert.Equal(2, env.GetConnections()[1].Id); + Assert.Equal(2, amqpEnv.Connections.Count); + Assert.Equal(2, amqpEnv.Connections[1].Id); await WaitUntilConnectionIsOpen(envConnectionName2); string envConnectionName3 = "EnvironmentConnection3_" + Guid.NewGuid().ToString(); IConnection connection3 = await env.CreateConnectionAsync( ConnectionSettingsBuilder.Create().ContainerId(envConnectionName3).Build()); - Assert.Equal(3, env.GetConnections().Count); - Assert.Equal(3, env.GetConnections()[2].Id); + Assert.Equal(3, amqpEnv.Connections.Count); + Assert.Equal(3, amqpEnv.Connections[2].Id); await WaitUntilConnectionIsOpen(envConnectionName3); // closing await connection.CloseAsync(); Assert.Equal(State.Closed, connection.State); - Assert.Equal(2, env.GetConnections().Count); + Assert.Equal(2, amqpEnv.Connections.Count); await WaitUntilConnectionIsClosed(envConnectionName); await connection2.CloseAsync(); Assert.Equal(State.Closed, connection2.State); - Assert.Single(env.GetConnections()); + Assert.Single(amqpEnv.Connections); await WaitUntilConnectionIsClosed(envConnectionName2); await connection3.CloseAsync(); Assert.Equal(State.Closed, connection3.State); await WaitUntilConnectionIsClosed(envConnectionName3); - Assert.Empty(env.GetConnections()); + Assert.Empty(amqpEnv.Connections); await env.CloseAsync(); } } From 5d565a2bd918873a26c3284a3982a0df4bef45f0 Mon Sep 17 00:00:00 2001 From: Luke Bakken Date: Wed, 18 Dec 2024 16:29:52 -0800 Subject: [PATCH 2/2] * XML doc for `AmqpEnvironment` --- RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs | 30 +++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs b/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs index 5059f06..df358a1 100644 --- a/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs +++ b/RabbitMQ.AMQP.Client/Impl/AmqpEnvironment.cs @@ -10,6 +10,14 @@ namespace RabbitMQ.AMQP.Client.Impl { + /// + /// + /// is the implementation of . + /// + /// + /// The method allows creating instances. + /// + /// public class AmqpEnvironment : IEnvironment { private ConnectionSettings ConnectionSettings { get; } @@ -23,12 +31,24 @@ private AmqpEnvironment(ConnectionSettings connectionSettings, IMetricsReporter? _metricsReporter = metricsReporter; } - // TODO to play nicely with IoC containers, we should not have static Create methods + /// + /// Create a new instance, using the provided + /// and optional + /// + /// + /// + /// instance. public static IEnvironment Create(ConnectionSettings connectionSettings, IMetricsReporter? metricsReporter = default) { + // TODO to play nicely with IoC containers, we should not have static Create methods return new AmqpEnvironment(connectionSettings, metricsReporter); } + /// + /// Create a new instance, using the provided . + /// + /// + /// instance. public async Task CreateConnectionAsync(ConnectionSettings connectionSettings) { IConnection c = await AmqpConnection.CreateAsync(connectionSettings, _metricsReporter).ConfigureAwait(false); @@ -49,6 +69,10 @@ public async Task CreateConnectionAsync(ConnectionSettings connecti return c; } + /// + /// Create a new instance, using the . + /// + /// instance. public Task CreateConnectionAsync() { if (ConnectionSettings is null) @@ -59,6 +83,10 @@ public Task CreateConnectionAsync() return CreateConnectionAsync(ConnectionSettings); } + /// + /// Close this environment and its resources. + /// + /// // TODO cancellation token public Task CloseAsync() {