Index: httpclient/src/main/java/org/apache/http/client/jmx/HostStats.java =================================================================== --- httpclient/src/main/java/org/apache/http/client/jmx/HostStats.java (revision ) +++ httpclient/src/main/java/org/apache/http/client/jmx/HostStats.java (revision ) @@ -0,0 +1,184 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.jmx; + +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.ConnectionPoolTimeoutException; + +import java.io.IOException; +import java.util.Date; + +/** + * Houses per-host statistics related to HTTP instrumentation. These statistics + * cover areas of transaction rates, errors, and connection pooling.

+ * + * To obtain a HostStats object, see the HostStatsManager class. + * + * @see HostStatsManager + */ +public class HostStats implements HostStatsMBean { + + private long statsStartTime; + + // Request duration related stats + private long totalRequestDurationMillis; + private Long minRequestDurationMillis; + private long minRequestTime; + private long maxRequestDurationMillis = 0; + private long maxRequestTime; + private int requestCount; + + // Timeout related stats + private int poolTimeoutCount; + private long mostRecentPoolTimeoutTime; + private int connectionTimeoutCount; + private long mostRecentConnectionTimeoutTime; + private int requestTimeoutCount; + private long mostRecentRequestTimeoutTime; + + /** + * Default constructor. + */ + public HostStats() { + this.statsStartTime = System.currentTimeMillis(); + } + + /** + * Records information about an request. + * + * @param requestDuration The duration of the request + */ + public void recordDuration(long requestDuration) { + this.requestCount++; + this.totalRequestDurationMillis += requestDuration; + + if (this.minRequestDurationMillis == null || requestDuration <= this.minRequestDurationMillis) { + this.minRequestDurationMillis = requestDuration; + this.minRequestTime = System.currentTimeMillis(); + } + + if (requestDuration >= this.maxRequestDurationMillis) { + this.maxRequestDurationMillis = requestDuration; + this.maxRequestTime = System.currentTimeMillis(); + } + } + + /** + * Records exception statistics. + * + * @param e The exception that occurred + */ + public void recordException(Throwable e) { + if (e instanceof ConnectionPoolTimeoutException) { + this.poolTimeoutCount++; + this.mostRecentPoolTimeoutTime = System.currentTimeMillis(); + } else if (e instanceof ConnectTimeoutException) { + this.connectionTimeoutCount++; + this.mostRecentConnectionTimeoutTime = System.currentTimeMillis(); + } else if (e instanceof IOException) { + this.requestTimeoutCount++; + this.mostRecentRequestTimeoutTime = System.currentTimeMillis(); + } + } + + public Date getStatsStartTime() { + return new Date(this.statsStartTime); + } + + public int getRequestCount() { + return this.requestCount; + } + + public long getTotalRequestDurationMillis() { + return this.totalRequestDurationMillis; + } + + public long getAverageRequestDurationMillis() { + return this.totalRequestDurationMillis / this.requestCount; + } + + public long getMinimumRequestDurationMillis() { + return this.minRequestDurationMillis; + } + + public Date getMinimumRequestTime() { + if (this.minRequestTime > 0) { + return new Date(this.minRequestTime); + } else { + return null; + } + } + + public long getMaximumRequestDurationMillis() { + return this.maxRequestDurationMillis; + } + + public Date getMaximumRequestTime() { + if (this.maxRequestTime > 0) { + return new Date(this.maxRequestTime); + } else { + return null; + } + } + + public int getConnectionTimeoutCount() { + return this.connectionTimeoutCount; + } + + public Date getMostRecentConnectionTimeoutTime() { + if (this.mostRecentConnectionTimeoutTime > 0) { + return new Date(this.mostRecentConnectionTimeoutTime); + } else { + return null; + } + } + + public Date getMostRecentPoolTimeoutTime() { + if (this.mostRecentPoolTimeoutTime > 0) { + return new Date(this.mostRecentPoolTimeoutTime); + } else { + return null; + } + } + + public Date getMostRecentRequestTimeoutTime() { + if (this.mostRecentRequestTimeoutTime > 0) { + return new Date(this.mostRecentRequestTimeoutTime); + } else { + return null; + } + } + + public int getPoolTimeoutCount() { + return this.poolTimeoutCount; + } + + public int getRequestTimeoutCount() { + return this.requestTimeoutCount; + } +} Index: httpclient/src/test/java/org/apache/http/client/jmx/HostStatsManagerTest.java =================================================================== --- httpclient/src/test/java/org/apache/http/client/jmx/HostStatsManagerTest.java (revision ) +++ httpclient/src/test/java/org/apache/http/client/jmx/HostStatsManagerTest.java (revision ) @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.jmx; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class HostStatsManagerTest { + + private static final String defaultUrl = "http://www.fakeurl.com"; + + private HostStatsManager hostStatsManager; + + @Before + public void initialize() { + hostStatsManager = new HostStatsManager(); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyUrl() throws Exception { + hostStatsManager.getHostStats(""); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidUrl() throws Exception { + hostStatsManager.getHostStats("bad-url-goes-here"); + } + + @Test + public void testCreateHostStats() { + HostStats myHostStats = hostStatsManager.getHostStats(defaultUrl); + + assertNotNull("test that getHostStats creates one if one does not exist", myHostStats); + } + + @Test + public void unregisterStats() throws Exception { + // Nothing to check here, but we can at least exercise the code and make sure + // it doesn't throw an exception + hostStatsManager.getHostStats(defaultUrl); + hostStatsManager.unregisterHostStats(); + } +} Index: httpclient/src/test/java/org/apache/http/client/jmx/HostStatsTest.java =================================================================== --- httpclient/src/test/java/org/apache/http/client/jmx/HostStatsTest.java (revision ) +++ httpclient/src/test/java/org/apache/http/client/jmx/HostStatsTest.java (revision ) @@ -0,0 +1,105 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.jmx; + +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.ConnectionPoolTimeoutException; +import org.junit.Before; +import org.junit.Test; + +import javax.management.MalformedObjectNameException; +import java.io.IOException; + +import static junit.framework.Assert.assertEquals; + +public class HostStatsTest { + + private static final String defaultUrl = "http://www.fakeurl.com"; + + private HostStatsManager hostStatsManager; + + @Before + public void initialize() { + hostStatsManager = new HostStatsManager(); + } + + @Test + public void testDurationHostStats() { + HostStats hostStats = hostStatsManager.getHostStats(defaultUrl); + + hostStats.recordDuration(100); + hostStats.recordDuration(200); + hostStats.recordDuration(300); + hostStats.recordDuration(400); + hostStats.recordDuration(500); + + // Look it up again and make sure it matches + HostStats durationHostStats = hostStatsManager.getHostStats(defaultUrl); + + // Now verify the statistics themselves + assertEquals("test hostStats produces the correct request count", 5, durationHostStats.getRequestCount()); + assertEquals("test hostStats produces the correct average request duration", 300, durationHostStats.getAverageRequestDurationMillis()); + assertEquals("test hostStats produces the correct max request duration", 500, durationHostStats.getMaximumRequestDurationMillis()); + assertEquals("test hostStats produces the correct min request duration", 100, durationHostStats.getMinimumRequestDurationMillis()); + assertEquals("test hostStats produces the correct total request duration", 1500, durationHostStats.getTotalRequestDurationMillis()); + } + + @Test + public void testRequestDurationExceededThresholdStats() throws MalformedObjectNameException { + int threshold = 98765; + + hostStatsManager.unregisterHostStats(); + HostStats myHostStats = hostStatsManager.getHostStats(defaultUrl); + + myHostStats.recordDuration(0); + myHostStats.recordDuration(threshold - 1); + myHostStats.recordDuration(threshold); + myHostStats.recordDuration(threshold + 1); + myHostStats.recordDuration(Integer.MAX_VALUE); + + HostStats durationHostStats = hostStatsManager.getHostStats(defaultUrl); + assertEquals("test hostStats produces the correct max request duration", Integer.MAX_VALUE, durationHostStats.getMaximumRequestDurationMillis()); + assertEquals("test hostStats produces the correct min request duration", 0, durationHostStats.getMinimumRequestDurationMillis()); + } + + @Test + public void testExceptionsHostStats() { + + HostStats hostStats = hostStatsManager.getHostStats(defaultUrl); + hostStats.recordException(new ConnectionPoolTimeoutException()); + hostStats.recordException(new ConnectTimeoutException()); + hostStats.recordException(new IOException()); + hostStats.recordException(new NullPointerException()); + hostStats.recordException(new ConnectionPoolTimeoutException()); + + HostStats exceptionHostStats = hostStatsManager.getHostStats(defaultUrl); + assertEquals("test hostStats produces the correct connection timeout count", 1, exceptionHostStats.getConnectionTimeoutCount()); + assertEquals("test hostStats produces the correct pool timeout count", 2, exceptionHostStats.getPoolTimeoutCount()); + assertEquals("test hostStats produces the correct request timeout count", 1, exceptionHostStats.getRequestTimeoutCount()); + } +} Index: httpclient/src/main/java/org/apache/http/client/jmx/HostStatsMBean.java =================================================================== --- httpclient/src/main/java/org/apache/http/client/jmx/HostStatsMBean.java (revision ) +++ httpclient/src/main/java/org/apache/http/client/jmx/HostStatsMBean.java (revision ) @@ -0,0 +1,106 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.jmx; + +import java.util.Date; + +/** + * Bean definition for host-specific JMX statistics. + */ +public interface HostStatsMBean { + + /** + * @return The time at which statistics collection began + */ + Date getStatsStartTime(); + + /** + * @return The number of HTTP requests that have been processed + */ + int getRequestCount(); + + /** + * @return The total duration of all requests in milliseconds + */ + long getTotalRequestDurationMillis(); + + /** + * @return The average request duration in milliseconds + */ + long getAverageRequestDurationMillis(); + + /** + * @return The duration of the shortest request in milliseconds + */ + long getMinimumRequestDurationMillis(); + + /** + * @return The time at which the shortest request occurred + */ + Date getMinimumRequestTime(); + + /** + * @return The duration of the longest request in milliseconds + */ + long getMaximumRequestDurationMillis(); + + /** + * @return The time at which the longest request occurred + */ + Date getMaximumRequestTime(); + + /** + * @return The number of times a connection was not available from the pool + */ + int getPoolTimeoutCount(); + + /** + * @return The time at which the most recent pool timeout occurred + */ + Date getMostRecentPoolTimeoutTime(); + + /** + * @return The number of times timeouts occurred which establishing a connection to the host + */ + int getConnectionTimeoutCount(); + + /** + * @return The time at which the most recent connection timeout occurred + */ + Date getMostRecentConnectionTimeoutTime(); + + /** + * @return The number of times a request has timed out + */ + int getRequestTimeoutCount(); + + /** + * @return The time of the most recent request timeout + */ + Date getMostRecentRequestTimeoutTime(); +} Index: httpclient/src/main/java/org/apache/http/client/jmx/HostStatsManager.java =================================================================== --- httpclient/src/main/java/org/apache/http/client/jmx/HostStatsManager.java (revision ) +++ httpclient/src/main/java/org/apache/http/client/jmx/HostStatsManager.java (revision ) @@ -0,0 +1,154 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.jmx; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpHost; + +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Creates and hands out HostsStats objects to callers. Also has + * utility methods for any tasks that deal with the entire + * HostStats collection. + */ +public class HostStatsManager { + + private static final Log log = LogFactory.getLog(HostStatsManager.class); + + private MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer(); + private Map hostStatBeans = new HashMap(); + + /** + * Look in our map of statistics to see if we've already instantiated one for the request. + * If so, hand it out. If not, instantiate one, register it, add it to our map, then hand it out. + * + * @param uriAsString The uri being used for the current request + * @return A HostStats object + */ + public HostStats getHostStats(String uriAsString) { + + // Take the incoming uri string and parse it apart. We do this so we can + // create the Host object, which the key for obtaining a HostStats object, + // and we also do it to create the bean name. Characters in a uri + // are not valid inside the bean naming structure. + URI uri; + HttpHost host; + String beanName; + + try { + uri = new URI(uriAsString); + host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); + beanName = uri.getScheme() + "-" + uri.getHost() + "-" + uri.getPort(); + } catch (URISyntaxException e) { + log.error("Exception: ", e); + return null; + } + + log.trace("looking up stats object for " + host); + + // Find the stats for the given host. If the stats object doesn't exist, create it. + HostStats hostStats = getHostStats(host, beanName); + + log.trace("returning HostStats for " + host); + + return hostStats; + } + + /** + * Manages handing out and creating HostStats objects. + * + * @param host The HttpHost we want our stats object for + * @param beanName The bean name to use in case the HostStats object needs to be created + * @return A HostStats object for the given host + */ + private synchronized HostStats getHostStats(HttpHost host, String beanName) { + HostStats hostStats = hostStatBeans.get(host); + if (hostStats == null) { + log.trace("creating MBean for " + host); + try { + ObjectName name = new ObjectName(makeJmxName(beanName)); + hostStats = new HostStats(); + beanServer.registerMBean(hostStats, name); + log.debug("registered bean " + name); + } catch (Throwable e) { + log.error("could not register MBean for " + host.toString(), e); + } + hostStatBeans.put(host, hostStats); + } + + return hostStats; + } + + /** + * Method to unregister all JMX beans we've registered as the webapp is shutting down. + * + * @throws javax.management.MalformedObjectNameException when object name is invalid + */ + public void unregisterHostStats() throws MalformedObjectNameException { + + log.debug("Unregistering JMX Beans"); + + // Create a wildcarded name to query for all beans that we registered + ObjectName queryName = new ObjectName(makeJmxName("*")); + + // Query for all the beans we registered and unregister each of them + Set beans = beanServer.queryMBeans(queryName, null); + for (ObjectInstance oi : beans) { + try { + beanServer.unregisterMBean(oi.getObjectName()); + log.debug("Unregistered " + oi.getObjectName()); + } catch (JMException e) { + log.error("Exception unregistering " + oi.getObjectName() + ": ", e); + } + } + + hostStatBeans.clear(); + } + + /** + * Creates a JMX name given the logical name. + * + * @param name The logical name of the bean + * @return A JMX-friendly name for the bean + */ + private String makeJmxName(String name) { + return "org.apache.http:category=" + name + ",type=Pools"; + } +} Index: httpclient/src/main/java/org/apache/http/client/jmx/JmxEnabledHttpClient.java =================================================================== --- httpclient/src/main/java/org/apache/http/client/jmx/JmxEnabledHttpClient.java (revision ) +++ httpclient/src/main/java/org/apache/http/client/jmx/JmxEnabledHttpClient.java (revision ) @@ -0,0 +1,217 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.jmx; + +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; + +import java.io.IOException; + +/** + * This class uses the decorator pattern to introduce JMX statistics to + * Apache HttpClient version 4. + */ +public class JmxEnabledHttpClient implements HttpClient { + + private HttpClient client; + + private HostStatsManager hostStatsManager; + + /** + * Constructs an JmxEnabledHttpClient given an HttpClient. + * + * @param client The HttpClient to decorate + */ + public JmxEnabledHttpClient(HttpClient client) { + this.client = client; + this.hostStatsManager = new HostStatsManager(); + } + + public HttpResponse execute(HttpUriRequest request) throws IOException { + HttpResponse response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(request); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException { + HttpResponse response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(request, context); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException { + HttpResponse response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(target, request); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public T execute(HttpUriRequest request, ResponseHandler responseHandler) throws IOException { + T response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(request, responseHandler); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public T execute(HttpUriRequest request, ResponseHandler responseHandler, HttpContext context) throws IOException { + T response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(request, responseHandler, context); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public T execute(HttpHost target, HttpRequest request, ResponseHandler responseHandler) throws IOException { + T response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(target, request, responseHandler); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public T execute(HttpHost target, HttpRequest request, ResponseHandler responseHandler, HttpContext context) throws IOException { + T response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(target, request, responseHandler, context); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException { + HttpResponse response; + + HostStats stats = hostStatsManager.getHostStats(request.getRequestLine().getUri()); + long requestStartTime = System.currentTimeMillis(); + + try { + response = this.client.execute(target, request, context); + } catch (IOException e) { + stats.recordException(e); + throw e; + } + + recordDuration(stats, System.currentTimeMillis() - requestStartTime); + + return response; + } + + public ClientConnectionManager getConnectionManager() { + return this.client.getConnectionManager(); + } + + public HttpParams getParams() { + return this.client.getParams(); + } + + private void recordDuration(HostStats stats, long duration) { + stats.recordDuration(duration); + } +}