I have two services with the same name in my Grails 2.4.4 project, called RemittanceService. One is in the package on ph.bank (which I created weeks ago) and another is on ph.gov.advice.slip(which I created today). Since now there are two instances of the service with the same name, I replaced all dependency-injection of ph.bank.RemittanceService from:
class ... {
def remittanceService
...
}
into this:
class ... {
def bankRemittanceService
// added the word `bank` from its package
...
}
and the injections for ph.gov.advice.slip.RemittanceService into:
class ... {
def slipRemittanceService
// added the word `slip` from its package
...
}
Now the problem is that it doesn't point the respective Service, and instead, returns an error:
java.lang.NullPointerException:
Cannot invoke method [...] on null object
I decided to revert it my previous code. When I return the declaration into:
def remittanceService
it always points to the Service found on ph.bank, never to my newly-created Service. My current fix is:
import ph.gov.advice.slip.RemittanceService
class ... {
def slipRemittanceService = new RemittanceService()
// Here I initialize it instead on how dependencies should be declared
...
}
Although, I felt that is wrong. Is there more Grails-ly way to do this?
You can declare beans in your resources.groovy file.
beans = {
bankRemittanceService(ph.bank.RemittanceService) {
}
slipRemittanceService(ph.gov.advice.slip.RemittanceService) {
}
}
now you can inject bankRemittanceService and slipRemittanceService
Related
Suppose the following situation...
I have to perform a process which takes a lot of time (about 1 hour)
and I do not want the user of my application to wait until it ends.
The business logic of this process is encapsulated in several services.
So:
class MyService{
def myService2
def myService3
public doSomething(){
myService2.doSomething()
myService3.doSomething()
}
}
class MyController{
def myService
def anAction(){
myService.doSomething()
redirect(action:'index')
}
}
I want the call to my service to be asynchronous.
I tried to create an asynchronous service as indicated by the Grails documentation, using #DelegateAsync.
class AsyncMyService {
#DelegateAsync MyService myService
}
class MyController{
def asyncMyService
def anAction(){
asyncMyService.doSomething()
.onComplete { List results ->
println "completed!"
}
redirect(action:'index')
}
}
But I get the following error when compiling my application:
Caused by ConversionNotSupportedException: Failed to convert property value of type
'MyService2$$EnhancerBySpringCGLIB$$f08a1b38' to equired type 'grails.async.Promise'
for property 'myService2'; nested exception is java.lang.IllegalStateException: Cannot
convert value of type [MyService2$$EnhancerBySpringCGLIB$$f08a1b38] to required type
[grails.async.Promise] for property 'myService2': no matching editors or conversion
strategy found
Any ideas to solve this problem?
Thanks!
Grails 3 allows authors to use startup hooks similar to the ones provided to Grails 2 plugins. I'm looking at defining beans in the doWithSpring closure, and I'd like to pass values into a new bean based on some configuration values. I can't figure out, however, how to get the grailsApplication instance or the application configuration. How do you do this with Grails 3?
Your plugin should extend grails.plugins.Plugin which defines the getConfig() method. See https://github.com/grails/grails-core/blob/9f78cdf17e140de37cfb5de6671131df3606f2fe/grails-core/src/main/groovy/grails/plugins/Plugin.groovy#L65.
You should be able to just refer to the config property.
Likewise you can refer to the inherited grailsApplication property which is defined at https://github.com/grails/grails-core/blob/9f78cdf17e140de37cfb5de6671131df3606f2fe/grails-core/src/main/groovy/grails/plugins/Plugin.groovy#L47.
I hope that helps.
Under Grails 3, I took Jeff Scott Brown's advice and used GrailsApplicationAware instead:
This is how you go about setting up a configuration bean:
So in your new plugin descriptor you need to change grails 2 style def doWithSpring to a ClosureDoWithSpring as below:
Notice in Grails 2 we injected grailsApplication, in grails 3 all we do is declare the bean:
/*
def doWithSpring = {
sshConfig(SshConfig) {
grailsApplication = ref('grailsApplication')
}
}
*/
Closure doWithSpring() { {->
sshConfig(SshConfig)
}
}
Now to get your plugin configuration:
src/main/groovy/grails/plugin/remotessh/SshConfigSshConfig.groovy
package grails.plugin.remotessh
import grails.core.GrailsApplication
import grails.core.support.GrailsApplicationAware
class SshConfig implements GrailsApplicationAware {
GrailsApplication grailsApplication
public ConfigObject getConfig() {
return grailsApplication.config.remotessh ?: ''
}
}
grails.plugin.remotessh.RemoteSsh.groovy:
String Result(SshConfig ac) throws InterruptedException {
Object sshuser = ac.config.USER ?: ''
Object sshpass = ac.config.PASS ?: ''
...
This is now your configuration object being passed into your src groovy classes. The end user application would pass in the sshConfig bean like this:
class TestController {
def sshConfig
def index() {
RemoteSSH rsh = new RemoteSSH()
....
def g = rsh.Result(sshConfig)
}
Edited to add, just found this :) which is relevant or duplicate question:
http://grails.1312388.n4.nabble.com/Getting-application-config-in-doWithSpring-closure-with-a-Grails-3-application-td4659165.html
we are preparing for Grails 2.4 upgrade. One of the issues we face is that
most of the command object unit tests fails because of injected properties, like
services, are required to be not null during validation.
Is there any suggested way how to test this? Should we mock all properties although some are not needed for test? or is there a way to do this differently?
After my question is answered by Jeff, I share links with more information about new functionalities:
doWithSpring and doWithConfig are shortly described in What's new in 2.4: http://grails.org/doc/latest/guide/introduction.html#whatsNew24 in Unit Testing improvements section
There is also a JIRA issue with example: https://jira.grails.org/browse/GRAILS-11003
Most unit tests don't want or need the application context all spun up and populated. Unit tests can add whatever they want (or nothing) to the application context. In recent versions of Grails you can do something like this...
A controller and command object:
// grails-app/controllers/demo/DemoController.groovy
package demo
class DemoController {
def processName(SomeCommand co) {
render co.someValue
}
}
class SomeCommand {
String name
def helperService
def getSomeValue() {
helperService.processValue(name)
}
}
A service:
// grails-app/services/demo/HelperService
package demo
class HelperService {
def processValue(String originalValue) {
"__${originalValue}__"
}
}
A unit test:
// grails-app/test/unit/demo/DemoControllerSpec.groovy
package demo
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(DemoController)
class DemoControllerSpec extends Specification {
def doWithSpring = {
helperService HelperService
}
void 'process name'() {
when:
params.name = 'Jeff'
controller.processName()
then:
response.contentAsString == '__Jeff__'
}
}
That test will pass with Grails 2.4.2.
I hope that helps.
Relating to Accessing grails application config from a quartz job:
Apparently, DI doesn't happen prior to the creation of a job. I'm guessing this is the same with other grails artefacts (couldn't spot relevant documentation).
In my particular case, I was aiming to load a property from config and expose that property from the job class. In general though, it seems a valid use-case to me, that artefacts will load configuration, and then return those properties via API.
I'm wondering then, how could this be achieved when a class cannot rely on access to grailsApplication.config at construction.
Thanks
Try with:
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
class MyJob {
def execute() {
def myConfigVar = CH.flatConfig.get('my.var.setup.in.config.groovy')
...
}
}
OR
import grails.util.Holders
class MyJob {
def execute() {
def myConfigVar = Holders.config.my.var.setup.in.config.groovy
...
}
}
I am using Grails 2.2.1, and I have a custom dataSource injected into the service so that I can execute some SQL queries.
Upon first execution, there is a dataSource, but on each subsequent call, the reference to the dataSource has become null.
class ReportService {
def dataSource_myds
Object[] reportRecords(int a) {
String query = "SELECT ..."
Object[] resultSet;
Sql sql = new Sql(dataSource_myds)
// ^ Here the NullPointerException is thrown
// But it always works at the first execution
sql.eachRow(query, [a]) {
...
resultSet += result
}
return resultSet
}
}
class ReportController {
ReportService reportService
def report = {
...
Object[] resultSet1 = reportService.reportRecords(1)
...
Object[] resultSet2 = reportService.reportRecords(2)
// ^ java.lang.NullPointerException : Must specify a non-null Connection
...
}
}
Has anyone ever seen this before, and if so, how can I avoid this?
Here is my DataSource.groovy
environments {
development {
dataSource_myds {
url = "jdbc:oracle:thin:#..."
driverClassName = "oracle.jdbc.driver.OracleDriver"
username = "..."
password = "..."
}
}
}
Try, to use resources.groovy way as well. This will also give you option for environment basis datasource.
Explained well on the link given below:
Grails 2 multiple dynamic datasources in services
Thanks
Solved avoiding 2 subsequent calls to the service. It seems the framework nulls the service connection after the first call from the controller.
James Kleeh's comment solved it for me - grails clean and then restart the app.
I had a similar issue and I got it fixed. Firstly, make sure your Service class is in the grails-app/services folder. Secondly, you need to make sure you get the object of the service class using the injection mechanism and not by using the constructor. I had my service class in the right folder but I was trying to create the instance of the service class as MyService.instance in my controller and having the issue of null dataSource/connection. Then I tried def myService in my controller instead of MyService.instance and it worked. Hope this helps. Thanks