remove dependency on s-c-config-client.

bump version to 1.0.0.BUILD-SNAPSHOT.
form java files
This commit is contained in:
Spencer Gibb
2015-03-19 19:35:09 -06:00
parent 5855752b99
commit c8a9d40547
38 changed files with 814 additions and 781 deletions

View File

@@ -2,6 +2,19 @@
Preview of Spring Cloud Consul implementation
### Short consul overview
consul does
* distributed configuration
* service registration and discovery
* messaging
* distributed locking and sessions
* supports multiple data centers
* has a slick ui
See the [intro](https://consul.io/intro/index.html) for more information.
### Running the sample
1. [Install consul](https://consul.io/downloads.html)

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath />
</parent>
<artifactId>spring-cloud-consul-docs</artifactId>

26
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Consul</name>
<description>Spring Cloud Consul</description>
@@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
@@ -61,32 +61,32 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-core</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-bus</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-config</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-discovery</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
@@ -112,8 +112,8 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<artifactId>spring-cloud-context</artifactId>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ecwid.consul</groupId>
@@ -141,12 +141,12 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-sidecar</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.netflix.eureka</groupId>
@@ -161,7 +161,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-core</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.consul.bus;
import com.ecwid.consul.v1.ConsulClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -34,6 +32,9 @@ import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
import org.springframework.messaging.MessageChannel;
import org.springframework.scheduling.annotation.EnableScheduling;
import com.ecwid.consul.v1.ConsulClient;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Spencer Gibb
*/
@@ -43,47 +44,48 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@AutoConfigureAfter(BusAutoConfiguration.class)
@EnableScheduling
public class ConsulBusAutoConfiguration {
@Autowired
@Qualifier("cloudBusInboundChannel") MessageChannel cloudBusInboundChannel;
@Autowired
@Qualifier("cloudBusInboundChannel")
MessageChannel cloudBusInboundChannel;
@Autowired
ObjectMapper objectMapper;
@Autowired
ObjectMapper objectMapper;
@Bean
public EventService eventService() {
return new EventService();
}
@Bean
public EventService eventService() {
return new EventService();
}
@Bean
public ConsulOutboundEndpoint consulOutboundEndpoint() {
return new ConsulOutboundEndpoint();
}
@Bean
public ConsulOutboundEndpoint consulOutboundEndpoint() {
return new ConsulOutboundEndpoint();
}
@Bean
public IntegrationFlow cloudBusConsulOutboundFlow(
@Qualifier("cloudBusOutboundChannel") MessageChannel cloudBusOutboundChannel) {
return IntegrationFlows
.from(cloudBusOutboundChannel)
//TODO: put the json headers as part of the message, here?
.transform(Transformers.toJson())
.handle(consulOutboundEndpoint())
.get();
}
@Bean
public IntegrationFlow cloudBusConsulOutboundFlow(
@Qualifier("cloudBusOutboundChannel") MessageChannel cloudBusOutboundChannel) {
return IntegrationFlows.from(cloudBusOutboundChannel)
// TODO: put the json headers as part of the message, here?
.transform(Transformers.toJson()).handle(consulOutboundEndpoint()).get();
}
@Bean
public IntegrationFlow cloudBusConsulInboundFlow() {
return IntegrationFlows
.from(consulInboundChannelAdapter())
.transform(Transformers.fromJson(RemoteApplicationEvent.class, new Jackson2JsonObjectMapper(objectMapper)))
.channel(cloudBusInboundChannel) // now set in consulInboundChannelAdapter bean
.get();
}
@Bean
public IntegrationFlow cloudBusConsulInboundFlow() {
return IntegrationFlows
.from(consulInboundChannelAdapter())
.transform(
Transformers.fromJson(RemoteApplicationEvent.class,
new Jackson2JsonObjectMapper(objectMapper)))
.channel(cloudBusInboundChannel) // now set in consulInboundChannelAdapter
// bean
.get();
}
@Bean
public ConsulInboundChannelAdapter consulInboundChannelAdapter() {
ConsulInboundChannelAdapter adapter = new ConsulInboundChannelAdapter();
adapter.setOutputChannel(cloudBusInboundChannel);
return adapter;
}
@Bean
public ConsulInboundChannelAdapter consulInboundChannelAdapter() {
ConsulInboundChannelAdapter adapter = new ConsulInboundChannelAdapter();
adapter.setOutputChannel(cloudBusInboundChannel);
return adapter;
}
}

View File

@@ -28,50 +28,51 @@ import org.springframework.scheduling.annotation.Scheduled;
import com.ecwid.consul.v1.event.model.Event;
/**
* Adapter that receives Messages from Consul Events, converts them into
* Spring Integration Messages, and sends the results to a Message Channel.
* Adapter that receives Messages from Consul Events, converts them into Spring
* Integration Messages, and sends the results to a Message Channel.
* @author Spencer Gibb
*/
public class ConsulInboundChannelAdapter extends MessageProducerSupport {
@Autowired
private EventService eventService;
@Autowired
private EventService eventService;
public ConsulInboundChannelAdapter() {
}
public ConsulInboundChannelAdapter() {
}
//link eventService to sendMessage
/*
Map<String, Object> headers = headerMapper.toHeadersFromRequest(message.getMessageProperties());
if (messageListenerContainer.getAcknowledgeMode() == AcknowledgeMode.MANUAL) {
headers.put(AmqpHeaders.DELIVERY_TAG, message.getMessageProperties().getDeliveryTag());
headers.put(AmqpHeaders.CHANNEL, channel);
}
sendMessage(AmqpInboundChannelAdapter.this.getMessageBuilderFactory().withPayload(payload).copyHeaders(headers).build());*/
// link eventService to sendMessage
/*
* Map<String, Object> headers =
* headerMapper.toHeadersFromRequest(message.getMessageProperties()); if
* (messageListenerContainer.getAcknowledgeMode() == AcknowledgeMode.MANUAL) {
* headers.put(AmqpHeaders.DELIVERY_TAG,
* message.getMessageProperties().getDeliveryTag()); headers.put(AmqpHeaders.CHANNEL,
* channel); }
* sendMessage(AmqpInboundChannelAdapter.this.getMessageBuilderFactory().withPayload
* (payload).copyHeaders(headers).build());
*/
//start thread
//make blocking calls
//foreach event -> send message
// start thread
// make blocking calls
// foreach event -> send message
@Override
protected void doStart() {
}
@Override
protected void doStart() {
}
@Scheduled(fixedDelayString = "10")
public void getEvents() throws IOException {
List<Event> events = eventService.watch();
for (Event event : events) {
// Map<String, Object> headers = new HashMap<>();
// headers.put(MessageHeaders.REPLY_CHANNEL, outputChannel.)
String decoded = new String(decodeFromString(event.getPayload()));
sendMessage(getMessageBuilderFactory().withPayload(decoded)
// TODO: support headers
.build());
}
}
@Scheduled(fixedDelayString = "10")
public void getEvents() throws IOException {
List<Event> events = eventService.watch();
for (Event event : events) {
//Map<String, Object> headers = new HashMap<>();
//headers.put(MessageHeaders.REPLY_CHANNEL, outputChannel.)
String decoded = new String(decodeFromString(event.getPayload()));
sendMessage(getMessageBuilderFactory()
.withPayload(decoded)
//TODO: support headers
.build());
}
}
@Override
protected void doStop() {
}
@Override
protected void doStop() {
}
}

View File

@@ -16,14 +16,15 @@
package org.springframework.cloud.consul.bus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.messaging.Message;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.event.model.Event;
import com.ecwid.consul.v1.event.model.EventParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.messaging.Message;
/**
* Adapter that converts and sends Messages as Consul events
@@ -31,16 +32,17 @@ import org.springframework.messaging.Message;
*/
public class ConsulOutboundEndpoint extends AbstractReplyProducingMessageHandler {
@Autowired
protected ConsulClient consul;
@Autowired
protected ConsulClient consul;
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
Object payload = requestMessage.getPayload();
//TODO: support headers
//TODO: support consul event filters: NodeFilter, ServiceFilter, TagFilter
Response<Event> event = consul.eventFire("springCloudBus", (String) payload, new EventParams(), QueryParams.DEFAULT);
//TODO: return event?
return null;
}
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
Object payload = requestMessage.getPayload();
// TODO: support headers
// TODO: support consul event filters: NodeFilter, ServiceFilter, TagFilter
Response<Event> event = consul.eventFire("springCloudBus", (String) payload,
new EventParams(), QueryParams.DEFAULT);
// TODO: return event?
return null;
}
}

