BAEL-7617: Intro to Apache Commons Configuration (#16281)

* BAEL-7617: Intro to Apache Commons Configuration

* BAEL-7617: Review Comments

* BAEL-7617: Review Comments

* BAEL-7617: Review Comments
This commit is contained in:
brokenhardisk 2024-04-28 19:06:40 +02:00 committed by GitHub
parent 639614cd65
commit 3240f5b820
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 344 additions and 0 deletions

View File

@ -44,6 +44,21 @@
<version>${mockftpserver.version}</version> <version>${mockftpserver.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>${commons-configuration2.version}</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons-beanutils.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl</artifactId>
<version>${commons-jexl.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.tukaani</groupId> <groupId>org.tukaani</groupId>
<artifactId>xz</artifactId> <artifactId>xz</artifactId>
@ -63,6 +78,9 @@
<apache-commons-text.version>1.10.0</apache-commons-text.version> <apache-commons-text.version>1.10.0</apache-commons-text.version>
<commons-net.version>3.6</commons-net.version> <commons-net.version>3.6</commons-net.version>
<mockftpserver.version>2.7.1</mockftpserver.version> <mockftpserver.version>2.7.1</mockftpserver.version>
<commons-configuration2.version>2.10.0</commons-configuration2.version>
<commons-beanutils.version>1.9.4</commons-beanutils.version>
<commons-jexl.version>2.1.1</commons-jexl.version>
<xz.version>1.9</xz.version> <xz.version>1.9</xz.version>
<zstd-jni.version>1.5.5-11</zstd-jni.version> <zstd-jni.version>1.5.5-11</zstd-jni.version>
</properties> </properties>

View File

@ -0,0 +1,12 @@
package com.baeldung.commons.configuration;
import org.apache.commons.configuration2.ConfigurationDecoder;
import org.apache.commons.net.util.Base64;
public class CustomDecoder implements ConfigurationDecoder {
@Override
public String decode(String encodedValue) {
return new String(Base64.decodeBase64(encodedValue));
}
}

View File

@ -0,0 +1,7 @@
package com.baeldung.commons.configuration;
public class ExternalServices {
public static final String BAELDUNG_WEBSITE = "https://www.baeldung.com";
}

View File

@ -0,0 +1,169 @@
package com.baeldung.commons.configuration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.configuration2.AbstractConfiguration;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.ex.ConversionException;
import org.junit.jupiter.api.Test;
class ConfigurationClassUnitTest {
@Test
void givenPropertiesFile_whenReadingWithConfigurationClass_thenIsLoaded() throws ConfigurationException {
Configurations configs = new Configurations();
Configuration config = configs.properties(new File("src/test/resources/configuration/file.properties"));
String dbHost = config.getString("db.host");
int dbPort = config.getInt("db.port");
String dbUser = config.getString("db.user");
String dbPassword = config.getString("undefinedKey", "defaultValue");
assertEquals("baeldung.com", dbHost);
assertEquals(9999, dbPort);
assertEquals("admin", dbUser);
assertEquals("defaultValue", dbPassword);
}
@Test
void givenXMLFile_whenReadingWithConfigurationClass_thenIsLoaded() throws ConfigurationException {
Configurations configs = new Configurations();
XMLConfiguration config = configs.xml(new File("src/test/resources/configuration/hierarchical.xml"));
String appender = config.getString("appender[@name]");
List<String> encoderPatterns = config.getList(String.class, "appender.encoder.pattern");
String pattern1 = config.getString("appender.encoder.pattern(0)");
assertEquals("STDOUT", appender);
assertEquals(2, encoderPatterns.size());
assertEquals("Pattern1", pattern1);
}
@Test
void givenPropertiesFile_whenCopyingConfiguration_thenIsSuccessful() throws ConfigurationException {
Configurations configs = new Configurations();
Configuration baseConfig = configs.properties(new File("src/test/resources/configuration/file.properties"));
Configuration subConfig = new PropertiesConfiguration();
subConfig.addProperty("db.host", "baeldung");
subConfig.addProperty("db.driver", "dummyDriver");
((AbstractConfiguration) subConfig).copy(baseConfig);
String dbHost = subConfig.getString("db.host");
String dbDriver = subConfig.getString("db.driver");
int dbPort = subConfig.getInt("db.port");
String dbUser = subConfig.getString("db.user");
assertEquals("baeldung.com", dbHost);
assertEquals(9999, dbPort);
assertEquals("admin", dbUser);
assertEquals("dummyDriver", dbDriver);
}
@Test
public void givenPropertiesFile_whenAppendingConfiguration_thenIsSuccessful() throws ConfigurationException {
Configurations configs = new Configurations();
Configuration baseConfig = configs.properties(new File("src/test/resources/configuration/file.properties"));
Configuration subConfig = new PropertiesConfiguration();
subConfig.addProperty("db.host", "baeldung");
subConfig.addProperty("db.driver", "dummyDriver");
((AbstractConfiguration) subConfig).append(baseConfig);
String dbHost = subConfig.getString("db.host");
String dbDriver = subConfig.getString("db.driver");
int dbPort = subConfig.getInt("db.port");
String dbUser = subConfig.getString("db.user");
assertEquals("baeldung", dbHost);
assertEquals(9999, dbPort);
assertEquals("admin", dbUser);
assertEquals("dummyDriver", dbDriver);
}
@Test
void givenXMLFile_whenCloningConfiguration_thenIsSuccessful() throws ConfigurationException {
Configurations configs = new Configurations();
XMLConfiguration baseConfig = configs.xml(new File("src/test/resources/configuration/hierarchical.xml"));
XMLConfiguration subConfig = new XMLConfiguration();
//subConfig = (XMLConfiguration) baseConfig.clone();
subConfig = new XMLConfiguration(baseConfig);
String appender = subConfig.getString("appender[@name]");
List<String> encoderPatterns = subConfig.getList(String.class, "appender.encoder.pattern");
String pattern1 = subConfig.getString("appender.encoder.pattern(0)");
assertEquals("STDOUT", appender);
assertEquals(2, encoderPatterns.size());
assertEquals("Pattern1", pattern1);
}
@Test
void givenEncodedProperty_whenCustomDecoderImplemented_thenIsSuccessful() throws ConfigurationException {
Configurations configs = new Configurations();
Configuration config = configs.properties(new File("src/test/resources/configuration/file.properties"));
((AbstractConfiguration) config).setConfigurationDecoder(new CustomDecoder());
assertEquals("mySecretString", config.getEncodedString("db.password"));
}
@Test
void whenDataTypeConversionAttempted_thenIsSuccessful() {
Configuration config = new PropertiesConfiguration();
config.addProperty("stringProperty", "This is a string");
config.addProperty("numericProperty", "9999");
config.addProperty("booleanProperty", "true");
assertEquals("This is a string", config.getString("stringProperty"));
assertEquals(9999, config.getInt("numericProperty"));
assertTrue(config.getBoolean("booleanProperty"));
}
@Test
void whenDataTypeConversionAttempted_thenThrowsException() {
Configuration config = new PropertiesConfiguration();
config.addProperty("numericProperty", "9999a");
assertThrows(ConversionException.class, () -> config.getInt("numericProperty"));
}
@Test
void whenInterpolationIsAttempted_thenIsSuccessful() throws ConfigurationException {
System.setProperty("user.name", "Baeldung");
Configurations configs = new Configurations();
Configuration config = configs.properties(new File("src/test/resources/configuration/file.properties"));
String dbUrl = config.getString("db.url");
String userName = config.getString("db.username");
String externalService = config.getString("db.external-service");
assertEquals("baeldung.com:9999", dbUrl);
assertEquals("Baeldung", userName);
assertEquals("https://www.baeldung.com", externalService);
}
@Test
void whenDelimiterIsSpecified_thenMultiValuePropertyIsLoaded() {
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
propertiesConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
propertiesConfig.addProperty("delimitedProperty", "admin;read-only;read-write");
propertiesConfig.addProperty("arrayProperty", "value1;value2");
List<Object> delimitedProperties = propertiesConfig.getList("delimitedProperty");
String[] arrayProperties = propertiesConfig.getStringArray("arrayProperty");
assertEquals(3, delimitedProperties.size());
assertEquals("admin", delimitedProperties.get(0));
assertEquals(2, arrayProperties.length);
assertEquals("value1", propertiesConfig.getString("arrayProperty"));
}
@Test
void whenPropertiesAreMissing_thenIsHandled() {
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
String objectProperty = propertiesConfig.getString("anyProperty");
int primitiveProperty = propertiesConfig.getInt("anyProperty", 1);
assertNull(objectProperty);
assertEquals(1, primitiveProperty);
}
@Test
void whenPropertiesAreMissing_thenExceptionIsThrown() {
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
assertThrows(NoSuchElementException.class, () -> propertiesConfig.getInt("anyProperty"));
}
}

View File

@ -0,0 +1,70 @@
package com.baeldung.commons.configuration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
import org.apache.commons.configuration2.interpol.ExprLookup;
import org.apache.commons.configuration2.interpol.Lookup;
import org.junit.jupiter.api.Test;
class FileBasedConfigurationBuilderUnitTest {
@Test
void whenReadingPropertiesFile_thenIsSuccessful() throws ConfigurationException {
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration> builder = new FileBasedConfigurationBuilder<FileBasedConfiguration>(
PropertiesConfiguration.class).configure(params.properties()
.setFileName("src/test/resources/configuration/file1.properties"));
Configuration config = builder.getConfiguration();
String dbHost = config.getString("db.host");
int dbPort = config.getInt("db.port");
String dbUser = config.getString("db.user");
assertEquals("baeldung.com", dbHost);
assertEquals(9999, dbPort);
assertEquals("admin", dbUser);
}
@Test
void whenReadingXMLFile_thenIsSuccessful() throws ConfigurationException {
Parameters params = new Parameters();
FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>(XMLConfiguration.class).configure(params.xml()
.setFileName("src/test/resources/configuration/hierarchical.xml")
.setValidating(true));
XMLConfiguration config = builder.getConfiguration();
String appender = config.getString("appender[@name]");
List<String> encoderPatterns = config.getList(String.class, "appender.encoder.pattern");
assertEquals("STDOUT", appender);
assertEquals(2, encoderPatterns.size());
}
@Test
void whenExpressionEvaluationIsAttempted_thenIsSuccessful() throws ConfigurationException {
System.setProperty("user.home", "/usr/lib");
Parameters params = new Parameters();
Map<String, Lookup> lookups = new HashMap<>(ConfigurationInterpolator.getDefaultPrefixLookups());
ExprLookup.Variables variables = new ExprLookup.Variables();
variables.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
ExprLookup exprLookup = new ExprLookup(variables);
exprLookup.setInterpolator(new ConfigurationInterpolator());
lookups.put("expr", exprLookup);
FileBasedConfigurationBuilder<FileBasedConfiguration> builder = new FileBasedConfigurationBuilder<FileBasedConfiguration>(
PropertiesConfiguration.class).configure(params.properties()
.setFileName("src/test/resources/configuration/file1.properties")
.setPrefixLookups(lookups));
Configuration config = builder.getConfiguration();
String dbDumpLocation = config.getString("db.data-dump-location");
assertEquals("/usr/lib/dump.dat", dbDumpLocation);
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.commons.configuration;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
import org.junit.jupiter.api.Test;
class MultiFileConfigurationBuilderUnitTest {
@Test
void givenMultiplePropertyFiles_whenReadingWithMultiFileConfigurationBuilder_thenIsLoaded() throws ConfigurationException {
System.setProperty("tenant", "A");
String filePattern = "src/test/resources/configuration/tenant-${sys:tenant}.properties";
MultiFileConfigurationBuilder<PropertiesConfiguration> builder = new MultiFileConfigurationBuilder<>(PropertiesConfiguration.class).configure(
new Parameters().multiFile()
.setFilePattern(filePattern)
.setPrefixLookups(ConfigurationInterpolator.getDefaultPrefixLookups()));
Configuration config = builder.getConfiguration();
String tenantAName = config.getString("name");
assertEquals("Tenant A", tenantAName);
}
}

View File

@ -0,0 +1,8 @@
db.host=baeldung.com
db.port=9999
db.user=admin
db.password=bXlTZWNyZXRTdHJpbmc=
db.url=${db.host}:${db.port}
db.username=${sys:user.name}
db.external-service=${const:com.baeldung.commons.configuration.ExternalServices.BAELDUNG_WEBSITE}
db.data-dump-location=${expr:System.getProperty("user.home")}/dump.dat

View File

@ -0,0 +1,4 @@
db.host=baeldung.com
include=file.properties
includeOptional=file2.properties

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration SYSTEM "validation-sample.dtd">
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>Pattern1</pattern>
<pattern>Pattern2</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1 @@
name=Tenant A

View File

@ -0,0 +1,13 @@
<!ELEMENT configuration (appender+, root)>
<!ELEMENT appender (encoder?)>
<!ATTLIST appender
name CDATA #REQUIRED
class CDATA #REQUIRED
>
<!ELEMENT encoder (pattern+)>
<!ELEMENT pattern (#PCDATA)>
<!ELEMENT root (appender-ref+)>
<!ELEMENT appender-ref EMPTY>
<!ATTLIST appender-ref
ref CDATA #REQUIRED
>