diff --git a/projects/client/RabbitMQ.Client/src/client/api/IConnection.cs b/projects/client/RabbitMQ.Client/src/client/api/IConnection.cs index 92c1b6d629..5bf48c3d50 100644 --- a/projects/client/RabbitMQ.Client/src/client/api/IConnection.cs +++ b/projects/client/RabbitMQ.Client/src/client/api/IConnection.cs @@ -170,6 +170,9 @@ public interface IConnection : NetworkConnection, IDisposable /// of those event handlers throws an exception, as well. /// event EventHandler CallbackException; + event EventHandler RecoverySucceeded; + event EventHandler ConnectionRecoveryError; + event EventHandler ConnectionBlocked; diff --git a/projects/client/RabbitMQ.Client/src/client/events/ConnectionRecoveryErrorEventArgs.cs b/projects/client/RabbitMQ.Client/src/client/events/ConnectionRecoveryErrorEventArgs.cs new file mode 100644 index 0000000000..51329e9977 --- /dev/null +++ b/projects/client/RabbitMQ.Client/src/client/events/ConnectionRecoveryErrorEventArgs.cs @@ -0,0 +1,54 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 1.1. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2016 Pivotal Software, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//--------------------------------------------------------------------------- +// +// The MPL v1.1: +// +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License +// at http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +// the License for the specific language governing rights and +// limitations under the License. +// +// The Original Code is RabbitMQ. +// +// The Initial Developer of the Original Code is Pivotal Software, Inc. +// Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; + +namespace RabbitMQ.Client.Events +{ + public sealed class ConnectionRecoveryErrorEventArgs : EventArgs + { + public ConnectionRecoveryErrorEventArgs(Exception ex) + { + Exception = ex; + } + + public Exception Exception { get; private set; } + } +} diff --git a/projects/client/RabbitMQ.Client/src/client/impl/AutorecoveringConnection.cs b/projects/client/RabbitMQ.Client/src/client/impl/AutorecoveringConnection.cs index 392a6fe5dc..81bbbe40e8 100644 --- a/projects/client/RabbitMQ.Client/src/client/impl/AutorecoveringConnection.cs +++ b/projects/client/RabbitMQ.Client/src/client/impl/AutorecoveringConnection.cs @@ -98,6 +98,8 @@ public class AutorecoveringConnection : IConnection, IRecoverable private EventHandler m_queueNameChange; private EventHandler m_recovery; + private EventHandler m_connectionRecoveryError; + public AutorecoveringConnection(ConnectionFactory factory, string clientProvidedName = null) { m_factory = factory; @@ -121,6 +123,42 @@ private bool ManuallyClosed } } + public event EventHandler RecoverySucceeded + { + add + { + lock (m_eventLock) + { + m_recovery += value; + } + } + remove + { + lock (m_eventLock) + { + m_recovery -= value; + } + } + } + + public event EventHandler ConnectionRecoveryError + { + add + { + lock (m_eventLock) + { + m_connectionRecoveryError += value; + } + } + remove + { + lock (m_eventLock) + { + m_connectionRecoveryError -= value; + } + } + } + public event EventHandler CallbackException { add @@ -235,6 +273,7 @@ public event EventHandler QueueNameChang } } + [Obsolete("Use RecoverySucceeded instead")] public event EventHandler Recovery { add @@ -798,14 +837,33 @@ protected void RecoverConnectionDelegate() m_delegate = new Connection(m_factory, false, fh, this.ClientProvidedName); recovering = false; } - catch (Exception) + catch (Exception e) { + // Trigger recovery error event + var handler = m_connectionRecoveryError; + if (handler != null) + { + var args = new ConnectionRecoveryErrorEventArgs(e); + foreach (EventHandler h in handler.GetInvocationList()) + { + try + { + h(this, args); + } + catch (Exception ex) + { + var a = new CallbackExceptionEventArgs(ex); + a.Detail["context"] = "OnConnectionRecoveryError"; + m_delegate.OnCallbackException(a); + } + } + } #if NETFX_CORE System.Threading.Tasks.Task.Delay(m_factory.NetworkRecoveryInterval).Wait(); #else Thread.Sleep(m_factory.NetworkRecoveryInterval); #endif - // TODO: provide a way to handle these exceptions + // TODO: provide a way to handle these exceptions } } } diff --git a/projects/client/RabbitMQ.Client/src/client/impl/Connection.cs b/projects/client/RabbitMQ.Client/src/client/impl/Connection.cs index 9ebd590d71..2e82144a91 100644 --- a/projects/client/RabbitMQ.Client/src/client/impl/Connection.cs +++ b/projects/client/RabbitMQ.Client/src/client/impl/Connection.cs @@ -57,7 +57,6 @@ using System.Net.Sockets; #endif -using System.Reflection; using System.Text; using System.Threading; @@ -72,6 +71,8 @@ public class Connection : IConnection private ManualResetEvent m_appContinuation = new ManualResetEvent(false); private EventHandler m_callbackException; + private EventHandler m_recoverySucceeded; + private EventHandler connectionRecoveryFailure; private IDictionary m_clientProperties; @@ -81,6 +82,7 @@ public class Connection : IConnection private EventHandler m_connectionBlocked; private EventHandler m_connectionShutdown; private EventHandler m_connectionUnblocked; + private IConnectionFactory m_factory; private IFrameHandler m_frameHandler; @@ -130,6 +132,24 @@ public Connection(IConnectionFactory factory, bool insist, IFrameHandler frameHa public Guid Id { get { return m_id; } } + public event EventHandler RecoverySucceeded + { + add + { + lock (m_eventLock) + { + m_recoverySucceeded += value; + } + } + remove + { + lock (m_eventLock) + { + m_recoverySucceeded -= value; + } + } + } + public event EventHandler CallbackException { add @@ -211,6 +231,23 @@ public event EventHandler ConnectionUnblocked } } + public event EventHandler ConnectionRecoveryError + { + add + { + lock (m_eventLock) + { + connectionRecoveryFailure += value; + } + } + remove + { + lock (m_eventLock) + { + connectionRecoveryFailure -= value; + } + } + } public string ClientProvidedName { get; private set; } public bool AutoClose diff --git a/projects/client/Unit/src/unit/TestConnectionRecovery.cs b/projects/client/Unit/src/unit/TestConnectionRecovery.cs index c0d8fe65f1..7c46f603ce 100644 --- a/projects/client/Unit/src/unit/TestConnectionRecovery.cs +++ b/projects/client/Unit/src/unit/TestConnectionRecovery.cs @@ -163,7 +163,7 @@ public void TestBasicConnectionRecoveryWithEndpointList() { using(var c = CreateAutorecoveringConnection( new List - { + { new AmqpTcpEndpoint("127.0.0.1"), new AmqpTcpEndpoint("localhost") })) @@ -174,6 +174,21 @@ public void TestBasicConnectionRecoveryWithEndpointList() } } + [Test] + public void TestBasicConnectionRecoveryErrorEvent() + { + Assert.IsTrue(Conn.IsOpen); + using(var c = CreateAutorecoveringConnection()) + { + var latch = new AutoResetEvent(false); + c.ConnectionRecoveryError += (o, _args) => latch.Set(); + StopRabbitMQ(); + latch.WaitOne(30000); + StartRabbitMQ(); + WaitForRecovery(c); + } + } + [Test] public void TestBasicConnectionRecoveryWithEndpointListAndUnreachableHosts() {