Async and event-driven
Grails applications
Álvaro Sánchez-Mariscal
@alvaro_sanchez
About me
— Coming from Madrid
!
— Developer since 2001 (Java / Spring stack).
— Grails fanboy since v0.4.
— Working @ OCI since 2015: Groovy, Grails & Micronaut!
— Father since 2017!
"
@alvaro_sanchez
Introduction
@alvaro_sanchez
Grails Async Framework
— https://async.grails.org
— Introduced in Grails 2.3 with the Promise API. Spun-
off in Grails 3.3
— Supports different async libraries: GPars, RxJava and
Reactor.
— Application events, Spring events, GORM events.
— Async GORM.
@alvaro_sanchez
Grails Async Framework
— Server Sent Events.
— RxGORM.
— Async request/response processing.
— Servlet 3.0 async support.
@alvaro_sanchez
 Ge"ing started
— Add the dependency in build.gradle:
runtime "org.grails.plugins:async"
— Check the Grails plugin page for more information:
http://plugins.grails.org/plugin/grails/async
@alvaro_sanchez
Servlet 3.0 API
@alvaro_sanchez
 Servlet 3.0
— Spec'ed in 2009!
!
— Allows offloading of blocking operations to a different
thread.
— Implement grails.async.web.AsyncController to get
started.
— Call startAsync() to get the javax.servlet.AsyncContext
— When done, call complete() or dispatch()
@alvaro_sanchez
Demo
@alvaro_sanchez
 Advanced usage
ctx.addListener(new AsyncListener() {
void onStartAsync(AsyncEvent event) throws IOException { }
void onComplete(AsyncEvent event) throws IOException { }
void onTimeout(AsyncEvent event) throws IOException { }
void onError(AsyncEvent event) throws IOException { }
})
ctx.timeout = 5_000
@alvaro_sanchez
Promise API
@alvaro_sanchez
Grails Promise API
— Builds on top of java.util.concurrent.Future.
— grails.async.Promises helps you to create Promise<T>
instances.
— In controllers, better use grails.async.web.WebPromises
(Servlet 3.0 Async under the covers)
— PromiseFactory allows pluggable implementations.
@alvaro_sanchez
PromiseFactory API
— CachedThreadPoolPromiseFactory (default): unbound
thread pool.
— GparsPromiseFactory: if org:grails:grails-async-gpars
dependecy is on the classpath.
— RxPromiseFactory: if either org.grails:grails-async-
rxjava or org.grails:grails-async-rxjava2 are on the
classpath.
@alvaro_sanchez
PromiseFactory API
— SynchronousPromiseFactory: useful for unit testing.
import org.grails.async.factory.*
import grails.async.*
Promises.promiseFactory = new SynchronousPromiseFactory()
@alvaro_sanchez
Demo
@alvaro_sanchez
PromiseList
import grails.async.*
PromiseList<Integer> list = new PromiseList<>()
list << { 2 * 2 }
list << { 4 * 4 }
list << { 8 * 8 }
list.onComplete { List<Integer> results ->
assert [4,16,64] == results
}
@alvaro_sanchez
 PromiseMap
