org.zodic.kubernetes.confcenter.reload.ConfigurationChangeDetector Maven / Gradle / Ivy
package org.zodic.kubernetes.confcenter.reload;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.PreDestroy;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.zodiac.core.bootstrap.config.AppPropertySourceLocator;
import org.zodiac.sdk.toolkit.util.collection.CollUtil;
public abstract class ConfigurationChangeDetector {
protected Logger log = LoggerFactory.getLogger(getClass());
protected ConfigurableEnvironment environment;
protected ConfigReloadInfo configReloadInfo;
protected KubernetesClient kubernetesClient;
protected ConfigurationUpdateStrategy strategy;
public ConfigurationChangeDetector(ConfigurableEnvironment environment, ConfigReloadInfo configReloadInfo,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy) {
this.environment = environment;
this.configReloadInfo = configReloadInfo;
this.kubernetesClient = kubernetesClient;
this.strategy = strategy;
}
@PreDestroy
public void shutdown() {
// Ensure the kubernetes client is cleaned up from spare threads when shutting
// down
this.kubernetesClient.close();
}
public void reloadProperties() {
this.log.info("Reloading using strategy: {}.", this.strategy.getName());
this.strategy.reload();
}
/**
* Determines if two property sources are different.
*
* @param mp1
* map property sources 1
* @param mp2
* map property sources 2
* @return {@code true} if source has changed
*/
protected boolean changed(MapPropertySource mp1, MapPropertySource mp2) {
if (mp1 == mp2) {
return false;
}
if (mp1 == null && mp2 != null || mp1 != null && mp2 == null) {
return true;
}
Map s1 = mp1.getSource();
Map s2 = mp2.getSource();
return s1 == null ? s2 != null : !s1.equals(s2);
}
protected boolean changed(List extends MapPropertySource> l1, List extends MapPropertySource> l2) {
if (l1.size() != l2.size()) {
this.log.debug("The current number of Confimap PropertySources does not match the ones loaded from the Kubernetes - No reload will take place");
return false;
}
for (int i = 0; i < l1.size(); i++) {
if (changed(l1.get(i), l2.get(i))) {
return true;
}
}
return false;
}
/**
* Finds one registered property source of the given type, logging a warning if multiple property sources of that
* type are available.
*
* @param
* property source type
* @param sourceClass
* class for which property sources will be searched for
* @return matched property source
*/
protected > S findPropertySource(Class sourceClass) {
List sources = findPropertySources(sourceClass);
if (sources.size() == 0) {
return null;
}
if (sources.size() > 1) {
this.log.warn("Found more than one property source of type {} .", sourceClass);
}
return sources.get(0);
}
/**
* @param
* property source type
* @param sourceClass
* class for which property sources will be found
* @return finds all registered property sources of the given type
*/
protected > List findPropertySources(Class sourceClass) {
List managedSources = new LinkedList<>();
LinkedList> sources = toLinkedList(this.environment.getPropertySources());
while (!sources.isEmpty()) {
PropertySource> source = sources.pop();
if (source instanceof CompositePropertySource) {
CompositePropertySource comp = (CompositePropertySource)source;
sources.addAll(comp.getPropertySources());
} else if (sourceClass.isInstance(source)) {
managedSources.add(sourceClass.cast(source));
}
}
return managedSources;
}
private LinkedList toLinkedList(Iterable it) {
LinkedList list = new LinkedList();
for (E e : it) {
list.add(e);
}
return list;
}
/**
* Returns a list of MapPropertySource that correspond to the current state of the system. This only handles the
* PropertySource objects that are returned.
*
* @param propertySourceLocator
* Spring's property source locator
* @param environment
* Spring environment
* @return A list of MapPropertySource that correspond to the current state of the system
*/
protected List locateMapPropertySources(AppPropertySourceLocator propertySourceLocator,
Environment environment) {
List result = CollUtil.list();
PropertySource> propertySource = propertySourceLocator.locatePropertySource(environment);
if (propertySource instanceof MapPropertySource) {
result.add((MapPropertySource)propertySource);
} else if (propertySource instanceof CompositePropertySource) {
result.addAll(((CompositePropertySource)propertySource).getPropertySources().stream()
.filter(p -> p instanceof MapPropertySource).map(p -> (MapPropertySource)p)
.collect(Collectors.toList()));
} else {
this.log.debug("Found property source that cannot be handled: {} .", propertySource.getClass());
}
return result;
}
}