From 9ab810aba15aa20cdd9be6e4c82875e8d3cad241 Mon Sep 17 00:00:00 2001 From: vizsoro Date: Mon, 27 Aug 2018 08:39:44 +0200 Subject: [PATCH 1/3] loading json props --- .../ConfigPropertiesDemoApplication.java | 8 ++- .../properties/CustomJsonProperties.java | 71 +++++++++++++++++++ .../baeldung/properties/JsonProperties.java | 64 +++++++++++++++++ .../JsonPropertyContextInitializer.java | 68 ++++++++++++++++++ .../properties/JsonPropertySourceFactory.java | 20 ++++++ .../src/main/resources/configprops.json | 10 +++ .../src/main/resources/configpropscustom.json | 9 +++ .../JsonPropertiesIntegrationTest.java | 59 +++++++++++++++ 8 files changed, 306 insertions(+), 3 deletions(-) create mode 100644 spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java create mode 100644 spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java create mode 100644 spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java create mode 100644 spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java create mode 100644 spring-boot/src/main/resources/configprops.json create mode 100644 spring-boot/src/main/resources/configpropscustom.json create mode 100644 spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java diff --git a/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java b/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java index cb0304fc41..7ca0ab4f60 100644 --- a/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java +++ b/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java @@ -1,13 +1,15 @@ package org.baeldung.properties; -import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; @EnableAutoConfiguration -@ComponentScan(basePackageClasses = ConfigProperties.class) +@ComponentScan(basePackageClasses = { ConfigProperties.class, JsonProperties.class, CustomJsonProperties.class }) public class ConfigPropertiesDemoApplication { public static void main(String[] args) { - SpringApplication.run(ConfigPropertiesDemoApplication.class); + new SpringApplicationBuilder(ConfigPropertiesDemoApplication.class).initializers(new JsonPropertyContextInitializer()) + .run(); } + } diff --git a/spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java b/spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java new file mode 100644 index 0000000000..9d5e8ce780 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java @@ -0,0 +1,71 @@ +package org.baeldung.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "custom") +public class CustomJsonProperties { + + private String host; + + private int port; + + private boolean resend; + + private Person sender; + + public static class Person { + + private String name; + private String address; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isResend() { + return resend; + } + + public void setResend(boolean resend) { + this.resend = resend; + } + + public Person getSender() { + return sender; + } + + public void setSender(Person sender) { + this.sender = sender; + } +} diff --git a/spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java b/spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java new file mode 100644 index 0000000000..2e6e24036e --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java @@ -0,0 +1,64 @@ +package org.baeldung.properties; + +import java.util.LinkedHashMap; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@PropertySource(value = "classpath:configprops.json", factory = JsonPropertySourceFactory.class) +@ConfigurationProperties +public class JsonProperties { + + private String host; + + private int port; + + private boolean resend; + + private List topics; + + private LinkedHashMap sender; + + public LinkedHashMap getSender() { + return sender; + } + + public void setSender(LinkedHashMap sender) { + this.sender = sender; + } + + public List getTopics() { + return topics; + } + + public void setTopics(List topics) { + this.topics = topics; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isResend() { + return resend; + } + + public void setResend(boolean resend) { + this.resend = resend; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } +} diff --git a/spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java new file mode 100644 index 0000000000..27abadb1e1 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java @@ -0,0 +1,68 @@ +package org.baeldung.properties; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.Resource; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonPropertyContextInitializer implements ApplicationContextInitializer { + + private final static String CUSTOM_PREFIX = "custom."; + + @Override + @SuppressWarnings("unchecked") + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + try { + Resource resource = configurableApplicationContext.getResource("classpath:configpropscustom.json"); + Map readValue = new ObjectMapper().readValue(resource.getInputStream(), Map.class); + Set set = readValue.entrySet(); + List propertySources = convertEntrySet(set, Optional.empty()); + for (PropertySource propertySource : propertySources) { + configurableApplicationContext.getEnvironment() + .getPropertySources() + .addFirst(propertySource); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static List convertEntrySet(Set entrySet, Optional parentKey) { + return entrySet.stream() + .map((Map.Entry e) -> convertToPropertySourceList(e, parentKey)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + private static List convertToPropertySourceList(Map.Entry e, Optional parentKey) { + String key = parentKey.map(s -> s + ".") + .orElse("") + (String) e.getKey(); + Object value = e.getValue(); + return covertToPropertySourceList(key, value); + } + + @SuppressWarnings("unchecked") + private static List covertToPropertySourceList(String key, Object value) { + if (value instanceof LinkedHashMap) { + LinkedHashMap map = (LinkedHashMap) value; + Set entrySet = map.entrySet(); + return convertEntrySet(entrySet, Optional.ofNullable(key)); + } + String finalKey = CUSTOM_PREFIX + key; + return Collections.singletonList(new MapPropertySource(finalKey, Collections.singletonMap(finalKey, value))); + } + +} \ No newline at end of file diff --git a/spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java new file mode 100644 index 0000000000..7e5739fd41 --- /dev/null +++ b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java @@ -0,0 +1,20 @@ +package org.baeldung.properties; + +import java.io.IOException; +import java.util.Map; + +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.io.support.EncodedResource; +import org.springframework.core.io.support.PropertySourceFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonPropertySourceFactory implements PropertySourceFactory { + + @Override + public org.springframework.core.env.PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { + Map readValue = new ObjectMapper().readValue(resource.getInputStream(), Map.class); + return new MapPropertySource("json-property", readValue); + } + +} \ No newline at end of file diff --git a/spring-boot/src/main/resources/configprops.json b/spring-boot/src/main/resources/configprops.json new file mode 100644 index 0000000000..1602663775 --- /dev/null +++ b/spring-boot/src/main/resources/configprops.json @@ -0,0 +1,10 @@ +{ + "host" : "mailer@mail.com", + "port" : 9090, + "resend" : true, + "topics" : ["spring", "boot"], + "sender" : { + "name": "sender", + "address": "street" + } +} diff --git a/spring-boot/src/main/resources/configpropscustom.json b/spring-boot/src/main/resources/configpropscustom.json new file mode 100644 index 0000000000..5449876be3 --- /dev/null +++ b/spring-boot/src/main/resources/configpropscustom.json @@ -0,0 +1,9 @@ +{ + "host" : "mailer.custom@mail.com", + "port" : 8081, + "resend" : true, + "sender" : { + "name": "sender.custom", + "address": "street.custom" + } +} diff --git a/spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java new file mode 100644 index 0000000000..20a19e043a --- /dev/null +++ b/spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java @@ -0,0 +1,59 @@ +package org.baeldung.properties; + +import java.util.Arrays; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = ConfigPropertiesDemoApplication.class, initializers = JsonPropertyContextInitializer.class) +public class JsonPropertiesIntegrationTest { + + @Autowired + private JsonProperties jsonProperties; + + @Autowired + private CustomJsonProperties customJsonProperties; + + @Test + public void givenJsonPropertySource_whenPropertySourceFactoryUsed_thenLoadFlatValues() { + Assert.assertEquals("mailer@mail.com", jsonProperties.getHost()); + Assert.assertEquals(9090, jsonProperties.getPort()); + Assert.assertTrue(jsonProperties.isResend()); + } + + @Test + public void givenJsonPropertySource_whenPropertySourceFactoryUsed_thenLoadListValues() { + Assert.assertThat(jsonProperties.getTopics(), Matchers.is(Arrays.asList("spring", "boot"))); + } + + @Test + public void givenJsonPropertySource_whenPropertySourceFactoryUsed_thenNestedValuesLoadedAsMap() { + Assert.assertEquals("sender", jsonProperties.getSender() + .get("name")); + Assert.assertEquals("street", jsonProperties.getSender() + .get("address")); + } + + @Test + public void givenCustomJsonPropertySource_whenLoadedIntoEnvironment_thenFlatValuesPopulated() { + Assert.assertEquals("mailer.custom@mail.com", customJsonProperties.getHost()); + Assert.assertEquals(8081, customJsonProperties.getPort()); + Assert.assertTrue(customJsonProperties.isResend()); + } + + @Test + public void givenCustomJsonPropertySource_whenLoadedIntoEnvironment_thenValuesLoadedIntoClassObject() { + Assert.assertNotNull(customJsonProperties.getSender()); + Assert.assertEquals("sender.custom", customJsonProperties.getSender() + .getName()); + Assert.assertEquals("street.custom", customJsonProperties.getSender() + .getAddress()); + } + +} From ae8efcd3286782e3dbf22782fa62dfebb492cb45 Mon Sep 17 00:00:00 2001 From: vizsoro Date: Thu, 30 Aug 2018 22:08:38 +0200 Subject: [PATCH 2/3] editorial review --- .../ConfigPropertiesDemoApplication.java | 4 ++-- .../properties/CustomJsonProperties.java | 4 ++-- .../baeldung/properties/JsonProperties.java | 4 ++-- .../JsonPropertyContextInitializer.java | 2 +- .../properties/JsonPropertySourceFactory.java | 3 ++- .../src/main/resources/configpropscustom.json | 9 --------- .../JsonPropertiesIntegrationTest.java | 18 +++++++++--------- 7 files changed, 18 insertions(+), 26 deletions(-) delete mode 100644 spring-boot/src/main/resources/configpropscustom.json diff --git a/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java b/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java index 7ca0ab4f60..395d68060b 100644 --- a/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java +++ b/spring-boot/src/main/java/org/baeldung/properties/ConfigPropertiesDemoApplication.java @@ -1,10 +1,10 @@ package org.baeldung.properties; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; -@EnableAutoConfiguration +@SpringBootApplication @ComponentScan(basePackageClasses = { ConfigProperties.class, JsonProperties.class, CustomJsonProperties.class }) public class ConfigPropertiesDemoApplication { public static void main(String[] args) { diff --git a/spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java b/spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java index 9d5e8ce780..3fae8a8e98 100644 --- a/spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java +++ b/spring-boot/src/main/java/org/baeldung/properties/CustomJsonProperties.java @@ -1,9 +1,9 @@ package org.baeldung.properties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; -@Configuration +@Component @ConfigurationProperties(prefix = "custom") public class CustomJsonProperties { diff --git a/spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java b/spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java index 2e6e24036e..5c31cd1344 100644 --- a/spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java +++ b/spring-boot/src/main/java/org/baeldung/properties/JsonProperties.java @@ -4,10 +4,10 @@ import java.util.LinkedHashMap; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; -@Configuration +@Component @PropertySource(value = "classpath:configprops.json", factory = JsonPropertySourceFactory.class) @ConfigurationProperties public class JsonProperties { diff --git a/spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java index 27abadb1e1..fd9b3f35a5 100644 --- a/spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java +++ b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertyContextInitializer.java @@ -26,7 +26,7 @@ public class JsonPropertyContextInitializer implements ApplicationContextInitial @SuppressWarnings("unchecked") public void initialize(ConfigurableApplicationContext configurableApplicationContext) { try { - Resource resource = configurableApplicationContext.getResource("classpath:configpropscustom.json"); + Resource resource = configurableApplicationContext.getResource("classpath:configprops.json"); Map readValue = new ObjectMapper().readValue(resource.getInputStream(), Map.class); Set set = readValue.entrySet(); List propertySources = convertEntrySet(set, Optional.empty()); diff --git a/spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java index 7e5739fd41..9578179519 100644 --- a/spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java +++ b/spring-boot/src/main/java/org/baeldung/properties/JsonPropertySourceFactory.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.Map; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertySourceFactory; @@ -12,7 +13,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class JsonPropertySourceFactory implements PropertySourceFactory { @Override - public org.springframework.core.env.PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { + public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { Map readValue = new ObjectMapper().readValue(resource.getInputStream(), Map.class); return new MapPropertySource("json-property", readValue); } diff --git a/spring-boot/src/main/resources/configpropscustom.json b/spring-boot/src/main/resources/configpropscustom.json deleted file mode 100644 index 5449876be3..0000000000 --- a/spring-boot/src/main/resources/configpropscustom.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "host" : "mailer.custom@mail.com", - "port" : 8081, - "resend" : true, - "sender" : { - "name": "sender.custom", - "address": "street.custom" - } -} diff --git a/spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java index 20a19e043a..e3d4c62953 100644 --- a/spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java +++ b/spring-boot/src/test/java/org/baeldung/properties/JsonPropertiesIntegrationTest.java @@ -21,19 +21,19 @@ public class JsonPropertiesIntegrationTest { private CustomJsonProperties customJsonProperties; @Test - public void givenJsonPropertySource_whenPropertySourceFactoryUsed_thenLoadFlatValues() { + public void whenPropertiesLoadedViaJsonPropertySource_thenLoadFlatValues() { Assert.assertEquals("mailer@mail.com", jsonProperties.getHost()); Assert.assertEquals(9090, jsonProperties.getPort()); Assert.assertTrue(jsonProperties.isResend()); } @Test - public void givenJsonPropertySource_whenPropertySourceFactoryUsed_thenLoadListValues() { + public void whenPropertiesLoadedViaJsonPropertySource_thenLoadListValues() { Assert.assertThat(jsonProperties.getTopics(), Matchers.is(Arrays.asList("spring", "boot"))); } @Test - public void givenJsonPropertySource_whenPropertySourceFactoryUsed_thenNestedValuesLoadedAsMap() { + public void whenPropertiesLoadedViaJsonPropertySource_thenNestedLoadedAsMap() { Assert.assertEquals("sender", jsonProperties.getSender() .get("name")); Assert.assertEquals("street", jsonProperties.getSender() @@ -41,18 +41,18 @@ public class JsonPropertiesIntegrationTest { } @Test - public void givenCustomJsonPropertySource_whenLoadedIntoEnvironment_thenFlatValuesPopulated() { - Assert.assertEquals("mailer.custom@mail.com", customJsonProperties.getHost()); - Assert.assertEquals(8081, customJsonProperties.getPort()); + public void whenLoadedIntoEnvironment_thenFlatValuesPopulated() { + Assert.assertEquals("mailer@mail.com", customJsonProperties.getHost()); + Assert.assertEquals(9090, customJsonProperties.getPort()); Assert.assertTrue(customJsonProperties.isResend()); } @Test - public void givenCustomJsonPropertySource_whenLoadedIntoEnvironment_thenValuesLoadedIntoClassObject() { + public void whenLoadedIntoEnvironment_thenValuesLoadedIntoClassObject() { Assert.assertNotNull(customJsonProperties.getSender()); - Assert.assertEquals("sender.custom", customJsonProperties.getSender() + Assert.assertEquals("sender", customJsonProperties.getSender() .getName()); - Assert.assertEquals("street.custom", customJsonProperties.getSender() + Assert.assertEquals("street", customJsonProperties.getSender() .getAddress()); } From 692e1cbecf4f6c8a332337049149f70a12ab2345 Mon Sep 17 00:00:00 2001 From: vizsoro Date: Thu, 6 Sep 2018 11:30:23 +0200 Subject: [PATCH 3/3] moving between packages --- .../baeldung/properties/ConfigProperties.java | 117 ++++++++++++++++++ .../ConfigPropertiesDemoApplication.java | 15 +++ .../properties/CustomJsonProperties.java | 71 +++++++++++ .../baeldung/properties/JsonProperties.java | 64 ++++++++++ .../JsonPropertyContextInitializer.java | 68 ++++++++++ .../properties/JsonPropertySourceFactory.java | 21 ++++ .../ConfigPropertiesIntegrationTest.java | 45 +++++++ .../JsonPropertiesIntegrationTest.java | 63 ++++++++++ 8 files changed, 464 insertions(+) create mode 100644 spring-boot/src/main/java/com/baeldung/properties/ConfigProperties.java create mode 100644 spring-boot/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java create mode 100644 spring-boot/src/main/java/com/baeldung/properties/CustomJsonProperties.java create mode 100644 spring-boot/src/main/java/com/baeldung/properties/JsonProperties.java create mode 100644 spring-boot/src/main/java/com/baeldung/properties/JsonPropertyContextInitializer.java create mode 100644 spring-boot/src/main/java/com/baeldung/properties/JsonPropertySourceFactory.java create mode 100644 spring-boot/src/test/java/com/baeldung/properties/ConfigPropertiesIntegrationTest.java create mode 100644 spring-boot/src/test/java/com/baeldung/properties/JsonPropertiesIntegrationTest.java diff --git a/spring-boot/src/main/java/com/baeldung/properties/ConfigProperties.java b/spring-boot/src/main/java/com/baeldung/properties/ConfigProperties.java new file mode 100644 index 0000000000..863510738b --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/properties/ConfigProperties.java @@ -0,0 +1,117 @@ +package com.baeldung.properties; + +import java.util.List; +import java.util.Map; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.Pattern; + +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotBlank; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.validation.annotation.Validated; + +@Configuration +@PropertySource("classpath:configprops.properties") +@ConfigurationProperties(prefix = "mail") +@Validated +public class ConfigProperties { + + @Validated + public static class Credentials { + + @Length(max = 4, min = 1) + private String authMethod; + private String username; + private String password; + + public String getAuthMethod() { + return authMethod; + } + + public void setAuthMethod(String authMethod) { + this.authMethod = authMethod; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + } + + @NotBlank + private String host; + + @Min(1025) + @Max(65536) + private int port; + + @Pattern(regexp = "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$") + private String from; + + private Credentials credentials; + private List defaultRecipients; + private Map additionalHeaders; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public Credentials getCredentials() { + return credentials; + } + + public void setCredentials(Credentials credentials) { + this.credentials = credentials; + } + + public List getDefaultRecipients() { + return defaultRecipients; + } + + public void setDefaultRecipients(List defaultRecipients) { + this.defaultRecipients = defaultRecipients; + } + + public Map getAdditionalHeaders() { + return additionalHeaders; + } + + public void setAdditionalHeaders(Map additionalHeaders) { + this.additionalHeaders = additionalHeaders; + } +} diff --git a/spring-boot/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java b/spring-boot/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java new file mode 100644 index 0000000000..ee9671c755 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.properties; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackageClasses = { ConfigProperties.class, JsonProperties.class, CustomJsonProperties.class }) +public class ConfigPropertiesDemoApplication { + public static void main(String[] args) { + new SpringApplicationBuilder(ConfigPropertiesDemoApplication.class).initializers(new JsonPropertyContextInitializer()) + .run(); + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/properties/CustomJsonProperties.java b/spring-boot/src/main/java/com/baeldung/properties/CustomJsonProperties.java new file mode 100644 index 0000000000..084138ec6f --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/properties/CustomJsonProperties.java @@ -0,0 +1,71 @@ +package com.baeldung.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "custom") +public class CustomJsonProperties { + + private String host; + + private int port; + + private boolean resend; + + private Person sender; + + public static class Person { + + private String name; + private String address; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isResend() { + return resend; + } + + public void setResend(boolean resend) { + this.resend = resend; + } + + public Person getSender() { + return sender; + } + + public void setSender(Person sender) { + this.sender = sender; + } +} diff --git a/spring-boot/src/main/java/com/baeldung/properties/JsonProperties.java b/spring-boot/src/main/java/com/baeldung/properties/JsonProperties.java new file mode 100644 index 0000000000..31b3be14b4 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/properties/JsonProperties.java @@ -0,0 +1,64 @@ +package com.baeldung.properties; + +import java.util.LinkedHashMap; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +@Component +@PropertySource(value = "classpath:configprops.json", factory = JsonPropertySourceFactory.class) +@ConfigurationProperties +public class JsonProperties { + + private String host; + + private int port; + + private boolean resend; + + private List topics; + + private LinkedHashMap sender; + + public LinkedHashMap getSender() { + return sender; + } + + public void setSender(LinkedHashMap sender) { + this.sender = sender; + } + + public List getTopics() { + return topics; + } + + public void setTopics(List topics) { + this.topics = topics; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isResend() { + return resend; + } + + public void setResend(boolean resend) { + this.resend = resend; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } +} diff --git a/spring-boot/src/main/java/com/baeldung/properties/JsonPropertyContextInitializer.java b/spring-boot/src/main/java/com/baeldung/properties/JsonPropertyContextInitializer.java new file mode 100644 index 0000000000..0aee149123 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/properties/JsonPropertyContextInitializer.java @@ -0,0 +1,68 @@ +package com.baeldung.properties; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.Resource; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonPropertyContextInitializer implements ApplicationContextInitializer { + + private final static String CUSTOM_PREFIX = "custom."; + + @Override + @SuppressWarnings("unchecked") + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + try { + Resource resource = configurableApplicationContext.getResource("classpath:configprops.json"); + Map readValue = new ObjectMapper().readValue(resource.getInputStream(), Map.class); + Set set = readValue.entrySet(); + List propertySources = convertEntrySet(set, Optional.empty()); + for (PropertySource propertySource : propertySources) { + configurableApplicationContext.getEnvironment() + .getPropertySources() + .addFirst(propertySource); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static List convertEntrySet(Set entrySet, Optional parentKey) { + return entrySet.stream() + .map((Map.Entry e) -> convertToPropertySourceList(e, parentKey)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + private static List convertToPropertySourceList(Map.Entry e, Optional parentKey) { + String key = parentKey.map(s -> s + ".") + .orElse("") + (String) e.getKey(); + Object value = e.getValue(); + return covertToPropertySourceList(key, value); + } + + @SuppressWarnings("unchecked") + private static List covertToPropertySourceList(String key, Object value) { + if (value instanceof LinkedHashMap) { + LinkedHashMap map = (LinkedHashMap) value; + Set entrySet = map.entrySet(); + return convertEntrySet(entrySet, Optional.ofNullable(key)); + } + String finalKey = CUSTOM_PREFIX + key; + return Collections.singletonList(new MapPropertySource(finalKey, Collections.singletonMap(finalKey, value))); + } + +} \ No newline at end of file diff --git a/spring-boot/src/main/java/com/baeldung/properties/JsonPropertySourceFactory.java b/spring-boot/src/main/java/com/baeldung/properties/JsonPropertySourceFactory.java new file mode 100644 index 0000000000..c14d3faea5 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/properties/JsonPropertySourceFactory.java @@ -0,0 +1,21 @@ +package com.baeldung.properties; + +import java.io.IOException; +import java.util.Map; + +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.EncodedResource; +import org.springframework.core.io.support.PropertySourceFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonPropertySourceFactory implements PropertySourceFactory { + + @Override + public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { + Map readValue = new ObjectMapper().readValue(resource.getInputStream(), Map.class); + return new MapPropertySource("json-property", readValue); + } + +} \ No newline at end of file diff --git a/spring-boot/src/test/java/com/baeldung/properties/ConfigPropertiesIntegrationTest.java b/spring-boot/src/test/java/com/baeldung/properties/ConfigPropertiesIntegrationTest.java new file mode 100644 index 0000000000..3a4b6551b1 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/properties/ConfigPropertiesIntegrationTest.java @@ -0,0 +1,45 @@ +package com.baeldung.properties; + +import org.baeldung.properties.ConfigProperties; +import org.baeldung.properties.ConfigPropertiesDemoApplication; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = ConfigPropertiesDemoApplication.class) +@TestPropertySource("classpath:configprops-test.properties") +public class ConfigPropertiesIntegrationTest { + + @Autowired + private ConfigProperties properties; + + @Test + public void whenSimplePropertyQueriedthenReturnsProperty() throws Exception { + Assert.assertTrue("From address is read as null!", properties.getFrom() != null); + } + + @Test + public void whenListPropertyQueriedthenReturnsProperty() throws Exception { + Assert.assertTrue("Couldn't bind list property!", properties.getDefaultRecipients().size() == 2); + Assert.assertTrue("Incorrectly bound list property. Expected 2 entries!", properties.getDefaultRecipients().size() == 2); + } + + @Test + public void whenMapPropertyQueriedthenReturnsProperty() throws Exception { + Assert.assertTrue("Couldn't bind map property!", properties.getAdditionalHeaders() != null); + Assert.assertTrue("Incorrectly bound map property. Expected 3 Entries!", properties.getAdditionalHeaders().size() == 3); + } + + @Test + public void whenObjectPropertyQueriedthenReturnsProperty() throws Exception { + Assert.assertTrue("Couldn't bind map property!", properties.getCredentials() != null); + Assert.assertTrue("Incorrectly bound object property!", properties.getCredentials().getAuthMethod().equals("SHA1")); + Assert.assertTrue("Incorrectly bound object property!", properties.getCredentials().getUsername().equals("john")); + Assert.assertTrue("Incorrectly bound object property!", properties.getCredentials().getPassword().equals("password")); + } +} diff --git a/spring-boot/src/test/java/com/baeldung/properties/JsonPropertiesIntegrationTest.java b/spring-boot/src/test/java/com/baeldung/properties/JsonPropertiesIntegrationTest.java new file mode 100644 index 0000000000..48c551d1dd --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/properties/JsonPropertiesIntegrationTest.java @@ -0,0 +1,63 @@ +package com.baeldung.properties; + +import java.util.Arrays; + +import org.baeldung.properties.ConfigPropertiesDemoApplication; +import org.baeldung.properties.CustomJsonProperties; +import org.baeldung.properties.JsonProperties; +import org.baeldung.properties.JsonPropertyContextInitializer; +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = ConfigPropertiesDemoApplication.class, initializers = JsonPropertyContextInitializer.class) +public class JsonPropertiesIntegrationTest { + + @Autowired + private JsonProperties jsonProperties; + + @Autowired + private CustomJsonProperties customJsonProperties; + + @Test + public void whenPropertiesLoadedViaJsonPropertySource_thenLoadFlatValues() { + Assert.assertEquals("mailer@mail.com", jsonProperties.getHost()); + Assert.assertEquals(9090, jsonProperties.getPort()); + Assert.assertTrue(jsonProperties.isResend()); + } + + @Test + public void whenPropertiesLoadedViaJsonPropertySource_thenLoadListValues() { + Assert.assertThat(jsonProperties.getTopics(), Matchers.is(Arrays.asList("spring", "boot"))); + } + + @Test + public void whenPropertiesLoadedViaJsonPropertySource_thenNestedLoadedAsMap() { + Assert.assertEquals("sender", jsonProperties.getSender() + .get("name")); + Assert.assertEquals("street", jsonProperties.getSender() + .get("address")); + } + + @Test + public void whenLoadedIntoEnvironment_thenFlatValuesPopulated() { + Assert.assertEquals("mailer@mail.com", customJsonProperties.getHost()); + Assert.assertEquals(9090, customJsonProperties.getPort()); + Assert.assertTrue(customJsonProperties.isResend()); + } + + @Test + public void whenLoadedIntoEnvironment_thenValuesLoadedIntoClassObject() { + Assert.assertNotNull(customJsonProperties.getSender()); + Assert.assertEquals("sender", customJsonProperties.getSender() + .getName()); + Assert.assertEquals("street", customJsonProperties.getSender() + .getAddress()); + } + +}