Merge pull request #2 from marcingrzejszczak/serviceRegistration

4financeIT zookeper stuff vol.1 ;)
This commit is contained in:
Spencer Gibb
2015-05-28 14:01:03 -04:00
48 changed files with 1862 additions and 169 deletions

84
pom.xml
View File

@@ -39,17 +39,41 @@
</modules>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Spec.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencyManagement>
@@ -161,6 +185,43 @@
<version>1.12.6</version>
<scope>provided</scope>
</dependency>
<!-- Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>${spock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>${spock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
@@ -171,6 +232,7 @@
<spring.cloud.netflix.core>1.0.1.BUILD-SNAPSHOT</spring.cloud.netflix.core>
<curator.version>2.7.0</curator.version>
<ribbon.version>2.0-RC9</ribbon.version>
<spock.version>1.0-groovy-2.4</spock.version>
</properties>
</project>

View File

@@ -16,6 +16,15 @@
<relativePath>..</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>

View File

@@ -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;
}
}

View File

@@ -16,6 +16,15 @@
<relativePath>..</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>

View File

@@ -42,16 +42,14 @@ public class ZookeeperPropertySource extends EnumerablePropertySource<CuratorFra
cache = TreeCache.newBuilder(source, context).build();
cache.start();
running = true;
/*
TODO: race condition since TreeCache.process(..) is invoked asynchronously.
Methods getProperty and getPropertyNames could be invoked before that TreeCache.process(..) receives
all the WatchedEvents.
*/
}
catch (NoNodeException e) {
/*
TODO: race condition since TreeCache.process(..) is invoked asynchronously.
Methods getProperty and getPropertyNames could be invoked before that TreeCache.process(..) receives
all the WatchedEvents.
*/
} catch (NoNodeException e) {
// no node, ignore
}
catch (Exception e) {
} catch (Exception e) {
LOG.error("Error initializing ZookeperPropertySource", e);
}
}
@@ -79,10 +77,9 @@ public class ZookeeperPropertySource extends EnumerablePropertySource<CuratorFra
return;
for (Map.Entry<String, ChildData> 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('/', '.'));
}
}

View File

@@ -16,6 +16,19 @@
<relativePath>..</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -49,7 +62,37 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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()
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -16,6 +16,22 @@
<relativePath>..</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
@@ -52,6 +68,48 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
<version>1.53</version>
</dependency>
</dependencies>
</project>

View File

@@ -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<NetworkInterface> enumNic = NetworkInterface.getNetworkInterfaces();
enumNic.hasMoreElements(); ) {
NetworkInterface ifc = enumNic.nextElement();
if (ifc.isUp()) {
for (Enumeration<InetAddress> enumAddr = ifc.getInetAddresses();
enumAddr.hasMoreElements(); ) {
InetAddress address = enumAddr.nextElement();
if (address instanceof Inet4Address && !address.isLoopbackAddress()) {
return address.getHostAddress();
}
}
}
}
} catch (IOException e) {
// ignore
}
return "unknown";
}
}

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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<ZookeeperInstance> 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<org.springframework.cloud.client.ServiceInstance> getInstances(
final String serviceId) {
String serviceIdToQuery = getServiceIdToQuery(serviceId);
Collection<ServiceInstance<ZookeeperInstance>> zkInstances = discovery
.queryForInstances(serviceId);
.queryForInstances(serviceIdToQuery);
ArrayList<org.springframework.cloud.client.ServiceInstance> instances = new ArrayList<>();
for (ServiceInstance<ZookeeperInstance> 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<String> getServices() {
ArrayList<String> services = null;

View File

@@ -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<ZookeeperInstance> serviceInstance() throws Exception {
@ConditionalOnMissingBean
public ServiceInstance<ZookeeperInstance> 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.<ZookeeperInstance> 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<ZookeeperInstance> serviceDiscovery(CuratorFramework curator)
@ConditionalOnMissingBean
public ServiceDiscovery<ZookeeperInstance> serviceDiscovery(CuratorFramework curator, ServiceInstance<ZookeeperInstance> 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();
}
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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<ZookeeperServer> {
@@ -30,6 +31,10 @@ public class ZookeeperServerList extends AbstractServerList<ZookeeperServer> {
this.serviceId = clientConfig.getClientName();
}
public void initFromDependencies(IClientConfig clientConfig, ZookeeperDependencies zookeeperDependencies) {
this.serviceId = zookeeperDependencies.getPathForAlias(clientConfig.getClientName());
}
@Override
public List<ZookeeperServer> getInitialListOfServers() {
return getServers();

View File

@@ -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<String, Object> subProperties = new RelaxedPropertyResolver(context.getEnvironment()).getSubProperties(ZOOKEEPER_DEPENDENCIES_PROP);
return subProperties.isEmpty() ?
ConditionOutcome.noMatch("No dependencies have been passed for the service") :
ConditionOutcome.match();
}
}

View File

@@ -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;
}
}

View File

@@ -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<String, ZookeeperDependency> dependencies = new LinkedHashMap<>();
@PostConstruct
public void init() {
for (Map.Entry<String, ZookeeperDependency> 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<String, String> headers;
private boolean required;
}
public Collection<ZookeeperDependency> getDependencyConfigurations() {
return dependencies.values();
}
public boolean hasDependencies() {
return !dependencies.isEmpty();
}
public String getPathForAlias(final String alias) {
for (Map.Entry<String, ZookeeperDependency> zookeeperDependencyEntry : dependencies.entrySet()) {
if (zookeeperDependencyEntry.getKey().equals(alias)) {
return zookeeperDependencyEntry.getValue().getPath();
}
}
return "";
}
}

View File

@@ -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();
}
}

