// Path: core/src/main/java/com/bazaarvoice/ostrich/LoadBalanceAlgorithm.java
// public interface LoadBalanceAlgorithm {
//     /**
//      * Selects an end point to use based on a load balancing algorithm.  If no end point can be chosen, then
//      * <code>null</code> is returned.
//      *
//      * @param endPoints The end points to choose from.
//      * @param statistics Usage statistics about the end points in case the load balancing algorithm needs some
//      *                   knowledge of the service pool's state.
//      * @return Which end point to use or <code>null</code> if one couldn't be chosen.
//      */
//     ServiceEndPoint choose(Iterable<ServiceEndPoint> endPoints, ServicePoolStatistics statistics);
// }
// 
// Path: core/src/main/java/com/bazaarvoice/ostrich/PartitionContext.java
// public interface PartitionContext {
//     /**
//      * Gets the default piece of context. In many cases, there is only a single piece of relevant context, which this
//      * method should provide.
//      *
//      * @return The default context data.
//      */
//     Object get();
// 
//     /**
//      * Gets the context for the specified key.
//      * @param key The key for the desired context data.
//      * @return The context data.
//      */
//     Object get(String key);
// 
//     /**
//      * Gets a {@code Map} version of the context. The Map should be immutable.
//      * @return A {@code Map} with the same key/value pairs as this context.
//      */
//     Map<String, Object> asMap();
// }
// 
// Path: core/src/main/java/com/bazaarvoice/ostrich/healthcheck/FixedHealthCheckRetryDelay.java
// public class FixedHealthCheckRetryDelay implements HealthCheckRetryDelay {
//     public static final FixedHealthCheckRetryDelay ZERO = new FixedHealthCheckRetryDelay(0, TimeUnit.MILLISECONDS);
// 
//     private final long _retryMillis;
// 
//     public FixedHealthCheckRetryDelay(long duration, TimeUnit unit) {
//         checkArgument(duration >= 0);
//         checkNotNull(unit);
// 
//         _retryMillis = unit.toMillis(duration);
//     }
// 
//     @Override
//     public long getDelay(int numAttempts, HealthCheckResult result) {
//         return _retryMillis;
//     }
// }


