initial commit
This commit is contained in:
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*~
|
||||
#*
|
||||
*#
|
||||
.#*
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
.springBeans
|
||||
target/
|
||||
_site/
|
||||
.idea
|
||||
*.iml
|
||||
*.swp
|
||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
spring-cloud-zookeeper
|
||||
===================
|
||||
|
||||
Developer Preview
|
||||
|
||||
### TODO
|
||||
|
||||
- [X] zookeeper config
|
||||
- [X] zookeeper service discovery
|
||||
- [X] zookeeper ribbon load balancer
|
||||
- [ ] zookeeper ui
|
||||
- [X] zookeeper property source
|
||||
- [ ] zookeeper event bus
|
||||
- [ ] send messages
|
||||
- [ ] receive messages
|
||||
177
pom.xml
Normal file
177
pom.xml
Normal file
@@ -0,0 +1,177 @@
|
||||
<?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>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Zookeeper</name>
|
||||
<description>Spring Cloud Zookeeper</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
<!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-zookeeper</url>
|
||||
<connection>
|
||||
scm:git:git://github.com/spring-cloud/spring-cloud-zookeeper.git
|
||||
</connection>
|
||||
<developerConnection>
|
||||
scm:git:ssh://git@github.com/spring-cloud/spring-cloud-zookeeper.git
|
||||
</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud-zookeeper-core</module>
|
||||
<module>spring-cloud-zookeeper-config</module>
|
||||
<module>spring-cloud-zookeeper-discovery</module>
|
||||
<module>spring-cloud-zookeeper-bus</module>
|
||||
<module>spring-cloud-zookeeper-sample</module>
|
||||
</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>
|
||||
</build>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-core</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-bus</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-config</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-discovery</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-bus</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-amqp</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-spring-service-connector
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-localconfig-connector
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-cloudfoundry-connector
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-framework</artifactId>
|
||||
<version>${curator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-recipes</artifactId>
|
||||
<version>${curator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-x-discovery</artifactId>
|
||||
<version>${curator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-test</artifactId>
|
||||
<version>${curator.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.12.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>18.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<properties>
|
||||
<curator.version>2.7.0</curator.version>
|
||||
<ribbon.version>2.0-RC9</ribbon.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
46
spring-cloud-zookeeper-bus/pom.xml
Normal file
46
spring-cloud-zookeeper-bus/pom.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?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>
|
||||
|
||||
<artifactId>spring-cloud-zookeeper-bus</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Zookeeper Bus</name>
|
||||
<description>Spring Cloud Zookeeper Bus</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-bus</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!-- Only needed at compile time -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.springframework.cloud.zookeeper.bus;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
||||
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@JsonTypeName("simple")
|
||||
@Data
|
||||
public class SimpleRemoteEvent extends RemoteApplicationEvent {
|
||||
|
||||
private String message;
|
||||
|
||||
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 message) {
|
||||
super(source, originService);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Auto Configuration
|
||||
#org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
#org.springframework.cloud.zookeeper.bus.ZookeeperBusAutoConfiguration
|
||||
50
spring-cloud-zookeeper-config/pom.xml
Normal file
50
spring-cloud-zookeeper-config/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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>
|
||||
|
||||
<artifactId>spring-cloud-zookeeper-config</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Zookeeper Config</name>
|
||||
<description>Spring Cloud Zookeeper Config</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-recipes</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!-- Only needed at compile time -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@Import(ZookeeperAutoConfiguration.class)
|
||||
@EnableConfigurationProperties
|
||||
public class ZookeeperConfigBootstrapConfiguration {
|
||||
@Bean
|
||||
public ZookeeperPropertySourceLocator zookeeperPropertySourceLocator() {
|
||||
return new ZookeeperPropertySourceLocator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZookeeperConfigProperties zookeeperConfigProperties() {
|
||||
return new ZookeeperConfigProperties();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("zookeeper.config")
|
||||
@Data
|
||||
public class ZookeeperConfigProperties {
|
||||
private boolean enabled = true;
|
||||
|
||||
private String root = "config";
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.recipes.cache.ChildData;
|
||||
import org.apache.curator.framework.recipes.cache.TreeCache;
|
||||
import org.apache.zookeeper.KeeperException.NoNodeException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ZookeeperPropertySource extends EnumerablePropertySource<CuratorFramework>
|
||||
implements Lifecycle {
|
||||
private static final Logger LOG = LoggerFactory
|
||||
.getLogger(ZookeeperPropertySource.class);
|
||||
|
||||
private String context;
|
||||
|
||||
private TreeCache cache;
|
||||
private boolean running;
|
||||
|
||||
public ZookeeperPropertySource(String context, CuratorFramework source) {
|
||||
super(context, source);
|
||||
this.context = context;
|
||||
|
||||
if (!this.context.startsWith("/")) {
|
||||
this.context = "/" + this.context;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
try {
|
||||
cache = TreeCache.newBuilder(source, context).build();
|
||||
cache.start();
|
||||
running = true;
|
||||
}
|
||||
catch (NoNodeException e) {
|
||||
// no node, ignore
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error("Error initializing ZookeperPropertySource", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
String fullPath = context + "/" + name.replace(".", "/");
|
||||
ChildData data = cache.getCurrentData(fullPath);
|
||||
if (data == null)
|
||||
return null;
|
||||
return new String(data.getData(), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
List<String> keys = new ArrayList<>();
|
||||
findKeys(keys, context);
|
||||
return keys.toArray(new String[0]);
|
||||
}
|
||||
|
||||
protected void findKeys(List<String> keys, String path) {
|
||||
Map<String, ChildData> children = cache.getCurrentChildren(path);
|
||||
|
||||
if (children == null)
|
||||
return;
|
||||
for (Map.Entry<String, ChildData> entry : children.entrySet()) {
|
||||
ChildData child = entry.getValue();
|
||||
if (child.getData().length == 0) {
|
||||
findKeys(keys, child.getPath());
|
||||
}
|
||||
else {
|
||||
keys.add(child.getPath().replace(context + "/", "").replace('/', '.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
cache.close();
|
||||
running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.config.client.PropertySourceLocator;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ZookeeperPropertySourceLocator implements PropertySourceLocator {
|
||||
|
||||
@Autowired
|
||||
private ZookeeperConfigProperties properties;
|
||||
|
||||
@Autowired
|
||||
private CuratorFramework curator;
|
||||
|
||||
@Override
|
||||
public PropertySource<?> locate(Environment environment) {
|
||||
if (environment instanceof ConfigurableEnvironment) {
|
||||
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
|
||||
String appName = env.getProperty("spring.application.name");
|
||||
List<String> profiles = Arrays.asList(env.getActiveProfiles());
|
||||
|
||||
String root = properties.getRoot();
|
||||
List<String> contexts = new ArrayList<>();
|
||||
|
||||
String defaultContext = root + "/application";
|
||||
contexts.add(defaultContext);
|
||||
addProfiles(contexts, defaultContext, profiles);
|
||||
|
||||
String baseContext = root + "/" + appName;
|
||||
contexts.add(baseContext);
|
||||
addProfiles(contexts, baseContext, profiles);
|
||||
|
||||
CompositePropertySource composite = new CompositePropertySource("zookeeper");
|
||||
|
||||
for (String propertySourceContext : contexts) {
|
||||
ZookeeperPropertySource propertySource = create(propertySourceContext);
|
||||
propertySource.start();
|
||||
composite.addPropertySource(propertySource);
|
||||
// TODO: howto call close when /refresh
|
||||
}
|
||||
|
||||
return composite;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
// TODO: call close on zkps's
|
||||
}
|
||||
|
||||
private ZookeeperPropertySource create(String context) {
|
||||
return new ZookeeperPropertySource(context, curator);
|
||||
}
|
||||
|
||||
private void addProfiles(List<String> contexts, String baseContext,
|
||||
List<String> profiles) {
|
||||
for (String profile : profiles) {
|
||||
contexts.add(baseContext + "::" + profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Bootstrap Configuration
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.zookeeper.config.ZookeeperConfigBootstrapConfiguration
|
||||
59
spring-cloud-zookeeper-core/pom.xml
Normal file
59
spring-cloud-zookeeper-core/pom.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?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>
|
||||
|
||||
<artifactId>spring-cloud-zookeeper-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Zookeeper Core</name>
|
||||
<description>Spring Cloud Zookeeper Core</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!-- Only needed at compile time -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-framework</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.springframework.cloud.zookeeper;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public class ZookeeperAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZookeeperProperties zookeeperProperties() {
|
||||
return new ZookeeperProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CuratorFramework curatorFramework() {
|
||||
CuratorFramework curator = CuratorFrameworkFactory.builder()
|
||||
// TODO: configurable retry policy
|
||||
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
|
||||
// .retryPolicy(new RetryOneTime(100))
|
||||
// TODO: support ensembleProvider via ExhibitorEnsembleProvider
|
||||
// .ensembleProvider(new ExhibitorEnsembleProvider())
|
||||
.connectString(zookeeperProperties().getConnectString()).build();
|
||||
curator.start();
|
||||
return curator;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
curatorFramework().close();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZookeeperEndpoint zookeeperEndpoint() {
|
||||
return new ZookeeperEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZookeeperHealthIndicator zookeeperHealthIndicator() {
|
||||
return new ZookeeperHealthIndicator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.springframework.cloud.zookeeper;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.zookeeper", ignoreUnknownFields = false)
|
||||
public class ZookeeperEndpoint extends AbstractEndpoint<ZookeeperEndpoint.ZookeeperData> {
|
||||
|
||||
@Autowired
|
||||
public ZookeeperEndpoint() {
|
||||
super("zookeeper", false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZookeeperData invoke() {
|
||||
ZookeeperData data = new ZookeeperData();
|
||||
return data;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ZookeeperData {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.springframework.cloud.zookeeper;
|
||||
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.imps.CuratorFrameworkState;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ZookeeperHealthIndicator extends AbstractHealthIndicator {
|
||||
@Autowired
|
||||
CuratorFramework curator;
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
try {
|
||||
if (curator.getState() != CuratorFrameworkState.STARTED) {
|
||||
builder.down().withDetail("error", "Client not started");
|
||||
}
|
||||
else if (curator.checkExists().forPath("/") == null) {
|
||||
builder.down().withDetail("error", "Root for namespace does not exist");
|
||||
}
|
||||
else {
|
||||
builder.up();
|
||||
}
|
||||
builder.withDetail("connectionString",
|
||||
curator.getZookeeperClient().getCurrentConnectionString())
|
||||
.withDetail("state", curator.getState());
|
||||
}
|
||||
catch (Exception e) {
|
||||
builder.down(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.springframework.cloud.zookeeper;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("zookeeper")
|
||||
@Data
|
||||
public class ZookeeperProperties {
|
||||
@NotNull
|
||||
private String connectString = "localhost:2181";
|
||||
|
||||
private boolean enabled = true;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Auto Configuration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration
|
||||
@@ -0,0 +1,43 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
61
spring-cloud-zookeeper-discovery/pom.xml
Normal file
61
spring-cloud-zookeeper-discovery/pom.xml
Normal file
@@ -0,0 +1,61 @@
|
||||
<?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>
|
||||
|
||||
<artifactId>spring-cloud-zookeeper-discovery</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Zookeeper Discovery</name>
|
||||
<description>Spring Cloud Zookeeper Discovery</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-zookeeper-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-x-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!-- Only needed at compile time -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnBean(SpringClientFactory.class)
|
||||
@ConditionalOnProperty(value = "ribbon.zookeeper.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter(RibbonAutoConfiguration.class)
|
||||
@RibbonClients(defaultConfiguration = ZookeeperRibbonClientConfiguration.class)
|
||||
public class RibbonZookeeperAutoConfiguration {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
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.context.ApplicationContext;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ZookeeperDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext context;
|
||||
|
||||
@Autowired(required = false)
|
||||
ServiceInstance<ZookeeperInstance> instance;
|
||||
|
||||
@Autowired
|
||||
ServiceDiscovery<ZookeeperInstance> discovery;
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Spring Cloud Zookeeper Discovery Client";
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.springframework.cloud.client.ServiceInstance getLocalServiceInstance() {
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("Unable to locate instance in zookeeper: "
|
||||
+ context.getId());
|
||||
}
|
||||
return new DefaultServiceInstance(instance.getId(), instance.getAddress(),
|
||||
instance.getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public List<org.springframework.cloud.client.ServiceInstance> getInstances(
|
||||
final String serviceId) {
|
||||
Collection<ServiceInstance<ZookeeperInstance>> zkInstances = discovery
|
||||
.queryForInstances(serviceId);
|
||||
|
||||
Iterable<org.springframework.cloud.client.ServiceInstance> instances = transform(
|
||||
zkInstances,
|
||||
new Function<ServiceInstance<ZookeeperInstance>, org.springframework.cloud.client.ServiceInstance>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public org.springframework.cloud.client.ServiceInstance apply(
|
||||
@Nullable ServiceInstance<ZookeeperInstance> input) {
|
||||
return new DefaultServiceInstance(serviceId, input.getAddress(),
|
||||
input.getPort());
|
||||
}
|
||||
});
|
||||
|
||||
return Lists.newArrayList(instances);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public List<org.springframework.cloud.client.ServiceInstance> getAllInstances() {
|
||||
Iterable<org.springframework.cloud.client.ServiceInstance> instances = transform(
|
||||
concat(transform(
|
||||
discovery.queryForNames(),
|
||||
new Function<String, Collection<ServiceInstance<ZookeeperInstance>>>() {
|
||||
@SneakyThrows
|
||||
public Collection<ServiceInstance<ZookeeperInstance>> apply(
|
||||
String input) {
|
||||
return discovery.queryForInstances(input);
|
||||
}
|
||||
})),
|
||||
new Function<ServiceInstance<ZookeeperInstance>, org.springframework.cloud.client.ServiceInstance>() {
|
||||
public org.springframework.cloud.client.ServiceInstance apply(
|
||||
ServiceInstance<ZookeeperInstance> input) {
|
||||
return new DefaultServiceInstance(input.getName(), input
|
||||
.getAddress(), input.getPort());
|
||||
}
|
||||
});
|
||||
|
||||
return Lists.newArrayList(instances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getServices() {
|
||||
ArrayList<String> services = null;
|
||||
try {
|
||||
services = Lists.newArrayList(discovery.queryForNames());
|
||||
}
|
||||
catch (Exception e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
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;
|
||||
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.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public class ZookeeperDiscoveryClientConfiguration {
|
||||
@Autowired
|
||||
ApplicationContext context;
|
||||
|
||||
@Bean
|
||||
public ZookeeperDiscoveryProperties zookeeperDiscoveryProperties() {
|
||||
return new ZookeeperDiscoveryProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZookeeperLifecycle zookeeperLifecycle() {
|
||||
return new ZookeeperLifecycle();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZookeeperDiscoveryClient zookeeperDiscoveryClient() {
|
||||
return new ZookeeperDiscoveryClient();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServiceInstance<ZookeeperInstance> serviceInstance() 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)
|
||||
.uriSpec(uriSpec).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InstanceSerializer<ZookeeperInstance> instanceSerializer() {
|
||||
return new JsonInstanceSerializer<>(ZookeeperInstance.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServiceDiscovery<ZookeeperInstance> serviceDiscovery(CuratorFramework curator)
|
||||
throws Exception {
|
||||
return ServiceDiscoveryBuilder.builder(ZookeeperInstance.class).client(curator)
|
||||
.basePath(zookeeperDiscoveryProperties().getRoot())
|
||||
.serializer(instanceSerializer()).thisInstance(serviceInstance()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZookeeperDiscoveryHealthIndicator zookeeperDiscoveryHealthIndicator() {
|
||||
return new ZookeeperDiscoveryHealthIndicator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.curator.x.discovery.ServiceDiscovery;
|
||||
import org.apache.curator.x.discovery.ServiceInstance;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZookeeperDiscoveryHealthIndicator extends AbstractHealthIndicator {
|
||||
@Autowired
|
||||
ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
try {
|
||||
Collection<String> names = serviceDiscovery.queryForNames();
|
||||
ArrayList<ServiceInstance<ZookeeperInstance>> allInstances = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
Collection<ServiceInstance<ZookeeperInstance>> instances = serviceDiscovery
|
||||
.queryForInstances(name);
|
||||
for (ServiceInstance<ZookeeperInstance> instance : instances) {
|
||||
allInstances.add(instance);
|
||||
}
|
||||
}
|
||||
builder.up().withDetail("services", allInstances);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Error", e);
|
||||
builder.down(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("zookeeper.discovery")
|
||||
@Data
|
||||
public class ZookeeperDiscoveryProperties {
|
||||
private boolean enabled = true;
|
||||
|
||||
private String root = "/services";
|
||||
|
||||
private String uriSpec = "{scheme}://{address}:{port}";
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ZookeeperInstance {
|
||||
private String id;
|
||||
|
||||
private ZookeeperInstance() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
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.discovery.AbstractDiscoveryLifecycle;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZookeeperLifecycle extends AbstractDiscoveryLifecycle {
|
||||
|
||||
@Autowired
|
||||
private ZookeeperDiscoveryProperties properties;
|
||||
|
||||
@Autowired
|
||||
private ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
|
||||
|
||||
@Autowired
|
||||
private ServiceInstance<ZookeeperInstance> instance;
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void register() {
|
||||
serviceDiscovery.start();
|
||||
}
|
||||
|
||||
// TODO: implement registerManagement
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void deregister() {
|
||||
serviceDiscovery.unregisterService(instance);
|
||||
}
|
||||
|
||||
// TODO: implement deregisterManagement
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled() {
|
||||
return properties.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getConfiguration() {
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
|
||||
* <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
|
||||
* NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
public class ZookeeperRibbonClientConfiguration {
|
||||
protected static final String VALUE_NOT_SET = "__not__set__";
|
||||
protected static final String DEFAULT_NAMESPACE = "ribbon";
|
||||
@Autowired
|
||||
private ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
|
||||
@Value("${ribbon.client.name}")
|
||||
private String serviceId = "client";
|
||||
|
||||
public ZookeeperRibbonClientConfiguration() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServerList<?> ribbonServerList(IClientConfig config) {
|
||||
ZookeeperServerList serverList = new ZookeeperServerList(serviceDiscovery);
|
||||
serverList.initWithNiwsConfig(config);
|
||||
return serverList;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void preprocess() {
|
||||
setProp(this.serviceId, DeploymentContextBasedVipAddresses.key(), this.serviceId);
|
||||
setProp(this.serviceId, EnableZoneAffinity.key(), "true");
|
||||
}
|
||||
|
||||
protected void setProp(String serviceId, String suffix, String value) {
|
||||
// how to set the namespace properly?
|
||||
String key = getKey(serviceId, suffix);
|
||||
DynamicStringProperty property = getProperty(key);
|
||||
if (property.get().equals(VALUE_NOT_SET)) {
|
||||
ConfigurationManager.getConfigInstance().setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected DynamicStringProperty getProperty(String key) {
|
||||
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
|
||||
}
|
||||
|
||||
protected String getKey(String serviceId, String suffix) {
|
||||
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import org.apache.curator.x.discovery.ServiceInstance;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ZookeeperServer extends Server {
|
||||
|
||||
private final MetaInfo metaInfo;
|
||||
|
||||
public ZookeeperServer(final ServiceInstance<ZookeeperInstance> instance) {
|
||||
// TODO: ssl support
|
||||
super(instance.getAddress(), instance.getPort());
|
||||
metaInfo = new MetaInfo() {
|
||||
@Override
|
||||
public String getAppName() {
|
||||
return instance.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerGroup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceIdForDiscovery() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return instance.getId();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaInfo getMetaInfo() {
|
||||
return metaInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import static com.google.common.collect.Collections2.transform;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.curator.x.discovery.ServiceDiscovery;
|
||||
import org.apache.curator.x.discovery.ServiceInstance;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AbstractServerList;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ZookeeperServerList extends AbstractServerList<ZookeeperServer> {
|
||||
|
||||
private String serviceId;
|
||||
private final ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
|
||||
|
||||
public ZookeeperServerList(ServiceDiscovery<ZookeeperInstance> serviceDiscovery) {
|
||||
this.serviceDiscovery = serviceDiscovery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initWithNiwsConfig(IClientConfig clientConfig) {
|
||||
this.serviceId = clientConfig.getClientName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZookeeperServer> getInitialListOfServers() {
|
||||
return getServers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZookeeperServer> getUpdatedListOfServers() {
|
||||
return getServers();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<ZookeeperServer> getServers() {
|
||||
try {
|
||||
Collection<ServiceInstance<ZookeeperInstance>> instances = serviceDiscovery
|
||||
.queryForInstances(serviceId);
|
||||
if (instances == null || instances.isEmpty()) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
Collection<ZookeeperServer> servers = transform(instances,
|
||||
new Function<ServiceInstance<ZookeeperInstance>, ZookeeperServer>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public ZookeeperServer apply(
|
||||
@Nullable ServiceInstance<ZookeeperInstance> instance) {
|
||||
ZookeeperServer server = new ZookeeperServer(instance);
|
||||
return server;
|
||||
}
|
||||
});
|
||||
|
||||
return newArrayList(servers);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Auto Configuration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration
|
||||
|
||||
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
|
||||
org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClientConfiguration
|
||||
58
spring-cloud-zookeeper-sample/pom.xml
Normal file
58
spring-cloud-zookeeper-sample/pom.xml
Normal file
@@ -0,0 +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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.springframework.cloud.zookeeper.sample;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.cloud.bus.jackson.SubtypeModule;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.zookeeper.bus.SimpleRemoteEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableDiscoveryClient
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class SampleApplication implements ApplicationListener<SimpleRemoteEvent> {
|
||||
|
||||
public static final String CLIENT_NAME = "testZookeeperApp";
|
||||
|
||||
@Autowired
|
||||
LoadBalancerClient loadBalancer;
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
|
||||
@Autowired(required = false)
|
||||
RelaxedPropertyResolver resolver;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleApplication.class, args);
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public ServiceInstance lb() {
|
||||
return loadBalancer.choose(CLIENT_NAME);
|
||||
}
|
||||
|
||||
@RequestMapping("/myenv")
|
||||
public String env(@RequestParam("prop") String prop) {
|
||||
String property = new RelaxedPropertyResolver(env).getProperty(prop, "Not Found");
|
||||
return property;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SubtypeModule sampleSubtypeModule() {
|
||||
return new SubtypeModule(SimpleRemoteEvent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(SimpleRemoteEvent event) {
|
||||
log.info("Received event: {}", event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
#TODO: figure out why I need this here and in bootstrap.yml
|
||||
spring:
|
||||
application:
|
||||
name: testZookeeperApp
|
||||
|
||||
endpoints:
|
||||
restart:
|
||||
enabled: true
|
||||
shutdown:
|
||||
enabled: true
|
||||
health:
|
||||
sensitive: false
|
||||
@@ -0,0 +1,7 @@
|
||||
spring:
|
||||
application:
|
||||
name: testZookeeperApp
|
||||
cloud:
|
||||
config:
|
||||
# TODO: refactor spring-cloud-config to use refresh, etc.. with out config client
|
||||
enabled: false
|
||||
Reference in New Issue
Block a user