diff --git a/core-java-modules/core-java-properties/pom.xml b/core-java-modules/core-java-properties/pom.xml
index 67e0204c6e24..cc481127d6a8 100644
--- a/core-java-modules/core-java-properties/pom.xml
+++ b/core-java-modules/core-java-properties/pom.xml
@@ -1,7 +1,7 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
core-java-properties
0.1.0-SNAPSHOT
@@ -14,4 +14,21 @@
0.0.1-SNAPSHOT
-
\ No newline at end of file
+
+ 2.11.0
+ 1.10.1
+
+
+
+
+ org.apache.commons
+ commons-configuration2
+ ${apache.commons.version}
+
+
+ commons-beanutils
+ commons-beanutils
+ ${commons.beanutils.version}
+
+
+
diff --git a/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/ApacheCommonsPropertyMutator.java b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/ApacheCommonsPropertyMutator.java
new file mode 100644
index 000000000000..80a78f43adf4
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/ApacheCommonsPropertyMutator.java
@@ -0,0 +1,41 @@
+package com.baeldung.core.java.properties;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.FileBasedConfiguration;
+import org.apache.commons.configuration2.PropertiesConfiguration;
+import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
+import org.apache.commons.configuration2.builder.fluent.Parameters;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+
+public class ApacheCommonsPropertyMutator implements PropertyMutator {
+
+ private final String propertyFileName;
+
+ public ApacheCommonsPropertyMutator(String propertyFileName) {
+ this.propertyFileName = propertyFileName;
+ }
+
+ @Override
+ public String getProperty(String key) throws ConfigurationException {
+ FileBasedConfigurationBuilder builder = getAppPropertiesConfigBuilder();
+ Configuration properties = builder.getConfiguration();
+
+ return (String) properties.getProperty(key);
+ }
+
+ @Override
+ public void addOrUpdateProperty(String key, String value) throws ConfigurationException {
+ FileBasedConfigurationBuilder builder = getAppPropertiesConfigBuilder();
+ Configuration configuration = builder.getConfiguration();
+
+ configuration.setProperty(key, value);
+ builder.save();
+ }
+
+ private FileBasedConfigurationBuilder getAppPropertiesConfigBuilder() {
+ Parameters params = new Parameters();
+
+ return new FileBasedConfigurationBuilder(PropertiesConfiguration.class).configure(params.properties()
+ .setFileName(propertyFileName));
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/FileAPIPropertyMutator.java b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/FileAPIPropertyMutator.java
new file mode 100644
index 000000000000..13a4b023f1e0
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/FileAPIPropertyMutator.java
@@ -0,0 +1,78 @@
+package com.baeldung.core.java.properties;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileAPIPropertyMutator {
+
+ private final String propertyFileName;
+ private final PropertyLoader propertyLoader;
+
+ public FileAPIPropertyMutator(String propertyFileName, PropertyLoader propertyLoader) {
+ this.propertyFileName = propertyFileName;
+ this.propertyLoader = propertyLoader;
+ }
+
+ public String getPropertyKeyWithValue(int lineNumber) throws IOException {
+ List fileLines = getFileLines();
+ // depending on the system, sometimes the first line will be a comment with a timestamp of the file read
+ // the next line will make this method compatible with all systems
+ if (fileLines.get(0).startsWith("#")) {
+ lineNumber++;
+ }
+
+ return fileLines.get(lineNumber);
+ }
+
+ public String getLastPropertyKeyWithValue() throws IOException {
+ List fileLines = getFileLines();
+
+ return fileLines.get(fileLines.size() - 1);
+ }
+
+ public void addPropertyKeyWithValue(String keyAndValue) throws IOException {
+ File propertiesFile = new File(propertyLoader.getFilePathFromResources(propertyFileName));
+ List fileContent = getFileLines(propertiesFile);
+
+ fileContent.add(keyAndValue);
+ Files.write(propertiesFile.toPath(), fileContent, StandardCharsets.UTF_8);
+ }
+
+ public int updateProperty(String oldKeyValuePair, String newKeyValuePair) throws IOException {
+ File propertiesFile = new File(propertyLoader.getFilePathFromResources(propertyFileName));
+ List fileContent = getFileLines(propertiesFile);
+ int updatedIndex = -1;
+
+ for (int i = 0; i < fileContent.size(); i++) {
+ if (fileContent.get(i)
+ .replaceAll("\\s+", "")
+ .equals(oldKeyValuePair)) {
+ fileContent.set(i, newKeyValuePair);
+ updatedIndex = i;
+ break;
+ }
+ }
+ Files.write(propertiesFile.toPath(), fileContent, StandardCharsets.UTF_8);
+
+ // depending on the system, sometimes the first line will be a comment with a timestamp of the file read
+ // the next line will make this method compatible with all systems
+ if (fileContent.get(0).startsWith("#")) {
+ updatedIndex--;
+ }
+
+ return updatedIndex;
+ }
+
+ private List getFileLines() throws IOException {
+ File propertiesFile = new File(propertyLoader.getFilePathFromResources(propertyFileName));
+ return getFileLines(propertiesFile);
+ }
+
+ private List getFileLines(File propertiesFile) throws IOException {
+ return new ArrayList<>(Files.readAllLines(propertiesFile.toPath(), StandardCharsets.UTF_8));
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/FileStreamsPropertyMutator.java b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/FileStreamsPropertyMutator.java
new file mode 100644
index 000000000000..7ca19654372a
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/FileStreamsPropertyMutator.java
@@ -0,0 +1,33 @@
+package com.baeldung.core.java.properties;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class FileStreamsPropertyMutator implements PropertyMutator {
+
+ private final String propertyFileName;
+ private final PropertyLoader propertyLoader;
+
+ public FileStreamsPropertyMutator(String propertyFileName, PropertyLoader propertyLoader) {
+ this.propertyFileName = propertyFileName;
+ this.propertyLoader = propertyLoader;
+ }
+
+ @Override
+ public String getProperty(String key) throws IOException {
+ Properties properties = propertyLoader.fromFile(propertyFileName);
+
+ return properties.getProperty(key);
+ }
+
+ @Override
+ public void addOrUpdateProperty(String key, String value) throws IOException {
+ Properties properties = propertyLoader.fromFile(propertyFileName);
+ properties.setProperty(key, value);
+
+ FileOutputStream out = new FileOutputStream(propertyLoader.getFilePathFromResources(propertyFileName));
+ properties.store(out, null);
+ out.close();
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/PropertyLoader.java b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/PropertyLoader.java
new file mode 100644
index 000000000000..fb016afcca08
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/PropertyLoader.java
@@ -0,0 +1,29 @@
+package com.baeldung.core.java.properties;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Objects;
+import java.util.Properties;
+
+public class PropertyLoader {
+
+ public Properties fromFile(String filename) throws IOException {
+ String appPropertiesFileName = getFilePathFromResources(filename);
+ FileInputStream in = new FileInputStream(appPropertiesFileName);
+ Properties properties = new Properties();
+
+ properties.load(in);
+ in.close();
+
+ return properties;
+ }
+
+ public String getFilePathFromResources(String filename) {
+ URL resourceUrl = getClass().getClassLoader()
+ .getResource(filename);
+ Objects.requireNonNull(resourceUrl, "Property file with name [" + filename + "] was not found.");
+
+ return resourceUrl.getFile();
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/PropertyMutator.java b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/PropertyMutator.java
new file mode 100644
index 000000000000..a3da91e7b531
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/PropertyMutator.java
@@ -0,0 +1,12 @@
+package com.baeldung.core.java.properties;
+
+import java.io.IOException;
+
+import org.apache.commons.configuration2.ex.ConfigurationException;
+
+public interface PropertyMutator {
+
+ String getProperty(String key) throws IOException, ConfigurationException;
+
+ void addOrUpdateProperty(String key, String value) throws IOException, ConfigurationException;
+}
diff --git a/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/XMLFilePropertyMutator.java b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/XMLFilePropertyMutator.java
new file mode 100644
index 000000000000..63b63c778ea6
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/main/java/com/baeldung/core/java/properties/XMLFilePropertyMutator.java
@@ -0,0 +1,58 @@
+package com.baeldung.core.java.properties;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Objects;
+import java.util.Properties;
+
+public class XMLFilePropertyMutator implements PropertyMutator {
+
+ private final String propertyFileName;
+
+ public XMLFilePropertyMutator(String propertyFileName) {
+ this.propertyFileName = propertyFileName;
+ }
+
+ @Override
+ public String getProperty(String key) throws IOException {
+ Properties properties = loadProperties();
+
+ return properties.getProperty(key);
+ }
+
+ @Override
+ public void addOrUpdateProperty(String key, String value) throws IOException {
+ String filePath = getXMLAppPropertiesWithFileStreamFilePath();
+ Properties properties = loadProperties(filePath);
+
+ try (OutputStream os = Files.newOutputStream(Paths.get(filePath))) {
+ properties.setProperty(key, value);
+ properties.storeToXML(os, null);
+ }
+ }
+
+ private Properties loadProperties() throws IOException {
+ return loadProperties(getXMLAppPropertiesWithFileStreamFilePath());
+ }
+
+ private Properties loadProperties(String filepath) throws IOException {
+ Properties props = new Properties();
+ try (InputStream is = Files.newInputStream(Paths.get(filepath))) {
+ props.loadFromXML(is);
+ }
+
+ return props;
+ }
+
+ String getXMLAppPropertiesWithFileStreamFilePath() {
+ URL resourceUrl = getClass().getClassLoader()
+ .getResource(propertyFileName);
+ Objects.requireNonNull(resourceUrl, "Property file with name [" + propertyFileName + "] was not found.");
+
+ return resourceUrl.getFile();
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/ApacheCommonsPropertyMutatorUnitTest.java b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/ApacheCommonsPropertyMutatorUnitTest.java
new file mode 100644
index 000000000000..4213d6953c33
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/ApacheCommonsPropertyMutatorUnitTest.java
@@ -0,0 +1,56 @@
+package com.baeldung.core.java.properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class ApacheCommonsPropertyMutatorUnitTest {
+
+ private static final String PROPERTY_FILE_NAME = "app.properties";
+ private final PropertyLoader propertyLoader = new PropertyLoader();
+ private Properties initialProperties;
+
+ private final ApacheCommonsPropertyMutator propertyMutator = new ApacheCommonsPropertyMutator(PROPERTY_FILE_NAME);
+
+ @BeforeEach
+ public void loadInitialPropertiesFromFile() throws IOException {
+ initialProperties = propertyLoader.fromFile(PROPERTY_FILE_NAME);
+ }
+
+ @AfterEach
+ public void restoreInitialPropertiesToFile() throws IOException {
+ FileOutputStream out = new FileOutputStream(propertyLoader.getFilePathFromResources(PROPERTY_FILE_NAME));
+ initialProperties.store(out, null);
+ out.close();
+ }
+
+ @Test
+ public void givenApacheCommons_whenAddNonExistingProperty_thenNewPropertyWithoutAffectingOtherProperties() throws ConfigurationException {
+ assertNull(propertyMutator.getProperty("new.property"));
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+
+ propertyMutator.addOrUpdateProperty("new.property", "new-value");
+
+ assertEquals("new-value", propertyMutator.getProperty("new.property"));
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+ }
+
+ @Test
+ public void givenApacheCommons_whenUpdateExistingProperty_thenUpdatedPropertyWithoutAffectingOtherProperties() throws ConfigurationException {
+ assertEquals("1.0", propertyMutator.getProperty("version"));
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+
+ propertyMutator.addOrUpdateProperty("version", "2.0");
+
+ assertEquals("2.0", propertyMutator.getProperty("version"));
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/FileAPIPropertyMutatorUnitTest.java b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/FileAPIPropertyMutatorUnitTest.java
new file mode 100644
index 000000000000..1f66d34a04c1
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/FileAPIPropertyMutatorUnitTest.java
@@ -0,0 +1,52 @@
+package com.baeldung.core.java.properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class FileAPIPropertyMutatorUnitTest {
+
+ private static final String PROPERTY_FILE_NAME = "app.properties";
+ private final PropertyLoader propertyLoader = new PropertyLoader();
+ private Properties initialProperties;
+
+ private final FileAPIPropertyMutator propertyMutator = new FileAPIPropertyMutator(PROPERTY_FILE_NAME, propertyLoader);
+
+ @BeforeEach
+ public void loadInitialPropertiesFromFile() throws IOException {
+ initialProperties = propertyLoader.fromFile(PROPERTY_FILE_NAME);
+ }
+
+ @AfterEach
+ public void restoreInitialPropertiesToFile() throws IOException {
+ FileOutputStream out = new FileOutputStream(propertyLoader.getFilePathFromResources(PROPERTY_FILE_NAME));
+ initialProperties.store(out, null);
+ out.close();
+ }
+
+ @Test
+ public void givenFilesAPI_whenAddNonExistingProperty_thenNewPropertyWithoutAffectingOtherProperties() throws IOException {
+ assertEquals("name=TestApp", propertyMutator.getPropertyKeyWithValue(1));
+
+ propertyMutator.addPropertyKeyWithValue("new.property=new-value");
+
+ assertEquals("new.property=new-value", propertyMutator.getLastPropertyKeyWithValue());
+ assertEquals("name=TestApp", propertyMutator.getPropertyKeyWithValue(1));
+ }
+
+ @Test
+ public void givenFilesAPI_whenUpdateExistingProperty_thenUpdatedPropertyWithoutAffectingOtherProperties() throws IOException {
+ assertEquals("name=TestApp", propertyMutator.getPropertyKeyWithValue(1));
+
+ int updatedPropertyIndex = propertyMutator.updateProperty("version=1.0", "version=2.0");
+
+ assertEquals("version=2.0", propertyMutator.getPropertyKeyWithValue(updatedPropertyIndex));
+ assertEquals("name=TestApp", propertyMutator.getPropertyKeyWithValue(1));
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/FileStreamsPropertyMutatorUnitTest.java b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/FileStreamsPropertyMutatorUnitTest.java
new file mode 100644
index 000000000000..a6c20c0225ea
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/FileStreamsPropertyMutatorUnitTest.java
@@ -0,0 +1,55 @@
+package com.baeldung.core.java.properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class FileStreamsPropertyMutatorUnitTest {
+
+ private static final String PROPERTY_FILE_NAME = "app.properties";
+ private final PropertyLoader propertyLoader = new PropertyLoader();
+ private Properties initialProperties;
+
+ private final FileStreamsPropertyMutator propertyMutator = new FileStreamsPropertyMutator(PROPERTY_FILE_NAME, propertyLoader);
+
+ @BeforeEach
+ public void loadInitialPropertiesFromFile() throws IOException {
+ initialProperties = propertyLoader.fromFile(PROPERTY_FILE_NAME);
+ }
+
+ @AfterEach
+ public void restoreInitialPropertiesToFile() throws IOException {
+ FileOutputStream out = new FileOutputStream(propertyLoader.getFilePathFromResources(PROPERTY_FILE_NAME));
+ initialProperties.store(out, null);
+ out.close();
+ }
+
+ @Test
+ public void givenFileStreams_whenAddNonExistingProperty_thenNewPropertyWithoutAffectingOtherProperties() throws IOException {
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+ assertNull(propertyMutator.getProperty("new.property"));
+
+ propertyMutator.addOrUpdateProperty("new.property", "new-value");
+
+ assertEquals("new-value", propertyMutator.getProperty("new.property"));
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+ }
+
+ @Test
+ public void givenFileStreams_whenUpdateExistingProperty_thenUpdatedPropertyWithoutAffectingOtherProperties() throws IOException {
+ assertEquals("1.0", propertyMutator.getProperty("version"));
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+
+ propertyMutator.addOrUpdateProperty("version", "2.0");
+
+ assertEquals("2.0", propertyMutator.getProperty("version"));
+ assertEquals("TestApp", propertyMutator.getProperty("name"));
+ }
+}
diff --git a/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/XMLFilePropertyMutatorUnitTest.java b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/XMLFilePropertyMutatorUnitTest.java
new file mode 100644
index 000000000000..aa6ab4be90ec
--- /dev/null
+++ b/core-java-modules/core-java-properties/src/test/java/com/baeldung/core/java/properties/XMLFilePropertyMutatorUnitTest.java
@@ -0,0 +1,59 @@
+package com.baeldung.core.java.properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class XMLFilePropertyMutatorUnitTest {
+
+ Properties initialProperties = new Properties();
+ XMLFilePropertyMutator propertyMutator = new XMLFilePropertyMutator("icons.xml");
+
+ @BeforeEach
+ public void setUp() throws IOException {
+ String filepath = propertyMutator.getXMLAppPropertiesWithFileStreamFilePath();
+ try (InputStream is = Files.newInputStream(Paths.get(filepath))) {
+ initialProperties.loadFromXML(is);
+ }
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ String filepath = propertyMutator.getXMLAppPropertiesWithFileStreamFilePath();
+ try (OutputStream os = Files.newOutputStream(Paths.get(filepath))) {
+ initialProperties.storeToXML(os, null);
+ }
+ }
+
+ @Test
+ public void givenXMLPropertyFile_whenAddNonExistingProperty_thenNewPropertyWithoutAffectingOtherProperties() throws IOException {
+ assertEquals("icon1.jpg", propertyMutator.getProperty("fileIcon"));
+ assertNull(propertyMutator.getProperty("new.property"));
+
+ propertyMutator.addOrUpdateProperty("new.property", "new-value");
+
+ assertEquals("new-value", propertyMutator.getProperty("new.property"));
+ assertEquals("icon1.jpg", propertyMutator.getProperty("fileIcon"));
+ }
+
+ @Test
+ public void givenXMLPropertyFile_whenUpdateExistingProperty_thenUpdatedPropertyWithoutAffectingOtherProperties() throws IOException {
+ assertEquals("icon1.jpg", propertyMutator.getProperty("fileIcon"));
+ assertEquals("icon2.jpg", propertyMutator.getProperty("imageIcon"));
+
+ propertyMutator.addOrUpdateProperty("fileIcon", "icon5.jpg");
+
+ assertEquals("icon5.jpg", propertyMutator.getProperty("fileIcon"));
+ assertEquals("icon2.jpg", propertyMutator.getProperty("imageIcon"));
+ }
+}