DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Why and How To Integrate Elastic APM in Apache JMeter
  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts
  • Why "Polyglot Programming" or "Do It Yourself Programming Languages" or "Language Oriented Programming" sucks?

Trending

  • Why High-Performance AI/ML Is Essential in Modern Cybersecurity
  • Endpoint Security Controls: Designing a Secure Endpoint Architecture, Part 1
  • Revolutionizing Financial Monitoring: Building a Team Dashboard With OpenObserve
  • Ensuring Configuration Consistency Across Global Data Centers
  1. DZone
  2. Coding
  3. Languages
  4. Asynchronous Method calls with Groovy: @Async AST

Asynchronous Method calls with Groovy: @Async AST

By 
Felipe Gutierrez user avatar
Felipe Gutierrez
·
Sep. 11, 11 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
27.5K Views

Join the DZone community and get the full member experience.

Join For Free

At work, I needed to create a very simple background job, without any concern about what I could get back, because mostly all the hard work was just batch processing and persistence, and all exceptions or roll-back concerns were already taking care of.

At the beginning I used a very simple way to call my background job, using Java's: Executors.newSingleThreadExecutor()

void myBackgroundJob() {
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
//My Background Job
}
});
  }

 And it worked great, just what I needed. Using Groovy facilitate even more the way to create a new Background job, as simple as:

def myBackgroundJob() {
Thread.start {
//My Background Job
}
}

 Then, after this simple way to send something into the background, I decided to create a new AST in groovy, that remove the need to remember or copy and paste the same logic.

I created two annotations that help to identify the class and the methods that are going to be put into a new Thread. 

One for the Class:

package async

import org.codehaus.groovy.transform.GroovyASTTransformationClass
import java.lang.annotation.*
import xml.ToXmlTransformation

@Retention (RetentionPolicy.SOURCE)
@Target ([ElementType.TYPE])
@GroovyASTTransformationClass (["async.AsyncTransformation"])
public @interface Asynchronous { }

 And the other for the Method:

package async

import org.codehaus.groovy.transform.GroovyASTTransformationClass
import java.lang.annotation.*
import async.AsyncTransformation

@Retention (RetentionPolicy.SOURCE)
@Target ([ElementType.METHOD])
@GroovyASTTransformationClass (["async.AsyncTransformation"])
public @interface Async { }

 then the Asynchronous Transformation, using the AstBuilder().buildFromString(). Here I combined a GroovyInterceptable to connect the method being call with the AST transformation to wrapped with the Thread logic.

 

package async

import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.transform.*
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.ast.builder.AstBuilder

import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.ast.expr.MethodCallExpression
import org.codehaus.groovy.ast.expr.ClosureExpression
import org.codehaus.groovy.ast.expr.ConstantExpression

import org.codehaus.groovy.ast.stmt.BlockStatement

import org.codehaus.groovy.ast.expr.ClassExpression
import org.codehaus.groovy.ast.expr.ArgumentListExpression

@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) //CompilePhase.SEMANTIC_ANALYSIS
class AsyncTransformation implements ASTTransformation{

    void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {


        if (!astNodes ) return
        if (!astNodes[0] || !astNodes[1]) return
        if (!(astNodes[0] instanceof AnnotationNode)) return
        if (astNodes[0].classNode?.name != Asynchronous.class.name) return

        def methods = makeMethods(astNodes[1])
        if(methods){
            astNodes[1]?.interfaces = [  ClassHelper.make(GroovyInterceptable, false), ] as ClassNode []
            astNodes[1]?.addMethod(methods?.find { it.name == 'invokeMethod' })
        }
    }

    def makeMethods(ClassNode source){
         def methods = source.methods
         def annotatedMethods = methods.findAll {  it?.annotations?.findAll { it?.classNode?.name == Async.class.name } }

         if(annotatedMethods){

             def expression = annotatedMethods.collect { "name == \"${it.name}\"" }.join(" || ")

             def ast = new AstBuilder().buildFromString(CompilePhase.INSTRUCTION_SELECTION, false, """

                package ${source.packageName}

                class ${source.nameWithoutPackage} implements GroovyInterceptable {

                    def invokeMethod(String name, Object args){

                        if(${expression}){
                            Thread.start{
                                def calledMethod = ${source.nameWithoutPackage}.metaClass.getMetaMethod(name, args)
                                calledMethod?.invoke(this, args)
                            }
                        }else{
                           def calledMethod = ${source.nameWithoutPackage}.metaClass.getMetaMethod(name, args)?.invoke(this,args)
                        }
                    }

                }

             """)

             ast[1].methods
         }
    }

}

 The example:

package async

@Asynchronous
class Sample{

    String name
    String phone

    @Async
    def expensiveMethod(){
println "[${Thread.currentThread()}] Started expensiveMethod"
        sleep 15000
        println "[${Thread.currentThread()}] Finished expensiveMethod..."
    }

    @Async
    def otherMethod(){
println "[${Thread.currentThread()}] Started otherMethod"
        sleep 5000
        println "[${Thread.currentThread()}] Finished otherMethod"
    }
}

println "[${Thread.currentThread()}] Start"
def sample = new Sample(name:"AST EXample",phone:"1800-GROOVY")
sample.expensiveMethod()
sample.otherMethod()
println "[${Thread.currentThread()}] Finished"

 Final Notes:

As you can see on the example  I need to have the Asynchronous annotation on the class still. It could be better without it and just annotate the methods, something like the Groovy's SynchronizedASTTransformation. If you have any idea to complement this small example, please
clone the source code [here], and let me know what you think.

I could used the @javax.ejb.Asynchronous or the Spring's @org.springframework.scheduling.annotation.Async, but I only needed a very simple solution without any other configuration or library inclusion.

The remain logic here could be play more with multi threading and expect some results like: java.util.concurrent.Future<V> and its java.util.concurrent.Future<V>.get() method or maybe integrated with another frameworks like Spring.

 Source: [Here]

Groovy (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Why and How To Integrate Elastic APM in Apache JMeter
  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts
  • Why "Polyglot Programming" or "Do It Yourself Programming Languages" or "Language Oriented Programming" sucks?

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!