Hi Arjan,
I did some changes recently in Weld interceptors and this usecase now
works smoothly. The code is not part of a release yet. See this test for
a simple implementation of an @Async interceptor (basically the same as
your initial attempt). Note that the chain is repeatable but at the same
time it is not reset after dispatch to a different thread so you no
longer need the ThreadLocal nor any other workaround.
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
_______________________________________________
cdi-dev mailing list
cdi-dev(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/cdi-dev
Note that for all code provided on this list, the provider licenses the code under the
Apache License, Version 2 (
http://www.apache.org/licenses/LICENSE-2.0.html). For all other
ideas provided on this list, the provider waives all patent and other intellectual
property rights inherent in such information.