I'm running a small test using Groovy/Grails v5 to apply an AST transformation to a controller method. The method takes a single parameter, to represent a typical command object. I've added some logging to the AST transform class so within the visit() method, I can see that it is being applied.
If the controller method has no parameters, then I see the transform is being applied, no issue. However, if I add the parameter, the command object, to the method then I see my logging output twice.
The first time it's applied I can see the expected output that tells me it's being applied to the method, and that it's detected the parameter.
It then gets called a second time and tells me that it's being applied BUT this time it doesn't see any parameters.
This is the visit() method:
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class AjaxResponseASTTransformation extends AbstractAstTransformation {
private static final Logger LOG = LoggerFactory.getLogger(AjaxResponseASTTransformation.class);
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
final MethodNode methodNode = (MethodNode) nodes[1];
log("detected AjaxResponse annotation for method " + methodNode.getName());
final Parameter[] parameters = methodNode.getParameters();
if (parameters != null) {
for (Parameter parameter : parameters) {
log("Found Parameter : " + parameter.getName());
}
if (parameters.length == 0) {
log("No parameters present - what gives??");
}
}
}
and this is the controller method:
@AjaxResponse
def index(MyCommand myCommand) { render "" }
Building this project yields the following output:
20:01:00.537 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - detected AjaxResponse annotation for method index
20:01:00.538 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - Found Parameter : myCommand
20:01:00.538 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - detected AjaxResponse annotation for method index
20:01:00.538 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - No parameters present - what gives??
Can anybody explain this behaviour as to why it's being applied twice?
When you write a controller action like this:
def index(MyCommand myCommand) {
render ""
}
At compile time we generate a no-arg version of that method which looks something like this:
def index() {
MyCommand c = new MyCommand()
bindData c, request
// subject c to dependency injection here, code ommitted
// this call to validate is only generated if `MyCommand` is validateable...
c.validate()
// this will call your method
index(c)
}
Because of that, there are 2 methods in the class named index
even though you only wrote one.
At grails-plugin-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java#L395 is the part of the controller action transformer which is copying your annotation to the no-arg method.
This by the way is the reason that method overloading is not supported for controller action methods. We have to generate the no-arg one with the same name, so you can't have 2 action methods with the same name that accept different parameters. We generate a compiler error indicating that when that scenario is encountered.