{"id":29308,"date":"2015-10-30T18:28:15","date_gmt":"2015-10-30T12:58:15","guid":{"rendered":"http:\/\/www.tothenew.com\/blog\/?p=29308"},"modified":"2022-01-11T11:23:18","modified_gmt":"2022-01-11T05:53:18","slug":"intercept-grails-service-class-method-calls","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/intercept-grails-service-class-method-calls\/","title":{"rendered":"Intercept Grails Service class method calls"},"content":{"rendered":"<p>I was trying to intercept method calls of a Grails Service class for a little while. Adding interceptors to Controllers is really easy and I wanted to intercept calls to one of the methods in a Service class in a similar fashion. But adding interceptors to Grails Service Classes is not as straightforward as for Controllers. After doing some research I came up with two solutions.<\/p>\n<p><strong>Problem statement<\/strong>: Default flush mode in Grails 2.3 is AUTO, but for some Service Classes we needed the default flush mode as COMMIT. We can change default flush mode for whole application by specifying it in <code>grails-app\/conf\/DataSource.groovy<\/code>. Rather than changing it for whole application we wanted to change it for some selected Service classes only.<\/p>\n<p><strong>How to do it?<\/strong><br \/>\nBefore going for solution, lets see our sample domain model and demo service class for our example.<\/p>\n<p>Domain Class:<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\npackage com.ttnd.demo<\/p>\n<p>class Group {<\/p>\n<p>    String name<br \/>\n    String assignedRepId<br \/>\n    Date activationDate<br \/>\n    Date deactivationDate<\/p>\n<p>    static mapping = {<br \/>\n        table(name: &quot;_group&quot;)<br \/>\n    }<\/p>\n<p>    static constraints = {<br \/>\n        deactivationDate nullable: true<br \/>\n    }<\/p>\n<p>    @Override<br \/>\n    public String toString() {<br \/>\n        return &quot;Group{ $name(id: $id) }&quot;<br \/>\n    }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>Service Class:<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\npackage com.ttnd.demo<\/p>\n<p>import grails.transaction.Transactional<br \/>\nimport groovy.util.logging.Log4j<br \/>\nimport org.hibernate.FlushMode<\/p>\n<p>@Log4j<br \/>\n@Transactional<br \/>\nclass GroupService {<\/p>\n<p>    def grailsApplication<\/p>\n<p>    public Group persistGroup(Group group) {<\/p>\n<p>        FlushMode flushMode = grailsApplication.mainContext.sessionFactory.currentSession.flushMode<br \/>\n        log.debug(&quot;Flush mode for current Session: ${flushMode}&quot;)<\/p>\n<p>        if (group.validate()) {<br \/>\n            group.save(failOnError: true)<br \/>\n        } else {<br \/>\n            log.error(&quot;Error while saving Group Object $group&quot;)<br \/>\n        }<\/p>\n<p>        return group<br \/>\n    }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>Bootstrap some data:<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\nimport com.ttnd.demo.Group<br \/>\nimport com.ttnd.demo.GroupService<\/p>\n<p>class BootStrap {<\/p>\n<p>    GroupService groupService<\/p>\n<p>    def init = { servletContext -&gt;<br \/>\n        Group group = new Group(name: &quot;Admin&quot;, assignedRepId: &quot;USR-00-12&quot;, activationDate: new Date())<br \/>\n        groupService.persistGroup(group)<br \/>\n    }<\/p>\n<p>    def destroy = {<br \/>\n    }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p><strong>1. First Solution &#8211; Using MetaInjection<\/strong><br \/>\nWith this approach, we have to look up the <a title=\"Grails Development\" href=\"http:\/\/www.tothenew.com\/grails-application-development\">Grails services<\/a> and override the invokeMethod() for the Service classes. Here before invoking the original method, we will modify the default flush mode for the\u00a0current session. We will also add logging code so we can log when we enter and exit the method.<\/p>\n<p>We will put our code in <code>grails-app\/conf\/BootStrap.groovy<\/code> of our Grails application. After the above mentioned changes, here it is how the Bootstrap.groovy code will look like &#8211;<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\nimport com.ttnd.demo.Group<br \/>\nimport com.ttnd.demo.GroupService<br \/>\nimport org.hibernate.FlushMode<\/p>\n<p>class BootStrap {<\/p>\n<p>    def grailsApplication<br \/>\n    GroupService groupService<\/p>\n<p>    def init = { servletContext -&gt;<br \/>\n        setupServiceInterceptor()<\/p>\n<p>        Group group = new Group(name: &quot;Admin&quot;, assignedRepId: &quot;USR-00-12&quot;, activationDate: new Date())<br \/>\n        groupService.persistGroup(group)<br \/>\n    }<\/p>\n<p>    def destroy = {<br \/>\n    }<\/p>\n<p>    private void setupServiceInterceptor() {<br \/>\n        grailsApplication.serviceClasses.each { serviceClass -&gt;<br \/>\n            serviceClass.metaClass.invokeMethod = { name, args -&gt;<br \/>\n                delegate.log.info &quot;Invoking $name in ${delegate.class.name}&quot;<br \/>\n                def metaMethod = delegate.metaClass.getMetaMethod(name, args)<br \/>\n                try {<br \/>\n                    FlushMode flushMode = grailsApplication.mainContext.sessionFactory.currentSession.flushMode<br \/>\n                    delegate.log.debug(&quot;Inside setupServiceInterceptor: Flush mode for current Session: ${flushMode}&quot;)<br \/>\n                    grailsApplication.mainContext.sessionFactory.currentSession.setFlushMode(FlushMode.COMMIT)<\/p>\n<p>                    def result = metaMethod.invoke(delegate, args)<br \/>\n                    delegate.log.info &quot;Execution completed for method $name with result [$result]&quot;<br \/>\n                    return result<br \/>\n                } catch (Exception ex) {<br \/>\n                    delegate.log.error &quot;Exception occurred during execution of $name: ${ex.message}&quot;<br \/>\n                    throw ex<br \/>\n                }<br \/>\n            }<br \/>\n        }<br \/>\n    }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>When we run the application, it will print the following logs:<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\n2015-10-30 17:39:14,482 [localhost-startStop-1] INFO  demo.GroupService  &#8211; Invoking persistGroup in com.ttnd.demo.GroupService$$EnhancerBySpringCGLIB$$d26dc9bf<br \/>\n2015-10-30 17:39:14,511 [localhost-startStop-1] DEBUG demo.GroupService  &#8211; Inside setupServiceInterceptor: Flush mode for current Session: AUTO<br \/>\n2015-10-30 17:39:14,593 [localhost-startStop-1] DEBUG demo.GroupService  &#8211; Inside GroupService.persistGroup(): Flush mode for current Session: COMMIT<br \/>\n2015-10-30 17:39:14,714 [localhost-startStop-1] INFO  demo.GroupService  &#8211; Execution completed for method persistGroup with result [Group{ Admin(id: 1) }]<br \/>\n[\/code]<\/p>\n<p><strong>2. Second Solution &#8211; Using Spring AOP<\/strong><br \/>\nInstead of using MetaInjection we can use Spring AOP Interceptors. Don&#8217;t worry if you are not familiar with Spring AOP. For this basic example, you are not required to have an expertise in Spring AOP.<\/p>\n<p>Key definitions of some AOP concepts that we are going to use here:<\/p>\n<ul>\n<li>Aspect: a modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in J2EE applications. In Spring AOP, aspects are implemented using regular classes (the schema-based approach) or regular classes annotated with the @Aspect annotation (the @AspectJ style).<\/li>\n<li>Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.<\/li>\n<li>Advice: action taken by an aspect at a particular join point. Different types of advice include &#8220;around,&#8221; &#8220;before&#8221; and &#8220;after&#8221; advice.<\/li>\n<li>Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.<\/li>\n<li>Around advice: Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.<\/li>\n<\/ul>\n<p>Create an interceptor class under <code>src\/groovy<\/code> named ServiceInterceptor.groovy. Annotate it using @Aspect annotation. Define its bean under <code>grails-app\/conf\/spring\/resources.groovy<\/code>.<\/p>\n<p>Now after creating an AOP bean, we need to add a PointCut method. This method will identify which Service Method calls to intercept. Create a method with name executeMethods. Annotate this method with <code>@Pointcut<\/code> annotation and define the pointcut expression here. Leave the definition body blank.<\/p>\n<p>Now we need to declare an advice. Advice is associated with a pointcut expression, and runs before, after, or around method executions matched by the pointcut. Here we will use the around advice. Create another method with name interceptJobExecuteMethod and annotate it with @Around(&#8220;executeMethods()&#8221;) annotation. In <code>@Around<\/code> annotation, we are providing the pointcut method name.<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\npackage com.ttnd.demo<\/p>\n<p>import groovy.util.logging.Log4j<br \/>\nimport org.aspectj.lang.ProceedingJoinPoint<br \/>\nimport org.aspectj.lang.annotation.Around<br \/>\nimport org.aspectj.lang.annotation.Aspect<br \/>\nimport org.aspectj.lang.annotation.Pointcut<br \/>\nimport org.hibernate.FlushMode<\/p>\n<p>@Log4j<br \/>\n@Aspect<br \/>\nclass ServiceInterceptor {<\/p>\n<p>    def grailsApplication<\/p>\n<p>\t\/**<br \/>\n\t * expression to identify which method calls to intercept<br \/>\n\t*\/<br \/>\n    @Pointcut(&quot;execution(public * com.ttnd.demo.GroupService.persistGroup(..))&quot;)<br \/>\n    public void executeMethods() {}<\/p>\n<p>    @Around(&quot;executeMethods()&quot;)<br \/>\n    def interceptJobExecuteMethod(ProceedingJoinPoint joinPoint) {<br \/>\n        FlushMode flushMode = grailsApplication.mainContext.sessionFactory.currentSession.flushMode<br \/>\n        log.debug(&quot;Inside ServiceInterceptor: Flush mode for current Session: ${flushMode}&quot;)<\/p>\n<p>\t\t\/\/Modify defaul flush mode for current session<br \/>\n\t\tgrailsApplication.mainContext.sessionFactory.currentSession.setFlushMode(FlushMode.COMMIT)<\/p>\n<p>\t\t\/\/Proceed with method execution<br \/>\n\t\tjoinPoint.proceed()<br \/>\n    }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p><code>grails-app\/conf\/spring\/resources.groovy<\/code><\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\nbeans = {<br \/>\n    serviceInterceptor(com.ttnd.demo.ServiceInterceptor){<br \/>\n        grailsApplication = ref(&quot;grailsApplication&quot;)<br \/>\n    }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>When we run the application, it will print the following logs:<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\n2015-10-30 18:07:49,814 [localhost-startStop-1] DEBUG demo.ServiceInterceptor  &#8211; Inside ServiceInterceptor: Flush mode for current Session: AUTO<br \/>\n2015-10-30 18:07:49,947 [localhost-startStop-1] DEBUG demo.GroupService  &#8211; Inside GroupService.persistGroup(): Flush mode for current Session: COMMIT<br \/>\n[\/code]<\/p>\n<p>Please share your thoughts in the comment section. Will be happy to hear from you. Thanks.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was trying to intercept method calls of a Grails Service class for a little while. Adding interceptors to Controllers is really easy and I wanted to intercept calls to one of the methods in a Service class in a similar fashion. But adding interceptors to Grails Service Classes is not as straightforward as for [&hellip;]<\/p>\n","protected":false},"author":182,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":11},"categories":[7,1],"tags":[4840,2708,2685,2684],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/29308"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/182"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=29308"}],"version-history":[{"count":1,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/29308\/revisions"}],"predecessor-version":[{"id":54510,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/29308\/revisions\/54510"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=29308"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=29308"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=29308"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}