Skip to content

Commit 49be07a

Browse files
committed
Merge branch '3.1.x'
2 parents 6b7baa7 + 7347629 commit 49be07a

File tree

6 files changed

+281
-15
lines changed

6 files changed

+281
-15
lines changed

pom.xml

+22
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,26 @@
126126
<artifactId>spring-cloud-test-support</artifactId>
127127
<version>${spring-cloud-commons.version}</version>
128128
</dependency>
129+
<dependency>
130+
<groupId>org.testcontainers</groupId>
131+
<artifactId>testcontainers</artifactId>
132+
<version>${testcontainers.version}</version>
133+
</dependency>
134+
<dependency>
135+
<groupId>org.testcontainers</groupId>
136+
<artifactId>mockserver</artifactId>
137+
<version>${testcontainers.version}</version>
138+
</dependency>
139+
<dependency>
140+
<groupId>org.testcontainers</groupId>
141+
<artifactId>junit-jupiter</artifactId>
142+
<version>${testcontainers.version}</version>
143+
</dependency>
144+
<dependency>
145+
<groupId>org.mock-server</groupId>
146+
<artifactId>mockserver-client-java</artifactId>
147+
<version>${mockserverclient.version}</version>
148+
</dependency>
129149
</dependencies>
130150
</dependencyManagement>
131151

@@ -134,6 +154,8 @@
134154
<spring-cloud-commons.version>4.0.4-SNAPSHOT</spring-cloud-commons.version>
135155
<spring-cloud-config.version>4.0.4-SNAPSHOT</spring-cloud-config.version>
136156
<spring-cloud-openfeign.version>4.0.4-SNAPSHOT</spring-cloud-openfeign.version>
157+
<testcontainers.version>1.17.6</testcontainers.version>
158+
<mockserverclient.version>5.15.0</mockserverclient.version>
137159
</properties>
138160

139161
<profiles>

spring-cloud-zookeeper-discovery/pom.xml

+20
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,26 @@
156156
<artifactId>reactor-test</artifactId>
157157
<scope>test</scope>
158158
</dependency>
159+
<dependency>
160+
<groupId>org.testcontainers</groupId>
161+
<artifactId>testcontainers</artifactId>
162+
<scope>test</scope>
163+
</dependency>
164+
<dependency>
165+
<groupId>org.testcontainers</groupId>
166+
<artifactId>junit-jupiter</artifactId>
167+
<scope>test</scope>
168+
</dependency>
169+
<dependency>
170+
<groupId>org.testcontainers</groupId>
171+
<artifactId>mockserver</artifactId>
172+
<scope>test</scope>
173+
</dependency>
174+
<dependency>
175+
<groupId>org.mock-server</groupId>
176+
<artifactId>mockserver-client-java</artifactId>
177+
<scope>test</scope>
178+
</dependency>
159179
</dependencies>
160180

161181
</project>

spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerBootstrapper.java

+75-11
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,31 @@
1717
package org.springframework.cloud.zookeeper.discovery.configclient;
1818

1919
import java.util.Collections;
20+
import java.util.List;
21+
import java.util.stream.Stream;
2022

23+
import org.apache.commons.logging.Log;
24+
import org.apache.curator.RetryPolicy;
2125
import org.apache.curator.framework.CuratorFramework;
26+
import org.apache.curator.retry.ExponentialBackoffRetry;
2227
import org.apache.curator.x.discovery.ServiceDiscovery;
2328
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
2429
import org.apache.curator.x.discovery.details.InstanceSerializer;
2530
import org.apache.curator.x.discovery.details.JsonInstanceSerializer;
2631

32+
import org.springframework.boot.BootstrapContext;
2733
import org.springframework.boot.BootstrapRegistry;
2834
import org.springframework.boot.BootstrapRegistryInitializer;
2935
import org.springframework.boot.context.properties.bind.BindHandler;
3036
import org.springframework.boot.context.properties.bind.Bindable;
3137
import org.springframework.boot.context.properties.bind.Binder;
38+
import org.springframework.cloud.client.ServiceInstance;
3239
import org.springframework.cloud.commons.util.InetUtils;
3340
import org.springframework.cloud.commons.util.InetUtilsProperties;
3441
import org.springframework.cloud.config.client.ConfigClientProperties;
3542
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
3643
import org.springframework.cloud.zookeeper.CuratorFactory;
44+
import org.springframework.cloud.zookeeper.ZookeeperProperties;
3745
import org.springframework.cloud.zookeeper.discovery.ConditionalOnZookeeperDiscoveryEnabled;
3846
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient;
3947
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties;
@@ -45,6 +53,12 @@
4553

