2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2024 Nordix Foundation.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.drools.persistence;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.nio.file.StandardCopyOption;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Comparator;
34 import java.util.List;
35 import java.util.Properties;
36 import java.util.function.BiPredicate;
38 import lombok.ToString;
39 import org.onap.policy.drools.properties.DroolsPropertyConstants;
40 import org.onap.policy.drools.utils.PropertyUtil;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * Properties based Persistence.
49 public class FileSystemPersistence implements SystemPersistence {
52 * Properties file extension.
54 public static final String PROPERTIES_FILE_EXTENSION = ".properties";
57 * Policy controllers suffix.
59 public static final String CONTROLLER_SUFFIX_IDENTIFIER = "-controller";
64 public static final String FILE_BACKUP_SUFFIX = ".bak";
67 * Policy controller properties file suffix.
69 public static final String PROPERTIES_FILE_CONTROLLER_SUFFIX =
70 CONTROLLER_SUFFIX_IDENTIFIER + PROPERTIES_FILE_EXTENSION;
73 * Topic configuration suffix.
75 public static final String TOPIC_SUFFIX_IDENTIFIER = "-topic";
78 * Topic properties file suffix.
80 public static final String PROPERTIES_FILE_TOPIC_SUFFIX = TOPIC_SUFFIX_IDENTIFIER + PROPERTIES_FILE_EXTENSION;
83 * Policy engine properties file name.
85 public static final String PROPERTIES_FILE_ENGINE = "engine" + PROPERTIES_FILE_EXTENSION;
87 public static final String HTTP_SERVER_SUFFIX_IDENTIFIER = "-http-server";
88 public static final String PROPERTIES_FILE_HTTP_SERVER_SUFFIX =
89 HTTP_SERVER_SUFFIX_IDENTIFIER + PROPERTIES_FILE_EXTENSION;
91 public static final String HTTP_CLIENT_SUFFIX_IDENTIFIER = "-http-client";
92 public static final String PROPERTIES_FILE_HTTP_CLIENT_SUFFIX =
93 HTTP_CLIENT_SUFFIX_IDENTIFIER + PROPERTIES_FILE_EXTENSION;
96 * Installation environment suffix for files.
98 public static final String ENV_FILE_SUFFIX = ".environment";
101 * Environment properties extension.
103 public static final String ENV_FILE_EXTENSION = ENV_FILE_SUFFIX;
106 * Installation environment suffix for files.
108 public static final String SYSTEM_PROPERTIES_SUFFIX = "-system";
109 public static final String SYSTEM_PROPERTIES_FILE_SUFFIX = SYSTEM_PROPERTIES_SUFFIX + PROPERTIES_FILE_EXTENSION;
114 private static final Logger logger = LoggerFactory.getLogger(FileSystemPersistence.class);
117 * Configuration directory.
119 protected Path configurationPath = Paths.get(SystemPersistenceConstants.DEFAULT_CONFIGURATION_DIR);
123 public void setConfigurationDir(String configDir) {
124 String tempConfigDir = configDir;
126 if (tempConfigDir == null) {
127 tempConfigDir = SystemPersistenceConstants.DEFAULT_CONFIGURATION_DIR;
128 this.configurationPath = Paths.get(SystemPersistenceConstants.DEFAULT_CONFIGURATION_DIR);
131 if (!tempConfigDir.equals(SystemPersistenceConstants.DEFAULT_CONFIGURATION_DIR)) {
132 this.configurationPath = Paths.get(tempConfigDir);
135 setConfigurationDir();
139 public void setConfigurationDir() {
140 if (Files.notExists(this.configurationPath)) {
142 Files.createDirectories(this.configurationPath);
143 } catch (final IOException e) {
144 throw new IllegalStateException("cannot create " + this.configurationPath, e);
148 if (!Files.isDirectory(this.configurationPath)) {
149 throw new IllegalStateException(
150 "config directory: " + this.configurationPath + " is not a directory");
154 protected Properties getProperties(Path propertiesPath) {
155 if (!Files.exists(propertiesPath)) {
156 throw new IllegalArgumentException("properties for " + propertiesPath + " are not persisted.");
160 return PropertyUtil.getProperties(propertiesPath.toFile());
161 } catch (final Exception e) {
162 throw new IllegalArgumentException("can't read properties for " + propertiesPath, e);
167 public Properties getProperties(String name) {
168 if (name == null || name.isEmpty()) {
169 throw new IllegalArgumentException("properties name must be provided");
172 var propertiesPath = Paths.get(this.configurationPath.toString());
173 if (name.endsWith(PROPERTIES_FILE_EXTENSION) || name.endsWith(ENV_FILE_EXTENSION)) {
174 propertiesPath = propertiesPath.resolve(name);
176 propertiesPath = propertiesPath.resolve(name + PROPERTIES_FILE_EXTENSION);
179 return getProperties(propertiesPath);
182 protected List<Properties> getPropertiesList(String suffix) {
183 return getPropertiesList(suffix, (name, props) -> true);
186 protected List<Properties> getPropertiesList(String suffix, BiPredicate<String, Properties> preCondition) {
187 List<Properties> properties = new ArrayList<>();
188 File[] files = this.sortedListFiles();
189 for (File file : files) {
190 if (file.getName().endsWith(suffix)) {
191 addToPropertiesList(file, properties, preCondition);
197 private void addToPropertiesList(File file, List<Properties> properties,
198 BiPredicate<String, Properties> preCondition) {
200 var proposedProps = getProperties(file.getName());
201 if (preCondition.test(file.getName(), proposedProps)) {
202 properties.add(proposedProps);
204 } catch (final Exception e) {
205 logger.error("{}: cannot get properties {} because of {}", this, file.getName(),
211 public Properties getEnvironmentProperties(String name) {
212 if (name == null || name.isEmpty()) {
213 throw new IllegalArgumentException("environment name must be provided");
216 return this.getProperties(Paths.get(this.configurationPath.toString(), name + ENV_FILE_SUFFIX));
220 public List<Properties> getEnvironmentProperties() {
221 return getPropertiesList(ENV_FILE_SUFFIX);
225 public Properties getSystemProperties(String name) {
226 return this.getProperties(name + SYSTEM_PROPERTIES_SUFFIX);
230 public List<Properties> getSystemProperties() {
231 return getPropertiesList(SYSTEM_PROPERTIES_FILE_SUFFIX);
235 public Properties getEngineProperties() {
236 return this.getProperties(PROPERTIES_FILE_ENGINE);
240 public Properties getControllerProperties(String controllerName) {
241 return this.getProperties(controllerName + CONTROLLER_SUFFIX_IDENTIFIER);
245 public List<Properties> getControllerProperties() {
246 return getPropertiesList(PROPERTIES_FILE_CONTROLLER_SUFFIX, this::testControllerName);
250 public Properties getTopicProperties(String topicName) {
251 return this.getProperties(topicName + TOPIC_SUFFIX_IDENTIFIER);
255 public List<Properties> getTopicProperties() {
256 return getPropertiesList(PROPERTIES_FILE_TOPIC_SUFFIX);
260 public Properties getHttpServerProperties(String serverName) {
261 return this.getProperties(serverName + HTTP_SERVER_SUFFIX_IDENTIFIER);
265 public List<Properties> getHttpServerProperties() {
266 return getPropertiesList(PROPERTIES_FILE_HTTP_SERVER_SUFFIX);
270 public Properties getHttpClientProperties(String clientName) {
271 return this.getProperties(clientName + HTTP_CLIENT_SUFFIX_IDENTIFIER);
275 public List<Properties> getHttpClientProperties() {
276 return getPropertiesList(PROPERTIES_FILE_HTTP_CLIENT_SUFFIX);
279 private boolean testControllerName(String controllerFilename, Properties controllerProperties) {
280 var controllerName = controllerFilename
281 .substring(0, controllerFilename.length() - PROPERTIES_FILE_CONTROLLER_SUFFIX.length());
282 String controllerPropName = controllerProperties.getProperty(DroolsPropertyConstants.PROPERTY_CONTROLLER_NAME);
283 if (controllerPropName == null) {
284 controllerProperties.setProperty(DroolsPropertyConstants.PROPERTY_CONTROLLER_NAME, controllerName);
285 } else if (!controllerPropName.equals(controllerName)) {
286 logger.error("{}: mismatch controller named {} against {} in file {}",
287 this, controllerPropName, controllerName, controllerFilename);
295 public boolean backupController(String controllerName) {
296 return backup(controllerName, PROPERTIES_FILE_CONTROLLER_SUFFIX);
300 public boolean backupTopic(String topicName) {
301 return backup(topicName, PROPERTIES_FILE_TOPIC_SUFFIX);
305 public boolean backupHttpServer(String serverName) {
306 return backup(serverName, PROPERTIES_FILE_HTTP_SERVER_SUFFIX);
310 public boolean backupHttpClient(String clientName) {
311 return backup(clientName, PROPERTIES_FILE_HTTP_CLIENT_SUFFIX);
314 protected boolean backup(String name, String fileSuffix) {
315 var path = Paths.get(this.configurationPath.toString(), name + fileSuffix);
316 if (Files.exists(path)) {
318 logger.info("{}: there is an existing configuration file @ {} ", this, path);
319 var bakPath = Paths.get(this.configurationPath.toString(),
320 name + fileSuffix + FILE_BACKUP_SUFFIX);
321 Files.copy(path, bakPath, StandardCopyOption.REPLACE_EXISTING);
322 } catch (Exception e) {
323 logger.warn("{}: {} cannot be backed up", this, name, e);
331 public boolean storeController(String controllerName, Object configuration) {
332 checkPropertiesParam(configuration);
333 return store(controllerName, (Properties) configuration, PROPERTIES_FILE_CONTROLLER_SUFFIX);
337 public boolean storeTopic(String topicName, Object configuration) {
338 checkPropertiesParam(configuration);
339 return store(topicName, (Properties) configuration, PROPERTIES_FILE_TOPIC_SUFFIX);
343 public boolean storeHttpServer(String serverName, Object configuration) {
344 checkPropertiesParam(configuration);
345 return store(serverName, (Properties) configuration, PROPERTIES_FILE_HTTP_SERVER_SUFFIX);
349 public boolean storeHttpClient(String clientName, Object configuration) {
350 checkPropertiesParam(configuration);
351 return store(clientName, (Properties) configuration, PROPERTIES_FILE_HTTP_CLIENT_SUFFIX);
354 private boolean store(String name, Properties properties, String fileSuffix) {
355 var path = Paths.get(this.configurationPath.toString(), name + fileSuffix);
356 if (Files.exists(path)) {
358 var oldProperties = PropertyUtil.getProperties(path.toFile());
359 if (oldProperties.equals(properties)) {
360 logger.info("{}: noop: a properties file with the same contents exists for controller {}.", this,
364 this.backupController(name);
366 } catch (Exception e) {
367 logger.info("{}: no existing {} properties", this, name, e);
372 var file = path.toFile();
373 try (var writer = new FileWriter(file)) {
374 properties.store(writer, "Machine created Policy Controller Configuration");
375 } catch (Exception e) {
376 logger.warn("{}: {} cannot be saved", this, name, e);
383 private void checkPropertiesParam(Object configuration) {
384 if (!(configuration instanceof Properties)) {
385 throw new IllegalArgumentException(
386 "configuration must be of type properties to be handled by this manager");
392 public boolean deleteController(String controllerName) {
393 return delete(controllerName, PROPERTIES_FILE_CONTROLLER_SUFFIX);
397 public boolean deleteTopic(String topicName) {
398 return delete(topicName, PROPERTIES_FILE_TOPIC_SUFFIX);
402 public boolean deleteHttpServer(String serverName) {
403 return delete(serverName, PROPERTIES_FILE_HTTP_SERVER_SUFFIX);
407 public boolean deleteHttpClient(String clientName) {
408 return delete(clientName, PROPERTIES_FILE_HTTP_CLIENT_SUFFIX);
411 protected boolean delete(String name, String fileSuffix) {
412 var path = Paths.get(this.configurationPath.toString(), name + fileSuffix);
414 if (Files.exists(path)) {
416 var bakPath = Paths.get(this.configurationPath.toString(),
417 name + fileSuffix + FILE_BACKUP_SUFFIX);
418 Files.move(path, bakPath, StandardCopyOption.REPLACE_EXISTING);
419 } catch (final Exception e) {
420 logger.warn("{}: {} cannot be deleted", this, name, e);
429 * provides a list of files sorted by name in ascending order in the configuration directory.
431 private File[] sortedListFiles() {
432 File[] dirFiles = this.configurationPath.toFile().listFiles();
433 if (dirFiles != null) {
434 Arrays.sort(dirFiles, Comparator.comparing(File::getName));
436 dirFiles = new File[]{};