diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClientFactory.java index f3e964aa433..b54440b0350 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClientFactory.java @@ -37,6 +37,12 @@ public interface IRestfulClientFactory { */ public static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = 10000; + + /** + * Default value for {@link #getConnectionTimeToLive()} + */ + public static final int DEFAULT_CONNECTION_TTL = 5000; + /** * Default value for {@link #getServerValidationModeEnum()} */ @@ -75,6 +81,17 @@ public interface IRestfulClientFactory { */ int getConnectTimeout(); + + /** + * Gets the connection time to live, in milliseconds. This is the amount of time to keep connections alive for reuse. + *

+ * The default value for this setting is defined by {@link #DEFAULT_CONNECTION_TTL} + *

+ */ + default int getConnectionTimeToLive() { + return DEFAULT_CONNECTION_TTL; + } + /** * Returns the HTTP client instance. This method will not return null. * @param theUrl @@ -179,6 +196,14 @@ public interface IRestfulClientFactory { */ void setConnectTimeout(int theConnectTimeout); + /** + * Sets the connection time to live, in milliseconds. This is the amount of time to keep connections alive for reuse. + *

+ * The default value for this setting is defined by {@link #DEFAULT_CONNECTION_TTL} + *

+ */ + default void setConnectionTimeToLive(int theConnectionTimeToLive) {} + /** * Sets the Apache HTTP client instance to be used by any new restful clients created by this factory. If set to * null, a new HTTP client with default settings will be created. diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactory.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactory.java index f07ce7e9310..33b04cc2958 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactory.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactory.java @@ -106,9 +106,9 @@ public class HapiFhirCliRestfulClientFactory extends RestfulClientFactory { .register("https", sslConnectionSocketFactory) .build(); connectionManager = - new PoolingHttpClientConnectionManager(registry, null, null, null, 5000, TimeUnit.MILLISECONDS); + new PoolingHttpClientConnectionManager(registry, null, null, null, getConnectionTimeToLive(), TimeUnit.MILLISECONDS); } else { - connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + connectionManager = new PoolingHttpClientConnectionManager(getConnectionTimeToLive(), TimeUnit.MILLISECONDS); } connectionManager.setMaxTotal(getPoolMaxTotal()); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ApacheRestfulClientFactoryTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ApacheRestfulClientFactoryTest.java index 207603c9708..064976f1fce 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ApacheRestfulClientFactoryTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ApacheRestfulClientFactoryTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.cli; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; +import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.test.BaseFhirVersionParameterizedTest; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; @@ -10,6 +11,7 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.util.EntityUtils; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -79,4 +81,13 @@ public class ApacheRestfulClientFactoryTest extends BaseFhirVersionParameterized assertEquals(SSLHandshakeException.class, e.getCause().getCause().getClass()); } } + + @Test + public void testConnectionTimeToLive() { + ApacheRestfulClientFactory clientFactory = new ApacheRestfulClientFactory(); + + assertEquals(IRestfulClientFactory.DEFAULT_CONNECTION_TTL, clientFactory.getConnectionTimeToLive()); + clientFactory.setConnectionTimeToLive(25000); + assertEquals(25000, clientFactory.getConnectionTimeToLive()); + } } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactoryTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactoryTest.java index c4f1445f015..7f683b5443e 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactoryTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/client/HapiFhirCliRestfulClientFactoryTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.cli.client; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.test.BaseFhirVersionParameterizedTest; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; @@ -159,4 +160,15 @@ public class HapiFhirCliRestfulClientFactoryTest extends BaseFhirVersionParamete } } + @ParameterizedTest + @MethodSource("baseParamsProvider") + public void testConnectionTimeToLive(FhirVersionEnum theFhirVersion) { + FhirVersionParams fhirVersionParams = getFhirVersionParams(theFhirVersion); + HapiFhirCliRestfulClientFactory clientFactory = new HapiFhirCliRestfulClientFactory(fhirVersionParams.getFhirContext()); + + assertEquals(IRestfulClientFactory.DEFAULT_CONNECTION_TTL, clientFactory.getConnectionTimeToLive()); + clientFactory.setConnectionTimeToLive(25000); + assertEquals(25000, clientFactory.getConnectionTimeToLive()); + } + } diff --git a/hapi-fhir-client-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientFactory.java b/hapi-fhir-client-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientFactory.java index 5dcc0bd5e8b..ea2664da5cf 100644 --- a/hapi-fhir-client-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientFactory.java +++ b/hapi-fhir-client-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientFactory.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; import okhttp3.Call; +import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import java.net.InetSocketAddress; @@ -65,6 +66,7 @@ public class OkHttpRestfulClientFactory extends RestfulClientFactory { myNativeClient = new OkHttpClient() .newBuilder() .connectTimeout(getConnectTimeout(), TimeUnit.MILLISECONDS) + .connectionPool(new ConnectionPool(5, getConnectionTimeToLive(), TimeUnit.MILLISECONDS)) .readTimeout(getSocketTimeout(), TimeUnit.MILLISECONDS) .writeTimeout(getSocketTimeout(), TimeUnit.MILLISECONDS) .build(); diff --git a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/OkHttpRestfulClientFactoryTest.java b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/OkHttpRestfulClientFactoryTest.java index d4c6014bce1..8d866644902 100644 --- a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/OkHttpRestfulClientFactoryTest.java +++ b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/OkHttpRestfulClientFactoryTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.okhttp; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory; +import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.test.BaseFhirVersionParameterizedTest; import okhttp3.Call; import okhttp3.OkHttpClient; @@ -71,6 +72,13 @@ public class OkHttpRestfulClientFactoryTest extends BaseFhirVersionParameterized assertEquals(1516, ((OkHttpClient) clientFactory.getNativeClient()).connectTimeoutMillis()); } + @Test + public void testConnectionTimeToLive() { + assertEquals(IRestfulClientFactory.DEFAULT_CONNECTION_TTL, clientFactory.getConnectionTimeToLive()); + clientFactory.setConnectionTimeToLive(25000); + assertEquals(25000, clientFactory.getConnectionTimeToLive()); + } + @ParameterizedTest @MethodSource("baseParamsProvider") public void testNativeClientHttp(FhirVersionEnum theFhirVersion) throws Exception { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java index 999001f2509..408d788bdb1 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java @@ -103,7 +103,7 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { .disableCookieManagement(); PoolingHttpClientConnectionManager connectionManager = - new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + new PoolingHttpClientConnectionManager(getConnectionTimeToLive(), TimeUnit.MILLISECONDS); connectionManager.setMaxTotal(getPoolMaxTotal()); connectionManager.setDefaultMaxPerRoute(getPoolMaxPerRoute()); builder.setConnectionManager(connectionManager); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java index a46547112b5..9193ed66e45 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java @@ -57,6 +57,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { private final Set myValidatedServerBaseUrls = Collections.synchronizedSet(new HashSet<>()); private int myConnectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT; private int myConnectTimeout = DEFAULT_CONNECT_TIMEOUT; + private int myConnectionTimeToLive = DEFAULT_CONNECTION_TTL; private FhirContext myContext; private final Map, ClientInvocationHandlerFactory> myInvocationHandlers = new HashMap<>(); @@ -91,6 +92,11 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { return myConnectTimeout; } + @Override + public synchronized int getConnectionTimeToLive() { + return myConnectionTimeToLive; + } + /** * Return the proxy username to authenticate with the HTTP proxy */ @@ -210,6 +216,12 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { resetHttpClient(); } + @Override + public synchronized void setConnectionTimeToLive(int theConnectionTimeToLive) { + myConnectionTimeToLive = theConnectionTimeToLive; + resetHttpClient(); + } + /** * Sets the context associated with this client factory. Must not be called more than once. */ diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6184-add-configuration-setting-for-ttl-of-http-connections.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6184-add-configuration-setting-for-ttl-of-http-connections.yaml new file mode 100644 index 00000000000..0feb397f211 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6184-add-configuration-setting-for-ttl-of-http-connections.yaml @@ -0,0 +1,10 @@ +--- +type: add +issue: 6184 +title: "Added a configuration setting for the TTL of HTTP connections to IRestfulClientFactory. + The following implementations have been updated to respect this new setting: + 1. ApacheRestfulClientFactory + 2. OkHttpRestfulClientFactory + 3. HapiFhirCliRestfulClientFactory + Thanks to Alex Kopp and Alex Cote for the contribution! +" diff --git a/pom.xml b/pom.xml index d385e1799c9..e2bbc3767f3 100644 --- a/pom.xml +++ b/pom.xml @@ -919,15 +919,21 @@ adriennesox Adrienne Sox Galileo, Inc. - - - melihaydogd - Ahmet Melih Aydoğdu - - - alexrkopp - Alex Kopp - + + + melihaydogd + Ahmet Melih Aydoğdu + + + alexrkopp + Alex Kopp + athenahealth + + + acoteathn + Alex Cote + athenahealth +