4654
public class ZookeeperConfigServerBootstrapper implements BootstrapRegistryInitializer {
4755

56+
private static boolean isEnabled(Binder binder) {
57+
return binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false) &&
58+
binder.bind(ConditionalOnZookeeperDiscoveryEnabled.PROPERTY, Boolean.class).orElse(true) &&
59+
binder.bind("spring.cloud.discovery.enabled", Boolean.class).orElse(true);
60+
}
61+
4862
@Override
4963
@SuppressWarnings("unchecked")
5064
public void initialize(BootstrapRegistry registry) {
@@ -95,20 +109,17 @@ public void initialize(BootstrapRegistry registry) {
95109
}
96110
ServiceDiscovery<ZookeeperInstance> serviceDiscovery = context.get(ServiceDiscovery.class);
97111
ZookeeperDependencies dependencies = binder.bind(ZookeeperDependencies.PREFIX, Bindable
98-
.of(ZookeeperDependencies.class), getBindHandler(context))
112+
.of(ZookeeperDependencies.class), getBindHandler(context))
99113
.orElseGet(ZookeeperDependencies::new);
100114
ZookeeperDiscoveryProperties discoveryProperties = context.get(ZookeeperDiscoveryProperties.class);
101115

102116
return new ZookeeperDiscoveryClient(serviceDiscovery, dependencies, discoveryProperties);
103117
});
104118

105119
// create instance provider
106-
registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, context -> {
107-
if (!isEnabled(context.get(Binder.class))) {
108-
return (id) -> Collections.emptyList();
109-
}
110-
return context.get(ZookeeperDiscoveryClient.class)::getInstances;
111-
});
120+
// We need to pass the lambda here so we do not create a new instance of ConfigServerInstanceProvider.Function
121+
// which would result in a ClassNotFoundException when Spring Cloud Config is not on the classpath
122+
registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, ZookeeperFunction::create);
112123

113124
// promote beans to context
114125
registry.addCloseListener(event -> {
@@ -124,10 +135,63 @@ private BindHandler getBindHandler(org.springframework.boot.BootstrapContext con
124135
return context.getOrElse(BindHandler.class, null);
125136
}
126137

127-
private boolean isEnabled(Binder binder) {
128-
return binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false) &&
129-
binder.bind(ConditionalOnZookeeperDiscoveryEnabled.PROPERTY, Boolean.class).orElse(true) &&
130-
binder.bind("spring.cloud.discovery.enabled", Boolean.class).orElse(true);
138+
/*
139+
* This Function is executed when loading config data. Because of this we cannot rely on the
140+
* BootstrapContext because Boot has not finished loading all the configuration data so if we
141+
* ask the BootstrapContext for configuration data it will not have it. The apply method in this function
142+
* is passed the Binder and BindHandler from the config data context which has the configuration properties that
143+
* have been loaded so far in the config data process.
144+
*
145+
* We will create many of the same beans in this function as we do above in the initializer above. We do both
146+
* to maintain compatibility since we are promoting those beans to the main application context.
147+
*/
148+
static final class ZookeeperFunction implements ConfigServerInstanceProvider.Function {
149+
150+
private final BootstrapContext context;
151+
152+
private ZookeeperFunction(BootstrapContext context) {
153+
this.context = context;
154+
}
155+
156+
static ZookeeperFunction create(BootstrapContext context) {
157+
return new ZookeeperFunction(context);
158+
}
159+
160+
@Override
161+
public List<ServiceInstance> apply(String serviceId) {
162+
return apply(serviceId, null, null, null);
163+
}
164+
165+
@Override
166+
public List<ServiceInstance> apply(String serviceId, Binder binder, BindHandler bindHandler, Log log) {
167+
if (binder == null || !isEnabled(binder)) {
168+
return Collections.emptyList();
169+
}
170+
171+
ZookeeperProperties properties = binder.bind(ZookeeperProperties.PREFIX, Bindable.of(ZookeeperProperties.class))
172+
.orElse(new ZookeeperProperties());
173+
RetryPolicy retryPolicy = new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(), properties.getMaxRetries(),
174+
properties.getMaxSleepMs());
175+
try {
176+
CuratorFramework curatorFramework = CuratorFactory.curatorFramework(properties, retryPolicy, Stream::of,
177+
() -> null, () -> null);
178+
InstanceSerializer<ZookeeperInstance> serializer = new JsonInstanceSerializer<>(ZookeeperInstance.class);
179+
ZookeeperDiscoveryProperties discoveryProperties = binder.bind(ZookeeperDiscoveryProperties.PREFIX, Bindable
180+
.of(ZookeeperDiscoveryProperties.class), bindHandler)
181+
.orElseGet(() -> new ZookeeperDiscoveryProperties(new InetUtils(new InetUtilsProperties())));
182+
DefaultServiceDiscoveryCustomizer customizer = new DefaultServiceDiscoveryCustomizer(curatorFramework, discoveryProperties, serializer);
183+
ServiceDiscovery<ZookeeperInstance> serviceDiscovery = customizer.customize(ServiceDiscoveryBuilder.builder(ZookeeperInstance.class));
184+
ZookeeperDependencies dependencies = binder.bind(ZookeeperDependencies.PREFIX, Bindable
185+
.of(ZookeeperDependencies.class), bindHandler)
186+
.orElseGet(ZookeeperDependencies::new);
187+
return new ZookeeperDiscoveryClient(serviceDiscovery, dependencies, discoveryProperties).getInstances(serviceId);
188+
}
189+
catch (Exception e) {
190+
log.warn("Error fetching config server instance from Zookeeper", e);
191+
return Collections.emptyList();
192+
}
193+
194+
}
131195
}
132196

133197
}

spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerBootstrapperTests.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.concurrent.atomic.AtomicReference;
2020

21+
import org.apache.commons.logging.Log;
2122
import org.apache.curator.framework.CuratorFramework;
2223
import org.junit.jupiter.api.AfterEach;
2324
import org.junit.jupiter.api.Test;
@@ -31,6 +32,7 @@
3132
import org.springframework.boot.context.properties.bind.BindContext;
3233
import org.springframework.boot.context.properties.bind.BindHandler;
3334
import org.springframework.boot.context.properties.bind.Bindable;
35+
import org.springframework.boot.context.properties.bind.Binder;
3436
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
3537
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
3638
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient;
@@ -39,6 +41,7 @@
3941

4042
import static org.assertj.core.api.Assertions.assertThat;
4143
import static org.assertj.core.api.Assertions.assertThatThrownBy;
44+
import static org.mockito.Mockito.mock;
4245

4346
public class ZookeeperConfigServerBootstrapperTests {
4447

@@ -59,7 +62,8 @@ public void notEnabledReturnsEmptyList() {
5962
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
6063
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
6164
.get(ConfigServerInstanceProvider.Function.class);
62-
assertThat(providerFn.apply("id")).as("ConfigServerInstanceProvider.Function should return empty list")
65+
Log log = mock(Log.class);
66+
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), log)).as("ConfigServerInstanceProvider.Function should return empty list")
6367
.isEmpty();
6468
})).run();
6569
CuratorFramework curatorFramework = context.getBean("curatorFramework", CuratorFramework.class);
@@ -80,7 +84,8 @@ public void zookeeperDiscoveryClientDisabledReturnsEmptyList() {
8084
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
8185
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
8286
.get(ConfigServerInstanceProvider.Function.class);
83-
assertThat(providerFn.apply("id")).as("ConfigServerInstanceProvider.Function should return empty list")
87+
Log log = mock(Log.class);
88+
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), log)).as("ConfigServerInstanceProvider.Function should return empty list")
8489
.isEmpty();
8590
})).run();
8691
CuratorFramework curatorFramework = context.getBean("curatorFramework", CuratorFramework.class);
@@ -101,7 +106,7 @@ public void discoveryClientDisabledReturnsEmptyList() {
101106
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
102107
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
103108
.get(ConfigServerInstanceProvider.Function.class);
104-
assertThat(providerFn.apply("id")).as("ConfigServerInstanceProvider.Function should return empty list")
109+
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), mock(Log.class))).as("ConfigServerInstanceProvider.Function should return empty list")
105110
.isEmpty();
106111
})).run();
107112
CuratorFramework curatorFramework = context.getBean("curatorFramework", CuratorFramework.class);
@@ -124,7 +129,7 @@ public void enabledAddsInstanceProviderFn() {
124129
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
125130
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
126131
.get(ConfigServerInstanceProvider.Function.class);
127-
assertThat(providerFn.apply("id")).as("Should return empty list.")
132+
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), mock(Log.class))).as("Should return empty list.")
128133
.isNotNull();
129134
bootstrapDiscoveryClient.set(event.getBootstrapContext().get(ZookeeperDiscoveryClient.class));
130135
})).run();

0 commit comments

Comments
 (0)