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()
+ )
)
)
);