RESOLVED - issue BATCH-1093: Make AbstractJobTests.makeUniqueJobParameters() public
RESOLVED - issue BATCH-1096: Add late binding to some io samples RESOLVED - issue BATCH-1087: ClassPathXmlJobRegistry does not accept patterns for resources RESOLVED - BATCH-1098: added test for @AfterWrite in FaultTolerantChinkProcessorTests
This commit is contained in:
@@ -39,13 +39,20 @@ public class JobParametersBuilder {
|
||||
private final Map<String, JobParameter> parameterMap;
|
||||
|
||||
/**
|
||||
* Default constructor. Initializes the builder
|
||||
* Default constructor. Initializes the builder with empty parameters.
|
||||
*/
|
||||
public JobParametersBuilder() {
|
||||
|
||||
this.parameterMap = new LinkedHashMap<String, JobParameter>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. Initializes the builder with the supplied parameters.
|
||||
*/
|
||||
public JobParametersBuilder(JobParameters jobParameters) {
|
||||
this.parameterMap = new LinkedHashMap<String, JobParameter>(jobParameters.getParameters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new String parameter for the given key.
|
||||
*
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.batch.retry.RetryCallback;
|
||||
import org.springframework.batch.retry.RetryContext;
|
||||
import org.springframework.batch.retry.RetryException;
|
||||
import org.springframework.batch.retry.support.DefaultRetryState;
|
||||
import org.springframework.batch.support.BinaryExceptionClassifier;
|
||||
import org.springframework.batch.support.Classifier;
|
||||
|
||||
public class FaultTolerantChunkProcessor<I, O> extends SimpleChunkProcessor<I, O> {
|
||||
@@ -40,11 +41,11 @@ public class FaultTolerantChunkProcessor<I, O> extends SimpleChunkProcessor<I, O
|
||||
|
||||
private final BatchRetryTemplate batchRetryTemplate;
|
||||
|
||||
private Classifier<Throwable, Boolean> rollbackClassifier;
|
||||
private Classifier<Throwable, Boolean> rollbackClassifier = new BinaryExceptionClassifier(true);
|
||||
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean buffering;
|
||||
private boolean buffering = true;
|
||||
|
||||
public void setProcessSkipPolicy(SkipPolicy SkipPolicy) {
|
||||
this.itemProcessSkipPolicy = SkipPolicy;
|
||||
@@ -155,7 +156,7 @@ public class FaultTolerantChunkProcessor<I, O> extends SimpleChunkProcessor<I, O
|
||||
}
|
||||
|
||||
try {
|
||||
doWrite(Collections.singletonList(item));
|
||||
writeItems(Collections.singletonList(item));
|
||||
contribution.incrementWriteCount(1);
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
@@ -121,7 +121,7 @@ public class SimpleChunkProcessor<I, O> implements ChunkProcessor<I>, Initializi
|
||||
protected final void doWrite(List<O> items) throws Exception {
|
||||
try {
|
||||
listener.beforeWrite(items);
|
||||
itemWriter.write(items);
|
||||
writeItems(items);
|
||||
listener.afterWrite(items);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -130,6 +130,10 @@ public class SimpleChunkProcessor<I, O> implements ChunkProcessor<I>, Initializi
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeItems(List<O> items) throws Exception {
|
||||
itemWriter.write(items);
|
||||
}
|
||||
|
||||
public final void process(StepContribution contribution, Chunk<I> inputs) throws Exception {
|
||||
|
||||
// If there is no input we don't have to do anything more
|
||||
|
||||
@@ -28,6 +28,13 @@ public class JobParametersBuilderTests extends TestCase {
|
||||
assertEquals("string value", parameters.getString("STRING"));
|
||||
}
|
||||
|
||||
public void testCopy(){
|
||||
parametersBuilder.addString("STRING", "string value");
|
||||
parametersBuilder = new JobParametersBuilder(parametersBuilder.toJobParameters());
|
||||
Iterator<String> parameters = parametersBuilder.toJobParameters().getParameters().keySet().iterator();
|
||||
assertEquals("STRING", parameters.next());
|
||||
}
|
||||
|
||||
public void testOrderedTypes(){
|
||||
parametersBuilder.addDate("SCHEDULE_DATE", date);
|
||||
parametersBuilder.addLong("LONG", new Long(1));
|
||||
|
||||
@@ -3,14 +3,20 @@ package org.springframework.batch.core.step.item;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.listener.ItemListenerSupport;
|
||||
import org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.support.PassThroughItemProcessor;
|
||||
import org.springframework.batch.retry.policy.NeverRetryPolicy;
|
||||
|
||||
public class FaultTolerantChunkProcessorTests {
|
||||
|
||||
@@ -18,37 +24,74 @@ public class FaultTolerantChunkProcessorTests {
|
||||
|
||||
private List<String> list = new ArrayList<String>();
|
||||
|
||||
@Test
|
||||
public void testWrite() throws Exception {
|
||||
FaultTolerantChunkProcessor<String, String> processor = new FaultTolerantChunkProcessor<String, String>(
|
||||
new PassThroughItemProcessor<String>(), new ItemWriter<String>() {
|
||||
private List<String> after = new ArrayList<String>();
|
||||
|
||||
private FaultTolerantChunkProcessor<String, String> processor;
|
||||
|
||||
private StepContribution contribution = new StepExecution("foo", new JobExecution(0L)).createStepContribution();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
processor = new FaultTolerantChunkProcessor<String, String>(new PassThroughItemProcessor<String>(),
|
||||
new ItemWriter<String>() {
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
if (items.contains("fail")) {
|
||||
throw new RuntimeException("Planned failure!");
|
||||
}
|
||||
list.addAll(items);
|
||||
}
|
||||
}, batchRetryTemplate);
|
||||
Chunk<String> inputs = new Chunk<String>();
|
||||
inputs.add("1");
|
||||
inputs.add("2");
|
||||
processor.process(new StepExecution("foo", new JobExecution(0L)).createStepContribution(), inputs);
|
||||
batchRetryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() throws Exception {
|
||||
Chunk<String> inputs = new Chunk<String>(Arrays.asList("1", "2"));
|
||||
processor.process(contribution, inputs);
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransform() throws Exception {
|
||||
FaultTolerantChunkProcessor<String, String> processor = new FaultTolerantChunkProcessor<String, String>(
|
||||
new ItemProcessor<String, String>() {
|
||||
public String process(String item) throws Exception {
|
||||
return item.equals("1") ? null : item;
|
||||
}
|
||||
}, new ItemWriter<String>() {
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
list.addAll(items);
|
||||
}
|
||||
}, batchRetryTemplate);
|
||||
Chunk<String> inputs = new Chunk<String>();
|
||||
inputs.add("1");
|
||||
inputs.add("2");
|
||||
processor.process(new StepExecution("foo", new JobExecution(0L)).createStepContribution(), inputs);
|
||||
processor.setItemProcessor(new ItemProcessor<String, String>() {
|
||||
public String process(String item) throws Exception {
|
||||
return item.equals("1") ? null : item;
|
||||
}
|
||||
});
|
||||
Chunk<String> inputs = new Chunk<String>(Arrays.asList("1", "2"));
|
||||
processor.process(contribution, inputs);
|
||||
assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAfterWrite() throws Exception {
|
||||
Chunk<String> chunk = new Chunk<String>(Arrays.asList("foo", "fail", "bar"));
|
||||
processor.setListeners(Arrays.asList(new ItemListenerSupport<String, String>() {
|
||||
@Override
|
||||
public void afterWrite(List<? extends String> item) {
|
||||
after.addAll(item);
|
||||
}
|
||||
}));
|
||||
processor.setWriteSkipPolicy(new AlwaysSkipItemSkipPolicy());
|
||||
try {
|
||||
processor.process(contribution, chunk);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
assertEquals("Planned failure!", e.getMessage());
|
||||
}
|
||||
try {
|
||||
processor.process(contribution, chunk);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
assertEquals("Planned failure!", e.getMessage());
|
||||
}
|
||||
assertEquals(2, chunk.getItems().size());
|
||||
processor.process(contribution, chunk);
|
||||
// foo is written twice because the failure is detected on the second
|
||||
// attempt when throttling
|
||||
assertEquals("[foo, foo, bar]", list.toString());
|
||||
// but the after listener is only called once, which is important
|
||||
assertEquals(2, after.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,15 +22,19 @@ public class SimpleChunkProcessorTests {
|
||||
private StepContribution contribution = new StepContribution(new StepExecution("foo", new JobExecution(
|
||||
new JobInstance(123L, new JobParameters(), "job"))));
|
||||
|
||||
protected List<String> list = new ArrayList<String>();
|
||||
private List<String> list = new ArrayList<String>();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
processor = new SimpleChunkProcessor<String,String>(new PassThroughItemProcessor<String>(), new ItemWriter<String>() {
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
list.addAll(items);
|
||||
}
|
||||
});
|
||||
processor = new SimpleChunkProcessor<String, String>(new PassThroughItemProcessor<String>(),
|
||||
new ItemWriter<String>() {
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
if (items.contains("fail")) {
|
||||
throw new RuntimeException("Planned failure!");
|
||||
}
|
||||
list.addAll(items);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
|
||||
|
||||
<bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="data/iosample/input/delimited.csv" />
|
||||
<bean id="itemReaderParent" class="org.springframework.batch.item.file.FlatFileItemReader" abstract="true">
|
||||
<property name="lineMapper">
|
||||
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
|
||||
<property name="lineTokenizer">
|
||||
@@ -30,6 +29,14 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="itemReader" parent="itemReaderParent" scope="step" autowire-candidate="false">
|
||||
<property name="resource" value="#{jobParameters[fileName]}" />
|
||||
</bean>
|
||||
|
||||
<bean id="itemReaderForTest" parent="itemReaderParent" >
|
||||
<property name="resource" value="data/iosample/input/delimited.csv" />
|
||||
</bean>
|
||||
|
||||
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
|
||||
<property name="resource" ref="outputResource" />
|
||||
<property name="lineAggregator">
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
|
||||
|
||||
<bean id="itemReader"
|
||||
class="org.springframework.batch.item.file.MultiResourceItemReader">
|
||||
<property name="resources" value="classpath:data/iosample/input/delimited*.csv" />
|
||||
<bean id="itemReaderParent"
|
||||
class="org.springframework.batch.item.file.MultiResourceItemReader" abstract="true">
|
||||
<property name="delegate">
|
||||
<bean class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="lineMapper">
|
||||
@@ -38,6 +37,14 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="itemReader" parent="itemReaderParent" scope="step" autowire-candidate="false">
|
||||
<property name="resources" value="#{jobParameters[file.path]}/delimited*.csv" />
|
||||
</bean>
|
||||
|
||||
<bean id="itemReaderForTest" parent="itemReaderParent">
|
||||
<property name="resources" value="classpath:data/iosample/input/delimited*.csv" />
|
||||
</bean>
|
||||
|
||||
<bean id="itemWriter"
|
||||
class="org.springframework.batch.item.file.MultiResourceItemWriter">
|
||||
<property name="resource"
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.batch.sample.iosample;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.file.FlatFileItemReader;
|
||||
import org.springframework.batch.sample.domain.trade.CustomerCredit;
|
||||
@@ -36,11 +38,17 @@ public class DelimitedFunctionalTests extends AbstractIoSampleTests {
|
||||
|
||||
@Autowired
|
||||
private Resource outputResource;
|
||||
|
||||
|
||||
@Override
|
||||
protected void pointReaderToOutput(ItemReader<CustomerCredit> reader) {
|
||||
FlatFileItemReader<CustomerCredit> fileReader = (FlatFileItemReader<CustomerCredit>) reader;
|
||||
fileReader.setResource(outputResource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobParameters getUniqueJobParameters() {
|
||||
return new JobParametersBuilder(super.getUniqueJobParameters()).addString("fileName",
|
||||
"data/iosample/input/delimited.csv").toJobParameters();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.batch.sample.iosample;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.file.MultiResourceItemReader;
|
||||
import org.springframework.batch.sample.domain.trade.CustomerCredit;
|
||||
@@ -42,5 +44,11 @@ public class MultiResourceFunctionalTests extends AbstractIoSampleTests {
|
||||
new FileSystemResource("target/test-outputs/multiResourceOutput.csv.2") });
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobParameters getUniqueJobParameters() {
|
||||
JobParametersBuilder builder = new JobParametersBuilder(super.getUniqueJobParameters());
|
||||
return builder.addString("file.path", "classpath:data/iosample/input/").toJobParameters();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ import org.springframework.context.ApplicationContext;
|
||||
* entire {@link AbstractJob}, allowing for end to end testing of individual
|
||||
* steps, without having to run every step in the job. Any test classes
|
||||
* inheriting from this class should make sure they are part of an
|
||||
* {@link ApplicationContext}, which is generally expected to be done as part
|
||||
* of the Spring test framework. Furthermore, the {@link ApplicationContext} in
|
||||
* {@link ApplicationContext}, which is generally expected to be done as part of
|
||||
* the Spring test framework. Furthermore, the {@link ApplicationContext} in
|
||||
* which it is a part of is expected to have one {@link JobLauncher},
|
||||
* {@link JobRepository}, and a single {@link AbstractJob} implementation.
|
||||
*
|
||||
@@ -79,14 +79,14 @@ public abstract class AbstractJobTests {
|
||||
private StepRunner stepRunner;
|
||||
|
||||
/**
|
||||
* @return the job repository
|
||||
* @return the job repository which is autowired by type
|
||||
*/
|
||||
public JobRepository getJobRepository() {
|
||||
return jobRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the job
|
||||
* @return the job which is autowired by type
|
||||
*/
|
||||
public AbstractJob getJob() {
|
||||
return job;
|
||||
@@ -102,34 +102,40 @@ public abstract class AbstractJobTests {
|
||||
/**
|
||||
* Launch the entire job, including all steps.
|
||||
*
|
||||
* @return JobExecution, so that the test may validate the exit status
|
||||
* @return JobExecution, so that the test can validate the exit status
|
||||
* @throws Exception
|
||||
*/
|
||||
public JobExecution launchJob() throws Exception {
|
||||
return this.launchJob(this.makeUniqueJobParameters());
|
||||
protected JobExecution launchJob() throws Exception {
|
||||
return this.launchJob(this.getUniqueJobParameters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the entire job, including all steps
|
||||
*
|
||||
* @param jobParameters
|
||||
* @return JobExecution, so that the test may validate the exit status
|
||||
* @return JobExecution, so that the test can validate the exit status
|
||||
* @throws Exception
|
||||
*/
|
||||
public JobExecution launchJob(JobParameters jobParameters) throws Exception {
|
||||
protected JobExecution launchJob(JobParameters jobParameters) throws Exception {
|
||||
return getJobLauncher().run(this.job, jobParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new JobParameters object containing only a parameter for the
|
||||
* current timestamp, to ensure that the job instance will be unique
|
||||
* current timestamp, to ensure that the job instance will be unique.
|
||||
*/
|
||||
public JobParameters makeUniqueJobParameters() {
|
||||
protected JobParameters getUniqueJobParameters() {
|
||||
Map<String, JobParameter> parameters = new HashMap<String, JobParameter>();
|
||||
parameters.put("timestamp", new JobParameter(new Date().getTime()));
|
||||
return new JobParameters(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method for subclasses to grab a {@link StepRunner} for running
|
||||
* steps by name.
|
||||
*
|
||||
* @return a {@link StepRunner}
|
||||
*/
|
||||
protected StepRunner getStepRunner() {
|
||||
if (this.stepRunner == null) {
|
||||
this.stepRunner = new StepRunner(getJobLauncher(), getJobRepository());
|
||||
@@ -138,16 +144,15 @@ public abstract class AbstractJobTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step in the job. An IllegalStateException is thrown
|
||||
* if there is no Step with the given name.
|
||||
* Launch just the specified step in the job. An IllegalStateException is
|
||||
* thrown if there is no Step with the given name.
|
||||
*
|
||||
* @param stepName
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(String stepName) {
|
||||
Step step = this.job.getStep(stepName);
|
||||
if(step == null)
|
||||
{
|
||||
if (step == null) {
|
||||
throw new IllegalStateException("No Step found with name: [" + stepName + "]");
|
||||
}
|
||||
return getStepRunner().launchStep(step);
|
||||
|
||||
Reference in New Issue
Block a user