diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/domain/StepListener.java b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/StepListener.java index b0af45137..9c29a40d6 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/domain/StepListener.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/StepListener.java @@ -25,15 +25,25 @@ import org.springframework.batch.repeat.ExitStatus; public interface StepListener { /** - * Initialise the state of the listener with the job parameters from the - * current scope. - * @param jobParameters + * Initialise the state of the listener with the {@link StepExecution} from + * the current scope. + * @param stepExecution */ - void open(JobParameters jobParameters); + void open(StepExecution stepExecution); + + /** + * The value returned will be combined with the normal exit status using + * {@link ExitStatus#and(ExitStatus)}. + * + * @param e an exception thrown by the step execution + * @return an exit status to be combined with the normal one, or null + */ + ExitStatus onError(Throwable e); /** * Give a listener a chance to modify the exit status from a step. The value - * returned will be combined with the normal exit status. + * returned will be combined with the normal exit status using + * {@link ExitStatus#and(ExitStatus)}. * * @return an {@link ExitStatus} to combine with the normal value. Return * null to leave the old value unchanged. diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/CompositeStepListener.java b/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/CompositeStepListener.java index cf71b2200..10d2923f7 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/CompositeStepListener.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/CompositeStepListener.java @@ -20,7 +20,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; -import org.springframework.batch.core.domain.JobParameters; +import org.springframework.batch.core.domain.StepExecution; import org.springframework.batch.core.domain.StepListener; import org.springframework.batch.repeat.ExitStatus; @@ -79,11 +79,23 @@ public class CompositeStepListener implements StepListener { /* (non-Javadoc) * @see org.springframework.batch.core.domain.StepListener#open(org.springframework.batch.core.domain.JobParameters) */ - public void open(JobParameters jobParameters) { + public void open(StepExecution stepExecution) { for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { StepListener listener = (StepListener) iterator.next(); - listener.open(jobParameters); + listener.open(stepExecution); } } + /* (non-Javadoc) + * @see org.springframework.batch.core.domain.StepListener#onError(java.lang.Throwable) + */ + public ExitStatus onError(Throwable e) { + ExitStatus status = null; + for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { + StepListener listener = (StepListener) iterator.next(); + ExitStatus close = listener.onError(e); + status = status!=null ? status.and(close): close; + } + return status; + } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/StepListenerSupport.java b/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/StepListenerSupport.java index 4b5474ea6..bea731b51 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/StepListenerSupport.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/interceptor/StepListenerSupport.java @@ -15,7 +15,7 @@ */ package org.springframework.batch.core.interceptor; -import org.springframework.batch.core.domain.JobParameters; +import org.springframework.batch.core.domain.StepExecution; import org.springframework.batch.core.domain.StepListener; import org.springframework.batch.repeat.ExitStatus; @@ -29,13 +29,19 @@ public class StepListenerSupport implements StepListener { * @see org.springframework.batch.core.domain.StepListener#close() */ public ExitStatus close() { - return ExitStatus.CONTINUABLE; + return null; } /* (non-Javadoc) * @see org.springframework.batch.core.domain.StepListener#open(org.springframework.batch.item.ExecutionContext) */ - public void open(JobParameters jobParameters) { + public void open(StepExecution stepExecution) { } + /* (non-Javadoc) + * @see org.springframework.batch.core.domain.StepListener#onError(java.lang.Throwable) + */ + public ExitStatus onError(Throwable e) { + return null; + } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/interceptor/CompositeStepListenerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/interceptor/CompositeStepListenerTests.java index 156e60824..612c44a39 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/interceptor/CompositeStepListenerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/interceptor/CompositeStepListenerTests.java @@ -20,8 +20,9 @@ import java.util.List; import junit.framework.TestCase; -import org.springframework.batch.core.domain.JobParameters; +import org.springframework.batch.core.domain.StepExecution; import org.springframework.batch.core.domain.StepListener; +import org.springframework.batch.core.domain.StepSupport; import org.springframework.batch.repeat.ExitStatus; /** @@ -71,15 +72,30 @@ public class CompositeStepListenerTests extends TestCase { /** * Test method for - * {@link org.springframework.batch.core.interceptor.CompositeStepListener#open(JobParameters)}. + * {@link org.springframework.batch.core.interceptor.CompositeStepListener#open(StepExecution)}. */ public void testOpen() { listener.setListener(new StepListenerSupport() { - public void open(JobParameters jobParameters) { + public void open(StepExecution stepExecution) { list.add("foo"); } }); - listener.open(new JobParameters()); + listener.open(new StepExecution(new StepSupport("foo"), null)); + assertEquals(1, list.size()); + } + + /** + * Test method for + * {@link org.springframework.batch.core.interceptor.CompositeStepListener#open(StepExecution)}. + */ + public void testOnError() { + listener.setListener(new StepListenerSupport() { + public ExitStatus onError(Throwable e) { + list.add("foo"); + return null; + } + }); + listener.onError(new RuntimeException()); assertEquals(1, list.size()); } diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java index 5339b1ee7..02610125f 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java @@ -83,7 +83,8 @@ public class TaskletStep implements Step, InitializingBean, BeanNameAware { this.name = name; } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.batch.core.domain.Step#getStartLimit() */ public int getStartLimit() { @@ -99,7 +100,8 @@ public class TaskletStep implements Step, InitializingBean, BeanNameAware { this.startLimit = startLimit; } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.batch.core.domain.Step#isAllowStartIfComplete() */ public boolean isAllowStartIfComplete() { @@ -180,8 +182,14 @@ public class TaskletStep implements Step, InitializingBean, BeanNameAware { Exception fatalException = null; try { - listener.open(stepExecution.getJobParameters()); - exitStatus = tasklet.execute(); + listener.open(stepExecution); + exitStatus = tasklet.execute(); + try { + exitStatus = exitStatus.and(listener.close()); + } + catch (Exception e) { + logger.error("Encountered an error on listener close.", e); + } try { jobRepository.saveOrUpdateExecutionContext(stepExecution); @@ -193,23 +201,21 @@ public class TaskletStep implements Step, InitializingBean, BeanNameAware { } } - catch (RuntimeException e) { - logger.error("Encountered an error running the tasklet"); - updateStatus(stepExecution, BatchStatus.FAILED); - throw e; - } catch (Exception e) { logger.error("Encountered an error running the tasklet"); updateStatus(stepExecution, BatchStatus.FAILED); + try { + exitStatus = exitStatus.and(listener.onError(e)); + } + catch (Exception ex) { + logger.error("Encountered an error on listener close.", ex); + } + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } throw new BatchCriticalException(e); } finally { - try { - exitStatus = exitStatus.and(listener.close()); - } - catch (Exception e) { - logger.error("Encountered an error on listener close.", e); - } stepExecution.setExitStatus(exitStatus); stepExecution.setEndTime(new Date()); try { diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java index 08e158889..3111aa300 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java @@ -105,7 +105,7 @@ public class TaskletStepTests extends TestCase { public void testSuccessfulExecutionWithListener() throws Exception { TaskletStep step = new TaskletStep(new StubTasklet(false, false), new JobRepositorySupport()); step.setListener(new StepListenerSupport() { - public void open(JobParameters context) { + public void open(StepExecution context) { list.add("open"); } public ExitStatus close() {