-1

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.

Community
  • 1
  • 1
Michal_Szulc
  • 4,097
  • 6
  • 32
  • 59
  • why don't you use delegate? – injecteer Feb 02 '16 at 13:33
  • 1
    it's not because you can do this kind of thing in groovy you should : you shouldn't override the metaclass of an exception "just" to add translation in your ui layer. you're opening the doors to a ton of unpredictables bugs – Jérémie B Feb 02 '16 at 13:42
  • @injecteer yeah, right now I'm checking ExpandoMetaClass and delegate. – Michal_Szulc Feb 02 '16 at 14:12
  • @JérémieB instead of "just don't", please answer how would you translate such an exception if my way is not good. I'd like to write it as clean as possible, but I do not see any other solution right now. – Michal_Szulc Feb 02 '16 at 14:12

1 Answers1

2

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.

ataylor
  • 64,891
  • 24
  • 161
  • 189
  • This app doesn't have any views. It only generates JSON API. That's why I want to translate my errors and send back in most human-friendly form (and of course extended as JSON, not only http status). – Michal_Szulc Feb 02 '16 at 19:06
  • 1
    In that case, you could use an ErrorController to render server errors as JSON. The strategy of wrapping exceptions to provide i18n still applies. – ataylor Feb 02 '16 at 19:11
  • So in case of http status 400 and thrown GroovyCastException by service, how should I handle it? I'm not sure where put try-catch block and how should it looks like. – Michal_Szulc Feb 02 '16 at 19:18
  • 1
    You'd generally want to send a 400 on bad input. I'd recommend validating the input more carefully rather than relying on a cast exception. In particular, you can create a Command object to take advantage of grails's extensive support for data binding and validation: http://grails.github.io/grails-doc/latest/guide/single.html#commandObjects – ataylor Feb 02 '16 at 20:33
  • I heard about them but I always wonder if should I create command object for each domain class and try it (parse data through such a command object) before every create/update operation? Is it a common practise? – Michal_Szulc Feb 02 '16 at 21:49