I have a transactional controller with two actions inside it, edit and update. The update action has a command object parameter. If I call transactionStatus.setRollbackOnly() in the update action, I get MissingPropertyException for transactionStatus. The transactionStatus does not get injected when I have the action parameter. The edit action in the same controller, which does not have any parameters, has access to the transactionStatus property. If I remove the parameter from the problematic action, transactionStatus becomes available. I would like to keep the parameter and be able to rollback when there are errors.
I use grailsVersion=3.2.7. This is how my controller looks:
import com.sample.MyCommand
import grails.transaction.Transactional
#Transactional
class MyController {
def edit() {
//transactionStatus is available here
}
def update(MyCommand command) {
//transactionStatus.setRollbackOnly() causes
//groovy.lang.MissingPropertyException: No such property: transactionStatus for class: com.sample.MyController
}
}
P.S. I wanted to tag the questio with "grails-transactional," but it has not been created yet.
Related
I have a weird problem with integration testing restful controllers... In the following code snippet, when I make a post request from tests, the save method of the parent, RestfulController class is called instead of the save method of the child class, MyController and because they have different signatures, this ends up resulting in a UNPROCESSIBLE_ENTITY response.
class MyController extends RestfulController<MyDomain> {
static responseFormats = ['json', 'xml', 'hal']
MyController() {
super(MyDomain)
}
def save(MyCommand command) {
...
}
}
When I run the following test, the save() action of my controller's parent class, RestfulController gets executed, thus leading to UNPROCESSIBLE_ENTITY response, since I am using a Command object which is different from my domain class.
void "Test the save action correctly persists an instance"() {
when: "The save action is executed with valid data"
response = restBuilder.post(resourcePath) {
accept('application/json')
header('Authorization', "Bearer ${accessToken}")
json validJson
}
then: "The response is correct"
response.status == CREATED.value()
response.json.id
Vote.count() == 1
}
What can I do to fix this, please?
Overloading controller actions is not supported. You can override them, but you can't overload them.
What is happening is the framework is invoking the no-arg save() action in the parent class, which never invokes your method (nor should it).
You can rename your save(MyCommand command) so it doesn't have the same name as an action in your parent class and then provide a corresponding URL mapping and you will be on your way. Depending on what you want to do in the action, that may or may not be the best thing, but that is 1 path you can take.
I hope that makes sense.
In my Grails 2.3.8 app, I've defined the following controller action
class RegisterController {
def register(User user) {
render text: "User name is '$user.name'"
}
}
The user argument is a domain class instance. If I invoke this controller with the URL
http://localhost:8080/myapp/register/register
I get a NullPointerException. However my understanding of databinding is that if this action is invoked without any parameters, the argument should be assigned a new User()
However my understanding of databinding is that if this action is
invoked without any parameters, the argument should be assigned a new
User()
That is not necessarily the case. For domain class command objects if no parameters are present a new instance is only created for POST requests.
From http://grails.org/doc/2.4.0.RC1/guide/theWebLayer.html#commandObjects...
If the command object's type is a domain class and there is no id
request parameter then null will be passed into the controller action
unless the HTTP request method is "POST", in which case a new instance
of the domain class will be created by invoking the domain class
constructor.
That text may be missing from the 2.3.8 docs. I will verify that and add it if necessary.
What if you modify:
'$user.name'
To be:
'${user?.name}'
If I have a common static method in a class named Language.CheckLanguage(), and I want all action class execute() method will call CheckLanguage() like this
public String execute(){
Language.CheckLanguage();
// DO Business logic...
Return Action.SUCCESS;
}
Or any other convenient way?
Do it in a base Action, and extend it from any Action.
Then call super() as first row of every execute().
But, if you want it to be executed for every method, not only execute(), then you should write an Interceptor.
I have a filter and the controllerName var getting controller target.
For example:
when user try to access /myApp/book/index, my filter is triggered and controllerName is equals book. How can I get a BookController instance?
Tks
EDIT:
I can get an Artefact using:
grailsApplication.getArtefactByLogicalPropertyName("Controller", "book")
But what I do with this artefact?
The controller will be registered as a spring bean. Just grab it by name:
applicationContext.getBean('mypackage.BookController') // or
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", "book")
applicationContext.getBean(artefact.clazz.name)
As Burt said, you probably don't want one controller instance inside your filter. This is a wrong way to solve your problem.
Grails Controllers as injected automagically by Spring Framework, and there is some black magic and procedures made when creating it. So, I can assure you this is not the way to solve this problem.
As you yourself described, you want to call your action, and I can imagine you're trying to reuse some code that resides in your action, maybe to generate some data in your database, or even to work with your HTTP session, am I right?
So, you can do two things to solve this kind of issue.
1) Just redirect your request flow to to your controller/action like this:
if (something) {
redirect controller: 'xpto', action: 'desired'
return false
}
2) Or you can get the logic inside your action (that is doing that dirty job you want to run), separate that logic inside one service, and reuse the service in both classes (action / service) this way:
MyService.groovy
class MyService {
def methodToReuse() {
(...)
}
}
MyController.groovy
class MyController {
def myService //auto-injected by the green elf
def myAction = {
myService.methodToReuse()
}
}
MyFilters.groovy
class MyFilters {
def myService //auto-injected by the red elf
(...)
myService.methodToReuse()
(...)
}
[]s,
You should be able to call newInstance on the artefact you've retrieved. newInstance works just like the constructor so you can provide any parameters you would to a normal constructor call.
So you can probably just do:
def bookController = grailsApplication.getArtefactByLogicalPropertyName("Controller", "book").newInstance()
Working code:
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.context.ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) ServletContextHolder.getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
def grailsApplication
String nameController = "search"
def artefact = grailsApplication.getArtefactByLogicalPropertyName("Controller", nameController)
def controller = applicationContext.getBean(artefact.clazz.name)
In a Service of a Grails project, I like to find, at run time, the arguments of Dynamic Methods in order to inform callers.
Also, I like to call the method and if doesn't exist to return an error,
I will appeciate any help.
You can configure URLMappings in grails to get the value of the dynamic method and call it against your object for example you can do the following
In your urlMappings.groovy define a mapping with two embedded variables object and method
"/$object/$method" (controller:"api",action:"invoke")
Define a 'api' controller with an invoke action. See code below with the logic on how to invoke the method on the object
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
class ApiController {
def invoke = {
def object = params.object
def method = params.method
def args
if(object) {
def domainClass = AH.application.domainClasses.find{it.name == method}?.clazz
if(domainClass.metaClass.getStaticMetaMethod(method,args)) {
domainClass.metaClass.invokeStaticMethod(target,input.method,args)
}
}
}
}
In my example, I assumed that you're calling a static dynamic finder on the domain class. You can generalize this to handle instance methods as well. You need however to provide more information such as the object id, in your request to load the object and call the method against it.
"/$object/$id/$method" (controller:"api",action:"invoke")
-Ken
Not sure I understand your question, but the last part about checking if you can call a method on an object, this can be done by checking the meta class of the object you are dealing with like this.
obj.metaClass.respondsTo(obj, 'theMethodYouWantToCall')
obj is the object you want to call the method on, and theMethodYouWantToCall is the name of the method you want to call.
respondsTo will return an empty list [] if the method you are trying to call is not found