View File

@@ -30,116 +30,109 @@ import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.event.model.Event;
import com.ecwid.consul.v1.event.model.EventParams;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Spencer Gibb
*/
public class EventService {
@Autowired
protected ConsulClient consul;
@Autowired
protected ConsulClient consul;
@Autowired(required = false)
protected ObjectMapper objectMapper = new ObjectMapper();
@Autowired(required = false)
protected ObjectMapper objectMapper = new ObjectMapper();
private AtomicReference<BigInteger> lastIndex = new AtomicReference<>();
private AtomicReference<BigInteger> lastIndex = new AtomicReference<>();
@PostConstruct
public void init() {
setLastIndex(getEventsResponse());
}
@PostConstruct
public void init() {
setLastIndex(getEventsResponse());
}
private void setLastIndex(Response<?> response) {
Long consulIndex = response.getConsulIndex();
if (consulIndex != null) {
lastIndex.set(BigInteger.valueOf(consulIndex));
}
}
private void setLastIndex(Response<?> response) {
Long consulIndex = response.getConsulIndex();
if (consulIndex != null) {
lastIndex.set(BigInteger.valueOf(consulIndex));
}
}
public BigInteger getLastIndex() {
return lastIndex.get();
}
public BigInteger getLastIndex() {
return lastIndex.get();
}
public Event fire(String name, String payload) {
Response<Event> response = consul.eventFire(name, payload, new EventParams(), QueryParams.DEFAULT);
return response.getValue();
}
public Event fire(String name, String payload) {
Response<Event> response = consul.eventFire(name, payload, new EventParams(),
QueryParams.DEFAULT);
return response.getValue();
}
public Response<List<Event>> getEventsResponse() {
return consul.eventList(QueryParams.DEFAULT);
}
public Response<List<Event>> getEventsResponse() {
return consul.eventList(QueryParams.DEFAULT);
}
public List<Event> getEvents() {
return getEventsResponse().getValue();
}
public List<Event> getEvents() {
return getEventsResponse().getValue();
}
/**
* from https://github.com/hashicorp/consul/blob/master/api/event.go#L90-L104
// IDToIndex is a bit of a hack. This simulates the index generation to
// convert an event ID into a WaitIndex.
func (e *Event) IDToIndex(uuid string) uint64 {
lower := uuid[0:8] + uuid[9:13] + uuid[14:18]
upper := uuid[19:23] + uuid[24:36]
lowVal, err := strconv.ParseUint(lower, 16, 64)
if err != nil {
panic("Failed to convert " + lower)
}
highVal, err := strconv.ParseUint(upper, 16, 64)
if err != nil {
panic("Failed to convert " + upper)
}
return lowVal ^ highVal
//^ bitwise XOR integers
}
*/
public BigInteger toIndex(String eventId) {
String lower = eventId.substring(0, 8) + eventId.substring(9, 13) + eventId.substring(14, 18);
String upper = eventId.substring(19, 23) + eventId.substring(24, 36);
BigInteger lowVal = new BigInteger(lower, 16);
BigInteger highVal = new BigInteger(upper, 16);
BigInteger index = lowVal.xor(highVal);
return index;
}
/**
* from https://github.com/hashicorp/consul/blob/master/api/event.go#L90-L104 //
* IDToIndex is a bit of a hack. This simulates the index generation to // convert an
* event ID into a WaitIndex. func (e *Event) IDToIndex(uuid string) uint64 { lower :=
* uuid[0:8] + uuid[9:13] + uuid[14:18] upper := uuid[19:23] + uuid[24:36] lowVal, err
* := strconv.ParseUint(lower, 16, 64) if err != nil { panic("Failed to convert " +
* lower) } highVal, err := strconv.ParseUint(upper, 16, 64) if err != nil {
* panic("Failed to convert " + upper) } return lowVal ^ highVal //^ bitwise XOR
* integers }
*/
public BigInteger toIndex(String eventId) {
String lower = eventId.substring(0, 8) + eventId.substring(9, 13)
+ eventId.substring(14, 18);
String upper = eventId.substring(19, 23) + eventId.substring(24, 36);
BigInteger lowVal = new BigInteger(lower, 16);
BigInteger highVal = new BigInteger(upper, 16);
BigInteger index = lowVal.xor(highVal);
return index;
}
public List<Event> getEvents(BigInteger lastIndex) {
return filterEvents(readEvents(getEventsResponse()), lastIndex);
}
public List<Event> getEvents(BigInteger lastIndex) {
return filterEvents(readEvents(getEventsResponse()), lastIndex);
}
public List<Event> watch() {
return watch(lastIndex.get());
}
public List<Event> watch() {
return watch(lastIndex.get());
}
public List<Event> watch(BigInteger lastIndex) {
//TODO: parameterized or configurable watch time
long index = -1;
if (lastIndex != null) {
index = lastIndex.longValue();
}
Response<List<Event>> watch = consul.eventList(new QueryParams(2, index));
return filterEvents(readEvents(watch), lastIndex);
}
public List<Event> watch(BigInteger lastIndex) {
// TODO: parameterized or configurable watch time
long index = -1;
if (lastIndex != null) {
index = lastIndex.longValue();
}
Response<List<Event>> watch = consul.eventList(new QueryParams(2, index));
return filterEvents(readEvents(watch), lastIndex);
}
protected List<Event> readEvents(Response<List<Event>> response) {
setLastIndex(response);
return response.getValue();
}
protected List<Event> readEvents(Response<List<Event>> response) {
setLastIndex(response);
return response.getValue();
}
/**
* from https://github.com/hashicorp/consul/blob/master/watch/funcs.go#L169-L194
*/
protected List<Event> filterEvents(List<Event> toFilter, BigInteger lastIndex) {
List<Event> events = toFilter;
if (lastIndex != null) {
for (int i = 0; i < events.size(); i++) {
Event event = events.get(i);
BigInteger eventIndex = toIndex(event.getId());
if (eventIndex.equals(lastIndex)) {
events = events.subList(i + 1, events.size());
break;
}
}
}
return events;
}
/**
* from https://github.com/hashicorp/consul/blob/master/watch/funcs.go#L169-L194
*/
protected List<Event> filterEvents(List<Event> toFilter, BigInteger lastIndex) {
List<Event> events = toFilter;
if (lastIndex != null) {
for (int i = 0; i < events.size(); i++) {
Event event = events.get(i);
BigInteger eventIndex = toIndex(event.getId());
if (eventIndex.equals(lastIndex)) {
events = events.subList(i + 1, events.size());
break;
}
}
}
return events;
}
}

