From 6417923523798d218dcfe5e3e4b6edbeeb840d7a Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 5 Dec 2014 15:39:14 +0000 Subject: [PATCH] Extract ribbon client factory into ApplicationContext Using Spring as an object factory to control the lifecycle of ribbon clients. So far we have a single, parameterized configuration class for all clients, but can be extended to allow user to supply additional configuration. The current model for user extensions is @EnableRibbonClient(@RibbonClient(name = "foo", configuration = FooConfiguration.class)) public class MainConfiguration { ... } So in this example, MainConfiguration is part of the "main" application context and FooConfiguration is used to create the Ribbon client and load balancer for the "foo" service. --- .../archaius/ArchaiusAutoConfiguration.java | 147 +++++--- .../ArchaiusDelegatingProxyUtils.java | 48 +++ .../netflix/feign/FeignConfiguration.java | 1 + .../netflix/ribbon/EnableRibbonClient.java | 40 +++ .../ribbon/RibbonAutoConfiguration.java | 17 +- .../cloud/netflix/ribbon/RibbonClient.java | 34 ++ .../ribbon/RibbonClientConfiguration.java | 123 +++++++ .../RibbonClientConfigurationRegistrar.java | 50 +++ .../ribbon/RibbonClientSpecification.java | 32 ++ .../ribbon/RibbonLoadBalancerClient.java | 51 +-- .../netflix/ribbon/SpringClientFactory.java | 339 +++++++----------- .../EurekaRibbonClientPreprocessor.java | 66 ++-- .../eureka/RibbonEurekaAutoConfiguration.java | 6 +- .../ZonePreferenceServerListFilter.java | 5 + .../filters/route/RibbonRoutingFilter.java | 4 +- .../eureka/EurekaInstanceConfigBeanTests.java | 9 +- .../eureka/sample/ApplicationTests.java | 2 + .../netflix/feign/SpringDecoderTests.java | 4 +- ...rTest.java => RibbonInterceptorTests.java} | 2 +- ...ava => RibbonLoadBalancerClientTests.java} | 34 +- .../ribbon/SpringClientFactoryTests.java | 47 +++ .../DomainExtractingServerListTests.java | 24 +- ...bonClientPreprocessorIntegrationTests.java | 97 +++++ .../EurekaRibbonClientPreprocessorTests.java | 51 +-- .../zuul/SampleZuulProxyApplicationTests.java | 2 + 25 files changed, 812 insertions(+), 423 deletions(-) create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusDelegatingProxyUtils.java create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/EnableRibbonClient.java create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationRegistrar.java create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientSpecification.java rename spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/{RibbonInterceptorTest.java => RibbonInterceptorTests.java} (98%) rename spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/{RibbonLoadBalancerClientTest.java => RibbonLoadBalancerClientTests.java} (85%) create mode 100644 spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java create mode 100644 spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorIntegrationTests.java diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusAutoConfiguration.java index f915c7c3..618e4fb2 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusAutoConfiguration.java @@ -8,8 +8,11 @@ import static com.netflix.config.ConfigurationManager.ENV_CONFIG_NAME; import static com.netflix.config.ConfigurationManager.SYS_CONFIG_NAME; import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME; +import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.PreDestroy; + import org.apache.commons.configuration.ConfigurationBuilder; import org.apache.commons.configuration.EnvironmentConfiguration; import org.apache.commons.configuration.SystemConfiguration; @@ -20,85 +23,111 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.util.ReflectionUtils; import com.netflix.config.ConcurrentCompositeConfiguration; import com.netflix.config.ConfigurationManager; +import com.netflix.config.DynamicPropertyFactory; import com.netflix.config.DynamicURLConfiguration; /** * @author Spencer Gibb */ @Configuration -@ConditionalOnClass({ConcurrentCompositeConfiguration.class, ConfigurationBuilder.class}) +@ConditionalOnClass({ ConcurrentCompositeConfiguration.class, ConfigurationBuilder.class }) public class ArchaiusAutoConfiguration { - private static final Logger logger = LoggerFactory.getLogger(ArchaiusAutoConfiguration.class); - private static final AtomicBoolean initialized = new AtomicBoolean(false); + private static final Logger logger = LoggerFactory + .getLogger(ArchaiusAutoConfiguration.class); + private static final AtomicBoolean initialized = new AtomicBoolean(false); - @Autowired - private ConfigurableEnvironment env; + @Autowired + private ConfigurableEnvironment env; - @Bean - public ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration() { - ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(env); - configureArchaius(envConfig); - return envConfig; - } - - @Bean - protected ArchaiusEndpoint archaiusEndpoint() { - return new ArchaiusEndpoint(); - } + @PreDestroy + public void close() { + setStatic(ConfigurationManager.class, "instance", null); + setStatic(ConfigurationManager.class, "customConfigurationInstalled", false); + setStatic(DynamicPropertyFactory.class, "config", null); + setStatic(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false); + initialized.compareAndSet(true, false); + } - @SuppressWarnings("deprecation") + @Bean + public ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration() { + ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration( + env); + configureArchaius(envConfig); + return envConfig; + } + + @Bean + protected ArchaiusEndpoint archaiusEndpoint() { + return new ArchaiusEndpoint(); + } + + @SuppressWarnings("deprecation") protected void configureArchaius(ConfigurableEnvironmentConfiguration envConfig) { - if (initialized.compareAndSet(false, true)) { - String appName = env.getProperty("spring.application.name"); - if (appName == null) { - appName = "application"; - logger.warn("No spring.application.name found, defaulting to 'application'"); - } - //this is deprecated, but currently it seams the only way to set it initially - System.setProperty(DEPLOYMENT_APPLICATION_ID_PROPERTY, appName); + if (initialized.compareAndSet(false, true)) { + String appName = env.getProperty("spring.application.name"); + if (appName == null) { + appName = "application"; + logger.warn("No spring.application.name found, defaulting to 'application'"); + } + // this is deprecated, but currently it seams the only way to set it initially + System.setProperty(DEPLOYMENT_APPLICATION_ID_PROPERTY, appName); - //TODO: support for other DeploymentContexts + // TODO: support for other DeploymentContexts - ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration(); + ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration(); - //support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds, etc...) - /*if (factories != null && !factories.isEmpty()) { - for (PropertiesSourceFactory factory: factories) { - config.addConfiguration(factory.getConfiguration(), factory.getName()); - } - }*/ - config.addConfiguration(envConfig, ConfigurableEnvironmentConfiguration.class.getSimpleName()); + // support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds, + // etc...) + /* + * if (factories != null && !factories.isEmpty()) { for + * (PropertiesSourceFactory factory: factories) { + * config.addConfiguration(factory.getConfiguration(), factory.getName()); } } + */ + config.addConfiguration(envConfig, + ConfigurableEnvironmentConfiguration.class.getSimpleName()); - //below come from ConfigurationManager.createDefaultConfigInstance() - DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration(); - try { - config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME); - } catch (Throwable e) { - logger.error("Cannot create config from " + defaultURLConfig, e); - } + // below come from ConfigurationManager.createDefaultConfigInstance() + DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration(); + try { + config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME); + } + catch (Throwable e) { + logger.error("Cannot create config from " + defaultURLConfig, e); + } - //TODO: sys/env above urls? - if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) { - SystemConfiguration sysConfig = new SystemConfiguration(); - config.addConfiguration(sysConfig, SYS_CONFIG_NAME); - } - if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) { - EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration(); - config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME); - } + // TODO: sys/env above urls? + if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) { + SystemConfiguration sysConfig = new SystemConfiguration(); + config.addConfiguration(sysConfig, SYS_CONFIG_NAME); + } + if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) { + EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration(); + config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME); + } - ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration(); - config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES); - config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig)); + ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration(); + config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES); + config.setContainerConfigurationIndex(config + .getIndexOfConfiguration(appOverrideConfig)); + + ConfigurationManager.install(config); + } + else { + // TODO: reinstall ConfigurationManager + logger.warn("Netflix ConfigurationManager has already been installed, unable to re-install"); + } + } + + private static void setStatic(Class type, String name, Object value) { + // Hack a private static field + Field field = ReflectionUtils.findField(type, name); + ReflectionUtils.makeAccessible(field); + ReflectionUtils.setField(field, null, value); + } - ConfigurationManager.install(config); - } else { - //TODO: reinstall ConfigurationManager - logger.warn("Netflix ConfigurationManager has already been installed, unable to re-install"); - } - } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusDelegatingProxyUtils.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusDelegatingProxyUtils.java new file mode 100644 index 00000000..a74de871 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/archaius/ArchaiusDelegatingProxyUtils.java @@ -0,0 +1,48 @@ +/* + * 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.archaius; + +import org.apache.commons.configuration.AbstractConfiguration; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +import com.netflix.config.ConfigurationManager; + +/** + * @author Dave Syer + * + */ +public class ArchaiusDelegatingProxyUtils { + + public static String APPLICATION_CONTEXT = ApplicationContext.class.getName(); + + public static T getNamedInstance(Class type, String name) { + ApplicationContext context = (ApplicationContext) ConfigurationManager.getConfigInstance().getProperty(APPLICATION_CONTEXT); + return context!=null && context.containsBean(name) ? context.getBean(name, type) : null; + } + + public static T getInstanceWithPrefix(Class type, String prefix) { + String name = prefix + type.getSimpleName(); + return getNamedInstance(type, name); + } + + public static void addApplicationContext(ConfigurableApplicationContext context) { + AbstractConfiguration config = ConfigurationManager.getConfigInstance(); + config .clearProperty(APPLICATION_CONTEXT); + config.setProperty(APPLICATION_CONTEXT, context); + } + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java index 143625ab..88346fe0 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignConfiguration.java @@ -54,6 +54,7 @@ public class FeignConfiguration { protected T loadBalance(Feign.Builder builder, Class type, String schemeName) { String name = URI.create(schemeName).getHost(); + // TODO: This should be transparent ribbonClientPreprocessor.preprocess(name); if(ribbonClient != null) { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/EnableRibbonClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/EnableRibbonClient.java new file mode 100644 index 00000000..0f75728e --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/EnableRibbonClient.java @@ -0,0 +1,40 @@ +/* + * 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.ribbon; + +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.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * @author Dave Syer + * + */ +@Configuration +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE }) +@Documented +@Import(RibbonClientConfigurationRegistrar.class) +public @interface EnableRibbonClient { + + RibbonClient[] value() default {}; + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java index 107496d7..4c455873 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java @@ -1,7 +1,6 @@ package org.springframework.cloud.netflix.ribbon; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -16,7 +15,6 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; import com.netflix.client.IClient; -import com.netflix.loadbalancer.BaseLoadBalancer; /** * Auto configuration for Ribbon (client side load balancing) @@ -25,15 +23,18 @@ import com.netflix.loadbalancer.BaseLoadBalancer; */ @Configuration @ConditionalOnClass(IClient.class) +@EnableRibbonClient @AutoConfigureAfter(EurekaClientAutoConfiguration.class) public class RibbonAutoConfiguration { - @Autowired(required = false) - private List balancers = Collections.emptyList(); + @Autowired(required=false) + private List configurations = new ArrayList<>(); - @Bean + @Bean public SpringClientFactory springClientFactory() { - return new SpringClientFactory(); + SpringClientFactory factory = new SpringClientFactory(); + factory.setConfigurations(configurations); + return factory; } @Bean @@ -48,8 +49,8 @@ public class RibbonAutoConfiguration { @Bean @ConditionalOnMissingBean(LoadBalancerClient.class) - public LoadBalancerClient loadBalancerClient(RibbonClientPreprocessor clientPreprocessor) { - return new RibbonLoadBalancerClient(clientPreprocessor, springClientFactory(), balancers); + public LoadBalancerClient loadBalancerClient() { + return new RibbonLoadBalancerClient(springClientFactory()); } @Bean diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java new file mode 100644 index 00000000..a62a7c28 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClient.java @@ -0,0 +1,34 @@ +/* + * 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.ribbon; + +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; + +/** + * @author Dave Syer + * + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RibbonClient { + String name(); + Class[] configuration() default {}; +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java new file mode 100644 index 00000000..53f747f5 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java @@ -0,0 +1,123 @@ +/* + * 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.ribbon; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; +import com.netflix.discovery.EurekaClientConfig; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import com.netflix.loadbalancer.ServerListFilter; +import com.netflix.loadbalancer.ZoneAffinityServerListFilter; +import com.netflix.loadbalancer.ZoneAwareLoadBalancer; +import com.netflix.niws.client.http.RestClient; +import com.netflix.servo.monitor.Monitors; + +/** + * @author Dave Syer + * + */ +@SuppressWarnings("deprecation") +@Configuration +@EnableConfigurationProperties +public class RibbonClientConfiguration { + + @Value("${ribbon.client.name}") + private String name = "client"; + + @Autowired(required = false) + private EurekaClientConfig eurekaClientConfig; + + @Autowired(required = false) + private RibbonClientPreprocessor preprocessor; + + // TODO: maybe re-instate autowired load balancers: identified by name they could be + // associated with ribbon clients + + @PostConstruct + public void init() { + if (preprocessor!=null) { + preprocessor.preprocess(name); + } + } + + @Bean + @ConditionalOnMissingBean + public IClientConfig ribbonClientConfig() { + DefaultClientConfigImpl config = new DefaultClientConfigImpl(); + config.loadProperties(name); + return config; + } + + @Bean + @ConditionalOnMissingBean + public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer) { + RestClient client = new RestClient(config); + client.setLoadBalancer(loadBalancer); + Monitors.registerObject("Client_" + name, client); + return client; + } + + @Bean + @ConditionalOnMissingBean + public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerListFilter filter) { + ZoneAwareLoadBalancer balancer = new ZoneAwareLoadBalancer(config); + wrapServerList(balancer); + balancer.setFilter(filter); + return balancer; + } + + @Bean + @ConditionalOnMissingBean + public ServerListFilter ribbonServerListFilter(IClientConfig config) { + return new ZoneAffinityServerListFilter(config); + } + + @Bean + @ConditionalOnMissingBean + public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer, IClientConfig config) { + return new RibbonLoadBalancerContext(loadBalancer, config); + } + + private void wrapServerList(ILoadBalancer balancer) { + if (balancer instanceof DynamicServerListLoadBalancer) { + @SuppressWarnings("unchecked") + DynamicServerListLoadBalancer dynamic = (DynamicServerListLoadBalancer) balancer; + ServerList list = dynamic.getServerListImpl(); + if (!(list instanceof DomainExtractingServerList)) { + // This is optional: you can use the native Eureka AWS features as long as + // the server zone is populated. TODO: verify that we back off if AWS + // metadata *is* available. + // @see com.netflix.appinfo.AmazonInfo.Builder + dynamic.setServerListImpl(new DomainExtractingServerList(list, dynamic + .getClientConfig())); + } + } + } + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationRegistrar.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationRegistrar.java new file mode 100644 index 00000000..14c7846c --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfigurationRegistrar.java @@ -0,0 +1,50 @@ +/* + * 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.ribbon; + +import java.util.Map; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; + +/** + * @author Dave Syer + * + */ +public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar { + + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, + BeanDefinitionRegistry registry) { + Map attrs = metadata.getAnnotationAttributes( + EnableRibbonClient.class.getName(), true); + if (attrs.containsKey("value")) { + AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value"); + for (AnnotationAttributes attr : clients) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(RibbonClientSpecification.class); + builder.addConstructorArgValue(attr.get("name")); + builder.addConstructorArgValue(attr.get("configuration")); + registry.registerBeanDefinition(attr.get("name") + + "RibbonClientSpecification", builder.getBeanDefinition()); + } + } + } + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientSpecification.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientSpecification.java new file mode 100644 index 00000000..7f2afcc2 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientSpecification.java @@ -0,0 +1,32 @@ +/* + * 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.ribbon; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author Dave Syer + * + */ +@Data +@AllArgsConstructor +public class RibbonClientSpecification { + + private String name; + private Class[] configuration; + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java index 80996576..e53e7598 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java @@ -1,20 +1,17 @@ package org.springframework.cloud.netflix.ribbon; -import com.google.common.base.Throwables; -import com.netflix.loadbalancer.BaseLoadBalancer; -import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerStats; -import com.netflix.servo.monitor.Stopwatch; +import java.net.URI; +import java.util.concurrent.TimeUnit; + import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; -import java.net.URI; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; +import com.google.common.base.Throwables; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerStats; +import com.netflix.servo.monitor.Stopwatch; /** * @author Spencer Gibb @@ -22,25 +19,16 @@ import java.util.concurrent.TimeUnit; */ public class RibbonLoadBalancerClient implements LoadBalancerClient { - private RibbonClientPreprocessor ribbonClientPreprocessor; - private SpringClientFactory clientFactory; - private Map balancers = new ConcurrentHashMap<>(); - private Map contexts = new ConcurrentHashMap<>(); - - public RibbonLoadBalancerClient(RibbonClientPreprocessor ribbonClientPreprocessor, SpringClientFactory clientFactory, List balancers) { - this.ribbonClientPreprocessor = ribbonClientPreprocessor; + public RibbonLoadBalancerClient(SpringClientFactory clientFactory) { this.clientFactory = clientFactory; - for (BaseLoadBalancer balancer : balancers) { - this.balancers.put(balancer.getName(), balancer); - } } @Override public URI reconstructURI(ServiceInstance instance, URI original) { String serviceId = instance.getServiceId(); - RibbonLoadBalancerContext context = getOrCreateLoadBalancerContext(serviceId, getLoadBalancer(serviceId)); + RibbonLoadBalancerContext context = clientFactory.getLoadBalancerContext(serviceId); Server server = new Server(instance.getHost(), instance.getPort()); return context.reconstructURIWithServer(server, original); } @@ -53,7 +41,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @Override public T execute(String serviceId, LoadBalancerRequest request) { ILoadBalancer loadBalancer = getLoadBalancer(serviceId); - RibbonLoadBalancerContext context = getOrCreateLoadBalancerContext(serviceId, loadBalancer); + RibbonLoadBalancerContext context = clientFactory.getLoadBalancerContext(serviceId); Server server = getServer(serviceId, loadBalancer); RibbonServer ribbonServer = new RibbonServer(serviceId, server); @@ -79,18 +67,10 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { context.noteRequestCompletion(serverStats, entity, exception, duration, null/*errorHandler*/); } - protected RibbonLoadBalancerContext getOrCreateLoadBalancerContext(String serviceId, ILoadBalancer loadBalancer) { - RibbonLoadBalancerContext context = contexts.get(serviceId); - if (context == null) { - context = new RibbonLoadBalancerContext(loadBalancer); - contexts.put(serviceId, context); - } - return context; - } - protected Server getServer(String serviceId) { return getServer(serviceId, getLoadBalancer(serviceId)); } + protected Server getServer(String serviceId, ILoadBalancer loadBalancer) { Server server = loadBalancer.chooseServer("default"); if (server == null) { @@ -101,12 +81,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { } protected ILoadBalancer getLoadBalancer(String serviceId) { - ribbonClientPreprocessor.preprocess(serviceId); - ILoadBalancer loadBalancer = this.balancers.get(serviceId); - if (loadBalancer == null) { - loadBalancer = clientFactory.getNamedLoadBalancer(serviceId); - } - return loadBalancer; + return clientFactory.getLoadBalancer(serviceId); } protected static class RibbonServer implements ServiceInstance { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java index 19ad9470..d28b8331 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java @@ -1,238 +1,153 @@ package org.springframework.cloud.netflix.ribbon; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.env.MapPropertySource; -import com.netflix.client.AbstractLoadBalancerAwareClient; -import com.netflix.client.ClientException; import com.netflix.client.IClient; import com.netflix.client.IClientConfigAware; -import com.netflix.client.config.CommonClientConfigKey; -import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.servo.monitor.Monitors; /** - * A factory that creates client, load balancer and client configuration instances from properties. It also keeps mappings of client names to - * the created instances. + * A factory that creates client, load balancer and client configuration instances. It + * creates a Spring ApplicationContext per client name, and extracts the beans that it + * needs from there. * */ -@Slf4j -public class SpringClientFactory { +public class SpringClientFactory implements DisposableBean, ApplicationContextAware { - private Map> simpleClientMap = new ConcurrentHashMap<>(); - private Map namedLBMap = new ConcurrentHashMap<>(); - private ConcurrentHashMap namedConfig = new ConcurrentHashMap<>(); + private Map contexts = new ConcurrentHashMap<>(); + private Map configurations = new ConcurrentHashMap<>(); + private ApplicationContext parent; - /** - * Utility method to create client and load balancer (if enabled in client config) given the name and client config. - * Instances are created using reflection (see {@link #instantiateInstanceWithClientConfig(String, IClientConfig)} - * - * @param restClientName - * @param clientConfig - * @throws ClientException if any errors occurs in the process, or if the client with the same name already exists - */ - public synchronized IClient registerClientFromProperties(String restClientName, IClientConfig clientConfig) throws ClientException { - IClient client; - ILoadBalancer loadBalancer = null; - if (simpleClientMap.get(restClientName) != null) { - throw new ClientException( - ClientException.ErrorType.GENERAL, - "A Rest Client with this name is already registered. Please use a different name"); - } - try { - String clientClassName = (String) clientConfig.get(CommonClientConfigKey.ClientClassName); - client = (IClient) instantiateInstanceWithClientConfig(clientClassName, clientConfig); - boolean initializeNFLoadBalancer = Boolean.parseBoolean(clientConfig.get( - CommonClientConfigKey.InitializeNFLoadBalancer, DefaultClientConfigImpl.DEFAULT_ENABLE_LOADBALANCER).toString()); - if (initializeNFLoadBalancer) { - loadBalancer = getNamedLoadBalancer(restClientName, clientConfig.getClass()); - } - if (client instanceof AbstractLoadBalancerAwareClient) { - ((AbstractLoadBalancerAwareClient) client).setLoadBalancer(loadBalancer); - } - } catch (Throwable e) { - String message = "Unable to InitializeAndAssociateNFLoadBalancer set for RestClient:" - + restClientName; - log.warn(message); - throw new ClientException(ClientException.ErrorType.CONFIGURATION, - message, e); - } - simpleClientMap.put(restClientName, client); + @Override + public void setApplicationContext(ApplicationContext parent) throws BeansException { + this.parent = parent; + } + + public void setConfigurations(List configurations) { + for (RibbonClientSpecification client : configurations) { + this.configurations.put(client.getName(), client); + } + } - Monitors.registerObject("Client_" + restClientName, client); + @Override + public void destroy() { + Collection values = contexts.values(); + contexts.clear(); + for (AnnotationConfigApplicationContext context : values) { + context.close(); + } + } - log.info("Client Registered:" + client.toString()); - return client; - } + /** + * Get the rest client associated with the name. + * + * @throws RuntimeException if any error occurs + */ + public > C getClient(String name, Class clientClass) { + return getInstance(name, clientClass); + } - /** - * Return the named client from map if already created. Otherwise creates the client using the configuration returned by {@link #getNamedConfig(String)}. - * - * @throws RuntimeException if an error occurs in creating the client. - */ - public synchronized IClient getNamedClient(String name) { - return getNamedClient(name, DefaultClientConfigImpl.class); - } + /** + * Get the load balancer associated with the name. + * + * @throws RuntimeException if any error occurs + */ + public ILoadBalancer getLoadBalancer(String name) { + return getInstance(name, ILoadBalancer.class); + } - public synchronized > C namedClient(String name, Class clientClass) { - return clientClass.cast(getNamedClient(name, DefaultClientConfigImpl.class)); - } + /** + * Get the load balancer context associated with the name. + * + * @throws RuntimeException if any error occurs + */ + public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) { + return getInstance(serviceId, RibbonLoadBalancerContext.class); + } - /** - * Return the named client from map if already created. Otherwise creates the client using the configuration returned by {@link #createNamedClient(String, Class)}. - * - * @throws RuntimeException if an error occurs in creating the client. - */ - public synchronized IClient getNamedClient(String name, Class configClass) { - if (simpleClientMap.get(name) != null) { - return simpleClientMap.get(name); - } - try { - return createNamedClient(name, configClass); - } catch (ClientException e) { - throw new RuntimeException("Unable to create client", e); - } - } + private AnnotationConfigApplicationContext getContext(String name) { + if (!contexts.containsKey(name)) { + synchronized (contexts) { + if (!contexts.containsKey(name)) { + contexts.put(name, createContext(name)); + } + } + } + return contexts.get(name); + } - /** - * Creates a named client using a IClientConfig instance created off the configClass class object passed in as the parameter. - * - * @throws ClientException if any error occurs, or if the client with the same name already exists - */ - public synchronized IClient createNamedClient(String name, Class configClass) throws ClientException { - IClientConfig config = getNamedConfig(name, configClass); - return registerClientFromProperties(name, config); - } + private AnnotationConfigApplicationContext createContext(String name) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + if (configurations.containsKey(name)) { + for (Class configuration : configurations.get(name).getConfiguration()) { + context.register(configuration); + } + } + context.register(PropertyPlaceholderAutoConfiguration.class, + RibbonClientConfiguration.class); + context.getEnvironment() + .getPropertySources() + .addFirst( + new MapPropertySource("ribbon", + Collections. singletonMap( + "ribbon.client.name", name))); + if (parent != null) { + // Uses Environment from parent as well as beans + context.setParent(parent); + } + context.refresh(); + return context; + } - /** - * Get the load balancer associated with the name, or create one with an instance {@link DefaultClientConfigImpl} if does not exist - * - * @throws RuntimeException if any error occurs - */ - public synchronized ILoadBalancer getNamedLoadBalancer(String name) { - return getNamedLoadBalancer(name, DefaultClientConfigImpl.class); - } + private C instantiateWithConfig(AnnotationConfigApplicationContext context, + Class clazz, IClientConfig config) { + C result = null; + if (IClientConfigAware.class.isAssignableFrom(clazz)) { + IClientConfigAware obj = (IClientConfigAware) BeanUtils.instantiate(clazz); + obj.initWithNiwsConfig(config); + @SuppressWarnings("unchecked") + C value = (C) obj; + result = value; + } + else { + try { + if (clazz.getConstructor(IClientConfig.class) != null) { + result = clazz.getConstructor(IClientConfig.class) + .newInstance(config); + } + else { + result = BeanUtils.instantiate(clazz); + } + } + catch (Throwable e) { // NOPMD + } + } + context.getAutowireCapableBeanFactory().autowireBean(result); + return result; + } - /** - * Get the load balancer associated with the name, or create one with an instance of configClass if does not exist - * - * @throws RuntimeException if any error occurs - * @see #registerNamedLoadBalancerFromProperties(String, Class) - */ - public synchronized ILoadBalancer getNamedLoadBalancer(String name, Class configClass) { - ILoadBalancer lb = namedLBMap.get(name); - if (lb != null) { - return lb; - } else { - try { - lb = registerNamedLoadBalancerFromProperties(name, configClass); - } catch (ClientException e) { - throw new RuntimeException("Unable to create load balancer", e); - } - return lb; - } - } + private C getInstance(String name, Class type) { + AnnotationConfigApplicationContext context = getContext(name); + if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) { + return context.getBean(type); + } + IClientConfig config = getInstance(name, IClientConfig.class); + return instantiateWithConfig(context, type, config); + } - /** - * Create and register a load balancer with the name and given the class of configClass. - * - * @throws ClientException if load balancer with the same name already exists or any error occurs - * @see #instantiateInstanceWithClientConfig(String, IClientConfig) - */ - public ILoadBalancer registerNamedLoadBalancerFromclientConfig(String name, IClientConfig clientConfig) throws ClientException { - if (namedLBMap.get(name) != null) { - throw new ClientException("LoadBalancer for name " + name + " already exists"); - } - ILoadBalancer lb = null; - try { - String loadBalancerClassName = (String) clientConfig.get(CommonClientConfigKey.NFLoadBalancerClassName); - lb = (ILoadBalancer) instantiateInstanceWithClientConfig(loadBalancerClassName, clientConfig); - namedLBMap.put(name, lb); - log.info("Client:" + name - + " instantiated a LoadBalancer:" + lb.toString()); - return lb; - } catch (Exception e) { - throw new ClientException("Unable to instantiate/associate LoadBalancer with Client:" + name, e); - } - } - - /** - * Create and register a load balancer with the name and given the class of configClass. - * - * @throws ClientException if load balancer with the same name already exists or any error occurs - * @see #instantiateInstanceWithClientConfig(String, IClientConfig) - */ - public synchronized ILoadBalancer registerNamedLoadBalancerFromProperties(String name, Class configClass) throws ClientException { - if (namedLBMap.get(name) != null) { - throw new ClientException("LoadBalancer for name " + name + " already exists"); - } - IClientConfig clientConfig = getNamedConfig(name, configClass); - return registerNamedLoadBalancerFromclientConfig(name, clientConfig); - } - - /** - * Creates instance related to client framework using reflection. It first checks if the object is an instance of - * {@link IClientConfigAware} and if so invoke {@link IClientConfigAware#initWithNiwsConfig(IClientConfig)}. If that does not - * apply, it tries to find if there is a constructor with {@link IClientConfig} as a parameter and if so invoke that constructor. If neither applies, - * it simply invokes the no-arg constructor and ignores the clientConfig parameter. - * - * @param className Class name of the object - * @param clientConfig IClientConfig object used for initialization. - */ - public Object instantiateInstanceWithClientConfig(String className, IClientConfig clientConfig) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - Class clazz = Class.forName(className); - if (IClientConfigAware.class.isAssignableFrom(clazz)) { - IClientConfigAware obj = (IClientConfigAware) clazz.newInstance(); - obj.initWithNiwsConfig(clientConfig); - return obj; - } else { - try { - if (clazz.getConstructor(IClientConfig.class) != null) { - return clazz.getConstructor(IClientConfig.class).newInstance(clientConfig); - } - } catch (Throwable e) { // NOPMD - } - } - log.warn("Class " + className + " neither implements IClientConfigAware nor provides a constructor with IClientConfig as the parameter. Only default constructor will be used."); - return clazz.newInstance(); - } - - /** - * Get the client configuration given the name or create one with {@link DefaultClientConfigImpl} if it does not exist. - * - * @see #getNamedConfig(String, Class) - */ - public IClientConfig getNamedConfig(String name) { - return getNamedConfig(name, DefaultClientConfigImpl.class); - } - - /** - * Get the client configuration given the name or create one with clientConfigClass if it does not exist. An instance of IClientConfig - * is created and {@link IClientConfig#loadProperties(String)} will be called. - */ - public IClientConfig getNamedConfig(String name, Class clientConfigClass) { - IClientConfig config = namedConfig.get(name); - if (config != null) { - return config; - } else { - try { - config = clientConfigClass.newInstance(); - config.loadProperties(name); - } catch (Throwable e) { - log.error("Unable to create client config instance", e); - return null; - } - config.loadProperties(name); - IClientConfig old = namedConfig.putIfAbsent(name, config); - if (old != null) { - config = old; - } - return config; - } - } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessor.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessor.java index cedccd9e..df39e533 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessor.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessor.java @@ -6,41 +6,34 @@ import static com.netflix.client.config.CommonClientConfigKey.NFLoadBalancerRule import static com.netflix.client.config.CommonClientConfigKey.NIWSServerListClassName; import static com.netflix.client.config.CommonClientConfigKey.NIWSServerListFilterClassName; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; import org.springframework.cloud.netflix.ribbon.RibbonClientPreprocessor; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import com.netflix.config.ConfigurationManager; import com.netflix.config.DeploymentContext.ContextKey; +import com.netflix.config.DynamicPropertyFactory; +import com.netflix.config.DynamicStringProperty; import com.netflix.discovery.EurekaClientConfig; -import com.netflix.loadbalancer.DynamicServerListLoadBalancer; -import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; import com.netflix.loadbalancer.ZoneAvoidanceRule; import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList; /** - * Preprocessor that configures defaults for eureka-discovered ribbon clients. - * Such as: @zone, NIWSServerListClassName, DeploymentContextBasedVipAddresses, - * NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more + * Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as: + * @zone, NIWSServerListClassName, DeploymentContextBasedVipAddresses, + * NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more * * @author Spencer Gibb * @author Dave Syer */ public class EurekaRibbonClientPreprocessor implements RibbonClientPreprocessor { - protected static final String VALUE_NOT_SET = "__not__set__"; - protected static final String DEFAULT_NAMESPACE = "ribbon"; + protected static final String VALUE_NOT_SET = "__not__set__"; + protected static final String DEFAULT_NAMESPACE = "ribbon"; - private EurekaClientConfig clientConfig; - private SpringClientFactory clientFactory; + private EurekaClientConfig clientConfig; - public EurekaRibbonClientPreprocessor(EurekaClientConfig clientConfig, SpringClientFactory clientFactory) { + public EurekaRibbonClientPreprocessor(EurekaClientConfig clientConfig) { this.clientConfig = clientConfig; - this.clientFactory = clientFactory; - } + } @Override public void preprocess(String serviceId) { @@ -67,40 +60,23 @@ public class EurekaRibbonClientPreprocessor implements RibbonClientPreprocessor setProp(serviceId, NIWSServerListFilterClassName.key(), ZonePreferenceServerListFilter.class.getName()); setProp(serviceId, EnableZoneAffinity.key(), "true"); - ILoadBalancer loadBalancer = clientFactory.getNamedLoadBalancer(serviceId); - wrapServerList(loadBalancer); - } - - private void wrapServerList(ILoadBalancer balancer) { - if (balancer instanceof DynamicServerListLoadBalancer) { - @SuppressWarnings("unchecked") - DynamicServerListLoadBalancer dynamic = (DynamicServerListLoadBalancer) balancer; - ServerList list = dynamic.getServerListImpl(); - if (!(list instanceof DomainExtractingServerList)) { - // This is optional: you can use the native Eureka AWS features as long as - // the server zone is populated. TODO: find a way to back off if AWS - // metadata *is* available. - // @see com.netflix.appinfo.AmazonInfo.Builder - dynamic.setServerListImpl(new DomainExtractingServerList(list, dynamic.getClientConfig())); - } - } } protected void setProp(String serviceId, String suffix, String value) { // how to set the namespace properly? - String key = getKey(serviceId, suffix); - DynamicStringProperty property = getProperty(key); - if (property.get().equals(VALUE_NOT_SET)) { - ConfigurationManager.getConfigInstance().setProperty(key, value); - } + String key = getKey(serviceId, suffix); + DynamicStringProperty property = getProperty(key); + if (property.get().equals(VALUE_NOT_SET)) { + ConfigurationManager.getConfigInstance().setProperty(key, value); + } } - protected DynamicStringProperty getProperty(String key) { - return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET); - } + protected DynamicStringProperty getProperty(String key) { + return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET); + } - protected String getKey(String serviceId, String suffix) { - return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix; - } + protected String getKey(String serviceId, String suffix) { + return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix; + } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java index 5d1d2071..88932019 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java @@ -21,9 +21,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonClientPreprocessor; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,7 +46,7 @@ public class RibbonEurekaAutoConfiguration { private EurekaClientConfig clientConfig; @Bean - public RibbonClientPreprocessor ribbonClientPreprocessor(SpringClientFactory clientFactory) { - return new EurekaRibbonClientPreprocessor(clientConfig, clientFactory); + public RibbonClientPreprocessor ribbonClientPreprocessor() { + return new EurekaRibbonClientPreprocessor(clientConfig); } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilter.java index 9d34d55c..e3ccbcd1 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilter.java @@ -18,6 +18,9 @@ package org.springframework.cloud.netflix.ribbon.eureka; import java.util.ArrayList; import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; + import com.netflix.client.config.IClientConfig; import com.netflix.config.ConfigurationManager; import com.netflix.config.DeploymentContext.ContextKey; @@ -31,6 +34,8 @@ import com.netflix.loadbalancer.ZoneAffinityServerListFilter; * @author Dave Syer * */ +@Data +@EqualsAndHashCode(callSuper=false) public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter { private String zone; diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/RibbonRoutingFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/RibbonRoutingFilter.java index 23c7c086..894dfde2 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/RibbonRoutingFilter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/RibbonRoutingFilter.java @@ -81,9 +81,7 @@ public class RibbonRoutingFilter extends ZuulFilter { String serviceId = (String) context.get("serviceId"); - preprocessor.preprocess(serviceId); - - RestClient restClient = clientFactory.namedClient(serviceId, RestClient.class); + RestClient restClient = clientFactory.getClient(serviceId, RestClient.class); String uri = request.getRequestURI(); if (context.get("requestURI") != null) { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBeanTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBeanTests.java index 7b121fae..f80c4550 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBeanTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBeanTests.java @@ -15,6 +15,9 @@ */ package org.springframework.cloud.netflix.eureka; +import static org.junit.Assert.assertEquals; +import static org.springframework.boot.test.EnvironmentTestUtils.addEnvironment; + import org.junit.After; import org.junit.Test; import org.springframework.beans.factory.BeanCreationException; @@ -22,13 +25,9 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.AbstractApplicationContext; -import com.netflix.appinfo.UniqueIdentifier; import com.netflix.appinfo.InstanceInfo.InstanceStatus; - -import static org.junit.Assert.*; -import static org.springframework.boot.test.EnvironmentTestUtils.*; +import com.netflix.appinfo.UniqueIdentifier; /** * @author Dave Syer diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/sample/ApplicationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/sample/ApplicationTests.java index a7b0f7ba..8e4f4a0b 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/sample/ApplicationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/eureka/sample/ApplicationTests.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -11,6 +12,7 @@ import org.springframework.test.context.web.WebAppConfiguration; @SpringApplicationConfiguration(classes = Application.class) @WebAppConfiguration @IntegrationTest("server.port=0") +@DirtiesContext public class ApplicationTests { @Test diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java index 4e594df8..5907e5cd 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/SpringDecoderTests.java @@ -1,10 +1,10 @@ package org.springframework.cloud.netflix.feign; import static org.junit.Assert.*; - import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; @@ -13,6 +13,7 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.web.bind.annotation.RequestMapping; @@ -29,6 +30,7 @@ import java.util.List; @SpringApplicationConfiguration(classes = SpringDecoderTests.Application.class) @WebAppConfiguration @IntegrationTest({ "server.port=0", "spring.application.name=springdecodertest", "spring.jmx.enabled=true" }) +@DirtiesContext public class SpringDecoderTests extends FeignConfiguration { @Value("${local.server.port}") diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTests.java similarity index 98% rename from spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTest.java rename to spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTests.java index 1aa1d78c..10a64c40 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTest.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTests.java @@ -26,7 +26,7 @@ import static org.mockito.Mockito.*; /** * @author Spencer Gibb */ -public class RibbonInterceptorTest { +public class RibbonInterceptorTests { @Mock HttpRequest request; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTests.java similarity index 85% rename from spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTest.java rename to spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTests.java index 1ea19c8d..5845bcfa 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTest.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTests.java @@ -1,28 +1,34 @@ package org.springframework.cloud.netflix.ribbon; -import com.netflix.loadbalancer.BaseLoadBalancer; -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerStats; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyDouble; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.net.URI; +import java.net.URL; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.RibbonServer; -import java.net.URI; -import java.net.URL; -import java.util.Arrays; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import com.netflix.loadbalancer.BaseLoadBalancer; +import com.netflix.loadbalancer.LoadBalancerStats; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerStats; /** * @author Spencer Gibb */ -public class RibbonLoadBalancerClientTest { +public class RibbonLoadBalancerClientTests { @Mock RibbonClientPreprocessor preprocessor; @@ -42,6 +48,7 @@ public class RibbonLoadBalancerClientTest { @Before public void init() { MockitoAnnotations.initMocks(this); + Mockito.when(clientFactory.getLoadBalancerContext(anyString())).thenReturn(new RibbonLoadBalancerContext(loadBalancer)); } @Test @@ -122,11 +129,12 @@ public class RibbonLoadBalancerClientTest { } protected RibbonLoadBalancerClient getRibbonLoadBalancerClient(RibbonServer ribbonServer) { - when(loadBalancer.getName()).thenReturn(ribbonServer.getServiceId()); + when(loadBalancer.getName()).thenReturn(ribbonServer.getServiceId()); when(loadBalancer.chooseServer(anyString())).thenReturn(ribbonServer.server); when(loadBalancer.getLoadBalancerStats()).thenReturn(loadBalancerStats); when(loadBalancerStats.getSingleServerStat(ribbonServer.server)).thenReturn(serverStats); + when(clientFactory.getLoadBalancer(loadBalancer.getName())).thenReturn(loadBalancer); - return new RibbonLoadBalancerClient(preprocessor, clientFactory, Arrays.asList(loadBalancer)); + return new RibbonLoadBalancerClient(clientFactory); } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java new file mode 100644 index 00000000..dd49c27e --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringClientFactoryTests.java @@ -0,0 +1,47 @@ +/* + * 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.ribbon; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import com.netflix.client.DefaultLoadBalancerRetryHandler; + +/** + * @author Dave Syer + * + */ +public class SpringClientFactoryTests { + + private SpringClientFactory factory = new SpringClientFactory(); + + @Test + public void testConfigureRetry() { + AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(ArchaiusAutoConfiguration.class); + EnvironmentTestUtils.addEnvironment(parent, "foo.ribbon.MaxAutoRetries:2"); + factory.setApplicationContext(parent); + DefaultLoadBalancerRetryHandler retryHandler = (DefaultLoadBalancerRetryHandler) factory + .getLoadBalancerContext("foo").getRetryHandler(); + assertEquals(2, retryHandler.getMaxRetriesOnSameServer()); + parent.close(); + factory.destroy(); + } + +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerListTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerListTests.java index 04f0c2a7..8214b932 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerListTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerListTests.java @@ -1,5 +1,16 @@ package org.springframework.cloud.netflix.ribbon.eureka; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + import com.google.common.collect.ImmutableMap; import com.netflix.appinfo.InstanceInfo; import com.netflix.client.config.CommonClientConfigKey; @@ -7,14 +18,6 @@ import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; /** * @author Spencer Gibb @@ -64,7 +67,8 @@ public class DomainExtractingServerListTests { protected DomainExtractingServerList getDomainExtractingServerList(DefaultClientConfigImpl config) { DiscoveryEnabledServer server = mock(DiscoveryEnabledServer.class); - ServerList originalServerList = mock(ServerList.class); + @SuppressWarnings("unchecked") + ServerList originalServerList = mock(ServerList.class); InstanceInfo instanceInfo = mock(InstanceInfo.class); when(server.getInstanceInfo()).thenReturn(instanceInfo); @@ -75,7 +79,7 @@ public class DomainExtractingServerListTests { when(instanceInfo.getIPAddr()).thenReturn(IP_ADDR); when(instanceInfo.getPort()).thenReturn(PORT); - when(originalServerList.getInitialListOfServers()).thenReturn(Arrays.asList(server)); + when(originalServerList.getInitialListOfServers()).thenReturn(Arrays.asList(server)); return new DomainExtractingServerList(originalServerList, config); } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorIntegrationTests.java new file mode 100644 index 00000000..af146017 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorIntegrationTests.java @@ -0,0 +1,97 @@ +/* + * 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.ribbon.eureka; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; +import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; +import org.springframework.cloud.netflix.ribbon.EnableRibbonClient; +import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessorIntegrationTests.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ZoneAvoidanceRule; +import com.netflix.loadbalancer.ZoneAwareLoadBalancer; + +/** + * @author Dave Syer + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = TestConfiguration.class) +@DirtiesContext +public class EurekaRibbonClientPreprocessorIntegrationTests { + + @Autowired + private SpringClientFactory factory; + + @Test + public void serverListIsWrapped() throws Exception { + @SuppressWarnings("unchecked") + ZoneAwareLoadBalancer loadBalancer = (ZoneAwareLoadBalancer) factory + .getLoadBalancer("foo"); + DomainExtractingServerList.class.cast(loadBalancer.getServerListImpl()); + } + + @Test + public void ruleDefaultsToZoneAvoidance() throws Exception { + @SuppressWarnings("unchecked") + ZoneAwareLoadBalancer loadBalancer = (ZoneAwareLoadBalancer) factory + .getLoadBalancer("foo"); + ZoneAvoidanceRule.class.cast(loadBalancer.getRule()); + } + + @Test + public void serverListFilterOverride() throws Exception { + @SuppressWarnings("unchecked") + ZoneAwareLoadBalancer loadBalancer = (ZoneAwareLoadBalancer) factory + .getLoadBalancer("foo"); + assertEquals("myTestZone", + ZonePreferenceServerListFilter.class.cast(loadBalancer.getFilter()).getZone()); + } + + @Configuration + @EnableRibbonClient(@RibbonClient(name = "foo", configuration = FooConfiguration.class)) + @Import({ PropertyPlaceholderAutoConfiguration.class, + ArchaiusAutoConfiguration.class, EurekaClientAutoConfiguration.class, + RibbonAutoConfiguration.class, RibbonEurekaAutoConfiguration.class }) + protected static class TestConfiguration { + } + + @Configuration + protected static class FooConfiguration { + @Bean + public ZonePreferenceServerListFilter serverListFilter() { + ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter(); + filter.setZone("myTestZone"); + return filter; + } + } + +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorTests.java index d4345fb1..144fe7c3 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientPreprocessorTests.java @@ -18,16 +18,16 @@ package org.springframework.cloud.netflix.ribbon.eureka; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessor.*; +import static org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessor.VALUE_NOT_SET; -import com.netflix.config.DynamicStringProperty; import org.junit.After; import org.junit.Test; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import com.netflix.config.ConfigurationManager; import com.netflix.config.DeploymentContext.ContextKey; +import com.netflix.config.DynamicStringProperty; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ZoneAwareLoadBalancer; @@ -37,7 +37,7 @@ import com.netflix.loadbalancer.ZoneAwareLoadBalancer; * */ public class EurekaRibbonClientPreprocessorTests { - + @After public void close() { ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, ""); @@ -47,40 +47,41 @@ public class EurekaRibbonClientPreprocessorTests { public void basicConfigurationCreatedForLoadBalancer() { EurekaClientConfigBean client = new EurekaClientConfigBean(); client.getAvailabilityZones().put(client.getRegion(), "foo"); - SpringClientFactory clientFactory = new SpringClientFactory(); - EurekaRibbonClientPreprocessor clientPreprocessor = new EurekaRibbonClientPreprocessor( - client, clientFactory); + SpringClientFactory clientFactory = new SpringClientFactory(); + EurekaRibbonClientPreprocessor clientPreprocessor = new EurekaRibbonClientPreprocessor( + client); clientPreprocessor.preprocess("service"); - ILoadBalancer balancer = clientFactory.getNamedLoadBalancer("service"); + ILoadBalancer balancer = clientFactory.getLoadBalancer("service"); assertNotNull(balancer); @SuppressWarnings("unchecked") ZoneAwareLoadBalancer aware = (ZoneAwareLoadBalancer) balancer; assertTrue(aware.getServerListImpl() instanceof DomainExtractingServerList); - assertEquals("foo", ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone)); + assertEquals("foo", + ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone)); } - @Test - public void testSetProp() { - EurekaClientConfigBean client = new EurekaClientConfigBean(); - SpringClientFactory clientFactory = new SpringClientFactory(); - EurekaRibbonClientPreprocessor preprocessor = new EurekaRibbonClientPreprocessor( - client, clientFactory); + @Test + public void testSetProp() { + EurekaClientConfigBean client = new EurekaClientConfigBean(); + EurekaRibbonClientPreprocessor preprocessor = new EurekaRibbonClientPreprocessor( + client); - String serviceId = "myService"; - String suffix = "mySuffix"; - String value = "myValue"; + String serviceId = "myService"; + String suffix = "mySuffix"; + String value = "myValue"; - DynamicStringProperty property = preprocessor.getProperty(preprocessor.getKey(serviceId, suffix)); + DynamicStringProperty property = preprocessor.getProperty(preprocessor.getKey( + serviceId, suffix)); - assertEquals("property doesn't have default value", VALUE_NOT_SET, property.get()); + assertEquals("property doesn't have default value", VALUE_NOT_SET, property.get()); - preprocessor.setProp(serviceId, suffix, value); + preprocessor.setProp(serviceId, suffix, value); - assertEquals("property has wrong value", value, property.get()); + assertEquals("property has wrong value", value, property.get()); - preprocessor.setProp(serviceId, suffix, value); + preprocessor.setProp(serviceId, suffix, value); - assertEquals("property has wrong value", value, property.get()); - } + assertEquals("property has wrong value", value, property.get()); + } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java index b79e9404..bb67ffd6 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java @@ -13,6 +13,7 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -22,6 +23,7 @@ import org.springframework.test.context.web.WebAppConfiguration; @IntegrationTest({ "server.port: 0", "zuul.routes.other: /test/**=http://localhost:7777/local", "zuul.routes.simple: /simple/**" }) +@DirtiesContext public class SampleZuulProxyApplicationTests { @Value("${local.server.port}")