OPEN - issue BATCH-911: Consolidate Samples

http://jira.springframework.org/browse/BATCH-911

Consolidated non sequential and incrementer jobs into skipSample.
This commit is contained in:
lucasward
2008-11-18 20:23:49 +00:00
parent 43b5edba71
commit 6e39c1a572
51 changed files with 588 additions and 1364 deletions

View File

@@ -1,129 +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.sample;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.io.BufferedReader;
import java.io.FileReader;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.batch.sample.domain.trade.Trade;
import org.springframework.batch.sample.domain.trade.internal.TradeFieldSetMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration()
public class FixedLengthImportJobFunctionalTests extends AbstractValidatingBatchLauncherTests {
//expected line length in input file (sum of pattern lengths + 2, because the counter is appended twice)
private static final int LINE_LENGTH = 29;
//auto-injected attributes
private SimpleJdbcTemplate simpleJdbcTemplate;
private Resource fileLocator;
protected FlatFileItemReader<Trade> itemReader;
private LineTokenizer lineTokenizer;
@Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Autowired
public void setLineTokenizer(LineTokenizer lineTokenizer) {
this.lineTokenizer = lineTokenizer;
}
@Before
public void onSetUp() throws Exception {
simpleJdbcTemplate.update("delete from TRADE");
fileLocator = new ClassPathResource("data/fixedLengthImportJob/input/20070122.teststream.ImportTradeDataStep.txt");
itemReader = new FlatFileItemReader<Trade>();
FieldSetMapper<Trade> mapper = new TradeFieldSetMapper();
DefaultLineMapper<Trade> lineMapper = new DefaultLineMapper<Trade>();
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(mapper);
itemReader.setLineMapper(lineMapper);
itemReader.setResource(fileLocator);
itemReader.open(new ExecutionContext());
}
/**
* Check that records have been correctly written to database
* @throws Exception
*/
protected void validatePostConditions() throws Exception {
simpleJdbcTemplate.getJdbcOperations().query(
"SELECT ID, ISIN, QUANTITY, PRICE, CUSTOMER FROM trade ORDER BY id",
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
Trade trade;
try {
trade = itemReader.read();
}
catch (Exception e) {
throw new IllegalStateException(e.getMessage());
}
assertEquals(trade.getIsin(), rs.getString(2));
assertEquals(trade.getQuantity(),rs.getLong(3));
assertEquals(trade.getPrice(), rs.getBigDecimal(4));
assertEquals(trade.getCustomer(), rs.getString(5));
}
});
assertNull(itemReader.read());
}
/*
* fixed-length file is expected on input
*/
protected void validatePreConditions() throws Exception{
BufferedReader reader;
reader = new BufferedReader(new FileReader(fileLocator.getFile()));
String line;
while ((line = reader.readLine()) != null) {
assertEquals (LINE_LENGTH, line.length());
}
}
}

View File

