/* * Copyright 2013-2014 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 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 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.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.bind.RelaxedPropertyResolver; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.cloud.client.CommonsClientAutoConfiguration; import org.springframework.cloud.client.actuator.HasFeatures; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; import org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration; import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration; import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.util.StringUtils; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.EurekaInstanceConfig; import com.netflix.appinfo.HealthCheckHandler; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.DiscoveryClient.DiscoveryClientOptionalArgs; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId; /** * @author Dave Syer * @author Spencer Gibb * @author Jon Schneider * @author Matt Jenkins * @author Ryan Baxter */ @Configuration @EnableConfigurationProperties @ConditionalOnClass(EurekaClientConfig.class) @ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class) @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true) @AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class }) @AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration") public class EurekaClientAutoConfiguration { @Value("${server.port:${SERVER_PORT:${PORT:8080}}}") private int nonSecurePort; @Value("${management.port:${MANAGEMENT_PORT:${server.port:${SERVER_PORT:${PORT:8080}}}}}") private int managementPort; @Autowired private ConfigurableEnvironment env; @Autowired(required = false) private HealthCheckHandler healthCheckHandler; @Bean public HasFeatures eurekaFeature() { return HasFeatures.namedFeature("Eureka Client", EurekaClient.class); } @Bean @ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT) public EurekaClientConfigBean eurekaClientConfigBean() { EurekaClientConfigBean client = new EurekaClientConfigBean(); if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) { // We don't register during bootstrap by default, but there will be another // chance later. client.setRegisterWithEureka(false); } return client; } @Bean @ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT) public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) { RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(env, "eureka.instance."); String hostname = relaxedPropertyResolver.getProperty("hostname"); boolean preferIpAddress = Boolean.parseBoolean(relaxedPropertyResolver.getProperty("preferIpAddress")); EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils); instance.setNonSecurePort(this.nonSecurePort); instance.setInstanceId(getDefaultInstanceId(this.env)); instance.setPreferIpAddress(preferIpAddress); if (this.managementPort != this.nonSecurePort && this.managementPort != 0) { if (StringUtils.hasText(hostname)) { instance.setHostname(hostname); } String statusPageUrlPath = relaxedPropertyResolver.getProperty("statusPageUrlPath"); String healthCheckUrlPath = relaxedPropertyResolver.getProperty("healthCheckUrlPath"); if (StringUtils.hasText(statusPageUrlPath)) { instance.setStatusPageUrlPath(statusPageUrlPath); } if (StringUtils.hasText(healthCheckUrlPath)) { instance.setHealthCheckUrlPath(healthCheckUrlPath); } String scheme = instance.getSecurePortEnabled() ? "https" : "http"; instance.setStatusPageUrl(scheme + "://" + instance.getHostname() + ":" + this.managementPort + instance.getStatusPageUrlPath()); instance.setHealthCheckUrl(scheme + "://" + instance.getHostname() + ":" + this.managementPort + instance.getHealthCheckUrlPath()); } return instance; } @Bean public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) { return new EurekaDiscoveryClient(config, client); } @Bean public EurekaServiceRegistry eurekaServiceRegistry() { return new EurekaServiceRegistry(); } @Bean @ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager) { return EurekaRegistration.builder(instanceConfig) .with(applicationInfoManager) .with(eurekaClient) .with(healthCheckHandler) .build(); } @Bean @ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry registry, EurekaRegistration registration) { return new EurekaAutoServiceRegistration(context, registry, registration); } @Bean @ConditionalOnMissingBean(value = DiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT) public MutableDiscoveryClientOptionalArgs discoveryClientOptionalArgs() { return new MutableDiscoveryClientOptionalArgs(); } @Configuration @ConditionalOnMissingRefreshScope protected static class EurekaClientConfiguration { @Autowired private ApplicationContext context; @Autowired(required = false) private DiscoveryClientOptionalArgs optionalArgs; @Bean(destroyMethod = "shutdown") @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) { return new CloudEurekaClient(manager, config, this.optionalArgs, this.context); } @Bean @ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT) public ApplicationInfoManager eurekaApplicationInfoManager( EurekaInstanceConfig config) { InstanceInfo instanceInfo = new InstanceInfoFactory().create(config); return new ApplicationInfoManager(config, instanceInfo); } } @Configuration @ConditionalOnRefreshScope protected static class RefreshableEurekaClientConfiguration { @Autowired private ApplicationContext context; @Autowired(required = false) private DiscoveryClientOptionalArgs optionalArgs; @Bean(destroyMethod = "shutdown") @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) @org.springframework.cloud.context.config.annotation.RefreshScope @Lazy public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) { manager.getInfo(); // force initialization return new CloudEurekaClient(manager, config, this.optionalArgs, this.context); } @Bean @ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT) @org.springframework.cloud.context.config.annotation.RefreshScope @Lazy public ApplicationInfoManager eurekaApplicationInfoManager( EurekaInstanceConfig config) { InstanceInfo instanceInfo = new InstanceInfoFactory().create(config); return new ApplicationInfoManager(config, instanceInfo); } } @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnMissingRefreshScopeCondition.class) @interface ConditionalOnMissingRefreshScope { } @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @ConditionalOnClass(RefreshScope.class) @ConditionalOnBean(RefreshAutoConfiguration.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(RefreshAutoConfiguration.class) static class MissingScope { } } }