View File

@@ -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<String, ServiceCache> dependencyRegistry = new HashMap<>();
private final List<DependencyWatcherListener> listeners;
private final DependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier;
private final ZookeeperDependencies zookeeperDependencies;
public DefaultDependencyWatcher(ServiceDiscovery serviceDiscovery,
DependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier,
List<DependencyWatcherListener> 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();
}
}
}

View File

@@ -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 <a href="http://www.4financeit.com>4financeIT</a>
*/
public interface DependencyRegistrationHookProvider {
/**
* Register hooks upon dependencies registration
*
* @throws Exception
*/
void registerDependencyRegistrationHooks() throws Exception;
/**
* Unregister hooks upon dependencies registration
*
* @throws IOException
*/
void clearDependencyRegistrationHooks() throws IOException;
}

View File

@@ -0,0 +1,24 @@
/*
* 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;
/**
* @author <a href="http://www.4financeit.com>4financeIT</a>
*/
public enum DependencyState {
CONNECTED,
DISCONNECTED
}

View File

@@ -0,0 +1,66 @@
/*
* 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.state.ConnectionState;
import org.apache.curator.x.discovery.ServiceCache;
import org.apache.curator.x.discovery.details.ServiceCacheListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* @author Marcin Grzejszczak, 4financeIT
* @author Tomasz Nurkiewicz, 4financeIT
*/
public class DependencyStateChangeListenerRegistry implements ServiceCacheListener {
private static final Logger log = LoggerFactory.getLogger(DependencyStateChangeListenerRegistry.class);
private final List<DependencyWatcherListener> listeners;
private final String dependencyName;
private final ServiceCache serviceCache;
public DependencyStateChangeListenerRegistry(List<DependencyWatcherListener> 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
}
}

View File

@@ -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);
}

View File

@@ -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<DependencyWatcherListener> 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);
}
}

View File

@@ -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 <a href="http://www.4financeit.com>4financeIT</a>
*/
public interface DependencyWatcherListener {
/**
* Method executed upon state change of a dependency
*
* @param dependencyName - alias from microservice configuration {@see ZookeeperDependencies}
* @param newState
*/
void stateChanged(String dependencyName, DependencyState newState);
}

View File

@@ -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 <a href="http://www.4financeit.com>4financeIT</a>
*/
public class DefaultDependencyPresenceOnStartupVerifier extends DependencyPresenceOnStartupVerifier {
public DefaultDependencyPresenceOnStartupVerifier() {
super(new LogMissingDependencyChecker());
}
}

View File

@@ -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.ServiceCache;
/**
* @author Marcin Grzejszczak, 4financeIT
* @author Tomasz Szymanski, 4financeIT
*/
@SuppressWarnings("unchecked")
public abstract class DependencyPresenceOnStartupVerifier {
private static final PresenceChecker MANDATORY_DEPENDENCY_CHECKER = new FailOnMissingDependencyChecker();
private final PresenceChecker optionalDependencyChecker;
public DependencyPresenceOnStartupVerifier(PresenceChecker optionalDependencyChecker) {
this.optionalDependencyChecker = optionalDependencyChecker;
}
public void verifyDependencyPresence(String dependencyName, ServiceCache serviceCache, boolean required) {
if (required) {
MANDATORY_DEPENDENCY_CHECKER.checkPresence(dependencyName, serviceCache.getInstances());
} else {
optionalDependencyChecker.checkPresence(dependencyName, serviceCache.getInstances());
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.springframework.cloud.zookeeper.discovery.watcher.presence.NoInstancesRunningException;
import org.springframework.cloud.zookeeper.discovery.watcher.presence.PresenceChecker;
import java.util.List;
/**
* @author Marcin Grzejszczak, 4financeIT
* @author Adam Chudzik, 4financeIT
*/
public class FailOnMissingDependencyChecker implements PresenceChecker {
@Override
public void checkPresence(String dependencyName, List<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
throw new NoInstancesRunningException(dependencyName);
}
}
}

View File

@@ -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<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
log.warn("Microservice dependency with name [" + dependencyName + "] is missing.");
}
}
}