@@ -47,8 +47,6 @@ public class HibernateFailureJobFunctionalTests extends AbstractCustomerCreditIn
setJobParameters(params);
writer.setFailOnFlush(2);
int before = simpleJdbcTemplate.queryForInt("SELECT COUNT(*) from CUSTOMER");
assertTrue(before > 0);
try {
super.testLaunchJob();
} catch (HibernateJdbcException e) {
@@ -62,7 +60,7 @@ public class HibernateFailureJobFunctionalTests extends AbstractCustomerCreditIn
throw e;
}
int after = simpleJdbcTemplate.queryForInt("SELECT COUNT(*) from CUSTOMER");
assertEquals(before, after);
assertEquals(4, after);
}
/*

View File

@@ -1,105 +0,0 @@
package org.springframework.batch.sample;
import static org.junit.Assert.*;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.JobParametersNotFoundException;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.jdbc.SimpleJdbcTestUtils;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/incrementer-job-launcher-context.xml" })
public class IncrementerJobFunctionalTests {
private SimpleJdbcTemplate simpleJdbcTemplate;
@Autowired
private JobOperator jobOperator;
/**
* This test calls the same job twice. However, using a job incrementer, the
* second launching is a separate job instance.<br>
* <br>
* Conditions:
* <ul>
* <li>Two flat files, each containing 20 player records
* <li>Job is started twice, using the job incrementer to chose the input
* file.
* </ul>
* Expected Results:
* <ul>
* <li>First run completes with 20 players in the database
* <li>Second run completes with 40 players in the database.
* </ul>
*/
@Test
public void testWithSkips() throws Exception {
simpleJdbcTemplate.update("DELETE from PLAYERS");
long id1 = this.launchJob();
Map<String, Object> execution1 = this.getJobExecution(id1);
assertEquals("COMPLETED", execution1.get("STATUS"));
assertEquals(20, this.countPlayers());
long id2 = this.launchJob();
Map<String, Object> execution2 = this.getJobExecution(id2);
assertEquals("COMPLETED", execution2.get("STATUS"));
assertEquals(40, this.countPlayers());
assertTrue(id1 != id2);
assertTrue(!execution1.get("JOB_INSTANCE_ID").equals(execution2.get("JOB_INSTANCE_ID")));
}
private Map<String, Object> getJobExecution(long jobExecutionId) {
return simpleJdbcTemplate.queryForMap("SELECT * from BATCH_JOB_EXECUTION where JOB_EXECUTION_ID = ?",
jobExecutionId);
}
private int countPlayers() {
return SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "PLAYERS");
}
/**
* Launch the entire job, including all steps, in order.
*
* @return JobExecution, so that the test may validate the exit status
*/
public long launchJob() {
try {
return this.jobOperator.startNextInstance("incrementerJob");
}
catch (NoSuchJobException e) {
throw new RuntimeException(e);
}
catch (JobExecutionAlreadyRunningException e) {
throw new RuntimeException(e);
}
catch (JobParametersNotFoundException e) {
throw new RuntimeException(e);
}
catch (JobRestartException e) {
throw new RuntimeException(e);
}
catch (JobInstanceAlreadyCompleteException e) {
throw new RuntimeException(e);
}
}
@Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
}

View File

@@ -1,26 +0,0 @@
package org.springframework.batch.sample;
import static org.junit.Assert.assertEquals;
import org.junit.runner.RunWith;
import org.springframework.batch.sample.tasklet.DummyMessageReceivingTasklet;
import org.springframework.batch.sample.tasklet.DummyMessageSendingTasklet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration()
public class JobExecutionContextSampleFunctionalTests extends AbstractValidatingBatchLauncherTests {
@Autowired
private DummyMessageSendingTasklet sender;
@Autowired
private DummyMessageReceivingTasklet receiver;
protected void validatePostConditions() throws Exception {
assertEquals(sender.getMessage(), receiver.getReceivedMessage());
}
}

View File

