From fa84fde0eb23b70687612dd4639c74e425af062a Mon Sep 17 00:00:00 2001 From: Yash Vyas Date: Fri, 30 Jun 2017 17:47:56 -0500 Subject: [PATCH 1/3] Add get methods to get configuration values wrapped in consulResponse. Add kvCache method to get configuration data with response metadata --- pom.xml | 2 +- .../com/orbitz/consul/KeyValueClient.java | 76 ++++++++++++++++++- .../com/orbitz/consul/cache/ConsulCache.java | 12 ++- .../java/com/orbitz/consul/KeyValueTests.java | 33 ++++++++ 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d2f8fd41..d8d65a07 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.orbitz.consul consul-client jar - 0.15.0 + 0.16.0 consul-client http://maven.apache.org Consul Client for Java diff --git a/src/main/java/com/orbitz/consul/KeyValueClient.java b/src/main/java/com/orbitz/consul/KeyValueClient.java index caa06600..bd21f800 100644 --- a/src/main/java/com/orbitz/consul/KeyValueClient.java +++ b/src/main/java/com/orbitz/consul/KeyValueClient.java @@ -72,6 +72,17 @@ public Optional getValue(String key) { return getValue(key, QueryOptions.BLANK); } + /** + * Retrieves a {@link com.orbitz.consul.model.ConsulResponse} with the + * {@link com.orbitz.consul.model.kv.Value} for a spefici key from the + * key/value store + * @param key The key to retrieve + * @return An {@link Optional} containing the {@link ConsulResponse} or {@link Optional#absent()} + */ + public Optional> getConsulResponseWithValue(String key) { + return getConsulResponseWithValue(key, QueryOptions.BLANK); + } + /** * Retrieves a {@link com.orbitz.consul.model.kv.Value} for a specific key * from the key/value store. @@ -94,6 +105,36 @@ public Optional getValue(String key, QueryOptions queryOptions) { return Optional.absent(); } + /** + * Returns a {@link ConsulResponse} for a specific key from the kv store. + * Contains the consul response headers along with the configuration value. + * + * GET /v1/kv/{key} + * + * @param key The key to retrieve. + * @param queryOptions The query options. + * @return An {@link Optional} containing the ConsulResponse or {@link Optional#absent()} + */ + public Optional> getConsulResponseWithValue(String key, QueryOptions queryOptions) { + try { + ConsulResponse> consulResponse = + extractConsulResponse(api.getValue(trimLeadingSlash(key), queryOptions.toQuery()), NOT_FOUND_404); + Optional consulValue = getSingleValue(consulResponse.getResponse()); + if (consulValue.isPresent()) { + ConsulResponse result = + new ConsulResponse<>(consulValue.get(), consulResponse.getLastContact(), + consulResponse.isKnownLeader(), consulResponse.getIndex()); + return Optional.of(result); + } + } catch (ConsulException ignored) { + if (ignored.getCode() != NOT_FOUND_404) { + throw ignored; + } + } + + return Optional.absent(); + } + /** * Asynchronously retrieves a {@link com.orbitz.consul.model.kv.Value} for a specific key * from the key/value store. @@ -123,7 +164,7 @@ public void onFailure(Throwable throwable) { extractConsulResponse(api.getValue(trimLeadingSlash(key), queryOptions.toQuery()), wrapper, NOT_FOUND_404); } - private Optional getSingleValue(List values){ + private Optional getSingleValue(List values) { return values != null && values.size() != 0 ? Optional.of(values.get(0)) : Optional.absent(); } @@ -140,6 +181,20 @@ public List getValues(String key) { return getValues(key, QueryOptions.BLANK); } + /** + * Retrieves a {@link ConsulResponse} with a list of {@link Value} objects along with + * consul response headers for a specific key from the key/value store. + * + * GET /v1/kv/{key}?recurse + * + * @param key The key to retrieve. + * @return A {@link ConsulResponse} with a list of zero to many {@link Value} objects and + * consul response headers. + */ + public ConsulResponse> getConsulResponseWithValues(String key) { + return getConsulResponseWithValues(key, QueryOptions.BLANK); + } + /** * Retrieves a list of {@link com.orbitz.consul.model.kv.Value} objects for a specific key * from the key/value store. @@ -160,6 +215,25 @@ public List getValues(String key, QueryOptions queryOptions) { return result == null ? Collections.emptyList() : result; } + /** + * Retrieves a {@link ConsulResponse} with a list of {@link Value} objects along with + * consul response headers for a specific key from the key/value store. + * + * GET /v1/kv/{key}?recurse + * + * @param key The key to retrieve. + * @param queryOptions The query options to use. + * @return A {@link ConsulResponse} with a list of zero to many {@link Value} objects and + * consul response headers. + */ + public ConsulResponse> getConsulResponseWithValues(String key, QueryOptions queryOptions) { + Map query = queryOptions.toQuery(); + + query.put("recursive", "true"); + + return extractConsulResponse(api.getValue(trimLeadingSlash(key), query), NOT_FOUND_404); + } + /** * Asynchronously retrieves a list of {@link com.orbitz.consul.model.kv.Value} objects for a specific key * from the key/value store. diff --git a/src/main/java/com/orbitz/consul/cache/ConsulCache.java b/src/main/java/com/orbitz/consul/cache/ConsulCache.java index cfb54b7f..a4b7a066 100644 --- a/src/main/java/com/orbitz/consul/cache/ConsulCache.java +++ b/src/main/java/com/orbitz/consul/cache/ConsulCache.java @@ -25,6 +25,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import static com.google.common.base.Preconditions.checkArgument; @@ -47,6 +49,8 @@ enum State {latent, starting, started, stopped } private static final long BACKOFF_DELAY_QTY_IN_MS = getBackOffDelayInMs(System.getProperties()); private final AtomicReference latestIndex = new AtomicReference(null); + private final AtomicLong lastContact = new AtomicLong(); + private final AtomicBoolean isKnownLeader = new AtomicBoolean(); private final AtomicReference> lastResponse = new AtomicReference>(null); private final AtomicReference state = new AtomicReference(State.latent); private final CountDownLatch initLatch = new CountDownLatch(1); @@ -82,6 +86,9 @@ public void onComplete(ConsulResponse> consulResponse) { if (changed) { // changes lastResponse.set(full); + // metadata changes + lastContact.set(consulResponse.getLastContact()); + isKnownLeader.set(consulResponse.isKnownLeader()); } if (changed) { @@ -164,12 +171,15 @@ public ImmutableMap getMap() { return lastResponse.get(); } + public ConsulResponse> getMapWithMetadata() { + return new ConsulResponse<>(lastResponse.get(), lastContact.get(), isKnownLeader.get(), latestIndex.get()); + } + @VisibleForTesting ImmutableMap convertToMap(final ConsulResponse> response) { if (response == null || response.getResponse() == null || response.getResponse().isEmpty()) { return ImmutableMap.of(); } - final ImmutableMap.Builder builder = ImmutableMap.builder(); final Set keySet = new HashSet<>(); for (final V v : response.getResponse()) { diff --git a/src/test/java/com/orbitz/consul/KeyValueTests.java b/src/test/java/com/orbitz/consul/KeyValueTests.java index d1db02d1..1b5b4790 100644 --- a/src/test/java/com/orbitz/consul/KeyValueTests.java +++ b/src/test/java/com/orbitz/consul/KeyValueTests.java @@ -20,6 +20,7 @@ import java.net.UnknownHostException; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -299,6 +300,38 @@ public void onFailure(Throwable throwable) { assertTrue(success.get()); } + @Test + public void testGetConsulResponseWithValue() { + KeyValueClient keyValueClient = client.keyValueClient(); + String key = UUID.randomUUID().toString(); + String value = UUID.randomUUID().toString(); + keyValueClient.putValue(key, value); + + Optional> response = keyValueClient.getConsulResponseWithValue(key); + + keyValueClient.deleteKey(key); + + assertTrue(response.get().getResponse().getKey().equals(key)); + assertTrue(response.get().getResponse().getValue().isPresent()); + assertTrue(Objects.nonNull(response.get().getIndex())); + + } + + @Test + public void testGetConsulResponseWithValues() { + KeyValueClient keyValueClient = client.keyValueClient(); + String key = UUID.randomUUID().toString(); + String value = UUID.randomUUID().toString(); + keyValueClient.putValue(key, value); + + ConsulResponse> response = keyValueClient.getConsulResponseWithValues(key); + + keyValueClient.deleteKey(key); + + assertTrue(!response.getResponse().isEmpty()); + assertTrue(Objects.nonNull(response.getIndex())); + } + @Test public void testGetValueNotFoundAsync() throws InterruptedException { KeyValueClient keyValueClient = client.keyValueClient(); From 66829ba5f9e902c820309d3ef2dfe9ca29dc5298 Mon Sep 17 00:00:00 2001 From: Rick Fast Date: Fri, 30 Jun 2017 20:14:56 -0500 Subject: [PATCH 2/3] remove java 8 stuff --- src/test/java/com/orbitz/consul/KeyValueTests.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/orbitz/consul/KeyValueTests.java b/src/test/java/com/orbitz/consul/KeyValueTests.java index 1b5b4790..c5e8fbee 100644 --- a/src/test/java/com/orbitz/consul/KeyValueTests.java +++ b/src/test/java/com/orbitz/consul/KeyValueTests.java @@ -20,7 +20,6 @@ import java.net.UnknownHostException; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -313,7 +312,7 @@ public void testGetConsulResponseWithValue() { assertTrue(response.get().getResponse().getKey().equals(key)); assertTrue(response.get().getResponse().getValue().isPresent()); - assertTrue(Objects.nonNull(response.get().getIndex())); + assertNotNull(response.get().getIndex()); } @@ -329,7 +328,7 @@ public void testGetConsulResponseWithValues() { keyValueClient.deleteKey(key); assertTrue(!response.getResponse().isEmpty()); - assertTrue(Objects.nonNull(response.getIndex())); + assertNotNull(response.get().getIndex()); } @Test From ccbbbbc78f18f42b78c19adac2b2d6bb444a3efb Mon Sep 17 00:00:00 2001 From: Yash Vyas Date: Fri, 30 Jun 2017 21:05:09 -0500 Subject: [PATCH 3/3] response is not an optional --- src/test/java/com/orbitz/consul/KeyValueTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/orbitz/consul/KeyValueTests.java b/src/test/java/com/orbitz/consul/KeyValueTests.java index c5e8fbee..7ab39201 100644 --- a/src/test/java/com/orbitz/consul/KeyValueTests.java +++ b/src/test/java/com/orbitz/consul/KeyValueTests.java @@ -328,7 +328,7 @@ public void testGetConsulResponseWithValues() { keyValueClient.deleteKey(key); assertTrue(!response.getResponse().isEmpty()); - assertNotNull(response.get().getIndex()); + assertNotNull(response.getIndex()); } @Test