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.utils;
24 import static org.junit.jupiter.api.Assertions.assertEquals;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.util.Arrays;
31 import java.util.Objects;
32 import java.util.Properties;
34 import java.util.TreeSet;
35 import java.util.UUID;
36 import org.apache.commons.lang3.StringUtils;
37 import org.junit.jupiter.api.AfterAll;
38 import org.junit.jupiter.api.BeforeAll;
39 import org.junit.jupiter.api.Test;
40 import org.onap.policy.common.utils.logging.LoggerUtils;
41 import org.onap.policy.common.utils.security.CryptoUtils;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 public class PropertyUtilTest {
47 * Note: to generate the encrypted values, invoke CryptoUtils passing both the value
48 * to be encrypted and the crypto key.
50 * The INTERPOLATION_CRYPTO_KEY is a 16 or 32 character string, base-64 encoded.
52 * For "INTERPOLATION_ENC_HELLOWORLD", the encrypted value was generated via:
53 * java org.onap.policy.common.utils.security.CryptoUtils enc HelloWorld MTIzNDU2Nzg5MDEyMzQ1Ng==
55 * The generated value should also be placed into the following properties within
56 * the file, interpolation.properties:
59 * interpolation.envenc
62 private static final String INTERPOLATION_PROPERTIES = "src/test/resources/interpolation.properties";
63 private static final String INTERPOLATION_CRYPTO_KEY = "MTIzNDU2Nzg5MDEyMzQ1Ng==";
64 private static final String INTERPOLATION_PLAINTEXT = "HelloWorld";
65 private static final String INTERPOLATION_ENVD_DEFAULT_VALUE = "default";
66 private static final String INTERPOLATION_ENC_HELLOWORLD =
67 "enc:MjGhDZTTIx1ihB7KvxLnOJcvb0WN/CSgpw7sY1hDnvL1VHa8wGRzOX3X";
68 private static final String INTERPOLATION_ENC_HELLOWORLD_VAR = "${" + INTERPOLATION_ENC_HELLOWORLD + "}";
70 private static final String INTERPOLATION_NO = "interpolation.no";
71 private static final String INTERPOLATION_ENV = "interpolation.env";
72 private static final String INTERPOLATION_ENVD = "interpolation.envd";
73 private static final String INTERPOLATION_CONST = "interpolation.const";
74 private static final String INTERPOLATION_SYS = "interpolation.sys";
75 private static final String INTERPOLATION_ENVD_NONE = "interpolation.envd.none";
76 private static final String INTERPOLATION_ENVD_DEFAULT = "interpolation.envd.default";
77 private static final String INTERPOLATION_ENVD_NO_DEFAULT = "interpolation.envd.nodefault";
78 private static final String INTERPOLATION_ENC = "interpolation.enc";
79 private static final String INTERPOLATION_ENC2 = "interpolation.enc2";
80 private static final String INTERPOLATION_ENVENC = "interpolation.envenc";
83 private static final Logger logger = LoggerFactory.getLogger(PropertyUtilTest.class);
85 private static File directory = null;
88 * Test Setup -- Create a directory for temporary files.
91 public static void setup() {
92 logger.info("setup: creating a temporary directory");
94 // create a directory for temporary files
95 directory = new File(UUID.randomUUID().toString());
100 * Test Cleanup -- Remove temporary files.
103 public static void teardown() {
104 logger.info("teardown: remove the temporary directory");
106 // the assumption is that we only have one level of temporary files
107 for (File file : Objects.requireNonNull(directory.listFiles())) {
108 assertTrue(file.delete());
110 assertTrue(directory.delete());
114 * Utility method to write a properties file.
116 * @param name the file name, relative to the temporary directory
117 * @param properties to store in the file
118 * @return a File instance associated with the newly-created file
119 * @throws IOException if the file can't be created for some reason
121 File createFile(String name, Properties properties) throws IOException {
122 File file = new File(directory, name);
123 try (FileOutputStream fos = new FileOutputStream(file)) {
124 properties.store(fos, "Property file '" + name + "'");
130 * Create a 'PropertyUtil.Listener' subclass, which receives property
131 * file updates. It stores the latest values in an array, and notifies
132 * any thread waiting on this array.
134 * @param returns this is an array of length 2 -- the first entry will
135 * contain the 'properties' value, and the second will contain
136 * 'changedKeys'. It is also used to signal any waiting thread
137 * using 'returns.notifyAll()'.
139 PropertyUtil.Listener createListenerThread(final Object[] returns) {
140 return (new PropertyUtil.Listener() {
141 public void propertiesChanged(Properties properties, Set<String> changedKeys) {
142 // When a notification is received, store the values in the
143 // 'returns' array, and signal using the same array.
144 logger.info("Listener invoked: properties=" + properties
145 + ", changedKeys=" + changedKeys);
146 returns[0] = properties;
147 returns[1] = changedKeys;
148 synchronized (returns) {
156 * Test the basic properties file interface.
159 void testGetProperties() throws Exception {
160 logger.info("testGetProperties: test the basic properties file interface");
162 // copy system properties
163 logger.info("Copy system properties to a file");
164 Properties prop1 = System.getProperties();
165 File file1 = createFile("createAndReadPropertyFile-1", prop1);
167 // read in properties, and compare
168 logger.info("Read in properties from new file");
169 Properties prop2 = PropertyUtil.getProperties(file1);
172 assertEquals(prop1, prop2);
174 // tests performed in sequence
175 testGetCryptoCoderArg();
176 testGetNoCryptoProps();
177 testGetDefaultCryptoProps();
178 testGetNoCryptoSystemProps();
179 testGetCryptoArgSystemProps();
180 testGetDefaultCryptoSystemProps();
184 private void testGetDefaultCryptoSystemProps() throws IOException {
185 // system properties + default crypto coder
186 PropertyUtil.setDefaultCryptoCoder(new CryptoUtils(INTERPOLATION_CRYPTO_KEY));
187 PropertyUtil.setSystemProperties(PropertyUtil.getPropertiesFile(new File(INTERPOLATION_PROPERTIES)));
188 assertPropInterpolation(System.getProperties());
189 assertPropEncInterpolation(System.getProperties());
192 private void testGetCryptoArgSystemProps() throws IOException {
193 // system properties + crypto coder passed in
195 .setSystemProperties(PropertyUtil
196 .getPropertiesFile(new File(INTERPOLATION_PROPERTIES)), new CryptoUtils(INTERPOLATION_CRYPTO_KEY));
197 assertPropInterpolation(System.getProperties());
198 assertPropEncInterpolation(System.getProperties());
201 private void testGetNoCryptoSystemProps() throws IOException {
202 /* system properties + no crypto coder */
203 PropertyUtil.setDefaultCryptoCoder(null);
204 PropertyUtil.setSystemProperties(PropertyUtil.getPropertiesFile(new File(INTERPOLATION_PROPERTIES)));
205 assertPropInterpolation(System.getProperties());
206 assertPropNoEncInterpolation(System.getProperties());
209 private void testGetDefaultCryptoProps() throws IOException {
210 /* properties + default crypto coder */
211 PropertyUtil.setDefaultCryptoCoder(new CryptoUtils(INTERPOLATION_CRYPTO_KEY));
212 Properties props = PropertyUtil.getProperties(INTERPOLATION_PROPERTIES);
213 assertPropInterpolation(props);
214 assertPropEncInterpolation(props);
217 private void testGetNoCryptoProps() throws IOException {
218 /* properties + no crypto coder */
219 Properties props = PropertyUtil.getProperties(INTERPOLATION_PROPERTIES);
220 assertPropInterpolation(props);
221 assertPropNoEncInterpolation(props);
224 private void testGetCryptoCoderArg() throws IOException {
225 /* properties + crypto coder passed in */
227 PropertyUtil.getProperties(INTERPOLATION_PROPERTIES, new CryptoUtils(INTERPOLATION_CRYPTO_KEY));
228 assertPropInterpolation(props);
229 assertPropEncInterpolation(props);
232 private void assertPropNoEncInterpolation(Properties props) {
233 assertEquals(INTERPOLATION_ENC_HELLOWORLD_VAR, props.getProperty(INTERPOLATION_ENC));
234 assertEquals(INTERPOLATION_ENC_HELLOWORLD, props.getProperty(INTERPOLATION_ENC2));
235 assertEquals(INTERPOLATION_ENC_HELLOWORLD, props.getProperty(INTERPOLATION_ENVENC));
238 private void assertPropEncInterpolation(Properties props) {
239 assertEquals(INTERPOLATION_PLAINTEXT, props.getProperty(INTERPOLATION_ENC));
240 assertEquals(INTERPOLATION_PLAINTEXT, props.getProperty(INTERPOLATION_ENC2));
241 assertEquals(INTERPOLATION_PLAINTEXT, props.getProperty(INTERPOLATION_ENVENC));
244 private void assertPropInterpolation(Properties props) {
245 assertEquals("no", props.getProperty(INTERPOLATION_NO));
246 assertEquals(System.getenv("HOME"), props.getProperty(INTERPOLATION_ENV));
247 assertEquals(System.getenv("HOME"), props.getProperty(INTERPOLATION_ENVD));
248 assertEquals(StringUtils.EMPTY, props.getProperty(INTERPOLATION_ENVD_NONE));
249 assertEquals(StringUtils.EMPTY, props.getProperty(INTERPOLATION_ENVD_NO_DEFAULT));
250 assertEquals(LoggerUtils.ROOT_LOGGER, props.getProperty(INTERPOLATION_CONST));
251 assertEquals(System.getProperty("user.home"), props.getProperty(INTERPOLATION_SYS));
252 assertEquals(INTERPOLATION_ENVD_DEFAULT_VALUE, props.getProperty(INTERPOLATION_ENVD_DEFAULT));
256 * This tests the 'PropertyUtil.Listener' interface.
259 void testListenerInterface() throws Exception {
260 logger.info("testListenerInterface: test receipt of dynamic updates");
262 // create initial property file
263 Properties prop1 = new Properties();
264 prop1.setProperty("p1", "p1 value");
265 prop1.setProperty("p2", "p2 value");
266 prop1.setProperty("p3", "p3 value");
267 logger.info("Create initial properties file: " + prop1);
268 File file1 = createFile("createAndReadPropertyFile-2", prop1);
270 // create a listener for the notification interface
271 Object[] returns = new Object[2];
272 PropertyUtil.Listener listener = createListenerThread(returns);
274 // read it in, and do a comparison
275 Properties prop2 = PropertyUtil.getProperties(file1, listener);
276 logger.info("Read in properties: " + prop2);
277 assertEquals(prop1, prop2);
278 assertEquals("p1 value", prop2.getProperty("p1"));
279 assertEquals("p2 value", prop2.getProperty("p2"));
280 assertEquals("p3 value", prop2.getProperty("p3"));
282 // make some changes, and update the file (p3 is left unchanged)
283 prop2.remove("p1"); // remove one property
284 prop2.setProperty("p2", "new p2 value"); // change one property
285 prop2.setProperty("p4", "p4 value"); // add a new property
286 logger.info("Modified properties: " + prop2);
288 // now, update the file, and wait for notification
289 synchronized (returns) {
290 createFile("createAndReadPropertyFile-2", prop2);
292 // wait up to 60 seconds, although we should receive notification
293 // in 10 seconds or less (if things are working)
294 returns.wait(60000L);
297 // verify we have the updates
298 assertEquals(prop2, returns[0]);
300 // verify that we have the expected set of keys
301 assertEquals(new TreeSet<String>(Arrays.asList(new String[]{"p1", "p2", "p4"})),