All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.awssdk.http.apache.internal.conn.IdleConnectionReaper Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package software.amazon.awssdk.http.apache.internal.conn;

import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.http.conn.HttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;

/**
 * Manages the reaping of idle connections.
 */
@SdkInternalApi
public final class IdleConnectionReaper {
    private static final Logger log = LoggerFactory.getLogger(IdleConnectionReaper.class);

    private static final IdleConnectionReaper INSTANCE = new IdleConnectionReaper();

    private final Map connectionManagers;

    private final Supplier executorServiceSupplier;

    private final long sleepPeriod;

    private volatile ExecutorService exec;

    private volatile ReaperTask reaperTask;

    private IdleConnectionReaper() {
        this.connectionManagers =  new ConcurrentHashMap<>();

        this.executorServiceSupplier = () -> {
            ExecutorService e = Executors.newSingleThreadExecutor(r -> {
                Thread t = new Thread(r, "idle-connection-reaper");
                t.setDaemon(true);
                return t;
            });
            return e;
        };

        this.sleepPeriod = Duration.ofMinutes(1).toMillis();
    }

    @SdkTestInternalApi
    IdleConnectionReaper(Map connectionManagers,
                         Supplier executorServiceSupplier,
                         long sleepPeriod) {

        this.connectionManagers = connectionManagers;
        this.executorServiceSupplier = executorServiceSupplier;
        this.sleepPeriod = sleepPeriod;
    }

    /**
     * Register the connection manager with this reaper.
     *
     * @param manager The connection manager.
     * @param maxIdleTime The maximum time connections in the connection manager are to remain idle before being reaped.
     * @return {@code true} If the connection manager was not previously registered with this reaper, {@code false}
     * otherwise.
     */
    public synchronized boolean registerConnectionManager(HttpClientConnectionManager manager, long maxIdleTime) {
        boolean notPreviouslyRegistered = connectionManagers.put(manager, maxIdleTime) == null;
        setupExecutorIfNecessary();
        return notPreviouslyRegistered;
    }

    /**
     * Deregister this connection manager with this reaper.
     *
     * @param manager The connection manager.
     * @return {@code true} If this connection manager was previously registered with this reaper and it was removed, {@code
     * false} otherwise.
     */
    public synchronized boolean deregisterConnectionManager(HttpClientConnectionManager manager) {
        boolean wasRemoved = connectionManagers.remove(manager) != null;
        cleanupExecutorIfNecessary();
        return wasRemoved;
    }

    /**
     * @return The singleton instance of this class.
     */
    public static IdleConnectionReaper getInstance() {
        return INSTANCE;
    }

    private void setupExecutorIfNecessary() {
        if (exec != null) {
            return;
        }

        ExecutorService e = executorServiceSupplier.get();

        this.reaperTask = new ReaperTask(connectionManagers, sleepPeriod);

        e.execute(this.reaperTask);

        exec = e;
    }

    private void cleanupExecutorIfNecessary() {
        if (exec == null || !connectionManagers.isEmpty()) {
            return;
        }

        reaperTask.stop();
        reaperTask = null;
        exec.shutdownNow();
        exec = null;
    }

    private static final class ReaperTask implements Runnable {
        private final Map connectionManagers;
        private final long sleepPeriod;

        private volatile boolean stopping = false;

        private ReaperTask(Map connectionManagers,
                           long sleepPeriod) {
            this.connectionManagers = connectionManagers;
            this.sleepPeriod = sleepPeriod;
        }

        @Override
        public void run() {
            while (!stopping) {
                try {
                    Thread.sleep(sleepPeriod);

                    for (Map.Entry entry : connectionManagers.entrySet()) {
                        try {
                            entry.getKey().closeIdleConnections(entry.getValue(), TimeUnit.MILLISECONDS);
                        } catch (Exception t) {
                            log.warn("Unable to close idle connections", t);
                        }
                    }
                } catch (Throwable t) {
                    log.debug("Reaper thread: ", t);
                }
            }
            log.debug("Shutting down reaper thread.");
        }

        private void stop() {
            stopping = true;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy