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; andExceptionUtils
is the one from org.apache.commons:commons-lang3:3.4
; andmyAppExceptionHandler
is of type com.example.myapp.MyAppExceptionHandler
; andSo 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).
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.*