import static grails.async.Promises.*
PromiseMap promiseMap = tasks one: { 2 * 2 },
two: { 4 * 4},
three:{ 8 * 8 }
assert [one:4,two:16,three:64] == promiseMap.get()
@alvaro_sanchez
@DelegateAsync
import grails.async.*
class BookService {
List<Book> findBooks(String title) { ... }
}
class AsyncBookService {
@DelegateAsync BookService bookService
}
@alvaro_sanchez
@DelegateAsync
import grails.async.*
class BookService {
List<Book> findBooks(String title) { ... }
}
class AsyncBookService { //Equivalent
Promise<List<Book>> findBooks(String title) {
task {
bookService.findBooks(title)
}
}
}
@alvaro_sanchez
Using the service
@Autowired AsyncBookService asyncBookService
def findBooks(String title) {
asyncBookService.findBooks(title)
.onComplete { List<Book> results ->
render "Books = ${results}"
}
}
@alvaro_sanchez
Events
@alvaro_sanchez
Grails Events abstraction
— Add the dependency in build.gradle:
runtime "org.grails.plugins:events"
— By default Grails creates an EventBus based off of the
currently active PromiseFactory.
— Or you can use:
— org.grails:grails-events-gpars
— org.grails:grails-events-rxjava
— org.grails:grails-events-rxjava2
@alvaro_sanchez
Publishing events
— With the @Publisher annotation:
class SumService {
@Publisher
int sum(int a, int b) {
a + b
}
}
@alvaro_sanchez
Publishing events
— With the EventPublisher API:
class SumService implements EventPublisher {
int sum(int a, int b) {
int result = a + b
notify("sum", result)
return result
}
}
@alvaro_sanchez
Subscribing to events
— With the @Subscriber annotation:
class TotalService {
AtomicInteger total = new AtomicInteger(0)
@Subscriber
void onSum(int num) {
total.addAndGet(num)
}
}
@alvaro_sanchez
Subscribing to events
— With the EventBusAware API:
class TotalService implements EventBusAware {
AtomicInteger total = new AtomicInteger(0)
@PostConstruct
void init() {
eventBus.subscribe("sum") { int num ->
total.addAndGet(num)
}
}
}
@alvaro_sanchez
GORM events
— DatastoreInitializedEvent
— PostDeleteEvent
— PostInsertEvent
— PostLoadEvent
— PostUpdateEvent
@alvaro_sanchez
GORM events
— PreDeleteEvent
— PreInsertEvent
— PreLoadEvent
— PreUpdateEvent
— SaveOrUpdateEvent
— ValidationEvent
@alvaro_sanchez
Asynchronous subscription
— They cannot cancel or manipulate the persistence
operations.
@Subscriber
void beforeInsert(PreInsertEvent event) {
//do stuff
}
@alvaro_sanchez
Synchronous subscription
@Listener
void tagFunnyBooks(PreInsertEvent event) {
String title = event.getEntityAccess()
.getPropertyValue("title")
if(title?.contains("funny")) {
event.getEntityAccess()
.setProperty("title", "Humor - ${title}".toString())
}
}
@alvaro_sanchez
Spring events
— Disabled by default, for performance reasons.
— Enable them by setting grails.events.spring to true.
@Events(namespace="spring")
class MyService {
@Subscriber
void applicationStarted(ApplicationStartedEvent event) {
// fired when the application starts
}
@Subscriber
void servletRequestHandled(RequestHandledEvent event) {
// fired each time a request is handled
}
}
@alvaro_sanchez
Async GORM
@alvaro_sanchez
 Get started
— NOTE: drivers are still blocking. This just offloads it to
a different thread pool.
— Include compile "org.grails:grails-datastore-gorm-
async" in your build.
— Implement the AsyncEntity trait:
class Book implements AsyncEntity<Book> {
...
}
@alvaro_sanchez
The async namespace
Promise<Person> p1 = Person.async.get(1L)
Promise<Person> p2 = Person.async.get(2L)
List<Person> people = waitAll(p1, p2) //Blocks
Person.async.list().onComplete { List<Person> results ->
println "Got people = ${results}"
}
Promise<Person> person = Person.where {
lastName == "Simpson"
}.async.list()
@alvaro_sanchez
Async and the Hibernate session
— The Hibernate session is not concurrency safe.
— Objects returned from asynchronous queries will be
detached entities.
— This will fail:
Person p = Person.async.findByFirstName("Homer").get()
p.firstName = "Bart"
p.save()
@alvaro_sanchez
Async and the Hibernate session
— Entities need to be merged first with the session
bound to the calling thread
Person p = Person.async.findByFirstName("Homer").get()
p.merge()
p.firstName = "Bart"
p.save()
— Also, be aware that association lazy loading will not
work. Use eager queries / fetch joins / etc.
@alvaro_sanchez
Multiple async GORM calls
Promise<Person> promise = Person.async.task {
withTransaction {
Person person = findByFirstName("Homer")
person.firstName = "Bart"
person.save(flush:true)
}
}
Person updatedPerson = promise.get()
@alvaro_sanchez
Async models
import static grails.async.WebPromises.*
def index() {
tasks books: Book.async.list(),
totalBooks: Book.async.count(),
otherValue: {
// do hard work
}
}
@alvaro_sanchez
RxJava Support
@alvaro_sanchez
RxJava support
— Add org.grails.plugins:rxjava to your dependencies.
— You can then return rx.Observable's from controllers,
and Grails will:
1. Create a new asynchronous request
2. Spawn a new thread that subscribes to the
observable
3. When the observable emits a result, process the
result using the respond method.
@alvaro_sanchez
Demo
@alvaro_sanchez
Q & A
Álvaro Sánchez-Mariscal
@alvaro_sanchez