@@ -1,50 +1,113 @@
package org.springframework.batch.sample;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration()
public class MultiResourceJobFunctionalTests extends FixedLengthImportJobFunctionalTests {
/**
* Context: 5 items overall, min. 2 items per output file, commitInterval=3,
* => two files created, with 3 items in the first and two in second.
*/
@Override
protected void validatePostConditions() throws Exception {
File file1 = new File("target/test-outputs/multiResourceOutput.txt.1");
File file2 = new File("target/test-outputs/multiResourceOutput.txt.2");
assertTrue(file1.exists());
assertTrue(file2.exists());
BufferedReader reader1 = new BufferedReader(new FileReader(file1));
for (int i = 1; i <= 3; i++) {
assertEquals(itemReader.read().toString(), reader1.readLine());
}
assertNull(reader1.readLine());
BufferedReader reader2 = new BufferedReader(new FileReader(file2));
for (int i = 1; i <= 2; i++) {
assertEquals(itemReader.read().toString(), reader2.readLine());
}
assertNull(reader2.readLine());
}
@Autowired
public void setJob(@Qualifier("multiResourceJob") Job job) {
super.setJob(job);
}
}
package org.springframework.batch.sample;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import javax.sql.DataSource;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.batch.sample.domain.trade.Trade;
import org.springframework.batch.sample.domain.trade.internal.TradeFieldSetMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration()
public class MultiResourceJobFunctionalTests extends AbstractValidatingBatchLauncherTests {
// expected line length in input file (sum of pattern lengths + 2, because
// the counter is appended twice)
private static final int LINE_LENGTH = 29;
// auto-injected attributes
private SimpleJdbcTemplate simpleJdbcTemplate;
private Resource fileLocator;
protected FlatFileItemReader<Trade> itemReader;
private LineTokenizer lineTokenizer;
@Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Autowired
public void setLineTokenizer(LineTokenizer lineTokenizer) {
this.lineTokenizer = lineTokenizer;
}
@Before
public void onSetUp() throws Exception {
simpleJdbcTemplate.update("delete from TRADE");
fileLocator = new ClassPathResource(
"data/multiResourceJob/input/20070122.teststream.ImportTradeDataStep.txt");
itemReader = new FlatFileItemReader<Trade>();
FieldSetMapper<Trade> mapper = new TradeFieldSetMapper();
DefaultLineMapper<Trade> lineMapper = new DefaultLineMapper<Trade>();
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(mapper);
itemReader.setLineMapper(lineMapper);
itemReader.setResource(fileLocator);
itemReader.open(new ExecutionContext());
}
/*
* fixed-length file is expected on input
*/
protected void validatePreConditions() throws Exception {
BufferedReader reader;
reader = new BufferedReader(new FileReader(fileLocator.getFile()));
String line;
while ((line = reader.readLine()) != null) {
assertEquals(LINE_LENGTH, line.length());
}
}
/**
* Context: 5 items overall, min. 2 items per output file, commitInterval=3, =>
* two files created, with 3 items in the first and two in second.
*/
@Override
protected void validatePostConditions() throws Exception {
File file1 = new File("target/test-outputs/multiResourceOutput.txt.1");
File file2 = new File("target/test-outputs/multiResourceOutput.txt.2");
assertTrue(file1.exists());
assertTrue(file2.exists());
BufferedReader reader1 = new BufferedReader(new FileReader(file1));
for (int i = 1; i <= 3; i++) {
assertEquals(itemReader.read().toString(), reader1.readLine());
}
assertNull(reader1.readLine());
BufferedReader reader2 = new BufferedReader(new FileReader(file2));
for (int i = 1; i <= 2; i++) {
assertEquals(itemReader.read().toString(), reader2.readLine());
}
assertNull(reader2.readLine());
}
@Autowired
public void setJob(@Qualifier("multiResourceJob")
Job job) {
super.setJob(job);
}
}

View File

@@ -1,10 +0,0 @@
package org.springframework.batch.sample;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/nonSequentialDecisionJob.xml" })
public class NonSequentialDecisionJobFunctionalTests extends NonSequentialJobFunctionalTestsBase {
}

View File

@@ -1,10 +0,0 @@
package org.springframework.batch.sample;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/nonSequentialJob.xml" })
public class NonSequentialJobFunctionalTests extends NonSequentialJobFunctionalTestsBase {
}

View File

