RESOLVED - issue BATCH-1541, BATCH-1542: Thread safety for map daos
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
#Sat Mar 13 13:37:06 GMT 2010
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core-tests/src/test/resources/org/springframework/batch/core/test/step/SplitJobMapRepositoryIntegrationTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element type\="job">\n<structure end\="709" endstart\="703" start\="456" startend\="522"/>\n<bounds height\="136" width\="78" x\="93" y\="19"/>\n</element>\n<element type\="step">\n<structure end\="1133" endstart\="1126" start\="910" startend\="978"/>\n<bounds height\="34" width\="58" x\="17" y\="19"/>\n</element>\n<element type\="split">\n<structure end\="701" endstart\="693" start\="525" startend\="572"/>\n<bounds height\="189" width\="197" x\="183" y\="121"/>\n</element>\n</graph>
|
||||
eclipse.preferences.version=1
|
||||
@@ -1,239 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beansProjectDescription>
|
||||
<version>1</version>
|
||||
<pluginVersion><![CDATA[2.2.8.200911091054-RELEASE]]></pluginVersion>
|
||||
<configSuffixes>
|
||||
<configSuffix><![CDATA[xml]]></configSuffix>
|
||||
</configSuffixes>
|
||||
<enableImports><![CDATA[false]]></enableImports>
|
||||
<configs>
|
||||
<config>src/test/resources/data-source-context.xml</config>
|
||||
<config>src/test/resources/simple-job-launcher-context.xml</config>
|
||||
</configs>
|
||||
<configSets>
|
||||
<configSet>
|
||||
<name><![CDATA[repositoryTest]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[beanWrapper]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[delegating]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[football]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[infiniteLoop]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[multiline]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[parallel]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[restart]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[retry]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[trade]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[batchUpdate]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[quartzLauncher]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[compositeItemWriter]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[adhocLoop]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[staging]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[common]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[jmxLauncher]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[headerFooter]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[customerFilterJob]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[hibernate]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[multiResourceJob]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[skipSample]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-hibernate]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-jdbc]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-multiline]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-multiRecordType]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-jpa]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[data-source-contex]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[ibatis]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[partition]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[multilineOrder]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[restartFile]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
</configSets>
|
||||
</beansProjectDescription>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beansProjectDescription>
|
||||
<version>1</version>
|
||||
<pluginVersion><![CDATA[2.3.0.200912170948-RELEASE]]></pluginVersion>
|
||||
<configSuffixes>
|
||||
<configSuffix><![CDATA[xml]]></configSuffix>
|
||||
</configSuffixes>
|
||||
<enableImports><![CDATA[false]]></enableImports>
|
||||
<configs>
|
||||
<config>src/test/resources/data-source-context.xml</config>
|
||||
<config>src/test/resources/simple-job-launcher-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/batch/core/test/step/SplitJobMapRepositoryIntegrationTests-context.xml</config>
|
||||
</configs>
|
||||
<configSets>
|
||||
<configSet>
|
||||
<name><![CDATA[repositoryTest]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[beanWrapper]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[delegating]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[football]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[infiniteLoop]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[multiline]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[parallel]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[restart]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[retry]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[trade]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[batchUpdate]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[quartzLauncher]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[compositeItemWriter]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[adhocLoop]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[staging]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[common]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[jmxLauncher]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[headerFooter]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[customerFilterJob]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[hibernate]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[multiResourceJob]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[skipSample]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-hibernate]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-jdbc]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-multiline]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-multiRecordType]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[iosample-jpa]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[data-source-contex]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[ibatis]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[partition]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[multilineOrder]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
<configSet>
|
||||
<name><![CDATA[restartFile]]></name>
|
||||
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
|
||||
<incomplete>false</incomplete>
|
||||
<configs>
|
||||
</configs>
|
||||
</configSet>
|
||||
</configSets>
|
||||
</beansProjectDescription>
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -121,6 +122,10 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
|
||||
if (i%100==0) {
|
||||
logger.info("Starting step: "+i);
|
||||
}
|
||||
|
||||
SimpleJdbcTemplate jdbcTemplate = new SimpleJdbcTemplate(dataSource);
|
||||
assertEquals(0, SimpleJdbcTestUtils.countRowsInTable(jdbcTemplate, "ERROR_LOG"));
|
||||
|
||||
@@ -145,6 +150,9 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
assertEquals("[]", writer.getCommitted().toString());
|
||||
assertEquals("[]", processor.getCommitted().toString());
|
||||
List<String> processed = new ArrayList<String>(processor.getProcessed());
|
||||
Collections.sort(processed);
|
||||
assertEquals("[1, 1, 2, 2, 3, 3, 4, 4, 5, 5]", processed.toString());
|
||||
assertEquals(5, stepExecution.getSkipCount());
|
||||
|
||||
}
|
||||
@@ -196,7 +204,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
private static class SkipWriterStub implements ItemWriter<String> {
|
||||
|
||||
private List<String> written = new ArrayList<String>();
|
||||
private List<String> written = new CopyOnWriteArrayList<String>();
|
||||
|
||||
private Collection<String> failures = Collections.emptySet();
|
||||
|
||||
@@ -243,7 +251,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private List<String> processed = new ArrayList<String>();
|
||||
private List<String> processed = new CopyOnWriteArrayList<String>();
|
||||
|
||||
private SimpleJdbcTemplate jdbcTemplate;
|
||||
|
||||
@@ -253,6 +261,13 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
|
||||
public SkipProcessorStub(DataSource dataSource) {
|
||||
jdbcTemplate = new SimpleJdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the processed
|
||||
*/
|
||||
public List<String> getProcessed() {
|
||||
return processed;
|
||||
}
|
||||
|
||||
public List<String> getCommitted() {
|
||||
return jdbcTemplate.query("SELECT MESSAGE from ERROR_LOG where STEP_NAME='processed'",
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
package org.springframework.batch.core.test.step;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.ParseException;
|
||||
import org.springframework.batch.item.UnexpectedInputException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Tests for {@link FaultTolerantStepFactoryBean}.
|
||||
*/
|
||||
@ContextConfiguration(locations = "/simple-job-launcher-context.xml")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class MapRepositoryFaultTolerantStepFactoryBeanRollbackTests {
|
||||
|
||||
private static final int MAX_COUNT = 1000;
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private FaultTolerantStepFactoryBean<String, String> factory;
|
||||
|
||||
private SkipReaderStub reader;
|
||||
|
||||
private SkipProcessorStub processor;
|
||||
|
||||
private SkipWriterStub writer;
|
||||
|
||||
private JobExecution jobExecution;
|
||||
|
||||
private StepExecution stepExecution;
|
||||
|
||||
private JobRepository repository;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
repository = new MapJobRepositoryFactoryBean().getJobRepository();
|
||||
|
||||
reader = new SkipReaderStub();
|
||||
writer = new SkipWriterStub();
|
||||
processor = new SkipProcessorStub();
|
||||
|
||||
factory = new FaultTolerantStepFactoryBean<String, String>();
|
||||
|
||||
factory.setBeanName("stepName");
|
||||
factory.setTransactionManager(transactionManager);
|
||||
factory.setJobRepository(repository);
|
||||
factory.setCommitInterval(3);
|
||||
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
|
||||
taskExecutor.setCorePoolSize(3);
|
||||
taskExecutor.setMaxPoolSize(6);
|
||||
taskExecutor.setQueueCapacity(0);
|
||||
taskExecutor.afterPropertiesSet();
|
||||
factory.setTaskExecutor(taskExecutor);
|
||||
|
||||
factory.setSkipLimit(10);
|
||||
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesNoRollback() throws Exception {
|
||||
|
||||
writer.write(Arrays.asList("foo", "bar"));
|
||||
processor.process("spam");
|
||||
assertEquals(2, writer.getWritten().size());
|
||||
assertEquals(1, processor.getProcessed().size());
|
||||
|
||||
writer.clear();
|
||||
processor.clear();
|
||||
assertEquals(0, processor.getProcessed().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultithreadedSkipInWrite() throws Throwable {
|
||||
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
|
||||
if (i%100==0) {
|
||||
logger.info("Starting step: "+i);
|
||||
repository = new MapJobRepositoryFactoryBean().getJobRepository();
|
||||
factory.setJobRepository(repository);
|
||||
jobExecution = repository.createJobExecution("vanillaJob", new JobParameters());
|
||||
}
|
||||
|
||||
reader.clear();
|
||||
reader.setItems("1", "2", "3", "4", "5");
|
||||
factory.setItemReader(reader);
|
||||
writer.clear();
|
||||
factory.setItemWriter(writer);
|
||||
processor.clear();
|
||||
factory.setItemProcessor(processor);
|
||||
|
||||
writer.setFailures("1", "2", "3", "4", "5");
|
||||
|
||||
try {
|
||||
|
||||
Step step = (Step) factory.getObject();
|
||||
|
||||
stepExecution = jobExecution.createStepExecution(factory.getName());
|
||||
repository.add(stepExecution);
|
||||
step.execute(stepExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
|
||||
|
||||
assertEquals(5, stepExecution.getSkipCount());
|
||||
List<String> processed = new ArrayList<String>(processor.getProcessed());
|
||||
Collections.sort(processed);
|
||||
assertEquals("[1, 1, 2, 2, 3, 3, 4, 4, 5, 5]", processed.toString());
|
||||
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logger.info("Failed on iteration " + i + " of " + MAX_COUNT);
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SkipReaderStub implements ItemReader<String> {
|
||||
|
||||
private String[] items;
|
||||
|
||||
private int counter = -1;
|
||||
|
||||
public SkipReaderStub() throws Exception {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setItems(String... items) {
|
||||
Assert.isTrue(counter < 0, "Items cannot be set once reading has started");
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
counter = -1;
|
||||
}
|
||||
|
||||
public synchronized String read() throws Exception, UnexpectedInputException, ParseException {
|
||||
counter++;
|
||||
if (counter >= items.length) {
|
||||
return null;
|
||||
}
|
||||
String item = items[counter];
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SkipWriterStub implements ItemWriter<String> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private List<String> written = new CopyOnWriteArrayList<String>();
|
||||
|
||||
private Collection<String> failures = Collections.emptySet();
|
||||
|
||||
public void setFailures(String... failures) {
|
||||
this.failures = Arrays.asList(failures);
|
||||
}
|
||||
|
||||
public List<String> getWritten() {
|
||||
return written;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
written.clear();
|
||||
}
|
||||
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
for (String item : items) {
|
||||
logger.trace("Writing: "+item);
|
||||
written.add(item);
|
||||
checkFailure(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFailure(String item) {
|
||||
if (failures.contains(item)) {
|
||||
throw new RuntimeException("Planned failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SkipProcessorStub implements ItemProcessor<String, String> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private List<String> processed = new CopyOnWriteArrayList<String>();
|
||||
|
||||
public List<String> getProcessed() {
|
||||
return processed;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
processed.clear();
|
||||
}
|
||||
|
||||
public String process(String item) throws Exception {
|
||||
processed.add(item);
|
||||
logger.debug("Processed item: "+item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Class<? extends Throwable>, Boolean> getExceptionMap(Class<? extends Throwable>... args) {
|
||||
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
|
||||
for (Class<? extends Throwable> arg : args) {
|
||||
map.put(arg, true);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
package org.springframework.batch.core.test.step;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.ParseException;
|
||||
import org.springframework.batch.item.UnexpectedInputException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Tests for {@link FaultTolerantStepFactoryBean}.
|
||||
*/
|
||||
@ContextConfiguration(locations = "/simple-job-launcher-context.xml")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class MapRepositoryFaultTolerantStepFactoryBeanTests {
|
||||
|
||||
private static final int MAX_COUNT = 1000;
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private FaultTolerantStepFactoryBean<String, String> factory;
|
||||
|
||||
private SkipReaderStub reader;
|
||||
|
||||
private SkipProcessorStub processor;
|
||||
|
||||
private SkipWriterStub writer;
|
||||
|
||||
private JobExecution jobExecution;
|
||||
|
||||
private StepExecution stepExecution;
|
||||
|
||||
private JobRepository repository;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
repository = new MapJobRepositoryFactoryBean().getJobRepository();
|
||||
|
||||
reader = new SkipReaderStub();
|
||||
writer = new SkipWriterStub();
|
||||
processor = new SkipProcessorStub();
|
||||
|
||||
factory = new FaultTolerantStepFactoryBean<String, String>();
|
||||
|
||||
factory.setBeanName("stepName");
|
||||
factory.setTransactionManager(transactionManager);
|
||||
factory.setJobRepository(repository);
|
||||
factory.setCommitInterval(3);
|
||||
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
|
||||
taskExecutor.setCorePoolSize(3);
|
||||
taskExecutor.setMaxPoolSize(6);
|
||||
taskExecutor.setQueueCapacity(0);
|
||||
taskExecutor.afterPropertiesSet();
|
||||
factory.setTaskExecutor(taskExecutor);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesNoRollback() throws Exception {
|
||||
|
||||
writer.write(Arrays.asList("foo", "bar"));
|
||||
processor.process("spam");
|
||||
assertEquals(2, writer.getWritten().size());
|
||||
assertEquals(1, processor.getProcessed().size());
|
||||
|
||||
writer.clear();
|
||||
processor.clear();
|
||||
assertEquals(0, processor.getProcessed().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultithreadedSunnyDay() throws Throwable {
|
||||
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
|
||||
if (i%100==0) {
|
||||
logger.info("Starting step: "+i);
|
||||
repository = new MapJobRepositoryFactoryBean().getJobRepository();
|
||||
factory.setJobRepository(repository);
|
||||
jobExecution = repository.createJobExecution("vanillaJob", new JobParameters());
|
||||
}
|
||||
|
||||
reader.clear();
|
||||
reader.setItems("1", "2", "3", "4", "5");
|
||||
factory.setItemReader(reader);
|
||||
writer.clear();
|
||||
factory.setItemWriter(writer);
|
||||
processor.clear();
|
||||
factory.setItemProcessor(processor);
|
||||
|
||||
try {
|
||||
|
||||
Step step = (Step) factory.getObject();
|
||||
|
||||
stepExecution = jobExecution.createStepExecution(factory.getName());
|
||||
repository.add(stepExecution);
|
||||
step.execute(stepExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
|
||||
|
||||
List<String> committed = new ArrayList<String>(writer.getWritten());
|
||||
Collections.sort(committed);
|
||||
assertEquals("[1, 2, 3, 4, 5]", committed.toString());
|
||||
List<String> processed = new ArrayList<String>(processor.getProcessed());
|
||||
Collections.sort(processed);
|
||||
assertEquals("[1, 2, 3, 4, 5]", processed.toString());
|
||||
assertEquals(0, stepExecution.getSkipCount());
|
||||
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logger.info("Failed on iteration " + i + " of " + MAX_COUNT);
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SkipReaderStub implements ItemReader<String> {
|
||||
|
||||
private String[] items;
|
||||
|
||||
private int counter = -1;
|
||||
|
||||
public SkipReaderStub() throws Exception {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setItems(String... items) {
|
||||
Assert.isTrue(counter < 0, "Items cannot be set once reading has started");
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
counter = -1;
|
||||
}
|
||||
|
||||
public synchronized String read() throws Exception, UnexpectedInputException, ParseException {
|
||||
counter++;
|
||||
if (counter >= items.length) {
|
||||
return null;
|
||||
}
|
||||
String item = items[counter];
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SkipWriterStub implements ItemWriter<String> {
|
||||
|
||||
private List<String> written = new CopyOnWriteArrayList<String>();
|
||||
|
||||
private Collection<String> failures = Collections.emptySet();
|
||||
|
||||
public List<String> getWritten() {
|
||||
return written;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
written.clear();
|
||||
}
|
||||
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
for (String item : items) {
|
||||
written.add(item);
|
||||
checkFailure(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFailure(String item) {
|
||||
if (failures.contains(item)) {
|
||||
throw new RuntimeException("Planned failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SkipProcessorStub implements ItemProcessor<String, String> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private List<String> processed = new CopyOnWriteArrayList<String>();
|
||||
|
||||
public List<String> getProcessed() {
|
||||
return processed;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
processed.clear();
|
||||
}
|
||||
|
||||
public String process(String item) throws Exception {
|
||||
processed.add(item);
|
||||
logger.debug("Processed item: "+item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2006-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.core.test.step;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SplitJobMapRepositoryIntegrationTests {
|
||||
|
||||
private static final int MAX_COUNT = 1000;
|
||||
|
||||
/** Logger */
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Test
|
||||
public void testMultithreadedSplit() throws Throwable {
|
||||
|
||||
JobLauncher jobLauncher = null;
|
||||
Job job = null;
|
||||
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
|
||||
if (i % 100 == 0) {
|
||||
logger.info("Starting job: " + i);
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(getClass().getSimpleName()
|
||||
+ "-context.xml", getClass());
|
||||
jobLauncher = (JobLauncher) context.getBean("jobLauncher", JobLauncher.class);
|
||||
job = (Job) context.getBean("job", Job.class);
|
||||
}
|
||||
|
||||
try {
|
||||
JobExecution execution = jobLauncher.run(job, new JobParametersBuilder().addLong("count", new Long(i))
|
||||
.toJobParameters());
|
||||
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logger.info("Failed on iteration " + i + " of " + MAX_COUNT);
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CountingTasklet implements Tasklet {
|
||||
|
||||
private int maxCount = 10;
|
||||
|
||||
private AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
contribution.incrementReadCount();
|
||||
contribution.incrementWriteCount(1);
|
||||
return RepeatStatus.continueIf(count.incrementAndGet() < maxCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2006-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.test.step;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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.StepExecution;
|
||||
import org.springframework.batch.support.SerializationUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepExecutionSerializationUtilsTests {
|
||||
|
||||
@Test
|
||||
public void testCycle() throws Exception {
|
||||
StepExecution stepExecution = new StepExecution("step", new JobExecution(new JobInstance(123L,
|
||||
new JobParameters(), "job"), 321L), 11L);
|
||||
stepExecution.getExecutionContext().put("foo.bar.spam", 123);
|
||||
StepExecution result = getCopy(stepExecution);
|
||||
assertEquals(stepExecution, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleCycles() throws Throwable {
|
||||
|
||||
int count = 0;
|
||||
int repeats = 100;
|
||||
int threads = 10;
|
||||
|
||||
Executor executor = Executors.newFixedThreadPool(threads);
|
||||
CompletionService<StepExecution> completionService = new ExecutorCompletionService<StepExecution>(executor);
|
||||
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
final JobExecution jobExecution = new JobExecution(new JobInstance(123L, new JobParameters(), "job"), 321L);
|
||||
for (int j = 0; j < threads; j++) {
|
||||
completionService.submit(new Callable<StepExecution>() {
|
||||
public StepExecution call() throws Exception {
|
||||
final StepExecution stepExecution = jobExecution.createStepExecution("step");
|
||||
stepExecution.getExecutionContext().put("foo.bar.spam", 123);
|
||||
StepExecution result = getCopy(stepExecution);
|
||||
assertEquals(stepExecution.getExecutionContext(), result.getExecutionContext());
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
for (int j = 0; j < threads; j++) {
|
||||
Future<StepExecution> future = completionService.poll(repeats, TimeUnit.MILLISECONDS);
|
||||
if (future != null) {
|
||||
count++;
|
||||
try {
|
||||
future.get();
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException("Failed on count="+count, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (count < threads*repeats) {
|
||||
Future<StepExecution> future = completionService.poll();
|
||||
count++;
|
||||
try {
|
||||
future.get();
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException("Failed on count="+count, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StepExecution getCopy(StepExecution stepExecution) {
|
||||
return (StepExecution) SerializationUtils.deserialize(SerializationUtils.serialize(stepExecution));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
log4j.rootCategory=INFO, stdout
|
||||
log4j.rootCategory=WARN, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
@@ -7,8 +7,9 @@ log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{1}:%L - %m
|
||||
log4j.category.org.apache.activemq=ERROR
|
||||
# log4j.category.org.springframework=DEBUG
|
||||
log4j.category.org.springframework.jdbc=INFO
|
||||
log4j.category.org.springframework.context=INFO
|
||||
log4j.category.org.springframework.jms=INFO
|
||||
log4j.category.org.springframework.batch=INFO
|
||||
# log4j.category.org.springframework.batch.core.test=DEBUG
|
||||
# log4j.category.org.springframework.batch=INFO
|
||||
log4j.category.org.springframework.batch.core.test=INFO
|
||||
log4j.category.org.springframework.retry=INFO
|
||||
# log4j.category.org.springframework.beans.factory.config=TRACE
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:beans="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.5.xsd
|
||||
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">
|
||||
|
||||
<job id="job" xmlns="http://www.springframework.org/schema/batch">
|
||||
<split id="split" task-executor="taskExecutor">
|
||||
<flow>
|
||||
<step id="step1" parent="step" />
|
||||
</flow>
|
||||
<flow>
|
||||
<step id="step2" parent="step" />
|
||||
</flow>
|
||||
</split>
|
||||
</job>
|
||||
|
||||
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
|
||||
<property name="maxPoolSize" value="6"/>
|
||||
<property name="queueCapacity" value="0"/>
|
||||
</bean>
|
||||
|
||||
<step id="step" xmlns="http://www.springframework.org/schema/batch">
|
||||
<tasklet>
|
||||
<beans:bean class="org.springframework.batch.core.test.step.SplitJobMapRepositoryIntegrationTests$CountingTasklet" />
|
||||
</tasklet>
|
||||
</step>
|
||||
|
||||
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
</bean>
|
||||
|
||||
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
|
||||
<property name="transactionManager" ref="transactionManager" />
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,3 @@
|
||||
#Wed Mar 24 07:48:49 GMT 2010
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobRepositoryDefaultParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph/>
|
||||
eclipse.preferences.version=1
|
||||
@@ -20,11 +20,13 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
@@ -38,7 +40,7 @@ public class JobExecution extends Entity {
|
||||
|
||||
private JobInstance jobInstance;
|
||||
|
||||
private volatile Collection<StepExecution> stepExecutions = new LinkedHashSet<StepExecution>();
|
||||
private volatile Collection<StepExecution> stepExecutions = new CopyOnWriteArraySet<StepExecution>();
|
||||
|
||||
private volatile BatchStatus status = BatchStatus.STARTING;
|
||||
|
||||
@@ -54,7 +56,7 @@ public class JobExecution extends Entity {
|
||||
|
||||
private volatile ExecutionContext executionContext = new ExecutionContext();
|
||||
|
||||
private transient volatile List<Throwable> failureExceptions = new ArrayList<Throwable>();
|
||||
private transient volatile List<Throwable> failureExceptions = new CopyOnWriteArrayList<Throwable>();
|
||||
|
||||
/**
|
||||
* Because a JobExecution isn't valid unless the job is set, this
|
||||
@@ -164,7 +166,7 @@ public class JobExecution extends Entity {
|
||||
* @return the step executions that were registered
|
||||
*/
|
||||
public Collection<StepExecution> getStepExecutions() {
|
||||
return stepExecutions;
|
||||
return Collections.unmodifiableList(new ArrayList<StepExecution>(stepExecutions));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,7 +280,7 @@ public class JobExecution extends Entity {
|
||||
*
|
||||
* @param t
|
||||
*/
|
||||
public void addFailureException(Throwable t) {
|
||||
public synchronized void addFailureException(Throwable t) {
|
||||
this.failureExceptions.add(t);
|
||||
}
|
||||
|
||||
@@ -289,7 +291,7 @@ public class JobExecution extends Entity {
|
||||
* @return List<Throwable> containing all exceptions causing failure for
|
||||
* this JobExecution.
|
||||
*/
|
||||
public List<Throwable> getAllFailureExceptions() {
|
||||
public synchronized List<Throwable> getAllFailureExceptions() {
|
||||
|
||||
Set<Throwable> allExceptions = new HashSet<Throwable>(failureExceptions);
|
||||
for (StepExecution stepExecution : stepExecutions) {
|
||||
@@ -320,8 +322,8 @@ public class JobExecution extends Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the step executions. For internal use only.
|
||||
* @param stepExecutions
|
||||
* Add some step executions. For internal use only.
|
||||
* @param stepExecutions step executions to add to the current list
|
||||
*/
|
||||
public void addStepExecutions(List<StepExecution> stepExecutions) {
|
||||
if (stepExecutions!=null) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.io.ObjectInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -70,7 +71,7 @@ public class StepExecution extends Entity {
|
||||
|
||||
private volatile int filterCount;
|
||||
|
||||
private transient volatile List<Throwable> failureExceptions = new ArrayList<Throwable>();
|
||||
private transient volatile List<Throwable> failureExceptions = new CopyOnWriteArrayList<Throwable>();
|
||||
|
||||
/**
|
||||
* Constructor with mandatory properties.
|
||||
|
||||
@@ -23,11 +23,11 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.support.SerializationUtils;
|
||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class MapJobExecutionDao implements JobExecutionDao {
|
||||
|
||||
private Map<Long, JobExecution> executionsById = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
private Map<Long, JobExecution> executionsById = new ConcurrentHashMap<Long, JobExecution>();
|
||||
|
||||
private long currentId = 0;
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -33,7 +33,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class MapJobInstanceDao implements JobInstanceDao {
|
||||
|
||||
private Collection<JobInstance> jobInstances = TransactionAwareProxyFactory.createTransactionalSet();
|
||||
private Collection<JobInstance> jobInstances = new CopyOnWriteArraySet<JobInstance>();
|
||||
|
||||
private long currentId = 0;
|
||||
|
||||
|
||||
@@ -20,12 +20,13 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.springframework.batch.core.Entity;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.support.SerializationUtils;
|
||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -34,13 +35,11 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class MapStepExecutionDao implements StepExecutionDao {
|
||||
|
||||
private Map<Long, Map<Long, StepExecution>> executionsByJobExecutionId = TransactionAwareProxyFactory
|
||||
.createTransactionalMap();
|
||||
private Map<Long, Map<Long, StepExecution>> executionsByJobExecutionId = new ConcurrentHashMap<Long, Map<Long, StepExecution>>();
|
||||
|
||||
private Map<Long, StepExecution> executionsByStepExecutionId = TransactionAwareProxyFactory
|
||||
.createTransactionalMap();
|
||||
private Map<Long, StepExecution> executionsByStepExecutionId = new ConcurrentHashMap<Long, StepExecution>();
|
||||
|
||||
private long currentId = 0;
|
||||
private AtomicLong currentId = new AtomicLong();
|
||||
|
||||
public void clear() {
|
||||
executionsByJobExecutionId.clear();
|
||||
@@ -59,11 +58,11 @@ public class MapStepExecutionDao implements StepExecutionDao {
|
||||
|
||||
Map<Long, StepExecution> executions = executionsByJobExecutionId.get(stepExecution.getJobExecutionId());
|
||||
if (executions == null) {
|
||||
executions = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
executions = new ConcurrentHashMap<Long, StepExecution>();
|
||||
executionsByJobExecutionId.put(stepExecution.getJobExecutionId(), executions);
|
||||
}
|
||||
|
||||
stepExecution.setId(currentId++);
|
||||
|
||||
stepExecution.setId(currentId.incrementAndGet());
|
||||
stepExecution.incrementVersion();
|
||||
StepExecution copy = copy(stepExecution);
|
||||
executions.put(stepExecution.getId(), copy);
|
||||
|
||||
@@ -179,6 +179,8 @@ public class SimpleJobRepository implements JobRepository {
|
||||
}
|
||||
|
||||
public void updateExecutionContext(StepExecution stepExecution) {
|
||||
validateStepExecution(stepExecution);
|
||||
Assert.notNull(stepExecution.getId(), "StepExecution must already be saved (have an id assigned)");
|
||||
ecDao.updateExecutionContext(stepExecution);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ public class FlowJobTests {
|
||||
@Override
|
||||
public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
|
||||
JobExecution jobExecution = executor.getJobExecution();
|
||||
jobExecution.getStepExecutions().add(new StepExecution(getName(), jobExecution));
|
||||
jobExecution.createStepExecution(getName());
|
||||
if (fail) {
|
||||
return FlowExecutionStatus.FAILED;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.batch.core.job.flow;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.job.flow.support.state.AbstractState;
|
||||
|
||||
/**
|
||||
@@ -42,7 +41,7 @@ public class StateSupport extends AbstractState {
|
||||
public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
|
||||
JobExecution jobExecution = executor.getJobExecution();
|
||||
if (jobExecution != null) {
|
||||
jobExecution.getStepExecutions().add(new StepExecution(getName(), jobExecution));
|
||||
jobExecution.createStepExecution(getName());
|
||||
}
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
@@ -23,13 +23,15 @@ public class SimpleStepExecutionSplitterTests {
|
||||
|
||||
private JobRepository jobRepository;
|
||||
|
||||
private StepExecution stepExecution = new StepExecution("bar", new JobExecution(11L));
|
||||
private StepExecution stepExecution;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
step = new TaskletStep("step");
|
||||
MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean();
|
||||
jobRepository = (JobRepository) factory.getObject();
|
||||
stepExecution = jobRepository.createJobExecution("job", new JobParameters()).createStepExecution("bar");
|
||||
jobRepository.add(stepExecution);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -39,7 +39,7 @@ public class MapExecutionContextDaoTests extends AbstractExecutionContextDaoTest
|
||||
public void testPersistentCopy() throws Exception {
|
||||
MapExecutionContextDao tested = new MapExecutionContextDao();
|
||||
JobExecution jobExecution = new JobExecution((long)1);
|
||||
StepExecution stepExecution = new StepExecution("stepName", jobExecution);
|
||||
StepExecution stepExecution = new StepExecution("stepName", jobExecution, 123L);
|
||||
assertTrue(stepExecution.getExecutionContext().isEmpty());
|
||||
|
||||
tested.updateExecutionContext(stepExecution);
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OptionalDataException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -66,6 +67,9 @@ public class SerializationUtils {
|
||||
try {
|
||||
return new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject();
|
||||
}
|
||||
catch (OptionalDataException e) {
|
||||
throw new IllegalArgumentException("Could not deserialize object: eof="+e.eof+ " at length="+e.length, e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalArgumentException("Could not deserialize object", e);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
@@ -74,7 +75,7 @@ public class TransactionAwareProxyFactory<T> {
|
||||
return (T) new HashSet((Set) target);
|
||||
}
|
||||
else if (target instanceof Map) {
|
||||
return (T) new HashMap((Map) target);
|
||||
return (T) new ConcurrentHashMap((Map) target);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("Cannot copy target for this type: " + target.getClass());
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2006-2010 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.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class MapSerializationUtilsTests {
|
||||
|
||||
private Map<String, Object> map = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
@Test
|
||||
public void testCycle() throws Exception {
|
||||
map.put("foo.bar.spam", 123);
|
||||
Map<String, Object> result = getCopy(map);
|
||||
assertEquals(map, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleCycles() throws Exception {
|
||||
map.put("foo.bar.spam", 123);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
Map<String, Object> result = getCopy(map);
|
||||
assertEquals(map, result);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> getCopy(Map<String, Object> map) {
|
||||
return (Map<String, Object>) SerializationUtils.deserialize(SerializationUtils.serialize(map));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user