Skip to content

Commit 3240f5b

Browse files
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
1 parent 639614c commit 3240f5b

File tree

11 files changed

+344
-0
lines changed

11 files changed

+344
-0
lines changed

libraries-apache-commons-2/pom.xml

+18
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,21 @@
4444
<version>${mockftpserver.version}</version>
4545
<scope>test</scope>
4646
</dependency>
47+
<dependency>
48+
<groupId>org.apache.commons</groupId>
49+
<artifactId>commons-configuration2</artifactId>
50+
<version>${commons-configuration2.version}</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>commons-beanutils</groupId>
54+
<artifactId>commons-beanutils</artifactId>
55+
<version>${commons-beanutils.version}</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.apache.commons</groupId>
59+
<artifactId>commons-jexl</artifactId>
60+
<version>${commons-jexl.version}</version>
61+
</dependency>
4762
<dependency>
4863
<groupId>org.tukaani</groupId>
4964
<artifactId>xz</artifactId>
@@ -63,6 +78,9 @@
6378
<apache-commons-text.version>1.10.0</apache-commons-text.version>
6479
<commons-net.version>3.6</commons-net.version>
6580
<mockftpserver.version>2.7.1</mockftpserver.version>
81+
<commons-configuration2.version>2.10.0</commons-configuration2.version>
82+
<commons-beanutils.version>1.9.4</commons-beanutils.version>
83+
<commons-jexl.version>2.1.1</commons-jexl.version>
6684
<xz.version>1.9</xz.version>
6785
<zstd-jni.version>1.5.5-11</zstd-jni.version>
6886
</properties>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.baeldung.commons.configuration;
2+
3+
import org.apache.commons.configuration2.ConfigurationDecoder;
4+
import org.apache.commons.net.util.Base64;
5+
6+
public class CustomDecoder implements ConfigurationDecoder {
7+
8+
@Override
9+
public String decode(String encodedValue) {
10+
return new String(Base64.decodeBase64(encodedValue));
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.baeldung.commons.configuration;
2+
3+
public class ExternalServices {
4+
5+
public static final String BAELDUNG_WEBSITE = "https://www.baeldung.com";
6+
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package com.baeldung.commons.configuration;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import java.io.File;
9+
import java.util.List;
10+
import java.util.NoSuchElementException;
11+
12+
import org.apache.commons.configuration2.AbstractConfiguration;
13+
import org.apache.commons.configuration2.Configuration;
14+
import org.apache.commons.configuration2.PropertiesConfiguration;
15+
import org.apache.commons.configuration2.XMLConfiguration;
16+
import org.apache.commons.configuration2.builder.fluent.Configurations;
17+
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
18+
import org.apache.commons.configuration2.ex.ConfigurationException;
19+
import org.apache.commons.configuration2.ex.ConversionException;
20+
import org.junit.jupiter.api.Test;
21+
22+
class ConfigurationClassUnitTest {
23+
24+
@Test
25+
void givenPropertiesFile_whenReadingWithConfigurationClass_thenIsLoaded() throws ConfigurationException {
26+
Configurations configs = new Configurations();
27+
Configuration config = configs.properties(new File("src/test/resources/configuration/file.properties"));
28+
String dbHost = config.getString("db.host");
29+
int dbPort = config.getInt("db.port");
30+
String dbUser = config.getString("db.user");
31+
String dbPassword = config.getString("undefinedKey", "defaultValue");
32+
assertEquals("baeldung.com", dbHost);
33+
assertEquals(9999, dbPort);
34+
assertEquals("admin", dbUser);
35+
assertEquals("defaultValue", dbPassword);
36+
}
37+
38+
@Test
39+
void givenXMLFile_whenReadingWithConfigurationClass_thenIsLoaded() throws ConfigurationException {
40+
Configurations configs = new Configurations();
41+
XMLConfiguration config = configs.xml(new File("src/test/resources/configuration/hierarchical.xml"));
42+
String appender = config.getString("appender[@name]");
43+
List<String> encoderPatterns = config.getList(String.class, "appender.encoder.pattern");
44+
String pattern1 = config.getString("appender.encoder.pattern(0)");
45+
assertEquals("STDOUT", appender);
46+
assertEquals(2, encoderPatterns.size());
47+
assertEquals("Pattern1", pattern1);
48+
}
49+
50+
@Test
51+
void givenPropertiesFile_whenCopyingConfiguration_thenIsSuccessful() throws ConfigurationException {
52+
Configurations configs = new Configurations();
53+
Configuration baseConfig = configs.properties(new File("src/test/resources/configuration/file.properties"));
54+
Configuration subConfig = new PropertiesConfiguration();
55+
subConfig.addProperty("db.host", "baeldung");
56+
subConfig.addProperty("db.driver", "dummyDriver");
57+
((AbstractConfiguration) subConfig).copy(baseConfig);
58+
String dbHost = subConfig.getString("db.host");
59+
String dbDriver = subConfig.getString("db.driver");
60+
int dbPort = subConfig.getInt("db.port");
61+
String dbUser = subConfig.getString("db.user");
62+
assertEquals("baeldung.com", dbHost);
63+
assertEquals(9999, dbPort);
64+
assertEquals("admin", dbUser);
65+
assertEquals("dummyDriver", dbDriver);
66+
}
67+
68+
@Test
69+
public void givenPropertiesFile_whenAppendingConfiguration_thenIsSuccessful() throws ConfigurationException {
70+
Configurations configs = new Configurations();
71+
Configuration baseConfig = configs.properties(new File("src/test/resources/configuration/file.properties"));
72+
Configuration subConfig = new PropertiesConfiguration();
73+
subConfig.addProperty("db.host", "baeldung");
74+
subConfig.addProperty("db.driver", "dummyDriver");
75+
((AbstractConfiguration) subConfig).append(baseConfig);
76+
String dbHost = subConfig.getString("db.host");
77+
String dbDriver = subConfig.getString("db.driver");
78+
int dbPort = subConfig.getInt("db.port");
79+
String dbUser = subConfig.getString("db.user");
80+
assertEquals("baeldung", dbHost);
81+
assertEquals(9999, dbPort);
82+
assertEquals("admin", dbUser);
83+
assertEquals("dummyDriver", dbDriver);
84+
}
85+
86+
@Test
87+
void givenXMLFile_whenCloningConfiguration_thenIsSuccessful() throws ConfigurationException {
88+
Configurations configs = new Configurations();
89+
XMLConfiguration baseConfig = configs.xml(new File("src/test/resources/configuration/hierarchical.xml"));
90+
XMLConfiguration subConfig = new XMLConfiguration();
91+
//subConfig = (XMLConfiguration) baseConfig.clone();
92+
subConfig = new XMLConfiguration(baseConfig);
93+
String appender = subConfig.getString("appender[@name]");
94+
List<String> encoderPatterns = subConfig.getList(String.class, "appender.encoder.pattern");
95+
String pattern1 = subConfig.getString("appender.encoder.pattern(0)");
96+
assertEquals("STDOUT", appender);
97+
assertEquals(2, encoderPatterns.size());
98+
assertEquals("Pattern1", pattern1);
99+
}
100+
101+
@Test
102+
void givenEncodedProperty_whenCustomDecoderImplemented_thenIsSuccessful() throws ConfigurationException {
103+
Configurations configs = new Configurations();
104+
Configuration config = configs.properties(new File("src/test/resources/configuration/file.properties"));
105+
((AbstractConfiguration) config).setConfigurationDecoder(new CustomDecoder());
106+
assertEquals("mySecretString", config.getEncodedString("db.password"));
107+
}
108+
109+
@Test
110+
void whenDataTypeConversionAttempted_thenIsSuccessful() {
111+
Configuration config = new PropertiesConfiguration();
112+
config.addProperty("stringProperty", "This is a string");
113+
config.addProperty("numericProperty", "9999");
114+
config.addProperty("booleanProperty", "true");
115+
assertEquals("This is a string", config.getString("stringProperty"));
116+
assertEquals(9999, config.getInt("numericProperty"));
117+
assertTrue(config.getBoolean("booleanProperty"));
118+
}
119+
120+
@Test
121+
void whenDataTypeConversionAttempted_thenThrowsException() {
122+
Configuration config = new PropertiesConfiguration();
123+
config.addProperty("numericProperty", "9999a");
124+
assertThrows(ConversionException.class, () -> config.getInt("numericProperty"));
125+
}
126+
127+
@Test
128+
void whenInterpolationIsAttempted_thenIsSuccessful() throws ConfigurationException {
129+
System.setProperty("user.name", "Baeldung");
130+
Configurations configs = new Configurations();
131+
Configuration config = configs.properties(new File("src/test/resources/configuration/file.properties"));
132+
String dbUrl = config.getString("db.url");
133+
String userName = config.getString("db.username");
134+
String externalService = config.getString("db.external-service");
135+
assertEquals("baeldung.com:9999", dbUrl);
136+
assertEquals("Baeldung", userName);
137+
assertEquals("https://www.baeldung.com", externalService);
138+
}
139+
140+
@Test
141+
void whenDelimiterIsSpecified_thenMultiValuePropertyIsLoaded() {
142+
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
143+
propertiesConfig.setListDelimiterHandler(new DefaultListDelimiterHandler(';'));
144+
propertiesConfig.addProperty("delimitedProperty", "admin;read-only;read-write");
145+
propertiesConfig.addProperty("arrayProperty", "value1;value2");
146+
List<Object> delimitedProperties = propertiesConfig.getList("delimitedProperty");
147+
String[] arrayProperties = propertiesConfig.getStringArray("arrayProperty");
148+
assertEquals(3, delimitedProperties.size());
149+
assertEquals("admin", delimitedProperties.get(0));
150+
assertEquals(2, arrayProperties.length);
151+
assertEquals("value1", propertiesConfig.getString("arrayProperty"));
152+
}
153+
154+
@Test
155+
void whenPropertiesAreMissing_thenIsHandled() {
156+
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
157+
String objectProperty = propertiesConfig.getString("anyProperty");
158+
int primitiveProperty = propertiesConfig.getInt("anyProperty", 1);
159+
assertNull(objectProperty);
160+
assertEquals(1, primitiveProperty);
161+
}
162+
163+
@Test
164+
void whenPropertiesAreMissing_thenExceptionIsThrown() {
165+
PropertiesConfiguration propertiesConfig = new PropertiesConfiguration();
166+
assertThrows(NoSuchElementException.class, () -> propertiesConfig.getInt("anyProperty"));
167+
}
168+
169+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.baeldung.commons.configuration;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.HashMap;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
import org.apache.commons.configuration2.Configuration;
10+
import org.apache.commons.configuration2.FileBasedConfiguration;
11+
import org.apache.commons.configuration2.PropertiesConfiguration;
12+
import org.apache.commons.configuration2.XMLConfiguration;
13+
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
14+
import org.apache.commons.configuration2.builder.fluent.Parameters;
15+
import org.apache.commons.configuration2.ex.ConfigurationException;
16+
import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
17+
import org.apache.commons.configuration2.interpol.ExprLookup;
18+
import org.apache.commons.configuration2.interpol.Lookup;
19+
import org.junit.jupiter.api.Test;
20+
21+
class FileBasedConfigurationBuilderUnitTest {
22+
23+
@Test
24+
void whenReadingPropertiesFile_thenIsSuccessful() throws ConfigurationException {
25+
Parameters params = new Parameters();
26+
FileBasedConfigurationBuilder<FileBasedConfiguration> builder = new FileBasedConfigurationBuilder<FileBasedConfiguration>(
27+
PropertiesConfiguration.class).configure(params.properties()
28+
.setFileName("src/test/resources/configuration/file1.properties"));
29+
Configuration config = builder.getConfiguration();
30+
String dbHost = config.getString("db.host");
31+
int dbPort = config.getInt("db.port");
32+
String dbUser = config.getString("db.user");
33+
assertEquals("baeldung.com", dbHost);
34+
assertEquals(9999, dbPort);
35+
assertEquals("admin", dbUser);
36+
}
37+
38+
@Test
39+
void whenReadingXMLFile_thenIsSuccessful() throws ConfigurationException {
40+
Parameters params = new Parameters();
41+
FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>(XMLConfiguration.class).configure(params.xml()
42+
.setFileName("src/test/resources/configuration/hierarchical.xml")
43+
.setValidating(true));
44+
XMLConfiguration config = builder.getConfiguration();
45+
String appender = config.getString("appender[@name]");
46+
List<String> encoderPatterns = config.getList(String.class, "appender.encoder.pattern");
47+
assertEquals("STDOUT", appender);
48+
assertEquals(2, encoderPatterns.size());
49+
}
50+
51+
@Test
52+
void whenExpressionEvaluationIsAttempted_thenIsSuccessful() throws ConfigurationException {
53+
System.setProperty("user.home", "/usr/lib");
54+
Parameters params = new Parameters();
55+
Map<String, Lookup> lookups = new HashMap<>(ConfigurationInterpolator.getDefaultPrefixLookups());
56+
ExprLookup.Variables variables = new ExprLookup.Variables();
57+
variables.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
58+
ExprLookup exprLookup = new ExprLookup(variables);
59+
exprLookup.setInterpolator(new ConfigurationInterpolator());
60+
lookups.put("expr", exprLookup);
61+
FileBasedConfigurationBuilder<FileBasedConfiguration> builder = new FileBasedConfigurationBuilder<FileBasedConfiguration>(
62+
PropertiesConfiguration.class).configure(params.properties()
63+
.setFileName("src/test/resources/configuration/file1.properties")
64+
.setPrefixLookups(lookups));
65+
Configuration config = builder.getConfiguration();
66+
String dbDumpLocation = config.getString("db.data-dump-location");
67+
assertEquals("/usr/lib/dump.dat", dbDumpLocation);
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.baeldung.commons.configuration;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.apache.commons.configuration2.Configuration;
6+
import org.apache.commons.configuration2.PropertiesConfiguration;
7+
import org.apache.commons.configuration2.builder.combined.MultiFileConfigurationBuilder;
8+
import org.apache.commons.configuration2.builder.fluent.Parameters;
9+
import org.apache.commons.configuration2.ex.ConfigurationException;
10+
import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
11+
import org.junit.jupiter.api.Test;
12+
13+
class MultiFileConfigurationBuilderUnitTest {
14+
15+
@Test
16+
void givenMultiplePropertyFiles_whenReadingWithMultiFileConfigurationBuilder_thenIsLoaded() throws ConfigurationException {
17+
System.setProperty("tenant", "A");
18+
String filePattern = "src/test/resources/configuration/tenant-${sys:tenant}.properties";
19+
MultiFileConfigurationBuilder<PropertiesConfiguration> builder = new MultiFileConfigurationBuilder<>(PropertiesConfiguration.class).configure(
20+
new Parameters().multiFile()
21+
.setFilePattern(filePattern)
22+
.setPrefixLookups(ConfigurationInterpolator.getDefaultPrefixLookups()));
23+
Configuration config = builder.getConfiguration();
24+
String tenantAName = config.getString("name");
25+
26+
assertEquals("Tenant A", tenantAName);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
db.host=baeldung.com
2+
db.port=9999
3+
db.user=admin
4+
db.password=bXlTZWNyZXRTdHJpbmc=
5+
db.url=${db.host}:${db.port}
6+
db.username=${sys:user.name}
7+
db.external-service=${const:com.baeldung.commons.configuration.ExternalServices.BAELDUNG_WEBSITE}
8+
db.data-dump-location=${expr:System.getProperty("user.home")}/dump.dat
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
db.host=baeldung.com
2+
3+
include=file.properties
4+
includeOptional=file2.properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE configuration SYSTEM "validation-sample.dtd">
3+
<configuration>
4+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
5+
<encoder>
6+
<pattern>Pattern1</pattern>
7+
<pattern>Pattern2</pattern>
8+
</encoder>
9+
</appender>
10+
11+
<root>
12+
<appender-ref ref="STDOUT" />
13+
</root>
14+
</configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
name=Tenant A
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!ELEMENT configuration (appender+, root)>
2+
<!ELEMENT appender (encoder?)>
3+
<!ATTLIST appender
4+
name CDATA #REQUIRED
5+
class CDATA #REQUIRED
6+
>
7+
<!ELEMENT encoder (pattern+)>
8+
<!ELEMENT pattern (#PCDATA)>
9+
<!ELEMENT root (appender-ref+)>
10+
<!ELEMENT appender-ref EMPTY>
11+
<!ATTLIST appender-ref
12+
ref CDATA #REQUIRED
13+
>

0 commit comments

Comments
 (0)