BATCH-1714: Added ChunkListener#afterFailedChunk(ChunkContext context)
This commit is contained in:
@@ -94,3 +94,4 @@ payloads
|
||||
unflushed
|
||||
memento
|
||||
michael
|
||||
minella
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2006-2008 the original author or authors.
|
||||
* Copyright 2006-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,23 +15,42 @@
|
||||
*/
|
||||
package org.springframework.batch.core;
|
||||
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
|
||||
/**
|
||||
* Listener interface for the lifecycle of a chunk. A chunk
|
||||
* can be through of as a collection of items that will be
|
||||
* can be through of as a collection of items that will be
|
||||
* committed together.
|
||||
*
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Michael Minella
|
||||
*
|
||||
*/
|
||||
public interface ChunkListener extends StepListener {
|
||||
|
||||
static final String ROLLBACK_EXCEPTION_KEY = "sb_rollback_exception";
|
||||
|
||||
/**
|
||||
* Callback before the chunk is executed, but inside the transaction.
|
||||
*/
|
||||
void beforeChunk();
|
||||
|
||||
|
||||
/**
|
||||
* Callback after the chunk is executed, outside the transaction.
|
||||
*/
|
||||
void afterChunk();
|
||||
|
||||
/**
|
||||
* Callback after a chunk has been marked for rollback. It is invoked
|
||||
* after transaction rollback. While the rollback will have occurred,
|
||||
* transactional resources might still be active and accessible. Due to
|
||||
* this, data access code within this callback will still "participate" in
|
||||
* the original transaction unless it declares that it run in its own
|
||||
* transaction. Hence: <em> Use PROPAGATION_REQUIRES_NEW for any
|
||||
* transactional operation that is called from here.</em>
|
||||
*
|
||||
* @param context the chunk context containing the exception that caused
|
||||
* the underlying rollback.
|
||||
*/
|
||||
void afterChunkError(ChunkContext context);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
|
||||
/**
|
||||
* Marks a method to be called after a has failed and been
|
||||
* marked for rollback.<br>
|
||||
* <br/>
|
||||
* Expected signature: void afterFailedChunk(ChunkContext context)
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @since 2.2
|
||||
* @see ChunkListener#afterChunkError(ChunkContext)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface AfterChunkError {
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.batch.core.listener;
|
||||
|
||||
import org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
|
||||
/**
|
||||
* Basic support implementation of {@link ChunkListener}
|
||||
@@ -39,4 +40,12 @@ public class ChunkListenerSupport implements ChunkListener {
|
||||
public void beforeChunk() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.domain.ChunkListener#afterChunkError(ChunkContext)
|
||||
*/
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
@@ -73,4 +74,12 @@ public class CompositeChunkListener implements ChunkListener {
|
||||
listener.beforeChunk();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
for (Iterator<ChunkListener> iterator = listeners.iterator(); iterator.hasNext();) {
|
||||
ChunkListener listener = iterator.next();
|
||||
listener.afterChunkError(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
|
||||
/**
|
||||
@@ -317,4 +318,13 @@ ItemProcessListener<T, S>, ItemWriteListener<S>, SkipListener<T, S> {
|
||||
skipListener.onSkipInProcess(item, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
try {
|
||||
chunkListener.afterChunkError(context);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
throw new StepListenerFailedException("Error in afterFailedChunk.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.annotation.AfterChunk;
|
||||
import org.springframework.batch.core.annotation.AfterChunkError;
|
||||
import org.springframework.batch.core.annotation.AfterProcess;
|
||||
import org.springframework.batch.core.annotation.AfterRead;
|
||||
import org.springframework.batch.core.annotation.AfterStep;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.batch.core.annotation.OnSkipInProcess;
|
||||
import org.springframework.batch.core.annotation.OnSkipInRead;
|
||||
import org.springframework.batch.core.annotation.OnSkipInWrite;
|
||||
import org.springframework.batch.core.annotation.OnWriteError;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
|
||||
/**
|
||||
* Enumeration for {@link StepListener} meta data, which ties together the names
|
||||
@@ -59,6 +61,7 @@ public enum StepListenerMetaData implements ListenerMetaData {
|
||||
AFTER_STEP("afterStep", "after-step-method", AfterStep.class, StepExecutionListener.class, StepExecution.class),
|
||||
BEFORE_CHUNK("beforeChunk", "before-chunk-method", BeforeChunk.class, ChunkListener.class),
|
||||
AFTER_CHUNK("afterChunk", "after-chunk-method", AfterChunk.class, ChunkListener.class),
|
||||
AFTER_CHUNK_ERROR("afterChunkError", "after-chunk-error-method", AfterChunkError.class, ChunkListener.class, ChunkContext.class),
|
||||
BEFORE_READ("beforeRead", "before-read-method", BeforeRead.class, ItemReadListener.class),
|
||||
AFTER_READ("afterRead", "after-read-method", AfterRead.class, ItemReadListener.class, Object.class),
|
||||
ON_READ_ERROR("onReadError", "on-read-error-method", OnReadError.class, ItemReadListener.class, Exception.class),
|
||||
@@ -138,7 +141,7 @@ public enum StepListenerMetaData implements ListenerMetaData {
|
||||
}
|
||||
|
||||
public static ListenerMetaData[] taskletListenerMetaData() {
|
||||
return new ListenerMetaData[] {BEFORE_CHUNK, AFTER_CHUNK};
|
||||
return new ListenerMetaData[] {BEFORE_CHUNK, AFTER_CHUNK, AFTER_CHUNK_ERROR};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
|
||||
/**
|
||||
* Basic no-op implementations of all {@link StepListener} interfaces.
|
||||
@@ -149,4 +150,8 @@ ItemReadListener<T>, ItemProcessListener<T,S>, ItemWriteListener<S>, SkipListene
|
||||
public void onSkipInWrite(S item, Throwable t) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.batch.core.JobInterruptedException;
|
||||
import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.listener.StepListenerFactoryBean;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.FatalStepExecutionException;
|
||||
import org.springframework.batch.core.step.item.BatchRetryTemplate;
|
||||
import org.springframework.batch.core.step.item.ChunkMonitor;
|
||||
@@ -667,6 +668,14 @@ public class FaultTolerantStepBuilder<I, O> extends SimpleStepBuilder<I, O> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
try {
|
||||
chunkListener.afterChunkError(context);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new FatalStepExecutionException("ChunkListener threw exception, rethrowing as fatal", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ import org.springframework.util.Assert;
|
||||
* @author Lucas Ward
|
||||
* @author Ben Hale
|
||||
* @author Robert Kasanicky
|
||||
* @author Michael Minella
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TaskletStep extends AbstractStep {
|
||||
@@ -351,6 +352,7 @@ public class TaskletStep extends AbstractStep {
|
||||
rollback(stepExecution);
|
||||
}
|
||||
}
|
||||
chunkListener.afterChunkError(chunkContext);
|
||||
}
|
||||
if (status == TransactionSynchronization.STATUS_UNKNOWN) {
|
||||
logger.error("Rolling back with transaction in unknown state");
|
||||
@@ -396,10 +398,10 @@ public class TaskletStep extends AbstractStep {
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (transactionAttribute.rollbackOn(e)) {
|
||||
chunkContext.setAttribute(ChunkListener.ROLLBACK_EXCEPTION_KEY, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
finally {
|
||||
|
||||
|
||||
@@ -1069,6 +1069,7 @@ ref" is not required, and only needs to be specified explicitly
|
||||
<xsd:attribute name="after-step-method" type="xsd:string" />
|
||||
<xsd:attribute name="before-chunk-method" type="xsd:string" />
|
||||
<xsd:attribute name="after-chunk-method" type="xsd:string" />
|
||||
<xsd:attribute name="after-chunk-error-method" type="xsd:string" />
|
||||
<xsd:attribute name="before-read-method" type="xsd:string" />
|
||||
<xsd:attribute name="after-read-method" type="xsd:string" />
|
||||
<xsd:attribute name="on-read-error-method" type="xsd:string" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2006-2008 the original author or authors.
|
||||
* Copyright 2006-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,44 +15,56 @@
|
||||
*/
|
||||
package org.springframework.batch.core.listener;
|
||||
|
||||
import static org.easymock.EasyMock.*;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.listener.CompositeChunkListener;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
* @author Michael Minella
|
||||
*
|
||||
*/
|
||||
public class CompositeChunkListenerTests {
|
||||
|
||||
ChunkListener listener;
|
||||
CompositeChunkListener compositeListener;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
listener = createMock(ChunkListener.class);
|
||||
compositeListener = new CompositeChunkListener();
|
||||
compositeListener.register(listener);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBeforeChunk(){
|
||||
|
||||
|
||||
listener.beforeChunk();
|
||||
replay(listener);
|
||||
compositeListener.beforeChunk();
|
||||
verify(listener);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAfterChunk(){
|
||||
|
||||
|
||||
listener.afterChunk();
|
||||
replay(listener);
|
||||
compositeListener.afterChunk();
|
||||
verify(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAfterChunkFailed(){
|
||||
ChunkContext context = new ChunkContext(null);
|
||||
listener.afterChunkError(context);
|
||||
replay(listener);
|
||||
compositeListener.afterChunkError(context);
|
||||
verify(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.batch.core.listener;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_CHUNK;
|
||||
import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_STEP;
|
||||
import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_WRITE;
|
||||
|
||||
@@ -44,6 +43,8 @@ import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.annotation.AfterChunk;
|
||||
import org.springframework.batch.core.annotation.AfterChunkError;
|
||||
import org.springframework.batch.core.annotation.AfterProcess;
|
||||
import org.springframework.batch.core.annotation.AfterRead;
|
||||
import org.springframework.batch.core.annotation.AfterStep;
|
||||
@@ -57,6 +58,7 @@ import org.springframework.batch.core.annotation.OnProcessError;
|
||||
import org.springframework.batch.core.annotation.OnReadError;
|
||||
import org.springframework.batch.core.annotation.OnWriteError;
|
||||
import org.springframework.batch.core.configuration.xml.AbstractTestComponent;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -83,10 +85,10 @@ public class StepListenerFactoryBeanTests {
|
||||
public void testStepAndChunk() throws Exception {
|
||||
TestListener testListener = new TestListener();
|
||||
factoryBean.setDelegate(testListener);
|
||||
Map<String, String> metaDataMap = new HashMap<String, String>();
|
||||
metaDataMap.put(AFTER_STEP.getPropertyName(), "destroy");
|
||||
metaDataMap.put(AFTER_CHUNK.getPropertyName(), "afterChunk");
|
||||
factoryBean.setMetaDataMap(metaDataMap);
|
||||
// Map<String, String> metaDataMap = new HashMap<String, String>();
|
||||
// metaDataMap.put(AFTER_STEP.getPropertyName(), "destroy");
|
||||
// metaDataMap.put(AFTER_CHUNK.getPropertyName(), "afterChunk");
|
||||
// factoryBean.setMetaDataMap(metaDataMap);
|
||||
String readItem = "item";
|
||||
Integer writeItem = 2;
|
||||
List<Integer> writeItems = Arrays.asList(writeItem);
|
||||
@@ -95,6 +97,7 @@ public class StepListenerFactoryBeanTests {
|
||||
((StepExecutionListener) listener).afterStep(stepExecution);
|
||||
((ChunkListener) listener).beforeChunk();
|
||||
((ChunkListener) listener).afterChunk();
|
||||
((ChunkListener) listener).afterChunkError(new ChunkContext(null));
|
||||
((ItemReadListener<String>) listener).beforeRead();
|
||||
((ItemReadListener<String>) listener).afterRead(readItem);
|
||||
((ItemReadListener<String>) listener).onReadError(new Exception());
|
||||
@@ -110,6 +113,7 @@ public class StepListenerFactoryBeanTests {
|
||||
assertTrue(testListener.beforeStepCalled);
|
||||
assertTrue(testListener.beforeChunkCalled);
|
||||
assertTrue(testListener.afterChunkCalled);
|
||||
assertTrue(testListener.afterChunkErrorCalled);
|
||||
assertTrue(testListener.beforeReadCalled);
|
||||
assertTrue(testListener.afterReadCalled);
|
||||
assertTrue(testListener.onReadErrorCalled);
|
||||
@@ -132,7 +136,6 @@ public class StepListenerFactoryBeanTests {
|
||||
ThreeStepExecutionListener delegate = new ThreeStepExecutionListener();
|
||||
factoryBean.setDelegate(delegate);
|
||||
Map<String, String> metaDataMap = new HashMap<String, String>();
|
||||
;
|
||||
metaDataMap.put(AFTER_STEP.getPropertyName(), "destroy");
|
||||
factoryBean.setMetaDataMap(metaDataMap);
|
||||
StepListener listener = (StepListener) factoryBean.getObject();
|
||||
@@ -428,6 +431,8 @@ public class StepListenerFactoryBeanTests {
|
||||
|
||||
boolean afterChunkCalled = false;
|
||||
|
||||
boolean afterChunkErrorCalled = false;
|
||||
|
||||
boolean beforeReadCalled = false;
|
||||
|
||||
boolean afterReadCalled = false;
|
||||
@@ -457,6 +462,7 @@ public class StepListenerFactoryBeanTests {
|
||||
beforeStepCalled = true;
|
||||
}
|
||||
|
||||
@AfterStep
|
||||
public void destroy() {
|
||||
afterStepCalled = true;
|
||||
}
|
||||
@@ -466,10 +472,16 @@ public class StepListenerFactoryBeanTests {
|
||||
beforeChunkCalled = true;
|
||||
}
|
||||
|
||||
@AfterChunk
|
||||
public void afterChunk() {
|
||||
afterChunkCalled = true;
|
||||
}
|
||||
|
||||
@AfterChunkError
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
afterChunkErrorCalled = true;
|
||||
}
|
||||
|
||||
@BeforeRead
|
||||
public void beforeReadMethod() {
|
||||
beforeReadCalled = true;
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.FatalStepExecutionException;
|
||||
import org.springframework.batch.core.step.factory.FaultTolerantStepFactoryBean;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
@@ -110,7 +111,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
@Test
|
||||
public void testBeforeChunkListenerException() throws Exception{
|
||||
factory.setListeners(new StepListener []{new ExceptionThrowingChunkListener(true)});
|
||||
factory.setListeners(new StepListener []{new ExceptionThrowingChunkListener(1)});
|
||||
Step step = (Step) factory.getObject();
|
||||
step.execute(stepExecution);
|
||||
assertEquals(FAILED, stepExecution.getStatus());
|
||||
@@ -123,7 +124,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
@Test
|
||||
public void testAfterChunkListenerException() throws Exception{
|
||||
factory.setListeners(new StepListener []{new ExceptionThrowingChunkListener(false)});
|
||||
factory.setListeners(new StepListener []{new ExceptionThrowingChunkListener(2)});
|
||||
Step step = (Step) factory.getObject();
|
||||
step.execute(stepExecution);
|
||||
assertEquals(FAILED, stepExecution.getStatus());
|
||||
@@ -590,24 +591,31 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
class ExceptionThrowingChunkListener implements ChunkListener{
|
||||
|
||||
private boolean throwBefore = true;
|
||||
private int phase = -1;
|
||||
|
||||
public ExceptionThrowingChunkListener(boolean throwBefore) {
|
||||
this.throwBefore = throwBefore;
|
||||
public ExceptionThrowingChunkListener(int throwPhase) {
|
||||
this.phase = throwPhase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeChunk() {
|
||||
if(throwBefore){
|
||||
if(phase == 1){
|
||||
throw new IllegalArgumentException("Planned exception");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunk() {
|
||||
throw new IllegalArgumentException("Planned exception");
|
||||
if(phase == 2) {
|
||||
throw new IllegalArgumentException("Planned exception");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
if(phase == 3) {
|
||||
throw new IllegalArgumentException("Planned exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.listener.SkipListenerSupport;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.factory.FaultTolerantStepFactoryBean;
|
||||
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
|
||||
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
|
||||
@@ -806,6 +807,9 @@ public class FaultTolerantStepFactoryBeanTests {
|
||||
listenerCalls.add(5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
}
|
||||
}
|
||||
|
||||
factory.setItemWriter(new TestItemListenerWriter());
|
||||
@@ -1109,7 +1113,6 @@ public class FaultTolerantStepFactoryBeanTests {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class NonExistentException extends Exception {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.support.SimpleJobRepository;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
import org.springframework.batch.core.step.factory.SimpleStepFactoryBean;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
@@ -217,7 +218,7 @@ public class SimpleStepFactoryBeanTests {
|
||||
|
||||
@Test
|
||||
public void testChunkListeners() throws Exception {
|
||||
String[] items = new String[] { "1", "2", "3", "4", "5", "6", "7" };
|
||||
String[] items = new String[] { "1", "2", "3", "4", "5", "6", "7", "error" };
|
||||
int commitInterval = 3;
|
||||
|
||||
SimpleStepFactoryBean<String, String> factory = getStepFactory(items);
|
||||
@@ -227,6 +228,10 @@ public class SimpleStepFactoryBeanTests {
|
||||
|
||||
@Override
|
||||
public void beforeWrite(List<? extends Object> items) {
|
||||
if(items.contains("error")) {
|
||||
throw new RuntimeException("rollback the last chunk");
|
||||
}
|
||||
|
||||
trail = trail + "2";
|
||||
}
|
||||
|
||||
@@ -241,6 +246,8 @@ public class SimpleStepFactoryBeanTests {
|
||||
|
||||
int afterCount = 0;
|
||||
|
||||
int failedCount = 0;
|
||||
|
||||
private AssertingWriteListener writeListener;
|
||||
|
||||
public CountingChunkListener(AssertingWriteListener writeListener) {
|
||||
@@ -259,6 +266,12 @@ public class SimpleStepFactoryBeanTests {
|
||||
writeListener.trail = writeListener.trail + "1";
|
||||
beforeCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
writeListener.trail = writeListener.trail + "5";
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
AssertingWriteListener writeListener = new AssertingWriteListener();
|
||||
CountingChunkListener chunkListener = new CountingChunkListener(writeListener);
|
||||
@@ -272,13 +285,15 @@ public class SimpleStepFactoryBeanTests {
|
||||
JobExecution jobExecution = repository.createJobExecution(job.getName(), new JobParameters());
|
||||
job.execute(jobExecution);
|
||||
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
|
||||
assertNull(reader.read());
|
||||
assertEquals(items.length, written.size());
|
||||
assertEquals(6, written.size());
|
||||
|
||||
int expectedListenerCallCount = (items.length / commitInterval) + 1;
|
||||
assertEquals(expectedListenerCallCount, chunkListener.afterCount);
|
||||
assertEquals(expectedListenerCallCount - 1, chunkListener.afterCount);
|
||||
assertEquals(expectedListenerCallCount, chunkListener.beforeCount);
|
||||
assertEquals(1, chunkListener.failedCount);
|
||||
assertEquals("1234123415", writeListener.trail);
|
||||
assertTrue("Listener order not as expected: " + writeListener.trail, writeListener.trail.startsWith("1234"));
|
||||
}
|
||||
|
||||
@@ -392,6 +407,10 @@ public class SimpleStepFactoryBeanTests {
|
||||
public void beforeChunk() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterChunkError(ChunkContext context) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TestItemListenerWriter itemWriter = new TestItemListenerWriter();
|
||||
|
||||
Reference in New Issue
Block a user