From a33d9ae6443746660ad13ae623f63ab772ae6748 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 13 May 2015 09:52:27 +0200 Subject: [PATCH] Zookeeper registration and Zookeeper dependency setting in properties by 4financeIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Below you can find all people that made the initial solution come to be Kamil SzymaƄski Karol Kalinski Konrad Dobrzynski Marcin Grzejszczak Marcin Zajaczkowski Matus Hennel Michal Chmielarz Jakub Pilimon Tomasz Nurkiewicz Urszula Choromanska Jakub Nabrdalik --- pom.xml | 84 +++++++++-- spring-cloud-zookeeper-bus/pom.xml | 9 ++ .../zookeeper/bus/SimpleRemoteEvent.java | 21 +-- spring-cloud-zookeeper-config/pom.xml | 9 ++ .../config/ZookeeperPropertySource.java | 21 ++- spring-cloud-zookeeper-core/pom.xml | 45 +++++- .../zookeeper/ZookeeperAutoConfiguration.java | 21 ++- .../cloud/zookeeper/ZookeeperProperties.java | 21 ++- .../ZookeeperAutoConfigurationSpec.groovy | 37 +++++ .../ZookeeperAutoConfigurationTests.java | 43 ------ spring-cloud-zookeeper-discovery/pom.xml | 58 ++++++++ .../AddressProviderConfiguration.java | 81 +++++++++++ .../MicroserviceAddressProvider.java | 41 ++++++ .../RibbonZookeeperAutoConfiguration.java | 2 +- .../discovery/ZookeeperDiscoveryClient.java | 28 ++-- ...ZookeeperDiscoveryClientConfiguration.java | 15 +- .../ZookeeperDiscoveryProperties.java | 5 + .../ZookeeperRibbonClientConfiguration.java | 40 ++++-- .../discovery/ZookeeperServerList.java | 17 ++- .../DependenciesPassedCondition.java | 41 ++++++ .../dependency/LoadBalancerType.java | 46 ++++++ .../dependency/ZookeeperDependencies.java | 86 +++++++++++ ...ookeeperDependenciesAutoConfiguration.java | 48 +++++++ .../watcher/DefaultDependencyWatcher.java | 71 ++++++++++ .../DependencyRegistrationHookProvider.java | 39 +++++ .../discovery/watcher/DependencyState.java | 24 ++++ ...DependencyStateChangeListenerRegistry.java | 66 +++++++++ .../discovery/watcher/DependencyWatcher.java | 34 +++++ .../DependencyWatcherAutoConfiguration.java | 70 +++++++++ .../watcher/DependencyWatcherListener.java | 33 +++++ ...ltDependencyPresenceOnStartupVerifier.java | 25 ++++ .../DependencyPresenceOnStartupVerifier.java | 40 ++++++ .../FailOnMissingDependencyChecker.java | 36 +++++ .../presence/LogMissingDependencyChecker.java | 40 ++++++ .../presence/NoInstancesRunningException.java | 25 ++++ .../watcher/presence/PresenceChecker.java | 34 +++++ .../main/resources/META-INF/spring.factories | 4 +- .../discovery/CommonTestConfig.groovy | 49 +++++++ .../discovery/TestServiceRegistrar.groovy | 63 +++++++++ .../discovery/TestServiceRestClient.groovy | 39 +++++ .../discovery/ZookeeperDiscoveryISpec.groovy | 107 ++++++++++++++ ...eeperDiscoveryWithDependenciesISpec.groovy | 83 +++++++++++ ...DefaultDependencyWatcherSpringISpec.groovy | 133 ++++++++++++++++++ ...ndencyPresenceOnStartupVerifierSpec.groovy | 38 +++++ ...ndencyPresenceOnStartupVerifierSpec.groovy | 38 +++++ .../src/test/resources/application-ribbon.yml | 1 + .../test/resources/application-watcher.yml | 20 +++ spring-cloud-zookeeper-sample/pom.xml | 100 ++++++------- 48 files changed, 1862 insertions(+), 169 deletions(-) create mode 100644 spring-cloud-zookeeper-core/src/test/groovy/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationSpec.groovy delete mode 100644 spring-cloud-zookeeper-core/src/test/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationTests.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/AddressProviderConfiguration.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/MicroserviceAddressProvider.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesPassedCondition.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/LoadBalancerType.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependencies.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesAutoConfiguration.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcher.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyRegistrationHookProvider.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyState.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyStateChangeListenerRegistry.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcher.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherAutoConfiguration.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherListener.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/DefaultDependencyPresenceOnStartupVerifier.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/DependencyPresenceOnStartupVerifier.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/FailOnMissingDependencyChecker.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/LogMissingDependencyChecker.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/NoInstancesRunningException.java create mode 100755 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/PresenceChecker.java create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/CommonTestConfig.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRegistrar.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/TestServiceRestClient.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryWithDependenciesISpec.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcherSpringISpec.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DefaultDependencyPresenceOnStartupVerifierSpec.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DependencyPresenceOnStartupVerifierSpec.groovy create mode 100644 spring-cloud-zookeeper-discovery/src/test/resources/application-ribbon.yml create mode 100644 spring-cloud-zookeeper-discovery/src/test/resources/application-watcher.yml diff --git a/pom.xml b/pom.xml index fa7666be..73518c2c 100644 --- a/pom.xml +++ b/pom.xml @@ -39,17 +39,41 @@ - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - 1.7 - 1.7 - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + + + + org.codehaus.gmavenplus + gmavenplus-plugin + 1.4 + + + + testCompile + + + + + + maven-surefire-plugin + 2.6 + + false + + **/*Spec.java + + + + + @@ -161,6 +185,43 @@ 1.12.6 provided + + + org.spockframework + spock-core + ${spock.version} + test + + + org.spockframework + spock-spring + ${spock.version} + test + + + cglib + cglib-nodep + 3.1 + test + + + org.objenesis + objenesis + 2.1 + test + + + org.hamcrest + hamcrest-core + 1.3 + test + + + org.codehaus.groovy + groovy-all + 2.4.3 + test + @@ -171,6 +232,7 @@ 1.0.1.BUILD-SNAPSHOT 2.7.0 2.0-RC9 + 1.0-groovy-2.4 diff --git a/spring-cloud-zookeeper-bus/pom.xml b/spring-cloud-zookeeper-bus/pom.xml index 1b0fe0a6..df3ad807 100644 --- a/spring-cloud-zookeeper-bus/pom.xml +++ b/spring-cloud-zookeeper-bus/pom.xml @@ -16,6 +16,15 @@ .. + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.springframework.cloud diff --git a/spring-cloud-zookeeper-bus/src/main/java/org/springframework/cloud/zookeeper/bus/SimpleRemoteEvent.java b/spring-cloud-zookeeper-bus/src/main/java/org/springframework/cloud/zookeeper/bus/SimpleRemoteEvent.java index 3d567176..ddc8a1fc 100644 --- a/spring-cloud-zookeeper-bus/src/main/java/org/springframework/cloud/zookeeper/bus/SimpleRemoteEvent.java +++ b/spring-cloud-zookeeper-bus/src/main/java/org/springframework/cloud/zookeeper/bus/SimpleRemoteEvent.java @@ -11,17 +11,18 @@ import org.springframework.cloud.bus.event.RemoteApplicationEvent; @Data public class SimpleRemoteEvent extends RemoteApplicationEvent { - private String message; + private String message; - private SimpleRemoteEvent(){} + private SimpleRemoteEvent() { + } - public SimpleRemoteEvent(Object source, String originService, String destinationService, String message) { - super(source, originService, destinationService); - this.message = message; - } + public SimpleRemoteEvent(Object source, String originService, String destinationService, String message) { + super(source, originService, destinationService); + this.message = message; + } - public SimpleRemoteEvent(Object source, String originService, String message) { - super(source, originService); - this.message = message; - } + public SimpleRemoteEvent(Object source, String originService, String message) { + super(source, originService); + this.message = message; + } } diff --git a/spring-cloud-zookeeper-config/pom.xml b/spring-cloud-zookeeper-config/pom.xml index 3ba979e0..a32f8a9a 100644 --- a/spring-cloud-zookeeper-config/pom.xml +++ b/spring-cloud-zookeeper-config/pom.xml @@ -16,6 +16,15 @@ .. + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.springframework.cloud 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/config/ZookeeperPropertySource.java index 9ab7a72b..0dfd46ce 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/config/ZookeeperPropertySource.java @@ -42,16 +42,14 @@ public class ZookeeperPropertySource extends EnumerablePropertySource entry : children.entrySet()) { ChildData child = entry.getValue(); - if (child.getData()==null || child.getData().length == 0) { + if (child.getData() == null || child.getData().length == 0) { findKeys(keys, child.getPath()); - } - else { + } else { keys.add(child.getPath().replace(context + "/", "").replace('/', '.')); } } diff --git a/spring-cloud-zookeeper-core/pom.xml b/spring-cloud-zookeeper-core/pom.xml index 1f2f3119..0a973323 100644 --- a/spring-cloud-zookeeper-core/pom.xml +++ b/spring-cloud-zookeeper-core/pom.xml @@ -16,6 +16,19 @@ .. + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + org.springframework.boot @@ -49,7 +62,37 @@ spring-boot-starter-test test - + + + org.spockframework + spock-core + test + + + org.spockframework + spock-spring + test + + + cglib + cglib-nodep + test + + + org.objenesis + objenesis + test + + + org.hamcrest + hamcrest-core + test + + + org.codehaus.groovy + groovy-all + test + diff --git a/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfiguration.java b/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfiguration.java index c2d8596e..10140cf1 100644 --- a/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfiguration.java +++ b/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfiguration.java @@ -1,21 +1,26 @@ package org.springframework.cloud.zookeeper; -import javax.annotation.PreDestroy; - +import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.annotation.PreDestroy; + /** * @author Spencer Gibb */ @Configuration @EnableConfigurationProperties public class ZookeeperAutoConfiguration { + + private @Autowired RetryPolicy retryPolicy; + @Bean @ConditionalOnMissingBean public ZookeeperProperties zookeeperProperties() { @@ -26,9 +31,7 @@ public class ZookeeperAutoConfiguration { @ConditionalOnMissingBean public CuratorFramework curatorFramework() { CuratorFramework curator = CuratorFrameworkFactory.builder() - // TODO: configurable retry policy - .retryPolicy(new ExponentialBackoffRetry(1000, 3)) - // .retryPolicy(new RetryOneTime(100)) + .retryPolicy(retryPolicy) // TODO: support ensembleProvider via ExhibitorEnsembleProvider // .ensembleProvider(new ExhibitorEnsembleProvider()) .connectString(zookeeperProperties().getConnectString()).build(); @@ -41,6 +44,14 @@ public class ZookeeperAutoConfiguration { curatorFramework().close(); } + @Bean + @ConditionalOnMissingBean + public RetryPolicy exponentialBackoffRetry() { + return new ExponentialBackoffRetry(zookeeperProperties().getBaseSleepTimeMs(), + zookeeperProperties().getMaxRetries(), + zookeeperProperties().getMaxSleepMs()); + } + @Bean @ConditionalOnMissingBean public ZookeeperEndpoint zookeeperEndpoint() { diff --git a/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperProperties.java b/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperProperties.java index 3010dfbe..150a2ee6 100644 --- a/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperProperties.java +++ b/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperProperties.java @@ -1,11 +1,10 @@ package org.springframework.cloud.zookeeper; -import javax.validation.constraints.NotNull; - import lombok.Data; - import org.springframework.boot.context.properties.ConfigurationProperties; +import javax.validation.constraints.NotNull; + /** * @author Spencer Gibb */ @@ -16,4 +15,20 @@ public class ZookeeperProperties { private String connectString = "localhost:2181"; private boolean enabled = true; + + /** + * @param baseSleepTimeMs initial amount of time to wait between retries + */ + private Integer baseSleepTimeMs = 50; + + /** + * @param maxRetries max number of times to retry + */ + private Integer maxRetries = 50; + + /** + * @param maxSleepMs max time in ms to sleep on each retry + */ + private Integer maxSleepMs = 500; + } diff --git a/spring-cloud-zookeeper-core/src/test/groovy/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationSpec.groovy b/spring-cloud-zookeeper-core/src/test/groovy/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationSpec.groovy new file mode 100644 index 00000000..e210937d --- /dev/null +++ b/spring-cloud-zookeeper-core/src/test/groovy/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationSpec.groovy @@ -0,0 +1,37 @@ +package org.springframework.cloud.zookeeper + +import org.apache.curator.framework.CuratorFramework +import org.apache.curator.test.TestingServer +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Bean +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +/** + * @author Spencer Gibb + */ +@ContextConfiguration(classes = [ TestConfig, ZookeeperAutoConfiguration ]) +class ZookeeperAutoConfigurationSpec extends Specification { + + @Autowired(required = false) + CuratorFramework curator + + def 'should successfully inject Curator as a Spring bean'() { + expect: + curator != null + } + + static class TestConfig { + @Bean + ZookeeperProperties zookeeperProperties() throws Exception { + ZookeeperProperties properties = new ZookeeperProperties() + properties.connectString = testingServer().connectString + return properties + } + + @Bean + TestingServer testingServer() throws Exception { + return new TestingServer() + } + } +} diff --git a/spring-cloud-zookeeper-core/src/test/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationTests.java b/spring-cloud-zookeeper-core/src/test/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationTests.java deleted file mode 100644 index a7f754cf..00000000 --- a/spring-cloud-zookeeper-core/src/test/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfigurationTests.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.springframework.cloud.zookeeper; - -import static org.junit.Assert.assertNotNull; - -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.test.TestingServer; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Spencer Gibb - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { ZookeeperAutoConfigurationTests.TestConfig.class, - ZookeeperAutoConfiguration.class }) -public class ZookeeperAutoConfigurationTests { - - @Autowired(required = false) - CuratorFramework curator; - - @Test - public void testZookeeperFramework() { - assertNotNull("curator is null", curator); - } - - static class TestConfig { - @Bean - public ZookeeperProperties zookeeperProperties() throws Exception { - ZookeeperProperties properties = new ZookeeperProperties(); - properties.setConnectString(testingServer().getConnectString()); - return properties; - } - - @Bean - public TestingServer testingServer() throws Exception { - return new TestingServer(); - } - } -} diff --git a/spring-cloud-zookeeper-discovery/pom.xml b/spring-cloud-zookeeper-discovery/pom.xml index f11b53a2..33c1dfc1 100644 --- a/spring-cloud-zookeeper-discovery/pom.xml +++ b/spring-cloud-zookeeper-discovery/pom.xml @@ -16,6 +16,22 @@ .. + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + maven-surefire-plugin + + + + org.springframework.cloud @@ -52,6 +68,48 @@ spring-boot-starter-test test + + org.apache.curator + curator-test + test + + + + org.spockframework + spock-core + test + + + org.spockframework + spock-spring + test + + + cglib + cglib-nodep + test + + + org.objenesis + objenesis + test + + + org.hamcrest + hamcrest-core + test + + + org.codehaus.groovy + groovy-all + test + + + com.github.tomakehurst + wiremock + test + 1.53 + diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/AddressProviderConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/AddressProviderConfiguration.java new file mode 100644 index 00000000..56efb673 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/AddressProviderConfiguration.java @@ -0,0 +1,81 @@ +/* + * 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; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Enumeration; + +/** + * Configuration that registers a bean related to microservice's address and port providing. + * + * @see MicroserviceAddressProvider + * + * @author Marcin Grzejszczak, 4financeIT + * @author Tomasz Dziurko, 4financeIT + */ +@Configuration +public class AddressProviderConfiguration { + + @Autowired + private Environment environment; + + @Bean + MicroserviceAddressProvider microserviceAddressProvider() { + String microserviceHost = environment.getProperty("microservice.host", getIpAddress()); + Integer microservicePort = Integer.valueOf(environment.getProperty("server.port", "8080")); + return new MicroserviceAddressProvider(microserviceHost, microservicePort); + } + + /** + * Return a non loopback IPv4 address for the machine running this process. + * If the machine has multiple network interfaces, the IP address for the + * first interface returned by {@link java.net.NetworkInterface#getNetworkInterfaces} + * is returned. + * + * @return non loopback IPv4 address for the machine running this process + * @see java.net.NetworkInterface#getNetworkInterfaces + * @see java.net.NetworkInterface#getInetAddresses + */ + public static String getIpAddress() { + try { + for (Enumeration enumNic = NetworkInterface.getNetworkInterfaces(); + enumNic.hasMoreElements(); ) { + NetworkInterface ifc = enumNic.nextElement(); + if (ifc.isUp()) { + for (Enumeration enumAddr = ifc.getInetAddresses(); + enumAddr.hasMoreElements(); ) { + InetAddress address = enumAddr.nextElement(); + if (address instanceof Inet4Address && !address.isLoopbackAddress()) { + return address.getHostAddress(); + } + } + } + } + } catch (IOException e) { + // ignore + } + return "unknown"; + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/MicroserviceAddressProvider.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/MicroserviceAddressProvider.java new file mode 100644 index 00000000..897bbc78 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/MicroserviceAddressProvider.java @@ -0,0 +1,41 @@ +/* + * 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; + +/** + * Holder for microservice's host and port + * + * @author Marcin Grzejszczak, 4financeIT + * @author Adam Chudzik, 4financeIT + * + */ +public class MicroserviceAddressProvider { + private final String host; + private final int port; + + public MicroserviceAddressProvider(String microserviceHost, int microservicePort) { + this.host = microserviceHost; + this.port = microservicePort; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } +} \ No newline at end of file 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 f45f1c19..0861194e 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 @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClient.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClient.java index 30249953..c32efb6e 100644 --- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClient.java +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClient.java @@ -1,22 +1,23 @@ package org.springframework.cloud.zookeeper.discovery; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - import lombok.SneakyThrows; - import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies; import org.springframework.context.ApplicationContext; -import static org.springframework.util.ReflectionUtils.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.springframework.util.ReflectionUtils.rethrowRuntimeException; /** * @author Spencer Gibb + * @author Marcin Grzejszczak, 4financeIT */ public class ZookeeperDiscoveryClient implements DiscoveryClient { @@ -29,6 +30,9 @@ public class ZookeeperDiscoveryClient implements DiscoveryClient { @Autowired ServiceDiscovery discovery; + @Autowired(required = false) + ZookeeperDependencies zookeeperDependencies; + @Override public String description() { return "Spring Cloud Zookeeper Discovery Client"; @@ -59,18 +63,26 @@ public class ZookeeperDiscoveryClient implements DiscoveryClient { @SneakyThrows public List getInstances( final String serviceId) { + String serviceIdToQuery = getServiceIdToQuery(serviceId); Collection> zkInstances = discovery - .queryForInstances(serviceId); + .queryForInstances(serviceIdToQuery); ArrayList instances = new ArrayList<>(); for (ServiceInstance instance : zkInstances) { - instances.add(createServiceInstance(serviceId, instance)); + instances.add(createServiceInstance(serviceIdToQuery, instance)); } return instances; } + private String getServiceIdToQuery(String serviceId) { + if (zookeeperDependencies != null && zookeeperDependencies.hasDependencies()) { + return zookeeperDependencies.getPathForAlias(serviceId); + } + return serviceId; + } + @Override public List getServices() { ArrayList services = null; diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientConfiguration.java index 08e35d15..e4a82e53 100644 --- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientConfiguration.java +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientConfiguration.java @@ -8,6 +8,7 @@ import org.apache.curator.x.discovery.UriSpec; import org.apache.curator.x.discovery.details.InstanceSerializer; import org.apache.curator.x.discovery.details.JsonInstanceSerializer; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -19,6 +20,7 @@ import org.springframework.core.env.Environment; * @author Spencer Gibb */ @Configuration +@Import(AddressProviderConfiguration.class) @EnableConfigurationProperties public class ZookeeperDiscoveryClientConfiguration { @Autowired @@ -40,13 +42,14 @@ public class ZookeeperDiscoveryClientConfiguration { } @Bean - public ServiceInstance serviceInstance() throws Exception { + @ConditionalOnMissingBean + public ServiceInstance serviceInstance(MicroserviceAddressProvider microserviceAddressProvider) throws Exception { Environment environment = context.getEnvironment(); - Integer port = new Integer(environment.getProperty("server.port", "8080")); UriSpec uriSpec = new UriSpec(zookeeperDiscoveryProperties().getUriSpec()); return ServiceInstance. builder() .name(environment.getProperty("spring.application.name")) - .payload(new ZookeeperInstance(context.getId())).port(port) + .payload(new ZookeeperInstance(context.getId())).port(microserviceAddressProvider.getPort()) + .address(microserviceAddressProvider.getHost()) .uriSpec(uriSpec).build(); } @@ -56,15 +59,17 @@ public class ZookeeperDiscoveryClientConfiguration { } @Bean - public ServiceDiscovery serviceDiscovery(CuratorFramework curator) + @ConditionalOnMissingBean + public ServiceDiscovery serviceDiscovery(CuratorFramework curator, ServiceInstance serviceInstance) throws Exception { return ServiceDiscoveryBuilder.builder(ZookeeperInstance.class).client(curator) .basePath(zookeeperDiscoveryProperties().getRoot()) - .serializer(instanceSerializer()).thisInstance(serviceInstance()).build(); + .serializer(instanceSerializer()).thisInstance(serviceInstance).build(); } @Bean public ZookeeperDiscoveryHealthIndicator zookeeperDiscoveryHealthIndicator() { return new ZookeeperDiscoveryHealthIndicator(); } + } diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryProperties.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryProperties.java index f79c2216..8f466386 100644 --- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryProperties.java +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryProperties.java @@ -15,4 +15,9 @@ public class ZookeeperDiscoveryProperties { private String root = "/services"; private String uriSpec = "{scheme}://{address}:{port}"; + + /** + * @param realm allows you to register a service under specified realm + */ + private String realm; } 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 df398df8..8f8e78fb 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 @@ -5,7 +5,7 @@ * 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 + * 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, @@ -16,23 +16,26 @@ package org.springframework.cloud.zookeeper.discovery; -import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses; -import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity; - -import javax.annotation.PostConstruct; - -import org.apache.curator.x.discovery.ServiceDiscovery; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - 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.ServerList; +import org.apache.curator.x.discovery.ServiceDiscovery; +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.ZookeeperDependencies; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; + +import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses; +import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity; /** * Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as: @@ -41,6 +44,7 @@ import com.netflix.loadbalancer.ServerList; * * @author Spencer Gibb * @author Dave Syer + * @author Marcin Grzejszczak, 4financeIT */ @Configuration public class ZookeeperRibbonClientConfiguration { @@ -54,6 +58,16 @@ public class ZookeeperRibbonClientConfiguration { public ZookeeperRibbonClientConfiguration() { } + @Bean + @ConditionalOnMissingBean + @Conditional(DependenciesPassedCondition.class) + @ConditionalOnProperty(value = "zookeeper.dependencies.enabled", matchIfMissing = true) + public ServerList ribbonServerListFromDependencies(IClientConfig config, ZookeeperDependencies zookeeperDependencies) { + ZookeeperServerList serverList = new ZookeeperServerList(serviceDiscovery); + serverList.initFromDependencies(config, zookeeperDependencies); + return serverList; + } + @Bean @ConditionalOnMissingBean public ServerList ribbonServerList(IClientConfig config) { diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperServerList.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperServerList.java index bb0fb9cb..d44c7730 100644 --- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperServerList.java +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/ZookeeperServerList.java @@ -1,20 +1,21 @@ package org.springframework.cloud.zookeeper.discovery; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.AbstractServerList; +import org.apache.curator.x.discovery.ServiceDiscovery; +import org.apache.curator.x.discovery.ServiceInstance; +import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import org.apache.curator.x.discovery.ServiceDiscovery; -import org.apache.curator.x.discovery.ServiceInstance; - -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.AbstractServerList; - import static org.springframework.util.ReflectionUtils.rethrowRuntimeException; /** * @author Spencer Gibb + * @author Marcin Grzejszczak, 4financeIT */ public class ZookeeperServerList extends AbstractServerList { @@ -30,6 +31,10 @@ public class ZookeeperServerList extends AbstractServerList { this.serviceId = clientConfig.getClientName(); } + public void initFromDependencies(IClientConfig clientConfig, ZookeeperDependencies zookeeperDependencies) { + this.serviceId = zookeeperDependencies.getPathForAlias(clientConfig.getClientName()); + } + @Override public List getInitialListOfServers() { return getServers(); diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesPassedCondition.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesPassedCondition.java new file mode 100644 index 00000000..f4fc624d --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependenciesPassedCondition.java @@ -0,0 +1,41 @@ +/* + * 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.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +import java.util.Map; + +/** + * @author Marcin Grzejszczak, 4financeIT + */ +public class DependenciesPassedCondition extends SpringBootCondition { + + private static final String ZOOKEEPER_DEPENDENCIES_PROP = "zookeeper.dependencies"; + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + Map subProperties = new RelaxedPropertyResolver(context.getEnvironment()).getSubProperties(ZOOKEEPER_DEPENDENCIES_PROP); + return subProperties.isEmpty() ? + ConditionOutcome.noMatch("No dependencies have been passed for the service") : + ConditionOutcome.match(); + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/LoadBalancerType.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/LoadBalancerType.java new file mode 100644 index 00000000..139f3e80 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/LoadBalancerType.java @@ -0,0 +1,46 @@ +/* + * 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.commons.collections.CollectionUtils; +import org.apache.commons.collections.Predicate; + +import java.util.Arrays; + +import static org.apache.commons.lang.StringUtils.EMPTY; +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; + +/** + * @author Marcin Grzejszczak, 4financeIT + */ +public enum LoadBalancerType { + STICKY, RANDOM, ROUND_ROBIN; + + public static LoadBalancerType fromName(final String strategyName) { + LoadBalancerType loadBalancerType = (LoadBalancerType) CollectionUtils.find(Arrays.asList(values()), new Predicate() { + @Override + public boolean evaluate(Object o) { + LoadBalancerType input = (LoadBalancerType) o; + return input.name().equals(defaultIfEmpty(strategyName, EMPTY).toUpperCase()); + } + }); + if (loadBalancerType == null) { + return ROUND_ROBIN; + } + return loadBalancerType; + } + +} 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 new file mode 100644 index 00000000..763a4991 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependencies.java @@ -0,0 +1,86 @@ +/* + * 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 lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Marcin Grzejszczak, 4financeIT + */ +@Data +@ConfigurationProperties("zookeeper") +public class ZookeeperDependencies { + + private String prefix = ""; + + private Map dependencies = new LinkedHashMap<>(); + + @PostConstruct + public void init() { + for (Map.Entry entry : this.dependencies.entrySet()) { + ZookeeperDependency value = entry.getValue(); + if (StringUtils.hasText(prefix)) { + value.path = prefix + value.path; + } + } + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class ZookeeperDependency { + + private String id; + + private String path; + + private LoadBalancerType loadBalancerType; + + private String contentTypeTemplate; + + private String version; + + private Map headers; + + private boolean required; + } + + public Collection getDependencyConfigurations() { + return dependencies.values(); + } + + public boolean hasDependencies() { + return !dependencies.isEmpty(); + } + + public String getPathForAlias(final String alias) { + for (Map.Entry zookeeperDependencyEntry : dependencies.entrySet()) { + if (zookeeperDependencyEntry.getKey().equals(alias)) { + return zookeeperDependencyEntry.getValue().getPath(); + } + } + return ""; + } +} 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 new file mode 100644 index 00000000..efeec4d7 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/ZookeeperDependenciesAutoConfiguration.java @@ -0,0 +1,48 @@ +/* + * 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.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; + +/** + * Provides autoconfiguration for Zookeeper dependency set up in properties. + * + * @see ZookeeperDependencies + * + * @author Marcin Grzejszczak, 4financeIT + * + */ +@Configuration +@EnableConfigurationProperties +@Conditional(DependenciesPassedCondition.class) +@ConditionalOnProperty(value = "zookeeper.dependencies.enabled", matchIfMissing = true) +@AutoConfigureAfter(ZookeeperAutoConfiguration.class) +public class ZookeeperDependenciesAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public ZookeeperDependencies zookeeperDependencies() { + return new ZookeeperDependencies(); + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcher.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcher.java new file mode 100755 index 00000000..d3ef8f24 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcher.java @@ -0,0 +1,71 @@ +/* + * 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.watcher; + +import org.apache.curator.x.discovery.ServiceCache; +import org.apache.curator.x.discovery.ServiceDiscovery; +import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies; +import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies.ZookeeperDependency; +import org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Marcin Grzejszczak, 4financeIT + * @author Michal Chmielarz, 4financeIT + */ +public class DefaultDependencyWatcher implements DependencyRegistrationHookProvider { + + private final ServiceDiscovery serviceDiscovery; + private final Map dependencyRegistry = new HashMap<>(); + private final List listeners; + private final DependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier; + private final ZookeeperDependencies zookeeperDependencies; + + public DefaultDependencyWatcher(ServiceDiscovery serviceDiscovery, + DependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier, + List dependencyWatcherListeners, + ZookeeperDependencies zookeeperDependencies) { + this.serviceDiscovery = serviceDiscovery; + this.dependencyPresenceOnStartupVerifier = dependencyPresenceOnStartupVerifier; + this.listeners = dependencyWatcherListeners; + this.zookeeperDependencies = zookeeperDependencies; + } + + @Override + @SuppressWarnings("unchecked") + public void registerDependencyRegistrationHooks() throws Exception { + for (ZookeeperDependency zookeeperDependency : zookeeperDependencies.getDependencyConfigurations()) { + String dependencyPath = zookeeperDependency.getPath(); + ServiceCache serviceCache = serviceDiscovery.serviceCacheBuilder().name(dependencyPath).build(); + serviceCache.start(); + dependencyPresenceOnStartupVerifier.verifyDependencyPresence(dependencyPath, serviceCache, zookeeperDependency.isRequired()); + dependencyRegistry.put(dependencyPath, serviceCache); + serviceCache.addListener(new DependencyStateChangeListenerRegistry(listeners, dependencyPath, serviceCache)); + } + } + + @Override + public void clearDependencyRegistrationHooks() throws IOException { + for (ServiceCache cache : dependencyRegistry.values()) { + cache.close(); + } + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyRegistrationHookProvider.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyRegistrationHookProvider.java new file mode 100644 index 00000000..1b60fbc0 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyRegistrationHookProvider.java @@ -0,0 +1,39 @@ +/* + * 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.watcher; + +import java.io.IOException; + +/** + * @author listeners; + private final String dependencyName; + private final ServiceCache serviceCache; + + public DependencyStateChangeListenerRegistry(List listeners, String dependencyName, ServiceCache serviceCache) { + this.listeners = listeners; + this.dependencyName = dependencyName; + this.serviceCache = serviceCache; + } + + @Override + public void cacheChanged() { + DependencyState state = serviceCache.getInstances().isEmpty() ? DependencyState.DISCONNECTED : DependencyState.CONNECTED; + logCurrentState(state); + informListeners(state); + } + + private void logCurrentState(DependencyState dependencyState) { + log.info("Service cache state change for '{}' instances, current service state: {}", dependencyName, dependencyState); + } + + private void informListeners(DependencyState state) { + for (DependencyWatcherListener listener : listeners) { + listener.stateChanged(dependencyName, state); + } + } + + @Override + public void stateChanged(CuratorFramework client, ConnectionState newState) { + // todo do something or ignore for what is worth + } +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcher.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcher.java new file mode 100644 index 00000000..6284cd9e --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcher.java @@ -0,0 +1,34 @@ +package org.springframework.cloud.zookeeper.discovery.watcher; + +import java.io.IOException; + +public interface DependencyWatcher { + + /** + * Register hooks upon dependencies registration + * + * @throws Exception + */ + void registerDependencyRegistrationHooks() throws Exception; + + /** + * Unregister hooks upon dependencies registration + * + * @throws IOException + */ + void clearDependencyRegistrationHooks() throws IOException; + + /** + * Register a listener for a dependency + * + * @param listener + */ + void registerDependencyStateChangeListener(DependencyWatcherListener listener); + + /** + * Unregister a listener for a dependency + * + * @param listener + */ + void clearDependencyStateChangeListener(DependencyWatcherListener listener); +} 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 new file mode 100644 index 00000000..a0b62a1c --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherAutoConfiguration.java @@ -0,0 +1,70 @@ +/* + * 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.watcher; + +import org.apache.curator.x.discovery.ServiceDiscovery; +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.dependency.DependenciesPassedCondition; +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. + * + * @see ZookeeperDependencies + * + * @author Marcin Grzejszczak, 4financeIT + */ +@Configuration +@EnableConfigurationProperties +@Conditional(DependenciesPassedCondition.class) +@ConditionalOnProperty(value = "zookeeper.dependencies.enabled", matchIfMissing = true) +@AutoConfigureAfter(ZookeeperDependenciesAutoConfiguration.class) +public class DependencyWatcherAutoConfiguration { + + @Autowired(required = false) + private List dependencyWatcherListeners = new ArrayList<>(); + + @Bean + @ConditionalOnMissingBean + public DependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier() { + return new DefaultDependencyPresenceOnStartupVerifier(); + } + + @Bean(initMethod = "registerDependencyRegistrationHooks", destroyMethod = "clearDependencyRegistrationHooks") + @ConditionalOnMissingBean + public DependencyRegistrationHookProvider dependencyWatcher(ServiceDiscovery serviceDiscovery, + DependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier, + ZookeeperDependencies zookeeperDependencies) { + return new DefaultDependencyWatcher(serviceDiscovery, + dependencyPresenceOnStartupVerifier, + dependencyWatcherListeners, + zookeeperDependencies); + } +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherListener.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherListener.java new file mode 100755 index 00000000..a24ae717 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/DependencyWatcherListener.java @@ -0,0 +1,33 @@ +/* + * 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.watcher; + +/** + * Performs logic upon change of state of a dependency {@see DependencyState} + * in the service discovery system. + * + * @author serviceInstances) { + if (serviceInstances.isEmpty()) { + throw new NoInstancesRunningException(dependencyName); + } + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/LogMissingDependencyChecker.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/LogMissingDependencyChecker.java new file mode 100755 index 00000000..1808fbcd --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/LogMissingDependencyChecker.java @@ -0,0 +1,40 @@ +/* + * 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.watcher.presence; + +import org.apache.curator.x.discovery.ServiceInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.invoke.MethodHandles; +import java.util.List; + +/** + * @author Marcin Grzejszczak, 4financeIT + * @author Tomasz Dziurko, 4financeIT + */ +public class LogMissingDependencyChecker implements PresenceChecker { + + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Override + public void checkPresence(String dependencyName, List serviceInstances) { + if (serviceInstances.isEmpty()) { + log.warn("Microservice dependency with name [" + dependencyName + "] is missing."); + } + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/NoInstancesRunningException.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/NoInstancesRunningException.java new file mode 100755 index 00000000..5810b803 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/watcher/presence/NoInstancesRunningException.java @@ -0,0 +1,25 @@ +/* + * 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.watcher.presence; + +/** + * @author instances = discoveryClient.getInstances(TEST_INSTANCE_NAME) + ServiceInstance instance = instances.first() + expect: + 'pong' == testRibbonClient.pingOnUrl("${instance.host}:${instance.port}") + } + + def 'should properly find local instance'() { + expect: + AddressProviderConfiguration.ipAddress == discoveryClient.localServiceInstance.host + } + + @Configuration + @EnableAutoConfiguration + @Import(CommonTestConfig) + @EnableDiscoveryClient + static class Config { + + @Bean + TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate, + @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/ZookeeperDiscoveryWithDependenciesISpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryWithDependenciesISpec.groovy new file mode 100644 index 00000000..0feab1c0 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryWithDependenciesISpec.groovy @@ -0,0 +1,83 @@ +/* + * 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 + +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 +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.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import +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('watcher') +class ZookeeperDiscoveryWithDependenciesISpec extends Specification { + + @Autowired TestRibbonClient testRibbonClient + @Autowired WireMockServer wiremockServer + @Autowired DiscoveryClient discoveryClient + WireMock wireMock + + def setup() { + wireMock = new WireMock('localhost', wiremockServer.port()) + wireMock.register(get(urlEqualTo('/ping')).willReturn(aResponse().withBody('pong'))) + } + + def 'should find a collaborator via Ribbon by using its alias from dependencies'() { + expect: + 'pong' == testRibbonClient.pingService('someAlias') + } + + def 'should find a collaborator via discovery client'() { + given: + List instances = discoveryClient.getInstances('someAlias') + ServiceInstance instance = instances.first() + expect: + 'pong' == testRibbonClient.pingOnUrl("${instance.host}:${instance.port}") + } + + @Configuration + @EnableAutoConfiguration + @Import(CommonTestConfig) + @EnableDiscoveryClient + static class Config { + + @Bean + TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate) { + return new TestRibbonClient(restTemplate) + } + + } + + static class TestRibbonClient extends TestServiceRestClient { + + TestRibbonClient(RestTemplate restTemplate) { + super(restTemplate) + } + } +} \ 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 new file mode 100644 index 00000000..3245501a --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/DefaultDependencyWatcherSpringISpec.groovy @@ -0,0 +1,133 @@ +/* + * 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.watcher + +import org.apache.curator.framework.CuratorFramework +import org.apache.curator.framework.CuratorFrameworkFactory +import org.apache.curator.retry.ExponentialBackoffRetry +import org.apache.curator.test.TestingServer +import org.apache.curator.x.discovery.* +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.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.support.PropertySourcesPlaceholderConfigurer +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('watcher') +class DefaultDependencyWatcherSpringISpec extends Specification { + + @Autowired AssertableDependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier + @Autowired AssertableDependencyWatcherListener dependencyWatcherListener + @Autowired ServiceDiscovery serviceDiscovery + @Autowired ServiceInstance serviceInstance + + def 'should verify that presence of a dependency has been checked'() { + expect: + dependencyPresenceOnStartupVerifier.startupPresenceVerified + } + + def 'should verify that dependency watcher listener is successfully registered and operational'() { + when: + serviceDiscovery.unregisterService(serviceInstance) + then: + new PollingConditions().eventually { + dependencyWatcherListener.dependencyState == DependencyState.DISCONNECTED + } + } + + @Configuration + @EnableAutoConfiguration + static class Config { + + @Bean + static PropertySourcesPlaceholderConfigurer propertiesConfigurer() { + return new PropertySourcesPlaceholderConfigurer() + } + + @Bean(destroyMethod = 'close') + TestingServer testingServer() { + return new TestingServer(SocketUtils.findAvailableTcpPort()) + } + + @Bean + ServiceInstance serviceInstance() { + return ServiceInstance.builder().uriSpec(new UriSpec("{scheme}://{address}:{port}/")) + .address('anyUrl') + .port(10) + .name('testInstance') + .build() + } + + @Bean(initMethod = 'start', destroyMethod = 'close') + ServiceDiscovery serviceDiscovery() { + return ServiceDiscoveryBuilder + .builder(Void) + .basePath('/') + .client(curatorFramework()) + .thisInstance(serviceInstance()) + .build() + } + + @Bean(initMethod = 'start', destroyMethod = 'close') + CuratorFramework curatorFramework() { + return CuratorFrameworkFactory.newClient(testingServer().connectString, new ExponentialBackoffRetry(20, 20, 500)) + } + + @Bean + DependencyWatcherListener dependencyWatcherListener() { + return new AssertableDependencyWatcherListener() + } + + @Bean + DependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier() { + return new AssertableDependencyPresenceOnStartupVerifier() + } + + } + + static class AssertableDependencyWatcherListener implements DependencyWatcherListener { + + DependencyState dependencyState = DependencyState.CONNECTED + + @Override + void stateChanged(String dependencyName, DependencyState newState) { + dependencyState = newState + } + } + + static class AssertableDependencyPresenceOnStartupVerifier extends DependencyPresenceOnStartupVerifier { + + boolean startupPresenceVerified = false + + AssertableDependencyPresenceOnStartupVerifier() { + super(new LogMissingDependencyChecker()) + } + + @Override + void verifyDependencyPresence(String dependencyName, ServiceCache serviceCache, boolean required) { + startupPresenceVerified = true + } + } +} diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DefaultDependencyPresenceOnStartupVerifierSpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DefaultDependencyPresenceOnStartupVerifierSpec.groovy new file mode 100644 index 00000000..95ae1253 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DefaultDependencyPresenceOnStartupVerifierSpec.groovy @@ -0,0 +1,38 @@ +/* + * 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.watcher.presence + +import org.apache.curator.x.discovery.ServiceCache +import org.codehaus.groovy.runtime.StackTraceUtils +import spock.lang.Specification + +class DefaultDependencyPresenceOnStartupVerifierSpec extends Specification { + + private static final String SERVICE_NAME = 'service01' + + def 'should throw exception if obligatory dependencies are missing'() { + given: + DefaultDependencyPresenceOnStartupVerifier dependencyVerifier = new DefaultDependencyPresenceOnStartupVerifier() + ServiceCache serviceCache = Mock() + serviceCache.instances >> [] + when: + dependencyVerifier.verifyDependencyPresence(SERVICE_NAME, serviceCache, true) + then: + Throwable thrown = thrown(Throwable) + StackTraceUtils.extractRootCause(thrown).class == NoInstancesRunningException + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DependencyPresenceOnStartupVerifierSpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DependencyPresenceOnStartupVerifierSpec.groovy new file mode 100644 index 00000000..f8ed3809 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/watcher/presence/DependencyPresenceOnStartupVerifierSpec.groovy @@ -0,0 +1,38 @@ +/* + * 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.watcher.presence + +import org.apache.curator.x.discovery.ServiceCache +import spock.lang.Specification + +class DependencyPresenceOnStartupVerifierSpec extends Specification { + + private static final String SERVICE_NAME = 'service01' + + def 'should check optional dependency using optional dependency checker'() { + given: + PresenceChecker optionalDependencyChecker = Mock() + DependencyPresenceOnStartupVerifier dependencyVerifier = new DependencyPresenceOnStartupVerifier(optionalDependencyChecker) { + } + ServiceCache serviceCache = Mock() + serviceCache.instances >> [] + when: + dependencyVerifier.verifyDependencyPresence(SERVICE_NAME, serviceCache, false) + then: + 1 * optionalDependencyChecker.checkPresence(SERVICE_NAME, serviceCache.instances) + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/test/resources/application-ribbon.yml b/spring-cloud-zookeeper-discovery/src/test/resources/application-ribbon.yml new file mode 100644 index 00000000..73852a66 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/resources/application-ribbon.yml @@ -0,0 +1 @@ +spring.application.name: ribbonApp \ No newline at end of file diff --git a/spring-cloud-zookeeper-discovery/src/test/resources/application-watcher.yml b/spring-cloud-zookeeper-discovery/src/test/resources/application-watcher.yml new file mode 100644 index 00000000..b28092a1 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/resources/application-watcher.yml @@ -0,0 +1,20 @@ +spring.application.name: someName +zookeeper: + dependencies: + someAlias: + id: someId + path: testInstance + 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-sample/pom.xml b/spring-cloud-zookeeper-sample/pom.xml index 6f1c6658..40fb7488 100644 --- a/spring-cloud-zookeeper-sample/pom.xml +++ b/spring-cloud-zookeeper-sample/pom.xml @@ -1,58 +1,58 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - spring-cloud-zookeeper-sample - jar - Spring Cloud Zookeeper Sample - Spring Cloud Zookeeper Sample + spring-cloud-zookeeper-sample + jar + Spring Cloud Zookeeper Sample + Spring Cloud Zookeeper Sample - - org.springframework.cloud - spring-cloud-zookeeper - 1.0.0.BUILD-SNAPSHOT - .. - + + org.springframework.cloud + spring-cloud-zookeeper + 1.0.0.BUILD-SNAPSHOT + .. + - - - - - maven-deploy-plugin - - true - - - - + + + + + maven-deploy-plugin + + true + + + + - - - org.springframework.boot - spring-boot-starter-web - test - - - org.springframework.cloud - spring-cloud-zookeeper-config - test - - - org.springframework.cloud - spring-cloud-zookeeper-discovery - test - - - org.springframework.cloud - spring-cloud-zookeeper-bus - test - - - org.projectlombok - lombok - test - - + + + org.springframework.boot + spring-boot-starter-web + test + + + org.springframework.cloud + spring-cloud-zookeeper-config + test + + + org.springframework.cloud + spring-cloud-zookeeper-discovery + test + + + org.springframework.cloud + spring-cloud-zookeeper-bus + test + + + org.projectlombok + lombok + test + +