View File

@@ -16,10 +16,12 @@
package org.springframework.cloud.consul.bus;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
import com.fasterxml.jackson.annotation.JsonTypeName;
/**
* @author Spencer Gibb
*/
@@ -27,17 +29,19 @@ import org.springframework.cloud.bus.event.RemoteApplicationEvent;
@Data
public class SimpleRemoteEvent extends RemoteApplicationEvent {
private String message;
private String message;
private SimpleRemoteEvent(){}
private SimpleRemoteEvent() {
}
public SimpleRemoteEvent(Object source, String originService, String destinationService, String message) {
super(source, originService, destinationService);
this.message = message;
}
public SimpleRemoteEvent(Object source, String originService,
String destinationService, String message) {
super(source, originService, destinationService);
this.message = message;
}
public SimpleRemoteEvent(Object source, String originService, String message) {
super(source, originService);
this.message = message;
}
public SimpleRemoteEvent(Object source, String originService, String message) {
super(source, originService);
this.message = message;
}
}

View File

@@ -16,7 +16,9 @@
package org.springframework.cloud.consul.bus;
import com.fasterxml.jackson.databind.ObjectMapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@@ -37,88 +39,90 @@ import org.springframework.integration.support.json.Jackson2JsonObjectMapper;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import static org.junit.Assert.*;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Spencer Gibb
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ConsulBusIT {
@Test
public void test001ConsulOutboundEndpoint_HandleRequestMessage() {
ConfigurableApplicationContext context = getOutboundContext();
context.publishEvent(new SimpleRemoteEvent(this, "testService", "testMessage"));
}
@Test
public void test001ConsulOutboundEndpoint_HandleRequestMessage() {
ConfigurableApplicationContext context = getOutboundContext();
context.publishEvent(new SimpleRemoteEvent(this, "testService", "testMessage"));
}
private ConfigurableApplicationContext getOutboundContext() {
System.setProperty("spring.cloud.config.enabled", "false");
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.web(false)
.sources(OutboundConfig.class)
.run();
context.setId("testService");
return context;
}
private ConfigurableApplicationContext getOutboundContext() {
System.setProperty("spring.cloud.config.enabled", "false");
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.web(false).sources(OutboundConfig.class).run();
context.setId("testService");
return context;
}
/*@Test
public void test002ConsulInboundChannelAdapter() {
ConfigurableApplicationContext inbound = getInboundContext();
ConfigurableApplicationContext outbound = getOutboundContext();
outbound.publishEvent(new TestMessage(this, "testService", "inboundTestService", "testMessage"));
/*
* @Test public void test002ConsulInboundChannelAdapter() {
* ConfigurableApplicationContext inbound = getInboundContext();
* ConfigurableApplicationContext outbound = getOutboundContext();
* outbound.publishEvent(new TestMessage(this, "testService", "inboundTestService",
* "testMessage"));
*
* InboundConfig inboundConfig = inbound.getBean(InboundConfig.class);
* assertNotNull("message was null", inboundConfig.message); }
*
* private ConfigurableApplicationContext getInboundContext() {
* System.setProperty("spring.cloud.config.enabled", "false");
* ConfigurableApplicationContext context = new SpringApplicationBuilder() .web(false)
* .sources(InboundConfig.class) .run(); context.setId("inboundTestService"); return
* context; }
*/
InboundConfig inboundConfig = inbound.getBean(InboundConfig.class);
assertNotNull("message was null", inboundConfig.message);
}
protected static final String JSON_PAYLOAD = "{\"type\":\"simple\",\"timestamp\":1416349427372,\"originService\":\"testService\",\"destinationService\":null,\"message\":\"testMessage\"}";
private ConfigurableApplicationContext getInboundContext() {
System.setProperty("spring.cloud.config.enabled", "false");
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.web(false)
.sources(InboundConfig.class)
.run();
context.setId("inboundTestService");
return context;
}*/
@Test
public void test003JsonToObject() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SubtypeModule(SimpleRemoteEvent.class));
JsonToObjectTransformer transformer = Transformers.fromJson(
RemoteApplicationEvent.class, new Jackson2JsonObjectMapper(objectMapper));
/*
* HashMap<String, Object> map = new HashMap<>(); map.put(JsonHeaders.TYPE_ID,
* RemoteApplicationEvent.class);
*/
Message<?> message = transformer.transform(new GenericMessage<>(JSON_PAYLOAD));
Object payload = message.getPayload();
assertTrue("payload is of wrong type", payload instanceof RemoteApplicationEvent);
assertTrue("payload is of wrong type", payload instanceof SimpleRemoteEvent);
SimpleRemoteEvent event = (SimpleRemoteEvent) payload;
assertEquals("payload is wrong", "testMessage", event.getMessage());
}
protected static final String JSON_PAYLOAD = "{\"type\":\"simple\",\"timestamp\":1416349427372,\"originService\":\"testService\",\"destinationService\":null,\"message\":\"testMessage\"}";
@Configuration
@Import({ ConsulAutoConfiguration.class, BusAutoConfiguration.class,
ConsulBusAutoConfiguration.class })
@EnableIntegration
public static class OutboundConfig {
@Test
public void test003JsonToObject() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SubtypeModule(SimpleRemoteEvent.class));
JsonToObjectTransformer transformer = Transformers.fromJson(RemoteApplicationEvent.class, new Jackson2JsonObjectMapper(objectMapper));
/*HashMap<String, Object> map = new HashMap<>();
map.put(JsonHeaders.TYPE_ID, RemoteApplicationEvent.class);*/
Message<?> message = transformer.transform(new GenericMessage<>(JSON_PAYLOAD));
Object payload = message.getPayload();
assertTrue("payload is of wrong type", payload instanceof RemoteApplicationEvent);
assertTrue("payload is of wrong type", payload instanceof SimpleRemoteEvent);
SimpleRemoteEvent event = (SimpleRemoteEvent) payload;
assertEquals("payload is wrong", "testMessage", event.getMessage());
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SubtypeModule(SimpleRemoteEvent.class));
return objectMapper;
}
}
@Configuration
@Import({ConsulAutoConfiguration.class, BusAutoConfiguration.class, ConsulBusAutoConfiguration.class})
@EnableIntegration
public static class OutboundConfig {
@Configuration
@Import({ ConsulAutoConfiguration.class, BusAutoConfiguration.class,
ConsulBusAutoConfiguration.class })
@EnableIntegration
public static class InboundConfig implements
ApplicationListener<RemoteApplicationEvent> {
RemoteApplicationEvent message;
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SubtypeModule(SimpleRemoteEvent.class));
return objectMapper;
}
}
@Configuration
@Import({ConsulAutoConfiguration.class, BusAutoConfiguration.class, ConsulBusAutoConfiguration.class})
@EnableIntegration
public static class InboundConfig implements ApplicationListener<RemoteApplicationEvent> {
RemoteApplicationEvent message;
@Override
public void onApplicationEvent(RemoteApplicationEvent event) {
this.message = event;
}
}
@Override
public void onApplicationEvent(RemoteApplicationEvent event) {
this.message = event;
}
}
}

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
@@ -22,7 +22,7 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>

