Announcement Announcement Module
Collapse
No announcement yet.
Spring @Async with Future and Callable Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring @Async with Future and Callable

    Hi. I posted this over in SO, but haven't received any feedback and wanted to post this here too.

    I am trying to implement a generic class that executes a callable asynchronously, but I am not sure about the semantics.

    Code:
    @Component
    public class MyCallerImpl implements MyCaller {
    
        @Async
        @Override
        public <T> Future<T> runAsync(Callable<T> callable) throws Exception {
    
            return new AsyncResult<T>(callable.call());
        }
    }
    Basically, this component executes arbitrary actions from any callable asynchronously using the @Async annotation.

    I am unsure about the Exception in the throws clause of the method signature.

    A Junit test:

    Code:
    @ContextConfiguration("classpath:test-config.xml")
    @RunWith(SpringJUnit4ClassRunner.class)
    public class RunnerTest{
    
        @Resource(name="myCallerImpl")
        private MyCaller myCaller;
    
        @Test
        public void testException(){
    
            final Callable<String> callable = new Callable<String>(){
                @Override
                public String call() throws Exception{
                    throw new MyException("foobar");
                }
            };
    
            try
            {
                final Future<String> future = myCaller.runAsync(callable); // this can throw Exception due to Callable.call()
                future.get(); // this can throw InterruptedException and ExecutionException 
            }
            catch (final InterruptedException ie)
            {
                // do someting
            }
            catch (final ExecutionException ee)
            {
                // we want to check the cause
                final Throwable cause = ee.getCause();
                assertTrue(cause instanceof MyException);
            }
            catch (final Exception e)
            {
                // Not sure what to do here. 
                // Must be caught as it is declared to 
                // be thrown from the MyCaller.runAsync() method
                // but nothing will really ever get here 
                // since the method is @Async and any exception will be
                // wrapped by an ExecutionException and thrown during Future.get()
    
                fail("this is unexpected);
            }
    My question is what to do about the Exception declared in the throws clause of MyCallerImpl.runAsync() considering that any exception will be wrapped in an ExectionException?

    The only reason I have declared it is because of the way I am calling the callable. Originally I had the following in the async method:

    Code:
    FutureTask<T> futureTask = new FutureTask<T>(callable);
    futureTask.run();
    return futureTask;
    But when an exception is thrown from the callable in that instance, it gets wrapped twice in an ExecutionException, the first time when FutureTask.run() is called in which eventually FutureTask.Sync.innerRun() catches the exception and calls innnerSetException() and the second time when the AsyncExecutionIntercepter gets the result from the Future via Future.get(), which eventually again checks if there is an exception and throws a new ExecutionException wrapping the ExecutionException caught in innerRun().

    I also tried to do the following in the method:

    Code:
    FutureTask<T> futureTask = new FutureTask<T>(callable);
    return futureTask;
    I had figured that since the AsyncExecutionInterceptor calls Future.get(), that the callable would be called immediately, but that was not the case. It just hangs on FutureTask.acquireSharedInterruptibly() and never returns.

    Maybe I'm in over my head here. It works how I have it set-up with the callable now, but I rather not have the method signature declare a throws Exception if it is never going to be thrown.

    Any advice? Should I forget about this generic way of doing async calls with a callable?
Working...
X