Inject grails application configuration into service
Asked Answered
H

4

39

I'm creating a grails service that will interact with a 3rd party REST API via a Java library. The Java library requires credentials for the REST API by means of a url, username and password.

I'd like to store these credentials in configuration/Config.groovy, make them available to a service and ensure that credentials are available to the service before it requires them.

I appreciate that grailsApplication.config is available to controllers and that through a method of a service the relevant config values can be provided to the service, such as this:

package example

class ExampleController {

    def exampleService

    def index = { }

    def process = {
        exampleService.setCredentials(grailsApplication.config.apiCredentials)
        exampleService.relevantMethod()
    }
}


package example

import com.example.ExampleApiClient;

class ExampleService {

    def credentials

    def setCredentials(credentials) {
        this.credentials = credentials
    }

    def relevantMethod() {

        def client = new ExampleApiClient(
            credentials.baseUrl,
            credentials.username,
            credentials.password
        )

        return client.action();
    }
}

I feel this approach is slightly flawed as it depends on a controller calling setCredentials(). Having the credentials made available to the service automagically would be more robust.

Is either of these two options viable (I currently not familiar enough with grails):

  1. Inject grailsApplication.config.apiCredentials into the service in the controller when the service is created?

  2. Provide some form of contructor on the service that allows the credentials to be passed in to the service at instantiation time?

Having the credentials injected into the service is ideal. How could this be done?

Hypertonic answered 11/2, 2011 at 20:51 Comment(1)
would still be nice if there were some way to inject the actual config properties instead of the entire grailsApplication object.Uranie
H
80

The grailsApplication object is available within services, allowing this:

package example

import com.example.ExampleApiClient;

class ExampleService {

    def grailsApplication

    def relevantMethod() {

        def client = new ExampleApiClient(
            grailsApplication.config.apiCredentials.baseUrl
            grailsApplication.config.apiCredentials.username,
            grailsApplication.config.apiCredentials.password
        )

        return client.action();
    }
}
Hypertonic answered 11/2, 2011 at 21:11 Comment(2)
Note that I've found that you need to use grailsApplication.config.[...] inside a method. Otherwise, you'll see a null pointer if you try and pull data out of the config file outside a method.Mizzle
Thanks @arcdegree. Your comment spared me a lot of frustration.Cohabit
K
11

Even though grailsApplication can be injected in services, I think services should not have to deal with configuration because it's harder to test and breaks the Single Responsibility principle. Spring, on the other side, can handle configuration and instantiation in a more robust way. Grails have a dedicated section in its docs.

To make your example work using Spring, you should register your service as a bean in resources.groovy

// Resources.groovy
import com.example.ExampleApiClient

beans {
    // Defines your bean, with constructor params
    exampleApiClient ExampleApiClient, 'baseUrl', 'username', 'password'
}

Then you will be able to inject the dependency into your service

class ExampleService {
    def exampleApiClient

    def relevantMethod(){
        exampleApiClient.action()
    }
}

In addition, in your Config.groovyfile, you can override any bean property using the Grails convention over configuration syntax: beans.<beanName>.<property>:

// Config.groovy
...
beans.exampleApiClient.baseUrl = 'http://example.org'

Both Config.groovy and resources.groovy supports different environment configuration.

Kinghood answered 23/9, 2014 at 15:55 Comment(0)
H
4

For contexts where you can't inject the grailsApplication bean (service is not one of those, as described by Jon Cram), for example a helper class located in src/groovy, you can access it using the Holders class:

def MyController {
   def myAction() {
      render grailsApplication == grails.util.Holders.grailsApplication
   }
}
Hare answered 6/6, 2014 at 8:0 Comment(0)
R
0

The best options are (as from grails docs):

1 - Using Spring @Value annotation

import org.springframework.beans.factory.annotation.Value

class WidgetService {

    int area

    @Value('${widget.width}')
    int width

    def someServiceMethod() {
        // this method may use the width property...
    }
}

2 - Having your class implement GrailsConfigurationAware

import grails.config.Config
import grails.core.support.GrailsConfigurationAware

class WidgetService implements GrailsConfigurationAware {

    int area

    def someServiceMethod() {
        // this method may use the area property...
    }

    @Override
    void setConfiguration(Config co) {
        int width = co.getProperty('widget.width', Integer, 10)
        int height = co.getProperty('widget.height', Integer, 10)
        area = width * height
    }
}
Rectal answered 11/1, 2022 at 20:23 Comment(1)
FYI... This is consistent with our recommendations published at grails.org/blog/2016-08-31.html and grails.org/blog/2017-01-20-3.html.Feck

© 2022 - 2024 — McMap. All rights reserved.