Reputation: 29567
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).
Upvotes: 1
Views: 811
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
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