Search code examples
restgrailsservice

Grails Message source bug in Service


I am in trouble with one point in Grails 3.3.11 and I need help.

I am developing a rest-api in grails 3.3.11, but I can't use message source for i18n in service. It only works in my controller.

message = messageSource.getMessage('documentProperties.notFound', [partId,jsonRequestDoc.get("typeId")] as Object[], LocaleContextHolder.locale)

When I do that above, I receive this in service.

Cannot invoke method getMessage() on null object

My messageSource is defined in the beginning of my service class as below:

 @Autowired
 def messageSource

In order to resolve this, I manually created a service constructor which receives message source from the controller.

class CmbIdService {
 public CmbIdService(def messageSource){
        this.messageSource = messageSource
    }
}

It was working as expected, but when I started running my integration tests, I noticed that Grails did not know how to instantiate my service considering the presence of the constructor. This the message I receive when I run the Grails Integration tests.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cmbIdService': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'java.lang.Object' available: more than one 'primary' bean found among candidates... continue.

Could anyone help me. I don't know what to do. I need to use message source for internacionalization. And I need to do that in service, not only in the controller. And my tests need to continue working.

Thanks!

Alfredo


Solution

  • Instead of this...

    class CmbIdService {
     public CmbIdService(def messageSource){
            this.messageSource = messageSource
        }
    }
    

    Do this...

    class CmbIdService {
        def messageSource
    }
    

    If that class is defined in grails-app/services/[whatever your package is]/CmbIdService.groovy, then an instance will automatically be created for you, added to the application context as a bean, and that instance will be subjected to autowire-by-name.

    EDIT

    Comments below suggest that the OP is still getting an NPE. I have provided a working example at https://github.com/jeffbrown/dnunesmessagesource.

    https://github.com/jeffbrown/dnunesmessagesource/blob/d05fe05a6679351fd5ff6779aebf30b6e6f3790d/grails-app/services/dnunesmessagesource/CmbIdService.groovy

    package dnunesmessagesource
    
    import org.springframework.context.i18n.LocaleContextHolder
    
    class CmbIdService {
        def messageSource
    
        String getCustomMessage() {
            messageSource.getMessage('my.custom.message', [] as Object[], LocaleContextHolder.locale)
        }
    }
    

    https://github.com/jeffbrown/dnunesmessagesource/blob/d05fe05a6679351fd5ff6779aebf30b6e6f3790d/grails-app/controllers/dnunesmessagesource/DemoController.groovy

    package dnunesmessagesource
    
    
    class DemoController {
        CmbIdService cmbIdService
        
        def index() {
            String message = cmbIdService.customMessage
            [customMessage: message]
        }
    }
    

    https://github.com/jeffbrown/dnunesmessagesource/blob/d05fe05a6679351fd5ff6779aebf30b6e6f3790d/grails-app/i18n/dnunes.properties

    my.custom.message=This Is My Custom Message
    

    https://github.com/jeffbrown/dnunesmessagesource/blob/d05fe05a6679351fd5ff6779aebf30b6e6f3790d/grails-app/views/demo/index.gson

    model {
        String customMessage
    }
    
    json {
        message customMessage
    }
    

    That all appears to work as designed.

    $ http :8080/demo
    HTTP/1.1 200 
    Content-Language: en-US
    Content-Type: application/json;charset=UTF-8
    Date: Wed, 31 Mar 2021 18:31:37 GMT
    Transfer-Encoding: chunked
    X-Application-Context: application:development
    
    {
        "message": "This Is My Custom Message"
    }