This question is connected with another.
I'd like to add properties to constructor and overwrite getLocalisedMessage() function to get proper translated message with error. First I want to overload constructor to set properties, but when I add:
GroovyCastException.metaClass.constructor = { Object objectToCast, Class classToCastTo ->
def constructor = GroovyCastException.class.getConstructor(Object, Class)
def instance = constructor.newInstance(objectToCast, classToCastTo)
// ... do some further stuff with the instance ...
println "Created ${instance} and executed!"
instance
}
and then get thrown GroovyCastException I don't get println in console.
Why?
How to overload constructor, set properties (objectToCast, classToCastTo) and then overload getLocalizedMessage?
I tried also:
def originalMapConstructor = GroovyCastException.metaClass.retrieveConstructor(Map)
GroovyCastException.metaClass.constructor = { Map m ->
// do work before creation
print "boot do work before creation "
m.each{
print it
}
print "boot do work before creation 2"
def instance = originalMapConstructor.newInstance(m)
// do work after creation
print "boot do work after creation"
instance
}
I 've put it in controller (right before catching exception) and in Bootstrap.groovy. Unfortunatelly there is no printlns in console output.
You're better off not using meta-programming to do internationalization. In grails, you should do it in the view layer with the <g:message> tag if possible. If not, the next best choice is the controller layer.
If you just want to display localized messages on an error page when an exception occurs, the best practice is to have a "500" URL mapping, and render the exception with a <g:renderException> in the view.
If you want to intercept the exception, you can change the "500" URL mapping to a controller and wrap it there before passing it to the view. Example:
// UrlMappings.groovy
class UrlMappings {
static mappings = {
...
"500"(controller:"error", method: "serverError")
}
}
// ErrorController.groovy
class ErrorController {
def serverError() {
def exception = request.exception.cause
if (exception instanceof GroovyCastException) {
exception = new LocalizedGroovyCastException(exception)
}
[exception: exception]
}
}
And then do your localization in a new class LocalizedGroovyCastException.
Related
I have a method in service class which has to be called from the controller. Both the controller and the service class codes are as below.
folder grails-app/services, file FileFactoryService.groovy
package edu.rev.document
import grails.transaction.Transactional
Class FileFactoryService{
Document document = new Document() // Domain object
def build(byte[] fileArray){
String str = new String(fileArray, "UTF-8") // UTF encoding as invoice may contain negative values
String[] lines = str.split("\\r?\\n")
document.version = lines[0].substring(0,1)
document.name = lines[1].substring(0,25)
}
return document.properties.collect()
}
Controller Code: folder: grails-app/controllers, file: FileController.groovy
package edu.rev.document
Class FileController{
def fileFactoryService
def save(){
def file = request.getFile('file')
if(file.empty) {
flash.message = "File cannot be empty"
} else {
def myList = fileService.build(file.getBytes())
}
}
The error thrown is
NullPointer exception when processing [POST]/../save
Cannot invoke method build() on NULL object
Can you please point me to the mistake I might be committing? Let me know if you need any other information
EDIT:This is the code. Just a heads up, the same logic when taken out of the service and implemented in the controller itself works perfectly alright. One more thing, when I use the "." operator inside the service (say document.), it doesnt show may any auto complete options like document.name.
Posting all of your code helps in finding the error. This line in your controller class
def myList = fileService.build(file.getBytes())
should be
def myList = fileFactoryService.build(file.getBytes())
In your controller class, you declared the service as :
def fileService
But the name of your service class is :
Class FileFactoryService
For Grails dependency injection to work, you need to name the variable like your class name :
def fileFactoryService
Then this should work.
Where should the separation of the UI message elements be if a grails service throws an exception? Should the message get loaded by the service and passed to the controller via the exception, or should the controller load the message based on the type of exception thrown? This assumes the message will have some parameter values that need to be filled in.
Here is an exception:
class CustomException extends RuntimeException {
String message
}
Loading the message source from the controller after catching the exception:
class MyService {
void doSomething() {
...
if (somethingBad) {
String value = 'Mary Smith'
throw new CustomException(value)
}
...
}
}
class MyController {
def myService
void processRequest() {
try {
myService.doSomething()
}
catch (CustomException e) {
flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
render view:'some_gsp'
}
...
}
}
Loading error from message source in the service where the controller pulls the message string the from the exception:
class MyService {
def messageSource
void doSomething() {
...
if (somethingBad) {
String value = 'Mary Smith'
throw new CustomException(messageSource.getMessage('thread.inactive.user', [value]))
}
...
}
}
class MyController {
def myService
void processRequest() {
try {
myService.doSomething()
}
catch (CustomException e) {
flash.error = e.message
render view:'some_gsp'
}
...
}
}
Frankly speaking, neither of those two places do you need the translations. :)
Separation Of Concern
Controller should only worry about HTTP methods and its delegation.
Services should take care of transactions and underlying business logic.
Declarative Error Handling
For 2.0.* and above, Grails provides you a sweet spot for handling errors. Guess what? Declarative Error Handling
All exception related code moves to a separate controller (in house) where they are handled properly, keeping your business controllers and services clean and abstracted from boiler plate codes.
For Grails 2.3.*, an added feature was to handle exception in the controller itself but most of the boiler plate (try catch stuff) is abstracted from the controller implementation.
Conclusion
If you are using v2.0.* and above then your controllers would look something like:
class MyController {
def myService
def processRequest() {
myService.doSomething()
...
}
}
//URL Mapping
static mappings = {
"500"(controller: "errors", action: "customException",
exception: CustomException)
}
//Error Controller
class ErrorsController {
def customException() {
def exception = request.exception
// perform desired processing to handle the exception
}
}
You can move the logic of error handling to a separate plugin if required in order to handle variety of errors/exception and unhappy paths. It becomes elegant to separate this concern.
If you are using v2.3.* then your controller would look something like:
class MyController {
def myService
def processRequest() {
myService.doSomething()
...
}
def handleCustomException(CustomException e) {
//Move this translation to src/groovy/utility if feasible
flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
render view:'some_gsp'
}
}
In this case no handling required from services as well, you just need to throe that exception.
I suppose you would get more input from various sources as well if you look around and are interested to use this pattern.
I have a need to have a method to return Id in case of success and list of errors in case of fail.
ex code snippet:
def save = {
def errors = []
if(Employee.save(flush:true)){
return Employee.id
}else{
errors.add("Can't be saved")
return errors.
}
}
In Service class
ICalling
Employee.save() - .. so how to check if it is error or id that save method returns
Any suggestions around would be appreciated.
I agree with Burk not to return different types, it can lead to unexpected errors.
Another solution to the problem is using Java's exception handling mechanism. You can add a context field to the Exception which will hold the list of validation errors.After catching the exception you can extract the errors.
void save(Employee employee) {
// do save
// ...
// on error:
def errors = [ "terrible error nr. 5" ]
throw new ValidationException(errors)
}
try {
fooService.save(employee)
} catch(ValidationException e) {
def errors = e.erorrs
// do stuff with the errors
}
An additional advantage: When no validation error is expected, the try-catch block can be ommited in Groovy, which makes the code cleaner because you don't have to care about any validation error fields.
Don't do this - even if you can make it somewhat more usable with Groovy, it's a bad idea. In this case though, there are a few simple solutions. If you're just passing the Employee instance and saving it in the service method, you don't need to return anything:
void save(Employee employee) {
employee.save(flush:true)
}
This is because if it's successful, the id will be set on the instance you passed in, and if not there will be one or more validation errors in the errors property (there's no need for you to return a generic error message when there are actually useful error messages available).
For example this would be the code you'd have in a controller calling the service:
def employee = new Employee(...)
fooService.save(employee)
if (employee.hasErrors()) {
// do something with employee.errors
}
else {
// success - use the id if you need via employee.id
}
If you want to pass in the data to create and save the new instance and return an Employee (this is the approach I usually take), it's similar:
Employee save(String name, int foo, boolean bar, ...) {
Employee employee = new Employee(name: name, foo: foo, bar: bar, ...)
employee.save(flush:true)
return employee
}
In this second case it's important to separate the save call and the return, since if there is a validation error save returns null and you want to always return a non-null instance. So do not do this:
return employee.save(flush:true)
If you separate them you can check the errors and/or the id.
Also, make sure that you do not use closures in services like you have in your code (def save = { ...). Only methods will be transactional since the Spring transaction handling doesn't know about Groovy closures - they're just fields that Groovy calls as if they were methods, but they're not.
I am looking for ways on how to cleanup my Grails controller code. In various controllers i more or less have the same logic..
get the object
check if it exists
etc..
Is there a suggested way on making controller actions reuse common code?
--- solution ---
All answers to the question have contributed to the solution we have implemented.
We created a class that is used in our controllers using the Mixin approach. One of the methods that the mixin exposes is the withObject method. This method takes the domainname from the controller and uses this a base for the method. This behaviour can be overridden of course!
def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) {
assert object
def obj = grailsApplication.classLoader.loadClass(object).get(params[id])
if(obj) {
c.call obj
} else {
flash.message = "The object was not found"
redirect action: "list"
}
}
So all answers have contributed to the solution! Thanks a lot!
I always pull out this blog post when this question comes up:
http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/
Basically you have a private helper for various domains in your controllers.
private def withPerson(id="id", Closure c) {
def person = Person.get(params[id])
if(person) {
c.call person
} else {
flash.message = "The person was not found."
redirect action:"list"
}
}
The way you code the getter is very flexible and a typical use for me (that is not covered in the blog) is for editing etc.
I normally code this way (i like the pattern for its clear division and readability):
def editIssue() {
withIssue { Issue issue ->
def issueTypes = IssueTypeEnum.values().collect {it.text }
[issueTypes:issueTypes,activePage:"issue", issue: issue]
}
}
def doEditIssue(IssueCommand cmd) {
if(cmd.validate()) {
withIssue { Issue issue ->
issue.updateIssue(cmd)
redirect(action: "show", id: issue.id)
}
}
else {
def issueTypes = IssueTypeEnum.values().collect {it.text }
render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"])
}
}
With my getter helper being:
private def withIssue( Closure c) {
def issue = Issue.get(params.id)
if(issue) {
c.call issue
}
else {
response.sendError(404)
}
}
I do think that the mixin method (very similar to the 'extend a common abstract controller' way) is nice too, but this way gives two advantages:
You can type the helper, like you see I do in the closure giving you access to the methods etc in STS/IDEA (not tested Netbeans)
The repetition is not very high, and the ability to change the getter (to use for example BarDomain.findByFoo(params.id) etc)
In the view I bind to edit() I just put an id="${issue.id}" in the <g:form> and it works seamlessly.
I wouldn't recommend inheritance for that, as you can't spread generic methods in several super classes. Your abstract class would quickly become messy if you have many controllers. You can't use composition (for instance using a Service) because you don't have access to response, render, or params directly from there.
The approach I use is to inject generic methods via Mixins.
#Mixin(ControllerGenericActions)
#Mixin(ControllerUtil)
class BookController {
def show = &genericShow.curry(Book)
def exists = {
render(idExists(Book))
}
}
The first action show uses a generic method in ControllerGenericActions.groovy, with an argument binded to it. The second use of a mixin idExists method is inside a controller action.
Here is an example code for src/groovy/ControllerGenericActions.groovy
class ControllerGeneric {
def genericShow(Class clazz) {
render clazz.get(params.id) as XML
}
}
and in src/groovy/ControllerUtil.groovy
class ControllerUtil {
def idExists (Class clazz) {
return clazz.get(params.id) != null
}
Not very useful in this case, but you get the idea.
Implement abstract controller with common methods (use 'protected' directive) and extend from it your real controllers. Do not use 'get' and 'set' words at the beginning of this method's names. Not good, but it works.
Lets assume that I have the following configuration in my conf/InjectionConfig.groovy file:
x {
a = { attrs, body -> out << "hello" }
b = { attrs, body -> out << "goodbye" }
}
and that I have a simple taglib such as
class XTagLib {
static namespace = "x"
}
What I want to do is that when I type <x:a /> to any of my views, it would print hello. I've already tried to inject these to the metaclass of the taglib as both property and method but neither seem to work. As an example, here's basically what I'm doing right now in a service:
public void afterPropertiesSet() throws Exception {
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().classLoader)
def slurper = new ConfigSlurper(GrailsUtil.environment)
ConfigObject xConfig
try {
xConfig = slurper.parse(classLoader.loadClass('InjectionConfig'))
}
catch (e) {
e.printStackTrace()
}
xConfig.x.each({
if ( !XTagLib.metaClass.hasMetaProperty(it.key) ) {
XTagLib.metaClass.registerBeanProperty(it.key, { args ->
def attrs = args[0], body = args[1]
it.value.call(attrs, body)
}
}
})
}
Am I just doing it wrong or is this even possible currently?
Well, this
def shell = new GroovyShell() // or get a GroovyClassLoader
Class yTagLibClass = shell.evaluate("class YTagLib { static namespace = 'x' }; return YTagLib")
yTagLibClass.metaClass.a = { attrs, body -> delegate.out << 'blabla' }
grailsApplication.addArtefact(TagLibArtefactHandler.TYPE, yTagLibClass)
<x:a/> nearly worked for me - registered a tag, except for it didn't output anything. You still need to make the closure resolve out against Grails' taglib's out property.
I don't see a pretty way to do it, as there's no access to instance variables, and out is an instance variable. See Grails source, JspInvokeGrailsTagLibTag.doStartTagInternal() - you might find a way.
EDIT: I added delegate. prefix that should resolve out property of target object. Now I believe I deserve an acceptance :)
What I want to do is that when I type
to any of my views, it would
print hello
I think there's an alternative way to do what you intend: combine template & tagLib. First, create a template, then add it in your TagLib (with no complex configuration).
In my opinion, it's more simple than your approach.
Please take a look at this tutorial:
http://jan-so.blogspot.com/2008/02/example-of-template-and-taglib-with.html