com.rabbitmq.client.ConnectionFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.rabbitmq.client.ConnectionFactory.java

Source

// Copyright (c) 2007-Present Pivotal Software, Inc.  All rights reserved.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2.  For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// info@rabbitmq.com.

package com.rabbitmq.client;

import com.rabbitmq.client.impl.*;
import com.rabbitmq.client.impl.nio.NioParams;
import com.rabbitmq.client.impl.nio.SocketChannelFrameHandlerFactory;
import com.rabbitmq.client.impl.recovery.AutorecoveringConnection;
import com.rabbitmq.client.impl.recovery.RetryHandler;
import com.rabbitmq.client.impl.recovery.TopologyRecoveryFilter;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Predicate;

import static java.util.concurrent.TimeUnit.MINUTES;

/**
 * Convenience factory class to facilitate opening a {@link Connection} to a RabbitMQ node.
 *
 * Most connection and socket settings are configured using this factory.
 * Some settings that apply to connections can also be configured here
 * and will apply to all connections produced by this factory.
 */
public class ConnectionFactory implements Cloneable {

    /** Default user name */
    public static final String DEFAULT_USER = "guest";
    /** Default password */
    public static final String DEFAULT_PASS = "guest";
    /** Default virtual host */
    public static final String DEFAULT_VHOST = "/";
    /** Default maximum channel number;
     *  2047 because it's 2048 on the server side minus channel 0,
     *  which each connection uses for negotiation
     *  and error communication */
    public static final int DEFAULT_CHANNEL_MAX = 2047;
    /** Default maximum frame size;
     *  zero means no limit */
    public static final int DEFAULT_FRAME_MAX = 0;
    /** Default heart-beat interval;
     *  60 seconds */
    public static final int DEFAULT_HEARTBEAT = 60;
    /** The default host */
    public static final String DEFAULT_HOST = "localhost";
    /** 'Use the default port' port */
    public static final int USE_DEFAULT_PORT = -1;
    /** The default non-ssl port */
    public static final int DEFAULT_AMQP_PORT = AMQP.PROTOCOL.PORT;
    /** The default ssl port */
    public static final int DEFAULT_AMQP_OVER_SSL_PORT = 5671;
    /** The default TCP connection timeout: 60 seconds */
    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
    /**
     * The default AMQP 0-9-1 connection handshake timeout. See DEFAULT_CONNECTION_TIMEOUT
     * for TCP (socket) connection timeout.
     */
    public static final int DEFAULT_HANDSHAKE_TIMEOUT = 10000;
    /** The default shutdown timeout;
     *  zero means wait indefinitely */
    public static final int DEFAULT_SHUTDOWN_TIMEOUT = 10000;

    /** The default continuation timeout for RPC calls in channels: 10 minutes */
    public static final int DEFAULT_CHANNEL_RPC_TIMEOUT = (int) MINUTES.toMillis(10);

    /** The default network recovery interval: 5000 millis */
    public static final long DEFAULT_NETWORK_RECOVERY_INTERVAL = 5000;

    /** The default timeout for work pool enqueueing: no timeout */
    public static final int DEFAULT_WORK_POOL_TIMEOUT = -1;

    private static final String PREFERRED_TLS_PROTOCOL = "TLSv1.2";

    private static final String FALLBACK_TLS_PROTOCOL = "TLSv1";

    private String virtualHost = DEFAULT_VHOST;
    private String host = DEFAULT_HOST;
    private int port = USE_DEFAULT_PORT;
    private int requestedChannelMax = DEFAULT_CHANNEL_MAX;
    private int requestedFrameMax = DEFAULT_FRAME_MAX;
    private int requestedHeartbeat = DEFAULT_HEARTBEAT;
    private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
    private int handshakeTimeout = DEFAULT_HANDSHAKE_TIMEOUT;
    private int shutdownTimeout = DEFAULT_SHUTDOWN_TIMEOUT;
    private Map<String, Object> _clientProperties = AMQConnection.defaultClientProperties();
    private SocketFactory socketFactory = null;
    private SaslConfig saslConfig = DefaultSaslConfig.PLAIN;

    private ExecutorService sharedExecutor;
    private ThreadFactory threadFactory = Executors.defaultThreadFactory();
    // minimises the number of threads rapid closure of many
    // connections uses, see rabbitmq/rabbitmq-java-client#86
    private ExecutorService shutdownExecutor;
    private ScheduledExecutorService heartbeatExecutor;
    private SocketConfigurator socketConf = SocketConfigurators.defaultConfigurator();
    private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();
    private CredentialsProvider credentialsProvider = new DefaultCredentialsProvider(DEFAULT_USER, DEFAULT_PASS);

    private boolean automaticRecovery = true;
    private boolean topologyRecovery = true;
    private ExecutorService topologyRecoveryExecutor;

    // long is used to make sure the users can use both ints
    // and longs safely. It is unlikely that anybody'd need
    // to use recovery intervals > Integer.MAX_VALUE in practice.
    private long networkRecoveryInterval = DEFAULT_NETWORK_RECOVERY_INTERVAL;
    private RecoveryDelayHandler recoveryDelayHandler;

    private MetricsCollector metricsCollector;

    private boolean nio = false;
    private FrameHandlerFactory frameHandlerFactory;
    private NioParams nioParams = new NioParams();

    private SslContextFactory sslContextFactory;

    /**
     * Continuation timeout on RPC calls.
     * @since 4.1.0
     */
    private int channelRpcTimeout = DEFAULT_CHANNEL_RPC_TIMEOUT;

    /**
     * Whether or not channels check the reply type of an RPC call.
     * Default is false.
     * @since 4.2.0
     */
    private boolean channelShouldCheckRpcResponseType = false;

    /**
     * Listener called when a connection gets an IO error trying to write on the socket.
     * Default listener triggers connection recovery asynchronously and propagates
     * the exception.
     * @since 4.5.0
     */
    private ErrorOnWriteListener errorOnWriteListener;

    /**
     * Timeout in ms for work pool enqueuing.
     * @since 4.5.0
     */
    private int workPoolTimeout = DEFAULT_WORK_POOL_TIMEOUT;

    /**
     * Filter to include/exclude entities from topology recovery.
     * @since 4.8.0
     */
    private TopologyRecoveryFilter topologyRecoveryFilter;

