예제 #1
0
        /// <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);
                }
            }
        }
예제 #2
0
        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();
        }