package io.dropwizard.metrics.httpasyncclient; import io.dropwizard.metrics.MetricName; import io.dropwizard.metrics.MetricRegistry; import io.dropwizard.metrics.Timer; import io.dropwizard.metrics.httpclient.HttpClientMetricNameStrategy; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.nio.client.HttpAsyncClient; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class InstrumentedHttpClientsTimerTest extends HttpClientTestBase { private HttpAsyncClient asyncHttpClient; @Mock private Timer.Context context; @Mock private MetricRegistry metricRegistry; @Before public void setUp() throws Exception { CloseableHttpAsyncClient chac = new InstrumentedNHttpClientBuilder(metricRegistry, mock(HttpClientMetricNameStrategy.class)).build(); chac.start(); asyncHttpClient = chac; Timer timer = mock(Timer.class); when(timer.time()).thenReturn(context); when(metricRegistry.timer(Matchers.<MetricName>anyObject())).thenReturn(timer); } @Test public void timerIsStoppedCorrectly() throws Exception { HttpHost host = startServerWithGlobalRequestHandler(STATUS_OK); HttpGet get = new HttpGet("/?q=anything"); // Timer hasn't been stopped prior to executing the request verify(context, never()).stop(); Future<HttpResponse> responseFuture = asyncHttpClient.execute(host, get, null); // Timer should still be running verify(context, never()).stop(); responseFuture.get(20, TimeUnit.SECONDS); // After the computation is complete timer must be stopped // Materialzing the future and calling the future callback is not an atomic operation so // we need to wait for callback to succeed verify(context, timeout(100).times(1)).stop(); } @Test @SuppressWarnings("unchecked") public void timerIsStoppedCorrectlyWithProvidedFutureCallbackCompleted() throws Exception { HttpHost host = startServerWithGlobalRequestHandler(STATUS_OK); HttpGet get = new HttpGet("/?q=something"); FutureCallback<HttpResponse> futureCallback = mock(FutureCallback.class); // Timer hasn't been stopped prior to executing the request verify(context, never()).stop(); Future<HttpResponse> responseFuture = asyncHttpClient.execute(host, get, futureCallback); // Timer should still be running verify(context, never()).stop(); responseFuture.get(20, TimeUnit.SECONDS); // Callback must have been called assertTrue(responseFuture.isDone()); // After the computation is complete timer must be stopped // Materialzing the future and calling the future callback is not an atomic operation so // we need to wait for callback to succeed verify(futureCallback, timeout(100).times(1)).completed(Matchers.<HttpResponse>anyObject()); verify(context, timeout(100).times(1)).stop(); } @Test @SuppressWarnings("unchecked") public void timerIsStoppedCorrectlyWithProvidedFutureCallbackFailed() throws Exception { // There should be nothing listening on this port HttpHost host = HttpHost.create(String.format("http://127.0.0.1:%d", findAvailableLocalPort())); HttpGet get = new HttpGet("/?q=something"); FutureCallback<HttpResponse> futureCallback = mock(FutureCallback.class); // Timer hasn't been stopped prior to executing the request verify(context, never()).stop(); Future<HttpResponse> responseFuture = asyncHttpClient.execute(host, get, futureCallback); // Timer should still be running verify(context, never()).stop(); try { responseFuture.get(20, TimeUnit.SECONDS); fail("This should fail as the client should not be able to connect"); } catch (Exception e) { // Ignore } // After the computation is complete timer must be stopped // Materialzing the future and calling the future callback is not an atomic operation so // we need to wait for callback to succeed verify(futureCallback, timeout(100).times(1)).failed(Matchers.<Exception>anyObject()); verify(context, timeout(100).times(1)).stop(); } }