    /**
     * Condition to trigger automatic connection recovery.
     * @since 5.4.0
     */
    private Predicate<ShutdownSignalException> connectionRecoveryTriggeringCondition;

    /**
     * Retry handler for topology recovery.
     * Default is no retry.
     * @since 5.4.0
     */
    private RetryHandler topologyRecoveryRetryHandler;

    /**
     * Traffic listener notified of inbound and outbound {@link Command}s.
     * <p>
     * Useful for debugging purposes. Default is no-op.
     *
     * @since 5.5.0
     */
    private TrafficListener trafficListener = TrafficListener.NO_OP;

    private CredentialsRefreshService credentialsRefreshService;

    /** @return the default host to use for connections */
    public String getHost() {
        return host;
    }

    /** @param host the default host to use for connections */
    public void setHost(String host) {
        this.host = host;
    }

    public static int portOrDefault(int port, boolean ssl) {
        if (port != USE_DEFAULT_PORT)
            return port;
        else if (ssl)
            return DEFAULT_AMQP_OVER_SSL_PORT;
        else
            return DEFAULT_AMQP_PORT;
    }

    /** @return the default port to use for connections */
    public int getPort() {
        return portOrDefault(port, isSSL());
    }

    /**
     * Set the target port.
     * @param port the default port to use for connections
     */
    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Retrieve the user name.
     * @return the AMQP user name to use when connecting to the broker
     */
    public String getUsername() {
        return credentialsProvider.getUsername();
    }

    /**
     * Set the user name.
     * @param username the AMQP user name to use when connecting to the broker
     */
    public void setUsername(String username) {
        this.credentialsProvider = new DefaultCredentialsProvider(username, this.credentialsProvider.getPassword());
    }

    /**
     * Retrieve the password.
     * @return the password to use when connecting to the broker
     */
    public String getPassword() {
        return credentialsProvider.getPassword();
    }

    /**
     * Set the password.
     * @param password the password to use when connecting to the broker
     */
    public void setPassword(String password) {
        this.credentialsProvider = new DefaultCredentialsProvider(this.credentialsProvider.getUsername(), password);
    }

