From a9b64dc220ff8eeffd73a5c21cefc816dbac51cb Mon Sep 17 00:00:00 2001 From: Misagh Moayyed Date: Sun, 29 Sep 2024 10:51:43 +0400 Subject: [PATCH 1/4] Allow common messages to be specified for message sources Extend the configuration schema to support the specification of common messages, when defining a messageSource bean --- .../MessageSourceAutoConfiguration.java | 27 +++++++++++++++++++ .../context/MessageSourceProperties.java | 12 +++++++++ .../MessageSourceAutoConfigurationTests.java | 6 +++++ .../resources/test/common-messages.properties | 1 + 4 files changed, 46 insertions(+) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/resources/test/common-messages.properties diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java index 68e04c7f0159..8c8a94a8c9e8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java @@ -16,10 +16,16 @@ package org.springframework.boot.autoconfigure.context; +import java.io.IOException; +import java.io.UncheckedIOException; import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -39,7 +45,9 @@ import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.core.Ordered; +import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.ConcurrentReferenceHashMap; @@ -81,6 +89,25 @@ public MessageSource messageSource(MessageSourceProperties properties) { } messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat()); messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage()); + + try { + if (StringUtils.hasText(properties.getCommonMessages())) { + PropertiesFactoryBean propertiesFactory = new PropertiesFactoryBean(); + ResourceLoader resourceLoader = new DefaultResourceLoader(); + String[] commonMessages = StringUtils.commaDelimitedListToStringArray( + StringUtils.trimAllWhitespace(properties.getCommonMessages())); + List commonResources = Arrays.stream(commonMessages) + .map(resourceLoader::getResource) + .toList(); + propertiesFactory.setLocations(commonResources.toArray(Resource[]::new)); + propertiesFactory.setSingleton(true); + propertiesFactory.setIgnoreResourceNotFound(true); + propertiesFactory.afterPropertiesSet(); + messageSource.setCommonMessages(propertiesFactory.getObject()); + } + } catch (IOException e) { + throw new UncheckedIOException("Failed to load common messages", e); + } return messageSource; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java index a765308af201..8582f100894b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java @@ -42,6 +42,11 @@ public class MessageSourceProperties { */ private String basename = "messages"; + /** + * Comma-separated list of locale-independent common messages. + */ + private String commonMessages; + /** * Message bundles encoding. */ @@ -121,4 +126,11 @@ public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) { this.useCodeAsDefaultMessage = useCodeAsDefaultMessage; } + public String getCommonMessages() { + return this.commonMessages; + } + + public void setCommonMessages(String commonMessages) { + this.commonMessages = commonMessages; + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java index e30983164136..79e21ff6825b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java @@ -116,6 +116,12 @@ void testMessageSourceFromPropertySourceAnnotation() { .run((context) -> assertThat(context.getMessage("foo", null, "Foo message", Locale.UK)).isEqualTo("bar")); } + @Test + void testCommonMessages() { + this.contextRunner.withPropertyValues("spring.messages.basename:test/messages", "spring.messages.common-messages:test/common-messages") + .run((context) -> assertThat(context.getMessage("hello", null, "Hello!", Locale.UK)).isEqualTo("world")); + } + @Test void testFallbackDefault() { this.contextRunner.withPropertyValues("spring.messages.basename:test/messages") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/test/common-messages.properties b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/test/common-messages.properties new file mode 100644 index 000000000000..432ea479457a --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/test/common-messages.properties @@ -0,0 +1 @@ +hello=world From 7d70b3954e4d874b7ccc75ba62e5891ad05a97dd Mon Sep 17 00:00:00 2001 From: Misagh Moayyed Date: Wed, 16 Oct 2024 09:26:40 +0400 Subject: [PATCH 2/4] updates after code review --- .../MessageSourceAutoConfiguration.java | 27 ++++++------------- .../context/MessageSourceProperties.java | 8 +++--- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java index 8c8a94a8c9e8..ddec38081a70 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java @@ -19,13 +19,10 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; +import java.util.Properties; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -45,10 +42,9 @@ import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.core.Ordered; -import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.StringUtils; @@ -91,19 +87,12 @@ public MessageSource messageSource(MessageSourceProperties properties) { messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage()); try { - if (StringUtils.hasText(properties.getCommonMessages())) { - PropertiesFactoryBean propertiesFactory = new PropertiesFactoryBean(); - ResourceLoader resourceLoader = new DefaultResourceLoader(); - String[] commonMessages = StringUtils.commaDelimitedListToStringArray( - StringUtils.trimAllWhitespace(properties.getCommonMessages())); - List commonResources = Arrays.stream(commonMessages) - .map(resourceLoader::getResource) - .toList(); - propertiesFactory.setLocations(commonResources.toArray(Resource[]::new)); - propertiesFactory.setSingleton(true); - propertiesFactory.setIgnoreResourceNotFound(true); - propertiesFactory.afterPropertiesSet(); - messageSource.setCommonMessages(propertiesFactory.getObject()); + if (properties.getCommonMessages() != null) { + Properties commonProperties = new Properties(); + for (Resource commonResource : properties.getCommonMessages()) { + PropertiesLoaderUtils.fillProperties(commonProperties, commonResource); + } + messageSource.setCommonMessages(commonProperties); } } catch (IOException e) { throw new UncheckedIOException("Failed to load common messages", e); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java index 8582f100894b..45b632788497 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java @@ -20,9 +20,11 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.convert.DurationUnit; +import org.springframework.core.io.Resource; /** * Configuration properties for Message Source. @@ -45,7 +47,7 @@ public class MessageSourceProperties { /** * Comma-separated list of locale-independent common messages. */ - private String commonMessages; + private List commonMessages; /** * Message bundles encoding. @@ -126,11 +128,11 @@ public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) { this.useCodeAsDefaultMessage = useCodeAsDefaultMessage; } - public String getCommonMessages() { + public List getCommonMessages() { return this.commonMessages; } - public void setCommonMessages(String commonMessages) { + public void setCommonMessages(List commonMessages) { this.commonMessages = commonMessages; } } From 298899c607f8ec1c5ec7d6da7984680e1c9ae80f Mon Sep 17 00:00:00 2001 From: Misagh Moayyed Date: Wed, 16 Oct 2024 09:46:45 +0400 Subject: [PATCH 3/4] updates after code review --- .../context/MessageSourceAutoConfiguration.java | 3 ++- .../boot/autoconfigure/context/MessageSourceProperties.java | 3 ++- .../context/MessageSourceAutoConfigurationTests.java | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java index 91f1ccb86d61..c6a6d12f6ee8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java @@ -94,7 +94,8 @@ public MessageSource messageSource(MessageSourceProperties properties) { } messageSource.setCommonMessages(commonProperties); } - } catch (IOException e) { + } + catch (IOException e) { throw new UncheckedIOException("Failed to load common messages", e); } return messageSource; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java index 88461834725e..508be001a38c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java @@ -49,7 +49,7 @@ public class MessageSourceProperties { * Comma-separated list of locale-independent common messages. */ private List commonMessages; - + /** * Message bundles encoding. */ @@ -136,4 +136,5 @@ public List getCommonMessages() { public void setCommonMessages(List commonMessages) { this.commonMessages = commonMessages; } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java index 79e21ff6825b..523a5109743f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java @@ -118,10 +118,12 @@ void testMessageSourceFromPropertySourceAnnotation() { @Test void testCommonMessages() { - this.contextRunner.withPropertyValues("spring.messages.basename:test/messages", "spring.messages.common-messages:test/common-messages") + this.contextRunner + .withPropertyValues("spring.messages.basename:test/messages", + "spring.messages.common-messages:test/common-messages") .run((context) -> assertThat(context.getMessage("hello", null, "Hello!", Locale.UK)).isEqualTo("world")); } - + @Test void testFallbackDefault() { this.contextRunner.withPropertyValues("spring.messages.basename:test/messages") From 2015f536eaa7724ca8c5edd140c96e89ae078bca Mon Sep 17 00:00:00 2001 From: Misagh Moayyed Date: Wed, 16 Oct 2024 09:55:10 +0400 Subject: [PATCH 4/4] updates after code review --- .../autoconfigure/context/MessageSourceAutoConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java index c6a6d12f6ee8..8e803c3ed165 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java @@ -95,8 +95,8 @@ public MessageSource messageSource(MessageSourceProperties properties) { messageSource.setCommonMessages(commonProperties); } } - catch (IOException e) { - throw new UncheckedIOException("Failed to load common messages", e); + catch (IOException ex) { + throw new UncheckedIOException("Failed to load common messages", ex); } return messageSource; }