Support for new Spring Boot ConfigData framework. (#265)

This commit is contained in:
Spencer Gibb
2020-09-08 15:38:03 -04:00
committed by GitHub
parent 496aeaabd2
commit abed38689e
12 changed files with 638 additions and 11 deletions

View File

@@ -16,14 +16,20 @@
package org.springframework.cloud.zookeeper.config;
import java.util.Collections;
import java.util.List;
import org.apache.curator.framework.CuratorFramework;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.endpoint.RefreshEndpoint;
import org.springframework.cloud.zookeeper.ConditionalOnZookeeperEnabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
@@ -39,15 +45,24 @@ public class ZookeeperConfigAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RefreshEndpoint.class)
@ConditionalOnProperty(name = "spring.cloud.zookeeper.config.watcher.enabled", matchIfMissing = true)
protected static class ZkRefreshConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.cloud.zookeeper.config.watcher.enabled", matchIfMissing = true)
public ConfigWatcher configWatcher(ZookeeperPropertySourceLocator locator,
@ConditionalOnBean(ZookeeperPropertySourceLocator.class)
public ConfigWatcher propertySourceLocatorConfigWatcher(ZookeeperPropertySourceLocator locator,
CuratorFramework curator) {
return new ConfigWatcher(locator.getContexts(), curator);
}
@Bean
@ConditionalOnMissingBean(ZookeeperPropertySourceLocator.class)
public ConfigWatcher configDataConfigWatcher(CuratorFramework curator, Environment env) {
List<String> contexts = env.getProperty("spring.cloud.zookeeper.config.property-source-contexts",
List.class, Collections.emptyList());
return new ConfigWatcher(contexts, curator);
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2015-2020 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.zookeeper.config;
import java.util.Collections;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataLoader;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
public class ZookeeperConfigDataLoader implements ConfigDataLoader<ZookeeperConfigDataLocation> {
private static final Log log = LogFactory.getLog(ZookeeperPropertySourceLocator.class);
@Override
public ConfigData load(ConfigDataLoaderContext context, ZookeeperConfigDataLocation location) {
try {
ZookeeperPropertySource propertySource = new ZookeeperPropertySource(location.getContext(),
location.getCurator());
return new ConfigData(Collections.singletonList(propertySource));
}
catch (Exception e) {
if (location.getProperties().isFailFast() || !location.isOptional()) {
throw new ConfigDataLocationNotFoundException(location, e);
}
else {
log.warn("Unable to load zookeeper config from " + location.getContext(), e);
}
}
return null;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2015-2020 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.zookeeper.config;
import java.util.Objects;
import org.apache.curator.framework.CuratorFramework;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.core.style.ToStringCreator;
public class ZookeeperConfigDataLocation extends ConfigDataLocation {
private final CuratorFramework curator;
private final ZookeeperConfigProperties properties;
private final String context;
private final boolean optional;
public ZookeeperConfigDataLocation(CuratorFramework curator, ZookeeperConfigProperties properties, String context, boolean optional) {
this.curator = curator;
this.properties = properties;
this.context = context;
this.optional = optional;
}
public CuratorFramework getCurator() {
return this.curator;
}
public ZookeeperConfigProperties getProperties() {
return this.properties;
}
public String getContext() {
return this.context;
}
public boolean isOptional() {
return this.optional;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ZookeeperConfigDataLocation that = (ZookeeperConfigDataLocation) o;
return this.curator.equals(that.curator) &&
this.properties.equals(that.properties) &&
this.optional == that.optional &&
this.context.equals(that.context);
}
@Override
public int hashCode() {
return Objects.hash(this.curator, this.properties, this.context);
}
@Override
public String toString() {
return new ToStringCreator(this)
.append("context", context)
.append("optional", optional)
.append("properties", properties)
.toString();
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2015-2020 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.zookeeper.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
import org.springframework.boot.context.config.ConfigDataLocationResolver;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration;
import org.springframework.cloud.zookeeper.ZookeeperProperties;
import org.springframework.core.env.MapPropertySource;
public class ZookeeperConfigDataLocationResolver implements ConfigDataLocationResolver<ZookeeperConfigDataLocation> {
private static final Log log = LogFactory.getLog(ZookeeperAutoConfiguration.class);
@Override
public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
boolean zkEnabled = context.getBinder().bind(ZookeeperProperties.PREFIX + ".enabled", Boolean.class)
.orElse(true);
boolean zkConfigEnabled = context.getBinder().bind(ZookeeperConfigProperties.PREFIX + ".enabled", Boolean.class)
.orElse(true);
return location.startsWith("zookeeper:") && zkConfigEnabled && zkEnabled;
}
@Override
public List<ZookeeperConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location, boolean optional) throws ConfigDataLocationNotFoundException {
return Collections.emptyList();
}
@Override
public List<ZookeeperConfigDataLocation> resolveProfileSpecific(ConfigDataLocationResolverContext context, String location, boolean optional, Profiles profiles) throws ConfigDataLocationNotFoundException {
// TODO use location for host:port
CuratorFramework curator = curatorFramework(optional, loadProperties(context.getBinder()));
String appName = context.getBinder().bind("spring.application.name", String.class).orElse("application");
ZookeeperConfigProperties properties = loadConfigProperties(context.getBinder());
String root = properties.getRoot();
List<String> contexts = new ArrayList<>();
String defaultContext = root + "/" + properties.getDefaultContext();
contexts.add(defaultContext);
addProfiles(contexts, defaultContext, profiles, properties);
StringBuilder baseContext = new StringBuilder(root);
if (!appName.startsWith("/")) {
baseContext.append("/");
}
baseContext.append(appName);
contexts.add(baseContext.toString());
addProfiles(contexts, baseContext.toString(), profiles, properties);
Collections.reverse(contexts);
context.getBootstrapRegistry().register(CuratorFramework.class, () -> curator)
.onApplicationContextPrepared((ctxt, curatorFramework) -> {
ctxt.getBeanFactory().registerSingleton("configDataCuratorFramework", curatorFramework);
HashMap<String, Object> source = new HashMap<>();
source.put("spring.cloud.zookeeper.config.property-source-contexts", contexts);
MapPropertySource propertySource = new MapPropertySource("zookeeperConfigData", source);
ctxt.getEnvironment().getPropertySources().addFirst(propertySource);
});
ArrayList<ZookeeperConfigDataLocation> locations = new ArrayList<>();
contexts.forEach(propertySourceContext -> locations
.add(new ZookeeperConfigDataLocation(curator, properties, propertySourceContext, optional)));
return locations;
}
private void addProfiles(List<String> contexts, String baseContext, Profiles profiles,
ZookeeperConfigProperties properties) {
for (String profile : profiles.getAccepted()) {
contexts.add(baseContext + properties.getProfileSeparator() + profile);
}
}
protected CuratorFramework curatorFramework(boolean optional, ZookeeperProperties properties) {
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
builder.connectString(properties.getConnectString())
.sessionTimeoutMs((int) properties.getSessionTimeout().toMillis())
.connectionTimeoutMs((int) properties.getConnectionTimeout().toMillis())
.retryPolicy(retryPolicy(properties));
CuratorFramework curator = builder.build();
curator.start();
if (log.isTraceEnabled()) {
log.trace("blocking until connected to zookeeper for " + properties.getBlockUntilConnectedWait()
+ properties.getBlockUntilConnectedUnit());
}
try {
curator.blockUntilConnected(properties.getBlockUntilConnectedWait(),
properties.getBlockUntilConnectedUnit());
}
catch (InterruptedException e) {
if (!optional) {
log.error("Unable to connect to zookeeper", e);
throw new ConfigDataLocationNotFoundException("Unable to connect to zookeeper", null, e);
}
else if (log.isDebugEnabled()) {
log.debug("Unable to connect to zookeeper", e);
}
}
if (log.isTraceEnabled()) {
log.trace("connected to zookeeper");
}
return curator;
}
protected RetryPolicy retryPolicy(ZookeeperProperties properties) {
return new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(), properties.getMaxRetries(),
properties.getMaxSleepMs());
}
protected ZookeeperProperties loadProperties(Binder binder) {
ZookeeperProperties properties = binder.bind(ZookeeperProperties.PREFIX, Bindable.of(ZookeeperProperties.class))
.orElse(new ZookeeperProperties());
return properties;
}
protected ZookeeperConfigProperties loadConfigProperties(Binder binder) {
ZookeeperConfigProperties properties = binder
.bind(ZookeeperConfigProperties.PREFIX, Bindable.of(ZookeeperConfigProperties.class))
.orElse(new ZookeeperConfigProperties());
return properties;
}
}

View File

@@ -26,9 +26,14 @@ import org.springframework.util.Assert;
* @since 1.0.0
* @see ZookeeperPropertySourceLocator
*/
@ConfigurationProperties("spring.cloud.zookeeper.config")
@ConfigurationProperties(ZookeeperConfigProperties.PREFIX)
public class ZookeeperConfigProperties {
/**
* Configuration prefix for config properties.
*/
public static final String PREFIX = "spring.cloud.zookeeper.config";
private boolean enabled = true;
/**

View File

@@ -5,3 +5,11 @@ org.springframework.cloud.zookeeper.config.ZookeeperConfigAutoConfiguration
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.zookeeper.config.ZookeeperConfigBootstrapConfiguration
# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.cloud.zookeeper.config.ZookeeperConfigDataLocationResolver
# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.cloud.zookeeper.config.ZookeeperConfigDataLoader

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.zookeeper.config;
import java.net.ConnectException;
import org.apache.zookeeper.KeeperException;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -25,8 +25,9 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
@@ -72,7 +73,7 @@ public class ZookeeperConfigAutoConfigurationTests {
@Test
public void testConfigEnabledTrueLoadsZookeeperConfigAutoConfiguration()
throws Exception {
expectedException.expect(ConnectException.class);
expectedException.expectCause(Matchers.isA(KeeperException.class));
new SpringApplicationBuilder().sources(Config.class).web(WebApplicationType.NONE)
.run("--spring.application.name=testZookeeperConfigEnabledSetToTrue",
@@ -87,7 +88,8 @@ public class ZookeeperConfigAutoConfigurationTests {
"--spring.cloud.zookeeper.config.enabled=true");
}
@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
static class Config {
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright 2015-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.zookeeper.config;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryOneTime;
import org.apache.curator.test.TestingServer;
import org.apache.zookeeper.KeeperException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.context.refresh.ConfigDataContextRefresher;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
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.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
*/
public class ZookeeperConfigDataIntegrationTests {
private static final Log log = LogFactory
.getLog(ZookeeperConfigDataIntegrationTests.class);
public static final String PREFIX = "test__configdata__";
public static final String ROOT = "/" + PREFIX + UUID.randomUUID();
public static final String CONTEXT = ROOT + "/application/";
public static final String KEY_BASIC = "testProp";
public static final String KEY_BASIC_PATH = CONTEXT + KEY_BASIC;
public static final String VAL_BASIC = "testPropVal";
public static final String KEY_WITH_DOT = "testProp.dot";
public static final String KEY_WITH_DOT_PATH = CONTEXT + KEY_WITH_DOT;
public static final String VAL_WITH_DOT = "withDotVal";
public static final String KEY_NESTED = "testProp.nested";
public static final String KEY_NESTED_PATH = CONTEXT + KEY_NESTED.replace('.', '/');
public static final String VAL_NESTED = "nestedVal";
public static final String KEY_WITHOUT_VALUE = "testProp.novalue";
public static final String KEY_WITHOUT_VALUE_PATH = CONTEXT + KEY_WITHOUT_VALUE;
private ConfigurableEnvironment environment;
private ConfigurableApplicationContext context;
private TestingServer testingServer;
private CuratorFramework curator;
@Before
public void setup() throws Exception {
int port = SocketUtils.findAvailableTcpPort();
this.testingServer = new TestingServer(port);
String connectString = "localhost:" + port;
this.curator = CuratorFrameworkFactory.builder()
.retryPolicy(new RetryOneTime(500)).connectString(connectString).build();
this.curator.start();
List<String> children = this.curator.getChildren().forPath("/");
for (String child : children) {
if (child.startsWith(PREFIX) && child.length() > PREFIX.length()) {
delete("/" + child);
}
}
StringBuilder create = new StringBuilder(1024);
create.append(this.curator.create().creatingParentsIfNeeded()
.forPath(KEY_BASIC_PATH, VAL_BASIC.getBytes())).append('\n');
create.append(this.curator.create().creatingParentsIfNeeded()
.forPath(KEY_WITH_DOT_PATH, VAL_WITH_DOT.getBytes())).append('\n');
create.append(this.curator.create().creatingParentsIfNeeded()
.forPath(KEY_NESTED_PATH, VAL_NESTED.getBytes())).append('\n');
create.append(this.curator.create().creatingParentsIfNeeded()
.forPath(KEY_WITHOUT_VALUE_PATH, null)).append('\n');
this.curator.close();
System.out.println(create);
this.context = new SpringApplicationBuilder(Config.class)
.web(WebApplicationType.NONE)
.run("--spring.cloud.zookeeper.connectString=" + connectString,
"--debug=true",
//"--spring.cloud.bootstrap.enabled=false",
"--spring.config.import=zookeeper:",
"--spring.application.name=testZkConfigDataIntegration",
"--logging.level.org.springframework.cloud.zookeeper=DEBUG",
"--spring.cloud.zookeeper.config.root=" + ROOT);
this.curator = this.context.getBean(CuratorFramework.class);
this.environment = this.context.getEnvironment();
}
public void delete(String path) throws Exception {
try {
this.curator.delete().deletingChildrenIfNeeded().forPath(path);
}
catch (KeeperException e) {
if (e.code() != KeeperException.Code.NONODE) {
throw e;
}
}
}
@After
public void after() throws Exception {
try {
delete(ROOT);
}
finally {
this.context.close();
this.testingServer.close();
}
}
@Test
public void checkKeyValues() throws Exception {
String propValue = this.environment.getProperty(KEY_BASIC);
assertThat(propValue).as(KEY_BASIC + " was wrong").isEqualTo(VAL_BASIC);
propValue = this.environment.getProperty(KEY_NESTED);
assertThat(propValue).as(VAL_NESTED + " was wrong").isEqualTo(VAL_NESTED);
propValue = this.environment.getProperty(KEY_WITH_DOT);
assertThat(propValue).as(VAL_WITH_DOT + " was wrong").isEqualTo(VAL_WITH_DOT);
propValue = this.environment.getProperty(KEY_WITHOUT_VALUE);
assertThat(propValue).as(KEY_WITHOUT_VALUE + " was wrong").isEmpty();
}
@Test
public void propertyLoadedAndUpdated() throws Exception {
String testProp = this.environment.getProperty(KEY_BASIC);
assertThat(testProp).as("testProp was wrong").isEqualTo(VAL_BASIC);
this.curator.setData().forPath(KEY_BASIC_PATH, "testPropValUpdate".getBytes());
CountDownLatch latch = this.context.getBean(CountDownLatch.class);
boolean receivedEvent = latch.await(15, TimeUnit.SECONDS);
assertThat(receivedEvent).as("listener didn't receive event").isTrue();
testProp = this.environment.getProperty(KEY_BASIC);
assertThat(testProp).as("testProp was wrong after update")
.isEqualTo("testPropValUpdate");
}
@Configuration
@EnableAutoConfiguration
static class Config implements ApplicationListener<EnvironmentChangeEvent> {
@Bean
public CountDownLatch countDownLatch() {
return new CountDownLatch(1);
}
@Bean
public ContextRefresher contextRefresher(ConfigurableApplicationContext context,
RefreshScope scope) {
return new ConfigDataContextRefresher(context, scope);
}
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
log.debug("Event keys: " + event.getKeys());
if (event.getKeys().contains(KEY_BASIC)) {
countDownLatch().countDown();
}
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2015-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.zookeeper.config;
import org.assertj.core.api.Assertions;
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.boot.context.config.ConfigDataLocationNotFoundException;
import org.springframework.cloud.context.refresh.ConfigDataContextRefresher;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
public class ZookeeperConfigDataNotOptionalIntegrationTests {
@Test
public void configDataNotFoundThrowsException() {
Assertions.assertThatThrownBy(() -> {
ConfigurableApplicationContext context = null;
try {
context = new SpringApplicationBuilder(Config.class).web(WebApplicationType.NONE).run(
"--spring.cloud.zookeeper.connect-string=notexistantdomain:5000", "--debug=true",
"--spring.cloud.zookeeper.max-retries=0",
"--spring.cloud.zookeeper.blockUntilConnectedWait=1",
"--spring.cloud.zookeeper.blockUntilConnectedUnit=MILLISECONDS",
"--spring.cloud.zookeeper.connection-timeout=1ms",
"--spring.config.import=zookeeper:",
"--spring.application.name=testZkConfigDataNotOptionalIntegration",
"--logging.level.org.springframework.cloud.zookeeper=DEBUG",
"--spring.cloud.zookeeper.config.root=/shouldfail");
}
finally {
if (context != null) {
context.close();
}
}
}).isInstanceOf(ConfigDataLocationNotFoundException.class);
}
@Configuration
@EnableAutoConfiguration
static class Config {
@Bean
public ContextRefresher contextRefresher(ConfigurableApplicationContext context, RefreshScope scope) {
return new ConfigDataContextRefresher(context, scope);
}
}
}

View File

@@ -20,8 +20,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -78,7 +79,8 @@ public class ZookeeperPropertySourceLocatorFailFastTests {
.isNotNull();
}
@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
static class Config {
}

View File

@@ -30,9 +30,13 @@ import org.springframework.util.Assert;
* @author Spencer Gibb
* @since 1.0.0
*/
@ConfigurationProperties("spring.cloud.zookeeper")
@ConfigurationProperties(ZookeeperProperties.PREFIX)
public class ZookeeperProperties {
/**
* Configuration prefix.
*/
public static final String PREFIX = "spring.cloud.zookeeper";
/**
* Connection string to the Zookeeper cluster.
*/

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress files=".*ZookeeperPropertySourceLocatorTests\.java" checks="JavadocVariable"/>
</suppressions>