# ClosedConnectionRetryerProvider.java

    package org.codefilarete.stalactite.sql;

    import java.sql.Connection;
    import java.sql.SQLException;
    import java.time.Duration;

    import org.codefilarete.tool.Retryer;
    import org.codefilarete.tool.Retryer.RetryException;

    /**
     * {@link ConnectionProvider} that adds some retry attempts when getting a connection from another {@link ConnectionProvider}.
     * Retries check that the connection is not closed. A delay is observed between retries.
     *
     * @author Guillaume Mary
     */
    public class ClosedConnectionRetryerProvider implements ConnectionProvider {

    	private final ConnectionProvider dataSource;

    	private final ClosedConnectionRetryer closedConnectionRetryer;

    	/**
    	 * Default constructor that will attempt 3 times, with a delay of 10ms between each, before throwing an exception on connection providence.
    	 * Given {@link ConnectionProvider} is expected to be a simple one such as {@link ConnectionProvider.DataSourceConnectionProvider}
    	 * or {@link SimpleConnectionProvider}.
    	 *
    	 * @param dataSource the underlying {@link ConnectionProvider}
    	 */
    	public ClosedConnectionRetryerProvider(ConnectionProvider dataSource) {
    		this(dataSource, 3);
    	}

    	public ClosedConnectionRetryerProvider(ConnectionProvider dataSource, int connectionOpeningRetryMaxCount) {
    		this.dataSource = dataSource;
    		this.closedConnectionRetryer = new ClosedConnectionRetryer(connectionOpeningRetryMaxCount, Duration.ofMillis(10));
    	}

    	public ClosedConnectionRetryerProvider(ConnectionProvider dataSource, int connectionOpeningRetryMaxCount, Duration connectionOpeningRetryInterval) {
    		this.dataSource = dataSource;
    		this.closedConnectionRetryer = new ClosedConnectionRetryer(connectionOpeningRetryMaxCount, connectionOpeningRetryInterval);
    	}

    	@Override
    	public Connection giveConnection() {
    		return lookupForConnection();
    	}

    	private Connection lookupForConnection() {
    		Connection connection;
    		// Ensuring that connection is not already closed, hence trying until one is found
    		try {
    			connection = closedConnectionRetryer.execute(this.dataSource::giveConnection, "getting a connection");
    		} catch (RetryException e) {
    			throw new RetryException("Maximum attempt to open a connection reached : all are closed", e);
    		}
    		return connection;
    	}

    	/**
    	 * Class that asks for retry on closed {@link Connection} when obtaining them.
    	 */
    	private static class ClosedConnectionRetryer extends Retryer {

    		public ClosedConnectionRetryer(int retryCount, Duration connectionOpeningRetryInterval) {
    			super(retryCount, connectionOpeningRetryInterval);
    		}

    		@Override
    		protected boolean shouldRetry(Result result) {
    			if (result instanceof Failure) {
    				return false;	// An exception will be thrown in case of failure while getting a connection
    			}
    			try {
    				return ((Success<Connection>) result).getValue().isClosed();
    			} catch (SQLException e) {
    				throw new IllegalStateException("Impossible to know if connection is closed or not", e);
    			}
    		}
    	}
    }