View File

@@ -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 <a href="http://www.4financeit.com>4financeIT</a>
*/
public class NoInstancesRunningException extends RuntimeException {
public NoInstancesRunningException(String dependencyName) {
super("Required microservice dependency with name [" + dependencyName + "] is missing");
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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 java.util.List;
/**
* @author <a href="http://www.4financeit.com>4financeIT</a>
*/
public interface PresenceChecker {
/**
* Checks if a given dependency is present
*
* @param dependencyName
* @param serviceInstances - instances to check the dependency for
*/
void checkPresence(String dependencyName, List<ServiceInstance> serviceInstances);
}

View File

@@ -1,6 +1,8 @@
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration
org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration,\
org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherAutoConfiguration
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClientConfiguration

View File

@@ -0,0 +1,49 @@
/*
* 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 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
import org.springframework.context.annotation.Configuration
import org.springframework.util.SocketUtils
@PackageScope
@CompileStatic
@Configuration
class CommonTestConfig {
@Bean(destroyMethod = 'close')
TestingServer testingServer() {
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}")
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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 groovy.transform.CompileStatic
import org.apache.curator.framework.CuratorFramework
import org.apache.curator.x.discovery.ServiceDiscovery
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder
import org.apache.curator.x.discovery.ServiceInstance
import org.apache.curator.x.discovery.UriSpec
@CompileStatic
class TestServiceRegistrar {
private final int wiremockServerPort
private final CuratorFramework curatorFramework
private final ServiceDiscovery serviceDiscovery
TestServiceRegistrar(int wiremockServerPort, CuratorFramework curatorFramework) {
this.wiremockServerPort = wiremockServerPort
this.curatorFramework = curatorFramework
this.serviceDiscovery = serviceDiscovery()
}
void start() {
serviceDiscovery.start()
}
ServiceInstance serviceInstance() {
return ServiceInstance.builder().uriSpec(new UriSpec("{scheme}://{address}:{port}/"))
.address('localhost')
.port(wiremockServerPort)
.name('testInstance')
.build()
}
ServiceDiscovery serviceDiscovery() {
return ServiceDiscoveryBuilder
.builder(Void)
.basePath('/services')
.client(curatorFramework)
.thisInstance(serviceInstance())
.build()
}
void stop() {
serviceDiscovery.close()
}
}

View File

@@ -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
import groovy.transform.PackageScope
import groovy.transform.CompileStatic
import org.springframework.web.client.RestTemplate
@PackageScope
@CompileStatic
class TestServiceRestClient {
final RestTemplate restTemplate;
TestServiceRestClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate
}
String pingService(String alias) {
return restTemplate.getForObject("http://$alias/ping", String)
}
String pingOnUrl(String url) {
return new RestTemplate().getForObject("http://$url/ping", String)
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.SpringApplicationContextLoader
import org.springframework.boot.test.WebIntegrationTest
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('ribbon')
@WebIntegrationTest
class ZookeeperDiscoveryISpec extends Specification {
public static final String TEST_INSTANCE_NAME = 'testInstance'
@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'() {
expect:
'pong' == testRibbonClient.pingService(TEST_INSTANCE_NAME)
}
def 'should find the app by its name via Ribbon'() {
expect:
'{"status":"UP"}' == testRibbonClient.thisHealthCheck()
}
def 'should find a collaborator via discovery client'() {
given:
List<ServiceInstance> 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)
}
}
}

View File

@@ -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<ServiceInstance> 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)
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -0,0 +1 @@
spring.application.name: ribbonApp

View File

@@ -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

View File

@@ -1,58 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-zookeeper-sample</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Zookeeper Sample</name>
<description>Spring Cloud Zookeeper Sample</description>
<artifactId>spring-cloud-zookeeper-sample</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Zookeeper Sample</name>
<description>Spring Cloud Zookeeper Sample</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<build>
<plugins>
<plugin>
<!--skip deploy -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<!--skip deploy -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-config</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-discovery</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-bus</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-config</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-discovery</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-bus</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>