();
+ }
+
+ /**
+ * Associates the given IP address to the given host in this DNS overrider.
+ *
+ * @param host
+ * The host name to be associated with the given IP.
+ * @param ip
+ * IPv4 address to be resolved by this DNS overrider to the given
+ * host name.
+ *
+ * @throws IllegalArgumentException
+ * if the given IP is not a valid IPv4 address or an InetAddress
+ * instance cannot be built based on the given IPv4 address.
+ *
+ * @see InetAddress#getByAddress
+ */
+ public void add(final String host, final String ip) {
+ if (!InetAddressUtils.isIPv4Address(ip)) {
+ throw new IllegalArgumentException(ip
+ + " must be a valid IPv4 address");
+ }
+
+ String[] ipParts = ip.split("\\.");
+
+ byte[] byteIpAddress = new byte[4];
+
+ for (int i = 0; i < 4; i++) {
+ byteIpAddress[i] = Integer.decode(ipParts[i]).byteValue();
+ }
+
+ try {
+ dnsMap.put(
+ host,
+ new InetAddress[] { InetAddress.getByAddress(byteIpAddress) });
+ } catch (UnknownHostException e) {
+ log.error("Unable to build InetAddress for " + ip, e);
+ throw new IllegalArgumentException(e);
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public InetAddress[] resolve(String host) {
+ InetAddress[] resolvedAddresses = dnsMap.get(host);
+ if (log.isInfoEnabled()) {
+ log.info("Resolving " + host + " to "
+ + Arrays.deepToString(resolvedAddresses));
+ }
+ return resolvedAddresses;
+ }
+
+}
Index: src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java
===================================================================
--- src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java (revision 1164883)
+++ src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java (working copy)
@@ -49,25 +49,25 @@
import org.apache.http.impl.conn.SchemeRegistryFactory;
/**
- * Manages a pool of {@link OperatedClientConnection client connections} and
- * is able to service connection requests from multiple execution threads.
+ * Manages a pool of {@link OperatedClientConnection client connections} and is
+ * able to service connection requests from multiple execution threads.
* Connections are pooled on a per route basis. A request for a route which
- * already the manager has persistent connections for available in the pool
- * will be services by leasing a connection from the pool rather than
- * creating a brand new connection.
+ * already the manager has persistent connections for available in the pool will
+ * be services by leasing a connection from the pool rather than creating a
+ * brand new connection.
*
- * PoolingConnectionManager maintains a maximum limit of connection on
- * a per route basis and in total. Per default this implementation will
- * create no more than than 2 concurrent connections per given route
- * and no more 20 connections in total. For many real-world applications
- * these limits may prove too constraining, especially if they use HTTP
- * as a transport protocol for their services. Connection limits, however,
- * can be adjusted using HTTP parameters.
- *
+ * PoolingConnectionManager maintains a maximum limit of connection on a per
+ * route basis and in total. Per default this implementation will create no more
+ * than than 2 concurrent connections per given route and no more 20 connections
+ * in total. For many real-world applications these limits may prove too
+ * constraining, especially if they use HTTP as a transport protocol for their
+ * services. Connection limits, however, can be adjusted using HTTP parameters.
+ *
* @since 4.2
*/
@ThreadSafe
-public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl {
+public class PoolingClientConnectionManager implements ClientConnectionManager,
+ ConnPoolControl {
private final Log log = LogFactory.getLog(getClass());
@@ -76,27 +76,50 @@
private final HttpConnPool pool;
private final ClientConnectionOperator operator;
+
+ /** the custom-configured DNS lookup mechanism. */
+ private final DnsResolver dnsResolver;
public PoolingClientConnectionManager(final SchemeRegistry schreg) {
this(schreg, -1, TimeUnit.MILLISECONDS);
}
+
+ public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) {
+ this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver);
+ }
public PoolingClientConnectionManager() {
this(SchemeRegistryFactory.createDefault());
}
- public PoolingClientConnectionManager(
- final SchemeRegistry schemeRegistry,
+ public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry,
final long timeToLive, final TimeUnit tunit) {
super();
if (schemeRegistry == null) {
- throw new IllegalArgumentException("Scheme registry may not be null");
+ throw new IllegalArgumentException(
+ "Scheme registry may not be null");
}
this.schemeRegistry = schemeRegistry;
+ this.dnsResolver = null;
this.operator = createConnectionOperator(schemeRegistry);
this.pool = new HttpConnPool(this.log, 2, 20, timeToLive, tunit);
+
}
+ public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry,
+ final long timeToLive, final TimeUnit tunit,
+ final DnsResolver dnsResolver) {
+ super();
+ if (schemeRegistry == null) {
+ throw new IllegalArgumentException(
+ "Scheme registry may not be null");
+ }
+ this.schemeRegistry = schemeRegistry;
+ this.dnsResolver = dnsResolver;
+ this.operator = createConnectionOperator(schemeRegistry);
+ this.pool = new HttpConnPool(this.log, 2, 20, timeToLive, tunit);
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -107,19 +130,20 @@
}
/**
- * Hook for creating the connection operator.
- * It is called by the constructor.
- * Derived classes can override this method to change the
- * instantiation of the operator.
- * The default implementation here instantiates
- * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
- *
- * @param schreg the scheme registry.
- *
- * @return the connection operator to use
+ * Hook for creating the connection operator. It is called by the
+ * constructor. Derived classes can override this method to change the
+ * instantiation of the operator. The default implementation here
+ * instantiates {@link DefaultClientConnectionOperator
+ * DefaultClientConnectionOperator}.
+ *
+ * @param schreg
+ * the scheme registry.
+ *
+ * @return the connection operator to use
*/
- protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
- return new DefaultClientConnectionOperator(schreg);
+ protected ClientConnectionOperator createConnectionOperator(
+ SchemeRegistry schreg) {
+ return new DefaultClientConnectionOperator(schreg, this.dnsResolver);
}
public SchemeRegistry getSchemeRegistry() {
@@ -139,10 +163,13 @@
StringBuilder buf = new StringBuilder();
PoolStats totals = this.pool.getTotalStats();
PoolStats stats = this.pool.getStats(route);
- buf.append("[total kept alive: ").append(totals.getAvailable()).append("; ");
- buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable());
+ buf.append("[total kept alive: ").append(totals.getAvailable())
+ .append("; ");
+ buf.append("route allocated: ").append(
+ stats.getLeased() + stats.getAvailable());
buf.append(" of ").append(stats.getMax()).append("; ");
- buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable());
+ buf.append("total allocated: ").append(
+ totals.getLeased() + totals.getAvailable());
buf.append(" of ").append(totals.getMax()).append("]");
return buf.toString();
}
@@ -158,14 +185,14 @@
return buf.toString();
}
- public ClientConnectionRequest requestConnection(
- final HttpRoute route,
+ public ClientConnectionRequest requestConnection(final HttpRoute route,
final Object state) {
if (route == null) {
throw new IllegalArgumentException("HTTP route may not be null");
}
if (this.log.isDebugEnabled()) {
- this.log.debug("Connection request: " + format(route, state) + formatStats(route));
+ this.log.debug("Connection request: " + format(route, state)
+ + formatStats(route));
}
final Future future = this.pool.lease(route, state);
@@ -175,9 +202,9 @@
future.cancel(true);
}
- public ManagedClientConnection getConnection(
- final long timeout,
- final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
+ public ManagedClientConnection getConnection(final long timeout,
+ final TimeUnit tunit) throws InterruptedException,
+ ConnectionPoolTimeoutException {
return leaseConnection(future, timeout, tunit);
}
@@ -185,10 +212,9 @@
}
- ManagedClientConnection leaseConnection(
- final Future future,
- final long timeout,
- final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
+ ManagedClientConnection leaseConnection(final Future future,
+ final long timeout, final TimeUnit tunit)
+ throws InterruptedException, ConnectionPoolTimeoutException {
HttpPoolEntry entry;
try {
entry = future.get(timeout, tunit);
@@ -199,7 +225,8 @@
throw new IllegalStateException("Pool entry with no connection");
}
if (this.log.isDebugEnabled()) {
- this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute()));
+ this.log.debug("Connection leased: " + format(entry)
+ + formatStats(entry.getRoute()));
}
return new ManagedClientConnectionImpl(this, this.operator, entry);
} catch (ExecutionException ex) {
@@ -207,25 +234,27 @@
if (cause == null) {
cause = ex;
}
- this.log.error("Unexpected exception leasing connection from pool", cause);
+ this.log.error("Unexpected exception leasing connection from pool",
+ cause);
// Should never happen
throw new InterruptedException();
} catch (TimeoutException ex) {
- throw new ConnectionPoolTimeoutException("Timeout waiting for connection");
+ throw new ConnectionPoolTimeoutException(
+ "Timeout waiting for connection");
}
}
- public void releaseConnection(
- final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) {
+ public void releaseConnection(final ManagedClientConnection conn,
+ final long keepalive, final TimeUnit tunit) {
if (!(conn instanceof ManagedClientConnectionImpl)) {
- throw new IllegalArgumentException
- ("Connection class mismatch, " +
- "connection not obtained from this manager.");
+ throw new IllegalArgumentException("Connection class mismatch, "
+ + "connection not obtained from this manager.");
}
ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn;
if (managedConn.getManager() != this) {
- throw new IllegalStateException("Connection not obtained from this manager.");
+ throw new IllegalStateException(
+ "Connection not obtained from this manager.");
}
synchronized (managedConn) {
@@ -239,11 +268,14 @@
managedConn.shutdown();
} catch (IOException iox) {
if (this.log.isDebugEnabled()) {
- this.log.debug("I/O exception shutting down released connection", iox);
+ this.log.debug(
+ "I/O exception shutting down released connection",
+ iox);
}
}
}
- entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
+ entry.updateExpiry(keepalive, tunit != null ? tunit
+ : TimeUnit.MILLISECONDS);
if (this.log.isDebugEnabled()) {
String s;
if (keepalive > 0) {
@@ -251,13 +283,15 @@
} else {
s = "indefinitely";
}
- this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
+ this.log.debug("Connection " + format(entry)
+ + " can be kept alive " + s);
}
} finally {
this.pool.release(entry, managedConn.isMarkedReusable());
}
if (this.log.isDebugEnabled()) {
- this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute()));
+ this.log.debug("Connection released: " + format(entry)
+ + formatStats(entry.getRoute()));
}
}
}
@@ -272,13 +306,15 @@
try {
shutdown(2000);
} catch (IOException ex) {
- this.log.error("I/O exception while shutting down connection pool", ex);
+ this.log.error("I/O exception while shutting down connection pool",
+ ex);
}
}
public void closeIdleConnections(long idleTimeout, TimeUnit tunit) {
if (log.isDebugEnabled()) {
- log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit);
+ log.debug("Closing connections idle longer than " + idleTimeout
+ + " " + tunit);
}
pool.closeIdle(idleTimeout, tunit);
}
@@ -309,4 +345,3 @@
}
}
-
Index: src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java
===================================================================
--- src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java (revision 0)
+++ src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java (revision 0)
@@ -0,0 +1,102 @@
+/*
+ * ====================================================================
+ * 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.impl.conn;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestDefaultClientConnectOperator {
+
+ @Test
+ public void testCustomDnsResolver() {
+ DnsResolver dnsResolver = mock(DnsResolver.class);
+
+ try {
+ InetAddress[] firstAddress = translateIp("192.168.1.1");
+ when(dnsResolver.resolve("somehost.example.com")).thenReturn(
+ firstAddress);
+
+ InetAddress[] secondAddress = translateIp("192.168.12.16");
+ when(dnsResolver.resolve("otherhost.example.com")).thenReturn(
+ secondAddress);
+
+ DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
+ SchemeRegistryFactory.createDefault(), dnsResolver);
+
+ Assert.assertArrayEquals(firstAddress,
+ operator.resolveHostname("somehost.example.com"));
+ Assert.assertArrayEquals(secondAddress,
+ operator.resolveHostname("otherhost.example.com"));
+
+ try {
+ Assert.assertNull(operator
+ .resolveHostname("unknown.example.com"));
+ Assert.fail("unknown.example.com shouldn't be resolved");
+ } catch (UnknownHostException ex) {
+ // expected
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.fail("Unexpected exception " + e);
+ }
+
+ }
+
+ @Test
+ public void testNullDnsResolver(){
+ DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
+ SchemeRegistryFactory.createDefault());
+
+ try {
+ Assert.assertNull(operator
+ .resolveHostname("unknown.example.com"));
+ Assert.fail("unknown.example.com shouldn't be resolved");
+ } catch (UnknownHostException ex) {
+ // expected
+ }
+ }
+
+ private InetAddress[] translateIp(String ip) throws UnknownHostException {
+ String[] ipParts = ip.split("\\.");
+
+ byte[] byteIpAddress = new byte[4];
+ for (int i = 0; i < 4; i++) {
+ byteIpAddress[i] = Integer.decode(ipParts[i]).byteValue();
+ }
+
+ return new InetAddress[] { InetAddress.getByAddress(byteIpAddress) };
+
+ }
+
+}