View File

@@ -17,7 +17,6 @@
package org.springframework.cloud.consul.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.cloud.consul.ConsulAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -35,13 +34,7 @@ public class ConsulConfigBootstrapConfiguration {
private ConfigurableEnvironment environment;
@Bean
public ConfigClientProperties configClientProperties() {
ConfigClientProperties client = new ConfigClientProperties(environment);
return client;
public ConsulPropertySourceLocator consulPropertySourceLocator() {
return new ConsulPropertySourceLocator();
}
@Bean
public ConsulPropertySourceLocator consulPropertySourceLocator() {
return new ConsulPropertySourceLocator();
}
}

View File

@@ -16,62 +16,64 @@
package org.springframework.cloud.consul.config;
import static org.springframework.util.Base64Utils.decodeFromString;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.env.EnumerablePropertySource;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.kv.model.GetValue;
import org.springframework.core.env.EnumerablePropertySource;
import java.util.*;
import static org.springframework.util.Base64Utils.*;
/**
* @author Spencer Gibb
*/
public class ConsulPropertySource extends EnumerablePropertySource<ConsulClient> {
private String context;
private String context;
private Map<String, String> properties = new LinkedHashMap<>();
private Map<String, String> properties = new LinkedHashMap<>();
public ConsulPropertySource(String context, ConsulClient source) {
super(context, source);
this.context = context;
public ConsulPropertySource(String context, ConsulClient source) {
super(context, source);
this.context = context;
if (!this.context.endsWith("/")) {
this.context = this.context + "/";
}
}
if (!this.context.endsWith("/")) {
this.context = this.context + "/";
}
}
public void init() {
Response<List<GetValue>> response = source.getKVValues(context, QueryParams.DEFAULT);
public void init() {
Response<List<GetValue>> response = source.getKVValues(context,
QueryParams.DEFAULT);
List<GetValue> values = response.getValue();
if (values != null) {
for (GetValue getValue : values) {
String key = getValue.getKey()
.replace(context, "")
.replace('/', '.');
String value = getDecoded(getValue.getValue());
properties.put(key, value);
}
}
}
if (values != null) {
for (GetValue getValue : values) {
String key = getValue.getKey().replace(context, "").replace('/', '.');
String value = getDecoded(getValue.getValue());
properties.put(key, value);
}
}
}
public String getDecoded(String value) {
if (value == null)
return null;
public String getDecoded(String value) {
if (value == null)
return null;
return new String(decodeFromString(value));
}
}
@Override
public Object getProperty(String name) {
return properties.get(name);
}
@Override
public Object getProperty(String name) {
return properties.get(name);
}
@Override
public String[] getPropertyNames() {
return properties.keySet().toArray(new String[0]);
}
@Override
public String[] getPropertyNames() {
return properties.keySet().toArray(new String[0]);
}
}

View File

@@ -16,65 +16,70 @@
package org.springframework.cloud.consul.config;
import com.ecwid.consul.v1.ConsulClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.config.client.PropertySourceLocator;
import org.springframework.cloud.consul.ConsulProperties;
import org.springframework.core.env.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.consul.ConsulProperties;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import com.ecwid.consul.v1.ConsulClient;
/**
* @author Spencer Gibb
*/
public class ConsulPropertySourceLocator implements PropertySourceLocator {
@Autowired
private ConsulClient consul;
@Autowired
private ConsulClient consul;
@Autowired
private ConsulProperties properties;
@Autowired
private ConsulProperties properties;
@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());
@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 prefix = properties.getPrefix();
List<String> contexts = new ArrayList<>();
String prefix = properties.getPrefix();
List<String> contexts = new ArrayList<>();
String defaultContext = prefix + "/application";
contexts.add(defaultContext + "/");
addProfiles(contexts, defaultContext, profiles);
String defaultContext = prefix + "/application";
contexts.add(defaultContext + "/");
addProfiles(contexts, defaultContext, profiles);
String baseContext = prefix + "/" + appName;
contexts.add(baseContext + "/");
addProfiles(contexts, baseContext, profiles);
String baseContext = prefix + "/" + appName;
contexts.add(baseContext + "/");
addProfiles(contexts, baseContext, profiles);
CompositePropertySource composite = new CompositePropertySource("consul");
CompositePropertySource composite = new CompositePropertySource("consul");
for (String propertySourceContext : contexts) {
ConsulPropertySource propertySource = create(propertySourceContext);
propertySource.init();
composite.addPropertySource(propertySource);
}
for (String propertySourceContext : contexts) {
ConsulPropertySource propertySource = create(propertySourceContext);
propertySource.init();
composite.addPropertySource(propertySource);
}
return composite;
}
return null;
}
return composite;
}
return null;
}
private ConsulPropertySource create(String context) {
return new ConsulPropertySource(context, consul);
}
private ConsulPropertySource create(String context) {
return new ConsulPropertySource(context, consul);
}
private void addProfiles(List<String> contexts, String baseContext, List<String> profiles) {
for (String profile : profiles) {
contexts.add(baseContext + "::" + profile + "/");
}
}
private void addProfiles(List<String> contexts, String baseContext,
List<String> profiles) {
for (String profile : profiles) {
contexts.add(baseContext + "::" + profile + "/");
}
}
}

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -16,12 +16,13 @@
package org.springframework.cloud.consul;
import com.ecwid.consul.v1.ConsulClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ecwid.consul.v1.ConsulClient;
/**
* @author Spencer Gibb
*/
@@ -29,27 +30,28 @@ import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties
public class ConsulAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ConsulProperties consulProperties() {
return new ConsulProperties();
}
@Bean
@ConditionalOnMissingBean
public ConsulProperties consulProperties() {
return new ConsulProperties();
}
@Bean
@ConditionalOnMissingBean
public ConsulClient consulClient() {
return new ConsulClient(consulProperties().getHost(), consulProperties().getPort());
}
@Bean
@ConditionalOnMissingBean
public ConsulClient consulClient() {
return new ConsulClient(consulProperties().getHost(), consulProperties()
.getPort());
}
@Bean
@ConditionalOnMissingBean
public ConsulEndpoint consulEndpoint() {
return new ConsulEndpoint();
}
@Bean
@ConditionalOnMissingBean
public ConsulEndpoint consulEndpoint() {
return new ConsulEndpoint();
}
@Bean
@ConditionalOnMissingBean
public ConsulHealthIndicator consulHealthIndicator() {
return new ConsulHealthIndicator();
}
@Bean
@ConditionalOnMissingBean
public ConsulHealthIndicator consulHealthIndicator() {
return new ConsulHealthIndicator();
}
}

