@Override
 public final Connection createConnection() throws AmqpException {
   Assert.state(
       !this.stopped,
       "The ApplicationContext is closed and the ConnectionFactory can no longer create connections.");
   synchronized (this.connectionMonitor) {
     if (this.cacheMode == CacheMode.CHANNEL) {
       if (this.connection == null) {
         this.connection = new ChannelCachingConnectionProxy(super.createBareConnection());
         // invoke the listener *after* this.connection is assigned
         getConnectionListener().onCreate(connection);
         this.checkoutPermits.put(this.connection, new Semaphore(this.channelCacheSize));
       }
       return this.connection;
     } else if (this.cacheMode == CacheMode.CONNECTION) {
       ChannelCachingConnectionProxy connection = null;
       while (connection == null && !this.idleConnections.isEmpty()) {
         connection = this.idleConnections.poll();
         if (connection != null) {
           if (!connection.isOpen()) {
             if (logger.isDebugEnabled()) {
               logger.debug("Removing closed connection '" + connection + "'");
             }
             connection.notifyCloseIfNecessary();
             this.openConnections.remove(connection);
             this.openConnectionNonTransactionalChannels.remove(connection);
             this.openConnectionTransactionalChannels.remove(connection);
             this.checkoutPermits.remove(connection);
             connection = null;
           }
         }
       }
       if (connection == null) {
         connection = new ChannelCachingConnectionProxy(super.createBareConnection());
         getConnectionListener().onCreate(connection);
         if (logger.isDebugEnabled()) {
           logger.debug("Adding new connection '" + connection + "'");
         }
         this.openConnections.add(connection);
         this.openConnectionNonTransactionalChannels.put(
             connection, new LinkedList<ChannelProxy>());
         this.openConnectionTransactionalChannels.put(connection, new LinkedList<ChannelProxy>());
         this.checkoutPermits.put(connection, new Semaphore(this.channelCacheSize));
       } else {
         if (logger.isDebugEnabled()) {
           logger.debug("Obtained connection '" + connection + "' from cache");
         }
       }
       return connection;
     }
   }
   return null;
 }
 private Channel createBareChannel(
     ChannelCachingConnectionProxy connection, boolean transactional) {
   if (this.cacheMode == CacheMode.CHANNEL) {
     if (this.connection == null || !this.connection.isOpen()) {
       synchronized (this.connectionMonitor) {
         if (this.connection != null && !this.connection.isOpen()) {
           this.connection.notifyCloseIfNecessary();
           this.checkoutPermits.remove(this.connection);
         }
         if (this.connection == null || !this.connection.isOpen()) {
           this.connection = null;
           createConnection();
         }
       }
     }
     return doCreateBareChannel(this.connection, transactional);
   } else if (this.cacheMode == CacheMode.CONNECTION) {
     if (!connection.isOpen()) {
       synchronized (connectionMonitor) {
         this.openConnectionNonTransactionalChannels.get(connection).clear();
         this.openConnectionTransactionalChannels.get(connection).clear();
         connection.notifyCloseIfNecessary();
         ChannelCachingConnectionProxy newConnection =
             (ChannelCachingConnectionProxy) createConnection();
         /*
          * Applications already have a reference to the proxy, so we steal the new (or idle) connection's
          * target and remove the connection from the open list.
          */
         connection.target = newConnection.target;
         connection.closeNotified.set(false);
         this.openConnections.remove(newConnection);
       }
     }
     return doCreateBareChannel(connection, transactional);
   }
   return null;
 }