Inject grails application configuration into service - grails

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):
Inject grailsApplication.config.apiCredentials into the service in the controller when the service is created?
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?

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();
}
}

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.

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
}
}

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
}
}

Related

Grails Dependency Injection from Service

I am trying to injecting my own service elasticsearchService from the following domain class:
class DocumentESO extends ElasticsearchObject{
ElasticsearchService elasticsearchService
def afterInsert() {
elasticsearchService.save(this) // <-- Cannot invoke method save() on null object
}
}
However, it tells me that it Cannot invoke method save() on null object. Here is my service:
#Transactional
class ElasticsearchService {
#Transactional
def save(ElasticsearchObject esObject) {...}
}
Did I misspell something? If I would use ElasticsearchService elasticsearchService = new ElasticsearchService() then it would work, but I don't have the transactional support anymore.
In this answer, robert mentions it needs to initialized, while using meta programming save() for example. Does it mean that I cannot go with dependency injection in this case?
Thus it would be:
def afterInsert() {
ElasticsearchService elasticsearchService = new ElasticsearchService()
elasticsearchService.save(this)
}
??
Service injection in GORM entities is disabled by default since Grails 3.2.8.
You can turn on autowiring in this one particular domain class by adding to DocumentESO:
static mapping = {
autowire true
}
however it's not recommended: https://grails.org/blog/2017-05-09.html

How to inject a mock for a field (using autowired) in a Grails service that is under unit test?

In my current setup i want to unit test a Grails service that has an #autowired dependency and inject a mock for the dependency.
class AcmeService {
#Autowired
FooService fooService // not a Grails service!
}
The FooService is not a Grails service but it is a dynamic implementation from a FeignClient. I am looking for a way to inject a Mock for the FooService service in a UnitTest. What would be the best solution to do this?
I tried setting the dependency in the setup, but then i get a 'Unsatisfied dependency expressed through field fooService'
class AcmeService extends Specification {
FooService mockedFooService = Mock(FooService)
def setup() {
service.fooService = mockedFooService
}
}
you can add the following to your unit test:
def doWithSpring = {
fooService( InstanceFactoryBean, Mock(FooService) )
}

Dropwizard and Guice: injecting Environment