View File

@@ -16,20 +16,22 @@
package org.springframework.cloud.consul;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.Service;
import com.ecwid.consul.v1.catalog.model.CatalogService;
import com.ecwid.consul.v1.catalog.model.Node;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author Spencer Gibb
@@ -37,43 +39,44 @@ import java.util.Map;
@ConfigurationProperties(prefix = "endpoints.consul", ignoreUnknownFields = false)
public class ConsulEndpoint extends AbstractEndpoint<ConsulEndpoint.ConsulData> {
@Autowired
private ConsulClient consul;
@Autowired
private ConsulClient consul;
@Autowired
public ConsulEndpoint() {
super("consul", false, true);
}
@Autowired
public ConsulEndpoint() {
super("consul", false, true);
}
@Override
public ConsulData invoke() {
ConsulData data = new ConsulData();
//data.setKeyValues(kvClient.getKeyValueRecurse());
Response<Map<String, Service>> agentServices = consul.getAgentServices();
data.setAgentServices(agentServices.getValue());
@Override
public ConsulData invoke() {
ConsulData data = new ConsulData();
// data.setKeyValues(kvClient.getKeyValueRecurse());
Response<Map<String, Service>> agentServices = consul.getAgentServices();
data.setAgentServices(agentServices.getValue());
Response<Map<String, List<String>>> catalogServices = consul.getCatalogServices(QueryParams.DEFAULT);
Response<Map<String, List<String>>> catalogServices = consul
.getCatalogServices(QueryParams.DEFAULT);
for (String serviceId : catalogServices.getValue().keySet()) {
Response<List<CatalogService>> response = consul.getCatalogService(serviceId,
QueryParams.DEFAULT);
data.getCatalogServices().put(serviceId, response.getValue());
}
for (String serviceId : catalogServices.getValue().keySet()) {
Response<List<CatalogService>> response = consul.getCatalogService(serviceId, QueryParams.DEFAULT);
data.getCatalogServices().put(serviceId, response.getValue());
}
Response<List<Node>> catalogNodes = consul.getCatalogNodes(QueryParams.DEFAULT);
data.setCatalogNodes(catalogNodes.getValue());
Response<List<Node>> catalogNodes = consul.getCatalogNodes(QueryParams.DEFAULT);
data.setCatalogNodes(catalogNodes.getValue());
return data;
}
return data;
}
@Data
public static class ConsulData {
Map<String, List<CatalogService>> catalogServices = new LinkedHashMap<>();
@Data
public static class ConsulData {
Map<String, List<CatalogService>> catalogServices = new LinkedHashMap<>();
Map<String, Service> agentServices;
Map<String, Service> agentServices;
List<Node> catalogNodes;
List<Node> catalogNodes;
//List<KeyValue> keyValues;
}
// List<KeyValue> keyValues;
}
}

View File

@@ -16,35 +16,37 @@
package org.springframework.cloud.consul;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.Self;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import java.util.List;
import java.util.Map;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.Self;
/**
* @author Spencer Gibb
*/
public class ConsulHealthIndicator extends AbstractHealthIndicator {
@Autowired
private ConsulClient consul;
@Autowired
private ConsulClient consul;
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
try {
Response<Self> self = consul.getAgentSelf();
Response<Map<String, List<String>>> services = consul.getCatalogServices(QueryParams.DEFAULT);
builder.up()
.withDetail("services", services.getValue())
.withDetail("agent", self.getValue());
} catch (Exception e) {
builder.down(e);
}
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
try {
Response<Self> self = consul.getAgentSelf();
Response<Map<String, List<String>>> services = consul
.getCatalogServices(QueryParams.DEFAULT);
builder.up().withDetail("services", services.getValue())
.withDetail("agent", self.getValue());
}
catch (Exception e) {
builder.down(e);
}
}
}

View File

@@ -16,31 +16,33 @@
package org.springframework.cloud.consul;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Spencer Gibb
*/
@ConfigurationProperties("consul")
@Data
public class ConsulProperties {
@NotNull
private String host = "localhost";
@NotNull
private String host = "localhost";
@NotNull
private int port = 8500;
@NotNull
private int port = 8500;
private List<String> tags = new ArrayList<>();
private List<String> tags = new ArrayList<>();
private boolean enabled = true;
private boolean enabled = true;
private String prefix = "config";
private String prefix = "config";
private List<String> managementTags = Arrays.asList("management");
private List<String> managementTags = Arrays.asList("management");
}

View File

@@ -17,22 +17,18 @@
package org.springframework.cloud.consul.model;
/**
* Gossip pool (serf) statuses.
* Created by nicu on 10.03.2015.
* Gossip pool (serf) statuses. Created by nicu on 10.03.2015.
*/
public enum SerfStatusEnum {
StatusAlive(1),
StatusLeaving(2),
StatusLeft(3),
StatusFailed(4);
private final int code;
StatusAlive(1), StatusLeaving(2), StatusLeft(3), StatusFailed(4);
private final int code;
SerfStatusEnum(int code) {
this.code=code;
}
SerfStatusEnum(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public int getCode() {
return code;
}
}

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -16,6 +16,16 @@
package org.springframework.cloud.consul.discovery;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationContext;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
@@ -23,78 +33,74 @@ import com.ecwid.consul.v1.agent.model.Member;
import com.ecwid.consul.v1.agent.model.Self;
import com.ecwid.consul.v1.agent.model.Service;
import com.ecwid.consul.v1.catalog.model.CatalogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationContext;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Spencer Gibb
*/
public class ConsulDiscoveryClient implements DiscoveryClient {
@Autowired
ApplicationContext context;
@Autowired
ApplicationContext context;
@Autowired
ConsulClient client;
@Autowired
ConsulClient client;
@Override
public String description() {
return "Spring Cloud Consul Discovery Client";
}
@Override
public String description() {
return "Spring Cloud Consul Discovery Client";
}
@Override
public ServiceInstance getLocalServiceInstance() {
@Override
public ServiceInstance getLocalServiceInstance() {
Response<Map<String, Service>> agentServices = client.getAgentServices();
Service service = agentServices.getValue().get(context.getId());
if (service == null) {
throw new IllegalStateException("Unable to locate service in consul agent: "+context.getId());
}
String host = "localhost";
if (service == null) {
throw new IllegalStateException("Unable to locate service in consul agent: "
+ context.getId());
}
String host = "localhost";
Response<Self> agentSelf = client.getAgentSelf();
Member member = agentSelf.getValue().getMember();
if (member != null) {
if (member != null) {
if (member.getName() != null) {
host = member.getName();
}
}
return new DefaultServiceInstance(service.getId(), host, service.getPort(), false);
}
host = member.getName();
}
}
return new DefaultServiceInstance(service.getId(), host, service.getPort(), false);
}
@Override
public List<ServiceInstance> getInstances(final String serviceId) {
@Override
public List<ServiceInstance> getInstances(final String serviceId) {
List<ServiceInstance> instances = new ArrayList<>();
addInstancesToList(instances, serviceId);
return instances;
}
return instances;
}
private void addInstancesToList(List<ServiceInstance> instances, String serviceId) {
Response<List<CatalogService>> services = client.getCatalogService(serviceId, QueryParams.DEFAULT);
Response<List<CatalogService>> services = client.getCatalogService(serviceId,
QueryParams.DEFAULT);
for (CatalogService service : services.getValue()) {
instances.add(new DefaultServiceInstance(serviceId, service.getNode(), service.getServicePort(), false));
instances.add(new DefaultServiceInstance(serviceId, service.getNode(),
service.getServicePort(), false));
}
}
public List<ServiceInstance> getAllInstances() {
List<ServiceInstance> instances = new ArrayList<>();
public List<ServiceInstance> getAllInstances() {
List<ServiceInstance> instances = new ArrayList<>();
Response<Map<String, List<String>>> services = client.getCatalogServices(QueryParams.DEFAULT);
Response<Map<String, List<String>>> services = client
.getCatalogServices(QueryParams.DEFAULT);
for (String serviceId : services.getValue().keySet()) {
addInstancesToList(instances, serviceId);
}
return instances;
}
}
@Override
public List<String> getServices() {
return new ArrayList<>(client.getCatalogServices(QueryParams.DEFAULT).getValue().keySet());
}
@Override
public List<String> getServices() {
return new ArrayList<>(client.getCatalogServices(QueryParams.DEFAULT).getValue()
.keySet());
}
}

View File

@@ -16,37 +16,38 @@
package org.springframework.cloud.consul.discovery;
import com.ecwid.consul.v1.ConsulClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ecwid.consul.v1.ConsulClient;
/**
* @author Spencer Gibb
*/
@Configuration
public class ConsulDiscoveryClientConfiguration {
@Autowired
private ConsulClient consulClient;
@Autowired
private ConsulClient consulClient;
@Bean
public ConsulLifecycle consulLifecycle() {
return new ConsulLifecycle();
}
@Bean
public ConsulLifecycle consulLifecycle() {
return new ConsulLifecycle();
}
@Bean
public TtlScheduler ttlScheduler() {
return new TtlScheduler(heartbeatProperties(), consulClient);
}
@Bean
public TtlScheduler ttlScheduler() {
return new TtlScheduler(heartbeatProperties(), consulClient);
}
@Bean
public HeartbeatProperties heartbeatProperties() {
return new HeartbeatProperties();
}
@Bean
public HeartbeatProperties heartbeatProperties() {
return new HeartbeatProperties();
}
@Bean
public ConsulDiscoveryClient consulDiscoveryClient() {
return new ConsulDiscoveryClient();
}
@Bean
public ConsulDiscoveryClient consulDiscoveryClient() {
return new ConsulDiscoveryClient();
}
}

View File

@@ -16,87 +16,89 @@
package org.springframework.cloud.consul.discovery;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;
import org.springframework.cloud.consul.ConsulProperties;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
/**
* @author Spencer Gibb
*/
@Slf4j
public class ConsulLifecycle extends AbstractDiscoveryLifecycle {
@Autowired
private ConsulClient client;
@Autowired
private ConsulClient client;
@Autowired
private ConsulProperties consulProperties;
@Autowired
private ConsulProperties consulProperties;
@Autowired
private TtlScheduler ttlScheduler;
@Autowired
private TtlScheduler ttlScheduler;
@Autowired
private HeartbeatProperties ttlConfig;
@Autowired
private HeartbeatProperties ttlConfig;
@Override
protected void register() {
NewService service = new NewService();
String appName = getAppName();
//TODO: move id to properties with context ID as default
service.setId(getContext().getId());
service.setName(appName);
//TODO: support port = 0 random assignment
Integer port = new Integer(getEnvironment().getProperty("server.port", "8080"));
service.setPort(port);
service.setTags(consulProperties.getTags());
NewService.Check check = new NewService.Check();
check.setTtl(ttlConfig.getTtl());
service.setCheck(check);
register(service);
}
@Override
protected void register() {
NewService service = new NewService();
String appName = getAppName();
// TODO: move id to properties with context ID as default
service.setId(getContext().getId());
service.setName(appName);
// TODO: support port = 0 random assignment
Integer port = new Integer(getEnvironment().getProperty("server.port", "8080"));
service.setPort(port);
service.setTags(consulProperties.getTags());
NewService.Check check = new NewService.Check();
check.setTtl(ttlConfig.getTtl());
service.setCheck(check);
register(service);
}
@Override
protected void registerManagement() {
NewService management = new NewService();
management.setId(getManagementServiceId());
management.setName(getManagementServiceName());
management.setPort(getManagementPort());
management.setTags(consulProperties.getManagementTags());
@Override
protected void registerManagement() {
NewService management = new NewService();
management.setId(getManagementServiceId());
management.setName(getManagementServiceName());
management.setPort(getManagementPort());
management.setTags(consulProperties.getManagementTags());
register(management);
}
register(management);
}
protected void register(NewService service) {
log.info("Registering service with consul: {}", service.toString());
client.agentServiceRegister(service);
ttlScheduler.add(service);
}
protected void register(NewService service) {
log.info("Registering service with consul: {}", service.toString());
client.agentServiceRegister(service);
ttlScheduler.add(service);
}
@Override
protected Object getConfiguration() {
return consulProperties;
}
@Override
protected Object getConfiguration() {
return consulProperties;
}
@Override
protected void deregister() {
deregister(getContext().getId());
}
@Override
protected void deregister() {
deregister(getContext().getId());
}
@Override
protected void deregisterManagement() {
deregister(getManagementServiceName());
}
@Override
protected void deregisterManagement() {
deregister(getManagementServiceName());
}
private void deregister(String serviceId) {
ttlScheduler.remove(serviceId);
client.agentServiceDeregister(serviceId);
}
private void deregister(String serviceId) {
ttlScheduler.remove(serviceId);
client.agentServiceDeregister(serviceId);
}
@Override
protected boolean isEnabled() {
return consulProperties.isEnabled();
}
@Override
protected boolean isEnabled() {
return consulProperties.isEnabled();
}
}

