17
17
package org .springframework .cloud .zookeeper .discovery .configclient ;
18
18
19
19
import java .util .Collections ;
20
+ import java .util .List ;
21
+ import java .util .stream .Stream ;
20
22
23
+ import org .apache .commons .logging .Log ;
24
+ import org .apache .curator .RetryPolicy ;
21
25
import org .apache .curator .framework .CuratorFramework ;
26
+ import org .apache .curator .retry .ExponentialBackoffRetry ;
22
27
import org .apache .curator .x .discovery .ServiceDiscovery ;
23
28
import org .apache .curator .x .discovery .ServiceDiscoveryBuilder ;
24
29
import org .apache .curator .x .discovery .details .InstanceSerializer ;
25
30
import org .apache .curator .x .discovery .details .JsonInstanceSerializer ;
26
31
32
+ import org .springframework .boot .BootstrapContext ;
27
33
import org .springframework .boot .BootstrapRegistry ;
28
34
import org .springframework .boot .BootstrapRegistryInitializer ;
29
35
import org .springframework .boot .context .properties .bind .BindHandler ;
30
36
import org .springframework .boot .context .properties .bind .Bindable ;
31
37
import org .springframework .boot .context .properties .bind .Binder ;
38
+ import org .springframework .cloud .client .ServiceInstance ;
32
39
import org .springframework .cloud .commons .util .InetUtils ;
33
40
import org .springframework .cloud .commons .util .InetUtilsProperties ;
34
41
import org .springframework .cloud .config .client .ConfigClientProperties ;
35
42
import org .springframework .cloud .config .client .ConfigServerInstanceProvider ;
36
43
import org .springframework .cloud .zookeeper .CuratorFactory ;
44
+ import org .springframework .cloud .zookeeper .ZookeeperProperties ;
37
45
import org .springframework .cloud .zookeeper .discovery .ConditionalOnZookeeperDiscoveryEnabled ;
38
46
import org .springframework .cloud .zookeeper .discovery .ZookeeperDiscoveryClient ;
39
47
import org .springframework .cloud .zookeeper .discovery .ZookeeperDiscoveryProperties ;
45
53
46
54
public class ZookeeperConfigServerBootstrapper implements BootstrapRegistryInitializer {
47
55
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
+
48
62
@ Override
49
63
@ SuppressWarnings ("unchecked" )
50
64
public void initialize (BootstrapRegistry registry ) {
@@ -95,20 +109,17 @@ public void initialize(BootstrapRegistry registry) {
95
109
}
96
110
ServiceDiscovery <ZookeeperInstance > serviceDiscovery = context .get (ServiceDiscovery .class );
97
111
ZookeeperDependencies dependencies = binder .bind (ZookeeperDependencies .PREFIX , Bindable
98
- .of (ZookeeperDependencies .class ), getBindHandler (context ))
112
+ .of (ZookeeperDependencies .class ), getBindHandler (context ))
99
113
.orElseGet (ZookeeperDependencies ::new );
100
114
ZookeeperDiscoveryProperties discoveryProperties = context .get (ZookeeperDiscoveryProperties .class );
101
115
102
116
return new ZookeeperDiscoveryClient (serviceDiscovery , dependencies , discoveryProperties );
103
117
});
104
118
105
119
// 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 );
112
123
113
124
// promote beans to context
114
125
registry .addCloseListener (event -> {
@@ -124,10 +135,63 @@ private BindHandler getBindHandler(org.springframework.boot.BootstrapContext con
124
135
return context .getOrElse (BindHandler .class , null );
125
136
}
126
137
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
+ }
131
195
}
132
196
133
197
}
0 commit comments