diff --git a/README.adoc b/README.adoc
index 56fe059..5cdb080 100644
--- a/README.adoc
+++ b/README.adoc
@@ -39,7 +39,7 @@ See the https://coreos.com/etcd/[overview] for more information.
== Building
-:jdkversion: 1.7
+:jdkversion: 1.8
=== Basic Compile and Test
diff --git a/docs/pom.xml b/docs/pom.xml
index e6dc302..7451a18 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -6,14 +6,14 @@
org.springframework.cloud
spring-cloud-etcd
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
..
spring-cloud-etcd-docs
pom
Spring Cloud Etcd Docs
Spring Cloud Docs
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
spring-cloud-etcd
${basedir}/..
diff --git a/pom.xml b/pom.xml
index da9b3cc..ffb7888 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.cloud
spring-cloud-etcd
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
pom
Spring Cloud Etcd
Spring Cloud Etcd
@@ -13,7 +13,7 @@
org.springframework.cloud
spring-cloud-build
- 1.3.1.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
@@ -41,8 +41,8 @@
org.apache.maven.plugins
maven-compiler-plugin
- 1.7
- 1.7
+ 1.8
+ 1.8
diff --git a/spring-cloud-etcd-config/pom.xml b/spring-cloud-etcd-config/pom.xml
index 0620ef0..a9431b5 100644
--- a/spring-cloud-etcd-config/pom.xml
+++ b/spring-cloud-etcd-config/pom.xml
@@ -11,7 +11,7 @@
org.springframework.cloud
spring-cloud-etcd
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
..
diff --git a/spring-cloud-etcd-core/pom.xml b/spring-cloud-etcd-core/pom.xml
index 64e2c6c..4e55ba3 100644
--- a/spring-cloud-etcd-core/pom.xml
+++ b/spring-cloud-etcd-core/pom.xml
@@ -11,7 +11,7 @@
org.springframework.cloud
spring-cloud-etcd
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
../
diff --git a/spring-cloud-etcd-core/src/main/java/org/springframework/cloud/etcd/EtcdEndpoint.java b/spring-cloud-etcd-core/src/main/java/org/springframework/cloud/etcd/EtcdEndpoint.java
index e713adc..8bec16e 100644
--- a/spring-cloud-etcd-core/src/main/java/org/springframework/cloud/etcd/EtcdEndpoint.java
+++ b/spring-cloud-etcd-core/src/main/java/org/springframework/cloud/etcd/EtcdEndpoint.java
@@ -18,23 +18,24 @@
import mousio.etcd4j.EtcdClient;
-import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
+import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Spencer Gibb
*/
@ConfigurationProperties(prefix = "endpoints.etcd", ignoreUnknownFields = false)
-public class EtcdEndpoint extends AbstractEndpoint {
+@Endpoint(id="etcd")
+public class EtcdEndpoint {
private EtcdClient etcd;
public EtcdEndpoint(EtcdClient etcd) {
- super("etcd", false, true);
this.etcd = etcd;
}
- @Override
+ @ReadOperation
public Data invoke() {
Data data = new Data();
data.setVersion(etcd.getVersion());
diff --git a/spring-cloud-etcd-dependencies/pom.xml b/spring-cloud-etcd-dependencies/pom.xml
index a2eceeb..a97f655 100644
--- a/spring-cloud-etcd-dependencies/pom.xml
+++ b/spring-cloud-etcd-dependencies/pom.xml
@@ -5,7 +5,7 @@
org.springframework.cloud
spring-cloud-etcd-dependencies
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
pom
spring-cloud-etcd-dependencies
Spring Cloud Etcd Dependencies
@@ -13,16 +13,16 @@
org.springframework.cloud
spring-cloud-dependencies-parent
- 1.3.1.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
- 1.1.0.BUILD-SNAPSHOT
- 1.1.0.BUILD-SNAPSHOT
- 0.7.1
- 2.7.0
- 4.1.0.Beta5
+ 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
+ 0.7.5
+ 2.15.0
+ 4.1.22.Final
1.8
diff --git a/spring-cloud-etcd-discovery/pom.xml b/spring-cloud-etcd-discovery/pom.xml
index d822144..8dc3007 100644
--- a/spring-cloud-etcd-discovery/pom.xml
+++ b/spring-cloud-etcd-discovery/pom.xml
@@ -11,7 +11,7 @@
org.springframework.cloud
spring-cloud-etcd
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
..
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdAutoServiceRegistrationAutoConfiguration.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdAutoServiceRegistrationAutoConfiguration.java
new file mode 100644
index 0000000..97fccf1
--- /dev/null
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdAutoServiceRegistrationAutoConfiguration.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 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.etcd.discovery;
+
+import javax.servlet.ServletContext;
+
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
+import org.springframework.cloud.etcd.discovery.EtcdDiscoveryProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Venil Noronha
+ */
+@Configuration
+@ConditionalOnBean(AutoServiceRegistrationProperties.class)
+@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
+@AutoConfigureAfter(EtcdServiceRegistryAutoConfiguration.class)
+public class EtcdAutoServiceRegistrationAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public EtcdRegistration etcdRegistration(EtcdDiscoveryProperties properties, ApplicationContext applicationContext,
+ ServletContext servletContext) {
+ return EtcdRegistration.registration(properties, applicationContext, servletContext);
+ }
+
+}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClient.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClient.java
index 87a862c..69bf881 100644
--- a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClient.java
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClient.java
@@ -17,6 +17,7 @@
package org.springframework.cloud.etcd.discovery;
import mousio.etcd4j.EtcdClient;
+import mousio.etcd4j.responses.EtcdAuthenticationException;
import mousio.etcd4j.responses.EtcdException;
import mousio.etcd4j.responses.EtcdKeysResponse;
import org.springframework.beans.BeansException;
@@ -34,20 +35,21 @@
/**
* @author Spencer Gibb
+ * @author Venil Noronha
*/
public class EtcdDiscoveryClient implements DiscoveryClient, ApplicationContextAware {
private final EtcdClient etcd;
- private final EtcdLifecycle lifecycle;
-
private final EtcdDiscoveryProperties properties;
+ private final EtcdRegistration registration;
+
private ApplicationContext context;
- public EtcdDiscoveryClient(EtcdClient etcd, EtcdLifecycle lifecycle, EtcdDiscoveryProperties properties) {
+ public EtcdDiscoveryClient(EtcdClient etcd, EtcdRegistration registration, EtcdDiscoveryProperties properties) {
this.etcd = etcd;
- this.lifecycle = lifecycle;
+ this.registration = registration;
this.properties = properties;
}
@@ -58,27 +60,31 @@ public String description() {
@Override
public ServiceInstance getLocalServiceInstance() {
- return new DefaultServiceInstance(lifecycle.getService().getAppName(),
- properties.getHostname(), lifecycle.getConfiguredPort(), false);
+ return new DefaultServiceInstance(registration.getService().getAppName(),
+ properties.getHostname(), registration.getService().getPort(), false);
}
@Override
public List getInstances(final String serviceId) {
List instances = null;
try {
- EtcdKeysResponse response = etcd.getDir(lifecycle.getAppKey(serviceId)).send().get();
+ EtcdKeysResponse response = etcd.getDir(getAppKey(registration.getService().getAppName())).send().get();
List nodes = response.node.nodes;
instances = new ArrayList<>();
for (EtcdKeysResponse.EtcdNode node : nodes) {
String[] parts = node.value.split(":");
instances.add(new DefaultServiceInstance(serviceId, parts[0], Integer.parseInt(parts[1]), false));
}
- } catch (IOException | TimeoutException | EtcdException e) {
+ } catch (IOException | TimeoutException | EtcdException | EtcdAuthenticationException e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return instances;
}
+ private String getAppKey(String appName) {
+ return properties.getDiscoveryPrefix() + "/" + appName;
+ }
+
@Override
public List getServices() {
List services = null;
@@ -91,7 +97,7 @@ public List getServices() {
serviceId = serviceId.substring(1);
services.add(serviceId);
}
- } catch (IOException | EtcdException | TimeoutException e) {
+ } catch (IOException | EtcdException | TimeoutException | EtcdAuthenticationException e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return services;
@@ -102,8 +108,8 @@ public void setApplicationContext(ApplicationContext context) throws BeansExcept
this.context = context;
}
- public EtcdLifecycle getLifecycle() {
- return lifecycle;
+ public EtcdRegistration getRegistration() {
+ return registration;
}
public EtcdDiscoveryProperties getProperties() {
@@ -126,7 +132,7 @@ public boolean equals(Object o) {
EtcdDiscoveryClient that = (EtcdDiscoveryClient) o;
if (etcd != null ? !etcd.equals(that.etcd) : that.etcd != null) return false;
- if (lifecycle != null ? !lifecycle.equals(that.lifecycle) : that.lifecycle != null) return false;
+ if (registration != null ? !registration.equals(that.registration) : that.registration != null) return false;
if (properties != null ? !properties.equals(that.properties) : that.properties != null) return false;
return context != null ? context.equals(that.context) : that.context == null;
}
@@ -134,7 +140,7 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
int result = etcd != null ? etcd.hashCode() : 0;
- result = 31 * result + (lifecycle != null ? lifecycle.hashCode() : 0);
+ result = 31 * result + (registration != null ? registration.hashCode() : 0);
result = 31 * result + (properties != null ? properties.hashCode() : 0);
result = 31 * result + (context != null ? context.hashCode() : 0);
return result;
@@ -142,6 +148,6 @@ public int hashCode() {
@Override
public String toString() {
- return String.format("EtcdDiscoveryClient{etcd=%s, lifecycle=%s, properties=%s, context=%s}", etcd, lifecycle, properties, context);
+ return String.format("EtcdDiscoveryClient{etcd=%s, registration=%s, properties=%s, context=%s}", etcd, registration, properties, context);
}
}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClientConfiguration.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClientConfiguration.java
index 6fdfd33..a2b4c86 100644
--- a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClientConfiguration.java
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdDiscoveryClientConfiguration.java
@@ -19,6 +19,8 @@
import mousio.etcd4j.EtcdClient;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -26,8 +28,10 @@
/**
* @author Spencer Gibb
+ * @author Venil Noronha
*/
@Configuration
+@ConditionalOnProperty(value = "spring.cloud.etcd.discovery.enabled", matchIfMissing = true)
@EnableScheduling
@EnableConfigurationProperties
public class EtcdDiscoveryClientConfiguration {
@@ -35,14 +39,15 @@ public class EtcdDiscoveryClientConfiguration {
@Autowired
private EtcdClient client;
- @Bean
- public EtcdLifecycle etcdLifecycle() {
- return new EtcdLifecycle(client, etcdDiscoveryProperties());
+ @ConditionalOnMissingBean
+ public HeartbeatScheduler heartbeatScheduler(EtcdDiscoveryProperties properties) {
+ return new HeartbeatScheduler(client, properties);
}
@Bean
- public EtcdDiscoveryClient etcdDiscoveryClient() {
- return new EtcdDiscoveryClient(client, etcdLifecycle(), etcdDiscoveryProperties());
+ @ConditionalOnMissingBean
+ public EtcdDiscoveryClient etcdDiscoveryClient(EtcdDiscoveryProperties properties, EtcdRegistration registration) {
+ return new EtcdDiscoveryClient(client, registration, properties);
}
@Bean
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdLifecycle.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdLifecycle.java
deleted file mode 100644
index cc12fd9..0000000
--- a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdLifecycle.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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.etcd.discovery;
-
-import mousio.etcd4j.EtcdClient;
-import mousio.etcd4j.responses.EtcdException;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.util.Assert;
-import org.springframework.util.ReflectionUtils;
-
-import java.io.IOException;
-import java.util.concurrent.TimeoutException;
-
-/**
- * @author Spencer Gibb
- */
-public class EtcdLifecycle extends AbstractDiscoveryLifecycle {
-
- private static final Log log = LogFactory.getLog(EtcdLifecycle.class);
-
- private final EtcdClient etcd;
- private final EtcdDiscoveryProperties props;
- private final Service service = new Service();
-
- public EtcdLifecycle(EtcdClient etcd, EtcdDiscoveryProperties props) {
- this.etcd = etcd;
- this.props = props;
- }
-
- @Override
- protected void register() {
- Assert.notNull(service.getPort(), "service.port has not been set");
-
- service.setAppName(getAppName());
- service.setId(getContext().getId());
-
- register(service);
- }
-
- @Scheduled(initialDelayString = "${spring.cloud.etcd.discovery.heartbeatInterval:25000}", fixedRateString = "${spring.cloud.etcd.discovery.heartbeatInterval:25000}")
- protected void sendHeartbeat() {
- register();
- }
-
- @Override
- protected void registerManagement() {
- Service management = new Service();
- management.setId(getManagementServiceId());
- management.setAppName(getManagementServiceName());
- management.setPort(getManagementPort());
-
- register(management);
- }
-
- protected void register(Service service) {
- try {
- log.info("Registering service with etcd: " + service);
- String key = getServiceKey(service.appName, service.getId());
- //TODO: what should be serialized about the service?
- String value = props.getHostname() + ":" + service.getPort();
- etcd.put(key, value).ttl(props.getTtl()).send().get();
- } catch (IOException | TimeoutException | EtcdException e) {
- ReflectionUtils.rethrowRuntimeException(e);
- }
- }
-
- private String getServiceKey(String appName, String serviceId) {
- return getAppKey(appName) + "/" + serviceId;
- }
-
- public String getAppKey(String appName) {
- return props.getDiscoveryPrefix() + "/" + appName;
- }
-
- class Service {
- String appName;
- String id;
- Integer port;
-
- public Service() {
- }
-
- public Service(String appName, String id, Integer port) {
- this.appName = appName;
- this.id = id;
- this.port = port;
- }
-
- public String getAppName() {
- return appName;
- }
-
- public void setAppName(String appName) {
- this.appName = appName;
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public Integer getPort() {
- return port;
- }
-
- public void setPort(Integer port) {
- this.port = port;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Service service = (Service) o;
-
- if (appName != null ? !appName.equals(service.appName) : service.appName != null) return false;
- if (id != null ? !id.equals(service.id) : service.id != null) return false;
- return port != null ? port.equals(service.port) : service.port == null;
- }
-
- @Override
- public int hashCode() {
- int result = appName != null ? appName.hashCode() : 0;
- result = 31 * result + (id != null ? id.hashCode() : 0);
- result = 31 * result + (port != null ? port.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return String.format("Service{appName='%s', id='%s', port=%d}", appName, id, port);
- }
- }
-
- @Override
- protected int getConfiguredPort() {
- return service.getPort() == null? 0 : service.getPort();
- }
-
- @Override
- protected void setConfiguredPort(int port) {
- service.setPort(port);
- }
-
- @Override
- protected EtcdDiscoveryProperties getConfiguration() {
- return props;
- }
-
- @Override
- protected void deregister() {
- deregister(getAppName(), getContext().getId());
- }
-
- @Override
- protected void deregisterManagement() {
- deregister(getAppName(), getManagementServiceName());
- }
-
- private void deregister(String appName, String serviceId) {
- try {
- etcd.delete(getServiceKey(appName, serviceId)).send();
- } catch (IOException e) {
- ReflectionUtils.rethrowRuntimeException(e);
- }
- }
-
- @Override
- protected boolean isEnabled() {
- return props.isEnabled();
- }
-
- public EtcdClient getEtcd() {
- return etcd;
- }
-
- public EtcdDiscoveryProperties getProps() {
- return props;
- }
-
- public Service getService() {
- return service;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- EtcdLifecycle that = (EtcdLifecycle) o;
-
- if (etcd != null ? !etcd.equals(that.etcd) : that.etcd != null) return false;
- if (props != null ? !props.equals(that.props) : that.props != null) return false;
- if (service != null ? !service.equals(that.service) : that.service != null) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = etcd != null ? etcd.hashCode() : 0;
- result = 31 * result + (props != null ? props.hashCode() : 0);
- result = 31 * result + (service != null ? service.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return String.format("EtcdLifecycle{etcd=%s, props=%s, service=%s}", etcd, props, service);
- }
-}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdRegistration.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdRegistration.java
new file mode 100644
index 0000000..11dbcc5
--- /dev/null
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdRegistration.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2017 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.etcd.discovery;
+
+import javax.servlet.ServletContext;
+
+import org.springframework.cloud.client.discovery.ManagementServerPortUtils;
+import org.springframework.cloud.client.serviceregistry.Registration;
+import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.Environment;
+
+/**
+ * @author Venil Noronha
+ */
+public class EtcdRegistration implements Registration {
+
+ public static final char SEPARATOR = ':';
+ public static final String MANAGEMENT = "management";
+
+ private final Service service;
+ private final EtcdDiscoveryProperties properties;
+ private final ApplicationContext context;
+ private String instanceId;
+
+ public EtcdRegistration(Service service, EtcdDiscoveryProperties properties, ApplicationContext context) {
+ this.service = service;
+ this.properties = properties;
+ this.context = context;
+
+ // cache instanceId, so on refresh this won't get recomputed
+ // this is a problem if ${random.value} is used
+ this.instanceId = EtcdRegistration.getServiceId(context);
+ }
+
+ public String getInstanceId() {
+ return this.instanceId;
+ }
+
+ public void initializePort(int knownPort) {
+ if (getService().getPort() == null) {
+ // not set by properties
+ getService().setPort(knownPort);
+ }
+ }
+
+ public EtcdRegistration managementRegistration() {
+ return managementRegistration(this.properties, this.context);
+ }
+
+ public static EtcdRegistration registration(EtcdDiscoveryProperties properties, ApplicationContext context,
+ ServletContext servletContext) {
+ Service service = new Service();
+ service.setAppName(getAppName(properties, context.getEnvironment()));
+ service.setId(getServiceId(context));
+
+ return new EtcdRegistration(service, properties, context);
+ }
+
+ public static EtcdRegistration managementRegistration(EtcdDiscoveryProperties properties,
+ ApplicationContext context) {
+ Service management = new Service();
+ management.setAppName(getManagementServiceName(properties, context.getEnvironment()));
+ management.setId(getManagementServiceId(properties, context));
+ management.setPort(getManagementPort(properties, context));
+
+ return new EtcdRegistration(management, properties, context);
+ }
+
+ public String getServiceId() {
+ return this.service.getId();
+ }
+
+ public static String getServiceId(ApplicationContext context) {
+ return context.getId();
+ }
+
+ /**
+ * @return the app name, currently the spring.application.name property
+ */
+ public static String getAppName(EtcdDiscoveryProperties properties, Environment environment) {
+ return environment.getProperty("spring.application.name", "application");
+ }
+
+ /**
+ * @return if the management service should be registered with the
+ * {@link ServiceRegistry}
+ */
+ public static boolean shouldRegisterManagement(EtcdDiscoveryProperties properties, ApplicationContext context) {
+ return getManagementPort(properties, context) != null && ManagementServerPortUtils.isDifferent(context);
+ }
+
+ /**
+ * @return the serviceId of the Management Service
+ */
+ public static String getManagementServiceId(EtcdDiscoveryProperties properties, ApplicationContext context) {
+ return context.getId() + SEPARATOR + MANAGEMENT;
+ }
+
+ /**
+ * @return the service name of the Management Service
+ */
+ public static String getManagementServiceName(EtcdDiscoveryProperties properties,
+ Environment environment) {
+ return getAppName(properties, environment) + SEPARATOR + MANAGEMENT;
+ }
+
+ /**
+ * @return the port of the Management Service
+ */
+ public static Integer getManagementPort(EtcdDiscoveryProperties properties, ApplicationContext context) {
+ return ManagementServerPortUtils.getPort(context);
+ }
+
+ public Service getService() {
+ return service;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ result = prime * result + ((service == null) ? 0 : service.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ EtcdRegistration other = (EtcdRegistration) obj;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+ if (service == null) {
+ if (other.service != null)
+ return false;
+ } else if (!service.equals(other.service))
+ return false;
+ return true;
+ }
+
+}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServerList.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServerList.java
index f90f50d..c066d25 100644
--- a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServerList.java
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServerList.java
@@ -19,6 +19,7 @@
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
import mousio.etcd4j.EtcdClient;
+import mousio.etcd4j.responses.EtcdAuthenticationException;
import mousio.etcd4j.responses.EtcdException;
import mousio.etcd4j.responses.EtcdKeysResponse;
import mousio.etcd4j.responses.EtcdKeysResponse.EtcdNode;
@@ -90,7 +91,7 @@ private List getServers() {
EtcdServer server = new EtcdServer(appInfo[0], appInfo[1], strings[0], strings[1]);
servers.add(server);
}
- } catch (IOException | TimeoutException | EtcdException e) {
+ } catch (IOException | TimeoutException | EtcdException | EtcdAuthenticationException e) {
ReflectionUtils.rethrowRuntimeException(e);
}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServiceRegistry.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServiceRegistry.java
new file mode 100644
index 0000000..0918124
--- /dev/null
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServiceRegistry.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017 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.etcd.discovery;
+
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import mousio.etcd4j.responses.EtcdAuthenticationException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
+import org.springframework.util.ReflectionUtils;
+
+import mousio.etcd4j.EtcdClient;
+import mousio.etcd4j.responses.EtcdException;
+
+/**
+ * @author Venil Noronha
+ */
+public class EtcdServiceRegistry implements ServiceRegistry {
+
+ private static Log log = LogFactory.getLog(EtcdServiceRegistry.class);
+
+ private final EtcdClient client;
+ private final EtcdDiscoveryProperties properties;
+ private final HeartbeatScheduler heartbeatScheduler;
+
+ public EtcdServiceRegistry(EtcdClient client, EtcdDiscoveryProperties properties, HeartbeatScheduler heartbeatScheduler) {
+ this.client = client;
+ this.properties = properties;
+ this.heartbeatScheduler = heartbeatScheduler;
+ }
+
+ @Override
+ public void register(EtcdRegistration reg) {
+ Service service = reg.getService();
+ log.info("Registering service with etcd: " + service);
+ try {
+ heartbeatScheduler.register(service);
+ }
+ catch (IOException | EtcdException | TimeoutException | EtcdAuthenticationException e) {
+ ReflectionUtils.rethrowRuntimeException(e);
+ }
+ heartbeatScheduler.add(service);
+ }
+
+ private String getServiceKey(String appName, String serviceId) {
+ return getAppKey(appName) + "/" + serviceId;
+ }
+
+ public String getAppKey(String appName) {
+ return properties.getDiscoveryPrefix() + "/" + appName;
+ }
+
+ @Override
+ public void deregister(EtcdRegistration reg) {
+ Service service = reg.getService();
+ heartbeatScheduler.remove(service.getId());
+ if (log.isInfoEnabled()) {
+ log.info("Deregistering service with etcd: " + service);
+ }
+ try {
+ client.delete(getServiceKey(service.getAppName(), service.getId())).send();
+ }
+ catch (IOException e) {
+ ReflectionUtils.rethrowRuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public void setStatus(EtcdRegistration registration, String status) {
+
+ }
+
+ @Override
+ public Object getStatus(EtcdRegistration registration) {
+ return null;
+ }
+
+}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServiceRegistryAutoConfiguration.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServiceRegistryAutoConfiguration.java
new file mode 100644
index 0000000..410f1a8
--- /dev/null
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/EtcdServiceRegistryAutoConfiguration.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 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.etcd.discovery;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.etcd.discovery.EtcdDiscoveryProperties;
+import org.springframework.cloud.etcd.discovery.HeartbeatScheduler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import mousio.etcd4j.EtcdClient;
+
+/**
+ * @author Venil Noronha
+ */
+@Configuration
+@ConditionalOnProperty(value = "spring.cloud.service-registry.enabled", matchIfMissing = true)
+public class EtcdServiceRegistryAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public EtcdServiceRegistry etcdServiceRegistry(EtcdClient etcdClient, EtcdDiscoveryProperties properties,
+ HeartbeatScheduler heartbeatScheduler) {
+ return new EtcdServiceRegistry(etcdClient, properties, heartbeatScheduler);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public HeartbeatScheduler heartbeatScheduler(EtcdClient etcdClient, EtcdDiscoveryProperties properties) {
+ return new HeartbeatScheduler(etcdClient, properties);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public EtcdDiscoveryProperties etcdDiscoveryProperties() {
+ return new EtcdDiscoveryProperties();
+ }
+
+}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/HeartbeatScheduler.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/HeartbeatScheduler.java
new file mode 100644
index 0000000..cacd61a
--- /dev/null
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/HeartbeatScheduler.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 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.etcd.discovery;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeoutException;
+
+import mousio.etcd4j.responses.EtcdAuthenticationException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
+
+import mousio.etcd4j.EtcdClient;
+import mousio.etcd4j.responses.EtcdException;
+
+/**
+ * @author Venil Noronha
+ */
+public class HeartbeatScheduler {
+
+ private static Log log = LogFactory.getLog(HeartbeatScheduler.class);
+
+ private final Map serviceHeartbeats = new ConcurrentHashMap<>();
+
+ private final TaskScheduler scheduler = new ConcurrentTaskScheduler(Executors.newSingleThreadScheduledExecutor());
+
+ private EtcdClient client;
+
+ private EtcdDiscoveryProperties properties;
+
+ public HeartbeatScheduler(EtcdClient client, EtcdDiscoveryProperties properties) {
+ this.client = client;
+ this.properties = properties;
+ }
+
+ public void add(Service service) {
+ ScheduledFuture task = scheduler.scheduleAtFixedRate(new EtcdHeartbeatTask(service),
+ properties.getHeartbeatInterval());
+ ScheduledFuture previousTask = serviceHeartbeats.put(service.getId(), task);
+ if (previousTask != null) {
+ previousTask.cancel(true);
+ }
+ }
+
+ public void remove(String serviceId) {
+ ScheduledFuture task = serviceHeartbeats.get(serviceId);
+ if (task != null) {
+ task.cancel(true);
+ }
+ serviceHeartbeats.remove(serviceId);
+ }
+
+ public void register(Service service) throws IOException, EtcdException, TimeoutException, EtcdAuthenticationException {
+ String key = getServiceKey(service.getAppName(), service.getId());
+ // TODO: what should be serialized about the service?
+ String value = properties.getHostname() + ":" + service.getPort();
+ client.put(key, value).ttl(properties.getTtl()).send().get();
+ }
+
+ private String getServiceKey(String appName, String serviceId) {
+ return getAppKey(appName) + "/" + serviceId;
+ }
+
+ public String getAppKey(String appName) {
+ return properties.getDiscoveryPrefix() + "/" + appName;
+ }
+
+ private class EtcdHeartbeatTask implements Runnable {
+
+ private Service service;
+
+ EtcdHeartbeatTask(Service service) {
+ this.service = service;
+ }
+
+ @Override
+ public void run() {
+ log.debug("Sending etcd heartbeat for: " + service.getId());
+ try {
+ register(service);
+ }
+ catch (IOException | EtcdException | TimeoutException | EtcdAuthenticationException e) {
+ log.error("Failed to send etcd heartbeat for: " + service.getId(), e);
+ }
+ }
+
+ }
+
+}
diff --git a/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/Service.java b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/Service.java
new file mode 100644
index 0000000..3d88146
--- /dev/null
+++ b/spring-cloud-etcd-discovery/src/main/java/org/springframework/cloud/etcd/discovery/Service.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017 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.etcd.discovery;
+
+/**
+ * @author Venil Noronha
+ */
+public class Service {
+
+ private String appName;
+ private String id;
+ private Integer port;
+
+ public Service() {
+ }
+
+ public Service(String appName, String id, Integer port) {
+ this.appName = appName;
+ this.id = id;
+ this.port = port;
+ }
+
+ public String getAppName() {
+ return appName;
+ }
+
+ public void setAppName(String appName) {
+ this.appName = appName;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ Service service = (Service) o;
+
+ if (appName != null ? !appName.equals(service.appName) : service.appName != null)
+ return false;
+ if (id != null ? !id.equals(service.id) : service.id != null)
+ return false;
+ return port != null ? port.equals(service.port) : service.port == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = appName != null ? appName.hashCode() : 0;
+ result = 31 * result + (id != null ? id.hashCode() : 0);
+ result = 31 * result + (port != null ? port.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Service{appName='%s', id='%s', port=%d}", appName, id, port);
+ }
+
+}
diff --git a/spring-cloud-etcd-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-etcd-discovery/src/main/resources/META-INF/spring.factories
index daaf880..e3b7b23 100644
--- a/spring-cloud-etcd-discovery/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-etcd-discovery/src/main/resources/META-INF/spring.factories
@@ -1,5 +1,7 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-org.springframework.cloud.etcd.discovery.RibbonEtcdAutoConfiguration
+org.springframework.cloud.etcd.discovery.RibbonEtcdAutoConfiguration,\
+org.springframework.cloud.etcd.discovery.EtcdAutoServiceRegistrationAutoConfiguration,\
+org.springframework.cloud.etcd.discovery.EtcdServiceRegistryAutoConfiguration
# Discovery Client Configuration
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
diff --git a/spring-cloud-etcd-sample/pom.xml b/spring-cloud-etcd-sample/pom.xml
index 9660c86..119b192 100644
--- a/spring-cloud-etcd-sample/pom.xml
+++ b/spring-cloud-etcd-sample/pom.xml
@@ -11,7 +11,7 @@
org.springframework.cloud
spring-cloud-etcd
- 1.0.0.BUILD-SNAPSHOT
+ 2.0.0.BUILD-SNAPSHOT
..
@@ -20,7 +20,7 @@
org.springframework.boot
spring-boot-maven-plugin
- 1.2.4.RELEASE
+ 2.0.0.RELEASE
diff --git a/spring-cloud-etcd-sample/src/main/java/org/springframework/cloud/etcd/sample/SampleEtcdApplication.java b/spring-cloud-etcd-sample/src/main/java/org/springframework/cloud/etcd/sample/SampleEtcdApplication.java
index 94d31b3..e6d2283 100644
--- a/spring-cloud-etcd-sample/src/main/java/org/springframework/cloud/etcd/sample/SampleEtcdApplication.java
+++ b/spring-cloud-etcd-sample/src/main/java/org/springframework/cloud/etcd/sample/SampleEtcdApplication.java
@@ -19,7 +19,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@@ -50,9 +49,6 @@ public class SampleEtcdApplication {
@Autowired
private Environment env;
- @Autowired(required = false)
- private RelaxedPropertyResolver resolver;
-
@RequestMapping("/me")
public ServiceInstance me() {
return discoveryClient.getLocalServiceInstance();
@@ -65,8 +61,7 @@ public ServiceInstance lb() {
@RequestMapping("/myenv")
public String env(@RequestParam("prop") String prop) {
- String property = new RelaxedPropertyResolver(env).getProperty(prop, "Not Found");
- return property;
+ return env.getProperty(prop, "Not Found");
}
@RequestMapping("/all")
diff --git a/src/main/bash/travis_install_etcd.sh b/src/main/bash/travis_install_etcd.sh
index 984e482..45889e8 100755
--- a/src/main/bash/travis_install_etcd.sh
+++ b/src/main/bash/travis_install_etcd.sh
@@ -3,7 +3,7 @@
#!/usr/bin/env bash
ETCD_URL="https://github.com/coreos/etcd/releases/download"
-ETCD_VER="v2.2.0"
+ETCD_VER="v3.3.1"
ETCD_ARC="linux-amd64"
# cleanup