import com.bazaarvoice.ostrich.HostDiscovery;
import com.bazaarvoice.ostrich.LoadBalanceAlgorithm;
import com.bazaarvoice.ostrich.PartitionContext;
import com.bazaarvoice.ostrich.RetryPolicy;
import com.bazaarvoice.ostrich.ServiceCallback;
import com.bazaarvoice.ostrich.ServiceEndPoint;
import com.bazaarvoice.ostrich.ServiceFactory;
import com.bazaarvoice.ostrich.ServicePoolStatistics;
import com.bazaarvoice.ostrich.exceptions.MaxRetriesException;
import com.bazaarvoice.ostrich.exceptions.ServiceException;
import com.bazaarvoice.ostrich.healthcheck.FixedHealthCheckRetryDelay;
import com.bazaarvoice.ostrich.partition.PartitionFilter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Ticker;
import com.google.common.collect.ImmutableList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.google.common.collect.Lists.newArrayList;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
                    });
                }
            });

            // Wait until the callable has definitely started and allocated a service instance...
            assertTrue(callableStarted.await(10, TimeUnit.SECONDS));


            // Capture the end point listener that was registered with HostDiscovery
            ArgumentCaptor<HostDiscovery.EndPointListener> listener = ArgumentCaptor.forClass(
                    HostDiscovery.EndPointListener.class);
            verify(_hostDiscovery).addListener(listener.capture());

            // Remove the end point from host discovery then add it back
            listener.getValue().onEndPointRemoved(FOO_ENDPOINT);
            listener.getValue().onEndPointAdded(FOO_ENDPOINT);

            // Let the initial callback terminate...
            canReturn.countDown();

            pool.forceHealthChecks();

            assertNotSame(serviceFuture.get(), pool.execute(NEVER_RETRY, IDENTITY_CALLBACK));
        } finally {
            executor.shutdown();
        }
    }

    private ServicePool<Service> newPool(ServiceCachingPolicy cachingPolicy) {
        ServicePool<Service> pool = new ServicePool<>(_ticker, _hostDiscovery, false, _serviceFactory, cachingPolicy,
// Path: core/src/main/java/com/bazaarvoice/ostrich/LoadBalanceAlgorithm.java
// public interface LoadBalanceAlgorithm {
//     /**
//      * Selects an end point to use based on a load balancing algorithm.  If no end point can be chosen, then
//      * <code>null</code> is returned.
//      *
//      * @param endPoints The end points to choose from.
//      * @param statistics Usage statistics about the end points in case the load balancing algorithm needs some
//      *                   knowledge of the service pool's state.
//      * @return Which end point to use or <code>null</code> if one couldn't be chosen.
//      */
//     ServiceEndPoint choose(Iterable<ServiceEndPoint> endPoints, ServicePoolStatistics statistics);
// }
// 
// Path: core/src/main/java/com/bazaarvoice/ostrich/PartitionContext.java
// public interface PartitionContext {
//     /**
//      * Gets the default piece of context. In many cases, there is only a single piece of relevant context, which this
//      * method should provide.
//      *
//      * @return The default context data.
//      */
//     Object get();
// 
//     /**
//      * Gets the context for the specified key.
//      * @param key The key for the desired context data.
//      * @return The context data.
//      */
//     Object get(String key);
// 
//     /**
//      * Gets a {@code Map} version of the context. The Map should be immutable.
//      * @return A {@code Map} with the same key/value pairs as this context.
//      */
//     Map<String, Object> asMap();
// }
// 
// Path: core/src/main/java/com/bazaarvoice/ostrich/healthcheck/FixedHealthCheckRetryDelay.java
// public class FixedHealthCheckRetryDelay implements HealthCheckRetryDelay {
//     public static final FixedHealthCheckRetryDelay ZERO = new FixedHealthCheckRetryDelay(0, TimeUnit.MILLISECONDS);
// 
//     private final long _retryMillis;
// 
//     public FixedHealthCheckRetryDelay(long duration, TimeUnit unit) {
//         checkArgument(duration >= 0);
//         checkNotNull(unit);
// 
//         _retryMillis = unit.toMillis(duration);
//     }
// 
//     @Override
//     public long getDelay(int numAttempts, HealthCheckResult result) {
//         return _retryMillis;
//     }
// }


// Path: core/src/test/java/com/bazaarvoice/ostrich/pool/ServicePoolCachingTest.java
import com.bazaarvoice.ostrich.HostDiscovery;
import com.bazaarvoice.ostrich.LoadBalanceAlgorithm;
import com.bazaarvoice.ostrich.PartitionContext;
import com.bazaarvoice.ostrich.RetryPolicy;
import com.bazaarvoice.ostrich.ServiceCallback;
import com.bazaarvoice.ostrich.ServiceEndPoint;
import com.bazaarvoice.ostrich.ServiceFactory;
import com.bazaarvoice.ostrich.ServicePoolStatistics;
import com.bazaarvoice.ostrich.exceptions.MaxRetriesException;
import com.bazaarvoice.ostrich.exceptions.ServiceException;
import com.bazaarvoice.ostrich.healthcheck.FixedHealthCheckRetryDelay;
import com.bazaarvoice.ostrich.partition.PartitionFilter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Ticker;
import com.google.common.collect.ImmutableList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.google.common.collect.Lists.newArrayList;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

                    });
                }
            });

            // Wait until the callable has definitely started and allocated a service instance...
            assertTrue(callableStarted.await(10, TimeUnit.SECONDS));


            // Capture the end point listener that was registered with HostDiscovery
            ArgumentCaptor<HostDiscovery.EndPointListener> listener = ArgumentCaptor.forClass(
                    HostDiscovery.EndPointListener.class);
            verify(_hostDiscovery).addListener(listener.capture());

            // Remove the end point from host discovery then add it back
            listener.getValue().onEndPointRemoved(FOO_ENDPOINT);
            listener.getValue().onEndPointAdded(FOO_ENDPOINT);

            // Let the initial callback terminate...
            canReturn.countDown();

            pool.forceHealthChecks();

            assertNotSame(serviceFuture.get(), pool.execute(NEVER_RETRY, IDENTITY_CALLBACK));
        } finally {
            executor.shutdown();
        }
    }

    private ServicePool<Service> newPool(ServiceCachingPolicy cachingPolicy) {
        ServicePool<Service> pool = new ServicePool<>(_ticker, _hostDiscovery, false, _serviceFactory, cachingPolicy,
                _partitionFilter, _loadBalanceAlgorithm, _healthCheckExecutor, true, FixedHealthCheckRetryDelay.ZERO, _registry);