@@ -1,97 +0,0 @@
package org.springframework.batch.sample;
import static org.junit.Assert.assertEquals;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.test.AbstractJobTests;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.jdbc.SimpleJdbcTestUtils;
public abstract class NonSequentialJobFunctionalTestsBase extends AbstractJobTests {
private SimpleJdbcTemplate simpleJdbcTemplate;
@Before
public void removeOldData() throws Exception {
simpleJdbcTemplate.update("DELETE FROM PLAYERS");
simpleJdbcTemplate.update("DELETE FROM GAMES");
simpleJdbcTemplate.update("DELETE FROM PLAYER_SUMMARY");
}
/**
* This test processes a file that contains bad records. Those records will
* skip. The step execution listener will detect that skips have occurred,
* and return an exit status that directs the flow job to the error logging
* step. The error logging step will log an error. <br>
* <br>
* Conditions:
* <ul>
* <li>Flat file containing 20 player records, 5 are invalid
* <li>Skipping is allowed
* </ul>
* Expected Results:
* <ul>
* <li>15 player records written to the database
* <li>1 error logged to the database
* </ul>
*/
@Test
public void testWithSkips() throws Exception {
launchTest("player-containsBadRecords.csv");
assertEquals(1, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "ERROR_LOG"));
assertEquals(15, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "PLAYERS"));
}
/**
* This test processes a file that contains all valid record. The step
* execution listener will detect that NO skips have occurred, and return an
* exit status that direct the flow job to bypass the error logging step.<br>
* <br>
* Conditions:
* <ul>
* <li>Flat file containing 20 player records, all are valid
* <li>Skipping is allowed
* </ul>
* Expected Results:
* <ul>
* <li>20 player records written to the database
* <li>NO errors logged to the database
* </ul>
*/
@Test
public void testWithoutSkips() throws Exception {
launchTest("player-small1.csv");
assertEquals(0, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "ERROR_LOG"));
assertEquals(20, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "PLAYERS"));
}
private void launchTest(String playerInputfile) throws Exception {
simpleJdbcTemplate.update("DELETE from ERROR_LOG");
simpleJdbcTemplate.update("DELETE from PLAYER_SUMMARY");
simpleJdbcTemplate.update("DELETE from PLAYERS");
simpleJdbcTemplate.update("DELETE from GAMES");
Map<String, JobParameter> parameters = new HashMap<String, JobParameter>();
parameters.put("timestamp", new JobParameter(new Date().getTime()));
parameters.put("player.file.name", new JobParameter(playerInputfile));
JobParameters jobParameters = new JobParameters(parameters);
assertEquals(BatchStatus.COMPLETED, this.launchJob(jobParameters).getStatus());
}
@Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
}

View File