I am currently building a Dropwizard + Guice + Jersey-based application where the database access is being handled by JDBI for the time being.
What I am trying to achieve is to have your typical enterprise architecture, where Resources access Service classes accessing a DAO class that in turn accesses the database. It would be nice to get all this wired up in a proper DI way, although I guess I can build my object graph in the run() method of the application if all else fails.
So, I'm running into this problem that has been mentioned here before: Getting a DBIFactory requires both the Environment and the Configuration, which somehow need to be available at the time when Guice does its injection magic and not at run()-time.
Being a Dropwizard and Guice noob, what I've managed to put together so far is that I need a Provider for my DAO objects, something to the tune of
public class UserDAOProvider implements Provider<UserDAO> {
#Inject
Environment environment;
#Inject
Configuration configuration;
#Override
public UserDAO get() {
final DBIFactory factory = new DBIFactory();
final (MyConfiguration) config = (MyConfiguration) configuration;
DBI jdbi = null;
try {
jdbi = factory.build(environment, config.getDataSourceFactory(),
"mysql");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return jdbi.onDemand(UserDAO.class);
}
}
Registering this as a singleton provider should let me then inject the UserDAO into my Services.
Now, how do we actually get the environment injected into the Provider? Currently I am stuck at Guice complaining about not finding a suitable constructor for the Environment, so it is trying to instantiate it and not grab it from Dropwizard itself.
It seems like this is doable; there is the dropwizard-guice package whose DropWizardEnvironmentModule is, I think, what I need. But I feel like I'm just missing some piece of the puzzle here for an understanding of how to put things together. I've not managed to find a complete working example so far...
I had the same issue as OP but using Hibernate rather than JDBI. My simple solution is applicable to JDBI, though - just switch DBIFactory for SessionFactory.
First add an injection provider for a singleton SessionFactory in your Guice module:
public class MyModule extends AbstractModule {
private SessionFactory sessionFactory;
#Override
protected void configure() {
}
#Provides
SessionFactory providesSessionFactory() {
if (sessionFactory == null) {
throw new ProvisionException("The Hibernate session factory has not yet been set. This is likely caused by forgetting to call setSessionFactory during Application.run()");
}
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
You need to set the singleton SessionFactory from your application's run() method. In your case, using JDBI, this is where you would create and configure your DBIFactory before handing it over to the Guice module:
public void run(MyConfiguration configuration, Environment environment) {
myModule.setSessionFactory(hibernateBundle.getSessionFactory());
...
}
Now SessionFactory can be injected wherever it is needed. I now use implicit binding for my DAO classes by just annotating the constructor with #Inject and injecting the SessionFactory singleton. I don't explicitly create providers for DAO classes:
#Singleton
public class WidgetDAO extends AbstractDAO<App> {
#Inject
public WidgetDAO(SessionFactory factory) {
super(factory);
}
public Optional<Widget> findById(Long id) {
return Optional.fromNullable(get(id));
}
...
}
Now I can inject my DAO singleton instances into resources:
#Path("/widgets")
#Produces(MediaType.APPLICATION_JSON)
public class WidgetsResource {
private final WidgetDAO widgetDAO;
#Inject
public WidgetsResource(WidgetDAO widgetDAO) {
this.widgetDAO = widgetDAO;
}
...
}
Note that this approach follows the Guice recommendation of injecting direct dependencies only. Don't try to inject Envrionment and Configuration just so that you can create a DBI factory - inject the prebuilt DBI factory itself.
This is how I use Guice with Dropwizard. Inside your run() method add the line
Guice.createInjector(new ConsoleModule());
You cannot inject Environ
Create the class ConsoleModule
public class ConsoleModule extends AbstractModule {
//configuration and env variable declaration
public ConsoleModule(ConsoleConfiguration consoleConfig, Environment env)
{
this.consoleConfig = consoleConfig;
this.env= env;
}
protected void configure()
{
//You should not inject Configuration and Environment in your provider since you are mixing
//dropwizard framework stuff with Guice.Neverthless you will have to bind them in the below order
bind(Configuration.class).toInstance(consoleConfig.class);
bind(Environment.class).toInstance(env.class);
bind(UserDAO.class).toProvider(UserDAOProvider.class).in(Singleton.class);
}
}
We have the same configuration (dw-jdbi-guice) and also an abstract 'base' Application class which complicates things even more.
Since a lot of things happen during run method, and many things depend on the configuration objects we ended up creating the injector in the run method. But since we need objects from bootsrap also (e.g. ObjectMapper), we ended up having a List<Module> field in the Application class. Not the prettiest solution but can handle variety of scenarios.

How to use g.formatNumber in grails Service class

I want to use g.formatNumber in service, I have tried a below method, Which i got online. This is not working, its giving me the error "Cannot invoke method formatNumber() on null object", The code is below
import org.springframework.beans.factory.InitializingBean
class MyService implements InitializingBean {
boolean transactional = false
def gspTagLibraryLookup // being automatically injected by spring
def g
public void afterPropertiesSet() {
g = gspTagLibraryLookup.lookupNamespaceDispatcher("g")
assert g
}
def getFormattedNumber(){
def number = g.formatNumber(number: 5000,234 , type: "number" , maxFractionDigits: 2)
return number
}
}
How to do this.
I want to use g.formatNumber in service
Rather than jumping through the hoops you need to use a taglib within a service, it would be simpler to just use java.text.NumberFormat directly
NumberFormat format = NumberFormat.getNumberInstance()
format.maximumFractionDigits = 2
def number = format.format(5000.234)
If the service method is being called from a web request handling thread then you may wish to use the LocaleContextHolder to get the correct locale for the current web request, rather than just using the server's default.
This should work
def g = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib');
You will of course need grailsApplication injected by defining it ala
def grailsApplication

Grails service injection into integration test

I have a very simple Grails Service:
class UserService {
def returnHi() { return "Hi" }
}
I'm trying to get access to the service in an integration test, like this:
def testService() {
UserService userService
assertEquals( "Hi", userService.returnHi() )
}
Why do I get the failure:
java.lang.NullPointerException: Cannot invoke method returnHi() on null object?
Thanks for your time
It's enough to put 'def userService' as your class field instead of putting in inside of the method. In integration tests, beans are injected the same as in controllers, services and other beans.
Do something like:
class MyTests {
def userService
void serviceTest(){
assert userService.returnHi(), 'Hi'
}
}
P.S. Make sure the name of the service is correct and written in camelCase.
Add the following lines to the integration test file:
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def userService = AH.application.mainContext.userService
as described here: Service is not getting injected into Grails domain class , Grails 2.0.0 and 2.0.3

Resources