Merge remote-tracking branch 'origin/master' into 2.0.x

This commit is contained in:
Ryan Baxter
2017-08-03 11:44:44 -04:00
35 changed files with 1233 additions and 83 deletions

View File

@@ -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`.

View File

@@ -13,6 +13,10 @@
<packaging>jar</packaging>
<name>Spring Cloud Commons</name>
<description>Spring Cloud Commons</description>
<properties>
<okhttp3.version>3.6.0</okhttp3.version>
<apachehttpclient.version>4.5.1</apachehttpclient.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -75,11 +79,22 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>${okhttp3.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${apachehttpclient.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -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<String, String> metadata;
public DefaultServiceInstance(String serviceId, String host, int port, boolean secure,
Map<String, String> 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<String, String>());
@@ -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);
}
}

View File

@@ -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<FeaturesEndpoint.Features
type.getPackage().getImplementationVendor()));
}
@Value
class Features {
List<Feature> enabled = new ArrayList<>();
List<String> disabled = new ArrayList<>();
final List<Feature> enabled = new ArrayList<>();
final List<String> disabled = new ArrayList<>();
public List<Feature> getEnabled() {
return enabled;
}
public List<String> 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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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<InstanceRegisteredEvent<?>> {
@@ -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());

View File

@@ -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;
@@ -36,8 +38,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
@@ -48,7 +48,6 @@ import lombok.extern.apachecommons.CommonsLog;
@Configuration
@EnableConfigurationProperties
@ConditionalOnMissingBean(DiscoveryClient.class)
@CommonsLog
@Deprecated
public class NoopDiscoveryClientAutoConfiguration
implements ApplicationListener<ContextRefreshedEvent> {
@@ -67,6 +66,8 @@ public class NoopDiscoveryClientAutoConfiguration
private DefaultServiceInstance serviceInstance;
private final Log log = LogFactory.getLog(NoopDiscoveryClientAutoConfiguration.class);
@PostConstruct
public void init() {
String host = "localhost";

View File

@@ -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;
}
}
}
}

View File

@@ -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
*/

View File

@@ -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() {

View File

@@ -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<RemoteResource> discoveredResources;
private final int fixedDelay, initialDelay;
public RemoteResourceRefresher(List<RemoteResource> 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() {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -0,0 +1,37 @@
/*
*
* * 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;
import org.apache.http.impl.client.HttpClientBuilder;
/**
* Factory for creating a new {@link CloseableHttpClient}.
* @author Ryan Baxter
*/
public interface ApacheHttpClientFactory {
/**
* Creates an {@link HttpClientBuilder} that can be used to create a new {@link CloseableHttpClient}.
* @return A {@link HttpClientBuilder}
*/
public HttpClientBuilder createBuilder();
}

View File

@@ -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.<ConnectionSocketFactory> 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<ConnectionSocketFactory> registry = registryBuilder.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
registry, null, null, null, timeToLive, timeUnit);
connectionManager.setMaxTotal(maxTotalConnections);
connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute);
return connectionManager;
}
}

View File

@@ -0,0 +1,23 @@
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 {
/**
* 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()
.disableCookieManagement().useSystemProperties();
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,47 @@
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.Builder createBuilder(boolean disableSslValidation) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
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);
}
}
return builder;
}
}

View File

@@ -0,0 +1,51 @@
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;
import org.springframework.context.annotation.Configuration;
/**
* @author Ryan Baxter
*/
@Configuration
public class HttpClientConfiguration {
@Configuration
@ConditionalOnProperty(name = "spring.cloud.httpclientfactories.apache.enabled", matchIfMissing = true)
static class ApacheHttpClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ApacheHttpClientConnectionManagerFactory connManFactory() {
return new DefaultApacheHttpClientConnectionManagerFactory();
}
@Bean
@ConditionalOnMissingBean
public ApacheHttpClientFactory apacheHttpClientFactory() {
return new DefaultApacheHttpClientFactory();
}
}
@Configuration
@ConditionalOnProperty(name = "spring.cloud.httpclientfactories.ok.enabled", matchIfMissing = true)
@ConditionalOnClass(OkHttpClient.class)
static class OkHttpClientConfiguration {
@Bean
@ConditionalOnMissingBean
public OkHttpClientConnectionPoolFactory connPoolFactory() {
return new DefaultOkHttpClientConnectionPoolFactory();
}
@Bean
@ConditionalOnMissingBean
public OkHttpClientFactory okHttpClientFactory() {
return new DefaultOkHttpClientFactory();
}
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,54 @@
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 {@link OkHttpClient.Builder} used to build an {@link OkHttpClient}.
* @param disableSslValidation Disables SSL validation
* @return A new {@link OkHttpClient.Builder}
*/
public OkHttpClient.Builder createBuilder(boolean disableSslValidation);
/**
* 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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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<String> 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<String> getIgnoredInterfaces() {
return ignoredInterfaces;
}
public void setIgnoredInterfaces(List<String> ignoredInterfaces) {
this.ignoredInterfaces = ignoredInterfaces;
}
public boolean isUseOnlySiteLocalInterfaces() {
return useOnlySiteLocalInterfaces;
}
public void setUseOnlySiteLocalInterfaces(boolean useOnlySiteLocalInterfaces) {
this.useOnlySiteLocalInterfaces = useOnlySiteLocalInterfaces;
}
public List<String> getPreferredNetworks() {
return preferredNetworks;
}
public void setPreferredNetworks(List<String> preferredNetworks) {
this.preferredNetworks = preferredNetworks;
}
}

View File

@@ -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<T>
implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware {
@@ -49,6 +49,8 @@ public abstract class SpringFactoryImportSelector<T>
private Environment environment;
private final Log log = LogFactory.getLog(SpringFactoryImportSelector.class);
@SuppressWarnings("unchecked")
protected SpringFactoryImportSelector() {
this.annotationClass = (Class<T>) GenericTypeResolver

View File

@@ -8,7 +8,8 @@ org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
org.springframework.cloud.commons.httpclient.HttpClientConfiguration
# Environment Post Processors

View File

@@ -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> T execute(String serviceId, LoadBalancerRequest<T> request) {
return request.apply(choose(serviceId));
try {
return request.apply(choose(serviceId));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
@SneakyThrows
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
return request.apply(choose(serviceId));
try {
return request.apply(choose(serviceId));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override

View File

@@ -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> T execute(String serviceId, LoadBalancerRequest<T> request) {
return request.apply(choose(serviceId));
try {
return request.apply(choose(serviceId));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
@SneakyThrows
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
return request.apply(choose(serviceId));
try {
return request.apply(choose(serviceId));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override

View File

@@ -0,0 +1,142 @@
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.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;
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, properties = {"spring.cloud.httpclient.ok.enabled: true"})
public class CustomHttpClientConfigurationTests {
@Autowired
ApacheHttpClientFactory httpClientFactory;
@Autowired
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
@Autowired
OkHttpClientFactory okHttpClientFactory;
@Autowired
OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory;
@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));
}
@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
@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
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 {
@Override
public HttpClientBuilder createBuilder() {
return HttpClientBuilder.create();
}
}
static class MyApacheHttpClientConnectionManagerFactory
implements ApacheHttpClientConnectionManagerFactory {
@Override
public HttpClientConnectionManager newConnectionManager(
boolean disableSslValidation, int maxTotalConnections,
int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit,
RegistryBuilder registryBuilder) {
return null;
}
}
static class MyOkHttpClientFactory implements OkHttpClientFactory {
@Override
public OkHttpClient.Builder createBuilder(boolean disableSslValidation) {
return new OkHttpClient.Builder();
}
}
static class MyOkHttpConnectionPoolFactory implements OkHttpClientConnectionPoolFactory {
@Override
public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
return null;
}
}
}

View File

@@ -0,0 +1,53 @@
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.assertEquals;
/**
* @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, ((Long)getField(pool, "timeToLive")).longValue());
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, ((Long)getField(pool, "timeToLive")).longValue());
TimeUnit timeUnit = getField(pool, "tunit");
assertEquals(TimeUnit.DAYS, timeUnit);
}
protected <T> 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;
}
}

View File

@@ -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.apache.http.impl.client.HttpClientBuilder;
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().createBuilder().
setConnectionManager(mock(HttpClientConnectionManager.class)).
setDefaultRequestConfig(requestConfig).build();
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> 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;
}
}

View File

@@ -0,0 +1,66 @@
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, properties = {"spring.cloud.httpclient.ok.enabled: true"})
public class DefaultHttpClientConfigurationTests {
@Autowired
ApacheHttpClientFactory httpClientFactory;
@Autowired
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
@Autowired
OkHttpClientFactory okHttpClientFactory;
@Autowired
OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory;
@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));
}
@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
@EnableAutoConfiguration
class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}

View File

@@ -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> 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;
}
}

View File

@@ -0,0 +1,47 @@
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.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");
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> 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;
}
}

View File

@@ -68,9 +68,8 @@ public class EnvironmentManager implements ApplicationEventPublisherAware {
public Map<String, Object> reset() {
Map<String, Object> result = new LinkedHashMap<String, Object>(map);
if (!map.isEmpty()) {
Set<String> keys = map.keySet();
map.clear();
publish(new EnvironmentChangeEvent(keys));
publish(new EnvironmentChangeEvent(result.keySet()));
}
return result;
}

View File

@@ -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<ApplicationEvent> 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");
}
}