BATCH-919: reuse ChunkProcessor in integration
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user