class AbstractJerseyRestClient {

    /**
     * Time unit: hour
     */
    private static final long TTL = 24L;
    /**
     * Time unit: ms
     */
    private static final long IDLE_TIME = 40L * 1000L;
    private static final String PROPERTY_MAX_TOTAL = "maxTotal";
    private static final String PROPERTY_MAX_PER_ROUTE = "maxPerRoute";
    private static final String PROPERTY_IDLE_TIME = "idleTime";
    private static final String CONNECTION_MANAGER = ApacheClientProperties.CONNECTION_MANAGER;

    private final Client client;
    private final WebTarget webTarget;
    private final PoolingHttpClientConnectionManager pool;
    private ScheduledExecutorService cleanExecutor;

    public AbstractJerseyRestClient(String url, int timeout, int maxTotal, int maxPerRoute) {
        this(url, new ConfigBuilder().configTimeout(timeout)
                                     .configPool(maxTotal, maxPerRoute)
                                     .build());
    }

    public AbstractJerseyRestClient(String url, ClientConfig config) {
        this.pool = configConnectionManager(config);

        this.client = JerseyClientBuilder.newClient(config);
        this.client.register(GZipEncoder.class);
        this.webTarget = this.client.target(url);

        cleanThreadPoolExecutor(config);
    }

    private static PoolingHttpClientConnectionManager configConnectionManager(ClientConfig conf) {
        /*
         * Using httpclient with connection pooling, and configuring the
         * jersey connector. But the jersey that has been released in the maven central
         * repository seems to have a bug: https://github.com/jersey/jersey/pull/3752
         */
        PoolingHttpClientConnectionManager pool =
                new PoolingHttpClientConnectionManager(TTL, TimeUnit.HOURS);
        Integer maxTotal = (Integer) conf.getProperty(PROPERTY_MAX_TOTAL);
        Integer maxPerRoute = (Integer) conf.getProperty(PROPERTY_MAX_PER_ROUTE);

        if (maxTotal != null) {
            pool.setMaxTotal(maxTotal);
        }
        if (maxPerRoute != null) {
            pool.setDefaultMaxPerRoute(maxPerRoute);
        }
        conf.property(CONNECTION_MANAGER, pool);
        conf.connectorProvider(new ApacheConnectorProvider());
        return pool;
    }

    private void cleanThreadPoolExecutor(ClientConfig config) {<FILL_FUNCTION_BODY>}

    protected WebTarget getWebTarget() {
        return this.webTarget;
    }

    public void close() {
        try {
            if (this.pool != null) {
                this.pool.close();
                this.cleanExecutor.shutdownNow();
            }
        } finally {
            this.client.close();
        }
    }

    private static class ConfigBuilder {

        private final ClientConfig config;

        ConfigBuilder() {
            this.config = new ClientConfig();
        }

        public ConfigBuilder configTimeout(int timeout) {
            this.config.property(ClientProperties.CONNECT_TIMEOUT, timeout);
            this.config.property(ClientProperties.READ_TIMEOUT, timeout);
            return this;
        }

        public ConfigBuilder configPool(int maxTotal, int maxPerRoute) {
            this.config.property(PROPERTY_MAX_TOTAL, maxTotal);
            this.config.property(PROPERTY_MAX_PER_ROUTE, maxPerRoute);
            return this;
        }

        public ClientConfig build() {
            return this.config;
        }
    }
}

        this.cleanExecutor = ExecutorUtil.newScheduledThreadPool("conn-clean-worker-%d");
        Number idleTimeProp = (Number) config.getProperty(PROPERTY_IDLE_TIME);
        final long idleTime = idleTimeProp == null ? IDLE_TIME : idleTimeProp.longValue();
        final long checkPeriod = idleTime / 2L;
        this.cleanExecutor.scheduleWithFixedDelay(() -> {
            PoolStats stats = this.pool.getTotalStats();
            int using = stats.getLeased() + stats.getPending();
            if (using > 0) {
                // Do clean only when all connections are idle
                return;
            }
            // Release connections when all clients are inactive
            this.pool.closeIdleConnections(idleTime, TimeUnit.MILLISECONDS);
            this.pool.closeExpiredConnections();
        }, checkPeriod, checkPeriod, TimeUnit.MILLISECONDS);
    
<no_super_class>