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