diff --git a/.travis.yml b/.travis.yml
index 958d94a3..6e4067b9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,9 @@
+sudo: false
+
+cache:
+ directories:
+ - $HOME/.m2
+
language: java
before_install:
- git config user.name "$GIT_NAME"
@@ -10,8 +16,8 @@ install:
- mvn --settings .settings.xml install -P docs -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true
- ./docs/src/main/asciidoc/ghpages.sh
script:
-- '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || mvn --settings .settings.xml deploy -nsu -Dmaven.test.redirectTestOutputToFile=true'
-- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] || mvn --settings .settings.xml install -nsu -Dmaven.test.redirectTestOutputToFile=true'
+- '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || mvn --settings .settings.xml clean deploy -nsu'
+- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] || mvn --settings .settings.xml clean install -nsu'
env:
global:
- GIT_NAME="Spencer Gibb"
diff --git a/pom.xml b/pom.xml
index d233bd8f..0b81ab10 100644
--- a/pom.xml
+++ b/pom.xml
@@ -224,7 +224,7 @@
org.codehaus.groovy
groovy-all
- 2.4.3
+ 2.4.4
test
diff --git a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperConfigBootstrapConfiguration.java b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperConfigBootstrapConfiguration.java
similarity index 93%
rename from spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperConfigBootstrapConfiguration.java
rename to spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperConfigBootstrapConfiguration.java
index 87408dee..e0900993 100644
--- a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperConfigBootstrapConfiguration.java
+++ b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperConfigBootstrapConfiguration.java
@@ -1,4 +1,4 @@
-package org.springframework.cloud.zookeeper.config;
+package org.springframework.cloud.zookeeper.common;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration;
diff --git a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperConfigProperties.java b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperConfigProperties.java
similarity index 90%
rename from spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperConfigProperties.java
rename to spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperConfigProperties.java
index 843aad81..a2782539 100644
--- a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperConfigProperties.java
+++ b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperConfigProperties.java
@@ -1,4 +1,4 @@
-package org.springframework.cloud.zookeeper.config;
+package org.springframework.cloud.zookeeper.common;
import lombok.Data;
diff --git a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperPropertySource.java b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperPropertySource.java
similarity index 98%
rename from spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperPropertySource.java
rename to spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperPropertySource.java
index 0dfd46ce..b57bb233 100644
--- a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperPropertySource.java
+++ b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperPropertySource.java
@@ -1,4 +1,4 @@
-package org.springframework.cloud.zookeeper.config;
+package org.springframework.cloud.zookeeper.common;
import java.nio.charset.Charset;
import java.util.ArrayList;
diff --git a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperPropertySourceLocator.java b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperPropertySourceLocator.java
similarity index 97%
rename from spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperPropertySourceLocator.java
rename to spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperPropertySourceLocator.java
index 5c4405b1..f90bf779 100644
--- a/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/config/ZookeeperPropertySourceLocator.java
+++ b/spring-cloud-zookeeper-config/src/main/java/org/springframework/cloud/zookeeper/common/ZookeeperPropertySourceLocator.java
@@ -1,4 +1,4 @@
-package org.springframework.cloud.zookeeper.config;
+package org.springframework.cloud.zookeeper.common;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ConditionalOnRibbonZookeeper.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ConditionalOnRibbonZookeeper.java
new file mode 100644
index 00000000..2681fd2c
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ConditionalOnRibbonZookeeper.java
@@ -0,0 +1,17 @@
+package org.springframework.cloud.zookeeper.discovery;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Wrapper annotation to enable Ribbon for Zookeeper
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@ConditionalOnProperty(value = "ribbon.zookeeper.enabled", matchIfMissing = true)
+public @interface ConditionalOnRibbonZookeeper {
+}
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/RibbonZookeeperAutoConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/RibbonZookeeperAutoConfiguration.java
index 0861194e..e0251f26 100644
--- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/RibbonZookeeperAutoConfiguration.java
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/RibbonZookeeperAutoConfiguration.java
@@ -18,7 +18,6 @@ package org.springframework.cloud.zookeeper.discovery;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
@@ -31,7 +30,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties
@ConditionalOnBean(SpringClientFactory.class)
-@ConditionalOnProperty(value = "ribbon.zookeeper.enabled", matchIfMissing = true)
+@ConditionalOnRibbonZookeeper
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = ZookeeperRibbonClientConfiguration.class)
public class RibbonZookeeperAutoConfiguration {
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperRibbonClientConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperRibbonClientConfiguration.java
index e5f46889..6725f75d 100644
--- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperRibbonClientConfiguration.java
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperRibbonClientConfiguration.java
@@ -20,15 +20,15 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
+import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.ServerList;
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.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.cloud.zookeeper.discovery.dependency.DependenciesPassedCondition;
+import org.springframework.cloud.zookeeper.discovery.dependency.ConditionalOnDependenciesPassed;
+import org.springframework.cloud.zookeeper.discovery.dependency.DependenciesBasedLoadBalancer;
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@@ -37,7 +37,7 @@ import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextB
import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity;
/**
- * Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
+ * Preprocessor that configures defaults for zookeeper-discovered ribbon clients. Such as:
* @zone, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
* NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more
*
@@ -61,14 +61,20 @@ public class ZookeeperRibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
- @Conditional(DependenciesPassedCondition.class)
- @ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
+ @ConditionalOnDependenciesPassed
public ServerList> ribbonServerListFromDependencies(IClientConfig config, ZookeeperDependencies zookeeperDependencies) {
ZookeeperServerList serverList = new ZookeeperServerList(serviceDiscovery.getServiceDiscovery());
serverList.initFromDependencies(config, zookeeperDependencies);
return serverList;
}
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnDependenciesPassed
+ public ILoadBalancer dependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies, ServerList serverList) {
+ return new DependenciesBasedLoadBalancer(zookeeperDependencies, serverList);
+ }
+
@Bean
@ConditionalOnMissingBean
public ServerList> ribbonServerList(IClientConfig config) {
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ConditionalOnDependenciesPassed.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ConditionalOnDependenciesPassed.java
new file mode 100644
index 00000000..fb8980ba
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ConditionalOnDependenciesPassed.java
@@ -0,0 +1,21 @@
+package org.springframework.cloud.zookeeper.discovery.dependency;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Conditional;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to turn on a feature if Zookeeper dependencies have been passed
+ *
+ * @author Marcin Grzejszczak, 4financeIT
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Conditional(DependenciesPassedCondition.class)
+@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
+public @interface ConditionalOnDependenciesPassed {
+}
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesBasedLoadBalancer.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesBasedLoadBalancer.java
new file mode 100644
index 00000000..8b8dedf0
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesBasedLoadBalancer.java
@@ -0,0 +1,75 @@
+package org.springframework.cloud.zookeeper.discovery.dependency;
+
+import com.netflix.loadbalancer.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * LoadBalancer that delegates to other rules depending on the provided load balancing strategy
+ * in the {@link ZookeeperDependencies.ZookeeperDependency#getLoadBalancerType()}
+ *
+ * @author Marcin Grzejszczak, 4financeIT
+ */
+@Slf4j
+public class DependenciesBasedLoadBalancer extends BaseLoadBalancer {
+
+ private final Map ruleCache = new ConcurrentHashMap<>();
+
+ private final ZookeeperDependencies zookeeperDependencies;
+
+ public DependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies, ServerList serverList) {
+ this.zookeeperDependencies = zookeeperDependencies;
+ setServersList(serverList.getInitialListOfServers());
+ }
+
+ @Override
+ public Server chooseServer(Object key) {
+ String keyAsString = (String) key;
+ ZookeeperDependencies.ZookeeperDependency dependency = zookeeperDependencies.getDependencyForAlias(keyAsString);
+ if (dependency == null) {
+ log.debug("No dependency found for alias [{}] - will use the default rule which is [{}]", keyAsString, rule);
+ return rule.choose(key);
+ };
+ cacheEntryIfMissing(keyAsString, dependency);
+ log.debug("Will try to retrieve dependency for key [{}]. Current cache contents [{}]", keyAsString, ruleCache);
+ return ruleCache.get(keyAsString).choose(key);
+ }
+
+ private void cacheEntryIfMissing(String keyAsString, ZookeeperDependencies.ZookeeperDependency dependency) {
+ if (!ruleCache.containsKey(keyAsString)) {
+ log.debug("Cache doesn't contain entry for [{}]", keyAsString);
+ ruleCache.put(keyAsString, chooseRuleForLoadBalancerType(dependency.getLoadBalancerType()));
+ }
+ }
+
+ private IRule chooseRuleForLoadBalancerType(LoadBalancerType type) {
+ switch (type) {
+ case ROUND_ROBIN:
+ return getRoundRobinRule();
+ case RANDOM:
+ return getRandomRule();
+ case STICKY:
+ return getStickyRule();
+ default:
+ throw new IllegalArgumentException("Unknown load balancer type " + type);
+ }
+ }
+
+ private RoundRobinRule getRoundRobinRule() {
+ return new RoundRobinRule(this);
+ }
+
+ private IRule getRandomRule() {
+ RandomRule randomRule = new RandomRule();
+ randomRule.setLoadBalancer(this);
+ return randomRule;
+ }
+
+ private IRule getStickyRule() {
+ StickyRule stickyRule = new StickyRule(getRoundRobinRule());
+ stickyRule.setLoadBalancer(this);
+ return stickyRule;
+ }
+}
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependencyRibbonAutoConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependencyRibbonAutoConfiguration.java
new file mode 100644
index 00000000..a808d0a1
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependencyRibbonAutoConfiguration.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013-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.zookeeper.discovery.dependency;
+
+import com.netflix.loadbalancer.ILoadBalancer;
+import com.netflix.loadbalancer.Server;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
+import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
+import org.springframework.cloud.zookeeper.discovery.ConditionalOnRibbonZookeeper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ *
+ * Provides LoadBalancerClient that at runtime can pick proper load balancing strategy
+ * basing on the Zookeeper dependencies from properties
+ *
+ * @author Marcin Grzejszczak, 4financeIT
+ */
+@AutoConfigureBefore(RibbonAutoConfiguration.class)
+@ConditionalOnRibbonZookeeper
+@Configuration
+@Slf4j
+public class DependencyRibbonAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnDependenciesPassed
+ public LoadBalancerClient loadBalancerClient(SpringClientFactory springClientFactory) {
+ return new RibbonLoadBalancerClient(springClientFactory) {
+ @Override
+ protected Server getServer(String serviceId) {
+ ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
+ return loadBalancer == null ? null : chooseServerByServiceIdOrDefault(loadBalancer, serviceId);
+ }
+
+ private Server chooseServerByServiceIdOrDefault(ILoadBalancer loadBalancer, String serviceId) {
+ log.debug("Dependencies are set - will try to load balance via provided load balancer [{}] for key [{}]", loadBalancer, serviceId);
+ Server server = loadBalancer.chooseServer(serviceId);
+ log.debug("Retrieved server [{}] via load balancer", server);
+ return server != null ? server : loadBalancer.chooseServer("default");
+ }
+ };
+ }
+
+}
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/StickyRule.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/StickyRule.java
new file mode 100644
index 00000000..6da517d2
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/StickyRule.java
@@ -0,0 +1,64 @@
+package org.springframework.cloud.zookeeper.discovery.dependency;
+
+import com.netflix.client.config.IClientConfig;
+import com.netflix.loadbalancer.AbstractLoadBalancerRule;
+import com.netflix.loadbalancer.IRule;
+import com.netflix.loadbalancer.Server;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Load balancing rule that returns always the same instance.
+ *
+ * Ported from {@link org.apache.curator.x.discovery.strategies.StickyStrategy}
+ *
+ * author: Marcin Grzejszczak, 4financeIT
+ */
+@Slf4j
+public class StickyRule extends AbstractLoadBalancerRule {
+ private final IRule masterStrategy;
+ private final AtomicReference ourInstance = new AtomicReference<>(null);
+ private final AtomicInteger instanceNumber = new AtomicInteger(-1);
+
+ public StickyRule(IRule masterStrategy) {
+ this.masterStrategy = masterStrategy;
+ }
+
+ @Override
+ public void initWithNiwsConfig(IClientConfig iClientConfig) {
+
+ }
+
+ @Override
+ public Server choose(Object key) {
+ final List instances = getLoadBalancer().getServerList(true);
+ log.debug("Instances taken from load balancer {}", instances);
+ Server localOurInstance = ourInstance.get();
+ log.debug("Current saved instance [{}]", localOurInstance);
+ if (!instances.contains(localOurInstance)) {
+ ourInstance.compareAndSet(localOurInstance, null);
+ }
+ if (ourInstance.get() == null) {
+ Server instance = masterStrategy.choose(key);
+ if (ourInstance.compareAndSet(null, instance)) {
+ instanceNumber.incrementAndGet();
+ }
+ }
+ return ourInstance.get();
+ }
+
+ /**
+ * Each time a new instance is picked, an internal counter is incremented. This way you
+ * can track when/if the instance changes. The instance can change when the selected instance
+ * is not in the current list of instances returned by the instance provider
+ *
+ * @return instance number
+ */
+ public int getInstanceNumber() {
+ return instanceNumber.get();
+ }
+
+}
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependencies.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependencies.java
index 516bd8a4..aabecc21 100644
--- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependencies.java
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependencies.java
@@ -56,7 +56,7 @@ public class ZookeeperDependencies {
private String path;
- private LoadBalancerType loadBalancerType;
+ private LoadBalancerType loadBalancerType = LoadBalancerType.ROUND_ROBIN;
private String contentTypeTemplate;
@@ -65,6 +65,7 @@ public class ZookeeperDependencies {
private Map headers;
private boolean required;
+
}
public Collection getDependencyConfigurations() {
@@ -75,6 +76,15 @@ public class ZookeeperDependencies {
return !dependencies.isEmpty();
}
+ public ZookeeperDependency getDependencyForPath(final String path) {
+ for (Map.Entry zookeeperDependencyEntry : dependencies.entrySet()) {
+ if (zookeeperDependencyEntry.getValue().getPath().equals(path)) {
+ return zookeeperDependencyEntry.getValue();
+ }
+ }
+ return null;
+ }
+
public ZookeeperDependency getDependencyForAlias(final String alias) {
for (Map.Entry zookeeperDependencyEntry : dependencies.entrySet()) {
if (zookeeperDependencyEntry.getKey().equals(alias)) {
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesAutoConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesAutoConfiguration.java
index fedb4e40..73ad22e4 100644
--- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesAutoConfiguration.java
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesAutoConfiguration.java
@@ -17,11 +17,9 @@ package org.springframework.cloud.zookeeper.discovery.dependency;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
@@ -34,8 +32,7 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@EnableConfigurationProperties
-@Conditional(DependenciesPassedCondition.class)
-@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
+@ConditionalOnDependenciesPassed
@AutoConfigureAfter(ZookeeperAutoConfiguration.class)
public class ZookeeperDependenciesAutoConfiguration {
diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherAutoConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherAutoConfiguration.java
index b10613ac..169751eb 100644
--- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherAutoConfiguration.java
+++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherAutoConfiguration.java
@@ -15,24 +15,22 @@
*/
package org.springframework.cloud.zookeeper.discovery.watcher;
-import java.util.ArrayList;
-import java.util.List;
-
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.zookeeper.discovery.ZookeeperServiceDiscovery;
-import org.springframework.cloud.zookeeper.discovery.dependency.DependenciesPassedCondition;
+import org.springframework.cloud.zookeeper.discovery.dependency.ConditionalOnDependenciesPassed;
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration;
import org.springframework.cloud.zookeeper.discovery.watcher.presence.DefaultDependencyPresenceOnStartupVerifier;
import org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Provides hooks for observing dependency lifecycle in Zookeeper.
* Needs custom dependencies to be set in order to work.
@@ -43,8 +41,7 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@EnableConfigurationProperties
-@Conditional(DependenciesPassedCondition.class)
-@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
+@ConditionalOnDependenciesPassed
@AutoConfigureAfter(ZookeeperDependenciesAutoConfiguration.class)
public class DependencyWatcherAutoConfiguration {
diff --git a/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories
index 93d38d8e..6e8246ff 100644
--- a/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories
@@ -1,5 +1,6 @@
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.springframework.cloud.zookeeper.discovery.dependency.DependencyRibbonAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherAutoConfiguration
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/CommonTestConfig.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/CommonTestConfig.groovy
similarity index 70%
rename from spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/CommonTestConfig.groovy
rename to spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/CommonTestConfig.groovy
index 46271055..c4053bb6 100644
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/CommonTestConfig.groovy
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/CommonTestConfig.groovy
@@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.cloud.zookeeper.discovery
+package org.springframework.cloud.zookeeper.common
-import com.github.tomakehurst.wiremock.WireMockServer
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
-import org.apache.curator.framework.CuratorFramework
import org.apache.curator.test.TestingServer
import org.springframework.cloud.zookeeper.ZookeeperProperties
import org.springframework.context.annotation.Bean
@@ -34,15 +32,6 @@ class CommonTestConfig {
return new TestingServer(SocketUtils.findAvailableTcpPort())
}
- @Bean(initMethod = "start", destroyMethod = "stop")
- TestServiceRegistrar testServiceRegistrar(CuratorFramework curatorFramework) {
- return new TestServiceRegistrar(wiremockServer().port(), curatorFramework)
- }
-
- @Bean(initMethod = "start", destroyMethod = "shutdown") WireMockServer wiremockServer() {
- return new WireMockServer(SocketUtils.findAvailableTcpPort())
- }
-
@Bean ZookeeperProperties zookeeperProperties() {
return new ZookeeperProperties(connectString: "localhost:${testingServer().port}")
}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/TestRibbonClient.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/TestRibbonClient.groovy
new file mode 100644
index 00000000..c546f8cd
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/TestRibbonClient.groovy
@@ -0,0 +1,23 @@
+package org.springframework.cloud.zookeeper.common
+
+import org.springframework.web.client.RestTemplate
+
+class TestRibbonClient extends TestServiceRestClient {
+
+ private final String thisAppName
+
+ TestRibbonClient(RestTemplate restTemplate) {
+ super(restTemplate)
+ this.thisAppName = 'someName'
+ }
+
+ TestRibbonClient(RestTemplate restTemplate, String thisAppName) {
+ super(restTemplate)
+ this.thisAppName = thisAppName
+ }
+
+ String thisHealthCheck() {
+ return restTemplate.getForObject("http://$thisAppName/health", String)
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRestClient.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/TestServiceRestClient.groovy
similarity index 74%
rename from spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRestClient.groovy
rename to spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/TestServiceRestClient.groovy
index 75060d89..7ca681ec 100644
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRestClient.groovy
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/common/TestServiceRestClient.groovy
@@ -13,13 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.cloud.zookeeper.discovery
+package org.springframework.cloud.zookeeper.common
-import groovy.transform.PackageScope
import groovy.transform.CompileStatic
import org.springframework.web.client.RestTemplate
-@PackageScope
@CompileStatic
class TestServiceRestClient {
@@ -29,11 +27,11 @@ class TestServiceRestClient {
this.restTemplate = restTemplate
}
- String pingService(String alias) {
- return restTemplate.getForObject("http://$alias/ping", String)
+ String callService(String alias, String endpoint) {
+ return restTemplate.getForObject("http://$alias/$endpoint", String)
}
- String pingOnUrl(String url) {
- return new RestTemplate().getForObject("http://$url/ping", String)
+ String callOnUrl(String url, String endpoint) {
+ return new RestTemplate().getForObject("http://$url/$endpoint", String)
}
}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/PollingUtils.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/PollingUtils.groovy
new file mode 100644
index 00000000..251c14ea
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/PollingUtils.groovy
@@ -0,0 +1,14 @@
+package org.springframework.cloud.zookeeper.discovery
+
+trait PollingUtils {
+ Closure willPass(Closure closure) {
+ return {
+ try {
+ closure()
+ } catch (Exception e) {
+ throw new AssertionError("Exception occurred while evaluating closure", e)
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRegistrar.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRegistrar.groovy
index ff8891d7..b6f9cec2 100644
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRegistrar.groovy
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRegistrar.groovy
@@ -25,12 +25,12 @@ import org.apache.curator.x.discovery.UriSpec
@CompileStatic
class TestServiceRegistrar {
- private final int wiremockServerPort
+ private final int serverPort
private final CuratorFramework curatorFramework
private final ServiceDiscovery serviceDiscovery
- TestServiceRegistrar(int wiremockServerPort, CuratorFramework curatorFramework) {
- this.wiremockServerPort = wiremockServerPort
+ TestServiceRegistrar(int serverPort, CuratorFramework curatorFramework) {
+ this.serverPort = serverPort
this.curatorFramework = curatorFramework
this.serviceDiscovery = serviceDiscovery()
}
@@ -42,7 +42,7 @@ class TestServiceRegistrar {
ServiceInstance serviceInstance() {
return ServiceInstance.builder().uriSpec(new UriSpec("{scheme}://{address}:{port}/"))
.address('localhost')
- .port(wiremockServerPort)
+ .port(serverPort)
.name('testInstance')
.build()
}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy
index f62bb21d..e8b52a83 100644
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy
@@ -15,8 +15,6 @@
*/
package org.springframework.cloud.zookeeper.discovery
-import com.github.tomakehurst.wiremock.WireMockServer
-import com.github.tomakehurst.wiremock.client.WireMock
import groovy.json.JsonSlurper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
@@ -27,53 +25,47 @@ import org.springframework.cloud.client.ServiceInstance
import org.springframework.cloud.client.discovery.DiscoveryClient
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.cloud.client.loadbalancer.LoadBalanced
+import org.springframework.cloud.zookeeper.common.CommonTestConfig
+import org.springframework.cloud.zookeeper.common.TestRibbonClient
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
+import org.springframework.context.annotation.Profile
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
-import static com.github.tomakehurst.wiremock.client.WireMock.*
-
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
@ActiveProfiles('ribbon')
@WebIntegrationTest(randomPort = true)
class ZookeeperDiscoveryISpec extends Specification {
- public static final String TEST_INSTANCE_NAME = 'testInstance'
-
@Autowired TestRibbonClient testRibbonClient
- @Autowired WireMockServer wiremockServer
@Autowired DiscoveryClient discoveryClient
@Autowired ZookeeperServiceDiscovery serviceDiscovery
- WireMock wireMock
+ @Value('${spring.application.name}') String springAppName
- def setup() {
- wireMock = new WireMock('localhost', wiremockServer.port())
- wireMock.register(get(urlEqualTo('/ping')).willReturn(aResponse().withBody('pong')))
- }
-
- def 'should find a collaborator via Ribbon'() {
- expect:
- 'pong' == testRibbonClient.pingService(TEST_INSTANCE_NAME)
- }
def 'should find the app by its name via Ribbon'() {
- given:
- def jsonSlurper = new JsonSlurper()
- def health = jsonSlurper.parseText(testRibbonClient.thisHealthCheck())
expect:
- 'UP' == health.status
+ 'UP' == registeredServiceStatusViaServiceName()
}
def 'should find a collaborator via discovery client'() {
given:
- List instances = discoveryClient.getInstances(TEST_INSTANCE_NAME)
+ List instances = discoveryClient.getInstances(springAppName)
ServiceInstance instance = instances.first()
expect:
- 'pong' == testRibbonClient.pingOnUrl("${instance.host}:${instance.port}")
+ 'UP' == registeredServiceStatus(instance)
+ }
+
+ private String registeredServiceStatusViaServiceName() {
+ return new JsonSlurper().parseText(testRibbonClient.thisHealthCheck()).status
+ }
+
+ private String registeredServiceStatus(ServiceInstance instance) {
+ return new JsonSlurper().parseText(testRibbonClient.callOnUrl("${instance.host}:${instance.port}", 'health')).status
}
def 'should properly find local instance'() {
@@ -85,6 +77,7 @@ class ZookeeperDiscoveryISpec extends Specification {
@EnableAutoConfiguration
@Import(CommonTestConfig)
@EnableDiscoveryClient
+ @Profile('ribbon')
static class Config {
@Bean
@@ -92,21 +85,5 @@ class ZookeeperDiscoveryISpec extends Specification {
@Value('${spring.application.name}') String springAppName) {
return new TestRibbonClient(restTemplate, springAppName)
}
-
- }
-
- static class TestRibbonClient extends TestServiceRestClient {
-
- private final String thisAppName
-
- TestRibbonClient(RestTemplate restTemplate, String thisAppName) {
- super(restTemplate)
- this.thisAppName = thisAppName
- }
-
- String thisHealthCheck() {
- return restTemplate.getForObject("http://$thisAppName/health", String)
- }
-
}
}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/StickyRuleISpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/StickyRuleISpec.groovy
new file mode 100644
index 00000000..0bb581d7
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/StickyRuleISpec.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012-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.zookeeper.discovery.dependency
+import org.apache.curator.framework.CuratorFramework
+import org.apache.curator.test.TestingServer
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.boot.test.SpringApplicationContextLoader
+import org.springframework.boot.test.WebIntegrationTest
+import org.springframework.cloud.client.discovery.DiscoveryClient
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient
+import org.springframework.cloud.zookeeper.ZookeeperProperties
+import org.springframework.cloud.zookeeper.discovery.PollingUtils
+import org.springframework.cloud.zookeeper.discovery.TestServiceRegistrar
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.context.annotation.Profile
+import org.springframework.test.context.ActiveProfiles
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.util.SocketUtils
+import spock.lang.Specification
+import spock.util.concurrent.PollingConditions
+
+@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
+@ActiveProfiles('loadbalancerclient')
+@WebIntegrationTest(randomPort = true)
+class StickyRuleISpec extends Specification implements PollingUtils {
+
+ @Autowired LoadBalancerClient loadBalancerClient
+ @Autowired DiscoveryClient discoveryClient
+ PollingConditions conditions
+
+ def setup() {
+ conditions = new PollingConditions()
+ }
+
+ def 'should use sticky load balancing strategy taken from Zookeeper dependencies'() {
+ expect:
+ thereAreTwoRegisteredServices()
+ URI uri = getUriForAlias()
+ conditions.eventually willPass {
+ 2.times {
+ assert uri == getUriForAlias()
+ }
+ }
+ }
+
+ private boolean thereAreTwoRegisteredServices() {
+ return discoveryClient.getInstances('someAlias')?.size() == 2
+ }
+
+ private URI getUriForAlias() {
+ return loadBalancerClient.choose('someAlias')?.uri
+ }
+
+
+ @Configuration
+ @EnableAutoConfiguration
+ @EnableDiscoveryClient
+ @Profile('loadbalancerclient')
+ static class Config {
+
+ @Bean(destroyMethod = 'close')
+ TestingServer testingServer() {
+ return new TestingServer(SocketUtils.findAvailableTcpPort())
+ }
+
+ @Bean ZookeeperProperties zookeeperProperties() {
+ return new ZookeeperProperties(connectString: "localhost:${testingServer().port}")
+ }
+
+ @Bean(initMethod = "start", destroyMethod = "stop") TestServiceRegistrar serviceOne(CuratorFramework curatorFramework) {
+ return new TestServiceRegistrar(SocketUtils.findAvailableTcpPort(), curatorFramework)
+ }
+
+ @Bean(initMethod = "start", destroyMethod = "stop") TestServiceRegistrar serviceTwo(CuratorFramework curatorFramework) {
+ return new TestServiceRegistrar(SocketUtils.findAvailableTcpPort(), curatorFramework)
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesSpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesSpec.groovy
new file mode 100644
index 00000000..927e1ebb
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesSpec.groovy
@@ -0,0 +1,64 @@
+package org.springframework.cloud.zookeeper.discovery.dependency
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ZookeeperDependenciesSpec extends Specification {
+
+ private static final ZookeeperDependencies.ZookeeperDependency EXPECTED_DEPENDENCY = new ZookeeperDependencies.ZookeeperDependency(
+ 'id',
+ 'path',
+ LoadBalancerType.RANDOM,
+ 'contentTypeTemplate',
+ 'version',
+ [header: 'value'],
+ false
+ )
+ private static final Map DEPENDENCIES = [
+ alias: EXPECTED_DEPENDENCY
+ ]
+
+ ZookeeperDependencies zookeeperDependencies = new ZookeeperDependencies(dependencies: DEPENDENCIES)
+
+ @Unroll
+ def "should retrieve dependency [#expectedDependency] for path [#path]"() {
+ expect:
+ expectedDependency == zookeeperDependencies.getDependencyForPath(path)
+ where:
+ path || expectedDependency
+ 'unknownPath' || null
+ 'path' || EXPECTED_DEPENDENCY
+ }
+
+ @Unroll
+ def "should retrieve dependency [#expectedDependency] for alias [#alias]"() {
+ expect:
+ expectedDependency == zookeeperDependencies.getDependencyForAlias(alias)
+ where:
+ alias || expectedDependency
+ 'unknownAlias' || null
+ 'alias' || EXPECTED_DEPENDENCY
+ }
+
+ @Unroll
+ def "should retrieve alias [#expectedAlias] for path [#path]"() {
+ expect:
+ expectedAlias == zookeeperDependencies.getAliasForPath(path)
+ where:
+ path || expectedAlias
+ 'unknownPath' || ''
+ 'path' || 'alias'
+ }
+
+ @Unroll
+ def "should retrieve path [#expectedPath] for alias [#alias]"() {
+ expect:
+ expectedPath == zookeeperDependencies.getPathForAlias(alias)
+ where:
+ alias || expectedPath
+ 'unknownAlias' || ''
+ 'alias' || 'path'
+ }
+
+
+}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryWithDependenciesISpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDiscoveryWithDependenciesISpec.groovy
similarity index 63%
rename from spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryWithDependenciesISpec.groovy
rename to spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDiscoveryWithDependenciesISpec.groovy
index cef1479f..33ab8ca5 100644
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryWithDependenciesISpec.groovy
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDiscoveryWithDependenciesISpec.groovy
@@ -13,10 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.cloud.zookeeper.discovery
+package org.springframework.cloud.zookeeper.discovery.dependency
-import com.github.tomakehurst.wiremock.WireMockServer
-import com.github.tomakehurst.wiremock.client.WireMock
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.SpringApplicationContextLoader
@@ -25,39 +23,46 @@ import org.springframework.cloud.client.ServiceInstance
import org.springframework.cloud.client.discovery.DiscoveryClient
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.cloud.client.loadbalancer.LoadBalanced
+import org.springframework.cloud.zookeeper.common.CommonTestConfig
+import org.springframework.cloud.zookeeper.common.TestRibbonClient
+import org.springframework.cloud.zookeeper.discovery.PollingUtils
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
+import org.springframework.context.annotation.Profile
+import org.springframework.stereotype.Controller
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
+import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
-
-import static com.github.tomakehurst.wiremock.client.WireMock.*
+import spock.util.concurrent.PollingConditions
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
-@ActiveProfiles('watcher')
+@ActiveProfiles('dependencies')
@WebIntegrationTest(randomPort = true)
-class ZookeeperDiscoveryWithDependenciesISpec extends Specification {
+class ZookeeperDiscoveryWithDependenciesISpec extends Specification implements PollingUtils {
@Autowired TestRibbonClient testRibbonClient
- @Autowired WireMockServer wiremockServer
@Autowired DiscoveryClient discoveryClient
- WireMock wireMock
+ PollingConditions conditions
def setup() {
- wireMock = new WireMock('localhost', wiremockServer.port())
- wireMock.register(get(urlEqualTo('/ping')).willReturn(aResponse().withBody('pong')))
+ conditions = new PollingConditions()
}
def 'should find an instance via path when alias is not found'() {
expect:
- !discoveryClient.getInstances('some/name/without/alias').empty
+ conditions.eventually willPass {
+ assert !discoveryClient.getInstances('nameWithoutAlias').empty
+ }
}
def 'should find a collaborator via Ribbon by using its alias from dependencies'() {
expect:
- 'pong' == testRibbonClient.pingService('someAlias')
+ conditions.eventually willPass {
+ assert callingServiceAtBeansEndpointIsNotEmpty()
+ }
}
def 'should find a collaborator via discovery client'() {
@@ -65,13 +70,24 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification {
List instances = discoveryClient.getInstances('someAlias')
ServiceInstance instance = instances.first()
expect:
- 'pong' == testRibbonClient.pingOnUrl("${instance.host}:${instance.port}")
+ conditions.eventually willPass {
+ assert callingServiceViaUrlOnBeansEndpointIsNotEmpty(instance)
+ }
+ }
+
+ private boolean callingServiceAtBeansEndpointIsNotEmpty() {
+ return !testRibbonClient.callService('someAlias', 'beans').empty
+ }
+
+ private boolean callingServiceViaUrlOnBeansEndpointIsNotEmpty(ServiceInstance instance) {
+ return !testRibbonClient.callOnUrl("${instance.host}:${instance.port}", 'beans').empty
}
@Configuration
@EnableAutoConfiguration
@Import(CommonTestConfig)
@EnableDiscoveryClient
+ @Profile('dependencies')
static class Config {
@Bean
@@ -81,10 +97,12 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification {
}
- static class TestRibbonClient extends TestServiceRestClient {
+ @Controller
+ @Profile('dependencies')
+ class PingController {
- TestRibbonClient(RestTemplate restTemplate) {
- super(restTemplate)
+ @RequestMapping('/ping') String ping() {
+ return 'pong'
}
}
}
\ No newline at end of file
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcherSpringISpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcherSpringISpec.groovy
index 7f604fc4..6640231b 100644
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcherSpringISpec.groovy
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcherSpringISpec.groovy
@@ -14,7 +14,6 @@
* limitations under the License.
*/
package org.springframework.cloud.zookeeper.discovery.watcher
-
import org.apache.curator.framework.CuratorFramework
import org.apache.curator.framework.CuratorFrameworkFactory
import org.apache.curator.retry.ExponentialBackoffRetry
@@ -26,11 +25,13 @@ import org.apache.curator.x.discovery.UriSpec
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.SpringApplicationContextLoader
+import org.springframework.cloud.zookeeper.discovery.PollingUtils
import org.springframework.cloud.zookeeper.discovery.ZookeeperServiceDiscovery
import org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier
import org.springframework.cloud.zookeeper.discovery.watcher.presence.LogMissingDependencyChecker
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
+import org.springframework.context.annotation.Profile
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
@@ -42,12 +43,16 @@ import javax.annotation.PreDestroy
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
@ActiveProfiles('watcher')
-class DefaultDependencyWatcherSpringISpec extends Specification {
+class DefaultDependencyWatcherSpringISpec extends Specification implements PollingUtils {
@Autowired AssertableDependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier
@Autowired AssertableDependencyWatcherListener dependencyWatcherListener
- @Autowired
- ZookeeperServiceDiscovery serviceDiscovery
+ @Autowired ZookeeperServiceDiscovery serviceDiscovery
+ PollingConditions conditions
+
+ def setup() {
+ conditions = new PollingConditions()
+ }
def 'should verify that presence of a dependency has been checked'() {
expect:
@@ -56,15 +61,17 @@ class DefaultDependencyWatcherSpringISpec extends Specification {
def 'should verify that dependency watcher listener is successfully registered and operational'() {
when:
- serviceDiscovery.serviceDiscovery.unregisterService(serviceDiscovery.serviceInstance)
+ serviceDiscovery.serviceDiscovery.unregisterService(serviceDiscovery.serviceInstance)
+
then:
- new PollingConditions().eventually {
- dependencyWatcherListener.dependencyState == DependencyState.DISCONNECTED
+ conditions.eventually willPass {
+ assert dependencyWatcherListener.dependencyState == DependencyState.DISCONNECTED
}
}
@Configuration
@EnableAutoConfiguration
+ @Profile('watcher')
static class Config {
@Bean
@@ -79,9 +86,7 @@ class DefaultDependencyWatcherSpringISpec extends Specification {
@Bean
ZookeeperServiceDiscovery zookeeperServiceDiscovery() {
- return new MyZookeeperServiceDiscovery(curatorFramework()) {
-
- }
+ return new MyZookeeperServiceDiscovery(curatorFramework())
}
@Bean(initMethod = 'start', destroyMethod = 'close')
diff --git a/spring-cloud-zookeeper-discovery/src/test/resources/application-dependencies.yml b/spring-cloud-zookeeper-discovery/src/test/resources/application-dependencies.yml
new file mode 100644
index 00000000..502c8393
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/resources/application-dependencies.yml
@@ -0,0 +1,20 @@
+spring.application.name: nameWithoutAlias
+spring.cloud.zookeeper:
+ dependencies:
+ someAlias:
+ id: someId
+ path: nameWithoutAlias
+ loadBalancerType: ROUND_ROBIN
+ contentTypeTemplate: application/vnd.newsletter.$version+json
+ version: v1
+ headers:
+ header1: value1
+ header2: value2
+ required: false
+ testInstance2:
+ id: someId2
+ path: somePath2
+ loadBalancerType: ROUND_ROBIN
+ contentTypeTemplate: application/vnd.newsletter.$version+json2
+ version: v1
+ required: false
diff --git a/spring-cloud-zookeeper-discovery/src/test/resources/application-loadbalancerclient.yml b/spring-cloud-zookeeper-discovery/src/test/resources/application-loadbalancerclient.yml
new file mode 100644
index 00000000..50ae0842
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/resources/application-loadbalancerclient.yml
@@ -0,0 +1,7 @@
+spring.application.name: loadbalancerclient
+spring.cloud.zookeeper:
+ dependencies:
+ someAlias:
+ id: someId
+ path: testInstance
+ loadBalancerType: STICKY
diff --git a/spring-cloud-zookeeper-discovery/src/test/resources/logback-test.xml b/spring-cloud-zookeeper-discovery/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..f93b3936
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/resources/logback-test.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file