Concurrency / asynchronous / background process plugin for grails 3
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
gradle/wrapper
grails-app
src
.gitignore
LICENSE
README.md
build.gradle
gradle.properties
gradlew
gradlew.bat

README.md

grails-executor

Concurrency / asynchronous / background process plugin for grails 3

grails2 version can be found here: https://github.com/basejump/grails-executor

Summary

This grails plugin incorporates the java concurrency Executor Framework into a plugin so your grails app can take advantage of asynchronous (background thread / concurrent) processing. The main need for this as opposed to just using an ExecutorService from Executors is that we need to wrap the calls so there is a Hibernate or other Data provider session bound to the thread. This uses the following pattern to wrap Runnable/Closures so they get a session for whatever Gorm you are using. Hibernate being the default but this is also tested with Mongo (no heavily) See the info on the PersistenceContextInterceptor grails bean for more info

// injected spring bean
PersistenceContextInterceptor persistenceInterceptor

protected wrap(Closure wrapped) {
	persistenceInterceptor.init()
	try {
		wrapped()
	} finally {
		persistenceInterceptor.flush()
		persistenceInterceptor.destroy()
	}
}

Here are a couple of links to get give you some background information.

http://www.ibm.com/developerworks/java/library/j-jtp1126.html
http://www.vogella.de/articles/JavaConcurrency/article.html

and here are few good write up on groovy concurrency
http://groovy.codehaus.org/Concurrency+with+Groovy
and a slide show
http://www.slideshare.net/paulk_asert/groovy-and-concurrency-paul-king

Installation

Edit build.gradle, by adding the following:

repositories {
    ...
    maven { url "http://dl.bintray.com/uberall/plugins" }
    ...
}

dependencies {
    ...
    compile 'org.grails.plugins:grails-executor:0.2'
    ...
}

Setup

The plugin sets up a Grails service bean called executorService so you need do nothing really. It delegates to an implementation of an Java ExecutorService (not to be confused with a Grails Service) interface so read up on that for more info on what you can do with the executorService. It basically wraps another thread pool ExecutorService. By default it uses the java Executors utility class to setup the injected thread pool ExecutorService implementation. The default Grails executorService config looks like this

    executorService(PersistenceContextExecutorWrapper) { bean ->
        bean.destroyMethod = 'destroy'
        persistenceInterceptor = ref("persistenceInterceptor")
        executor = Executors.newCachedThreadPool()
    }

You can override it and inject your own special thread pool executor using Executors by overriding the bean in conf/spring/resources.groovy or the doWithSpring closure in your plugin.

    executorService(PersistenceContextExecutorWrapper) { bean ->
        bean.destroyMethod = 'destroy'
        persistenceInterceptor = ref("persistenceInterceptor")
    // this can be whatever from Executors (don't write your own and pre-optimize)
    executor = Executors.newCachedThreadPool(new YourSpecialThreadFactory()) 
    }

Usage

You can inject the executorService into any bean. It's a PersistenceContextExecutorWrapper that delegates any calls to a concrete ExecutorService implementation.Remember that a Closure is a Runnable so you can pass it to any of the methods that accept a runnable. A great example exists here on the groovy site

The plugin adds shortcut methods to any service/controller/domain artifacts.

  • runAsync closure - takes any closure and passes it through to the executorService.execute
  • callAsync closure - takes any closure that returns a value and passes it through to the executorService.submit . You will get a Future back that you can work with. This will not bind a session in java 1.5 and only works on 1.6 or later

NOTE ON TRANSACTIONS: keep in mind that this is spinning off a new thread and that any call will be outside of the transaction you are in. Use .withTransaction inside your closure, runnable or callable to make your process run in a transaction that is not calling a transactional service method (such as when using this in a controller).

Examples

in a service/domain/controller just pass a Closure or Runnable to runAsync

class someService {

	def myMethod(){
		..do some stuff
		runAsync {
			//this will be in its own trasaction 
			//since each of these service methods are Transactional
			calcAging() 
		}
		.. do some other stuff while aging is calced in background
	}

	def calcAging(){
		...do long process
	}
}

or inject the executorService

class someService {
	def executorService

	def myMethod(){
		....do stuff
		def future = executorService.submit({
			return calcAging() //you can of course leave out the "return" here
		} as Callable)
		.. do some other stuff while its processing
		//now block and wait with get()
		def aging = future.get()
		..do something
	}

	def calcAging(){
		...do long process
		return agingCalcObject
	}

}

or during a domain event

class Book {
	def myNotifyService
	
	String name
	
	def afterInsert(){
		runAsync {
			myNotifyService.informLibraryOfCongress(this)
		}
	}
}

the callAsync allows you to spin of a process and calls the underlying executorService submit

class someService {
	def myMethod(){
		....do stuff
		def future = callAsync {
			return calcAging() //you can of course leave out the "return" here
		}
		.. do some other stuff while its processing
		//now block and wait with get()
		def aging = future.get()
		..do something with the aging
	}

	def calcAging(){
		...do stuff
		return agingCalcObject
	}
}

TODOs

Release Notes

  • 0.2 - released 2016-08-17
    • First publicly announced version
  • 0.4 - released 2016-08-19
    • Fixed "submit returns void future"