From 9bf1fe4685b034243d756ad58f6c26addd534fb5 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Tue, 27 Jun 2017 15:47:11 -0400 Subject: [PATCH 1/8] Implementation of factories to provide Apache HTTP Clients --- spring-cloud-commons/pom.xml | 23 +++++ ...cheHttpClientConnectionManagerFactory.java | 47 ++++++++++ .../httpclient/ApacheHttpClientFactory.java | 38 ++++++++ ...cheHttpClientConnectionManagerFactory.java | 85 ++++++++++++++++++ .../DefaultApacheHttpClientFactory.java | 22 +++++ .../httpclient/HttpClientConfiguration.java | 30 +++++++ .../main/resources/META-INF/spring.factories | 3 +- .../CustomHttpClientConfigurationTests.java | 87 +++++++++++++++++++ ...tpClientConnectionManagerFactoryTests.java | 46 ++++++++++ .../DefaultApacheHttpClientFactoryTests.java | 42 +++++++++ .../DefaultHttpClientConfigurationTests.java | 46 ++++++++++ 11 files changed, 468 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java diff --git a/spring-cloud-commons/pom.xml b/spring-cloud-commons/pom.xml index 7b120d36..82a678a9 100644 --- a/spring-cloud-commons/pom.xml +++ b/spring-cloud-commons/pom.xml @@ -35,6 +35,11 @@ + + + 3.6.0 + 4.5.1 + org.springframework.boot @@ -102,6 +107,24 @@ compile true + + com.squareup.okhttp3 + okhttp + ${okhttp3.version} + true + + + com.squareup.okhttp3 + logging-interceptor + ${okhttp3.version} + true + + + org.apache.httpcomponents + httpclient + ${apachehttpclient.version} + true + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java new file mode 100644 index 00000000..55e1b167 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java @@ -0,0 +1,47 @@ +/* + * + * * Copyright 2013-2016 the original author or authors. + * * + * * Licensed 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. + * + */ + +package org.springframework.cloud.commons.httpclient; + +import java.util.concurrent.TimeUnit; + +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.HttpClientConnectionManager; + +/** + * Interface for creating an {@link HttpClientConnectionManager}. + * @author Ryan Baxter + */ +public interface ApacheHttpClientConnectionManagerFactory { + public static final String HTTP_SCHEME = "http"; + public static final String HTTPS_SCHEME = "https"; + + /** + * Creates a new {@link HttpClientConnectionManager}. + * @param disableSslValidation True to disable SSL validation, false otherwise + * @param maxTotalConnections The total number of connections + * @param maxConnectionsPerRoute The total number of connections per route + * @param timeToLive The time a connection is allowed to exist + * @param timeUnit The time unit for the time to live value + * @param registryBuilder The {@link RegistryBuilder} to use in the connection manager + * @return A new {@link HttpClientConnectionManager} + */ + public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, + int maxTotalConnections, int maxConnectionsPerRoute, + long timeToLive, TimeUnit timeUnit, RegistryBuilder registryBuilder); +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java new file mode 100644 index 00000000..6dfa3a3c --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java @@ -0,0 +1,38 @@ +/* + * + * * Copyright 2013-2016 the original author or authors. + * * + * * Licensed 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. + * + */ + +package org.springframework.cloud.commons.httpclient; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.impl.client.CloseableHttpClient; + +/** + * Factory for creating a new {@link CloseableHttpClient}. + * @author Ryan Baxter + */ +public interface ApacheHttpClientFactory { + + /** + * Creates a new {@link CloseableHttpClient}. + * @param requestConfig Configuration to be used for all requests by default + * @param connectionManager The {@link HttpClientConnectionManager} to use for the client + * @return A new {@link CloseableHttpClient} + */ + public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager); +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java new file mode 100644 index 00000000..18939595 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java @@ -0,0 +1,85 @@ +package org.springframework.cloud.commons.httpclient; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.commons.logging.LogFactory; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.commons.logging.Log; + +/** + * Default implementation of {@link ApacheHttpClientConnectionManagerFactory}. + * @author Ryan Baxter + */ +public class DefaultApacheHttpClientConnectionManagerFactory implements ApacheHttpClientConnectionManagerFactory{ + + private static final Log LOG = LogFactory.getLog(DefaultApacheHttpClientConnectionManagerFactory.class); + + public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, + int maxTotalConnections, + int maxConnectionsPerRoute) { + return newConnectionManager(disableSslValidation, maxTotalConnections, maxConnectionsPerRoute, + -1, TimeUnit.MILLISECONDS, null); + } + + @Override + public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, + int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, + TimeUnit timeUnit, RegistryBuilder registryBuilder) { + if (registryBuilder == null) { + registryBuilder = RegistryBuilder. create() + .register(HTTP_SCHEME, PlainConnectionSocketFactory.INSTANCE); + } + if (disableSslValidation) { + try { + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, new TrustManager[] { new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, + String s) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, + String s) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } }, new SecureRandom()); + registryBuilder.register(HTTPS_SCHEME, new SSLConnectionSocketFactory( + sslContext, NoopHostnameVerifier.INSTANCE)); + } + catch (NoSuchAlgorithmException e) { + LOG.warn("Error creating SSLContext", e); + } + catch (KeyManagementException e) { + LOG.warn("Error creating SSLContext", e); + } + + } + final Registry registry = registryBuilder.build(); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager( + registry, null, null, null, timeToLive, timeUnit); + connectionManager.setMaxTotal(maxTotalConnections); + connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); + + return connectionManager; + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java new file mode 100644 index 00000000..c4e895d8 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java @@ -0,0 +1,22 @@ +package org.springframework.cloud.commons.httpclient; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; + +/** + * Default implementation of {@link ApacheHttpClientFactory}. + * @author Ryan Baxter + */ +public class DefaultApacheHttpClientFactory implements ApacheHttpClientFactory { + + public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) { + return HttpClientBuilder.create().disableContentCompression() + .setDefaultRequestConfig(requestConfig) + .setConnectionManager(connectionManager) + .disableCookieManagement() + .useSystemProperties() + .build(); + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java new file mode 100644 index 00000000..d0e70c3c --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java @@ -0,0 +1,30 @@ +package org.springframework.cloud.commons.httpclient; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Ryan Baxter + */ +@Configuration +public class HttpClientConfiguration { + + @Configuration + @ConditionalOnProperty(name = "spring.cloud.httpclient.apache.enabled", matchIfMissing = true) + static class ApacheHttpClientConfiguration { + + @Bean + @ConditionalOnMissingBean + public ApacheHttpClientConnectionManagerFactory connManFactory() { + return new DefaultApacheHttpClientConnectionManagerFactory(); + } + + @Bean + @ConditionalOnMissingBean + public ApacheHttpClientFactory apacheHttpClientFactory() { + return new DefaultApacheHttpClientFactory(); + } + } +} diff --git a/spring-cloud-commons/src/main/resources/META-INF/spring.factories b/spring-cloud-commons/src/main/resources/META-INF/spring.factories index 4e17ff9a..5a433f60 100644 --- a/spring-cloud-commons/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-commons/src/main/resources/META-INF/spring.factories @@ -7,7 +7,8 @@ org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\ org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\ org.springframework.cloud.commons.util.UtilAutoConfiguration,\ -org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration +org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\ +org.springframework.cloud.commons.httpclient.HttpClientConfiguration # Environment Post Processors diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java new file mode 100644 index 00000000..4bd1167c --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java @@ -0,0 +1,87 @@ +package org.springframework.cloud.commons.httpclient; + +import java.util.concurrent.TimeUnit; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertTrue; + +/** + * @author Ryan Baxter + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CustomApplication.class) +public class CustomHttpClientConfigurationTests { + + @Autowired + ApacheHttpClientFactory httpClientFactory; + + @Autowired + ApacheHttpClientConnectionManagerFactory connectionManagerFactory; + + @Test + public void connManFactory() throws Exception { + assertTrue(ApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); + assertTrue(CustomApplication.MyApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); + } + + @Test + public void apacheHttpClientFactory() throws Exception { + assertTrue(ApacheHttpClientFactory.class.isInstance(httpClientFactory)); + assertTrue(CustomApplication.MyApacheHttpClientFactory.class.isInstance(httpClientFactory)); + } + + +} + +@Configuration +@EnableAutoConfiguration +class CustomApplication { + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Configuration + static class MyConfig { + + @Bean + public ApacheHttpClientFactory clientFactory() { + return new MyApacheHttpClientFactory(); + } + + @Bean ApacheHttpClientConnectionManagerFactory connectionManagerFactory() { + return new MyApacheHttpClientConnectionManagerFactory(); + } + + } + + static class MyApacheHttpClientFactory implements ApacheHttpClientFactory { + + @Override + public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) { + return null; + } + } + + static class MyApacheHttpClientConnectionManagerFactory implements ApacheHttpClientConnectionManagerFactory { + + @Override + public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, RegistryBuilder registryBuilder) { + return null; + } + } +} + + diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java new file mode 100644 index 00000000..a202438a --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java @@ -0,0 +1,46 @@ +package org.springframework.cloud.commons.httpclient; + +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.junit.Test; +import org.springframework.util.ReflectionUtils; + +import static org.junit.Assert.*; + +/** + * @author Ryan Baxter + */ +public class DefaultApacheHttpClientConnectionManagerFactoryTests { + @Test + public void newConnectionManager() throws Exception { + HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory().newConnectionManager( + false, 2, 6); + assertEquals(6, ((PoolingHttpClientConnectionManager)connectionManager).getDefaultMaxPerRoute()); + assertEquals(2,((PoolingHttpClientConnectionManager)connectionManager).getMaxTotal()); + Object pool = getField(((PoolingHttpClientConnectionManager)connectionManager), "pool"); + assertEquals(-1l, getField(pool, "timeToLive")); + TimeUnit timeUnit = getField(pool, "tunit"); + assertEquals(TimeUnit.MILLISECONDS, timeUnit); + } + + @Test + public void newConnectionManagerWithTTL() throws Exception { + HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory().newConnectionManager( + false, 2, 6, 56l, TimeUnit.DAYS, null); + assertEquals(6, ((PoolingHttpClientConnectionManager)connectionManager).getDefaultMaxPerRoute()); + assertEquals(2,((PoolingHttpClientConnectionManager)connectionManager).getMaxTotal()); + Object pool = getField(((PoolingHttpClientConnectionManager)connectionManager), "pool"); + assertEquals(56l, getField(pool, "timeToLive")); + TimeUnit timeUnit = getField(pool, "tunit"); + assertEquals(TimeUnit.DAYS, timeUnit); + } + + protected T getField(Object target, String name) { + Field field = ReflectionUtils.findField(target.getClass(), name); + ReflectionUtils.makeAccessible(field); + Object value = ReflectionUtils.getField(field, target); + return (T)value; + } +} \ No newline at end of file diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java new file mode 100644 index 00000000..dd7d2398 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java @@ -0,0 +1,42 @@ +package org.springframework.cloud.commons.httpclient; + +import java.lang.reflect.Field; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.Configurable; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.impl.client.CloseableHttpClient; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.springframework.util.ReflectionUtils; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +/** + * @author Ryan Baxter + */ +public class DefaultApacheHttpClientFactoryTests { + @Test + public void createClient() throws Exception { + final RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(100) + .setConnectTimeout(200) + .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build(); + CloseableHttpClient httpClient = new DefaultApacheHttpClientFactory().createClient(requestConfig, + mock(HttpClientConnectionManager.class)); + Assertions.assertThat(httpClient).isInstanceOf(Configurable.class); + RequestConfig config = ((Configurable) httpClient).getConfig(); + assertEquals(100, config.getSocketTimeout()); + assertEquals(200, config.getConnectTimeout()); + assertEquals(CookieSpecs.IGNORE_COOKIES, config.getCookieSpec()); + } + + protected T getField(Object target, String name) { + Field field = ReflectionUtils.findField(target.getClass(), name); + ReflectionUtils.makeAccessible(field); + Object value = ReflectionUtils.getField(field, target); + return (T)value; + } + +} \ No newline at end of file diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java new file mode 100644 index 00000000..b4cfe27a --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java @@ -0,0 +1,46 @@ +package org.springframework.cloud.commons.httpclient; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.*; + +/** + * @author Ryan Baxter + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = MyApplication.class) +public class DefaultHttpClientConfigurationTests { + @Autowired + ApacheHttpClientFactory httpClientFactory; + + @Autowired + ApacheHttpClientConnectionManagerFactory connectionManagerFactory; + + @Test + public void connManFactory() throws Exception { + assertTrue(ApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); + assertTrue(DefaultApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); + } + + @Test + public void apacheHttpClientFactory() throws Exception { + assertTrue(ApacheHttpClientFactory.class.isInstance(httpClientFactory)); + assertTrue(DefaultApacheHttpClientFactory.class.isInstance(httpClientFactory)); + } +} + +@Configuration +@EnableAutoConfiguration +class MyApplication { + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } +} \ No newline at end of file From 8b6ec654ea7745bc81cbee1e0905abbdba27b041 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 28 Jun 2017 11:47:53 -0400 Subject: [PATCH 2/8] http client libraries are assumed to be present and not optional --- spring-cloud-commons/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-cloud-commons/pom.xml b/spring-cloud-commons/pom.xml index 82a678a9..bd23e04d 100644 --- a/spring-cloud-commons/pom.xml +++ b/spring-cloud-commons/pom.xml @@ -123,7 +123,6 @@ org.apache.httpcomponents httpclient ${apachehttpclient.version} - true org.springframework.boot From 473c278983a8617ab21581762be85e8d7039b81e Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 28 Jun 2017 11:53:33 -0400 Subject: [PATCH 3/8] Fixing code formatting --- ...cheHttpClientConnectionManagerFactory.java | 4 +-- .../httpclient/ApacheHttpClientFactory.java | 6 ++-- ...cheHttpClientConnectionManagerFactory.java | 15 +++++----- .../DefaultApacheHttpClientFactory.java | 9 +++--- .../CustomHttpClientConfigurationTests.java | 26 ++++++++++------- ...tpClientConnectionManagerFactoryTests.java | 28 +++++++++++-------- .../DefaultApacheHttpClientFactoryTests.java | 12 ++++---- .../DefaultHttpClientConfigurationTests.java | 6 ++-- 8 files changed, 60 insertions(+), 46 deletions(-) diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java index 55e1b167..5c97d8e6 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java @@ -42,6 +42,6 @@ public interface ApacheHttpClientConnectionManagerFactory { * @return A new {@link HttpClientConnectionManager} */ public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, - int maxTotalConnections, int maxConnectionsPerRoute, - long timeToLive, TimeUnit timeUnit, RegistryBuilder registryBuilder); + int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, + TimeUnit timeUnit, RegistryBuilder registryBuilder); } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java index 6dfa3a3c..cbd501b9 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java @@ -31,8 +31,10 @@ public interface ApacheHttpClientFactory { /** * Creates a new {@link CloseableHttpClient}. * @param requestConfig Configuration to be used for all requests by default - * @param connectionManager The {@link HttpClientConnectionManager} to use for the client + * @param connectionManager The {@link HttpClientConnectionManager} to use for the + * client * @return A new {@link CloseableHttpClient} */ - public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager); + public CloseableHttpClient createClient(RequestConfig requestConfig, + HttpClientConnectionManager connectionManager); } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java index 18939595..92f12166 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java @@ -24,15 +24,16 @@ import org.apache.commons.logging.Log; * Default implementation of {@link ApacheHttpClientConnectionManagerFactory}. * @author Ryan Baxter */ -public class DefaultApacheHttpClientConnectionManagerFactory implements ApacheHttpClientConnectionManagerFactory{ +public class DefaultApacheHttpClientConnectionManagerFactory + implements ApacheHttpClientConnectionManagerFactory { - private static final Log LOG = LogFactory.getLog(DefaultApacheHttpClientConnectionManagerFactory.class); + private static final Log LOG = LogFactory + .getLog(DefaultApacheHttpClientConnectionManagerFactory.class); - public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, - int maxTotalConnections, - int maxConnectionsPerRoute) { - return newConnectionManager(disableSslValidation, maxTotalConnections, maxConnectionsPerRoute, - -1, TimeUnit.MILLISECONDS, null); + public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, + int maxTotalConnections, int maxConnectionsPerRoute) { + return newConnectionManager(disableSslValidation, maxTotalConnections, + maxConnectionsPerRoute, -1, TimeUnit.MILLISECONDS, null); } @Override diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java index c4e895d8..76d7a88c 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java @@ -11,12 +11,11 @@ import org.apache.http.impl.client.HttpClientBuilder; */ public class DefaultApacheHttpClientFactory implements ApacheHttpClientFactory { - public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) { + public CloseableHttpClient createClient(RequestConfig requestConfig, + HttpClientConnectionManager connectionManager) { return HttpClientBuilder.create().disableContentCompression() .setDefaultRequestConfig(requestConfig) - .setConnectionManager(connectionManager) - .disableCookieManagement() - .useSystemProperties() - .build(); + .setConnectionManager(connectionManager).disableCookieManagement() + .useSystemProperties().build(); } } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java index 4bd1167c..40729d41 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java @@ -32,17 +32,19 @@ public class CustomHttpClientConfigurationTests { @Test public void connManFactory() throws Exception { - assertTrue(ApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); - assertTrue(CustomApplication.MyApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); + assertTrue(ApacheHttpClientConnectionManagerFactory.class + .isInstance(connectionManagerFactory)); + assertTrue(CustomApplication.MyApacheHttpClientConnectionManagerFactory.class + .isInstance(connectionManagerFactory)); } @Test public void apacheHttpClientFactory() throws Exception { assertTrue(ApacheHttpClientFactory.class.isInstance(httpClientFactory)); - assertTrue(CustomApplication.MyApacheHttpClientFactory.class.isInstance(httpClientFactory)); + assertTrue(CustomApplication.MyApacheHttpClientFactory.class + .isInstance(httpClientFactory)); } - } @Configuration @@ -61,7 +63,8 @@ class CustomApplication { return new MyApacheHttpClientFactory(); } - @Bean ApacheHttpClientConnectionManagerFactory connectionManagerFactory() { + @Bean + ApacheHttpClientConnectionManagerFactory connectionManagerFactory() { return new MyApacheHttpClientConnectionManagerFactory(); } @@ -70,18 +73,21 @@ class CustomApplication { static class MyApacheHttpClientFactory implements ApacheHttpClientFactory { @Override - public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) { + public CloseableHttpClient createClient(RequestConfig requestConfig, + HttpClientConnectionManager connectionManager) { return null; } } - static class MyApacheHttpClientConnectionManagerFactory implements ApacheHttpClientConnectionManagerFactory { + static class MyApacheHttpClientConnectionManagerFactory + implements ApacheHttpClientConnectionManagerFactory { @Override - public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, RegistryBuilder registryBuilder) { + public HttpClientConnectionManager newConnectionManager( + boolean disableSslValidation, int maxTotalConnections, + int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, + RegistryBuilder registryBuilder) { return null; } } } - - diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java index a202438a..00355006 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java @@ -15,11 +15,14 @@ import static org.junit.Assert.*; public class DefaultApacheHttpClientConnectionManagerFactoryTests { @Test public void newConnectionManager() throws Exception { - HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory().newConnectionManager( - false, 2, 6); - assertEquals(6, ((PoolingHttpClientConnectionManager)connectionManager).getDefaultMaxPerRoute()); - assertEquals(2,((PoolingHttpClientConnectionManager)connectionManager).getMaxTotal()); - Object pool = getField(((PoolingHttpClientConnectionManager)connectionManager), "pool"); + HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory() + .newConnectionManager(false, 2, 6); + assertEquals(6, ((PoolingHttpClientConnectionManager) connectionManager) + .getDefaultMaxPerRoute()); + assertEquals(2, + ((PoolingHttpClientConnectionManager) connectionManager).getMaxTotal()); + Object pool = getField(((PoolingHttpClientConnectionManager) connectionManager), + "pool"); assertEquals(-1l, getField(pool, "timeToLive")); TimeUnit timeUnit = getField(pool, "tunit"); assertEquals(TimeUnit.MILLISECONDS, timeUnit); @@ -27,11 +30,14 @@ public class DefaultApacheHttpClientConnectionManagerFactoryTests { @Test public void newConnectionManagerWithTTL() throws Exception { - HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory().newConnectionManager( - false, 2, 6, 56l, TimeUnit.DAYS, null); - assertEquals(6, ((PoolingHttpClientConnectionManager)connectionManager).getDefaultMaxPerRoute()); - assertEquals(2,((PoolingHttpClientConnectionManager)connectionManager).getMaxTotal()); - Object pool = getField(((PoolingHttpClientConnectionManager)connectionManager), "pool"); + HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory() + .newConnectionManager(false, 2, 6, 56l, TimeUnit.DAYS, null); + assertEquals(6, ((PoolingHttpClientConnectionManager) connectionManager) + .getDefaultMaxPerRoute()); + assertEquals(2, + ((PoolingHttpClientConnectionManager) connectionManager).getMaxTotal()); + Object pool = getField(((PoolingHttpClientConnectionManager) connectionManager), + "pool"); assertEquals(56l, getField(pool, "timeToLive")); TimeUnit timeUnit = getField(pool, "tunit"); assertEquals(TimeUnit.DAYS, timeUnit); @@ -41,6 +47,6 @@ public class DefaultApacheHttpClientConnectionManagerFactoryTests { Field field = ReflectionUtils.findField(target.getClass(), name); ReflectionUtils.makeAccessible(field); Object value = ReflectionUtils.getField(field, target); - return (T)value; + return (T) value; } } \ No newline at end of file diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java index dd7d2398..1f31d632 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java @@ -19,12 +19,10 @@ import static org.mockito.Mockito.mock; public class DefaultApacheHttpClientFactoryTests { @Test public void createClient() throws Exception { - final RequestConfig requestConfig = RequestConfig.custom() - .setSocketTimeout(100) - .setConnectTimeout(200) - .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build(); - CloseableHttpClient httpClient = new DefaultApacheHttpClientFactory().createClient(requestConfig, - mock(HttpClientConnectionManager.class)); + final RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100) + .setConnectTimeout(200).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build(); + CloseableHttpClient httpClient = new DefaultApacheHttpClientFactory() + .createClient(requestConfig, mock(HttpClientConnectionManager.class)); Assertions.assertThat(httpClient).isInstanceOf(Configurable.class); RequestConfig config = ((Configurable) httpClient).getConfig(); assertEquals(100, config.getSocketTimeout()); @@ -36,7 +34,7 @@ public class DefaultApacheHttpClientFactoryTests { Field field = ReflectionUtils.findField(target.getClass(), name); ReflectionUtils.makeAccessible(field); Object value = ReflectionUtils.getField(field, target); - return (T)value; + return (T) value; } } \ No newline at end of file diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java index b4cfe27a..f07c51fd 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java @@ -25,8 +25,10 @@ public class DefaultHttpClientConfigurationTests { @Test public void connManFactory() throws Exception { - assertTrue(ApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); - assertTrue(DefaultApacheHttpClientConnectionManagerFactory.class.isInstance(connectionManagerFactory)); + assertTrue(ApacheHttpClientConnectionManagerFactory.class + .isInstance(connectionManagerFactory)); + assertTrue(DefaultApacheHttpClientConnectionManagerFactory.class + .isInstance(connectionManagerFactory)); } @Test From 787bea18e915f98a664dc451c196d1a15a0c5596 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Thu, 29 Jun 2017 15:11:27 -0400 Subject: [PATCH 4/8] OK HTTP client factory implementations --- ...aultOkHttpClientConnectionPoolFactory.java | 17 +++++ .../DefaultOkHttpClientFactory.java | 57 ++++++++++++++++ .../httpclient/HttpClientConfiguration.java | 21 ++++++ .../OkHttpClientConnectionPoolFactory.java | 21 ++++++ .../httpclient/OkHttpClientFactory.java | 65 +++++++++++++++++++ .../CustomHttpClientConfigurationTests.java | 56 +++++++++++++++- .../DefaultHttpClientConfigurationTests.java | 20 +++++- ...OkHttpClientConnectionPoolFactoryTest.java | 34 ++++++++++ .../DefaultOkHttpClientFactoryTest.java | 44 +++++++++++++ 9 files changed, 332 insertions(+), 3 deletions(-) create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactory.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientConnectionPoolFactory.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactoryTest.java create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactory.java new file mode 100644 index 00000000..9c09dfcc --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactory.java @@ -0,0 +1,17 @@ +package org.springframework.cloud.commons.httpclient; + +import okhttp3.ConnectionPool; + +import java.util.concurrent.TimeUnit; + +/** + * Default implementation of {@link OkHttpClientConnectionPoolFactory}. + * @author Ryan Baxter + */ +public class DefaultOkHttpClientConnectionPoolFactory implements OkHttpClientConnectionPoolFactory { + + @Override + public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) { + return new ConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit); + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java new file mode 100644 index 00000000..0b0b61ae --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java @@ -0,0 +1,57 @@ +package org.springframework.cloud.commons.httpclient; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Default implementation of {@link OkHttpClientFactory}. + * @author Ryan Baxter + */ +public class DefaultOkHttpClientFactory implements OkHttpClientFactory { + + private static final Log LOG = LogFactory.getLog(DefaultOkHttpClientFactory.class); + + @Override + public OkHttpClient create(boolean disableSslValidation, long connectTimeout, + TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout, + TimeUnit readTimeoutUnit, ConnectionPool connectionPool, + SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager) { + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .connectTimeout(connectTimeout, connectTimeoutUnit) + .followRedirects(followRedirects) + .readTimeout(readTimeout, readTimeoutUnit).connectionPool(connectionPool) + .connectionPool(connectionPool); + if (disableSslValidation) { + try { + X509TrustManager disabledTrustManager = new DisableValidationTrustManager(); + TrustManager[] trustManagers = new TrustManager[1]; + trustManagers[0] = disabledTrustManager; + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustManagers, new java.security.SecureRandom()); + SSLSocketFactory disabledSSLSocketFactory = sslContext.getSocketFactory(); + builder.sslSocketFactory(disabledSSLSocketFactory, disabledTrustManager); + builder.hostnameVerifier(new TrustAllHostnames()); + } + catch (NoSuchAlgorithmException e) { + LOG.warn("Error setting SSLSocketFactory in OKHttpClient", e); + } + catch (KeyManagementException e) { + LOG.warn("Error setting SSLSocketFactory in OKHttpClient", e); + } + } + if (sslSocketFactory != null && x509TrustManager != null) { + builder.sslSocketFactory(sslSocketFactory, x509TrustManager); + } + return builder.build(); + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java index d0e70c3c..f64ab8a0 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java @@ -1,5 +1,8 @@ package org.springframework.cloud.commons.httpclient; +import okhttp3.OkHttpClient; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -27,4 +30,22 @@ public class HttpClientConfiguration { return new DefaultApacheHttpClientFactory(); } } + + @Configuration + @ConditionalOnProperty(name = "spring.cloud.httpclient.ok.enabled") + @ConditionalOnClass(OkHttpClient.class) + static class OkHttpClientConfiguration { + + @Bean + @ConditionalOnMissingBean + public OkHttpClientConnectionPoolFactory connPoolFactory() { + return new DefaultOkHttpClientConnectionPoolFactory(); + } + + @Bean + @ConditionalOnMissingBean + public OkHttpClientFactory okHttpClientFactory() { + return new DefaultOkHttpClientFactory(); + } + } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientConnectionPoolFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientConnectionPoolFactory.java new file mode 100644 index 00000000..d7682cc1 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientConnectionPoolFactory.java @@ -0,0 +1,21 @@ +package org.springframework.cloud.commons.httpclient; + +import okhttp3.ConnectionPool; + +import java.util.concurrent.TimeUnit; + +/** + * Creates {@link ConnectionPool}s for {@link okhttp3.OkHttpClient}s + * @author Ryan Baxter + */ +public interface OkHttpClientConnectionPoolFactory { + + /** + * Creates a new {@link ConnectionPool}. + * @param maxIdleConnections number of max idle connections to allow + * @param keepAliveDuration amount of time to keep connections alive + * @param timeUnit the time unit for the keep alive duration + * @return A new {@link ConnectionPool} + */ + public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit); +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java new file mode 100644 index 00000000..b7857e99 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java @@ -0,0 +1,65 @@ +package org.springframework.cloud.commons.httpclient; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + +/** + * Creates new {@link OkHttpClient}s. + * @author Ryan Baxter + */ +public interface OkHttpClientFactory { + + /** + * Creates a new {@link OkHttpClient}. + * @param disableSslValidation To disable SSL validation + * @param connectTimeout Connection timeout duration + * @param connectTimeoutUnit Connection timeout time unit + * @param followRedirects Whether to follow redirects + * @param readTimeout Read timeout duration + * @param readTimeoutUnit Read timeout time unit + * @param connectionPool The connection pool to use + * @param sslSocketFactory The socket factory to use, can be {@code null} + * @param x509TrustManager The trust manager to use, can be {@code null} + * @return A new {@link OkHttpClient} + */ + public OkHttpClient create(boolean disableSslValidation, long connectTimeout, + TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout, + TimeUnit readTimeoutUnit, ConnectionPool connectionPool, + SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager); + + /** + * A {@link X509TrustManager} that does not validate SSL certificates. + */ + public static class DisableValidationTrustManager implements X509TrustManager { + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {} + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {} + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } + + /** + * A {@link HostnameVerifier} that does not validate any hostnames. + */ + public static class TrustAllHostnames implements HostnameVerifier { + + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + } +} diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java index 40729d41..879e4010 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java @@ -1,6 +1,11 @@ package org.springframework.cloud.commons.httpclient; +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; + import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.HttpClientConnectionManager; @@ -21,7 +26,7 @@ import static org.junit.Assert.assertTrue; * @author Ryan Baxter */ @RunWith(SpringRunner.class) -@SpringBootTest(classes = CustomApplication.class) +@SpringBootTest(classes = CustomApplication.class, properties = {"spring.cloud.httpclient.ok.enabled: true"}) public class CustomHttpClientConfigurationTests { @Autowired @@ -30,6 +35,12 @@ public class CustomHttpClientConfigurationTests { @Autowired ApacheHttpClientConnectionManagerFactory connectionManagerFactory; + @Autowired + OkHttpClientFactory okHttpClientFactory; + + @Autowired + OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory; + @Test public void connManFactory() throws Exception { assertTrue(ApacheHttpClientConnectionManagerFactory.class @@ -45,6 +56,18 @@ public class CustomHttpClientConfigurationTests { .isInstance(httpClientFactory)); } + @Test + public void connectionPoolFactory() throws Exception { + assertTrue(OkHttpClientConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory)); + assertTrue(CustomApplication.MyOkHttpConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory)); + } + + @Test + public void okHttpClientFactory() throws Exception { + assertTrue(OkHttpClientFactory.class.isInstance(okHttpClientFactory)); + assertTrue(CustomApplication.MyOkHttpClientFactory.class.isInstance(okHttpClientFactory)); + } + } @Configuration @@ -64,10 +87,20 @@ class CustomApplication { } @Bean - ApacheHttpClientConnectionManagerFactory connectionManagerFactory() { + public ApacheHttpClientConnectionManagerFactory connectionManagerFactory() { return new MyApacheHttpClientConnectionManagerFactory(); } + @Bean + public OkHttpClientConnectionPoolFactory connectionPoolFactory() { + return new MyOkHttpConnectionPoolFactory(); + } + + @Bean + public OkHttpClientFactory okHttpClientFactory() { + return new MyOkHttpClientFactory(); + } + } static class MyApacheHttpClientFactory implements ApacheHttpClientFactory { @@ -90,4 +123,23 @@ class CustomApplication { return null; } } + + static class MyOkHttpClientFactory implements OkHttpClientFactory { + + @Override + public OkHttpClient create(boolean disableSslValidation, long connectTimeout, + TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout, + TimeUnit readTimeoutUnit, ConnectionPool connectionPool, + SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager) { + return null; + } + } + + static class MyOkHttpConnectionPoolFactory implements OkHttpClientConnectionPoolFactory { + + @Override + public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) { + return null; + } + } } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java index f07c51fd..d3f7d04f 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java @@ -15,7 +15,7 @@ import static org.junit.Assert.*; * @author Ryan Baxter */ @RunWith(SpringRunner.class) -@SpringBootTest(classes = MyApplication.class) +@SpringBootTest(classes = MyApplication.class, properties = {"spring.cloud.httpclient.ok.enabled: true"}) public class DefaultHttpClientConfigurationTests { @Autowired ApacheHttpClientFactory httpClientFactory; @@ -23,6 +23,12 @@ public class DefaultHttpClientConfigurationTests { @Autowired ApacheHttpClientConnectionManagerFactory connectionManagerFactory; + @Autowired + OkHttpClientFactory okHttpClientFactory; + + @Autowired + OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory; + @Test public void connManFactory() throws Exception { assertTrue(ApacheHttpClientConnectionManagerFactory.class @@ -36,6 +42,18 @@ public class DefaultHttpClientConfigurationTests { assertTrue(ApacheHttpClientFactory.class.isInstance(httpClientFactory)); assertTrue(DefaultApacheHttpClientFactory.class.isInstance(httpClientFactory)); } + + @Test + public void connPoolFactory() throws Exception { + assertTrue(OkHttpClientConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory)); + assertTrue(DefaultOkHttpClientConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory)); + } + + @Test + public void setOkHttpClientFactory() throws Exception { + assertTrue(OkHttpClientFactory.class.isInstance(okHttpClientFactory)); + assertTrue(DefaultOkHttpClientFactory.class.isInstance(okHttpClientFactory)); + } } @Configuration diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactoryTest.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactoryTest.java new file mode 100644 index 00000000..a7658e3b --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactoryTest.java @@ -0,0 +1,34 @@ +package org.springframework.cloud.commons.httpclient; + +import okhttp3.ConnectionPool; + +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.springframework.util.ReflectionUtils; + +import static org.junit.Assert.assertEquals; + +/** + * @author Ryan Baxter + */ +public class DefaultOkHttpClientConnectionPoolFactoryTest { + @Test + public void create() throws Exception { + DefaultOkHttpClientConnectionPoolFactory connectionPoolFactory = new DefaultOkHttpClientConnectionPoolFactory(); + ConnectionPool connectionPool = connectionPoolFactory.create(2, + 3, TimeUnit.MILLISECONDS); + int idleConnections = getField(connectionPool, "maxIdleConnections"); + long keepAliveDuration = getField(connectionPool, "keepAliveDurationNs"); + assertEquals(2, idleConnections); + assertEquals(TimeUnit.MILLISECONDS.toNanos(3), keepAliveDuration); + } + + protected T getField(Object target, String name) { + Field field = ReflectionUtils.findField(target.getClass(), name); + ReflectionUtils.makeAccessible(field); + Object value = ReflectionUtils.getField(field, target); + return (T) value; + } + +} \ No newline at end of file diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java new file mode 100644 index 00000000..476caf21 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java @@ -0,0 +1,44 @@ +package org.springframework.cloud.commons.httpclient; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; + +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.HostnameVerifier; +import org.junit.Test; +import org.springframework.util.ReflectionUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Ryan Baxter + */ +public class DefaultOkHttpClientFactoryTest { + @Test + public void create() throws Exception { + DefaultOkHttpClientFactory okHttpClientFactory = new DefaultOkHttpClientFactory(); + DefaultOkHttpClientConnectionPoolFactory poolFactory = new DefaultOkHttpClientConnectionPoolFactory(); + ConnectionPool pool = poolFactory.create(4, 5, TimeUnit.DAYS); + OkHttpClient httpClient = okHttpClientFactory.create(true, 2, + TimeUnit.MILLISECONDS, true, 3, TimeUnit.HOURS, pool, null, null); + int connectTimeout = getField(httpClient, "connectTimeout"); + assertEquals(2, connectTimeout); + int readTimeout = getField(httpClient, "readTimeout"); + assertEquals(TimeUnit.HOURS.toMillis(3), readTimeout); + boolean followRedirects = getField(httpClient, "followRedirects"); + assertTrue(followRedirects); + ConnectionPool poolFromClient = getField(httpClient, "connectionPool"); + assertEquals(pool, poolFromClient); + HostnameVerifier hostnameVerifier = getField(httpClient, "hostnameVerifier"); + assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier)); + } + + protected T getField(Object target, String name) { + Field field = ReflectionUtils.findField(target.getClass(), name); + ReflectionUtils.makeAccessible(field); + Object value = ReflectionUtils.getField(field, target); + return (T) value; + } +} \ No newline at end of file From 08c0c017740ec7a2b213182397d9a6312f2975fa Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Sun, 9 Jul 2017 13:35:05 -0400 Subject: [PATCH 5/8] Use a builder pattern to create clients. --- .../httpclient/ApacheHttpClientFactory.java | 11 ++++------- ...cheHttpClientConnectionManagerFactory.java | 1 - .../DefaultApacheHttpClientFactory.java | 12 +++++++----- .../DefaultOkHttpClientFactory.java | 16 +++------------- .../httpclient/HttpClientConfiguration.java | 2 +- .../httpclient/OkHttpClientFactory.java | 19 ++++--------------- .../CustomHttpClientConfigurationTests.java | 15 ++++++--------- .../DefaultApacheHttpClientFactoryTests.java | 6 ++++-- .../DefaultOkHttpClientFactoryTest.java | 7 +++++-- 9 files changed, 34 insertions(+), 55 deletions(-) diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java index cbd501b9..7ef38395 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java @@ -21,6 +21,7 @@ package org.springframework.cloud.commons.httpclient; import org.apache.http.client.config.RequestConfig; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; /** * Factory for creating a new {@link CloseableHttpClient}. @@ -29,12 +30,8 @@ import org.apache.http.impl.client.CloseableHttpClient; public interface ApacheHttpClientFactory { /** - * Creates a new {@link CloseableHttpClient}. - * @param requestConfig Configuration to be used for all requests by default - * @param connectionManager The {@link HttpClientConnectionManager} to use for the - * client - * @return A new {@link CloseableHttpClient} + * Creates an {@link HttpClientBuilder} that can be used to create a new {@link CloseableHttpClient}. + * @return A {@link HttpClientBuilder} */ - public CloseableHttpClient createClient(RequestConfig requestConfig, - HttpClientConnectionManager connectionManager); + public HttpClientBuilder createBuilder(); } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java index 92f12166..136ce607 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java @@ -72,7 +72,6 @@ public class DefaultApacheHttpClientConnectionManagerFactory catch (KeyManagementException e) { LOG.warn("Error creating SSLContext", e); } - } final Registry registry = registryBuilder.build(); diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java index 76d7a88c..66827bd8 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java @@ -11,11 +11,13 @@ import org.apache.http.impl.client.HttpClientBuilder; */ public class DefaultApacheHttpClientFactory implements ApacheHttpClientFactory { - public CloseableHttpClient createClient(RequestConfig requestConfig, - HttpClientConnectionManager connectionManager) { + /** + * A default {@link HttpClientBuilder}. The {@link HttpClientBuilder} returned will + * have content compression disabled, cookie management disabled, and use system properties. + */ + @Override + public HttpClientBuilder createBuilder() { return HttpClientBuilder.create().disableContentCompression() - .setDefaultRequestConfig(requestConfig) - .setConnectionManager(connectionManager).disableCookieManagement() - .useSystemProperties().build(); + .disableCookieManagement().useSystemProperties(); } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java index 0b0b61ae..7ea0aad8 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java @@ -22,15 +22,8 @@ public class DefaultOkHttpClientFactory implements OkHttpClientFactory { private static final Log LOG = LogFactory.getLog(DefaultOkHttpClientFactory.class); @Override - public OkHttpClient create(boolean disableSslValidation, long connectTimeout, - TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout, - TimeUnit readTimeoutUnit, ConnectionPool connectionPool, - SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager) { - OkHttpClient.Builder builder = new OkHttpClient.Builder() - .connectTimeout(connectTimeout, connectTimeoutUnit) - .followRedirects(followRedirects) - .readTimeout(readTimeout, readTimeoutUnit).connectionPool(connectionPool) - .connectionPool(connectionPool); + public OkHttpClient.Builder createBuilder(boolean disableSslValidation) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); if (disableSslValidation) { try { X509TrustManager disabledTrustManager = new DisableValidationTrustManager(); @@ -49,9 +42,6 @@ public class DefaultOkHttpClientFactory implements OkHttpClientFactory { LOG.warn("Error setting SSLSocketFactory in OKHttpClient", e); } } - if (sslSocketFactory != null && x509TrustManager != null) { - builder.sslSocketFactory(sslSocketFactory, x509TrustManager); - } - return builder.build(); + return builder; } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java index f64ab8a0..f992ff17 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java @@ -32,7 +32,7 @@ public class HttpClientConfiguration { } @Configuration - @ConditionalOnProperty(name = "spring.cloud.httpclient.ok.enabled") + @ConditionalOnProperty(name = "spring.cloud.httpclient.ok.enabled", matchIfMissing = true) @ConditionalOnClass(OkHttpClient.class) static class OkHttpClientConfiguration { diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java index b7857e99..e4323ca0 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java @@ -18,22 +18,11 @@ import javax.net.ssl.X509TrustManager; public interface OkHttpClientFactory { /** - * Creates a new {@link OkHttpClient}. - * @param disableSslValidation To disable SSL validation - * @param connectTimeout Connection timeout duration - * @param connectTimeoutUnit Connection timeout time unit - * @param followRedirects Whether to follow redirects - * @param readTimeout Read timeout duration - * @param readTimeoutUnit Read timeout time unit - * @param connectionPool The connection pool to use - * @param sslSocketFactory The socket factory to use, can be {@code null} - * @param x509TrustManager The trust manager to use, can be {@code null} - * @return A new {@link OkHttpClient} + * Creates a {@link OkHttpClient.Builder} used to build an {@link OkHttpClient}. + * @param disableSslValidation Disables SSL validation + * @return A new {@link OkHttpClient.Builder} */ - public OkHttpClient create(boolean disableSslValidation, long connectTimeout, - TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout, - TimeUnit readTimeoutUnit, ConnectionPool connectionPool, - SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager); + public OkHttpClient.Builder createBuilder(boolean disableSslValidation); /** * A {@link X509TrustManager} that does not validate SSL certificates. diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java index 879e4010..51a1189d 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java @@ -6,10 +6,12 @@ import okhttp3.OkHttpClient; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; +import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -106,9 +108,8 @@ class CustomApplication { static class MyApacheHttpClientFactory implements ApacheHttpClientFactory { @Override - public CloseableHttpClient createClient(RequestConfig requestConfig, - HttpClientConnectionManager connectionManager) { - return null; + public HttpClientBuilder createBuilder() { + return HttpClientBuilder.create(); } } @@ -125,13 +126,9 @@ class CustomApplication { } static class MyOkHttpClientFactory implements OkHttpClientFactory { - @Override - public OkHttpClient create(boolean disableSslValidation, long connectTimeout, - TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout, - TimeUnit readTimeoutUnit, ConnectionPool connectionPool, - SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager) { - return null; + public OkHttpClient.Builder createBuilder(boolean disableSslValidation) { + return new OkHttpClient.Builder(); } } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java index 1f31d632..3a69ea72 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java @@ -6,6 +6,7 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.Configurable; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.assertj.core.api.Assertions; import org.junit.Test; import org.springframework.util.ReflectionUtils; @@ -21,8 +22,9 @@ public class DefaultApacheHttpClientFactoryTests { public void createClient() throws Exception { final RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100) .setConnectTimeout(200).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build(); - CloseableHttpClient httpClient = new DefaultApacheHttpClientFactory() - .createClient(requestConfig, mock(HttpClientConnectionManager.class)); + CloseableHttpClient httpClient = new DefaultApacheHttpClientFactory().createBuilder(). + setConnectionManager(mock(HttpClientConnectionManager.class)). + setDefaultRequestConfig(requestConfig).build(); Assertions.assertThat(httpClient).isInstanceOf(Configurable.class); RequestConfig config = ((Configurable) httpClient).getConfig(); assertEquals(100, config.getSocketTimeout()); diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java index 476caf21..10c40516 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java @@ -21,8 +21,11 @@ public class DefaultOkHttpClientFactoryTest { DefaultOkHttpClientFactory okHttpClientFactory = new DefaultOkHttpClientFactory(); DefaultOkHttpClientConnectionPoolFactory poolFactory = new DefaultOkHttpClientConnectionPoolFactory(); ConnectionPool pool = poolFactory.create(4, 5, TimeUnit.DAYS); - OkHttpClient httpClient = okHttpClientFactory.create(true, 2, - TimeUnit.MILLISECONDS, true, 3, TimeUnit.HOURS, pool, null, null); + OkHttpClient httpClient = okHttpClientFactory.createBuilder(true). + connectTimeout(2, TimeUnit.MILLISECONDS). + readTimeout(3, TimeUnit.HOURS). + followRedirects(true). + connectionPool(pool).build(); int connectTimeout = getField(httpClient, "connectTimeout"); assertEquals(2, connectTimeout); int readTimeout = getField(httpClient, "readTimeout"); From 3d47188334c2a011f91c8c5a5ab6a337ad8b7b3f Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Sun, 9 Jul 2017 14:15:11 -0400 Subject: [PATCH 6/8] Added documentation --- docs/src/main/asciidoc/spring-cloud-commons.adoc | 13 +++++++++++++ .../commons/httpclient/HttpClientConfiguration.java | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-commons.adoc b/docs/src/main/asciidoc/spring-cloud-commons.adoc index b9921306..0dc29a29 100644 --- a/docs/src/main/asciidoc/spring-cloud-commons.adoc +++ b/docs/src/main/asciidoc/spring-cloud-commons.adoc @@ -487,3 +487,16 @@ spring: inetutils: useOnlySiteLocalInterfaces: true ---- + +[[http-clients]] +=== HTTP Client Factories + +Spring Cloud Commons provides beans for creating both Apache HTTP clients (`ApacheHttpClientFactory`) +as well as OK HTTP clients (`OkHttpClientFactory`). The `OkHttpClientFactory` bean will only be created +if the OK HTTP jar is on the classpath. In addition, Spring Cloud Commons provides beans for creating +the connection managers used by both clients, `ApacheHttpClientConnectionManagerFactory` for the Apache +HTTP client and `OkHttpClientConnectionPoolFactory` for the OK HTTP client. You can provide +your own implementation of these beans if you would like to customize how the HTTP clients are created +in downstream projects. You can also disable the creation of these beans by setting +`spring.cloud.httpclientfactories.apache.enabled` or `spring.cloud.httpclientfactories.ok.enabled` to +`false`. \ No newline at end of file diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java index f992ff17..df3de178 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java @@ -15,7 +15,7 @@ import org.springframework.context.annotation.Configuration; public class HttpClientConfiguration { @Configuration - @ConditionalOnProperty(name = "spring.cloud.httpclient.apache.enabled", matchIfMissing = true) + @ConditionalOnProperty(name = "spring.cloud.httpclientfactories.apache.enabled", matchIfMissing = true) static class ApacheHttpClientConfiguration { @Bean @@ -32,7 +32,7 @@ public class HttpClientConfiguration { } @Configuration - @ConditionalOnProperty(name = "spring.cloud.httpclient.ok.enabled", matchIfMissing = true) + @ConditionalOnProperty(name = "spring.cloud.httpclientfactories.ok.enabled", matchIfMissing = true) @ConditionalOnClass(OkHttpClient.class) static class OkHttpClientConfiguration { From 2250c836a42e31ff63775381542b8d40a4dedf93 Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Fri, 21 Jul 2017 11:40:08 -0700 Subject: [PATCH 7/8] Remove dependence on lombok (#231) --- spring-cloud-commons/pom.xml | 6 -- .../cloud/client/DefaultServiceInstance.java | 66 +++++++++++++++-- .../client/actuator/FeaturesEndpoint.java | 55 ++++++++++++-- .../cloud/client/actuator/NamedFeature.java | 30 +++++++- .../DiscoveryClientHealthIndicator.java | 8 ++- .../NoopDiscoveryClientAutoConfiguration.java | 9 +-- .../CloudHypermediaAutoConfiguration.java | 29 ++++++-- .../client/hypermedia/DiscoveredResource.java | 42 ++++++++--- .../DynamicServiceInstanceProvider.java | 16 +++-- .../hypermedia/RemoteResourceRefresher.java | 17 +++-- .../StaticServiceInstanceProvider.java | 15 ++-- .../cloud/commons/util/InetUtils.java | 34 +++++++-- .../commons/util/InetUtilsProperties.java | 71 ++++++++++++++++++- .../util/SpringFactoryImportSelector.java | 8 ++- ...actLoadBalancerAutoConfigurationTests.java | 34 ++++++--- ...yncLoadBalancerAutoConfigurationTests.java | 31 ++++++-- 16 files changed, 388 insertions(+), 83 deletions(-) diff --git a/spring-cloud-commons/pom.xml b/spring-cloud-commons/pom.xml index 93c07f14..5af3d5a7 100644 --- a/spring-cloud-commons/pom.xml +++ b/spring-cloud-commons/pom.xml @@ -96,12 +96,6 @@ json-path true - - org.projectlombok - lombok - compile - true - org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/DefaultServiceInstance.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/DefaultServiceInstance.java index 566a1228..d7e710c2 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/DefaultServiceInstance.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/DefaultServiceInstance.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,17 @@ package org.springframework.cloud.client; + import java.net.URI; import java.util.LinkedHashMap; import java.util.Map; - -import lombok.Data; -import lombok.RequiredArgsConstructor; +import java.util.Objects; /** * Default implementation of {@link ServiceInstance}. * * @author Spencer Gibb */ -@Data -@RequiredArgsConstructor public class DefaultServiceInstance implements ServiceInstance { private final String serviceId; @@ -42,6 +39,15 @@ public class DefaultServiceInstance implements ServiceInstance { private final Map metadata; + public DefaultServiceInstance(String serviceId, String host, int port, boolean secure, + Map metadata) { + this.serviceId = serviceId; + this.host = host; + this.port = port; + this.secure = secure; + this.metadata = metadata; + } + public DefaultServiceInstance(String serviceId, String host, int port, boolean secure) { this(serviceId, host, port, secure, new LinkedHashMap()); @@ -68,4 +74,52 @@ public class DefaultServiceInstance implements ServiceInstance { instance.getPort()); return URI.create(uri); } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public String getHost() { + return host; + } + + @Override + public int getPort() { + return port; + } + + @Override + public boolean isSecure() { + return secure; + } + + @Override + public String toString() { + return "DefaultServiceInstance{" + + "serviceId='" + serviceId + '\'' + + ", host='" + host + '\'' + + ", port=" + port + + ", secure=" + secure + + ", metadata=" + metadata + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DefaultServiceInstance that = (DefaultServiceInstance) o; + return port == that.port && + secure == that.secure && + Objects.equals(serviceId, that.serviceId) && + Objects.equals(host, that.host) && + Objects.equals(metadata, that.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(serviceId, host, port, secure, metadata); + } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/FeaturesEndpoint.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/FeaturesEndpoint.java index 7e6e19c4..319e27c6 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/FeaturesEndpoint.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/FeaturesEndpoint.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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. + */ + package org.springframework.cloud.client.actuator; import java.util.ArrayList; @@ -10,7 +26,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import lombok.Value; /** * @author Spencer Gibb @@ -75,17 +90,47 @@ public class FeaturesEndpoint extends AbstractEndpoint enabled = new ArrayList<>(); - List disabled = new ArrayList<>(); + final List enabled = new ArrayList<>(); + final List disabled = new ArrayList<>(); + + public List getEnabled() { + return enabled; + } + + public List getDisabled() { + return disabled; + } } - @Value + class Feature { final String type; final String name; final String version; final String vendor; + + public Feature(String type, String name, String version, String vendor) { + this.type = type; + this.name = name; + this.version = version; + this.vendor = vendor; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getVendor() { + return vendor; + } } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/NamedFeature.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/NamedFeature.java index f5062052..572263e0 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/NamedFeature.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/actuator/NamedFeature.java @@ -1,12 +1,38 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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. + */ package org.springframework.cloud.client.actuator; -import lombok.Value; /** * @author Spencer Gibb */ -@Value public class NamedFeature { private final String name; private final Class type; + + public NamedFeature(String name, Class type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public Class getType() { + return type; + } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/health/DiscoveryClientHealthIndicator.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/health/DiscoveryClientHealthIndicator.java index 75ec4a2b..8f86a386 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/health/DiscoveryClientHealthIndicator.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/health/DiscoveryClientHealthIndicator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package org.springframework.cloud.client.discovery.health; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; import org.springframework.cloud.client.discovery.DiscoveryClient; @@ -26,12 +28,10 @@ import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; -import lombok.extern.apachecommons.CommonsLog; /** * @author Spencer Gibb */ -@CommonsLog public class DiscoveryClientHealthIndicator implements DiscoveryHealthIndicator, Ordered, ApplicationListener> { @@ -42,6 +42,8 @@ public class DiscoveryClientHealthIndicator implements DiscoveryHealthIndicator, private final DiscoveryClient discoveryClient; private final DiscoveryClientHealthIndicatorProperties properties; + private final Log log = LogFactory.getLog(DiscoveryClientHealthIndicator.class); + @Deprecated public DiscoveryClientHealthIndicator(DiscoveryClient discoveryClient) { this(discoveryClient, new DiscoveryClientHealthIndicatorProperties()); diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/noop/NoopDiscoveryClientAutoConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/noop/NoopDiscoveryClientAutoConfiguration.java index e185b42b..c356d87c 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/noop/NoopDiscoveryClientAutoConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/noop/NoopDiscoveryClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ import java.net.UnknownHostException; import javax.annotation.PostConstruct; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -38,8 +40,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.env.Environment; -import lombok.extern.apachecommons.CommonsLog; - /** * * @deprecated Use @@ -50,7 +50,6 @@ import lombok.extern.apachecommons.CommonsLog; @Configuration @EnableConfigurationProperties @ConditionalOnMissingBean(DiscoveryClient.class) -@CommonsLog @Deprecated public class NoopDiscoveryClientAutoConfiguration implements ApplicationListener { @@ -69,6 +68,8 @@ public class NoopDiscoveryClientAutoConfiguration private DefaultServiceInstance serviceInstance; + private final Log log = LogFactory.getLog(NoopDiscoveryClientAutoConfiguration.class); + @PostConstruct public void init() { String host = "localhost"; diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/CloudHypermediaAutoConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/CloudHypermediaAutoConfiguration.java index a30f4f0c..8f31161e 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/CloudHypermediaAutoConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/CloudHypermediaAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package org.springframework.cloud.client.hypermedia; -import lombok.Data; import java.util.Collections; import java.util.List; @@ -50,17 +49,39 @@ public class CloudHypermediaAutoConfiguration { properties.getRefresh().getInitialDelay()); } - @Data @ConfigurationProperties(prefix = "spring.cloud.hypermedia") public static class CloudHypermediaProperties { private Refresh refresh = new Refresh(); - @Data + public Refresh getRefresh() { + return refresh; + } + + public void setRefresh(Refresh refresh) { + this.refresh = refresh; + } + public static class Refresh { private int fixedDelay = 5000; private int initialDelay = 10000; + + public int getFixedDelay() { + return fixedDelay; + } + + public void setFixedDelay(int fixedDelay) { + this.fixedDelay = fixedDelay; + } + + public int getInitialDelay() { + return initialDelay; + } + + public void setInitialDelay(int initialDelay) { + this.initialDelay = initialDelay; + } } } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DiscoveredResource.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DiscoveredResource.java index 6a9737a1..cae5a18e 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DiscoveredResource.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DiscoveredResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,10 @@ */ package org.springframework.cloud.client.hypermedia; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - import java.net.URI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.cloud.client.ServiceInstance; import org.springframework.hateoas.Link; import org.springframework.hateoas.MediaTypes; @@ -35,15 +33,22 @@ import org.springframework.web.client.RestTemplate; * * @author Oliver Gierke */ -@Slf4j -@RequiredArgsConstructor public class DiscoveredResource implements RemoteResource { private final ServiceInstanceProvider provider; private final TraversalDefinition traversal; + private RestOperations restOperations = new RestTemplate(); - private @Getter Link link = null; + private Link link = null; + + private final Logger log = LoggerFactory.getLogger(DiscoveredResource.class); + + + public DiscoveredResource(ServiceInstanceProvider provider, TraversalDefinition traversal) { + this.provider = provider; + this.traversal = traversal; + } /** * Configures the {@link RestOperations} to use to execute the traversal and verifying HEAD calls. @@ -54,6 +59,27 @@ public class DiscoveredResource implements RemoteResource { this.restOperations = restOperations == null ? new RestTemplate() : restOperations; } + public ServiceInstanceProvider getProvider() { + return provider; + } + + public TraversalDefinition getTraversal() { + return traversal; + } + + public RestOperations getRestOperations() { + return restOperations; + } + + @Override + public Link getLink() { + return link; + } + + public void setLink(Link link) { + this.link = link; + } + /** * Verifies the link to the current */ diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DynamicServiceInstanceProvider.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DynamicServiceInstanceProvider.java index 7f33015a..fa32769c 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DynamicServiceInstanceProvider.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/DynamicServiceInstanceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ */ package org.springframework.cloud.client.hypermedia; -import lombok.RequiredArgsConstructor; - import java.util.List; import org.springframework.cloud.client.ServiceInstance; @@ -28,16 +26,20 @@ import org.springframework.cloud.client.discovery.DiscoveryClient; * * @author Oliver Gierke */ -@RequiredArgsConstructor public class DynamicServiceInstanceProvider implements ServiceInstanceProvider { private final DiscoveryClient client; private final String serviceName; + public DynamicServiceInstanceProvider(DiscoveryClient client, String serviceName) { + this.client = client; + this.serviceName = serviceName; + } + /* - * (non-Javadoc) - * @see example.customers.integration.ServiceInstanceProvider#getServiceInstance() - */ + * (non-Javadoc) + * @see example.customers.integration.ServiceInstanceProvider#getServiceInstance() + */ @Override public ServiceInstance getServiceInstance() { diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/RemoteResourceRefresher.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/RemoteResourceRefresher.java index d3effdad..f167d57a 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/RemoteResourceRefresher.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/RemoteResourceRefresher.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ */ package org.springframework.cloud.client.hypermedia; -import lombok.RequiredArgsConstructor; - import java.util.List; import org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar; @@ -29,16 +27,21 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar; * * @author Oliver Gierke */ -@RequiredArgsConstructor public class RemoteResourceRefresher extends ContextLifecycleScheduledTaskRegistrar { private final List discoveredResources; private final int fixedDelay, initialDelay; + public RemoteResourceRefresher(List discoveredResources, int fixedDelay, int initialDelay) { + this.discoveredResources = discoveredResources; + this.fixedDelay = fixedDelay; + this.initialDelay = initialDelay; + } + /* - * (non-Javadoc) - * @see org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar#afterPropertiesSet() - */ + * (non-Javadoc) + * @see org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar#afterPropertiesSet() + */ @Override public void afterPropertiesSet() { diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/StaticServiceInstanceProvider.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/StaticServiceInstanceProvider.java index dfebb846..1dc7715a 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/StaticServiceInstanceProvider.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/hypermedia/StaticServiceInstanceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ */ package org.springframework.cloud.client.hypermedia; -import lombok.RequiredArgsConstructor; - import org.springframework.cloud.client.ServiceInstance; /** @@ -24,15 +22,18 @@ import org.springframework.cloud.client.ServiceInstance; * * @author Oliver Gierke */ -@RequiredArgsConstructor public class StaticServiceInstanceProvider implements ServiceInstanceProvider { private final ServiceInstance instance; + public StaticServiceInstanceProvider(ServiceInstance instance) { + this.instance = instance; + } + /* - * (non-Javadoc) - * @see example.customers.integration.ServiceInstanceProvider#getServiceInstance() - */ + * (non-Javadoc) + * @see example.customers.integration.ServiceInstanceProvider#getServiceInstance() + */ @Override public ServiceInstance getServiceInstance() { return instance; diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtils.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtils.java index e8de986f..34f31a30 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtils.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtils.java @@ -31,20 +31,21 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import lombok.Data; -import lombok.extern.apachecommons.CommonsLog; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * @author Spencer Gibb */ -@CommonsLog public class InetUtils implements Closeable { // TODO: maybe shutdown the thread pool if it isn't being used? private final ExecutorService executorService; private final InetUtilsProperties properties; private static final InetUtils instance = new InetUtils(new InetUtilsProperties()); - + + private final Log log = LogFactory.getLog(InetUtils.class); + public InetUtils(final InetUtilsProperties properties) { this.properties = properties; this.executorService = Executors @@ -200,7 +201,6 @@ public class InetUtils implements Closeable { return new HostInfo(host).getIpAddressAsInt(); } - @Data public static class HostInfo { public boolean override; private String ipAddress; @@ -227,6 +227,30 @@ public class InetUtils implements Closeable { } return ByteBuffer.wrap(inetAddress.getAddress()).getInt(); } + + public boolean isOverride() { + return override; + } + + public void setOverride(boolean override) { + this.override = override; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtilsProperties.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtilsProperties.java index 7fae030f..d4ec4613 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtilsProperties.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/InetUtilsProperties.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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. + */ + package org.springframework.cloud.commons.util; import java.net.InetAddress; @@ -7,12 +23,9 @@ import java.util.List; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; -import lombok.Data; - /** * @author Spencer Gibb */ -@Data @ConfigurationProperties(InetUtilsProperties.PREFIX) public class InetUtilsProperties { public static final String PREFIX = "spring.cloud.inetutils"; @@ -47,4 +60,56 @@ public class InetUtilsProperties { * List of Java regex expressions for network addresses that will be preferred. */ private List preferredNetworks = new ArrayList<>(); + + public static String getPREFIX() { + return PREFIX; + } + + public String getDefaultHostname() { + return defaultHostname; + } + + public void setDefaultHostname(String defaultHostname) { + this.defaultHostname = defaultHostname; + } + + public String getDefaultIpAddress() { + return defaultIpAddress; + } + + public void setDefaultIpAddress(String defaultIpAddress) { + this.defaultIpAddress = defaultIpAddress; + } + + public int getTimeoutSeconds() { + return timeoutSeconds; + } + + public void setTimeoutSeconds(int timeoutSeconds) { + this.timeoutSeconds = timeoutSeconds; + } + + public List getIgnoredInterfaces() { + return ignoredInterfaces; + } + + public void setIgnoredInterfaces(List ignoredInterfaces) { + this.ignoredInterfaces = ignoredInterfaces; + } + + public boolean isUseOnlySiteLocalInterfaces() { + return useOnlySiteLocalInterfaces; + } + + public void setUseOnlySiteLocalInterfaces(boolean useOnlySiteLocalInterfaces) { + this.useOnlySiteLocalInterfaces = useOnlySiteLocalInterfaces; + } + + public List getPreferredNetworks() { + return preferredNetworks; + } + + public void setPreferredNetworks(List preferredNetworks) { + this.preferredNetworks = preferredNetworks; + } } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/SpringFactoryImportSelector.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/SpringFactoryImportSelector.java index 3e72e5f9..ccc8d00e 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/SpringFactoryImportSelector.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/util/SpringFactoryImportSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.DeferredImportSelector; @@ -30,7 +32,6 @@ import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; -import lombok.extern.apachecommons.CommonsLog; /** * Selects configurations to load defined by the generic type T. Loads implementations @@ -39,7 +40,6 @@ import lombok.extern.apachecommons.CommonsLog; * @author Spencer Gibb * @author Dave Syer */ -@CommonsLog public abstract class SpringFactoryImportSelector implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware { @@ -49,6 +49,8 @@ public abstract class SpringFactoryImportSelector private Environment environment; + private final Log log = LogFactory.getLog(SpringFactoryImportSelector.class); + @SuppressWarnings("unchecked") protected SpringFactoryImportSelector() { this.annotationClass = (Class) GenericTypeResolver diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AbstractLoadBalancerAutoConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AbstractLoadBalancerAutoConfigurationTests.java index 52f5a290..9e8184a9 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AbstractLoadBalancerAutoConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AbstractLoadBalancerAutoConfigurationTests.java @@ -1,20 +1,33 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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. + */ + package org.springframework.cloud.client.loadbalancer; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import java.io.IOException; import java.net.URI; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Random; -import lombok.SneakyThrows; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +38,6 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; -import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; /** @@ -138,15 +150,21 @@ public abstract class AbstractLoadBalancerAutoConfigurationTests { } @Override - @SneakyThrows public T execute(String serviceId, LoadBalancerRequest request) { - return request.apply(choose(serviceId)); + try { + return request.apply(choose(serviceId)); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Override - @SneakyThrows public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException { - return request.apply(choose(serviceId)); + try { + return request.apply(choose(serviceId)); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Override diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerAutoConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerAutoConfigurationTests.java index 1d66d9ed..e270f5ee 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerAutoConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AsyncLoadBalancerAutoConfigurationTests.java @@ -1,6 +1,21 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed 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. + */ + package org.springframework.cloud.client.loadbalancer; -import lombok.SneakyThrows; import org.hamcrest.MatcherAssert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -140,15 +155,21 @@ public class AsyncLoadBalancerAutoConfigurationTests { } @Override - @SneakyThrows public T execute(String serviceId, LoadBalancerRequest request) { - return request.apply(choose(serviceId)); + try { + return request.apply(choose(serviceId)); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Override - @SneakyThrows public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException { - return request.apply(choose(serviceId)); + try { + return request.apply(choose(serviceId)); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Override From 94a42737ec482a47632c334ecac7eb03f4000c3d Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Tue, 25 Jul 2017 17:58:15 +0200 Subject: [PATCH 8/8] Fix empty keys in EnvironmentChangedEvent on resetting the Environment (#229) When the Environment is resetted the EnvironmentChangedEvent always contains an empty keySet, since the live view of the cleared map was used. This commit fixes this. --- .../environment/EnvironmentManager.java | 3 +- .../environment/EnvironmentManagerTest.java | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-context/src/test/java/org/springframework/cloud/context/environment/EnvironmentManagerTest.java diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/environment/EnvironmentManager.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/environment/EnvironmentManager.java index 91e625cf..6b4b7639 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/environment/EnvironmentManager.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/environment/EnvironmentManager.java @@ -68,9 +68,8 @@ public class EnvironmentManager implements ApplicationEventPublisherAware { public Map reset() { Map result = new LinkedHashMap(map); if (!map.isEmpty()) { - Set keys = map.keySet(); map.clear(); - publish(new EnvironmentChangeEvent(keys)); + publish(new EnvironmentChangeEvent(result.keySet())); } return result; } diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/context/environment/EnvironmentManagerTest.java b/spring-cloud-context/src/test/java/org/springframework/cloud/context/environment/EnvironmentManagerTest.java new file mode 100644 index 00000000..518afe74 --- /dev/null +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/context/environment/EnvironmentManagerTest.java @@ -0,0 +1,45 @@ +package org.springframework.cloud.context.environment; + + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class EnvironmentManagerTest { + + @Test + public void testCorrectEvents() { + MockEnvironment environment = new MockEnvironment(); + ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class); + EnvironmentManager environmentManager = new EnvironmentManager(environment); + environmentManager.setApplicationEventPublisher(publisher); + + environmentManager.setProperty("foo", "bar"); + + assertThat(environment.getProperty("foo")).isEqualTo("bar"); + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(ApplicationEvent.class); + verify(publisher, times(1)).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue()).isInstanceOf(EnvironmentChangeEvent.class); + EnvironmentChangeEvent event = (EnvironmentChangeEvent) eventCaptor.getValue(); + assertThat(event.getKeys()).containsExactly("foo"); + + reset(publisher); + + environmentManager.reset(); + assertThat(environment.getProperty("foo")).isNull(); + verify(publisher, times(1)).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue()).isInstanceOf(EnvironmentChangeEvent.class); + event = (EnvironmentChangeEvent) eventCaptor.getValue(); + assertThat(event.getKeys()).containsExactly("foo"); + } + +} \ No newline at end of file