private void close(boolean shutdownEventLoopGroup) {
    ServerChannel serverChannel = this.serverChannel.get();
    if (serverChannel == null) {
      LOG.assertTrue(clientChannels.isEmpty());
      return;
    }
    else if (!this.serverChannel.compareAndSet(serverChannel, null)) {
      return;
    }

    EventLoopGroup eventLoopGroup = shutdownEventLoopGroup ? serverChannel.eventLoop().parent() : null;
    try {
      long start = System.currentTimeMillis();
      Channel[] clientChannels = this.clientChannels.toArray(new Channel[]{});
      this.clientChannels.clear();

      final CountDownLatch countDown = new CountDownLatch(clientChannels.length + 1);
      GenericFutureListener<ChannelFuture> listener = new GenericFutureListener<>() {
        @Override
        public void operationComplete(@NotNull ChannelFuture future) {
          try {
            Throwable cause = future.cause();
            if (cause != null) {
              LOG.warn(cause);
            }
          }
          finally {
            countDown.countDown();
          }
        }
      };

      addCloseListener(serverChannel, countDown, listener);
      for (Channel channel : clientChannels) {
        addCloseListener(channel, countDown, listener);
      }

      try {
        countDown.await(5, TimeUnit.SECONDS);
      }
      catch (InterruptedException e) {
        LOG.warn("Cannot close all channels for 10 seconds, channels: " + Arrays.toString(clientChannels));
      }

      long duration = System.currentTimeMillis() - start;
      if (duration > 1000) {
        LOG.info("Close all channels took " + duration + " ms: " + (duration / 60000) + " min " + ((duration % 60000) / 1000) + "sec");
      }
    }
    finally {
      if (eventLoopGroup != null) {
        eventLoopGroup.shutdownGracefully(1, 2, TimeUnit.NANOSECONDS);
      }
    }
  }
private void close(boolean shutdownEventLoopGroup)