1

Grails 2.4.x here.

I have a requirement that all the methods of all my Grails services, generated by grails create-service <xyz>, be "wrapped"/intercepted with the following logic:

try {
    executeTheMethod()
} catch(MyAppException maExc) {
    log.error(ExceptionUtils.getStackTrace(maExc))
    myAppExceptionHandler.handleOrRethrow(maExc)
}

Where:

  • log.error(...) is the SLF4J-provided logger you get when you annotate your class with the @Slf4j annotation; and
  • ExceptionUtils is the one from org.apache.commons:commons-lang3:3.4; and
  • myAppExceptionHandler is of type com.example.myapp.MyAppExceptionHandler; and
  • This behavior exists (or has the option to exist in the event that it needs to be explicitly called somehow) for each method defined in a Grails service

So obviously this wrapper code needs to include import statements for those classes as well.

So for example if I have a WidgetService that looks like this:

class WidgetService {
    WidgetDataService widgetDataService = new WidgetDataService()

    Widget getWidgetById(Long widgetId) {
        List<Widget> widgets = widgetDataService.getAllWidgets()
        widgets.each {
            if(it.id.equals(widgetId)) {
                return it
            }
        }

        return null
    }
}

Then after this Groovy/Grails/closure magic occurs I need the code to behave as if I had written it like:

import groovy.util.logging.Slf4j
import org.apache.commons.lang3.exception.ExceptionUtils
import com.example.myapp.MyAppExceptionHandler

@Slf4j
class WidgetService {
    WidgetDataService widgetDataService = new WidgetDataService()

    MyAppExceptionHandler myAppExceptionHandler = new MyAppExceptionHandler()

    Widget getWidgetById(Long widgetId) {
        try {
            List<Widget> widgets = widgetDataService.getAllWidgets()
            widgets.each {
                if(it.id.equals(widgetId)) {
                    return it
                }
            }

            return null
        } catch(MyAppException maExc) {
            log.error(ExceptionUtils.getStackTrace(maExc))
            myAppExceptionHandler.handleOrRethrow(maExc)
        }
    }
}

Any ideas as to how I might be able to achieve this? I'm worried that a pure Groovy closure might interfere somehow with whatever Grails is doing to its services under the hood at runtime (since they are all classes that don't explicitly extend a parent class).

smeeb
  • 27,777
  • 57
  • 250
  • 447
  • 1
    You asked [a similar question a year ago](http://stackoverflow.com/questions/26407558/repetitive-try-catch-blocks-with-groovy-with-closure). Same approach can be used with a small tweak. by moving the common logic to a trait as a default implementation and then implementing the trait in service classes. Another approach would be to use `invokeMethod` to intercept each method in the service class and wrap them with try/catch. Demerit to use that approach is that it has to be repeated in each service class. I believe former approach will be cleaner and can be statically compiled. – dmahapatro Oct 30 '15 at 17:18
  • Thanks @dmahapatro (+1) - yes I remember that and actually had it in mind when asking this one, but like I said, I was worried that Grails might interfere some how. If you can move your comment into an answer with a code example showing one of your suggested approaches, I'll happily give you the green check! – smeeb Oct 30 '15 at 17:26

2 Answers2

2

Here is what I was trying to pin point in my comment:

package com.example

import groovy.util.logging.Log4j

@Log4j
trait SomeTrait {

    def withErrorHandler(Closure clos) {
        try {
            clos()
        } catch(Exception e) {
            log.error e.message
            throw new ApplicationSpecificException(
                "Application Specific Message: ${e.message}"
            )
        }
    }
}

Service class:

package com.example

class SampleService implements SomeTrait {

    def throwingException() {
        withErrorHandler {
            throw new Exception("I am an exception")
        }
    }

    def notThrowingException() {
        withErrorHandler {
            println "foo bar"
        }
    }
}

Test:

package com.example

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SampleService)
class SampleServiceSpec extends Specification {

    void "test something"() {
        when:
        service.throwingException()

        then:
        ApplicationSpecificException e = thrown(ApplicationSpecificException)
        e.message == "Application Specific Message: I am an exception"
    }

    void "test something again"() {
        when:
        service.notThrowingException()

        then:
        notThrown(Exception)
    }
}

Here is the sample app.

Grails 3.0.9 but it should not matter. this is applicable for Grails 2.4.*

dmahapatro
  • 49,365
  • 7
  • 88
  • 117
  • Thanks @dmahapatro (+1) - I will award you the green check and bounty as soon as SO permits me to do so. – smeeb Nov 03 '15 at 12:11
  • Also @dmahapatro - do you know what exact type `withErrorHandler` is returning? Void? Object? `def` always scares me for some reason. Thanks! – smeeb Nov 03 '15 at 12:21
  • Trait can be enhanced the way you want. It can return the result of the closure implementation. In Grails 3, we can go one step further and optimize the above logic using `TraitInjector`, but above should be good for Grails 2.4. Assign `def result = clos()` in trait and return result. Using def is advised because the default trait implementation can be generalized. def is a synonym for Object. :) It will statically compile as well. – dmahapatro Nov 03 '15 at 12:28
1

You can intercept the calls to your Service class methods either using MetaInjection or Spring AOP. So you don't have to write closure in each Service class. You can look into this blog that explains both the approaches with examples.

Sandeep Poonia
  • 2,158
  • 3
  • 16
  • 29