@@ -1,33 +1,46 @@
package org.springframework.batch.sample;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.sample.support.ItemTrackingItemWriter;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.JobParametersNotFoundException;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.sample.domain.trade.internal.ItemTrackingTradeItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.jdbc.SimpleJdbcTestUtils;
/**
* Error is encountered during writing - transaction is rolled back and the
* error item is skipped on second attempt to process the chunk.
*
* @author Robert Kasanicky
* @author Dan Garrette
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration()
public class SkipSampleFunctionalTests extends AbstractValidatingBatchLauncherTests {
@ContextConfiguration(locations = { "/skipSample-job-launcher-context.xml" })
public class SkipSampleFunctionalTests {
int before = -1;
SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcTemplate simpleJdbcTemplate;
@Autowired
ItemTrackingItemWriter<?> writer;
private JobOperator jobOperator;
@Autowired
private ItemTrackingTradeItemWriter itemTrackingWriter;
@Autowired
public void setDataSource(DataSource dataSource) {
@@ -35,18 +48,177 @@ public class SkipSampleFunctionalTests extends AbstractValidatingBatchLauncherTe
}
@Before
public void onSetUp() throws Exception {
before = simpleJdbcTemplate.queryForInt("SELECT COUNT(*) from TRADE");
public void setUp() {
simpleJdbcTemplate.update("DELETE from TRADE");
simpleJdbcTemplate.update("DELETE from CUSTOMER");
for (int i = 1; i < 10; i++) {
simpleJdbcTemplate.update("INSERT INTO CUSTOMER VALUES (" + i + ", 0, 'customer" + i + "', 100000)");
}
simpleJdbcTemplate.update("DELETE from ERROR_LOG");
itemTrackingWriter.clearItems();
itemTrackingWriter.setWriteFailureISIN("UK21341EAH47");
}
protected void validatePostConditions() throws Exception {
/**
* LAUNCH 1 <br>
* <br>
* step1
* <ul>
* <li>The step name is saved to the job execution context.
* <li>Read five records from flat file and insert them into the TRADE
* table.
* <li>One record will be invalid, and it will be skipped. Four records
* will be written to the database.
* <li>The skip will result in an exit status that directs the job to run
* the error logging step.
* </ul>
* errorPrint1
* <ul>
* <li>The error logging step will log one record using the step name from
* the job execution context.
* </ul>
* step2
* <ul>
* <li>The step name is saved to the job execution context.
* <li>Read four records from the TRADE table and processes them.
* <li>One record will be invalid, and it will be skipped. Three records
* will be stored in the writer's "items" property.
* <li>The skip will result in an exit status that directs the job to run
* the error logging step.
* </ul>
* errorPrint2
* <ul>
* <li>The error logging step will log one record using the step name from
* the job execution context.
* </ul>
* <br>
* <br>
* LAUNCH 2 <br>
* <br>
* step1
* <ul>
* <li>The step name is saved to the job execution context.
* <li>Read five records from flat file and insert them into the TRADE
* table.
* <li>No skips will occur.
* <li>The exist status of SUCCESS will direct the job to step2.
* </ul>
* errorPrint1
* <ul>
* <li>This step does not occur. No error records are logged.
* </ul>
* step2
* <ul>
* <li>The step name is saved to the job execution context.
* <li>Read five records from the TRADE table and processes them.
* <li>No skips will occur.
* <li>The exist status of SUCCESS will direct the job to end.
* </ul>
* errorPrint2
* <ul>
* <li>This step does not occur. No error records are logged.
* </ul>
*/
@Test
public void testJobIncrementing() {
//
// Launch 1
//
long id1 = this.launchJobWithIncrementer();
Map<String, Object> execution1 = this.getJobExecution(id1);
assertEquals("COMPLETED", execution1.get("STATUS"));
int after = simpleJdbcTemplate.queryForInt("SELECT COUNT(*) from TRADE");
// 5 input records, 1 skipped => 4 written to output
assertEquals(before + 4, after);
this.validateLaunchWithSkips();
//
// Clear the data
//
setUp();
//
// Launch 2
//
long id2 = this.launchJobWithIncrementer();
Map<String, Object> execution2 = this.getJobExecution(id2);
assertEquals("COMPLETED", execution2.get("STATUS"));
this.validateLaunchWithoutSkips();
//
// Make sure that the launches were separate executions and separate
// instances
//
assertTrue(id1 != id2);
assertTrue(!execution1.get("JOB_INSTANCE_ID").equals(execution2.get("JOB_INSTANCE_ID")));
}
/**
*
*/
private void validateLaunchWithSkips() {
// Step1: 5 input records, 1 skipped => 4 written to output
assertEquals(4, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "TRADE"));
// Step2: 4 input records, 1 skipped => 3 written to output
assertEquals(3, itemTrackingWriter.getItems().size());
for(Object o : simpleJdbcTemplate.queryForList(
"SELECT * from ERROR_LOG"))
{
System.err.println("DHG > "+o);
}
// no item was processed twice (one rollback occurred due to validation error)
assertEquals(after - 1, writer.getItems().size());
// Both steps contained skips
assertEquals(2, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "ERROR_LOG"));
assertEquals(1, simpleJdbcTemplate.queryForInt(
"SELECT Count(*) from ERROR_LOG where JOB_NAME = ? and STEP_NAME = ?", "skipJob", "step1"));
assertEquals(1, simpleJdbcTemplate.queryForInt(
"SELECT Count(*) from ERROR_LOG where JOB_NAME = ? and STEP_NAME = ?", "skipJob", "step2"));
}
/**
*
*/
private void validateLaunchWithoutSkips() {
// Step1: 5 input records => 5 written to output
assertEquals(5, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "TRADE"));
// Step2: 5 input records => 5 written to output
assertEquals(5, itemTrackingWriter.getItems().size());
// Neither step contained skips
assertEquals(0, SimpleJdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "ERROR_LOG"));
}
private Map<String, Object> getJobExecution(long jobExecutionId) {
return simpleJdbcTemplate.queryForMap("SELECT * from BATCH_JOB_EXECUTION where JOB_EXECUTION_ID = ?",
jobExecutionId);
}
/**
* Launch the entire job, including all steps, in order.
*
* @return JobExecution, so that the test may validate the exit status
*/
public long launchJobWithIncrementer() {
try {
return this.jobOperator.startNextInstance("skipJob");
}
catch (NoSuchJobException e) {
throw new RuntimeException(e);
}
catch (JobExecutionAlreadyRunningException e) {
throw new RuntimeException(e);
}
catch (JobParametersNotFoundException e) {
throw new RuntimeException(e);
}
catch (JobRestartException e) {
throw new RuntimeException(e);
}
catch (JobInstanceAlreadyCompleteException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,55 +0,0 @@
package org.springframework.batch.sample;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Deletes files in the given directory.
*
* @author Robert Kasanicky
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration()
public class TaskletJobFunctionalTests extends AbstractValidatingBatchLauncherTests {
private static Resource directory = new FileSystemResource("target/test-outputs/test-dir");
/*
* Create the directory and some files in it.
*/
@BeforeClass
public static void onSetUp() throws Exception {
File dir = directory.getFile();
dir.mkdirs();
new File(dir, "file1").createNewFile();
new File(dir, "file2").createNewFile();
}
/**
* We have directory with some files in it.
*/
@Override
protected void validatePreConditions() throws Exception {
assertTrue(directory.getFile().isDirectory());
assertTrue(directory.getFile().listFiles().length > 0);
}
/**
* Directory still exists but contains no files.
*/
@Override
protected void validatePostConditions() throws Exception {
assertTrue(directory.getFile().isDirectory());
assertEquals(0, directory.getFile().listFiles().length);
}
}

View File

@@ -0,0 +1,43 @@
package org.springframework.batch.sample.common;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.core.AttributeAccessor;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
public class ErrorLogTasklet implements Tasklet, StepExecutionListener {
protected final Log logger = LogFactory.getLog(getClass());
private SimpleJdbcTemplate simpleJdbcTemplate;
private String jobName;
private String stepName;
public RepeatStatus execute(StepContribution contribution, AttributeAccessor attributes) throws Exception {
this.simpleJdbcTemplate.update("insert into ERROR_LOG values ('"+jobName+"', '"+stepName+"', 'Some records were skipped!')");
return RepeatStatus.FINISHED;
}
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public ExitStatus afterStep(StepExecution stepExecution) {
// TODO Auto-generated method stub
return null;
}
public void beforeStep(StepExecution stepExecution) {
this.jobName = stepExecution.getJobExecution().getJobInstance().getJobName().trim();
this.stepName = (String)stepExecution.getJobExecution().getExecutionContext().get("stepName");
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.batch.sample.common;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.flow.support.state.JobExecutionDecider;
public class SkipCheckingDecider implements JobExecutionDecider {
public String decide(JobExecution jobExecution, StepExecution stepExecution) {
if (!stepExecution.getExitStatus().getExitCode().equals(
ExitStatus.FAILED.getExitCode())
&& stepExecution.getSkipCount() > 0) {
return "COMPLETED WITH SKIPS";
} else {
return ExitStatus.FINISHED.getExitCode();
}
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.batch.sample.common;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
public class SkipCheckingListener implements StepExecutionListener {
public ExitStatus afterStep(StepExecution stepExecution) {
if (!stepExecution.getExitStatus().getExitCode().equals(ExitStatus.FAILED.getExitCode())
&& stepExecution.getSkipCount() > 0) {
return new ExitStatus("COMPLETED WITH SKIPS");
} else {
return null;
}
}
public void beforeStep(StepExecution stepExecution) {
stepExecution.getJobExecution().getExecutionContext().put("stepName", stepExecution.getStepName());
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.batch.sample.domain.trade;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import org.junit.Test;
public class TradeTests {
@Test
public void testEquality(){
Trade trade1 = new Trade("isin", 1, new BigDecimal(1.1), "customer1");
Trade trade1Clone = new Trade("isin", 1, new BigDecimal(1.1), "customer1");
Trade trade2 = new Trade("isin", 1, new BigDecimal(2.3), "customer2");
assertEquals(trade1, trade1Clone);
assertFalse(trade1.equals(trade2));
}
}

View File

@@ -0,0 +1,39 @@
package org.springframework.batch.sample.domain.trade.internal;
import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.sample.domain.trade.Trade;
public class ItemTrackingTradeItemWriter implements ItemWriter<Trade> {
private List<Trade> items = new ArrayList<Trade>();
private String writeFailureISIN;
public void setWriteFailureISIN(String writeFailureISIN) {
this.writeFailureISIN = writeFailureISIN;
}
public void setItems(List<Trade> items) {
this.items = items;
}
public List<Trade> getItems() {
return items;
}
public void clearItems(){
this.items.clear();
}
public void write(List<? extends Trade> items) throws Exception {
List<Trade> newItems = new ArrayList<Trade>();
for(Trade t : items){
if (t.getIsin().equals(this.writeFailureISIN)){
throw new RuntimeException("write failed");
}
newItems.add(t);
}
this.items.addAll(newItems);
}
}

View File

@@ -21,49 +21,53 @@ import static org.junit.Assert.fail;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.batch.sample.domain.trade.Trade;
import org.springframework.batch.sample.domain.trade.internal.ItemTrackingTradeItemWriter;
/**
* @author Dave Syer
*
*
*/
public class ItemTrackingItemWriterTests {
private ItemTrackingItemWriter<String> writer = new ItemTrackingItemWriter<String>();
private ItemTrackingTradeItemWriter writer = new ItemTrackingTradeItemWriter();
/**
* Test method for {@link org.springframework.batch.sample.support.ItemTrackingItemWriter#write(java.util.List)}.
* @throws Exception
* Test method for
* {@link org.springframework.batch.sample.domain.trade.internal.ItemTrackingTradeItemWriter#write(java.util.List)}.
*
* @throws Exception
*/
@Test
public void testWrite() throws Exception {
assertEquals(0, writer.getItems().size());
writer.write(Arrays.asList("a", "b", "c"));
Trade a = new Trade("a", 0, null, null);
Trade b = new Trade("b", 0, null, null);
Trade c = new Trade("c", 0, null, null);
writer.write(Arrays.asList(a, b, c));
assertEquals(3, writer.getItems().size());
}
@Test
public void testWriteFailure() throws Exception {
writer.setWriteFailure(2);
writer.setWriteFailureISIN("c");
try {
writer.write(Arrays.asList("a", "b", "c"));
Trade a = new Trade("a", 0, null, null);
Trade b = new Trade("b", 0, null, null);
Trade c = new Trade("c", 0, null, null);
writer.write(Arrays.asList(a, b, c));
fail("Expected Write Failure Exception");
}
catch (RuntimeException e) {
// expected
}
// the failed item is removed
assertEquals(2, writer.getItems().size());
writer.write(Arrays.asList("a", "e", "c"));
assertEquals(5, writer.getItems().size());
try {
writer.write(Arrays.asList("f", "b", "g"));
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// expected
}
// barf immediately if a failure is detected
assertEquals(5, writer.getItems().size());
}
assertEquals(0, writer.getItems().size());
Trade e = new Trade("e", 0, null, null);
Trade f = new Trade("f", 0, null, null);
Trade g = new Trade("g", 0, null, null);
writer.write(Arrays.asList(e, f, g));
assertEquals(3, writer.getItems().size());
}
}

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.0.xsd">
<import resource="classpath:/simple-job-launcher-context.xml" />
<import resource="classpath:/jobs/fixedLengthImportJob.xml" />
</beans>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.0.xsd">
<import resource="classpath:/simple-job-launcher-context.xml" />
<import resource="classpath:/jobs/jobExecutionContextSample.xml" />
</beans>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.0.xsd">
<import resource="classpath:/simple-job-launcher-context.xml" />
<import resource="classpath:/jobs/skipSampleJob.xml" />
</beans>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.0.xsd">
<import resource="classpath:/simple-job-launcher-context.xml" />
<import resource="classpath:/jobs/taskletJob.xml" />
</beans>