From 438d2e08a54e0d930abb1859d130839be5967bc6 Mon Sep 17 00:00:00 2001 From: James Fredley Date: Thu, 12 Sep 2024 15:30:59 -0400 Subject: [PATCH] Support multiple messageSource beans (#13640) * Support multiple messageSource beans and default to grails or spring instance --- .../util/GrailsMessageSourceUtils.groovy | 57 +++++++++++++++++++ ...faultConstraintEvaluatorFactoryBean.groovy | 12 +++- .../errors/AbstractVndErrorRenderer.groovy | 13 ++++- .../util/AbstractLinkingRenderer.groovy | 12 +++- .../databinding/GrailsWebDataBinder.groovy | 11 ++-- 5 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 grails-core/src/main/groovy/grails/util/GrailsMessageSourceUtils.groovy diff --git a/grails-core/src/main/groovy/grails/util/GrailsMessageSourceUtils.groovy b/grails-core/src/main/groovy/grails/util/GrailsMessageSourceUtils.groovy new file mode 100644 index 00000000000..6de2f588cca --- /dev/null +++ b/grails-core/src/main/groovy/grails/util/GrailsMessageSourceUtils.groovy @@ -0,0 +1,57 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package grails.util + +import groovy.transform.CompileStatic +import org.springframework.context.MessageSource + + +/** + * A simple class that selects a single {@link org.springframework.context.MessageSource MessageSource} + * when two or more are present in the ApplicationContext. + * It defaults to the Grails or Spring {@link org.springframework.context.MessageSource MessageSource}, if present. + * + * @author James Fredley + * @since 7.0 + */ + +@CompileStatic +class GrailsMessageSourceUtils { + static MessageSource findPreferredMessageSource(List messageSources){ + if(!messageSources) { + return null + } + + if(messageSources.size() == 1) { + return messageSources.get(0) + } + + MessageSource firstGrailsSpring = messageSources.find {messageSource -> + String className = messageSource.class.name + // use the first Grails or Spring MessageSource + className.startsWith("org.grails") || className.startsWith("grails") || className.startsWith("org.springframework") + } + + // return the first Grails or Spring MessageSource + if(firstGrailsSpring) { + return firstGrailsSpring + } + + // return the first MessageSource from the list + return messageSources.get(0) + } +} \ No newline at end of file diff --git a/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/support/DefaultConstraintEvaluatorFactoryBean.groovy b/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/support/DefaultConstraintEvaluatorFactoryBean.groovy index 1fff431ab49..175685e4505 100644 --- a/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/support/DefaultConstraintEvaluatorFactoryBean.groovy +++ b/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/support/DefaultConstraintEvaluatorFactoryBean.groovy @@ -1,6 +1,7 @@ package org.grails.plugins.domain.support import grails.core.GrailsApplication +import grails.util.GrailsMessageSourceUtils import org.grails.datastore.gorm.validation.constraints.eval.ConstraintsEvaluator import org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator import org.grails.datastore.gorm.validation.constraints.registry.ConstraintRegistry @@ -14,10 +15,17 @@ import org.springframework.context.MessageSource class DefaultConstraintEvaluatorFactoryBean implements FactoryBean { - @Autowired - @Qualifier("pluginAwareResourceBundleMessageSource") MessageSource messageSource + @Autowired + setMessageSource(List messageSources) { + setMessageSource(GrailsMessageSourceUtils.findPreferredMessageSource(messageSources)) + } + + void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource + } + @Autowired @Qualifier('grailsDomainClassMappingContext') MappingContext grailsDomainClassMappingContext diff --git a/grails-plugin-rest/src/main/groovy/grails/rest/render/errors/AbstractVndErrorRenderer.groovy b/grails-plugin-rest/src/main/groovy/grails/rest/render/errors/AbstractVndErrorRenderer.groovy index 09014f4f1b0..b153895a0cf 100644 --- a/grails-plugin-rest/src/main/groovy/grails/rest/render/errors/AbstractVndErrorRenderer.groovy +++ b/grails-plugin-rest/src/main/groovy/grails/rest/render/errors/AbstractVndErrorRenderer.groovy @@ -17,13 +17,13 @@ package grails.rest.render.errors import grails.rest.render.ContainerRenderer import grails.util.Environment +import grails.util.GrailsMessageSourceUtils import grails.util.GrailsNameUtils import grails.util.GrailsWebUtil import groovy.transform.CompileStatic import groovy.transform.TypeCheckingMode import grails.web.mapping.LinkGenerator import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Qualifier import org.springframework.context.MessageSource import org.springframework.validation.Errors import org.springframework.validation.ObjectError @@ -47,10 +47,17 @@ abstract class AbstractVndErrorRenderer implements ContainerRenderer messageSources) { + setMessageSource(GrailsMessageSourceUtils.findPreferredMessageSource(messageSources)) + } + + void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource + } + @Autowired LinkGenerator linkGenerator diff --git a/grails-plugin-rest/src/main/groovy/grails/rest/render/util/AbstractLinkingRenderer.groovy b/grails-plugin-rest/src/main/groovy/grails/rest/render/util/AbstractLinkingRenderer.groovy index d7ff631f1b5..1468adcbe95 100644 --- a/grails-plugin-rest/src/main/groovy/grails/rest/render/util/AbstractLinkingRenderer.groovy +++ b/grails-plugin-rest/src/main/groovy/grails/rest/render/util/AbstractLinkingRenderer.groovy @@ -23,6 +23,7 @@ import grails.rest.render.AbstractIncludeExcludeRenderer import grails.rest.render.RenderContext import grails.rest.render.RendererRegistry import grails.util.Environment +import grails.util.GrailsMessageSourceUtils import grails.util.GrailsWebUtil import grails.web.mapping.LinkGenerator import grails.web.mime.MimeType @@ -61,10 +62,17 @@ abstract class AbstractLinkingRenderer extends AbstractIncludeExcludeRenderer public static final String TEMPLATED_ATTRIBUTE = 'templated' public static final String DEPRECATED_ATTRIBUTE = 'deprecated' - @Autowired - @Qualifier("pluginAwareResourceBundleMessageSource") MessageSource messageSource + @Autowired + setMessageSource(List messageSources) { + setMessageSource(GrailsMessageSourceUtils.findPreferredMessageSource(messageSources)) + } + + void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource + } + @Autowired LinkGenerator linkGenerator diff --git a/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy b/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy index 53fc9c98399..0aec78ced72 100644 --- a/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy +++ b/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy @@ -20,8 +20,8 @@ import grails.databinding.* import grails.databinding.converters.FormattedValueConverter import grails.databinding.converters.ValueConverter import grails.databinding.events.DataBindingListener -import grails.util.Environment import grails.util.GrailsClassUtils +import grails.util.GrailsMessageSourceUtils import grails.util.GrailsMetaClassUtils import grails.util.GrailsNameUtils import grails.validation.DeferredBindingActions @@ -46,13 +46,11 @@ import org.grails.datastore.mapping.model.types.OneToMany import org.grails.datastore.mapping.model.types.OneToOne import org.grails.datastore.mapping.model.types.Simple import org.grails.web.databinding.DataBindingEventMulticastListener -import org.grails.web.databinding.DefaultASTDatabindingHelper import org.grails.web.databinding.GrailsWebDataBindingListener import org.grails.web.databinding.SpringConversionServiceAdapter import org.grails.web.databinding.converters.ByteArrayMultipartFileValueConverter import org.grails.web.servlet.mvc.GrailsWebRequest import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Qualifier import org.springframework.context.MessageSource import org.springframework.validation.BeanPropertyBindingResult import org.springframework.validation.BindingResult @@ -60,8 +58,6 @@ import org.springframework.validation.FieldError import org.springframework.validation.ObjectError import java.lang.annotation.Annotation -import java.lang.reflect.Modifier -import java.util.concurrent.ConcurrentHashMap import static grails.web.databinding.DataBindingUtils.* @@ -646,7 +642,10 @@ class GrailsWebDataBinder extends SimpleDataBinder { } @Autowired - @Qualifier("pluginAwareResourceBundleMessageSource") + setMessageSource(List messageSources) { + setMessageSource(GrailsMessageSourceUtils.findPreferredMessageSource(messageSources)) + } + void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource }