BATCH-919: reuse ChunkProcessor in integration

This commit is contained in:
dsyer
2008-12-30 16:32:29 +00:00
parent 7d7480578d
commit a6479602e2
18 changed files with 166 additions and 200 deletions

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.batch.core;
import java.io.Serializable;
/**
* Represents a contribution to a {@link StepExecution}, buffering changes until
* they can be applied at a chunk boundary.
@@ -22,7 +24,7 @@ package org.springframework.batch.core;
* @author Dave Syer
*
*/
public class StepContribution {
public class StepContribution implements Serializable {
private volatile int readCount = 0;

View File

@@ -1,6 +1,7 @@
package org.springframework.batch.core.step.item;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -15,7 +16,7 @@ import java.util.List;
* @author Dave Syer
*
*/
class Chunk<W> implements Iterable<W> {
public class Chunk<W> implements Iterable<W> {
private List<W> items = new ArrayList<W>();
@@ -30,8 +31,12 @@ class Chunk<W> implements Iterable<W> {
public Chunk() {
this(null,null);
}
public Chunk(Collection<? extends W> items) {
this(items,null);
}
public Chunk(List<W> items, List<SkipWrapper<W>> skips) {
public Chunk(Collection<? extends W> items, List<SkipWrapper<W>> skips) {
super();
if (items!=null) {
this.items = new ArrayList<W>(items);

View File

@@ -8,20 +8,55 @@ import org.springframework.batch.core.listener.MulticasterBatchListener;
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class SimpleChunkProcessor<I, O> implements ChunkProcessor<I> {
public class SimpleChunkProcessor<I, O> implements ChunkProcessor<I>, InitializingBean {
private final ItemProcessor<? super I, ? extends O> itemProcessor;
private ItemProcessor<? super I, ? extends O> itemProcessor;
private final ItemWriter<? super O> itemWriter;
private ItemWriter<? super O> itemWriter;
private final MulticasterBatchListener<I, O> listener = new MulticasterBatchListener<I, O>();
/**
* Default constructor for ease of configuration (both itemWriter and
* itemProcessor are mandatory).
*/
@SuppressWarnings("unused")
private SimpleChunkProcessor() {
this(null, null);
}
public SimpleChunkProcessor(ItemProcessor<? super I, ? extends O> itemProcessor, ItemWriter<? super O> itemWriter) {
this.itemProcessor = itemProcessor;
this.itemWriter = itemWriter;
}
/**
* @param itemProcessor the {@link ItemProcessor} to set
*/
public void setItemProcessor(ItemProcessor<? super I, ? extends O> itemProcessor) {
this.itemProcessor = itemProcessor;
}
/**
* @param itemWriter the {@link ItemWriter} to set
*/
public void setItemWriter(ItemWriter<? super O> itemWriter) {
this.itemWriter = itemWriter;
}
/**
* Check mandatory properties.
*
* @see InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(itemWriter, "ItemWriter must be set");
Assert.notNull(itemProcessor, "ItemProcessor must be set");
}
/**
* Register some {@link StepListener}s with the handler. Each will get the
* callbacks in the order specified at the correct stage.

View File

@@ -6,8 +6,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
import org.springframework.batch.core.step.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemStreamException;
@@ -19,9 +21,9 @@ public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSuppo
private static final Log logger = LogFactory.getLog(ChunkMessageChannelItemWriter.class);
static final String ACTUAL = "ACTUAL";
static final String ACTUAL = ChunkMessageChannelItemWriter.class.getName()+".ACTUAL";
static final String EXPECTED = "EXPECTED";
static final String EXPECTED = ChunkMessageChannelItemWriter.class.getName()+".EXPECTED";
private static final long DEFAULT_THROTTLE_LIMIT = 6;
@@ -54,7 +56,7 @@ public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSuppo
if (!items.isEmpty()) {
logger.debug("Dispatching chunk: " + items);
ChunkRequest<T> request = new ChunkRequest<T>(items, localState.getJobId(), localState.getSkipCount());
ChunkRequest<T> request = new ChunkRequest<T>(new Chunk<T>(items), localState.getJobId(), localState.createStepContribution());
messagingGateway.send(request);
localState.expected++;
@@ -156,8 +158,8 @@ public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSuppo
return expected - actual;
}
public int getSkipCount() {
return stepExecution.getSkipCount();
public StepContribution createStepContribution() {
return stepExecution.createStepContribution();
}
public Long getJobId() {

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2006-2007 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.integration.chunk;
import java.util.Collection;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Dave Syer
*/
public interface ChunkProcessor<S> {
// This is transactional with REQUIRES_NEW because we need to force rollback
@Transactional(propagation = Propagation.REQUIRES_NEW)
int process(Collection<? extends S> items, int skipCount) throws Exception;
}

View File

@@ -2,17 +2,17 @@ package org.springframework.batch.integration.chunk;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.step.item.ChunkProcessor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.util.Assert;
@MessageEndpoint
public class ChunkProcessorChunkHandler<S> implements ChunkHandler<S>,
InitializingBean {
public class ChunkProcessorChunkHandler<S> implements ChunkHandler<S>, InitializingBean {
private static final Log logger = LogFactory
.getLog(ChunkProcessorChunkHandler.class);
private static final Log logger = LogFactory.getLog(ChunkProcessorChunkHandler.class);
private ChunkProcessor<S> chunkProcessor;
@@ -29,37 +29,33 @@ public class ChunkProcessorChunkHandler<S> implements ChunkHandler<S>,
/**
* Public setter for the {@link ChunkProcessor}.
*
* @param chunkProcessor
* the chunkProcessor to set
* @param chunkProcessor the chunkProcessor to set
*/
public void setChunkProcessor(ChunkProcessor<S> chunkProcessor) {
this.chunkProcessor = chunkProcessor;
}
/*
* (non-Javadoc)
/**
*
* @see
* org.springframework.integration.batch.slave.ChunkHandler#handleChunk(
* java.util.Collection)
* @see ChunkHandler#handleChunk(ChunkRequest)
*/
@ServiceActivator
public ChunkResponse handleChunk(ChunkRequest<S> chunkRequest) {
logger.debug("Handling chunk: " + chunkRequest);
int skipCount = 0;
StepContribution stepContribution = chunkRequest.getStepContribution();
try {
skipCount = chunkProcessor.process(chunkRequest.getItems(),
chunkRequest.getSkipCount());
} catch (Exception e) {
chunkProcessor.process(stepContribution, chunkRequest.getChunk());
}
catch (Exception e) {
logger.debug("Failed chunk", e);
return new ChunkResponse(false, chunkRequest.getJobId(), skipCount,
e.getClass().getName() + ": " + e.getMessage());
return new ChunkResponse(false, chunkRequest.getJobId(), stepContribution, e.getClass().getName() + ": "
+ e.getMessage());
}
logger.debug("Completed chunk handling with " + skipCount + " skips");
return new ChunkResponse(true, chunkRequest.getJobId(), skipCount);
logger.debug("Completed chunk handling with " + stepContribution);
return new ChunkResponse(true, chunkRequest.getJobId(), stepContribution);
}
}

View File

@@ -1,38 +1,43 @@
package org.springframework.batch.integration.chunk;
import java.io.Serializable;
import java.util.Collection;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.step.item.Chunk;
public class ChunkRequest<T> implements Serializable {
private final int skipCount;
private final Long jobId;
private final Collection<? extends T> items;
private final Chunk<T> items;
private final StepContribution stepContribution;
public ChunkRequest(Collection<? extends T> items, Long jobId, int skipCount) {
public ChunkRequest(Chunk<T> items, Long jobId, StepContribution stepContribution) {
this.items = items;
this.jobId = jobId;
this.skipCount = skipCount;
}
public int getSkipCount() {
return skipCount;
this.stepContribution = stepContribution;
}
public Long getJobId() {
return jobId;
}
public Collection<? extends T> getItems() {
public Chunk<T> getChunk() {
return items;
}
/**
* @return the {@link StepContribution} for this chunk
*/
public StepContribution getStepContribution() {
return stepContribution;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getClass().getSimpleName()+": jobId="+jobId+", skipCount="+skipCount+", item count="+items.size();
return getClass().getSimpleName()+": jobId="+jobId+", contribution="+stepContribution+", item count="+items.size();
}
}

View File

@@ -2,34 +2,32 @@ package org.springframework.batch.integration.chunk;
import java.io.Serializable;
import org.springframework.batch.core.StepContribution;
public class ChunkResponse implements Serializable {
private final int skipCount;
private final StepContribution stepContribution;
private final Long jobId;
private final boolean status;
private final String message;
public ChunkResponse(Long jobId) {
this(true, jobId, 0, null);
public ChunkResponse(Long jobId, StepContribution stepContribution) {
this(true, jobId, stepContribution, null);
}
public ChunkResponse(Long jobId, int skipCount) {
this(true, jobId, skipCount, null);
public ChunkResponse(boolean status, Long jobId, StepContribution stepContribution) {
this(status, jobId, stepContribution, null);
}
public ChunkResponse(boolean status, Long jobId, int skipCount) {
this(status, jobId, skipCount, null);
}
public ChunkResponse(boolean status, Long jobId, int skipCount, String message) {
public ChunkResponse(boolean status, Long jobId, StepContribution stepContribution, String message) {
this.status = status;
this.jobId = jobId;
this.skipCount = skipCount;
this.stepContribution = stepContribution;
this.message = message;
}
public int getSkipCount() {
return skipCount;
public StepContribution getStepContribution() {
return stepContribution;
}
public Long getJobId() {
@@ -49,7 +47,7 @@ public class ChunkResponse implements Serializable {
*/
@Override
public String toString() {
return getClass().getSimpleName()+": jobId="+jobId+", skipCount="+skipCount+", successful="+status;
return getClass().getSimpleName()+": jobId="+jobId+", stepContribution="+stepContribution+", successful="+status;
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2006-2007 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.integration.chunk;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* @author Dave Syer
*
*/
public class SimpleChunkProcessor<S, T> implements ChunkProcessor<S>, InitializingBean {
private ItemProcessor<? super S, ? extends T> itemProcessor;
private ItemWriter<? super T> itemWriter;
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(itemProcessor, "An ItemProcessor must be provided");
Assert.notNull(itemWriter, "An ItemWriter must be provided");
}
/**
* @param itemWriter
*/
public void setItemWriter(ItemWriter<? super T> itemWriter) {
this.itemWriter = itemWriter;
}
/**
* Public setter for the {@link ItemProcessor}.
* @param itemProcessor the {@link ItemProcessor} to set
*/
public void setItemProcessor(ItemProcessor<? super S, ? extends T> itemProcessor) {
this.itemProcessor = itemProcessor;
}
/*
* (non-Javadoc)
* @see org.springframework.batch.integration.chunk.ChunkProcessor#process(java.util.Collection,
* int)
*/
public int process(Collection<? extends S> items, int parentSkipCount) throws Exception {
List<T> processed = new ArrayList<T>();
for (S item : items) {
processed.add(itemProcessor.process(item));
}
itemWriter.write(processed);
return 0;
}
}

View File

@@ -12,8 +12,11 @@ import org.junit.runner.RunWith;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.SimpleJob;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
@@ -24,6 +27,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.step.item.Chunk;
import org.springframework.batch.core.step.item.SimpleStepFactoryBean;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.support.ListItemReader;
@@ -69,7 +73,7 @@ public class ChunkMessageItemWriterIntegrationTests {
factory.setBeanName("step");
factory.setItemWriter(writer);
factory.setCommitInterval(4);
SimpleMessagingGateway gateway = new SimpleMessagingGateway();
writer.setMessagingGateway(gateway);
@@ -85,7 +89,7 @@ public class ChunkMessageItemWriterIntegrationTests {
System.err.println(message);
message = replies.receive(10);
}
}
@After
@@ -186,7 +190,10 @@ public class ChunkMessageItemWriterIntegrationTests {
*/
@SuppressWarnings("unchecked")
private GenericMessage<ChunkRequest> getSimpleMessage(String string, Long jobId) {
ChunkRequest chunk = new ChunkRequest(StringUtils.commaDelimitedListToSet(string), jobId, 0);
StepContribution stepContribution = new JobExecution(new JobInstance(0L, new JobParameters(), "job"), 1L)
.createStepExecution("step").createStepContribution();
ChunkRequest chunk = new ChunkRequest(new Chunk<String>(StringUtils.commaDelimitedListToSet(string)), jobId,
stepContribution);
GenericMessage<ChunkRequest> message = new GenericMessage<ChunkRequest>(chunk);
return message;
}

View File

@@ -3,9 +3,13 @@ package org.springframework.batch.integration.chunk;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.step.item.Chunk;
import org.springframework.batch.core.step.item.ChunkProcessor;
import org.springframework.util.StringUtils;
public class ChunkProcessorChunkHandlerTests {
@@ -17,15 +21,15 @@ public class ChunkProcessorChunkHandlerTests {
@Test
public void testVanillaHandleChunk() {
handler.setChunkProcessor(new ChunkProcessor<Object>() {
public int process(Collection<? extends Object> items, int skipCount) throws Exception {
count += items.size();
return 0;
public void process(StepContribution contribution, Chunk<Object> chunk) throws Exception {
count += chunk.size();
}
});
StepContribution stepContribution = new JobExecution(new JobInstance(0L, new JobParameters(), "job"), 1L).createStepExecution("step").createStepContribution();
@SuppressWarnings("unchecked")
ChunkResponse response = handler.handleChunk(new ChunkRequest<Object>(StringUtils
.commaDelimitedListToSet("foo,bar"), 12L, 10));
assertEquals(0, response.getSkipCount());
ChunkResponse response = handler.handleChunk(new ChunkRequest<Object>(new Chunk<Object>(StringUtils
.commaDelimitedListToSet("foo,bar")), 12L, stepContribution));
assertEquals(stepContribution, response.getStepContribution());
assertEquals(12, response.getJobId().longValue());
assertTrue(response.isSuccessful());
assertEquals(2, count);

View File

@@ -30,7 +30,7 @@ public class RetryRepeatTransactionalPollingIntegrationTests implements Applicat
private Log logger = LogFactory.getLog(getClass());
private static List<String> list = new ArrayList<String>();
private volatile static List<String> list = new ArrayList<String>();
@Autowired
private SimpleRecoverer recoverer;
@@ -38,10 +38,10 @@ public class RetryRepeatTransactionalPollingIntegrationTests implements Applicat
@Autowired
private SimpleService service;
private Lifecycle bus;
private Lifecycle lifecycle;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
bus = (Lifecycle) applicationContext;
lifecycle = (Lifecycle) applicationContext;
}
private static volatile int count = 0;
@@ -73,7 +73,7 @@ public class RetryRepeatTransactionalPollingIntegrationTests implements Applicat
List<String> expected = TransactionAwareProxyFactory.createTransactionalList(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("a,b,c,d")));
service.setExpected(expected);
waitForResults(bus, expected.size(), 60);
waitForResults(lifecycle, expected.size(), 60);
assertEquals(4,service.getProcessed().size()); // a,b,c,d
assertEquals(expected, service.getProcessed());
}
@@ -86,8 +86,8 @@ public class RetryRepeatTransactionalPollingIntegrationTests implements Applicat
List<String> expected = TransactionAwareProxyFactory.createTransactionalList(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("a,b,fail,fail,d,e,f")));
service.setExpected(expected);
waitForResults(bus, expected.size(), 60);
waitForResults(bus, 6, 100); // (a,b), (fail), (fail), ([fail],d), (e,f)
waitForResults(lifecycle, expected.size(), 60); // (a,b), (fail), (fail), ([fail],d), (e,f)
System.err.println(service.getProcessed());
assertEquals(7,service.getProcessed().size()); // a,b,fail,fail,d,e,f
assertEquals(1,recoverer.getRecovered().size()); // fail
assertEquals(expected, service.getProcessed());

View File

@@ -69,7 +69,9 @@ public class TransactionalPollingIntegrationTests implements ApplicationContextA
}
public void output(String message) {
handled.add(message);
if (count < expected.size()) {
handled.add(message);
}
logger.debug("Handled: " + message);
}

View File

@@ -19,7 +19,7 @@
<tx:annotation-driven />
<beans:bean id="chunkHandler" class="org.springframework.batch.integration.chunk.ChunkProcessorChunkHandler">
<beans:property name="chunkProcessor">
<beans:bean class="org.springframework.batch.integration.chunk.SimpleChunkProcessor">
<beans:bean class="org.springframework.batch.core.step.item.SimpleChunkProcessor">
<beans:property name="itemWriter">
<beans:bean class="org.springframework.batch.integration.chunk.TestItemWriter" />
</beans:property>

View File

@@ -20,7 +20,7 @@
<integration:inbound-channel-adapter
ref="testCase" method="input" channel="requests">
<integration:poller max-messages-per-poll="1">
<integration:interval-trigger interval="10" />
<integration:interval-trigger interval="10" initial-delay="100"/>
<integration:advice-chain>
<ref bean="txAdvice"/>
<ref bean="repeatAdvice"/>

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
@@ -18,10 +20,16 @@
<integration:inbound-channel-adapter
ref="testCase" method="input" channel="requests">
<integration:poller max-messages-per-poll="1">
<integration:interval-trigger interval="10" />
<!--
TODO: the initial delay is a hack - it usually prevents the poller
from picking up data in the list before it is cleared, but one day
100ms will not be enough... (see INT-536)
-->
<integration:interval-trigger interval="10"
initial-delay="100" />
<integration:advice-chain>
<ref bean="txAdvice"/>
<ref bean="repeatAdvice"/>
<ref bean="txAdvice" />
<ref bean="repeatAdvice" />
</integration:advice-chain>
</integration:poller>
</integration:inbound-channel-adapter>
@@ -39,32 +47,44 @@
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<bean id="repeatAdvice" class="org.springframework.batch.repeat.interceptor.RepeatOperationsInterceptor">
<bean id="repeatAdvice"
class="org.springframework.batch.repeat.interceptor.RepeatOperationsInterceptor">
<property name="repeatOperations">
<bean class="org.springframework.batch.repeat.support.RepeatTemplate">
<property name="completionPolicy">
<bean class="org.springframework.batch.repeat.policy.SimpleCompletionPolicy">
<bean
class="org.springframework.batch.repeat.policy.SimpleCompletionPolicy">
<property name="chunkSize" value="2" />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="service" class="org.springframework.batch.integration.retry.SimpleService" />
<bean id="recoverer" class="org.springframework.batch.integration.retry.SimpleRecoverer" />
<bean id="retryAdvice" class="org.springframework.batch.retry.interceptor.StatefulRetryOperationsInterceptor">
<bean id="service"
class="org.springframework.batch.integration.retry.SimpleService" />
<bean id="recoverer"
class="org.springframework.batch.integration.retry.SimpleRecoverer" />
<bean id="retryAdvice"
class="org.springframework.batch.retry.interceptor.StatefulRetryOperationsInterceptor">
<property name="retryOperations">
<bean class="org.springframework.batch.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.batch.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="2"/>
<property name="maxAttempts" value="2" />
</bean>
</property>
</bean>
</property>
<property name="recoverer" ref="recoverer" />
</bean>
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="retryAdvice" pointcut="execution(* org.springframework.batch.integration.retry.Service+.process(..))" />
<aop:advisor advice-ref="retryAdvice"
pointcut="execution(* org.springframework.batch.integration.retry.Service+.process(..))" />
</aop:config>
</beans>

View File

@@ -18,7 +18,7 @@
<integration:inbound-channel-adapter
ref="testCase" method="input" channel="requests">
<integration:poller max-messages-per-poll="1">
<integration:interval-trigger interval="10" />
<integration:interval-trigger interval="10" initial-delay="100" />
<integration:transactional />
</integration:poller>
</integration:inbound-channel-adapter>

View File

@@ -20,7 +20,7 @@
<integration:inbound-channel-adapter
ref="testCase" method="input" channel="requests">
<integration:poller max-messages-per-poll="1">
<integration:interval-trigger interval="10" />
<integration:interval-trigger interval="10" initial-delay="100"/>
<integration:transactional />
</integration:poller>
</integration:inbound-channel-adapter>