From 0406208514c0dc6fd056e1310d13479a3b8d77fc Mon Sep 17 00:00:00 2001 From: Rasmus Jokinen <146736881+51-code@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:16:18 +0200 Subject: [PATCH 1/3] Support system properties as a configuration source Update README Support system properties as a configuration source --- README.adoc | 1 + .../cnf_01/PropertiesConfiguration.java | 107 ++++++++++++ .../cnf_01/PropertiesConfigurationTest.java | 154 ++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java create mode 100644 src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java diff --git a/README.adoc b/README.adoc index 9a227b6..39eb9bf 100644 --- a/README.adoc +++ b/README.adoc @@ -14,6 +14,7 @@ CNF-01 is a library that provides immutable configuration for Java projects. Imm // List your project's features - Provides immutable configurations for: . configuration files / path properties (`PathConfiguration`) +. System Properties (`PropertiesConfiguration`) . environment variables (`EnvironmentConfiguration`) - Default configurations in case the provided configurations from a source are not found or are otherwise broken (`DefaultConfiguration`) diff --git a/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java b/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java new file mode 100644 index 0000000..7cda258 --- /dev/null +++ b/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java @@ -0,0 +1,107 @@ +/* + * Teragrep Configuration Library for Java (cnf_01) + * Copyright (C) 2024 Suomen Kanuuna Oy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * + * Additional permission under GNU Affero General Public License version 3 + * section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with other code, such other code is not for that reason alone subject to any + * of the requirements of the GNU Affero GPL version 3 as long as this Program + * is the same Program as licensed from Suomen Kanuuna Oy without any additional + * modifications. + * + * Supplemented terms under GNU Affero General Public License version 3 + * section 7 + * + * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified + * versions must be marked as "Modified version of" The Program. + * + * Names of the licensors and authors may not be used for publicity purposes. + * + * No rights are granted for use of trade names, trademarks, or service marks + * which are in The Program if any. + * + * Licensee must indemnify licensors and authors for any liability that these + * contractual assumptions impose on licensors and authors. + * + * To the extent this program is licensed as part of the Commercial versions of + * Teragrep, the applicable Commercial License may apply to this file if you as + * a licensee so wish it. + */ +package com.teragrep.cnf_01; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * Provides unmodifiable configuration for Java's Properties object. Uses System properties by default if the + * constructor is not given a parameter. + */ +public final class PropertiesConfiguration implements Configuration { + + private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesConfiguration.class); + + private final Properties properties; + + public PropertiesConfiguration() { + this(System.getProperties()); + } + + public PropertiesConfiguration(final Properties properties) { + this.properties = properties; + } + + @Override + public Map asMap() { + final Map configuration = Collections + .unmodifiableMap( + properties + .entrySet() + .stream() + .collect(Collectors.toMap(k -> k.getKey().toString(), k -> k.getValue().toString())) + ); + + LOGGER.debug("Returning configuration map generated from properties."); + LOGGER.trace("Returning configuration map <[{}]>", configuration); + + return configuration; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + else if (o == null || getClass() != o.getClass()) { + return false; + } + final PropertiesConfiguration config = (PropertiesConfiguration) o; + return properties.equals(config.properties); + } + + @Override + public int hashCode() { + return Objects.hashCode(properties); + } +} diff --git a/src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java b/src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java new file mode 100644 index 0000000..ae3c271 --- /dev/null +++ b/src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java @@ -0,0 +1,154 @@ +/* + * Teragrep Configuration Library for Java (cnf_01) + * Copyright (C) 2024 Suomen Kanuuna Oy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * + * Additional permission under GNU Affero General Public License version 3 + * section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with other code, such other code is not for that reason alone subject to any + * of the requirements of the GNU Affero GPL version 3 as long as this Program + * is the same Program as licensed from Suomen Kanuuna Oy without any additional + * modifications. + * + * Supplemented terms under GNU Affero General Public License version 3 + * section 7 + * + * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified + * versions must be marked as "Modified version of" The Program. + * + * Names of the licensors and authors may not be used for publicity purposes. + * + * No rights are granted for use of trade names, trademarks, or service marks + * which are in The Program if any. + * + * Licensee must indemnify licensors and authors for any liability that these + * contractual assumptions impose on licensors and authors. + * + * To the extent this program is licensed as part of the Commercial versions of + * Teragrep, the applicable Commercial License may apply to this file if you as + * a licensee so wish it. + */ +package com.teragrep.cnf_01; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Properties; + +public class PropertiesConfigurationTest { + + @Test + public void testSystemProperties() { + System.setProperty("foo", "bar"); + System.setProperty("bar", "foo"); + + PropertiesConfiguration config = new PropertiesConfiguration(); // uses System.getProperties() + Map result = config.asMap(); + + System.clearProperty("foo"); + System.clearProperty("bar"); + + Assertions.assertEquals("bar", result.get("foo")); + Assertions.assertEquals("foo", result.get("bar")); + } + + @Test + public void testProperties() { + Properties properties = new Properties(); + properties.put("foo", "bar"); + properties.put("bar", "foo"); + + PropertiesConfiguration config = new PropertiesConfiguration(properties); + Map result = config.asMap(); + + // modifying the original properties doesn't modify the result map + properties.put("biz", "buz"); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals("bar", result.get("foo")); + Assertions.assertEquals("foo", result.get("bar")); + } + + @Test + public void testEmptyProperties() { + Properties properties = new Properties(); + + PropertiesConfiguration config = new PropertiesConfiguration(properties); + Map result = config.asMap(); + + Assertions.assertEquals(0, result.size()); + } + + @Test + public void testEqualsWithSystemProperties() { + PropertiesConfiguration config1 = new PropertiesConfiguration(); + PropertiesConfiguration config2 = new PropertiesConfiguration(); + + config1.asMap(); + Assertions.assertEquals(config1, config2); + } + + @Test + public void testEqualsWithProperties() { + Properties properties1 = new Properties(); + properties1.put("foo", "bar"); + + Properties properties2 = new Properties(); + properties2.put("foo", "bar"); + + PropertiesConfiguration config1 = new PropertiesConfiguration(properties1); + PropertiesConfiguration config2 = new PropertiesConfiguration(properties2); + + config1.asMap(); + Assertions.assertEquals(config1, config2); + } + + @Test + public void testNotEquals() { + Properties properties = new Properties(); + properties.put("foo", "bar"); + + PropertiesConfiguration config1 = new PropertiesConfiguration(); + PropertiesConfiguration config2 = new PropertiesConfiguration(properties); + + Assertions.assertNotEquals(config1, config2); + } + + @Test + public void testHashCode() { + Properties properties1 = new Properties(); + properties1.put("foo", "bar"); + + Properties properties2 = new Properties(); + properties2.put("foo", "bar"); + + PropertiesConfiguration config1 = new PropertiesConfiguration(properties1); + PropertiesConfiguration config2 = new PropertiesConfiguration(properties2); + PropertiesConfiguration difConfig = new PropertiesConfiguration(); + + Assertions.assertEquals(config1.hashCode(), config2.hashCode()); + Assertions.assertNotEquals(config1.hashCode(), difConfig.hashCode()); + } + + @Test + public void testEqualsVerifier() { + EqualsVerifier.forClass(PropertiesConfiguration.class).withNonnullFields("properties").verify(); + } +} From 4e4fc67fa18ee42d54da24eb3e7a8cd078caf5bc Mon Sep 17 00:00:00 2001 From: Rasmus Jokinen <146736881+51-code@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:24:08 +0200 Subject: [PATCH 2/3] Rewrite PropertiesConfiguration to use a Map instead of Properties as class variable, refine tests --- .../cnf_01/PropertiesConfiguration.java | 36 ++++++++++++------- .../cnf_01/PropertiesConfigurationTest.java | 14 +++++--- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java b/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java index 7cda258..939de83 100644 --- a/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java +++ b/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java @@ -62,26 +62,38 @@ public final class PropertiesConfiguration implements Configuration { private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesConfiguration.class); - private final Properties properties; + private final Map configuration; public PropertiesConfiguration() { this(System.getProperties()); } public PropertiesConfiguration(final Properties properties) { - this.properties = properties; + this( + Collections + .unmodifiableMap( + properties + .entrySet() + .stream() + .collect( + Collectors + .toMap(k -> k.getKey().toString(), k -> k.getValue().toString()) + ) + ) + ); + } + + /** + * Private constructor so that a map can't be given as parameter by users. It would break immutability. + * + * @param configuration the configuration as an immutable Map + */ + private PropertiesConfiguration(final Map configuration) { + this.configuration = configuration; } @Override public Map asMap() { - final Map configuration = Collections - .unmodifiableMap( - properties - .entrySet() - .stream() - .collect(Collectors.toMap(k -> k.getKey().toString(), k -> k.getValue().toString())) - ); - LOGGER.debug("Returning configuration map generated from properties."); LOGGER.trace("Returning configuration map <[{}]>", configuration); @@ -97,11 +109,11 @@ else if (o == null || getClass() != o.getClass()) { return false; } final PropertiesConfiguration config = (PropertiesConfiguration) o; - return properties.equals(config.properties); + return configuration.equals(config.configuration); } @Override public int hashCode() { - return Objects.hashCode(properties); + return Objects.hashCode(configuration); } } diff --git a/src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java b/src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java index ae3c271..c0b0354 100644 --- a/src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java +++ b/src/test/java/com/teragrep/cnf_01/PropertiesConfigurationTest.java @@ -60,11 +60,12 @@ public void testSystemProperties() { System.setProperty("bar", "foo"); PropertiesConfiguration config = new PropertiesConfiguration(); // uses System.getProperties() - Map result = config.asMap(); - + // clearing properties doesn't affect PropertiesConfiguration System.clearProperty("foo"); System.clearProperty("bar"); + Map result = config.asMap(); + Assertions.assertEquals("bar", result.get("foo")); Assertions.assertEquals("foo", result.get("bar")); } @@ -76,11 +77,11 @@ public void testProperties() { properties.put("bar", "foo"); PropertiesConfiguration config = new PropertiesConfiguration(properties); - Map result = config.asMap(); - // modifying the original properties doesn't modify the result map properties.put("biz", "buz"); + Map result = config.asMap(); + Assertions.assertEquals(2, result.size()); Assertions.assertEquals("bar", result.get("foo")); Assertions.assertEquals("foo", result.get("bar")); @@ -114,6 +115,9 @@ public void testEqualsWithProperties() { properties2.put("foo", "bar"); PropertiesConfiguration config1 = new PropertiesConfiguration(properties1); + // modifying properties1 doesn't modify immutable PropertiesConfiguration + properties1.put("bar", "foo"); + PropertiesConfiguration config2 = new PropertiesConfiguration(properties2); config1.asMap(); @@ -149,6 +153,6 @@ public void testHashCode() { @Test public void testEqualsVerifier() { - EqualsVerifier.forClass(PropertiesConfiguration.class).withNonnullFields("properties").verify(); + EqualsVerifier.forClass(PropertiesConfiguration.class).withNonnullFields("configuration").verify(); } } From b3dd20c529fa0d602a6e41ae82b7db121b87c73a Mon Sep 17 00:00:00 2001 From: Rasmus Jokinen <146736881+51-code@users.noreply.github.com> Date: Wed, 13 Nov 2024 08:45:03 +0200 Subject: [PATCH 3/3] Rename variable in PropertiesConfiguration --- .../java/com/teragrep/cnf_01/PropertiesConfiguration.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java b/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java index 939de83..bb492b5 100644 --- a/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java +++ b/src/main/java/com/teragrep/cnf_01/PropertiesConfiguration.java @@ -77,7 +77,10 @@ public PropertiesConfiguration(final Properties properties) { .stream() .collect( Collectors - .toMap(k -> k.getKey().toString(), k -> k.getValue().toString()) + .toMap( + entry -> entry.getKey().toString(), + entry -> entry.getValue().toString() + ) ) ) );