ConfigurationManager.java
package de.dlr.bt.stc.config;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.configuration2.HierarchicalConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.FileBasedBuilderParameters;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.greenrobot.eventbus.EventBus;
import de.dlr.bt.stc.eventbus.ConfigurationModifiedEvent;
import de.dlr.bt.stc.eventbus.ConfigurationModifiedEvent.ConfigurationModificationType;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public final class ConfigurationManager {
private final List<Path> configurationPaths = new ArrayList<>();
private final Map<String, ACfg> configurations = new HashMap<>();
@Getter
private final EventBus instanceEventBus;
@Getter
private final EventBus managementEventBus;
public ConfigurationManager(EventBus instanceEventBus, EventBus managementEventBus) {
this.instanceEventBus = Objects.requireNonNull(instanceEventBus);
this.managementEventBus = Objects.requireNonNull(managementEventBus);
}
/**
* Add a {@link Path} (either designating a single configuration file or a
* directory containing .yml files) to the configuration list.
*
* @param path A {@link Path} designating either a single file or a folder
* containing configuration files. The folder is not read
* recursively.
*/
public void addConfigurationPath(Path path) {
configurationPaths.add(path);
}
/**
* Load all configuration files previously added using
* {@link #addConfigurationPath(Path)}.
*
*/
public void loadConfigurations() {
final List<Path> configurationFiles = new ArrayList<>();
findConfigurations(configurationPaths, configurationFiles);
loadConfiguration(configurationFiles);
}
private void findConfigurations(List<Path> paths, List<Path> results) {
for (var p : paths) {
findConfigurations(p, results);
}
}
private void findConfigurations(Path path, List<Path> results) {
if (Files.isRegularFile(path)) {
results.add(path);
} else if (Files.isDirectory(path)) {
try (var ds = Files.list(path)) {
var files = ds.filter(ConfigurationManager::isYAMLFile).sorted().toList();
findConfigurations(files, results);
} catch (IOException ioe) {
log.error("Failed to list files in directory {}: {}", path, ioe);
}
} else {
log.warn("Path supplied to ConfigurationManager which is neither regular nor directory: {}", path);
}
}
/**
* Check whether given path designates a YAML configuration file (regular file
* ending with .yml or .yaml)
*
* @param path The path to check
* @return Whether given path is a configuration file
*/
private static boolean isYAMLFile(Path path) {
if (!Files.isRegularFile(path))
return false;
var lowerCase = path.getFileName().toString().toLowerCase();
return lowerCase.endsWith(".yml") || lowerCase.endsWith(".yaml");
}
/**
* Load a single configuration file
*
* @param parameters {@link FileBasedBuilderParameters} specifying the
* configuration file
* @throws ConfigurationException
*/
private void loadConfiguration(List<Path> configurationFiles) {
List<HierarchicalConfiguration<?>> ymls = new ArrayList<>();
for (var file : configurationFiles) {
FileBasedConfigurationBuilder<YAMLConfigurationSTC> configBuilder = new FileBasedConfigurationBuilder<>(
YAMLConfigurationSTC.class);
configBuilder.configure(new Parameters().fileBased().setFile(file.toFile()));
try {
ymls.add(configBuilder.getConfiguration());
} catch (ConfigurationException ce) {
log.error("Failed to parse configuration file {}: {}", file, ce);
}
}
var loadedConfigurations = ConfigHandler.separateAndResolveConfigs(ymls);
for (var entry : loadedConfigurations.entrySet()) {
try {
ACfg cfg = CfgFactory.getInstance().create(entry.getValue().getString("type"), entry.getValue());
configurations.put(entry.getKey(), cfg);
managementEventBus
.post(new ConfigurationModifiedEvent(entry.getKey(), cfg, ConfigurationModificationType.ADD));
} catch (ConfigurationException ce) {
log.error("Failed to parse configuration item {}: {}", entry.getKey(), ce);
}
}
}
/**
* Unload all configuration files, but keep information about the files/folders
* (to allow reloading configuration)
*/
public void unloadConfiguration() {
for (var cfg : configurations.entrySet())
managementEventBus.post(
new ConfigurationModifiedEvent(cfg.getKey(), cfg.getValue(), ConfigurationModificationType.REMOVE));
configurations.clear();
}
/**
* Clear all information about configuration files, including the files/folders.
*/
public void clearConfiguration() {
unloadConfiguration();
configurationPaths.clear();
}
public Map<String, ACfg> getConfigurations() {
return Collections.unmodifiableMap(configurations);
}
}