1 package de.dlr.bt.stc.config;
2
3 import java.io.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Objects;
12
13 import org.apache.commons.configuration2.HierarchicalConfiguration;
14 import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
15 import org.apache.commons.configuration2.builder.fluent.FileBasedBuilderParameters;
16 import org.apache.commons.configuration2.builder.fluent.Parameters;
17 import org.apache.commons.configuration2.ex.ConfigurationException;
18 import org.greenrobot.eventbus.EventBus;
19
20 import de.dlr.bt.stc.eventbus.ConfigurationModifiedEvent;
21 import de.dlr.bt.stc.eventbus.ConfigurationModifiedEvent.ConfigurationModificationType;
22 import lombok.Getter;
23 import lombok.extern.slf4j.Slf4j;
24
25 @Slf4j
26 public final class ConfigurationManager {
27 private final List<Path> configurationPaths = new ArrayList<>();
28
29 private final Map<String, ACfg> configurations = new HashMap<>();
30
31 @Getter
32 private final EventBus instanceEventBus;
33 @Getter
34 private final EventBus managementEventBus;
35
36 public ConfigurationManager(EventBus instanceEventBus, EventBus managementEventBus) {
37 this.instanceEventBus = Objects.requireNonNull(instanceEventBus);
38 this.managementEventBus = Objects.requireNonNull(managementEventBus);
39 }
40
41
42
43
44
45
46
47
48
49 public void addConfigurationPath(Path path) {
50 configurationPaths.add(path);
51 }
52
53
54
55
56
57
58 public void loadConfigurations() {
59 final List<Path> configurationFiles = new ArrayList<>();
60 findConfigurations(configurationPaths, configurationFiles);
61 loadConfiguration(configurationFiles);
62 }
63
64 private void findConfigurations(List<Path> paths, List<Path> results) {
65 for (var p : paths) {
66 findConfigurations(p, results);
67 }
68 }
69
70 private void findConfigurations(Path path, List<Path> results) {
71 if (Files.isRegularFile(path)) {
72 results.add(path);
73 } else if (Files.isDirectory(path)) {
74 try (var ds = Files.list(path)) {
75 var files = ds.filter(ConfigurationManager::isYAMLFile).sorted().toList();
76
77 findConfigurations(files, results);
78 } catch (IOException ioe) {
79 log.error("Failed to list files in directory {}: {}", path, ioe);
80 }
81 } else {
82 log.warn("Path supplied to ConfigurationManager which is neither regular nor directory: {}", path);
83 }
84 }
85
86
87
88
89
90
91
92
93 private static boolean isYAMLFile(Path path) {
94 if (!Files.isRegularFile(path))
95 return false;
96
97 var lowerCase = path.getFileName().toString().toLowerCase();
98
99 return lowerCase.endsWith(".yml") || lowerCase.endsWith(".yaml");
100 }
101
102
103
104
105
106
107
108
109 private void loadConfiguration(List<Path> configurationFiles) {
110 List<HierarchicalConfiguration<?>> ymls = new ArrayList<>();
111 for (var file : configurationFiles) {
112
113 FileBasedConfigurationBuilder<YAMLConfigurationSTC> configBuilder = new FileBasedConfigurationBuilder<>(
114 YAMLConfigurationSTC.class);
115
116 configBuilder.configure(new Parameters().fileBased().setFile(file.toFile()));
117
118 try {
119 ymls.add(configBuilder.getConfiguration());
120 } catch (ConfigurationException ce) {
121 log.error("Failed to parse configuration file {}: {}", file, ce);
122 }
123 }
124
125 var loadedConfigurations = ConfigHandler.separateAndResolveConfigs(ymls);
126
127 for (var entry : loadedConfigurations.entrySet()) {
128 try {
129 ACfg cfg = CfgFactory.getInstance().create(entry.getValue().getString("type"), entry.getValue());
130 configurations.put(entry.getKey(), cfg);
131 managementEventBus
132 .post(new ConfigurationModifiedEvent(entry.getKey(), cfg, ConfigurationModificationType.ADD));
133 } catch (ConfigurationException ce) {
134 log.error("Failed to parse configuration item {}: {}", entry.getKey(), ce);
135 }
136 }
137 }
138
139
140
141
142
143 public void unloadConfiguration() {
144 for (var cfg : configurations.entrySet())
145 managementEventBus.post(
146 new ConfigurationModifiedEvent(cfg.getKey(), cfg.getValue(), ConfigurationModificationType.REMOVE));
147
148 configurations.clear();
149 }
150
151
152
153
154 public void clearConfiguration() {
155 unloadConfiguration();
156
157 configurationPaths.clear();
158 }
159
160 public Map<String, ACfg> getConfigurations() {
161 return Collections.unmodifiableMap(configurations);
162 }
163
164 }