View File

@@ -21,8 +21,6 @@ import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity
import javax.annotation.PostConstruct;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerListFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -35,7 +33,9 @@ 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.Server;
import com.netflix.loadbalancer.ServerList;
import com.netflix.loadbalancer.ServerListFilter;
/**
* Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
@@ -72,10 +72,10 @@ public class ConsulRibbonClientConfiguration {
return serverList;
}
@Bean
public ServerListFilter<Server> ribbonServerListFilter() {
return new ServiceCheckServerListFilter(client);
}
@Bean
public ServerListFilter<Server> ribbonServerListFilter() {
return new ServiceCheckServerListFilter(client);
}
@PostConstruct
public void preprocess() {

View File

@@ -24,47 +24,47 @@ import com.netflix.loadbalancer.Server;
*/
public class ConsulServer extends Server {
private final MetaInfo metaInfo;
private final String address;
private final String node;
private final MetaInfo metaInfo;
private final String address;
private final String node;
public ConsulServer(final CatalogService service) {
super(service.getNode(), service.getServicePort());
address = service.getAddress();
node = service.getNode();
metaInfo = new MetaInfo() {
@Override
public String getAppName() {
return service.getServiceName();
}
public ConsulServer(final CatalogService service) {
super(service.getNode(), service.getServicePort());
address = service.getAddress();
node = service.getNode();
metaInfo = new MetaInfo() {
@Override
public String getAppName() {
return service.getServiceName();
}
@Override
public String getServerGroup() {
return null;
}
@Override
public String getServerGroup() {
return null;
}
@Override
public String getServiceIdForDiscovery() {
return null;
}
@Override
public String getServiceIdForDiscovery() {
return null;
}
@Override
public String getInstanceId() {
return service.getServiceId();
}
};
}
@Override
public String getInstanceId() {
return service.getServiceId();
}
};
}
@Override
public MetaInfo getMetaInfo() {
return metaInfo;
}
@Override
public MetaInfo getMetaInfo() {
return metaInfo;
}
public String getAddress() {
return address;
}
public String getAddress() {
return address;
}
public String getNode() {
return node;
}
public String getNode() {
return node;
}
}

View File

@@ -16,6 +16,10 @@
package org.springframework.cloud.consul.discovery;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
@@ -23,50 +27,47 @@ import com.ecwid.consul.v1.catalog.model.CatalogService;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Spencer Gibb
*/
public class ConsulServerList extends AbstractServerList<ConsulServer> {
private final ConsulClient client;
private final ConsulClient client;
private String serviceId;
private String serviceId;
public ConsulServerList(ConsulClient client) {
this.client = client;
}
public ConsulServerList(ConsulClient client) {
this.client = client;
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
this.serviceId = clientConfig.getClientName();
}
}
@Override
public List<ConsulServer> getInitialListOfServers() {
return getServers();
}
@Override
public List<ConsulServer> getInitialListOfServers() {
return getServers();
}
@Override
public List<ConsulServer> getUpdatedListOfServers() {
return getServers();
}
@Override
public List<ConsulServer> getUpdatedListOfServers() {
return getServers();
}
private List<ConsulServer> getServers() {
if (client == null) {
return Collections.emptyList();
}
Response<List<CatalogService>> response = client.getCatalogService(this.serviceId, QueryParams.DEFAULT);
if (response.getValue() == null || response.getValue().isEmpty()) {
return Collections.EMPTY_LIST;
}
ArrayList<ConsulServer> servers = new ArrayList<>();
private List<ConsulServer> getServers() {
if (client == null) {
return Collections.emptyList();
}
Response<List<CatalogService>> response = client.getCatalogService(
this.serviceId, QueryParams.DEFAULT);
if (response.getValue() == null || response.getValue().isEmpty()) {
return Collections.EMPTY_LIST;
}
ArrayList<ConsulServer> servers = new ArrayList<>();
for (CatalogService service : response.getValue()) {
servers.add(new ConsulServer(service));
}
return servers;
}
return servers;
}
}

View File

@@ -30,27 +30,27 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "consul.heartbeat")
@Data
public class HeartbeatProperties {
@Min(1)
private int ttlValue = 30;
@Min(1)
private int ttlValue = 30;
@NotNull
private String ttlUnit = "s";
@NotNull
private String ttlUnit = "s";
@DecimalMin("0.1")
@DecimalMax("0.9")
private double intervalRatio = 2.0 / 3.0;
@DecimalMin("0.1")
@DecimalMax("0.9")
private double intervalRatio = 2.0 / 3.0;
private Period heartbeatInterval;
private Period heartbeatInterval;
@PostConstruct
public void computeHeartbeatInterval() {
// heartbeat rate at ratio * ttl, but no later than ttl -1s and, (under lesser
// priority), no sooner than 1s from now
heartbeatInterval = new Period(Math.round(1000 * Math.max(ttlValue - 1,
Math.min(ttlValue * intervalRatio, 1))));
}
@PostConstruct
public void computeHeartbeatInterval() {
// heartbeat rate at ratio * ttl, but no later than ttl -1s and, (under lesser
// priority), no sooner than 1s from now
heartbeatInterval = new Period(Math.round(1000 * Math.max(ttlValue - 1,
Math.min(ttlValue * intervalRatio, 1))));
}
public String getTtl() {
return ttlValue + ttlUnit;
}
public String getTtl() {
return ttlValue + ttlUnit;
}
}

View File

