[cdi-dev] Building EJB-like @Asynchronous via interceptor in CDI

arjan tijms arjan.tijms at gmail.com
Fri Jan 16 12:17:24 EST 2015


Hi,

I'm attempting to emulate EJB's @Asynchronous in CDI using interceptors.

Originally I had defined my interceptor as follows;

@Interceptor
@Asynchronous
@Priority(APPLICATION)
public class AsynchronousInterceptor implements Serializable {

    private static final long serialVersionUID = 1L;

    @Resource
    private ManagedExecutorService managedExecutorService;

    @AroundInvoke
    public Object submitAsync(InvocationContext ctx) throws Exception {
        return new FutureDelegator(managedExecutorService.submit( ()-> {
return ctx.proceed(); } ));
    }

}

With FutureDelegator as follows:

public class FutureDelegator implements Future<Object> {

    private Future<?> future;

    public FutureDelegator(Future<?> future) {
        this.future = future;
    }

    @Override
    public Object get() throws InterruptedException, ExecutionException {
        AsyncResult<?> asyncResult = (AsyncResult<?>) future.get();
        if (asyncResult == null) {
            return null;
        }

        return asyncResult.get();
    }

    @Override
    public Object get(long timeout, TimeUnit unit) throws
InterruptedException,    ExecutionException, TimeoutException {
        AsyncResult<?> asyncResult = (AsyncResult<?>) future.get(timeout,
unit);
        if (asyncResult == null) {
            return null;
        }

        return asyncResult.get();
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return future.cancel(mayInterruptIfRunning);
    }

    @Override
    public boolean isCancelled() {
        return future.isCancelled();
    }
    @Override
    public boolean isDone() {
        return future.isDone();
    }

}

This of course didn't quite work, as the InvocationContext will be reset
after the @AroundInvoke method returns, and an infinite intercept loop
results (on Weld).

I got it to work though on Weld by using a thread local check to break that
loop:

@Interceptor
@Asynchronous
@Priority(PLATFORM_BEFORE)
public class AsynchronousInterceptor implements Serializable {

    private static final long serialVersionUID = 1L;

    @Resource
    private ManagedExecutorService managedExecutorService;

    private static final ThreadLocal<Boolean> asyncInvocation = new
ThreadLocal<Boolean>();

    @AroundInvoke
    public synchronized Object submitAsync(InvocationContext ctx) throws
Exception {

        if (TRUE.equals(asyncInvocation.get())) {
            return ctx.proceed();
        }

        return new FutureDelegator(managedExecutorService.submit( ()-> {
            try {
                asyncInvocation.set(TRUE);
                return ctx.proceed();
            } finally {
                 asyncInvocation.remove();
            }
        }));
    }

}

But I've got a feeling this works just by chance and not because the
workaround is so clever.

What do you guys think, what would be the best way to support this with the
current CDI version? Or would CDI/Interceptors need something like
Servlet's async support, where the InvocationContext is put into async mode
whereafter it "simply" allows an other thread to continue processing on it?

Kind regards,
Arjan Tijms
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/cdi-dev/attachments/20150116/75436b38/attachment.html 


More information about the cdi-dev mailing list