/// <summary> /// Receives notification of any IO exceptions on the connection. /// /// <p/>Upon receipt of a connection closed exception or any IOException, the fail-over process is attempted. If the fail-over fails, then /// all method listeners and the application connection object are notified of the connection failure exception. /// /// <p/>All other exception types are propagated to all method listeners. /// </summary> public void OnException(Exception cause) { _log.Warn("public void OnException(Exception cause = " + cause + "): called"); // Ensure that the method listener set cannot be changed whilst this exception is propagated to all listeners. This also // ensures that this exception is fully propagated to all listeners, before another one can be processed. lock (_lock) { if (cause is AMQConnectionClosedException || cause is System.IO.IOException) { // Try a fail-over because the connection has failed. FailoverState failoverState = AttemptFailover(); // Check if the fail-over has failed, in which case notify all method listeners of the exception. // The application connection object is also notified of the failure of the connection with the exception. if (failoverState == FailoverState.FAILED) { _log.Debug("Fail-over has failed. Notifying all method listeners of the exception."); AMQException amqe = new AMQException("Protocol handler error: " + cause, cause); PropagateExceptionToWaiters(amqe); _connection.ExceptionReceived(cause); } } // Notify all method listeners of the exception. else { PropagateExceptionToWaiters(cause); _connection.ExceptionReceived(cause); } } }
public void Run() { if (Thread.CurrentThread.IsBackground) { throw new InvalidOperationException("FailoverHandler must Run on a non-background thread."); } AMQProtocolListener pl = _connection.ProtocolListener; pl.FailoverLatch = new ManualResetEvent(false); // We wake up listeners. If they can handle failover, they will extend the // FailoverSupport class and will in turn block on the latch until failover // has completed before retrying the operation _connection.ProtocolListener.PropagateExceptionToWaiters(new FailoverException("Failing over about to start")); // Since failover impacts several structures we protect them all with a single mutex. These structures // are also in child objects of the connection. This allows us to manipulate them without affecting // client code which runs in a separate thread. lock (_connection.FailoverMutex) { _log.Info("Starting failover process"); // We switch in a new state manager temporarily so that the interaction to get to the "connection open" // state works, without us having to terminate any existing "state waiters". We could theoretically // have a state waiter waiting until the connection is closed for some reason. Or in future we may have // a slightly more complex state model therefore I felt it was worthwhile doing this. AMQStateManager existingStateManager = _connection.ProtocolListener.StateManager; _connection.ProtocolListener.StateManager = new AMQStateManager(); if (!_connection.FirePreFailover(_host != null)) { _connection.ProtocolListener.StateManager = existingStateManager; if (_host != null) { _connection.ExceptionReceived(new AMQDisconnectedException("Redirect was vetoed by client")); } else { _connection.ExceptionReceived(new AMQDisconnectedException("Failover was vetoed by client")); } pl.FailoverLatch.Set(); pl.FailoverLatch = null; return; } bool failoverSucceeded; // when host is non null we have a specified failover host otherwise we all the client to cycle through // all specified hosts // if _host has value then we are performing a redirect. if (_host != null) { // todo: fix SSL support! failoverSucceeded = _connection.AttemptReconnection(_host, _port, null); } else { failoverSucceeded = _connection.AttemptReconnection(); } // XXX: at this point it appears that we are going to set StateManager to existingStateManager in // XXX: both paths of control. if (!failoverSucceeded) { _connection.ProtocolListener.StateManager = existingStateManager; _connection.ExceptionReceived( new AMQDisconnectedException("Server closed connection and no failover " + "was successful")); } else { _connection.ProtocolListener.StateManager = existingStateManager; try { if (_connection.FirePreResubscribe()) { _log.Info("Resubscribing on new connection"); _connection.ResubscribeChannels(); } else { _log.Info("Client vetoed automatic resubscription"); } _connection.FireFailoverComplete(); _connection.ProtocolListener.FailoverState = FailoverState.NOT_STARTED; _log.Info("Connection failover completed successfully"); } catch (Exception e) { _log.Info("Failover process failed - exception being propagated by protocol handler"); _connection.ProtocolListener.FailoverState = FailoverState.FAILED; try { _connection.ProtocolListener.OnException(e); } catch (Exception ex) { _log.Error("Error notifying protocol session of error: " + ex, ex); } } } } pl.FailoverLatch.Set(); }