I'm trying to call a method on a grails service from a controller, but it looks like execution is just skipping the method call.
I've tried debugging the application with a breakpoint inside the method but it is never hit.
My service (generated with grails create-service) is:
class FormatterService {
static transactional = false
def formatList (List<Host>, String fmt) {
OutputObject somePOGO = new OutputObject();
(snip)
return somePOGO
}
}
Then on my controller I have:
class HostController {
def formatterService
def getHostsByLabels = {
(snip)
OutputObject o = formatterService.formatList(someHosts,params.format)
(snip)
}
}
When the formatterService.formatList method should be called in the controller, execution simply skips to the next line, no output is printed to the console and breakpoints within the method are not hit. The OutputObject o reference is null afterward.
What is wrong here? It could be a really basic mistake from my part, but I just can't put my finger on it...
To Me it seems a MetaProgramming Disaster..
Well there are 3 Tests to Debug:
_1) first try to do
println formatterService
println formatterService.getClass()
just to check if its injected bean is the desired one, some plugins sometimes inject beans which overrides the default.
_2) Make sure that the method with a name "formatList" is not injected in your services through metaprogramming by any plugin or core code.
How to test this is simple: Just change the name of the method to some Unrealistic One, ex: "formatListabcdewdw" and then call that one. If it works then its method overriden issue.
and if you are more enthusiastic you can see the metaMethods by
println formatterService.metaClass.methods
_3) just try to do "params.format as String" as the last argument in the method call.\
.
Hope any of these helps, please Do let me know of the findings, i am curious to know.. :)
I found the issue. It has to do with the method signature.
Printing out the thrown exception's message, it says:
No signature of method: hms.FormatterService.formatList() is applicable for argument types: (java.util.TreeSet, java.lang.String) values: (...)
Possible solutions: formatList(java.util.List, java.lang.String)
So, a rookie mistake (wanting to pass a TreeSet for a List) aided by weak typing in Groovy... :P
I've changed the method signature to
def formatList ( items, String fmt) {
and call it as
def activeHosts = ...
OutputObject o = formatterService.formatList(activeHosts, params.format as String)
and now it works.
Related
When I do custom rejectValue in a service method grails loses that error(s) between service method and return to controller. This seems to happen when updating a row instance, but not when creating one.
In service
def specialValidation(petInstance){
if(petInstance.petType.requiresStateId && !petInstance.StateId){
petInstance.errors.rejectValue('StateId','StateId required');
}
println petInstance.errors //shows 1 error
return petInstance;
}
In controller
...
petInstance.properties=params;
petInstance=petService.specialValidation(petInstance);
println petInstance.errors //shows 0 errors
How is the error being lost when the instance changes hands from service to controller?
It can be because of transactional service. Service opens separate transaction for each method and clears entities after method end. You can find this mentioned in docs(read the last paragraph of part )
I had the same problem. Than I've added NotTransactional annotation to validation method, and it helped. Errors were saved.
Well I did something simular :
orderService.validate(order, params)
if (order.hasErrors()) {
return render(view: 'create', model: [order: order])
}
In the Service I do some validation like this:
if (end.before(start)) {
order.errors.rejectValue("end", '', 'ERROR');
}
The different to yours is that i didn't set the errorCode but the message at itself, have a look at the rejectValue Methods:
void rejectValue(String field, String errorCode);
void rejectValue(String field, String errorCode, String defaultMessage);
You could also try to use the rejectValue method like me, maybe it helps.
I found you can also avoid this by using
MyDomain.read(id)
instead of
MyDomain.get(id)
I have a method looks like this:
public void save(DbSession session,Wrappe wrapper,Wrappe wrappe){
//...other logic
//save wrapper
wrapper=(Wrapper)session.save(wrapper)
//set wrapper's id into wrappee
wrappee.setWrapperId(wrapper.getId());
//save wrappee
session.save(wrappee);
}
and test code looks like this:
given:
session.save(_) >> wrapperWithGeneratedId
when:
obj.save(session,wrapper,wrappee)
then:"wrapper got saved"
1*session.save(_) >> {Wrapper save ->
diffs(wrapper,saved)==null
}
and:"wrappee"
1*session.save(_) >> {Wrappe saved ->
diffs(wrappee,saved)==null
}
These test code will give an exception:
java.lang.ClassCastException: java.lang.Boolean cannot be cast to com.company.model.Wrapper
If commented verification closure in "then" section,test will pass,so I guess this section
1*session.save(_) >> {Wrapper save ->
diffs(wrapper,saved)==null
}
overrode this mocking:
session.save(_) >> wrapperWithGeneratedId
Is any way do both correctly?
1st. 'and' is syntactic sugar. It's just a way to visually separate code within the same block. Your last two mocks are effectively the same (although since you're testing behaviorally it will still verify that save is called twice.)
2nd. Assuming you want to verify thatdiffs(wrapper,saved)==null, that won't currently happen because it's not a 'base level' evaluation. Anything within then/where/closures/etc needs to be prepended with 'assert ' if you want to evaluate it.
3rd. A then block is scoped to its when block and can override existing mocks; your assumption that your mock was being overwritten is correct.
4th. Is there any reason you don't just include your return value alongside your evaluation?
2 * session.save(_) >> {Wrapper save ->
diffs(wrapper,saved)==null
return wrapperWithGeneratedId
}
5th. Your error is due to your mock returning a boolean (your assertion logic) which Groovy then tries (and fails) to parse into a Wrapper. My assumption for why this is happening is that .save() has a return type of Wrapper. To fix that you will either need to create a boolean constructor for Wrapper, or change your mock to return something Groovy can turn into a Wrapper (how-to in point 4)
Official Stub/Mock/Spy documentation (quite good, worth a read)
I have requirement in which i need some logic of criteria query to be config driven. Earlier i used to query like :
e.g.:
User.createCriteria().list{
or{
eq('username',user.username)
eq('name',user.name)
}
}
But, i need this to be configurable in my use case so, i try this code snippet.
def criteriaCondition= grailsApplication.config.criteriaCondition?:{user->
or{
eq('username',user.username)
eq('name',user.name)
}
}
User.createCriteria().list{criteriaCondition(user)}
But, This doesn't work for me. I am getting missing method exception for "or" I tried few solution from some sources but it didn't worked for me.
So, can anyone help me :
1) How to make the above given code work.
2) Any other better way for my use case.
Thanks in advance!!!
you have to pass criteriaBuilder object to the closure, something like this:
def criteriaCondition = grailsApplication.config.criteriaCondition ?: { cb, user ->
cb.or{
cb.eq('username',user.username)
cb.eq('name',user.name)
}
}
def criteriaBuilder = User.createCriteria()
criteriaBuilder.list{
criteriaCondition(criteriaBuilder, user)
}
obviously, closure in the Config.groovy also has to have the same parameters list, including cb
The way the criteria builder mechanism works, the list method expects to be passed a closure which it will call, whereas your current code is calling the criteriaCondition closure itself rather than letting the criteria builder call it. "Currying" will help you here: given
def criteriaCondition= grailsApplication.config.criteriaCondition?:{user->
or{
eq('username',user.username)
eq('name',user.name)
}
}
instead of saying
User.createCriteria().list{criteriaCondition(user)}
you say
User.createCriteria().list(criteriaCondition.curry(user))
(note the round brackets rather than braces).
The curry method of Closure returns you another Closure with some or all of its arguments "pre-bound" to specific values. For example
def add = {a, b -> a + b}
def twoPlus = add.curry(2) // gives a closure equivalent to {b -> 2 + b}
println twoPlus(3) // prints 5
In your case, criteriaCondition.curry(user) gives you a zero-argument closure that you can pass to criteria.list. You can curry as many arguments as you like (up to the number that the closure can accept).
I've created a code sample that shows the issue I'm having:
class BindingExample {
public static void main(String[] args) {
Closure closure1 = {
printit.call("Hello from closure 1")
}
Closure closure2 = {
printit("Hello from closure 2")
}
Closure printit = { s ->
println("printing: "+s)
}
Binding binding = new Binding()
binding.setVariable("printit", printit)
closure1.delegate = binding
closure2.delegate = binding
closure1() //This works fine
closure2() //This does not.
//Why does .call() work and () alone not? Most documentation says they're the same.
}
}
Printit is a Closure, which the documentation indicates implements doCall and therefore is callable in short form via ().
However, when this closure is made available via binding to a delegate, only the long-form version of the call is permitted. The output is:
printing: Hello from closure 1
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: groovy.lang.Binding.printit() is applicable for argument types: (java.lang.String) values: [Hello from closure 2]
Can someone explain why this is the case? If possible, I'd like to also see how to make it so the short-form version works. I was able to make it work by defining printit as a proper static method (not a closure), but that won't work for my case because I actually need printit to be given some data available only inside of the method scope (not included in the example since my question relates to the binding itself).
As to WHY this is the case, I can't give a definite answer, unfortunately. There's some talk about implicit-"this" annotation, etc. It seems like it should work, but that there's some vagueness about what should be tried first (this-scope or delegate).
That the issue exists, currently, seems correct. I've found the following other resources that agree, with some discussion without resolution about why.
Nabble discussion about the issue:
http://groovy.329449.n5.nabble.com/Binding-Closure-property-not-called-as-method-td5562137.html
JIRA ticket resulting:
https://issues.apache.org/jira/browse/GROOVY-5367
In a controller I have this finder
User.findByEmail('test#test.com')
And works.
Works even if I write
User.findByEmail(null)
But if i write
User.findByEmail(session.email)
and session.email is not defined (ergo is null) it throw exception
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
Is this behavior right?
If i evaluate "session.email" it give me null so I think it must work as it do when I write
User.findByEmail(null)
Even more strange....
If I run this code in groovy console:
import myapp.User
User.findByEmail(null)
It return a user that has null email but if I run the same code a second time it return
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
You can't use standard findBySomething dynamic finders to search for null values, you need to use the findBySomethingIsNull version instead. Try
def user = (session.email ? User.findByEmail(session.email)
: User.findByEmailIsNull())
Note that even if User.findByEmail(null) worked correctly every time, it would not necessarily give you the correct results on all databases as a findBySomething(null) would translate to
WHERE something = null
in the underlying SQL query, and according to the SQL spec null is not equal to anything else (not even to null). You have to use something is null in SQL to match null values, which is what findBySomethingIsNull() translates to.
You could write a static utility method in the User class to gather this check into one place
public static User byOptEmail(val) {
if(val == null) {
return User.findByEmailIsNull()
}
User.findByEmail(val)
}
and then use User.byOptEmail(session.email) in your controllers.
Jeff Brown from grails nabble forum has identified my problem. It's a GORM bug. see jira
More info on this thread
This jira too
I tried with debugger and it looks it should be working, as you write. Maybe the groovy itself is a little bit confused here, try to help it this way:
User.findByEmail( session['email'] )