diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLoader.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLoader.java index f1b21fc1..5c118539 100644 --- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLoader.java +++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLoader.java @@ -40,8 +40,11 @@ public class ConsulConfigDataLoader implements ConfigDataLoader new ConsulConfigDataResource(propertySourceContext, - location.isOptional(), properties, consulPropertySources)).collect(Collectors.toList()); + properties, consulPropertySources)).collect(Collectors.toList()); } private BindHandler getBindHandler(ConfigDataLocationResolverContext context) { diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataResource.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataResource.java index 29968088..3a1e39df 100644 --- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataResource.java +++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataResource.java @@ -31,6 +31,12 @@ public class ConsulConfigDataResource extends ConfigDataResource { private final ConsulPropertySources consulPropertySources; + public ConsulConfigDataResource(String context, ConsulConfigProperties properties, + ConsulPropertySources consulPropertySources) { + this(context, true, properties, consulPropertySources); + } + + @Deprecated public ConsulConfigDataResource(String context, boolean optional, ConsulConfigProperties properties, ConsulPropertySources consulPropertySources) { this.properties = properties; @@ -43,6 +49,7 @@ public class ConsulConfigDataResource extends ConfigDataResource { return this.context; } + @Deprecated public boolean isOptional() { return this.optional; } diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySources.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySources.java index e6b38e72..20197127 100644 --- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySources.java +++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySources.java @@ -74,7 +74,7 @@ public class ConsulPropertySources { } protected String getContext(String prefix, String context) { - if (StringUtils.isEmpty(prefix)) { + if (!StringUtils.hasText(prefix)) { return context; } else { @@ -95,8 +95,14 @@ public class ConsulPropertySources { } } + @Deprecated public ConsulPropertySource createPropertySource(String propertySourceContext, boolean optional, ConsulClient consul, BiConsumer indexConsumer) { + return createPropertySource(propertySourceContext, consul, indexConsumer); + } + + public ConsulPropertySource createPropertySource(String propertySourceContext, ConsulClient consul, + BiConsumer indexConsumer) { try { ConsulPropertySource propertySource = null; @@ -109,9 +115,6 @@ public class ConsulPropertySources { filesPropertySource.init(response.getValue()); propertySource = filesPropertySource; } - else if (!optional) { - throw new PropertySourceNotFoundException(propertySourceContext); - } } else { propertySource = create(propertySourceContext, consul, indexConsumer); @@ -122,7 +125,7 @@ public class ConsulPropertySources { throw e; } catch (Exception e) { - if (properties.isFailFast() || !optional) { + if (properties.isFailFast()) { throw new PropertySourceNotFoundException(propertySourceContext, e); } else { diff --git a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataFileIntegrationTests.java b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataFileIntegrationTests.java new file mode 100644 index 00000000..70839fcf --- /dev/null +++ b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataFileIntegrationTests.java @@ -0,0 +1,169 @@ +/* + * Copyright 2013-2019 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 + * + * https://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.consul.config; + +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.ecwid.consul.v1.ConsulClient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.consul.test.ConsulTestcontainers; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.test.annotation.DirtiesContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Spencer Gibb + */ +@DirtiesContext +public class ConsulConfigDataFileIntegrationTests { + + private static final String APP_NAME = "testConsulConfigDataFile"; + + private static final String PREFIX = "_configDataIntegrationFileTests_config__"; + + private static final String ROOT = PREFIX + UUID.randomUUID(); + + private static final String VALUE1 = "testPropVal"; + + private static final String TEST_PROP = "testProp"; + + private static final String TEST_PROP_CANONICAL = "test-prop"; + + private static final String KEY1 = ROOT + "/application.properties"; + + private static final String VALUE2 = "testPropVal2"; + + private static final String TEST_PROP2 = "testProp2"; + + private static final String TEST_PROP2_CANONICAL = "test-prop2"; + + private static final String TEST_PROP3 = "testProp3"; + + private static final String TEST_PROP3_CANONICAL = "test-prop3"; + + private static final String KEY3 = ROOT + "/" + APP_NAME + ".properties"; + + private static ConfigurableApplicationContext context; + + private static ConfigurableEnvironment environment; + + private static ConsulClient client; + + @BeforeAll + public static void setup() { + ConsulTestcontainers.start(); + client = ConsulTestcontainers.client(); + client.deleteKVValues(PREFIX); + client.setKVValue(KEY1, TEST_PROP + "=" + VALUE1 + "\n" + TEST_PROP2 + "=" + VALUE2); + + context = new SpringApplicationBuilder(Config.class).web(WebApplicationType.NONE).run( + "--spring.application.name=" + APP_NAME, "--spring.cloud.consul.config.format=files", + "--spring.config.import=optional:consul:" + ConsulTestcontainers.getHost() + ":" + + ConsulTestcontainers.getPort(), + "--spring.cloud.consul.config.prefix=" + ROOT, "--spring.cloud.consul.config.watch.delay=10"); + + client = context.getBean(ConsulClient.class); + environment = context.getEnvironment(); + } + + @AfterAll + public static void teardown() { + client.deleteKVValues(PREFIX); + if (context != null) { + context.close(); + } + } + + @Test + public void propertyLoaded() { + String testProp = environment.getProperty(TEST_PROP_CANONICAL); + assertThat(testProp).as(TEST_PROP + " was wrong").isEqualTo(VALUE1); + String testProp2 = environment.getProperty(TEST_PROP2_CANONICAL); + assertThat(testProp2).as(TEST_PROP2 + " was wrong").isEqualTo(VALUE2); + } + + @Test + public void propertyLoadedAndUpdated() throws Exception { + String testProp = environment.getProperty(TEST_PROP_CANONICAL); + assertThat(testProp).as("testProp was wrong").isEqualTo(VALUE1); + + client.setKVValue(KEY1, TEST_PROP + "=testPropValUpdate\n" + TEST_PROP2 + "=" + VALUE2); + + CountDownLatch latch = context.getBean("countDownLatch1", CountDownLatch.class); + boolean receivedEvent = latch.await(15, TimeUnit.SECONDS); + assertThat(receivedEvent).as("listener didn't receive event").isTrue(); + + testProp = environment.getProperty(TEST_PROP_CANONICAL); + assertThat(testProp).as("testProp was wrong after update").isEqualTo("testPropValUpdate"); + } + + @Test + public void contextDoesNotExistThenExists() throws Exception { + String testProp = environment.getProperty(TEST_PROP3_CANONICAL); + assertThat(testProp).as(TEST_PROP3 + " was wrong").isNull(); + + client.setKVValue(KEY3, TEST_PROP3 + "=testPropValInsert"); + + CountDownLatch latch = context.getBean("countDownLatch2", CountDownLatch.class); + boolean receivedEvent = latch.await(15, TimeUnit.SECONDS); + assertThat(receivedEvent).as("listener didn't receive event").isTrue(); + + testProp = environment.getProperty(TEST_PROP3_CANONICAL); + assertThat(testProp).as(TEST_PROP3 + " was wrong after update").isEqualTo("testPropValInsert"); + } + + @Configuration + @EnableAutoConfiguration + static class Config implements ApplicationListener { + + @Bean + public CountDownLatch countDownLatch1() { + return new CountDownLatch(1); + } + + @Bean + public CountDownLatch countDownLatch2() { + return new CountDownLatch(1); + } + + @Override + public void onApplicationEvent(EnvironmentChangeEvent event) { + if (event.getKeys().contains(TEST_PROP)) { + countDownLatch1().countDown(); + } + else if (event.getKeys().contains(TEST_PROP3)) { + countDownLatch2().countDown(); + } + } + + } + +}