diff --git a/spring-batch-integration/src/main/java/org/springframework/batch/integration/async/AsyncItemWriter.java b/spring-batch-integration/src/main/java/org/springframework/batch/integration/async/AsyncItemWriter.java index ec98484fa..743b75591 100644 --- a/spring-batch-integration/src/main/java/org/springframework/batch/integration/async/AsyncItemWriter.java +++ b/spring-batch-integration/src/main/java/org/springframework/batch/integration/async/AsyncItemWriter.java @@ -15,6 +15,14 @@ */ package org.springframework.batch.integration.async; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStream; import org.springframework.batch.item.ItemStreamException; @@ -23,12 +31,10 @@ import org.springframework.batch.item.ItemWriter; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Future; - public class AsyncItemWriter implements ItemStreamWriter>, InitializingBean { + private static final Log logger = LogFactory.getLog(AsyncItemWriter.class); + private ItemWriter delegate; public void afterPropertiesSet() throws Exception { @@ -45,7 +51,9 @@ public class AsyncItemWriter implements ItemStreamWriter>, Initiali /** * In the processing of the {@link java.util.concurrent.Future}s passed, nulls are not passed to the * delegate since they are considered filtered out by the {@link org.springframework.batch.integration.async.AsyncItemProcessor}'s - * delegated {@link org.springframework.batch.item.ItemProcessor}. + * delegated {@link org.springframework.batch.item.ItemProcessor}. If the unwrapping + * of the {@link Future} results in an {@link ExecutionException}, that will be + * unwrapped and the cause will be thrown. * * @param items {@link java.util.concurrent.Future}s to be upwrapped and passed to the delegate * @throws Exception @@ -53,12 +61,27 @@ public class AsyncItemWriter implements ItemStreamWriter>, Initiali public void write(List> items) throws Exception { List list = new ArrayList(); for (Future future : items) { - T item = future.get(); + try { + T item = future.get(); - if(item != null) { - list.add(future.get()); + if(item != null) { + list.add(future.get()); + } + } + catch (ExecutionException e) { + Throwable cause = e.getCause(); + + if(cause != null && cause instanceof Exception) { + logger.debug("An exception was thrown while processing an item", e); + + throw (Exception) cause; + } + else { + throw e; + } } } + delegate.write(list); } diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/integration/async/AsyncItemWriterTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/integration/async/AsyncItemWriterTests.java index e1b62392b..0af171cc8 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/integration/async/AsyncItemWriterTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/integration/async/AsyncItemWriterTests.java @@ -15,8 +15,18 @@ */ package org.springframework.batch.integration.async; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + import org.junit.Before; import org.junit.Test; + import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStreamException; import org.springframework.batch.item.ItemStreamWriter; @@ -24,11 +34,6 @@ import org.springframework.batch.item.ItemWriter; import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.core.task.TaskExecutor; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -108,6 +113,82 @@ public class AsyncItemWriterTests { assertTrue(writtenItems.contains("foo")); } + @Test + public void testException() throws Exception { + writer.setDelegate(new ListItemWriter(writtenItems)); + List> processedItems = new ArrayList>(); + + processedItems.add(new FutureTask(new Callable() { + @Override + public String call() throws Exception { + return "foo"; + } + })); + + processedItems.add(new FutureTask(new Callable() { + @Override + public String call() throws Exception { + throw new RuntimeException("This was expected"); + } + })); + + for (FutureTask processedItem : processedItems) { + taskExecutor.execute(processedItem); + } + + try { + writer.write(processedItems); + } + catch (Exception e) { + assertTrue(e instanceof RuntimeException); + assertEquals("This was expected", e.getMessage()); + } + } + + @Test + public void testExecutionException() { + ListItemWriter delegate = new ListItemWriter(writtenItems); + writer.setDelegate(delegate); + List> processedItems = new ArrayList>(); + + processedItems.add(new Future() { + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return false; + } + + @Override + public String get() throws InterruptedException, ExecutionException { + throw new InterruptedException("expected"); + } + + @Override + public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return null; + } + }); + + try { + writer.write(processedItems); + } + catch (Exception e) { + assertFalse(e instanceof ExecutionException); + } + + assertEquals(0, writtenItems.size()); + } + @Test public void testStreamDelegate() throws Exception { ListItemStreamWriter itemWriter = new ListItemStreamWriter(writtenItems);