1717package org .springframework .cloud .zookeeper .discovery .configclient ;
1818
1919import 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 ;
2125import org .apache .curator .framework .CuratorFramework ;
26+ import org .apache .curator .retry .ExponentialBackoffRetry ;
2227import org .apache .curator .x .discovery .ServiceDiscovery ;
2328import org .apache .curator .x .discovery .ServiceDiscoveryBuilder ;
2429import org .apache .curator .x .discovery .details .InstanceSerializer ;
2530import org .apache .curator .x .discovery .details .JsonInstanceSerializer ;
2631
32+ import org .springframework .boot .BootstrapContext ;
2733import org .springframework .boot .BootstrapRegistry ;
2834import org .springframework .boot .BootstrapRegistryInitializer ;
2935import org .springframework .boot .context .properties .bind .BindHandler ;
3036import org .springframework .boot .context .properties .bind .Bindable ;
3137import org .springframework .boot .context .properties .bind .Binder ;
38+ import org .springframework .cloud .client .ServiceInstance ;
3239import org .springframework .cloud .commons .util .InetUtils ;
3340import org .springframework .cloud .commons .util .InetUtilsProperties ;
3441import org .springframework .cloud .config .client .ConfigClientProperties ;
3542import org .springframework .cloud .config .client .ConfigServerInstanceProvider ;
3643import org .springframework .cloud .zookeeper .CuratorFactory ;
44+ import org .springframework .cloud .zookeeper .ZookeeperProperties ;
3745import org .springframework .cloud .zookeeper .discovery .ConditionalOnZookeeperDiscoveryEnabled ;
3846import org .springframework .cloud .zookeeper .discovery .ZookeeperDiscoveryClient ;
3947import org .springframework .cloud .zookeeper .discovery .ZookeeperDiscoveryProperties ;
4553
4654public 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}
0 commit comments