Can't inject grailsApplication into service - grails

I'm having an issue injecting grailsApplication into a service I created in Grails 2.1. I have built a simple test application (hoping I could figure out this issue and then port the fix over to my real application) that consists of a single controller, view, and service.
The controller looks like this:
class IncidentController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def incidentService
def index() {
incidentService.serviceMethod()
redirect(action: "list", params: params)
}
// Other stuff
}
My service consists simply of this:
class IncidentService {
def grailsApplication
def serviceMethod() {
System.out.println("Grails application: " + grailsApplication)
}
}
And I declare the injection of IncidentService in my resources.groovy as follows:
beans = {
incidentService(org.myorg.test.IncidentService)
}
Now everything I have read online says that this should work fine and I should be able to access my configs through the grailsApplication in the service. However, grailsApplication is null whenever I try to. If I add a ConfigObject to the service and set it by passing grailsApplication.getConfig() from the controller, it works fine.
Can anyone please point me in the right direction for figuring out what the problem is here?
Thank you

You shouldn't have to add your IncidentService to resources.groovy. If that service is under grails-app/services it will be done for you. Remove that and try it again. I think that might be your problem

Related

Override Grails redirect method

I'm carrying the locale of my Grails application in the URL, such as
http://myapp.com/LANG/controller/action/id.
Therefore, I adjusted the URLMappings.groovy with the language attribute. Since I would like to save work and don't apply the lang parameter at every link, I have overridden the g:link taglib with the language from params.lang. This works pretty good.
When I do a redirect from a controller, such as
redirect(action: "logout")
I'd like to append the params.lang automatically instead of writing every time
redirect(action: "logout", params: [lang: params.lang])
I found the thread grails override redirect controller method, but I'm unsure how to proceed. Where does this code take part?
How can I override the redirect() method and add the params.lang-attribute?
Please advice, thank you very much.
The easiest way to do this in your application would be to use a bit of meta programming and the BootStrap.groovy for your Grails application. The following is just a simple example of what that might look like:
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
class BootStrap {
def init = { servletContext ->
def ctx = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
def app = ctx.getBean("grailsApplication")
app.controllerClasses.each() { controllerClass ->
def oldRedirect = controllerClass.metaClass.pickMethod("redirect", [Map] as Class[])
controllerClass.metaClass.redirect = { Map args ->
// new pre-redirect logic
if (!args['params']) args['params'] = [:] // just in case redirect was called without any parameters
args['params']['whatever'] = 'something' // add something into the parameters map
oldRedirect.invoke delegate, args
// new post-redirect logic
}
}
}
def destroy = {
}
}
The above example just wraps the implementation for redirect on all controllers within your Grails application and injects a new parameter called whatever with the value of something.
The above was tested (quickly) using Grails 2.4.2.

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

NullPointerException with dependency injection

I am trying to use dependency injection for export to excel functionality provided by "Export Plugin"
It seems that whenevery I try to use service in my project i get error like following
Stacktrace follows:
java.lang.NullPointerException: Cannot invoke method export() on null object
at pm.ProjectsController$_closure2.doCall(ProjectsController.groovy:39)
at pm.ProjectsController$_closure2.doCall(ProjectsController.groovy)
at java.lang.Thread.run(Thread.java:662)
The code I am using is following, this just means service variable is null
def exportService// i have tried with and without the initialization
if(params?.format && params.format != "html"){
response.contentType = ConfigurationHolder.config.grails.mime.types[params.format]
response.setHeader("Content-disposition", "attachment; filename=books.${params.extension}")
exportService.export(params.format, response.outputStream,projectsList, [:], [:])
}
It seems that no plugin that uses services is working in my project for example AsynchronousMailService in my project didn't work as it was suppose to and thus I have been using it like following
AsynchronousMailService asynchronousMailService = new AsynchronousMailService()
asynchronousMailService.sendAsynchronousMail {
to projectsInstance.projectLead.emailAddress
subject "New project is assigned to you"
html msg
}
Unless I am missing somethig very basic I do not beleive I should be instantiating this class if the plugin offers the same as service.
Thanks
Right, you should never instantiate services or other Spring beans - use dependency injection. It might work, but if the bean has any dependencies of its own they'll be null since you're bypassing Spring.
You're not using dependency injection, you're declaring a local variable and expecting magic.
Dependency injection in Grails uses public fields. Since Groovy creates a public field into a getter and setter under the hood, Spring sees the setter and Grails is configured to inject by name, so as long as the field/setter matches a Spring bean name it works.
So your controller should look something like this:
class MyController {
def exportService
def myAction = {
if (params?.format && params.format != "html") {
response.contentType = grailsApplication.config.grails.mime.types[params.format]
response.setHeader("Content-disposition",
"attachment; filename=books.${params.extension}")
exportService.export(params.format,
response.outputStream,projectsList, [:], [:])
}
}
}
Note that I also removed the use of ConfigurationHolder since it's deprecated. The best way to get access to the config is from the grailsApplication bean. It's already injected in controllers (and taglibs), and in a service or other bean you'd just need a def grailsApplication field declaration.

dependency inject Grails Webflow

Is it possible to inject a Spring bean into a Grails webflow? I tried the following
class CheckoutController {
ShoppingService shoppingService
def checkoutFlow = {
start {
action {
// This attempt to access the service doesn't work
flow.addresses = shoppingService.getOrder()
}
}
}
}
I can access shoppingService from a regular controller action, but I can't access it from an action of the webflow (see above).
add the following to your controller:
def transient shoppingService
There are issues with dependency injection with webflows in controllers that contain traditional actions plus webflows. It worked for me if the traditional action executed first.
see:
GRAILS-7095
GRAILS-4141
Webflows also break notions of defaultAction in mixed controllers. I have found the first webflow wins and becomes the default action.
separately using transient keeps your service from be serialized between flow states. (e.g. don't have to implement serializable)
At first I thought what you listed was pseudocode but I made a sample app using your example and got the NPE as well. I think it may be your flow structure that is the problem. action blocks should go within a flow state. Your flow definition should look something like:
class CheckoutController {
ShoppingService shoppingService
def checkoutFlow = {
start {
action {
flow.addresses = shoppingService.getOrder()
if(flow.addresses) {
showForm()
}
else {
showError()
}
}
on("showForm").to "showForm"
on("showError").to "showError"
}
showError {
...
}
//etc.
}
}
You can definitely use injected services in your web flows. I am guessing that the problem lies in your flow structure.

Inject grails application configuration into service

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

Resources