diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java index 402898ba..9d7fa37b 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java @@ -21,12 +21,11 @@ import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; -import com.netflix.appinfo.EurekaInstanceConfig; -import com.netflix.discovery.EurekaClientConfig; +import com.netflix.discovery.EurekaClient; /** * Bootstrap configuration for a config client that wants to lookup the config server via @@ -36,36 +35,23 @@ import com.netflix.discovery.EurekaClientConfig; */ @ConditionalOnBean({ EurekaDiscoveryClientConfiguration.class }) @ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false) -@Configuration +@Configuration(value = "DiscoveryClientConfigServiceAutoConfiguration") public class DiscoveryClientConfigServiceAutoConfiguration { @Autowired - private EurekaClientConfig clientConfig; - - @Autowired - private EurekaInstanceConfig instanceConfig; - - @Autowired - private ConfigurationPropertiesBindingPostProcessor binder; - - @Autowired - private EurekaDiscoveryClientConfiguration clientConfiguration; + private ConfigurableApplicationContext context; @PostConstruct public void init() { - this.clientConfiguration.stop(); - rebind(this.clientConfig, "eurekaClientConfig"); - rebind(this.instanceConfig, "eurekaInstanceConfig"); - // Danger, here be dragons (once it shuts down it's hard to resurrect it) - // eurekaClient.shutdown(); - // FIXME: reinit EurekaClient and ApplicationInfoManager - // applicationInfoManager.initComponent(this.instanceConfig); - // discoveryManager.initComponent(this.instanceConfig, this.clientConfig); - this.clientConfiguration.start(); - } - - private void rebind(Object bean, String name) { - this.binder.postProcessBeforeInitialization(bean, name); + if (this.context.getParent() != null) { + if (this.context.getBeanNamesForType(EurekaClient.class).length > 0 + && this.context.getParent() + .getBeanNamesForType(EurekaClient.class).length > 0) { + // If the parent has a EurekaClient as well it should be shutdown, so the + // local one can register accurate instance info + this.context.getParent().getBean(EurekaClient.class).shutdown(); + } + } } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java index 4596e0e6..7349f539 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java @@ -16,20 +16,35 @@ package org.springframework.cloud.netflix.eureka; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.AllNestedConditions; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.cloud.client.CommonsClientAutoConfiguration; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration; +import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import com.netflix.appinfo.ApplicationInfoManager; @@ -51,11 +66,9 @@ import lombok.SneakyThrows; @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true) @AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class }) +@AutoConfigureAfter(RefreshAutoConfiguration.class) public class EurekaClientAutoConfiguration { - @Autowired - private ApplicationContext context; - @Value("${server.port:${SERVER_PORT:${PORT:8080}}}") int nonSecurePort; @@ -69,27 +82,19 @@ public class EurekaClientAutoConfiguration { } @Bean - @ConditionalOnMissingBean(EurekaClientConfig.class) + @ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT) public EurekaClientConfigBean eurekaClientConfigBean() { return new EurekaClientConfigBean(); } @Bean - @ConditionalOnMissingBean(EurekaInstanceConfig.class) + @ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT) public EurekaInstanceConfigBean eurekaInstanceConfigBean() { EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(); instance.setNonSecurePort(this.nonSecurePort); return instance; } - @Bean - @ConditionalOnMissingBean(EurekaClient.class) - @SneakyThrows - public EurekaClient eurekaClient(ApplicationInfoManager applicationInfoManager, - EurekaClientConfig config) { - return new CloudEurekaClient(applicationInfoManager, config, this.context); - } - @Bean @ConditionalOnMissingBean(ApplicationInfoManager.class) public ApplicationInfoManager applicationInfoManager(EurekaInstanceConfig config, @@ -109,4 +114,84 @@ public class EurekaClientAutoConfiguration { return new EurekaDiscoveryClient(config, client); } + @Configuration + @ConditionalOnMissingRefreshScope + protected static class EurekaClientConfiguration { + + @Autowired + private ApplicationContext context; + + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) + @SneakyThrows + public EurekaClient eurekaClient(ApplicationInfoManager applicationInfoManager, + EurekaClientConfig config, EurekaInstanceConfig instance) { + applicationInfoManager.initComponent(instance); + return new CloudEurekaClient(applicationInfoManager, config, this.context); + } + } + + @Configuration + @ConditionalOnRefreshScope + protected static class RefreshableEurekaClientConfiguration { + + @Autowired + private ApplicationContext context; + + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) + @SneakyThrows + @org.springframework.cloud.context.config.annotation.RefreshScope + public EurekaClient eurekaClient(ApplicationInfoManager applicationInfoManager, + EurekaClientConfig config, EurekaInstanceConfig instance) { + applicationInfoManager.initComponent(instance); + return new CloudEurekaClient(applicationInfoManager, config, this.context); + } + + } + + @Target({ElementType.TYPE, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Conditional(OnMissingRefreshScopeCondition.class) + @interface ConditionalOnMissingRefreshScope { + + } + + @Target({ElementType.TYPE, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Conditional(OnRefreshScopeCondition.class) + @interface ConditionalOnRefreshScope { + + } + + private static class OnMissingRefreshScopeCondition extends AnyNestedCondition { + + public OnMissingRefreshScopeCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnMissingClass("org.springframework.cloud.context.scope.refresh.RefreshScope") + static class MissingClass { + } + + @ConditionalOnMissingBean(RefreshScope.class) + static class MissingScope { + } + + } + + private static class OnRefreshScopeCondition extends AllNestedConditions { + + public OnRefreshScopeCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnClass(RefreshScope.class) + @ConditionalOnBean(RefreshScope.class) + static class FoundScope { + } + + } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java index df4c3666..eb69fb7c 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java @@ -35,7 +35,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; -import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.SmartLifecycle; import org.springframework.context.annotation.Bean; @@ -98,6 +98,7 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order // because of containerPortInitializer below if (!this.running.get() && this.instanceConfig.getNonSecurePort() > 0) { + this.eurekaClient.getApplications(); // force initialization log.info("Registering application " + this.instanceConfig.getAppname() + " with eureka with status " + this.instanceConfig.getInitialStatus()); @@ -159,13 +160,13 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order } @Configuration - @ConditionalOnClass(EnvironmentChangeEvent.class) + @ConditionalOnClass(RefreshScopeRefreshedEvent.class) protected static class EurekaClientConfigurationRefresher { @Autowired private EurekaDiscoveryClientConfiguration clientConfig; - @EventListener(EnvironmentChangeEvent.class) - public void onApplicationEvent(EnvironmentChangeEvent event) { + @EventListener(RefreshScopeRefreshedEvent.class) + public void onApplicationEvent(RefreshScopeRefreshedEvent event) { // register in case meta data changed this.clientConfig.stop(); this.clientConfig.start(); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/ConditionalOnRefreshScopeTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/ConditionalOnRefreshScopeTests.java new file mode 100644 index 00000000..74c8158e --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/ConditionalOnRefreshScopeTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015 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.netflix.eureka; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import org.junit.After; +import org.junit.Test; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration.ConditionalOnRefreshScope; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Dave Syer + * + */ +public class ConditionalOnRefreshScopeTests { + + private ConfigurableApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void refreshScopeIncluded() { + this.context = new SpringApplicationBuilder(RefreshAutoConfiguration.class, + Beans.class).web(false).showBanner(false).run(); + assertNotNull(this.context.getBean( + org.springframework.cloud.context.scope.refresh.RefreshScope.class)); + assertEquals("foo", this.context.getBean("foo")); + } + + @Test + public void refreshScopeNotIncluded() { + this.context = new SpringApplicationBuilder(Beans.class).web(false) + .showBanner(false).run(); + assertFalse(this.context.containsBean("foo")); + } + + @Configuration + protected static class Beans { + @Bean + @ConditionalOnRefreshScope + public String foo() { + return "foo"; + } + } + +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/healthcheck/EurekaHealthCheckTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/healthcheck/EurekaHealthCheckTests.java index de56e655..ee8ab079 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/healthcheck/EurekaHealthCheckTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/healthcheck/EurekaHealthCheckTests.java @@ -35,7 +35,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.DiscoveryClient; +import com.netflix.discovery.EurekaClient; /** @@ -46,17 +46,17 @@ import com.netflix.discovery.DiscoveryClient; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = EurekaHealthCheckTests.EurekaHealthCheckApplication.class) @WebAppConfiguration -@IntegrationTest({"server.port=0", "eureka.client.healthcheck.enabled=true"}) +@IntegrationTest({"server.port=0", "eureka.client.healthcheck.enabled=true", "debug=true"}) @DirtiesContext public class EurekaHealthCheckTests { @Autowired - private DiscoveryClient discoveryClient; + private EurekaClient discoveryClient; @Test public void shouldRegisterService() { - InstanceInfo.InstanceStatus status = discoveryClient.getHealthCheckHandler() + InstanceInfo.InstanceStatus status = this.discoveryClient.getHealthCheckHandler() .getStatus(InstanceInfo.InstanceStatus.UNKNOWN); assertNotNull(status); diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java index fc03769a..3c64b54b 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java @@ -58,6 +58,7 @@ import org.springframework.web.context.ServletContextAware; import com.netflix.blitz4j.DefaultBlitz4jConfig; import com.netflix.blitz4j.LoggingConfiguration; +import com.netflix.discovery.EurekaClient; import com.netflix.discovery.converters.JsonXStream; import com.netflix.discovery.converters.XmlXStream; import com.netflix.eureka.AbstractInstanceRegistry; @@ -72,8 +73,8 @@ import com.netflix.eureka.PeerAwareInstanceRegistryImpl; */ @Configuration @EnableConfigurationProperties(EurekaServerConfigBean.class) -public class EurekaServerInitializerConfiguration implements ServletContextAware, - SmartLifecycle, Ordered { +public class EurekaServerInitializerConfiguration + implements ServletContextAware, SmartLifecycle, Ordered { private static Log logger = LogFactory .getLog(EurekaServerInitializerConfiguration.class); @@ -98,7 +99,8 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware @PostConstruct public void initLogging() { - if (!(LoggingSystem.get(ClassUtils.getDefaultClassLoader()) instanceof Log4JLoggingSystem)) { + if (!(LoggingSystem + .get(ClassUtils.getDefaultClassLoader()) instanceof Log4JLoggingSystem)) { LoggingConfiguration off = new LoggingConfiguration() { @Override @@ -139,7 +141,7 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware System.setProperty("log4j.configuration", new ClassPathResource("log4j.properties", Log4JLoggingSystem.class).getURL() - .toString()); + .toString()); } } catch (IOException ex) { @@ -206,11 +208,11 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware @Configuration @ConditionalOnClass(PeerAwareInstanceRegistry.class) - protected static class RegistryInstanceProxyInitializer implements - ApplicationListener { + protected static class RegistryInstanceProxyInitializer + implements ApplicationListener { - @Autowired - private ApplicationContext applicationContext; + @Autowired(required = false) + private EurekaClient client; private PeerAwareInstanceRegistryImpl instance; @@ -221,6 +223,9 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware @Override public void onApplicationEvent(EurekaRegistryAvailableEvent event) { + if (this.client != null) { + this.client.getApplications(); // force initialization + } if (this.instance == null) { this.instance = PeerAwareInstanceRegistryImpl.getInstance(); safeInit(); @@ -230,8 +235,8 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware } private void safeInit() { - Method method = ReflectionUtils - .findMethod(AbstractInstanceRegistry.class, "postInit"); + Method method = ReflectionUtils.findMethod(AbstractInstanceRegistry.class, + "postInit"); ReflectionUtils.makeAccessible(method); ReflectionUtils.invokeMethod(method, this.instance); }