@@ -16,15 +16,16 @@
package org.springframework.cloud.consul.discovery;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
/**
* Created by nicu on 11.03.2015.
@@ -32,43 +33,43 @@ import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
public class TtlScheduler {
public static final DateTime EXPIRED_DATE = new DateTime(0);
private final Map<String, DateTime> serviceHeartbeats = new ConcurrentHashMap<>();
public static final DateTime EXPIRED_DATE = new DateTime(0);
private final Map<String, DateTime> serviceHeartbeats = new ConcurrentHashMap<>();
private HeartbeatProperties configuration;
private HeartbeatProperties configuration;
private ConsulClient client;
private ConsulClient client;
public TtlScheduler(HeartbeatProperties configuration, ConsulClient client) {
this.configuration = configuration;
this.client = client;
}
public TtlScheduler(HeartbeatProperties configuration, ConsulClient client) {
this.configuration = configuration;
this.client = client;
}
/**
* Add a service to the checks loop.
*/
public void add(final NewService service) {
serviceHeartbeats.put(service.getId(), EXPIRED_DATE);
}
/**
* Add a service to the checks loop.
*/
public void add(final NewService service) {
serviceHeartbeats.put(service.getId(), EXPIRED_DATE);
}
public void remove(String serviceId) {
serviceHeartbeats.remove(serviceId);
}
public void remove(String serviceId) {
serviceHeartbeats.remove(serviceId);
}
@Scheduled(initialDelay = 0, fixedRateString = "${consul.heartbeat.fixedRate:15000}")
private void heartbeatServices() {
for (String serviceId : serviceHeartbeats.keySet()) {
DateTime latestHeartbeatDoneForService = serviceHeartbeats.get(serviceId);
if (latestHeartbeatDoneForService.plus(configuration.getHeartbeatInterval())
.isBefore(DateTime.now())) {
String checkId = serviceId;
if (!checkId.startsWith("service:")) {
checkId = "service:"+checkId;
}
client.agentCheckPass(checkId);
log.info("Sending consul heartbeat for: "+serviceId);
serviceHeartbeats.put(serviceId, DateTime.now());
}
}
}
@Scheduled(initialDelay = 0, fixedRateString = "${consul.heartbeat.fixedRate:15000}")
private void heartbeatServices() {
for (String serviceId : serviceHeartbeats.keySet()) {
DateTime latestHeartbeatDoneForService = serviceHeartbeats.get(serviceId);
if (latestHeartbeatDoneForService.plus(configuration.getHeartbeatInterval())
.isBefore(DateTime.now())) {
String checkId = serviceId;
if (!checkId.startsWith("service:")) {
checkId = "service:" + checkId;
}
client.agentCheckPass(checkId);
log.info("Sending consul heartbeat for: " + serviceId);
serviceHeartbeats.put(serviceId, DateTime.now());
}
}
}
}

View File

@@ -20,9 +20,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.netflix.loadbalancer.Server;
import org.springframework.cloud.consul.discovery.ConsulServer;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerListFilter;
/**
@@ -45,7 +45,7 @@ public class AliveServerListFilter implements ServerListFilter<Server> {
Set<String> liveNodes = filteringAgentClient.getAliveAgentsAddresses();
List<Server> filteredServers = new ArrayList<>();
for (Server server : servers) {
ConsulServer consulServer = ConsulServer.class.cast(server);
ConsulServer consulServer = ConsulServer.class.cast(server);
if (liveNodes.contains(consulServer.getAddress())) {
filteredServers.add(server);
}

View File

@@ -21,12 +21,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.netflix.loadbalancer.Server;
import org.springframework.cloud.consul.discovery.ConsulServer;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.health.model.Check;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerListFilter;
/**
@@ -47,7 +47,7 @@ public class ServiceCheckServerListFilter implements ServerListFilter<Server> {
for (Server server : servers) {
String serviceId = server.getMetaInfo().getInstanceId();
if (passingServiceIds.contains(serviceId)) {
ConsulServer consulServer = ConsulServer.class.cast(server);
ConsulServer consulServer = ConsulServer.class.cast(server);
List<Check> nodeChecks = client.getHealthChecksForNode(
consulServer.getNode(), QueryParams.DEFAULT).getValue();
boolean passingNodeChecks = true;

View File

@@ -16,9 +16,11 @@
package org.springframework.cloud.consul.discovery;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.Service;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Map;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,9 +36,9 @@ import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import java.util.Map;
import static org.junit.Assert.*;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.Service;
/**
* @author Spencer Gibb
@@ -44,7 +46,7 @@ import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SpringApplicationConfiguration(classes = TestConfig.class)
@IntegrationTest({"server.port=0", "spring.application.name=myTestService"})
@IntegrationTest({ "server.port=0", "spring.application.name=myTestService" })
@WebAppConfiguration
public class ConsulLifecycleTests {
@@ -70,7 +72,7 @@ public class ConsulLifecycleTests {
@Configuration
@EnableAutoConfiguration
@Import({ConsulAutoConfiguration.class, ConsulDiscoveryClientConfiguration.class})
@Import({ ConsulAutoConfiguration.class, ConsulDiscoveryClientConfiguration.class })
class TestConfig {
}

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.consul.sample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -45,47 +46,47 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j
public class SampleApplication implements ApplicationListener<SimpleRemoteEvent> {
public static final String CLIENT_NAME = "testConsulApp";
public static final String CLIENT_NAME = "testConsulApp";
@Autowired
LoadBalancerClient loadBalancer;
@Autowired
LoadBalancerClient loadBalancer;
@Autowired
DiscoveryClient discoveryClient;
@Autowired
DiscoveryClient discoveryClient;
@Autowired
Environment env;
@Autowired
Environment env;
@Autowired(required = false)
RelaxedPropertyResolver resolver;
@Autowired(required = false)
RelaxedPropertyResolver resolver;
@RequestMapping("/me")
public ServiceInstance me() {
return discoveryClient.getLocalServiceInstance();
}
@RequestMapping("/me")
public ServiceInstance me() {
return discoveryClient.getLocalServiceInstance();
}
@RequestMapping("/")
public ServiceInstance lb() {
return loadBalancer.choose(CLIENT_NAME);
}
@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;
}
@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);
}
@Bean
public SubtypeModule sampleSubtypeModule() {
return new SubtypeModule(SimpleRemoteEvent.class);
}
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
@Override
public void onApplicationEvent(SimpleRemoteEvent event) {
log.info("Received event: {}", event);
}
@Override
public void onApplicationEvent(SimpleRemoteEvent event) {
log.info("Received event: {}", event);
}
}

View File

@@ -2,9 +2,9 @@ server:
port: 8080
#TODO: figure out why I need this here and in bootstrap.yml
spring:
application:
name: testConsulApp
#spring:
# application:
# name: testConsulApp
ribbon:
ServerListRefreshInterval: 1000

View File

@@ -1,7 +1,3 @@
spring:
application:
name: testConsulApp
cloud:
config:
# TODO: refactor spring-cloud-config to use refresh, etc.. with out config client
enabled: false

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.1.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -42,7 +42,6 @@ public class ZuulApplicationTests {
public void contextLoads() {
}
@SpringBootApplication
@EnableCircuitBreaker
@EnableDiscoveryClient