    /**
     * Set a custom credentials provider.
     * Default implementation uses static username and password.
     * @param credentialsProvider The custom implementation of CredentialsProvider to use when connecting to the broker.
     * @see com.rabbitmq.client.impl.DefaultCredentialsProvider
     * @since 4.5.0
     */
    public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
        this.credentialsProvider = credentialsProvider;
    }

    /**
     * Retrieve the virtual host.
     * @return the virtual host to use when connecting to the broker
     */
    public String getVirtualHost() {
        return this.virtualHost;
    }

    /**
     * Set the virtual host.
     * @param virtualHost the virtual host to use when connecting to the broker
     */
    public void setVirtualHost(String virtualHost) {
        this.virtualHost = virtualHost;
    }

    /**
     * Convenience method for setting the fields in an AMQP URI: host,
     * port, username, password and virtual host.  If any part of the
     * URI is omitted, the ConnectionFactory's corresponding variable
     * is left unchanged.
     * @param uri is the AMQP URI containing the data
     */
    public void setUri(URI uri) throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
        if ("amqp".equals(uri.getScheme().toLowerCase())) {
            // nothing special to do
        } else if ("amqps".equals(uri.getScheme().toLowerCase())) {
            setPort(DEFAULT_AMQP_OVER_SSL_PORT);
            // SSL context factory not set yet, we use the default one
            if (this.sslContextFactory == null) {
                useSslProtocol();
            }
        } else {
            throw new IllegalArgumentException("Wrong scheme in AMQP URI: " + uri.getScheme());
        }

        String host = uri.getHost();
        if (host != null) {
            setHost(host);
        }

        int port = uri.getPort();
        if (port != -1) {
            setPort(port);
        }

        String userInfo = uri.getRawUserInfo();
        if (userInfo != null) {
            String userPass[] = userInfo.split(":");
            if (userPass.length > 2) {
                throw new IllegalArgumentException("Bad user info in AMQP " + "URI: " + userInfo);
            }

            setUsername(uriDecode(userPass[0]));
            if (userPass.length == 2) {
                setPassword(uriDecode(userPass[1]));
            }
        }

        String path = uri.getRawPath();
        if (path != null && path.length() > 0) {
            if (path.indexOf('/', 1) != -1) {
                throw new IllegalArgumentException("Multiple segments in " + "path of AMQP URI: " + path);
            }

            setVirtualHost(uriDecode(uri.getPath().substring(1)));
        }
    }

    /**
     * Convenience method for setting the fields in an AMQP URI: host,
     * port, username, password and virtual host.  If any part of the
     * URI is omitted, the ConnectionFactory's corresponding variable
     * is left unchanged.  Note that not all valid AMQP URIs are
     * accepted; in particular, the hostname must be given if the
     * port, username or password are given, and escapes in the
     * hostname are not permitted.
     * @param uriString is the AMQP URI containing the data
     */
    public void setUri(String uriString)
            throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
        setUri(new URI(uriString));
    }

    private static String uriDecode(String s) {
        try {
            // URLDecode decodes '+' to a space, as for
            // form encoding.  So protect plus signs.
            return URLDecoder.decode(s.replace("+", "%2B"), "US-ASCII");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Retrieve the requested maximum channel number
     * @return the initially requested maximum channel number; zero for unlimited
     */
    public int getRequestedChannelMax() {
        return this.requestedChannelMax;
    }

    /**
     * Set the requested maximum channel number
     * @param requestedChannelMax initially requested maximum channel number; zero for unlimited
     */
    public void setRequestedChannelMax(int requestedChannelMax) {
        this.requestedChannelMax = requestedChannelMax;
    }

    /**
     * Retrieve the requested maximum frame size
     * @return the initially requested maximum frame size, in octets; zero for unlimited
     */
    public int getRequestedFrameMax() {
        return this.requestedFrameMax;
    }

    /**
     * Set the requested maximum frame size
     * @param requestedFrameMax initially requested maximum frame size, in octets; zero for unlimited
     */
    public void setRequestedFrameMax(int requestedFrameMax) {
        this.requestedFrameMax = requestedFrameMax;
    }

    /**
     * Retrieve the requested heartbeat interval.
     * @return the initially requested heartbeat interval, in seconds; zero for none
     */
    public int getRequestedHeartbeat() {
        return this.requestedHeartbeat;
    }

    /**
     * Set the TCP connection timeout.
     * @param timeout connection TCP establishment timeout in milliseconds; zero for infinite
     */
    public void setConnectionTimeout(int timeout) {
        if (timeout < 0) {
            throw new IllegalArgumentException("TCP connection timeout cannot be negative");
        }
        this.connectionTimeout = timeout;
    }

    /**
     * Retrieve the TCP connection timeout.
     * @return the TCP connection timeout, in milliseconds; zero for infinite
     */
    public int getConnectionTimeout() {
        return this.connectionTimeout;
    }

    /**
     * Retrieve the AMQP 0-9-1 protocol handshake timeout.
     * @return the AMQP0-9-1 protocol handshake timeout, in milliseconds
     */
    public int getHandshakeTimeout() {
        return handshakeTimeout;
    }

    /**
     * Set the AMQP0-9-1 protocol handshake timeout.
     * @param timeout the AMQP0-9-1 protocol handshake timeout, in milliseconds
     */
    public void setHandshakeTimeout(int timeout) {
        if (timeout < 0) {
            throw new IllegalArgumentException("handshake timeout cannot be negative");
        }
        this.handshakeTimeout = timeout;
    }

    /**
     * Set the shutdown timeout. This is the amount of time that Consumer implementations have to
     * continue working through deliveries (and other Consumer callbacks) <b>after</b> the connection
     * has closed but before the ConsumerWorkService is torn down. If consumers exceed this timeout
     * then any remaining queued deliveries (and other Consumer callbacks, <b>including</b>
     * the Consumer's handleShutdownSignal() invocation) will be lost.
     * @param shutdownTimeout shutdown timeout in milliseconds; zero for infinite; default 10000
     */
    public void setShutdownTimeout(int shutdownTimeout) {
        this.shutdownTimeout = shutdownTimeout;
    }

    /**
     * Retrieve the shutdown timeout.
     * @return the shutdown timeout, in milliseconds; zero for infinite
     */
    public int getShutdownTimeout() {
        return shutdownTimeout;
    }

    /**
     * Set the requested heartbeat timeout. Heartbeat frames will be sent at about 1/2 the timeout interval.
     * If server heartbeat timeout is configured to a non-zero value, this method can only be used
     * to lower the value; otherwise any value provided by the client will be used.
     * @param requestedHeartbeat the initially requested heartbeat timeout, in seconds; zero for none
     * @see <a href="https://rabbitmq.com/heartbeats.html">RabbitMQ Heartbeats Guide</a>
     */
    public void setRequestedHeartbeat(int requestedHeartbeat) {
        this.requestedHeartbeat = requestedHeartbeat;
    }

    /**
     * Retrieve the currently-configured table of client properties
     * that will be sent to the server during connection
     * startup. Clients may add, delete, and alter keys in this
     * table. Such changes will take effect when the next new
     * connection is started using this factory.
     * @return the map of client properties
     * @see #setClientProperties
     */
    public Map<String, Object> getClientProperties() {
        return _clientProperties;
    }

    /**
     * Replace the table of client properties that will be sent to the
     * server during subsequent connection startups.
     * @param clientProperties the map of extra client properties
     * @see #getClientProperties
     */
    public void setClientProperties(Map<String, Object> clientProperties) {
        _clientProperties = clientProperties;
    }

    /**
     * Gets the sasl config to use when authenticating
     * @return the sasl config
     * @see com.rabbitmq.client.SaslConfig
     */
    public SaslConfig getSaslConfig() {
        return saslConfig;
    }

    /**
     * Sets the sasl config to use when authenticating
     * @param saslConfig
     * @see com.rabbitmq.client.SaslConfig
     */
    public void setSaslConfig(SaslConfig saslConfig) {
        this.saslConfig = saslConfig;
    }

    /**
     * Retrieve the socket factory used to make connections with.
     */
    public SocketFactory getSocketFactory() {
        return this.socketFactory;
    }

    /**
     * Set the socket factory used to create sockets for new connections. Can be
     * used to customize TLS-related settings by passing in a
     * javax.net.ssl.SSLSocketFactory instance.
     * Note this applies only to blocking IO, not to
     * NIO, as the NIO API doesn't use the SocketFactory API.
     * @see #useSslProtocol
     */
    public void setSocketFactory(SocketFactory factory) {
        this.socketFactory = factory;
    }

    /**
     * Get the socket configurator.
     *
     * @see #setSocketConfigurator(SocketConfigurator)
     */
    public SocketConfigurator getSocketConfigurator() {
        return socketConf;
    }

    /**
     * Set the socket configurator. This gets a chance to "configure" a socket
     * before it has been opened. The default socket configurator disables
     * Nagle's algorithm.
     *
     * @param socketConfigurator the configurator to use
     */
    public void setSocketConfigurator(SocketConfigurator socketConfigurator) {
        this.socketConf = socketConfigurator;
    }

    /**
     * Set the executor to use for consumer operation dispatch
     * by default for newly created connections.
     * All connections that use this executor share it.
     *
     * It's developer's responsibility to shut down the executor
     * when it is no longer needed.
     *
     * @param executor executor service to be used for
     *                 consumer operation
     */
    public void setSharedExecutor(ExecutorService executor) {
        this.sharedExecutor = executor;
    }

    /**
     * Set the executor to use for connection shutdown.
     * All connections that use this executor share it.
     *
     * It's developer's responsibility to shut down the executor
     * when it is no longer needed.
     *
     * @param executor executor service to be used for
     *                 connection shutdown
     */
    public void setShutdownExecutor(ExecutorService executor) {
        this.shutdownExecutor = executor;
    }

    /**
     * Set the executor to use to send heartbeat frames.
     * All connections that use this executor share it.
     *
     * It's developer's responsibility to shut down the executor
     * when it is no longer needed.
     *
     * @param executor executor service to be used to send heartbeat 
     */
    public void setHeartbeatExecutor(ScheduledExecutorService executor) {
        this.heartbeatExecutor = executor;
    }

    /**
     * Retrieve the thread factory used to instantiate new threads.
     * @see ThreadFactory
     */
    public ThreadFactory getThreadFactory() {
        return threadFactory;
    }

    /**
     * Set the thread factory used to instantiate new threads.
     * @see ThreadFactory
     */
    public void setThreadFactory(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
    }

    /**
    * Get the exception handler.
    *
    * @see com.rabbitmq.client.ExceptionHandler
    */
    public ExceptionHandler getExceptionHandler() {
        return exceptionHandler;
    }

    /**
     * Set the exception handler to use for newly created connections.
     * @see com.rabbitmq.client.ExceptionHandler
     */
    public void setExceptionHandler(ExceptionHandler exceptionHandler) {
        if (exceptionHandler == null) {
            throw new IllegalArgumentException("exception handler cannot be null!");
        }
        this.exceptionHandler = exceptionHandler;
    }

    public boolean isSSL() {
        return getSocketFactory() instanceof SSLSocketFactory || sslContextFactory != null;
    }

    /**
     * Convenience method for configuring TLS using
     * the default set of TLS protocols and a trusting TrustManager.
     * This setup is <strong>only suitable for development
     * and QA environments</strong>.
     * The trust manager will <strong>trust every server certificate presented</strong>
     * to it, this is convenient for local development but
     * <strong>not recommended to use in production</strong> as it provides no protection
     * against man-in-the-middle attacks. Prefer {@link #useSslProtocol(SSLContext)}.
     */
    public void useSslProtocol() throws NoSuchAlgorithmException, KeyManagementException {
        useSslProtocol(
                computeDefaultTlsProtocol(SSLContext.getDefault().getSupportedSSLParameters().getProtocols()));
    }

    /**
     * Convenience method for configuring TLS using
     * the supplied protocol and a very trusting TrustManager. This setup is <strong>only suitable for development
     * and QA environments</strong>.
     * The trust manager <strong>will trust every server certificate presented</strong>
     * to it, this is convenient for local development but
     * not recommended to use in production as it <strong>provides no protection
     * against man-in-the-middle attacks</strong>.
     *
     * Use {@link #useSslProtocol(SSLContext)} in production environments.
     * The produced {@link SSLContext} instance will be shared by all
     * the connections created by this connection factory.
     *
     * Use {@link #setSslContextFactory(SslContextFactory)} for more flexibility.
     * @see #setSslContextFactory(SslContextFactory)
     */
    public void useSslProtocol(String protocol) throws NoSuchAlgorithmException, KeyManagementException {
        useSslProtocol(protocol, new TrustEverythingTrustManager());
    }

    /**
     * Convenience method for configuring TLS.
     * Pass in the TLS protocol version to use, e.g. "TLSv1.2" or "TLSv1.1", and
     * a desired {@link TrustManager}.
     *
     *
     * The produced {@link SSLContext} instance will be shared with all
     * the connections created by this connection factory. Use
     * {@link #setSslContextFactory(SslContextFactory)} for more flexibility.
     * @param protocol the TLS protocol to use.
     * @param trustManager the {@link TrustManager} implementation to use.
     * @see #setSslContextFactory(SslContextFactory)
     * @see #useSslProtocol(SSLContext)
     */
    public void useSslProtocol(String protocol, TrustManager trustManager)
            throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext c = SSLContext.getInstance(protocol);
        c.init(null, new TrustManager[] { trustManager }, null);
        useSslProtocol(c);
    }

    /**
     * Sets up TLS with an initialized {@link SSLContext}. The caller is responsible
     * for setting up the context with a {@link TrustManager} with suitable security guarantees,
     * e.g. peer verification.
     *
     *
     * The {@link SSLContext} instance will be shared with all
     * the connections created by this connection factory. Use
     * {@link #setSslContextFactory(SslContextFactory)} for more flexibility.
     * @param context An initialized SSLContext
     * @see #setSslContextFactory(SslContextFactory)
     */
    public void useSslProtocol(SSLContext context) {
        this.sslContextFactory = name -> context;
        setSocketFactory(context.getSocketFactory());
    }

    /**
     * Enable server hostname verification for TLS connections.
     * <p>
     * This enables hostname verification regardless of the IO mode
     * used (blocking or non-blocking IO).
     * <p>
     * This can be called typically after setting the {@link SSLContext}
     * with one of the <code>useSslProtocol</code> methods.
     *
     * @see NioParams#enableHostnameVerification()
     * @see NioParams#setSslEngineConfigurator(SslEngineConfigurator)
     * @see SslEngineConfigurators#ENABLE_HOSTNAME_VERIFICATION
     * @see SocketConfigurators#ENABLE_HOSTNAME_VERIFICATION
     * @see ConnectionFactory#useSslProtocol(String)
     * @see ConnectionFactory#useSslProtocol(SSLContext)
     * @see ConnectionFactory#useSslProtocol()
     * @see ConnectionFactory#useSslProtocol(String, TrustManager)
     * @since 5.4.0
     */
    public void enableHostnameVerification() {
        enableHostnameVerificationForNio();
        enableHostnameVerificationForBlockingIo();
    }

    protected void enableHostnameVerificationForNio() {
        if (this.nioParams == null) {
            this.nioParams = new NioParams();
        }
        this.nioParams = this.nioParams.enableHostnameVerification();
    }

    protected void enableHostnameVerificationForBlockingIo() {
        if (this.socketConf == null) {
            this.socketConf = SocketConfigurators.builder().defaultConfigurator().enableHostnameVerification()
                    .build();
        } else {
            this.socketConf = this.socketConf.andThen(SocketConfigurators.enableHostnameVerification());
        }
    }

    public static String computeDefaultTlsProtocol(String[] supportedProtocols) {
        if (supportedProtocols != null) {
            for (String supportedProtocol : supportedProtocols) {
                if (PREFERRED_TLS_PROTOCOL.equalsIgnoreCase(supportedProtocol)) {
                    return supportedProtocol;
                }
            }
        }
        return FALLBACK_TLS_PROTOCOL;
    }

    /**
     * Returns true if <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, false otherwise
     * @return true if automatic connection recovery is enabled, false otherwise
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public boolean isAutomaticRecoveryEnabled() {
        return automaticRecovery;
    }

    /**
     * Enables or disables <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>.
     * @param automaticRecovery if true, enables connection recovery
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public void setAutomaticRecoveryEnabled(boolean automaticRecovery) {
        this.automaticRecovery = automaticRecovery;
    }

    /**
     * Returns true if topology recovery is enabled, false otherwise
     * @return true if topology recovery is enabled, false otherwise
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public boolean isTopologyRecoveryEnabled() {
        return topologyRecovery;
    }

    /**
     * Enables or disables topology recovery
     * @param topologyRecovery if true, enables topology recovery
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public void setTopologyRecoveryEnabled(boolean topologyRecovery) {
        this.topologyRecovery = topologyRecovery;
    }

    /**
     * Get the executor to use for parallel topology recovery. If null (the default), recovery is done single threaded on the main connection thread.
     * @return thread pool executor
     * @since 4.7.0
     */
    public ExecutorService getTopologyRecoveryExecutor() {
        return topologyRecoveryExecutor;
    }

    /**
     * Set the executor to use for parallel topology recovery. If null (the default), recovery is done single threaded on the main connection thread.
     * It is recommended to pass a ThreadPoolExecutor that will allow its core threads to timeout so these threads can die when recovery is complete.
     * It's developer's responsibility to shut down the executor when it is no longer needed.
     * Note: your {@link ExceptionHandler#handleTopologyRecoveryException(Connection, Channel, TopologyRecoveryException)} method should be thread-safe.
     * @param topologyRecoveryExecutor thread pool executor
     * @since 4.7.0
     */
    public void setTopologyRecoveryExecutor(final ExecutorService topologyRecoveryExecutor) {
        this.topologyRecoveryExecutor = topologyRecoveryExecutor;
    }

    public void setMetricsCollector(MetricsCollector metricsCollector) {
        this.metricsCollector = metricsCollector;
    }

    public MetricsCollector getMetricsCollector() {
        return metricsCollector;
    }

    /**
     * Set a {@link CredentialsRefreshService} instance to handle credentials refresh if appropriate.
     * <p>
     * Each created connection will register to the refresh service to send an AMQP <code>update.secret</code>
     * frame when credentials are about to expire. This is the refresh service responsibility to schedule
     * credentials refresh and <code>udpate.secret</code> frame sending, based on the information provided
     * by the {@link CredentialsProvider}.
     * <p>
     * Note the {@link CredentialsRefreshService} is used only when the {@link CredentialsProvider}
     * signals credentials can expire, by returning a non-null value from {@link CredentialsProvider#getTimeBeforeExpiration()}.
     *
     * @param credentialsRefreshService the refresh service to use
     * @see #setCredentialsProvider(CredentialsProvider)
     * @see DefaultCredentialsRefreshService
     */
    public void setCredentialsRefreshService(CredentialsRefreshService credentialsRefreshService) {
        this.credentialsRefreshService = credentialsRefreshService;
    }

    protected synchronized FrameHandlerFactory createFrameHandlerFactory() throws IOException {
        if (nio) {
            if (this.frameHandlerFactory == null) {
                if (this.nioParams.getNioExecutor() == null && this.nioParams.getThreadFactory() == null) {
                    this.nioParams.setThreadFactory(getThreadFactory());
                }
                this.frameHandlerFactory = new SocketChannelFrameHandlerFactory(connectionTimeout, nioParams,
                        isSSL(), sslContextFactory);
            }
            return this.frameHandlerFactory;
        } else {
            return new SocketFrameHandlerFactory(connectionTimeout, socketFactory, socketConf, isSSL(),
                    this.shutdownExecutor, sslContextFactory);
        }

    }

    /**
     * Create a new broker connection, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param addrs an array of known broker addresses (hostname/port pairs) to try in order
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection(Address[] addrs) throws IOException, TimeoutException {
        return newConnection(this.sharedExecutor, Arrays.asList(addrs), null);
    }

    /**
     * Create a new broker connection, picking the first available address from
     * the list provided by the {@link AddressResolver}.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address provided by the {@link AddressResolver}.
     *
     * @param addressResolver discovery service to list potential addresses (hostname/port pairs) to connect to
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public Connection newConnection(AddressResolver addressResolver) throws IOException, TimeoutException {
        return newConnection(this.sharedExecutor, addressResolver, null);
    }

    /**
     * Create a new broker connection with a client-provided name, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param addrs an array of known broker addresses (hostname/port pairs) to try in order
     * @param clientProvidedName application-specific connection name, will be displayed
     *                           in the management UI if RabbitMQ server supports it.
     *                           This value doesn't have to be unique and cannot be used
     *                           as a connection identifier e.g. in HTTP API requests.
     *                           This value is supposed to be human-readable.
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection(Address[] addrs, String clientProvidedName)
            throws IOException, TimeoutException {
        return newConnection(this.sharedExecutor, Arrays.asList(addrs), clientProvidedName);
    }

    /**
     * Create a new broker connection, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param addrs a List of known broker addresses (hostname/port pairs) to try in order
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection(List<Address> addrs) throws IOException, TimeoutException {
        return newConnection(this.sharedExecutor, addrs, null);
    }

    /**
     * Create a new broker connection with a client-provided name, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param addrs a List of known broker addresses (hostname/port pairs) to try in order
     * @param clientProvidedName application-specific connection name, will be displayed
     *                           in the management UI if RabbitMQ server supports it.
     *                           This value doesn't have to be unique and cannot be used
     *                           as a connection identifier e.g. in HTTP API requests.
     *                           This value is supposed to be human-readable.
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection(List<Address> addrs, String clientProvidedName)
            throws IOException, TimeoutException {
        return newConnection(this.sharedExecutor, addrs, clientProvidedName);
    }

    /**
     * Create a new broker connection, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param executor thread execution service for consumers on the connection
     * @param addrs an array of known broker addresses (hostname/port pairs) to try in order
     * @return an interface to the connection
     * @throws java.io.IOException if it encounters a problem
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public Connection newConnection(ExecutorService executor, Address[] addrs)
            throws IOException, TimeoutException {
        return newConnection(executor, Arrays.asList(addrs), null);
    }

    /**
     * Create a new broker connection with a client-provided name, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param executor thread execution service for consumers on the connection
     * @param addrs an array of known broker addresses (hostname/port pairs) to try in order
     * @param clientProvidedName application-specific connection name, will be displayed
     *                           in the management UI if RabbitMQ server supports it.
     *                           This value doesn't have to be unique and cannot be used
     *                           as a connection identifier e.g. in HTTP API requests.
     *                           This value is supposed to be human-readable.
     * @return an interface to the connection
     * @throws java.io.IOException if it encounters a problem
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public Connection newConnection(ExecutorService executor, Address[] addrs, String clientProvidedName)
            throws IOException, TimeoutException {
        return newConnection(executor, Arrays.asList(addrs), clientProvidedName);
    }

    /**
     * Create a new broker connection, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param executor thread execution service for consumers on the connection
     * @param addrs a List of known broker addrs (hostname/port pairs) to try in order
     * @return an interface to the connection
     * @throws java.io.IOException if it encounters a problem
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public Connection newConnection(ExecutorService executor, List<Address> addrs)
            throws IOException, TimeoutException {
        return newConnection(executor, addrs, null);
    }

    /**
     * Create a new broker connection, picking the first available address from
     * the list provided by the {@link AddressResolver}.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address provided by the {@link AddressResolver}.
     *
     * @param executor thread execution service for consumers on the connection
     * @param addressResolver discovery service to list potential addresses (hostname/port pairs) to connect to
     * @return an interface to the connection
     * @throws java.io.IOException if it encounters a problem
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public Connection newConnection(ExecutorService executor, AddressResolver addressResolver)
            throws IOException, TimeoutException {
        return newConnection(executor, addressResolver, null);
    }

    /**
     * Create a new broker connection with a client-provided name, picking the first available address from
     * the list.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address from the provided list.
     *
     * @param executor thread execution service for consumers on the connection
     * @param addrs a List of known broker addrs (hostname/port pairs) to try in order
     * @param clientProvidedName application-specific connection name, will be displayed
     *                           in the management UI if RabbitMQ server supports it.
     *                           This value doesn't have to be unique and cannot be used
     *                           as a connection identifier e.g. in HTTP API requests.
     *                           This value is supposed to be human-readable.
     * @return an interface to the connection
     * @throws java.io.IOException if it encounters a problem
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public Connection newConnection(ExecutorService executor, List<Address> addrs, String clientProvidedName)
            throws IOException, TimeoutException {
        return newConnection(executor, createAddressResolver(addrs), clientProvidedName);
    }

    /**
     * Create a new broker connection with a client-provided name, picking the first available address from
     * the list provided by the {@link AddressResolver}.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Future
     * reconnection attempts will pick a random accessible address provided by the {@link AddressResolver}.
     *
     * @param executor thread execution service for consumers on the connection
     * @param addressResolver discovery service to list potential addresses (hostname/port pairs) to connect to
     * @param clientProvidedName application-specific connection name, will be displayed
     *                           in the management UI if RabbitMQ server supports it.
     *                           This value doesn't have to be unique and cannot be used
     *                           as a connection identifier e.g. in HTTP API requests.
     *                           This value is supposed to be human-readable.
     * @return an interface to the connection
     * @throws java.io.IOException if it encounters a problem
     * @see <a href="https://www.rabbitmq.com/api-guide.html#recovery">Automatic Recovery</a>
     */
    public Connection newConnection(ExecutorService executor, AddressResolver addressResolver,
            String clientProvidedName) throws IOException, TimeoutException {
        if (this.metricsCollector == null) {
            this.metricsCollector = new NoOpMetricsCollector();
        }
        // make sure we respect the provided thread factory
        FrameHandlerFactory fhFactory = createFrameHandlerFactory();
        ConnectionParams params = params(executor);
        // set client-provided via a client property
        if (clientProvidedName != null) {
            Map<String, Object> properties = new HashMap<String, Object>(params.getClientProperties());
            properties.put("connection_name", clientProvidedName);
            params.setClientProperties(properties);
        }

        if (isAutomaticRecoveryEnabled()) {
            // see com.rabbitmq.client.impl.recovery.RecoveryAwareAMQConnectionFactory#newConnection
            // No Sonar: no need to close this resource because we're the one that creates it
            // and hands it over to the user
            AutorecoveringConnection conn = new AutorecoveringConnection(params, fhFactory, addressResolver,
                    metricsCollector); //NOSONAR

            conn.init();
            return conn;
        } else {
            List<Address> addrs = addressResolver.getAddresses();
            Exception lastException = null;
            for (Address addr : addrs) {
                try {
                    FrameHandler handler = fhFactory.create(addr, clientProvidedName);
                    AMQConnection conn = createConnection(params, handler, metricsCollector);
                    conn.start();
                    this.metricsCollector.newConnection(conn);
                    return conn;
                } catch (IOException e) {
                    lastException = e;
                } catch (TimeoutException te) {
                    lastException = te;
                }
            }
            if (lastException != null) {
                if (lastException instanceof IOException) {
                    throw (IOException) lastException;
                } else if (lastException instanceof TimeoutException) {
                    throw (TimeoutException) lastException;
                }
            }
            throw new IOException("failed to connect");
        }
    }

    public ConnectionParams params(ExecutorService consumerWorkServiceExecutor) {
        ConnectionParams result = new ConnectionParams();

        result.setCredentialsProvider(credentialsProvider);
        result.setConsumerWorkServiceExecutor(consumerWorkServiceExecutor);
        result.setVirtualHost(virtualHost);
        result.setClientProperties(getClientProperties());
        result.setRequestedFrameMax(requestedFrameMax);
        result.setRequestedChannelMax(requestedChannelMax);
        result.setShutdownTimeout(shutdownTimeout);
        result.setSaslConfig(saslConfig);
        result.setNetworkRecoveryInterval(networkRecoveryInterval);
        result.setRecoveryDelayHandler(recoveryDelayHandler);
        result.setTopologyRecovery(topologyRecovery);
        result.setTopologyRecoveryExecutor(topologyRecoveryExecutor);
        result.setExceptionHandler(exceptionHandler);
        result.setThreadFactory(threadFactory);
        result.setHandshakeTimeout(handshakeTimeout);
        result.setRequestedHeartbeat(requestedHeartbeat);
        result.setShutdownExecutor(shutdownExecutor);
        result.setHeartbeatExecutor(heartbeatExecutor);
        result.setChannelRpcTimeout(channelRpcTimeout);
        result.setChannelShouldCheckRpcResponseType(channelShouldCheckRpcResponseType);
        result.setWorkPoolTimeout(workPoolTimeout);
        result.setErrorOnWriteListener(errorOnWriteListener);
        result.setTopologyRecoveryFilter(topologyRecoveryFilter);
        result.setConnectionRecoveryTriggeringCondition(connectionRecoveryTriggeringCondition);
        result.setTopologyRecoveryRetryHandler(topologyRecoveryRetryHandler);
        result.setTrafficListener(trafficListener);
        result.setCredentialsRefreshService(credentialsRefreshService);
        return result;
    }

    protected AMQConnection createConnection(ConnectionParams params, FrameHandler frameHandler,
            MetricsCollector metricsCollector) {
        return new AMQConnection(params, frameHandler, metricsCollector);
    }

    /**
     * Create a new broker connection.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Reconnection
     * attempts will always use the address configured on {@link ConnectionFactory}.
     *
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection() throws IOException, TimeoutException {
        return newConnection(this.sharedExecutor, Collections.singletonList(new Address(getHost(), getPort())));
    }

    /**
     * Create a new broker connection.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Reconnection
     * attempts will always use the address configured on {@link ConnectionFactory}.
     *
     * @param connectionName client-provided connection name (an arbitrary string). Will
     *                       be displayed in management UI if the server supports it.
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection(String connectionName) throws IOException, TimeoutException {
        return newConnection(this.sharedExecutor, Collections.singletonList(new Address(getHost(), getPort())),
                connectionName);
    }

    /**
     * Create a new broker connection.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Reconnection
     * attempts will always use the address configured on {@link ConnectionFactory}.
     *
     * @param executor thread execution service for consumers on the connection
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection(ExecutorService executor) throws IOException, TimeoutException {
        return newConnection(executor, Collections.singletonList(new Address(getHost(), getPort())));
    }

    /**
     * Create a new broker connection.
     *
     * If <a href="https://www.rabbitmq.com/api-guide.html#recovery">automatic connection recovery</a>
     * is enabled, the connection returned by this method will be {@link Recoverable}. Reconnection
     * attempts will always use the address configured on {@link ConnectionFactory}.
     *
     * @param executor thread execution service for consumers on the connection
     * @param connectionName client-provided connection name (an arbitrary string). Will
     *                       be displayed in management UI if the server supports it.
     * @return an interface to the connection
     * @throws IOException if it encounters a problem
     */
    public Connection newConnection(ExecutorService executor, String connectionName)
            throws IOException, TimeoutException {
        return newConnection(executor, Collections.singletonList(new Address(getHost(), getPort())),
                connectionName);
    }

    protected AddressResolver createAddressResolver(List<Address> addresses) {
        return new ListAddressResolver(addresses);
    }

    @Override
    public ConnectionFactory clone() {
        try {
            ConnectionFactory clone = (ConnectionFactory) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Load settings from a property file.
     * Keys must be prefixed with <code>rabbitmq.</code>,
     * use {@link ConnectionFactory#load(String, String)} to
     * specify your own prefix.
     * @param propertyFileLocation location of the property file to use
     * @throws IOException when something goes wrong reading the file
     * @since 4.4.0
     * @see ConnectionFactoryConfigurator
     */
    public ConnectionFactory load(String propertyFileLocation) throws IOException {
        ConnectionFactoryConfigurator.load(this, propertyFileLocation);
        return this;
    }

    /**
     * Load settings from a property file.
     * @param propertyFileLocation location of the property file to use
     * @param prefix key prefix for the entries in the file
     * @throws IOException when something goes wrong reading the file
     * @since 4.4.0
     * @see ConnectionFactoryConfigurator
     */
    public ConnectionFactory load(String propertyFileLocation, String prefix) throws IOException {
        ConnectionFactoryConfigurator.load(this, propertyFileLocation, prefix);
        return this;
    }

    /**
     * Load settings from a {@link Properties} instance.
     * Keys must be prefixed with <code>rabbitmq.</code>,
     * use {@link ConnectionFactory#load(Properties, String)} to
     * specify your own prefix.
     * @param properties source for settings
     * @since 4.4.0
     * @see ConnectionFactoryConfigurator
     */
    public ConnectionFactory load(Properties properties) {
        ConnectionFactoryConfigurator.load(this, properties);
        return this;
    }

    /**
     * Load settings from a {@link Properties} instance.
     * @param properties source for settings
     * @param prefix key prefix for properties entries
     * @since 4.4.0
     * @see ConnectionFactoryConfigurator
     */
    @SuppressWarnings("unchecked")
    public ConnectionFactory load(Properties properties, String prefix) {
        ConnectionFactoryConfigurator.load(this, (Map) properties, prefix);
        return this;
    }

    /**
     * Load settings from a {@link Map} instance.
     * Keys must be prefixed with <code>rabbitmq.</code>,
     * use {@link ConnectionFactory#load(Map, String)} to
     * specify your own prefix.
     * @param properties source for settings
     * @since 4.4.0
     * @see ConnectionFactoryConfigurator
     */
    public ConnectionFactory load(Map<String, String> properties) {
        ConnectionFactoryConfigurator.load(this, properties);
        return this;
    }

    /**
     * Load settings from a {@link Map} instance.
     * @param properties source for settings
     * @param prefix key prefix for map entries
     * @since 4.4.0
     * @see ConnectionFactoryConfigurator
     */
    public ConnectionFactory load(Map<String, String> properties, String prefix) {
        ConnectionFactoryConfigurator.load(this, properties, prefix);
        return this;
    }

    /**
     * Returns automatic connection recovery interval in milliseconds.
     * @return how long will automatic recovery wait before attempting to reconnect, in ms; default is 5000
     */
    public long getNetworkRecoveryInterval() {
        return networkRecoveryInterval;
    }

    /**
     * Sets connection recovery interval. Default is 5000.
     * Uses {@link com.rabbitmq.client.RecoveryDelayHandler.DefaultRecoveryDelayHandler} by default.
     * Use another {@link RecoveryDelayHandler} implementation for more flexibility.
     * @param networkRecoveryInterval how long will automatic recovery wait before attempting to reconnect, in ms
     * @see RecoveryDelayHandler
     */
    public void setNetworkRecoveryInterval(int networkRecoveryInterval) {
        this.networkRecoveryInterval = networkRecoveryInterval;
    }

    /**
     * Sets connection recovery interval. Default is 5000.
     * Uses {@link com.rabbitmq.client.RecoveryDelayHandler.DefaultRecoveryDelayHandler} by default.
     * Use another {@link RecoveryDelayHandler} implementation for more flexibility.
     * @param networkRecoveryInterval how long will automatic recovery wait before attempting to reconnect, in ms
     * @see RecoveryDelayHandler
     */
    public void setNetworkRecoveryInterval(long networkRecoveryInterval) {
        this.networkRecoveryInterval = networkRecoveryInterval;
    }

    /**
     * Returns automatic connection recovery delay handler.
     * @return recovery delay handler. May be null if not set.
     * @since 4.3.0
     */
    public RecoveryDelayHandler getRecoveryDelayHandler() {
        return recoveryDelayHandler;
    }

    /**
     * Sets the automatic connection recovery delay handler.
     * @param recoveryDelayHandler the recovery delay handler
     * @since 4.3.0
     */
    public void setRecoveryDelayHandler(final RecoveryDelayHandler recoveryDelayHandler) {
        this.recoveryDelayHandler = recoveryDelayHandler;
    }

    /**
     * Sets the parameters when using NIO.
     *
     *
     * @param nioParams
     * @see NioParams
     */
    public void setNioParams(NioParams nioParams) {
        this.nioParams = nioParams;
    }

    /**
     * Retrieve the parameters for NIO mode.
     * @return
     */
    public NioParams getNioParams() {
        return nioParams;
    }

    /**
     * Use non-blocking IO (NIO) for communication with the server.
     * With NIO, several connections created from the same {@link ConnectionFactory}
     * can use the same IO thread.
     *
     * A client process using a lot of not-so-active connections can benefit
     * from NIO, as it would use fewer threads than with the traditional, blocking IO mode.
     *
     * Use {@link NioParams} to tune NIO and a {@link SocketChannelConfigurator} to
     * configure the underlying {@link java.nio.channels.SocketChannel}s for connections.
     *
     * @see NioParams
     * @see SocketChannelConfigurator
     * @see java.nio.channels.SocketChannel
     * @see java.nio.channels.Selector
     */
    public void useNio() {
        this.nio = true;
    }

    /**
     * Use blocking IO for communication with the server.
     * With blocking IO, each connection creates its own thread
     * to read data from the server.
     */
    public void useBlockingIo() {
        this.nio = false;
    }

    /**
     * Set the continuation timeout for RPC calls in channels.
     * Default is 10 minutes. 0 means no timeout.
     * @param channelRpcTimeout
     */
    public void setChannelRpcTimeout(int channelRpcTimeout) {
        if (channelRpcTimeout < 0) {
            throw new IllegalArgumentException("Timeout cannot be less than 0");
        }
        this.channelRpcTimeout = channelRpcTimeout;
    }

    /**
     * Get the timeout for RPC calls in channels.
     * @return
     */
    public int getChannelRpcTimeout() {
        return channelRpcTimeout;
    }

    /**
     * The factory to create SSL contexts.
     * This provides more flexibility to create {@link SSLContext}s
     * for different connections than sharing the {@link SSLContext}
     * with all the connections produced by the connection factory
     * (which is the case with the {@link #useSslProtocol()} methods).
     * This way, different connections with a different certificate
     * for each of them is a possible scenario.
     * @param sslContextFactory
     * @see #useSslProtocol(SSLContext)
     * @since 5.0.0
     */
    public void setSslContextFactory(SslContextFactory sslContextFactory) {
        this.sslContextFactory = sslContextFactory;
    }

    /**
     * When set to true, channels will check the response type (e.g. queue.declare
     * expects a queue.declare-ok response) of RPC calls
     * and ignore those that do not match.
     * Default is false.
     * @param channelShouldCheckRpcResponseType
     */
    public void setChannelShouldCheckRpcResponseType(boolean channelShouldCheckRpcResponseType) {
        this.channelShouldCheckRpcResponseType = channelShouldCheckRpcResponseType;
    }

    public boolean isChannelShouldCheckRpcResponseType() {
        return channelShouldCheckRpcResponseType;
    }

    /**
     * Timeout (in ms) for work pool enqueueing.
     * The {@link com.rabbitmq.client.impl.WorkPool} dispatches several types of responses
     * from the broker (e.g. deliveries). A high-traffic
     * client with slow consumers can exhaust the work pool and
     * compromise the whole connection (by e.g. letting the broker
     * saturate the receive TCP buffers). Setting a timeout
     * would make the connection fail early and avoid hard-to-diagnose
     * TCP connection failure. Note this shouldn't happen
     * with clients that set appropriate QoS values.
     * Default is no timeout.
     *
     * @param workPoolTimeout timeout in ms
     * @since 4.5.0
     */
    public void setWorkPoolTimeout(int workPoolTimeout) {
        this.workPoolTimeout = workPoolTimeout;
    }

    public int getWorkPoolTimeout() {
        return workPoolTimeout;
    }

    /**
     * Set a listener to be called when connection gets an IO error trying to write on the socket.
     * Default listener triggers connection recovery asynchronously and propagates
     * the exception. Override the default listener to disable or
     * customise automatic connection triggering on write operations.
     *
     * @param errorOnWriteListener the listener
     * @since 4.5.0
     */
    public void setErrorOnWriteListener(ErrorOnWriteListener errorOnWriteListener) {
        this.errorOnWriteListener = errorOnWriteListener;
    }

    /**
     * Set filter to include/exclude entities from topology recovery.
     *
     * @since 4.8.0
     */
    public void setTopologyRecoveryFilter(TopologyRecoveryFilter topologyRecoveryFilter) {
        this.topologyRecoveryFilter = topologyRecoveryFilter;
    }

    /**
     * Allows to decide on automatic connection recovery is triggered.
     * Default is for shutdown not initiated by application or missed heartbeat errors.
     *
     * @param connectionRecoveryTriggeringCondition
     */
    public void setConnectionRecoveryTriggeringCondition(
            Predicate<ShutdownSignalException> connectionRecoveryTriggeringCondition) {
        this.connectionRecoveryTriggeringCondition = connectionRecoveryTriggeringCondition;
    }

    /**
     * Set retry handler for topology recovery.
     * Default is no retry.
     *
     * @param topologyRecoveryRetryHandler
     * @since 5.4.0
     */
    public void setTopologyRecoveryRetryHandler(RetryHandler topologyRecoveryRetryHandler) {
        this.topologyRecoveryRetryHandler = topologyRecoveryRetryHandler;
    }

    /**
     * Traffic listener notified of inbound and outbound {@link Command}s.
     * <p>
     * Useful for debugging purposes, e.g. logging all sent and received messages.
     * Default is no-op.
     *
     * @param trafficListener
     * @see TrafficListener
     * @see com.rabbitmq.client.impl.LogTrafficListener
     * @since 5.5.0
     */
    public void setTrafficListener(TrafficListener trafficListener) {
        this.trafficListener = trafficListener;
    }
}