Asynchronous and event-driven Grails applications

  • 1.
    Async and event-driven Grailsapplications Álvaro Sánchez-Mariscal @alvaro_sanchez
  • 2.
    About me — Comingfrom Madrid ! — Developer since 2001 (Java / Spring stack). — Grails fanboy since v0.4. — Working @ OCI since 2015: Groovy, Grails & Micronaut! — Father since 2017! " @alvaro_sanchez
  • 3.
  • 4.
    Grails Async Framework —https://async.grails.org — Introduced in Grails 2.3 with the Promise API. Spun- off in Grails 3.3 — Supports different async libraries: GPars, RxJava and Reactor. — Application events, Spring events, GORM events. — Async GORM. @alvaro_sanchez
  • 5.
    Grails Async Framework —Server Sent Events. — RxGORM. — Async request/response processing. — Servlet 3.0 async support. @alvaro_sanchez
  • 6.
     Ge"ing started — Addthe dependency in build.gradle: runtime "org.grails.plugins:async" — Check the Grails plugin page for more information: http://plugins.grails.org/plugin/grails/async @alvaro_sanchez
  • 7.
  • 8.
     Servlet 3.0 — Spec'edin 2009! ! — Allows offloading of blocking operations to a different thread. — Implement grails.async.web.AsyncController to get started. — Call startAsync() to get the javax.servlet.AsyncContext — When done, call complete() or dispatch() @alvaro_sanchez
  • 9.
  • 10.
     Advanced usage ctx.addListener(new AsyncListener(){ void onStartAsync(AsyncEvent event) throws IOException { } void onComplete(AsyncEvent event) throws IOException { } void onTimeout(AsyncEvent event) throws IOException { } void onError(AsyncEvent event) throws IOException { } }) ctx.timeout = 5_000 @alvaro_sanchez
  • 11.
  • 12.
    Grails Promise API —Builds on top of java.util.concurrent.Future. — grails.async.Promises helps you to create Promise<T> instances. — In controllers, better use grails.async.web.WebPromises (Servlet 3.0 Async under the covers) — PromiseFactory allows pluggable implementations. @alvaro_sanchez
  • 13.
    PromiseFactory API — CachedThreadPoolPromiseFactory(default): unbound thread pool. — GparsPromiseFactory: if org:grails:grails-async-gpars dependecy is on the classpath. — RxPromiseFactory: if either org.grails:grails-async- rxjava or org.grails:grails-async-rxjava2 are on the classpath. @alvaro_sanchez
  • 14.
    PromiseFactory API — SynchronousPromiseFactory:useful for unit testing. import org.grails.async.factory.* import grails.async.* Promises.promiseFactory = new SynchronousPromiseFactory() @alvaro_sanchez
  • 15.
  • 16.
    PromiseList import grails.async.* PromiseList<Integer> list= new PromiseList<>() list << { 2 * 2 } list << { 4 * 4 } list << { 8 * 8 } list.onComplete { List<Integer> results -> assert [4,16,64] == results } @alvaro_sanchez
  • 17.
     PromiseMap import static grails.async.Promises.* PromiseMappromiseMap = tasks one: { 2 * 2 }, two: { 4 * 4}, three:{ 8 * 8 } assert [one:4,two:16,three:64] == promiseMap.get() @alvaro_sanchez
  • 18.
    @DelegateAsync import grails.async.* class BookService{ List<Book> findBooks(String title) { ... } } class AsyncBookService { @DelegateAsync BookService bookService } @alvaro_sanchez
  • 19.
    @DelegateAsync import grails.async.* class BookService{ List<Book> findBooks(String title) { ... } } class AsyncBookService { //Equivalent Promise<List<Book>> findBooks(String title) { task { bookService.findBooks(title) } } } @alvaro_sanchez
  • 20.
    Using the service @AutowiredAsyncBookService asyncBookService def findBooks(String title) { asyncBookService.findBooks(title) .onComplete { List<Book> results -> render "Books = ${results}" } } @alvaro_sanchez
  • 21.
  • 22.
    Grails Events abstraction —Add the dependency in build.gradle: runtime "org.grails.plugins:events" — By default Grails creates an EventBus based off of the currently active PromiseFactory. — Or you can use: — org.grails:grails-events-gpars — org.grails:grails-events-rxjava — org.grails:grails-events-rxjava2 @alvaro_sanchez
  • 23.
    Publishing events — Withthe @Publisher annotation: class SumService { @Publisher int sum(int a, int b) { a + b } } @alvaro_sanchez
  • 24.
    Publishing events — Withthe EventPublisher API: class SumService implements EventPublisher { int sum(int a, int b) { int result = a + b notify("sum", result) return result } } @alvaro_sanchez
  • 25.
    Subscribing to events —With the @Subscriber annotation: class TotalService { AtomicInteger total = new AtomicInteger(0) @Subscriber void onSum(int num) { total.addAndGet(num) } } @alvaro_sanchez
  • 26.
    Subscribing to events —With the EventBusAware API: class TotalService implements EventBusAware { AtomicInteger total = new AtomicInteger(0) @PostConstruct void init() { eventBus.subscribe("sum") { int num -> total.addAndGet(num) } } } @alvaro_sanchez
  • 27.
    GORM events — DatastoreInitializedEvent —PostDeleteEvent — PostInsertEvent — PostLoadEvent — PostUpdateEvent @alvaro_sanchez
  • 28.
    GORM events — PreDeleteEvent —PreInsertEvent — PreLoadEvent — PreUpdateEvent — SaveOrUpdateEvent — ValidationEvent @alvaro_sanchez
  • 29.
    Asynchronous subscription — Theycannot cancel or manipulate the persistence operations. @Subscriber void beforeInsert(PreInsertEvent event) { //do stuff } @alvaro_sanchez
  • 30.
    Synchronous subscription @Listener void tagFunnyBooks(PreInsertEventevent) { String title = event.getEntityAccess() .getPropertyValue("title") if(title?.contains("funny")) { event.getEntityAccess() .setProperty("title", "Humor - ${title}".toString()) } } @alvaro_sanchez
  • 31.
    Spring events — Disabledby default, for performance reasons. — Enable them by setting grails.events.spring to true. @Events(namespace="spring") class MyService { @Subscriber void applicationStarted(ApplicationStartedEvent event) { // fired when the application starts } @Subscriber void servletRequestHandled(RequestHandledEvent event) { // fired each time a request is handled } } @alvaro_sanchez
  • 32.
  • 33.
     Get started — NOTE:drivers are still blocking. This just offloads it to a different thread pool. — Include compile "org.grails:grails-datastore-gorm- async" in your build. — Implement the AsyncEntity trait: class Book implements AsyncEntity<Book> { ... } @alvaro_sanchez
  • 34.
    The async namespace Promise<Person>p1 = Person.async.get(1L) Promise<Person> p2 = Person.async.get(2L) List<Person> people = waitAll(p1, p2) //Blocks Person.async.list().onComplete { List<Person> results -> println "Got people = ${results}" } Promise<Person> person = Person.where { lastName == "Simpson" }.async.list() @alvaro_sanchez
  • 35.
    Async and theHibernate session — The Hibernate session is not concurrency safe. — Objects returned from asynchronous queries will be detached entities. — This will fail: Person p = Person.async.findByFirstName("Homer").get() p.firstName = "Bart" p.save() @alvaro_sanchez
  • 36.
    Async and theHibernate session — Entities need to be merged first with the session bound to the calling thread Person p = Person.async.findByFirstName("Homer").get() p.merge() p.firstName = "Bart" p.save() — Also, be aware that association lazy loading will not work. Use eager queries / fetch joins / etc. @alvaro_sanchez
  • 37.
    Multiple async GORMcalls Promise<Person> promise = Person.async.task { withTransaction { Person person = findByFirstName("Homer") person.firstName = "Bart" person.save(flush:true) } } Person updatedPerson = promise.get() @alvaro_sanchez
  • 38.
    Async models import staticgrails.async.WebPromises.* def index() { tasks books: Book.async.list(), totalBooks: Book.async.count(), otherValue: { // do hard work } } @alvaro_sanchez
  • 39.
  • 40.
    RxJava support — Addorg.grails.plugins:rxjava to your dependencies. — You can then return rx.Observable's from controllers, and Grails will: 1. Create a new asynchronous request 2. Spawn a new thread that subscribes to the observable 3. When the observable emits a result, process the result using the respond method. @alvaro_sanchez
  • 41.
  • 42.
    Q & A ÁlvaroSánchez-Mariscal @alvaro_sanchez