Support for new Spring Boot ConfigData framework. (#265)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
7
src/checkstyle/checkstyle-suppressions.xml
Normal file
7
src/checkstyle/checkstyle-suppressions.xml
Normal 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>
|
||||
Reference in New Issue
Block a user