smeeb
smeeb

Reputation: 29567

How to wrap all Grails service methods with a Groovy closure?

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:

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).

Upvotes: 1

Views: 811

Answers (2)

dmahapatro
dmahapatro

Reputation: 50275

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.*

Upvotes: 2

Sandeep Poonia
Sandeep Poonia

Reputation: 2188

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.

Upvotes: 1

Related Questions