2015-09-07

Groovy, Callable and ExecutorService

Suppose you want submit job to ExecutorService.

The Baroque version

You could create a class that implements Callable:
class MyJob implements Callable<Integer>{
    @Override
    Integer call() throws Exception {
        return 42
    }
}

and give it to the executor service:

def 'submit callable as MyJob object'() {
    expect:
        executorService.submit(new MyJob()).get() == 42
}

The response is, as expected, 42.

Map as Callable version

You want to use this job only in one place so why not inline this class:
def 'submit callable as map'() {
    expect:
        executorService.submit([call: { 42 }] as Callable).get() == 42
}

The response is again 42.

Groovy closure version

Why not use closure instead of map?
def 'submit callable as closure'(){
    expect:
        executorService.submit { 42 }.get() == 42
}

The response is ... null.

Condition not satisfied:
executorService.submit { 42 }.get() == 42
|               |             |     |
|               |             null  false
|               java.util.concurrent.FutureTask@21de60b4
java.util.concurrent.Executors$FinalizableDelegatedExecutorService@1700915

Why? It is because Groovy treats this closure as Runnable, not Callable and Future#get returns null when task is complete.

Groovy closure version with cast

We have to cast our closure before submiting to executor service:
def 'submit callable as closure with cast'() {
    when:
        int result = executorService.submit({ return 42 } as Callable<Integer>).get()
    then:
        result == 42
}

The response is, as expected, again 42.

What interesting, the same test with inlined result variable fails... Strange... It could be Spock framework error.

Source code is available here.

No comments:

Post a Comment