public void testCustomThreadPool() throws Exception {
    ThreadPool threadPool = null;
    try {
      Settings nodeSettings =
          Settings.builder()
              .put("threadpool.my_pool1.type", "scaling")
              .put("threadpool.my_pool1.min", 1)
              .put(
                  "threadpool.my_pool1.size", EsExecutors.boundedNumberOfProcessors(Settings.EMPTY))
              .put("threadpool.my_pool1.keep_alive", "1m")
              .put("threadpool.my_pool2.type", "fixed")
              .put("threadpool.my_pool2.size", "1")
              .put("threadpool.my_pool2.queue_size", "1")
              .put("node.name", "testCustomThreadPool")
              .build();
      threadPool = new ThreadPool(nodeSettings);
      ClusterSettings clusterSettings =
          new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
      threadPool.setClusterSettings(clusterSettings);
      ThreadPoolInfo groups = threadPool.info();
      boolean foundPool1 = false;
      boolean foundPool2 = false;
      outer:
      for (ThreadPool.Info info : groups) {
        if ("my_pool1".equals(info.getName())) {
          foundPool1 = true;
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.SCALING);
        } else if ("my_pool2".equals(info.getName())) {
          foundPool2 = true;
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.FIXED);
          assertThat(info.getMin(), equalTo(1));
          assertThat(info.getMax(), equalTo(1));
          assertThat(info.getQueueSize().singles(), equalTo(1L));
        } else {
          for (Field field : Names.class.getFields()) {
            if (info.getName().equalsIgnoreCase(field.getName())) {
              // This is ok it is a default thread pool
              continue outer;
            }
          }
          fail("Unexpected pool name: " + info.getName());
        }
      }
      assertThat(foundPool1, is(true));
      assertThat(foundPool2, is(true));

      // Updating my_pool2
      Settings settings = Settings.builder().put("threadpool.my_pool2.size", "10").build();
      clusterSettings.applySettings(settings);

      groups = threadPool.info();
      foundPool1 = false;
      foundPool2 = false;
      outer:
      for (ThreadPool.Info info : groups) {
        if ("my_pool1".equals(info.getName())) {
          foundPool1 = true;
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.SCALING);
        } else if ("my_pool2".equals(info.getName())) {
          foundPool2 = true;
          assertThat(info.getMax(), equalTo(10));
          assertThat(info.getMin(), equalTo(10));
          assertThat(info.getQueueSize().singles(), equalTo(1L));
          assertEquals(info.getThreadPoolType(), ThreadPool.ThreadPoolType.FIXED);
        } else {
          for (Field field : Names.class.getFields()) {
            if (info.getName().equalsIgnoreCase(field.getName())) {
              // This is ok it is a default thread pool
              continue outer;
            }
          }
          fail("Unexpected pool name: " + info.getName());
        }
      }
      assertThat(foundPool1, is(true));
      assertThat(foundPool2, is(true));
    } finally {
      terminateThreadPoolIfNeeded(threadPool);
    }
  }
  private Table buildTable(
      RestRequest req,
      ClusterStateResponse state,
      NodesInfoResponse nodesInfo,
      NodesStatsResponse nodesStats) {
    final String[] threadPools = req.paramAsStringArray("thread_pool_patterns", new String[] {"*"});
    final DiscoveryNodes nodes = state.getState().nodes();
    final Table table = getTableWithHeader(req);

    // collect all thread pool names that we see across the nodes
    final Set<String> candidates = new HashSet<>();
    for (final NodeStats nodeStats : nodesStats.getNodes()) {
      for (final ThreadPoolStats.Stats threadPoolStats : nodeStats.getThreadPool()) {
        candidates.add(threadPoolStats.getName());
      }
    }

    // collect all thread pool names that match the specified thread pool patterns
    final Set<String> included = new HashSet<>();
    for (final String candidate : candidates) {
      if (Regex.simpleMatch(threadPools, candidate)) {
        included.add(candidate);
      }
    }

    for (final DiscoveryNode node : nodes) {
      final NodeInfo info = nodesInfo.getNodesMap().get(node.getId());
      final NodeStats stats = nodesStats.getNodesMap().get(node.getId());

      final Map<String, ThreadPoolStats.Stats> poolThreadStats;
      final Map<String, ThreadPool.Info> poolThreadInfo;

      if (stats == null) {
        poolThreadStats = Collections.emptyMap();
        poolThreadInfo = Collections.emptyMap();
      } else {
        // we use a sorted map to ensure that thread pools are sorted by name
        poolThreadStats = new TreeMap<>();
        poolThreadInfo = new HashMap<>();

        ThreadPoolStats threadPoolStats = stats.getThreadPool();
        for (ThreadPoolStats.Stats threadPoolStat : threadPoolStats) {
          poolThreadStats.put(threadPoolStat.getName(), threadPoolStat);
        }
        if (info != null) {
          for (ThreadPool.Info threadPoolInfo : info.getThreadPool()) {
            poolThreadInfo.put(threadPoolInfo.getName(), threadPoolInfo);
          }
        }
      }
      for (Map.Entry<String, ThreadPoolStats.Stats> entry : poolThreadStats.entrySet()) {

        if (!included.contains(entry.getKey())) continue;

        table.startRow();

        table.addCell(node.getName());
        table.addCell(node.getId());
        table.addCell(node.getEphemeralId());
        table.addCell(info == null ? null : info.getProcess().getId());
        table.addCell(node.getHostName());
        table.addCell(node.getHostAddress());
        table.addCell(node.getAddress().address().getPort());
        final ThreadPoolStats.Stats poolStats = entry.getValue();
        final ThreadPool.Info poolInfo = poolThreadInfo.get(entry.getKey());

        Long maxQueueSize = null;
        String keepAlive = null;
        Integer minThreads = null;
        Integer maxThreads = null;

        if (poolInfo != null) {
          if (poolInfo.getQueueSize() != null) {
            maxQueueSize = poolInfo.getQueueSize().singles();
          }
          if (poolInfo.getKeepAlive() != null) {
            keepAlive = poolInfo.getKeepAlive().toString();
          }
          if (poolInfo.getMin() >= 0) {
            minThreads = poolInfo.getMin();
          }
          if (poolInfo.getMax() >= 0) {
            maxThreads = poolInfo.getMax();
          }
        }

        table.addCell(entry.getKey());
        table.addCell(poolInfo == null ? null : poolInfo.getThreadPoolType().getType());
        table.addCell(poolStats == null ? null : poolStats.getActive());
        table.addCell(poolStats == null ? null : poolStats.getThreads());
        table.addCell(poolStats == null ? null : poolStats.getQueue());
        table.addCell(maxQueueSize);
        table.addCell(poolStats == null ? null : poolStats.getRejected());
        table.addCell(poolStats == null ? null : poolStats.getLargest());
        table.addCell(poolStats == null ? null : poolStats.getCompleted());
        table.addCell(minThreads);
        table.addCell(maxThreads);
        table.addCell(keepAlive);

        table.endRow();
      }
    }

    return table;
  }