Merge spring-batch-core-tests in spring-batch-core
This commit is contained in:
@@ -29,7 +29,7 @@ import org.springframework.batch.core.job.flow.FlowJob;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.batch.core.step.tasklet.TaskletStep;
|
||||
import org.springframework.batch.test.namespace.config.DummyNamespaceHandler;
|
||||
import org.springframework.batch.core.test.namespace.config.DummyNamespaceHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
|
||||
/**
|
||||
* @author Mahmoud Ben Hassine
|
||||
*/
|
||||
public class AbstractIntegrationTests {
|
||||
|
||||
protected DataSource dataSource;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
databasePopulator.addScript(new ClassPathResource("/org/springframework/batch/core/schema-drop-hsqldb.sql"));
|
||||
databasePopulator.addScript(new ClassPathResource("/org/springframework/batch/core/schema-hsqldb.sql"));
|
||||
databasePopulator.addScript(new ClassPathResource("/business-schema-hsqldb.sql"));
|
||||
databasePopulator.execute(this.dataSource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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
|
||||
*
|
||||
* https://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.concurrent;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.core.job.builder.FlowBuilder;
|
||||
import org.springframework.batch.core.job.flow.Flow;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
|
||||
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.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.jdbc.datasource.embedded.ConnectionProperties;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseConfigurer;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Michael Minella
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = ConcurrentTransactionTests.ConcurrentJobConfiguration.class)
|
||||
public class ConcurrentTransactionTests {
|
||||
|
||||
@Autowired
|
||||
private Job concurrentJob;
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@DirtiesContext
|
||||
@Test
|
||||
public void testConcurrentLongRunningJobExecutions() throws Exception {
|
||||
|
||||
JobExecution jobExecution = jobLauncher.run(concurrentJob, new JobParameters());
|
||||
|
||||
assertEquals(jobExecution.getStatus(), BatchStatus.COMPLETED);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class ConcurrentJobConfiguration extends DefaultBatchConfigurer {
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobBuilderFactory;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory stepBuilderFactory;
|
||||
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
/**
|
||||
* This datasource configuration configures the HSQLDB instance using MVCC. When
|
||||
* configured using the default behavior, transaction serialization errors are
|
||||
* thrown (default configuration example below).
|
||||
*
|
||||
* return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder().
|
||||
* addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql").
|
||||
* addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql").
|
||||
* build());
|
||||
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
ResourceLoader defaultResourceLoader = new DefaultResourceLoader();
|
||||
EmbeddedDatabaseFactory embeddedDatabaseFactory = new EmbeddedDatabaseFactory();
|
||||
embeddedDatabaseFactory.setDatabaseConfigurer(new EmbeddedDatabaseConfigurer() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
|
||||
try {
|
||||
properties.setDriverClass((Class<? extends Driver>) ClassUtils.forName("org.hsqldb.jdbcDriver", this.getClass().getClassLoader()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
properties.setUrl("jdbc:hsqldb:mem:" + databaseName + ";hsqldb.tx=mvcc");
|
||||
properties.setUsername("sa");
|
||||
properties.setPassword("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(DataSource dataSource, String databaseName) {
|
||||
try {
|
||||
Connection connection = dataSource.getConnection();
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.execute("SHUTDOWN");
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
databasePopulator.addScript(defaultResourceLoader.getResource("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql"));
|
||||
databasePopulator.addScript(defaultResourceLoader.getResource("classpath:org/springframework/batch/core/schema-hsqldb.sql"));
|
||||
embeddedDatabaseFactory.setDatabasePopulator(databasePopulator);
|
||||
|
||||
return embeddedDatabaseFactory.getDatabase();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Flow flow() {
|
||||
return new FlowBuilder<Flow>("flow")
|
||||
.start(stepBuilderFactory.get("flow.step1")
|
||||
.tasklet(new Tasklet() {
|
||||
@Nullable
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
}).build()
|
||||
).next(stepBuilderFactory.get("flow.step2")
|
||||
.tasklet(new Tasklet() {
|
||||
@Nullable
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
}).build()
|
||||
).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Step firstStep() {
|
||||
return stepBuilderFactory.get("firstStep")
|
||||
.tasklet(new Tasklet() {
|
||||
@Nullable
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
System.out.println(">> Beginning concurrent job test");
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
}).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Step lastStep() {
|
||||
return stepBuilderFactory.get("lastStep")
|
||||
.tasklet(new Tasklet() {
|
||||
@Nullable
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
System.out.println(">> Ending concurrent job test");
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
}).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Job concurrentJob() {
|
||||
Flow splitFlow = new FlowBuilder<Flow>("splitflow").split(new SimpleAsyncTaskExecutor()).add(flow(), flow(), flow(), flow(), flow(), flow(), flow()).build();
|
||||
|
||||
return jobBuilderFactory.get("concurrentJob")
|
||||
.start(firstStep())
|
||||
.next(stepBuilderFactory.get("splitFlowStep")
|
||||
.flow(splitFlow)
|
||||
.build())
|
||||
.next(lastStep())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobRepository createJobRepository() throws Exception {
|
||||
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
|
||||
factory.setDataSource(dataSource());
|
||||
factory.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
|
||||
factory.setTransactionManager(getTransactionManager());
|
||||
factory.afterPropertiesSet();
|
||||
return factory.getObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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.football;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.StepExecution;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.test.AbstractIntegrationTests;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Mahmoud Ben Hassine
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"/simple-job-launcher-context.xml", "/META-INF/batch/footballJob.xml"})
|
||||
public class FootballJobIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
/** Logger */
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Autowired
|
||||
private Job job;
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchJob() throws Exception {
|
||||
JobExecution execution = jobLauncher.run(job, new JobParametersBuilder().addLong("commit.interval", 10L)
|
||||
.toJobParameters());
|
||||
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
|
||||
for (StepExecution stepExecution : execution.getStepExecutions()) {
|
||||
logger.info("Processed: " + stepExecution);
|
||||
if (stepExecution.getStepName().equals("playerload")) {
|
||||
// The effect of the retries
|
||||
assertEquals(new Double(Math.ceil(stepExecution.getReadCount() / 10. + 1)).intValue(),
|
||||
stepExecution.getCommitCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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.football;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.StepExecution;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.test.AbstractIntegrationTests;
|
||||
import org.springframework.batch.support.DatabaseType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Mahmoud Ben Hassine
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"/simple-job-launcher-context.xml", "/META-INF/batch/footballSkipJob.xml"})
|
||||
public class FootballJobSkipIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
/** Logger */
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Autowired
|
||||
private Job job;
|
||||
|
||||
private DatabaseType databaseType;
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) throws Exception {
|
||||
this.dataSource = dataSource;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
databaseType = DatabaseType.fromMetaData(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchJob() throws Exception {
|
||||
try {
|
||||
if (databaseType == DatabaseType.POSTGRES || databaseType == DatabaseType.ORACLE) {
|
||||
// Extra special test for these platforms (would have failed
|
||||
// the job with UNKNOWN status in Batch 2.0):
|
||||
jdbcTemplate.update("SET CONSTRAINTS ALL DEFERRED");
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore (wrong platform)
|
||||
}
|
||||
JobExecution execution = jobLauncher.run(job, new JobParametersBuilder().addLong("skip.limit", 0L)
|
||||
.toJobParameters());
|
||||
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
|
||||
for (StepExecution stepExecution : execution.getStepExecutions()) {
|
||||
logger.info("Processed: " + stepExecution);
|
||||
}
|
||||
// They all skip on the second execution because of a primary key
|
||||
// violation
|
||||
long retryLimit = 2L;
|
||||
execution = jobLauncher.run(job,
|
||||
new JobParametersBuilder().addLong("skip.limit", 100000L).addLong("retry.limit", retryLimit)
|
||||
.toJobParameters());
|
||||
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
|
||||
for (StepExecution stepExecution : execution.getStepExecutions()) {
|
||||
logger.info("Processed: " + stepExecution);
|
||||
if (stepExecution.getStepName().equals("playerload")) {
|
||||
// The effect of the retries is to increase the number of
|
||||
// rollbacks
|
||||
int commitInterval = stepExecution.getReadCount() / (stepExecution.getCommitCount() - 1);
|
||||
// Account for the extra empty commit if the read count is
|
||||
// commensurate with the commit interval
|
||||
int effectiveCommitCount = stepExecution.getReadCount() % commitInterval == 0 ? stepExecution
|
||||
.getCommitCount() - 1 : stepExecution.getCommitCount();
|
||||
long expectedRollbacks = Math.max(1, retryLimit) * effectiveCommitCount + stepExecution.getReadCount();
|
||||
assertEquals(expectedRollbacks, stepExecution.getRollbackCount());
|
||||
assertEquals(stepExecution.getReadCount(), stepExecution.getWriteSkipCount());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
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.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"/simple-job-launcher-context.xml", "/META-INF/batch/parallelJob.xml"})
|
||||
public class ParallelJobIntegrationTests {
|
||||
|
||||
/** Logger */
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private Job job;
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void clear() {
|
||||
JdbcTestUtils.deleteFromTables(jdbcTemplate, "PLAYER_SUMMARY", "GAMES", "PLAYERS");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchJob() throws Exception {
|
||||
JobExecution execution = jobLauncher.run(job, new JobParametersBuilder().toJobParameters());
|
||||
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
|
||||
for (StepExecution stepExecution : execution.getStepExecutions()) {
|
||||
logger.info("Processed: "+stepExecution);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class Game implements Serializable {
|
||||
|
||||
private String id;
|
||||
|
||||
private int year;
|
||||
|
||||
private String team;
|
||||
|
||||
private int week;
|
||||
|
||||
private String opponent;
|
||||
|
||||
private int completes;
|
||||
|
||||
private int attempts;
|
||||
|
||||
private int passingYards;
|
||||
|
||||
private int passingTd;
|
||||
|
||||
private int interceptions;
|
||||
|
||||
private int rushes;
|
||||
|
||||
private int rushYards;
|
||||
|
||||
private int receptions;
|
||||
|
||||
private int receptionYards;
|
||||
|
||||
private int totalTd;
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the year
|
||||
*/
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the team
|
||||
*/
|
||||
public String getTeam() {
|
||||
return team;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the week
|
||||
*/
|
||||
public int getWeek() {
|
||||
return week;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the opponent
|
||||
*/
|
||||
public String getOpponent() {
|
||||
return opponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the completes
|
||||
*/
|
||||
public int getCompletes() {
|
||||
return completes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the attempts
|
||||
*/
|
||||
public int getAttempts() {
|
||||
return attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the passingYards
|
||||
*/
|
||||
public int getPassingYards() {
|
||||
return passingYards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the passingTd
|
||||
*/
|
||||
public int getPassingTd() {
|
||||
return passingTd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the interceptions
|
||||
*/
|
||||
public int getInterceptions() {
|
||||
return interceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rushes
|
||||
*/
|
||||
public int getRushes() {
|
||||
return rushes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rushYards
|
||||
*/
|
||||
public int getRushYards() {
|
||||
return rushYards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the receptions
|
||||
*/
|
||||
public int getReceptions() {
|
||||
return receptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the receptionYards
|
||||
*/
|
||||
public int getReceptionYards() {
|
||||
return receptionYards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the totalTd
|
||||
*/
|
||||
public int getTotalTd() {
|
||||
return totalTd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param year the year to set
|
||||
*/
|
||||
public void setYear(int year) {
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param team the team to set
|
||||
*/
|
||||
public void setTeam(String team) {
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param week the week to set
|
||||
*/
|
||||
public void setWeek(int week) {
|
||||
this.week = week;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param opponent the opponent to set
|
||||
*/
|
||||
public void setOpponent(String opponent) {
|
||||
this.opponent = opponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param completes the completes to set
|
||||
*/
|
||||
public void setCompletes(int completes) {
|
||||
this.completes = completes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param attempts the attempts to set
|
||||
*/
|
||||
public void setAttempts(int attempts) {
|
||||
this.attempts = attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param passingYards the passingYards to set
|
||||
*/
|
||||
public void setPassingYards(int passingYards) {
|
||||
this.passingYards = passingYards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param passingTd the passingTd to set
|
||||
*/
|
||||
public void setPassingTd(int passingTd) {
|
||||
this.passingTd = passingTd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param interceptions the interceptions to set
|
||||
*/
|
||||
public void setInterceptions(int interceptions) {
|
||||
this.interceptions = interceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rushes the rushes to set
|
||||
*/
|
||||
public void setRushes(int rushes) {
|
||||
this.rushes = rushes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rushYards the rushYards to set
|
||||
*/
|
||||
public void setRushYards(int rushYards) {
|
||||
this.rushYards = rushYards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param receptions the receptions to set
|
||||
*/
|
||||
public void setReceptions(int receptions) {
|
||||
this.receptions = receptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param receptionYards the receptionYards to set
|
||||
*/
|
||||
public void setReceptionYards(int receptionYards) {
|
||||
this.receptionYards = receptionYards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param totalTd the totalTd to set
|
||||
*/
|
||||
public void setTotalTd(int totalTd) {
|
||||
this.totalTd = totalTd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "Game: ID=" + id + " " + team + " vs. " + opponent + " - " + year;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Game other = (Game) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
}
|
||||
else if (!id.equals(other.id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class Player implements Serializable {
|
||||
|
||||
private String id;
|
||||
private String lastName;
|
||||
private String firstName;
|
||||
private String position;
|
||||
private int birthYear;
|
||||
private int debutYear;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "PLAYER:id=" + id + ",Last Name=" + lastName +
|
||||
",First Name=" + firstName + ",Position=" + position +
|
||||
",Birth Year=" + birthYear + ",DebutYear=" +
|
||||
debutYear;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
public String getPosition() {
|
||||
return position;
|
||||
}
|
||||
public int getBirthYear() {
|
||||
return birthYear;
|
||||
}
|
||||
public int getDebutYear() {
|
||||
return debutYear;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
public void setPosition(String position) {
|
||||
this.position = position;
|
||||
}
|
||||
public void setBirthYear(int birthYear) {
|
||||
this.birthYear = birthYear;
|
||||
}
|
||||
public void setDebutYear(int debutYear) {
|
||||
this.debutYear = debutYear;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.domain;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for writing {@link Player} objects to arbitrary output.
|
||||
*/
|
||||
public interface PlayerDao {
|
||||
|
||||
void savePlayer(Player player);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.domain;
|
||||
|
||||
|
||||
/**
|
||||
* Domain object representing the summary of a given Player's
|
||||
* year.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class PlayerSummary {
|
||||
|
||||
private String id;
|
||||
private int year;
|
||||
private int completes;
|
||||
private int attempts;
|
||||
private int passingYards;
|
||||
private int passingTd;
|
||||
private int interceptions;
|
||||
private int rushes;
|
||||
private int rushYards;
|
||||
private int receptions;
|
||||
private int receptionYards;
|
||||
private int totalTd;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
public void setYear(int year) {
|
||||
this.year = year;
|
||||
}
|
||||
public int getCompletes() {
|
||||
return completes;
|
||||
}
|
||||
public void setCompletes(int completes) {
|
||||
this.completes = completes;
|
||||
}
|
||||
public int getAttempts() {
|
||||
return attempts;
|
||||
}
|
||||
public void setAttempts(int attempts) {
|
||||
this.attempts = attempts;
|
||||
}
|
||||
public int getPassingYards() {
|
||||
return passingYards;
|
||||
}
|
||||
public void setPassingYards(int passingYards) {
|
||||
this.passingYards = passingYards;
|
||||
}
|
||||
public int getPassingTd() {
|
||||
return passingTd;
|
||||
}
|
||||
public void setPassingTd(int passingTd) {
|
||||
this.passingTd = passingTd;
|
||||
}
|
||||
public int getInterceptions() {
|
||||
return interceptions;
|
||||
}
|
||||
public void setInterceptions(int interceptions) {
|
||||
this.interceptions = interceptions;
|
||||
}
|
||||
public int getRushes() {
|
||||
return rushes;
|
||||
}
|
||||
public void setRushes(int rushes) {
|
||||
this.rushes = rushes;
|
||||
}
|
||||
public int getRushYards() {
|
||||
return rushYards;
|
||||
}
|
||||
public void setRushYards(int rushYards) {
|
||||
this.rushYards = rushYards;
|
||||
}
|
||||
public int getReceptions() {
|
||||
return receptions;
|
||||
}
|
||||
public void setReceptions(int receptions) {
|
||||
this.receptions = receptions;
|
||||
}
|
||||
public int getReceptionYards() {
|
||||
return receptionYards;
|
||||
}
|
||||
public void setReceptionYards(int receptionYards) {
|
||||
this.receptionYards = receptionYards;
|
||||
}
|
||||
public int getTotalTd() {
|
||||
return totalTd;
|
||||
}
|
||||
public void setTotalTd(int totalTd) {
|
||||
this.totalTd = totalTd;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Player Summary: ID=" + id + " Year=" + year + "[" + completes + ";" + attempts + ";" + passingYards +
|
||||
";" + passingTd + ";" + interceptions + ";" + rushes + ";" + rushYards + ";" + receptions +
|
||||
";" + receptionYards + ";" + totalTd;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
PlayerSummary other = (PlayerSummary) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
}
|
||||
else if (!id.equals(other.id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.exception.ExceptionHandler;
|
||||
|
||||
public class FootballExceptionHandler implements ExceptionHandler {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(FootballExceptionHandler.class);
|
||||
|
||||
@Override
|
||||
public void handleException(RepeatContext context, Throwable throwable)
|
||||
throws Throwable {
|
||||
|
||||
if (!(throwable instanceof NumberFormatException)) {
|
||||
throw throwable;
|
||||
} else {
|
||||
logger.error("Number Format Exception!", throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.Game;
|
||||
import org.springframework.batch.item.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
|
||||
public class GameFieldSetMapper implements FieldSetMapper<Game> {
|
||||
|
||||
@Override
|
||||
public Game mapFieldSet(FieldSet fs) {
|
||||
|
||||
if(fs == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
Game game = new Game();
|
||||
game.setId(fs.readString("id"));
|
||||
game.setYear(fs.readInt("year"));
|
||||
game.setTeam(fs.readString("team"));
|
||||
game.setWeek(fs.readInt("week"));
|
||||
game.setOpponent(fs.readString("opponent"));
|
||||
game.setCompletes(fs.readInt("completes"));
|
||||
game.setAttempts(fs.readInt("attempts"));
|
||||
game.setPassingYards(fs.readInt("passingYards"));
|
||||
game.setPassingTd(fs.readInt("passingTd"));
|
||||
game.setInterceptions(fs.readInt("interceptions"));
|
||||
game.setRushes(fs.readInt("rushes"));
|
||||
game.setRushYards(fs.readInt("rushYards"));
|
||||
game.setReceptions(fs.readInt("receptions", 0));
|
||||
game.setReceptionYards(fs.readInt("receptionYards"));
|
||||
game.setTotalTd(fs.readInt("totalTd"));
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.Game;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
|
||||
import org.springframework.jdbc.core.support.JdbcDaoSupport;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
|
||||
|
||||
public class JdbcGameDao extends JdbcDaoSupport implements ItemWriter<Game> {
|
||||
|
||||
private SimpleJdbcInsert insertGame;
|
||||
|
||||
@Override
|
||||
protected void initDao() throws Exception {
|
||||
super.initDao();
|
||||
insertGame = new SimpleJdbcInsert(getDataSource()).withTableName("GAMES").usingColumns("player_id", "year_no",
|
||||
"team", "week", "opponent", " completes", "attempts", "passing_yards", "passing_td", "interceptions",
|
||||
"rushes", "rush_yards", "receptions", "receptions_yards", "total_td");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(List<? extends Game> games) {
|
||||
|
||||
for (Game game : games) {
|
||||
|
||||
SqlParameterSource values = new MapSqlParameterSource().addValue("player_id", game.getId()).addValue(
|
||||
"year_no", game.getYear()).addValue("team", game.getTeam()).addValue("week", game.getWeek())
|
||||
.addValue("opponent", game.getOpponent()).addValue("completes", game.getCompletes()).addValue(
|
||||
"attempts", game.getAttempts()).addValue("passing_yards", game.getPassingYards()).addValue(
|
||||
"passing_td", game.getPassingTd()).addValue("interceptions", game.getInterceptions())
|
||||
.addValue("rushes", game.getRushes()).addValue("rush_yards", game.getRushYards()).addValue(
|
||||
"receptions", game.getReceptions()).addValue("receptions_yards", game.getReceptionYards())
|
||||
.addValue("total_td", game.getTotalTd());
|
||||
this.insertGame.execute(values);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.Player;
|
||||
import org.springframework.batch.core.test.football.domain.PlayerDao;
|
||||
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class JdbcPlayerDao implements PlayerDao {
|
||||
|
||||
public static final String INSERT_PLAYER =
|
||||
"INSERT into PLAYERS (player_id, last_name, first_name, pos, year_of_birth, year_drafted)" +
|
||||
" values (:id, :lastName, :firstName, :position, :birthYear, :debutYear)";
|
||||
|
||||
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void savePlayer(Player player) {
|
||||
namedParameterJdbcTemplate.update(INSERT_PLAYER, new BeanPropertySqlParameterSource(player));
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.PlayerSummary;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class JdbcPlayerSummaryDao implements ItemWriter<PlayerSummary> {
|
||||
|
||||
private static final String INSERT_SUMMARY = "INSERT into PLAYER_SUMMARY(ID, YEAR_NO, COMPLETES, ATTEMPTS, PASSING_YARDS, PASSING_TD, "
|
||||
+ "INTERCEPTIONS, RUSHES, RUSH_YARDS, RECEPTIONS, RECEPTIONS_YARDS, TOTAL_TD) "
|
||||
+ "values(:id, :year, :completes, :attempts, :passingYards, :passingTd, "
|
||||
+ ":interceptions, :rushes, :rushYards, :receptions, :receptionYards, :totalTd)";
|
||||
|
||||
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void write(List<? extends PlayerSummary> summaries) {
|
||||
|
||||
for (PlayerSummary summary : summaries) {
|
||||
|
||||
MapSqlParameterSource args = new MapSqlParameterSource().addValue("id", summary.getId()).addValue("year",
|
||||
summary.getYear()).addValue("completes", summary.getCompletes()).addValue("attempts",
|
||||
summary.getAttempts()).addValue("passingYards", summary.getPassingYards()).addValue("passingTd",
|
||||
summary.getPassingTd()).addValue("interceptions", summary.getInterceptions()).addValue("rushes",
|
||||
summary.getRushes()).addValue("rushYards", summary.getRushYards()).addValue("receptions",
|
||||
summary.getReceptions()).addValue("receptionYards", summary.getReceptionYards()).addValue(
|
||||
"totalTd", summary.getTotalTd());
|
||||
|
||||
namedParameterJdbcTemplate.update(INSERT_SUMMARY, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.Player;
|
||||
import org.springframework.batch.item.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
|
||||
public class PlayerFieldSetMapper implements FieldSetMapper<Player> {
|
||||
|
||||
@Override
|
||||
public Player mapFieldSet(FieldSet fs) {
|
||||
|
||||
if(fs == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
Player player = new Player();
|
||||
player.setId(fs.readString("ID"));
|
||||
player.setLastName(fs.readString("lastName"));
|
||||
player.setFirstName(fs.readString("firstName"));
|
||||
player.setPosition(fs.readString("position"));
|
||||
player.setDebutYear(fs.readInt("debutYear"));
|
||||
player.setBirthYear(fs.readInt("birthYear"));
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.Player;
|
||||
import org.springframework.batch.core.test.football.domain.PlayerDao;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
public class PlayerItemWriter implements ItemWriter<Player> {
|
||||
|
||||
private PlayerDao playerDao;
|
||||
|
||||
@Override
|
||||
public void write(List<? extends Player> players) throws Exception {
|
||||
for (Player player : players) {
|
||||
playerDao.savePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlayerDao(PlayerDao playerDao) {
|
||||
this.playerDao = playerDao;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.PlayerSummary;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* RowMapper used to map a ResultSet to a {@link org.springframework.batch.core.test.football.domain.PlayerSummary}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Mahmoud Ben Hassine
|
||||
*
|
||||
*/
|
||||
public class PlayerSummaryMapper implements RowMapper<PlayerSummary> {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
|
||||
*/
|
||||
@Override
|
||||
public PlayerSummary mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
PlayerSummary summary = new PlayerSummary();
|
||||
|
||||
summary.setId(rs.getString(1));
|
||||
summary.setYear(rs.getInt(2));
|
||||
summary.setCompletes(rs.getInt(3));
|
||||
summary.setAttempts(rs.getInt(4));
|
||||
summary.setPassingYards(rs.getInt(5));
|
||||
summary.setPassingTd(rs.getInt(6));
|
||||
summary.setInterceptions(rs.getInt(7));
|
||||
summary.setRushes(rs.getInt(8));
|
||||
summary.setRushYards(rs.getInt(9));
|
||||
summary.setReceptions(rs.getInt(10));
|
||||
summary.setReceptionYards(rs.getInt(11));
|
||||
summary.setTotalTd(rs.getInt(12));
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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.football.internal;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.batch.core.test.football.domain.PlayerSummary;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* RowMapper used to map a ResultSet to a {@link org.springframework.batch.core.test.football.domain.PlayerSummary}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Mahmoud Ben Hassine
|
||||
*
|
||||
*/
|
||||
public class PlayerSummaryRowMapper implements RowMapper<PlayerSummary> {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
|
||||
*/
|
||||
@Override
|
||||
public PlayerSummary mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
PlayerSummary summary = new PlayerSummary();
|
||||
|
||||
summary.setId(rs.getString(1));
|
||||
summary.setYear(rs.getInt(2));
|
||||
summary.setCompletes(rs.getInt(3));
|
||||
summary.setAttempts(rs.getInt(4));
|
||||
summary.setPassingYards(rs.getInt(5));
|
||||
summary.setPassingTd(rs.getInt(6));
|
||||
summary.setInterceptions(rs.getInt(7));
|
||||
summary.setRushes(rs.getInt(8));
|
||||
summary.setRushYards(rs.getInt(9));
|
||||
summary.setReceptions(rs.getInt(10));
|
||||
summary.setReceptionYards(rs.getInt(11));
|
||||
summary.setTotalTd(rs.getInt(12));
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2005-2014 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
|
||||
*
|
||||
* https://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.ldif;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
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.core.io.UrlResource;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"/simple-job-launcher-context.xml", "/applicationContext-test1.xml"})
|
||||
public class LdifReaderTests {
|
||||
|
||||
private Resource expected;
|
||||
private Resource actual;
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job1")
|
||||
private Job job1;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job2")
|
||||
private Job job2;
|
||||
|
||||
public LdifReaderTests() throws MalformedURLException {
|
||||
expected = new ClassPathResource("/expectedOutput.ldif");
|
||||
actual = new UrlResource("file:target/test-outputs/output.ldif");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void checkFiles() {
|
||||
Assert.isTrue(expected.exists(), "Expected does not exist.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidRun() throws Exception {
|
||||
JobExecution jobExecution = jobLauncher.run(job1, new JobParameters());
|
||||
|
||||
//Ensure job completed successfully.
|
||||
Assert.isTrue(jobExecution.getExitStatus().equals(ExitStatus.COMPLETED), "Step Execution did not complete normally: " + jobExecution.getExitStatus());
|
||||
|
||||
//Check output.
|
||||
Assert.isTrue(actual.exists(), "Actual does not exist.");
|
||||
compareFiles(expected.getFile(), actual.getFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceNotExists() throws Exception {
|
||||
JobExecution jobExecution = jobLauncher.run(job2, new JobParameters());
|
||||
|
||||
Assert.isTrue(jobExecution.getExitStatus().getExitCode().equals("FAILED"), "The job exit status is not FAILED.");
|
||||
Assert.isTrue(jobExecution.getAllFailureExceptions().get(0).getMessage().contains("Failed to initialize the reader"), "The job failed for the wrong reason.");
|
||||
}
|
||||
|
||||
private void compareFiles(File expected, File actual) throws Exception {
|
||||
BufferedReader expectedReader = new BufferedReader(new FileReader(expected));
|
||||
BufferedReader actualReader = new BufferedReader(new FileReader(actual));
|
||||
try {
|
||||
int lineNum = 1;
|
||||
for (String expectedLine = null; (expectedLine = expectedReader.readLine()) != null; lineNum++) {
|
||||
String actualLine = actualReader.readLine();
|
||||
assertEquals("Line number " + lineNum + " does not match.", expectedLine, actualLine);
|
||||
}
|
||||
|
||||
String actualLine = actualReader.readLine();
|
||||
assertEquals("More lines than expected. There should not be a line number " + lineNum + ".", null, actualLine);
|
||||
}
|
||||
finally {
|
||||
expectedReader.close();
|
||||
actualReader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2005-2014 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
|
||||
*
|
||||
* https://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.ldif;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
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.core.io.UrlResource;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"/simple-job-launcher-context.xml", "/applicationContext-test2.xml"})
|
||||
public class MappingLdifReaderTests {
|
||||
private static Logger log = LoggerFactory.getLogger(MappingLdifReaderTests.class);
|
||||
|
||||
private Resource expected;
|
||||
private Resource actual;
|
||||
|
||||
@Autowired
|
||||
private JobLauncher launcher;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job1")
|
||||
private Job job1;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job2")
|
||||
private Job job2;
|
||||
|
||||
public MappingLdifReaderTests() throws MalformedURLException {
|
||||
expected = new ClassPathResource("/expectedOutput.ldif");
|
||||
actual = new UrlResource("file:target/test-outputs/output.ldif");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void checkFiles() {
|
||||
Assert.isTrue(expected.exists(), "Expected does not exist.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidRun() throws Exception {
|
||||
JobExecution jobExecution = launcher.run(job1, new JobParameters());
|
||||
|
||||
//Ensure job completed successfully.
|
||||
Assert.isTrue(jobExecution.getExitStatus().equals(ExitStatus.COMPLETED), "Step Execution did not complete normally: " + jobExecution.getExitStatus());
|
||||
|
||||
//Check output.
|
||||
Assert.isTrue(actual.exists(), "Actual does not exist.");
|
||||
Assert.isTrue(compareFiles(expected.getFile(), actual.getFile()), "Files were not equal");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceNotExists() throws Exception {
|
||||
JobExecution jobExecution = launcher.run(job2, new JobParameters());
|
||||
|
||||
Assert.isTrue(jobExecution.getExitStatus().getExitCode().equals("FAILED"), "The job exit status is not FAILED.");
|
||||
Assert.isTrue(jobExecution.getAllFailureExceptions().get(0).getMessage().contains("Failed to initialize the reader"), "The job failed for the wrong reason.");
|
||||
}
|
||||
|
||||
|
||||
private boolean compareFiles(File expected, File actual) throws Exception {
|
||||
boolean equal = true;
|
||||
|
||||
FileInputStream expectedStream = new FileInputStream(expected);
|
||||
FileInputStream actualStream = new FileInputStream(actual);
|
||||
|
||||
//Construct BufferedReader from InputStreamReader
|
||||
BufferedReader expectedReader = new BufferedReader(new InputStreamReader(expectedStream));
|
||||
BufferedReader actualReader = new BufferedReader(new InputStreamReader(actualStream));
|
||||
|
||||
String line = null;
|
||||
while ((line = expectedReader.readLine()) != null) {
|
||||
if(!line.equals(actualReader.readLine())) {
|
||||
equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(actualReader.readLine() != null) {
|
||||
equal = false;
|
||||
}
|
||||
|
||||
expectedReader.close();
|
||||
|
||||
return equal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2005-2019 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
|
||||
*
|
||||
* https://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.ldif;
|
||||
|
||||
import org.springframework.batch.item.ldif.RecordMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ldap.core.LdapAttributes;
|
||||
|
||||
/**
|
||||
* This default implementation simply returns the LdapAttributes object and is only intended for test. As its not required
|
||||
* to return an object of a specific type to make the MappingLdifReader implementation work, this basic setting is sufficient
|
||||
* to demonstrate its function.
|
||||
*
|
||||
* @author Keith Barlow
|
||||
*
|
||||
*/
|
||||
public class MyMapper implements RecordMapper<LdapAttributes> {
|
||||
|
||||
@Nullable
|
||||
public LdapAttributes mapRecord(LdapAttributes attributes) {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* https://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.ldif.builder;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.ldif.LdifReader;
|
||||
import org.springframework.batch.item.ldif.RecordCallbackHandler;
|
||||
import org.springframework.batch.item.ldif.builder.LdifReaderBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ldap.core.LdapAttributes;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Glenn Renfro
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
public class LdifReaderBuilderTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
private LdifReader ldifReader;
|
||||
|
||||
private String callbackAttributeName;
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
this.callbackAttributeName = null;
|
||||
if (this.ldifReader != null) {
|
||||
this.ldifReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRecord() throws Exception {
|
||||
this.ldifReader = new LdifReaderBuilder().recordsToSkip(1).resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo").build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the second record did not match expected result",
|
||||
"cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicRead() throws Exception {
|
||||
this.ldifReader = new LdifReaderBuilder().resource(context.getResource("classpath:/test.ldif")).name("foo").build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the first record did not match expected result",
|
||||
"cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentItemCount() throws Exception {
|
||||
this.ldifReader = new LdifReaderBuilder().currentItemCount(3)
|
||||
.resource(context.getResource("classpath:/test.ldif")).name("foo").build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the third record did not match expected result",
|
||||
"cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxItemCount() throws Exception {
|
||||
this.ldifReader = new LdifReaderBuilder().maxItemCount(1).resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo").build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the first record did not match expected result",
|
||||
"cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
ldapAttributes = this.ldifReader.read();
|
||||
assertNull("The second read should have returned null", ldapAttributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRecordCallback() throws Exception {
|
||||
this.ldifReader = new LdifReaderBuilder().recordsToSkip(1).skippedRecordsCallback(new TestCallBackHandler())
|
||||
.resource(context.getResource("classpath:/test.ldif")).name("foo").build();
|
||||
firstRead();
|
||||
assertEquals("The attribute name from the callback handler did not match the expected result",
|
||||
"cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com", this.callbackAttributeName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveState() throws Exception {
|
||||
this.ldifReader = new LdifReaderBuilder().resource(context.getResource("classpath:/test.ldif")).name("foo").build();
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
firstRead(executionContext);
|
||||
this.ldifReader.update(executionContext);
|
||||
assertEquals("foo.read.count did not have the expected result", 1,
|
||||
executionContext.getInt("foo.read.count"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveStateDisabled() throws Exception {
|
||||
this.ldifReader = new LdifReaderBuilder().saveState(false).resource(context.getResource("classpath:/test.ldif"))
|
||||
.build();
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
firstRead(executionContext);
|
||||
this.ldifReader.update(executionContext);
|
||||
assertEquals("ExecutionContext should have been empty", 0, executionContext.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrict() {
|
||||
// Test that strict when enabled will throw an exception.
|
||||
try {
|
||||
this.ldifReader = new LdifReaderBuilder().resource(context.getResource("classpath:/teadsfst.ldif")).name("foo").build();
|
||||
this.ldifReader.open(new ExecutionContext());
|
||||
fail("IllegalStateException should have been thrown, because strict was set to true");
|
||||
}
|
||||
catch (ItemStreamException ise) {
|
||||
assertEquals("IllegalStateException message did not match the expected result.",
|
||||
"Failed to initialize the reader", ise.getMessage());
|
||||
}
|
||||
// Test that strict when disabled will still allow the ldap resource to be opened.
|
||||
this.ldifReader = new LdifReaderBuilder().strict(false)
|
||||
.resource(context.getResource("classpath:/teadsfst.ldif")).name("foo").build();
|
||||
this.ldifReader.open(new ExecutionContext());
|
||||
}
|
||||
|
||||
private LdapAttributes firstRead() throws Exception {
|
||||
return firstRead(new ExecutionContext());
|
||||
}
|
||||
|
||||
private LdapAttributes firstRead(ExecutionContext executionContext) throws Exception {
|
||||
this.ldifReader.open(executionContext);
|
||||
return this.ldifReader.read();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class LdifConfiguration {
|
||||
|
||||
}
|
||||
|
||||
public class TestCallBackHandler implements RecordCallbackHandler {
|
||||
|
||||
@Override
|
||||
public void handleRecord(LdapAttributes attributes) {
|
||||
callbackAttributeName = attributes.getName().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright 2017-2019 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
|
||||
*
|
||||
* https://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.ldif.builder;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.ldif.MappingLdifReader;
|
||||
import org.springframework.batch.item.ldif.RecordCallbackHandler;
|
||||
import org.springframework.batch.item.ldif.RecordMapper;
|
||||
import org.springframework.batch.item.ldif.builder.MappingLdifReaderBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ldap.core.LdapAttributes;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Glenn Renfro
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
public class MappingLdifReaderBuilderTests {
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
private MappingLdifReader<LdapAttributes> mappingLdifReader;
|
||||
|
||||
private String callbackAttributeName;
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
this.callbackAttributeName = null;
|
||||
if (this.mappingLdifReader != null) {
|
||||
this.mappingLdifReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRecord() throws Exception {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.recordsToSkip(1)
|
||||
.recordMapper(new TestMapper())
|
||||
.resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo")
|
||||
.build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the second record did not match expected result",
|
||||
"cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicRead() throws Exception {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.recordMapper(new TestMapper())
|
||||
.resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo")
|
||||
.build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the first record did not match expected result",
|
||||
"cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentItemCount() throws Exception {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.currentItemCount(3)
|
||||
.recordMapper(new TestMapper())
|
||||
.resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo")
|
||||
.build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the third record did not match expected result",
|
||||
"cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxItemCount() throws Exception {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.maxItemCount(1)
|
||||
.recordMapper(new TestMapper())
|
||||
.resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo")
|
||||
.build();
|
||||
LdapAttributes ldapAttributes = firstRead();
|
||||
assertEquals("The attribute name for the first record did not match expected result",
|
||||
"cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com", ldapAttributes.getName().toString());
|
||||
ldapAttributes = this.mappingLdifReader.read();
|
||||
assertNull("The second read should have returned null", ldapAttributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipRecordCallback() throws Exception {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.recordsToSkip(1)
|
||||
.recordMapper(new TestMapper())
|
||||
.skippedRecordsCallback(new TestCallBackHandler())
|
||||
.resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo")
|
||||
.build();
|
||||
firstRead();
|
||||
assertEquals("The attribute name from the callback handler did not match the expected result",
|
||||
"cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com", this.callbackAttributeName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveState() throws Exception {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.recordMapper(new TestMapper())
|
||||
.resource(context.getResource("classpath:/test.ldif"))
|
||||
.name("foo")
|
||||
.build();
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
firstRead(executionContext);
|
||||
this.mappingLdifReader.update(executionContext);
|
||||
assertEquals("foo.read.count did not have the expected result", 1,
|
||||
executionContext.getInt("foo.read.count"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveStateDisabled() throws Exception {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.saveState(false)
|
||||
.recordMapper(new TestMapper())
|
||||
.resource(context.getResource("classpath:/test.ldif"))
|
||||
.build();
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
firstRead(executionContext);
|
||||
this.mappingLdifReader.update(executionContext);
|
||||
assertEquals("ExecutionContext should have been empty", 0, executionContext.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrict() {
|
||||
// Test that strict when enabled will throw an exception.
|
||||
try {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.recordMapper(new TestMapper())
|
||||
.resource(context.getResource("classpath:/teadsfst.ldif"))
|
||||
.name("foo")
|
||||
.build();
|
||||
this.mappingLdifReader.open(new ExecutionContext());
|
||||
fail("IllegalStateException should have been thrown, because strict was set to true");
|
||||
}
|
||||
catch (ItemStreamException ise) {
|
||||
assertEquals("IllegalStateException message did not match the expected result.",
|
||||
"Failed to initialize the reader", ise.getMessage());
|
||||
}
|
||||
// Test that strict when disabled will still allow the ldap resource to be opened.
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>().strict(false).name("foo")
|
||||
.recordMapper(new TestMapper()).resource(context.getResource("classpath:/teadsfst.ldif")).build();
|
||||
this.mappingLdifReader.open(new ExecutionContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullRecordMapper() {
|
||||
try {
|
||||
this.mappingLdifReader = new MappingLdifReaderBuilder<LdapAttributes>()
|
||||
.resource(context.getResource("classpath:/teadsfst.ldif"))
|
||||
.build();
|
||||
fail("IllegalArgumentException should have been thrown");
|
||||
}
|
||||
catch (IllegalArgumentException ise) {
|
||||
assertEquals("IllegalArgumentException message did not match the expected result.",
|
||||
"RecordMapper is required.", ise.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private LdapAttributes firstRead() throws Exception {
|
||||
return firstRead(new ExecutionContext());
|
||||
}
|
||||
|
||||
private LdapAttributes firstRead(ExecutionContext executionContext) throws Exception {
|
||||
this.mappingLdifReader.open(executionContext);
|
||||
return this.mappingLdifReader.read();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class LdifConfiguration {
|
||||
|
||||
}
|
||||
|
||||
public class TestCallBackHandler implements RecordCallbackHandler {
|
||||
|
||||
@Override
|
||||
public void handleRecord(LdapAttributes attributes) {
|
||||
callbackAttributeName = attributes.getName().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestMapper implements RecordMapper<LdapAttributes> {
|
||||
@Nullable
|
||||
@Override
|
||||
public LdapAttributes mapRecord(LdapAttributes attributes) {
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.test.namespace.config;
|
||||
package org.springframework.batch.core.test.namespace.config;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.repository;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.repository.dao.MapExecutionContextDao;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ConcurrentMapExecutionContextDaoTests {
|
||||
|
||||
private MapExecutionContextDao dao = new MapExecutionContextDao();
|
||||
|
||||
private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
|
||||
@Test
|
||||
public void testSaveUpdate() throws Exception {
|
||||
StepExecution stepExecution = new StepExecution("step", new JobExecution(11L));
|
||||
stepExecution.setId(123L);
|
||||
stepExecution.getExecutionContext().put("foo", "bar");
|
||||
dao.saveExecutionContext(stepExecution);
|
||||
ExecutionContext executionContext = dao.getExecutionContext(stepExecution);
|
||||
assertEquals("bar", executionContext.get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionalSaveUpdate() throws Exception {
|
||||
final StepExecution stepExecution = new StepExecution("step", new JobExecution(11L));
|
||||
stepExecution.setId(123L);
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback<Void>() {
|
||||
@Override
|
||||
public Void doInTransaction(TransactionStatus status) {
|
||||
stepExecution.getExecutionContext().put("foo", "bar");
|
||||
dao.saveExecutionContext(stepExecution);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
ExecutionContext executionContext = dao.getExecutionContext(stepExecution);
|
||||
assertEquals("bar", executionContext.get("foo"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcurrentTransactionalSaveUpdate() throws Exception {
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(3);
|
||||
CompletionService<StepExecution> completionService = new ExecutorCompletionService<>(executor);
|
||||
|
||||
final int outerMax = 10;
|
||||
final int innerMax = 100;
|
||||
|
||||
for (int i = 0; i < outerMax; i++) {
|
||||
|
||||
final StepExecution stepExecution1 = new StepExecution("step", new JobExecution(11L));
|
||||
stepExecution1.setId(123L + i);
|
||||
final StepExecution stepExecution2 = new StepExecution("step", new JobExecution(11L));
|
||||
stepExecution2.setId(1234L + i);
|
||||
|
||||
completionService.submit(new Callable<StepExecution>() {
|
||||
@Override
|
||||
public StepExecution call() throws Exception {
|
||||
for (int i = 0; i < innerMax; i++) {
|
||||
String value = "bar" + i;
|
||||
saveAndAssert(stepExecution1, value);
|
||||
}
|
||||
return stepExecution1;
|
||||
}
|
||||
});
|
||||
|
||||
completionService.submit(new Callable<StepExecution>() {
|
||||
@Override
|
||||
public StepExecution call() throws Exception {
|
||||
for (int i = 0; i < innerMax; i++) {
|
||||
String value = "spam" + i;
|
||||
saveAndAssert(stepExecution2, value);
|
||||
}
|
||||
return stepExecution2;
|
||||
}
|
||||
});
|
||||
|
||||
completionService.take().get();
|
||||
completionService.take().get();
|
||||
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
|
||||
}
|
||||
|
||||
private void saveAndAssert(final StepExecution stepExecution, final String value) {
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback<Void>() {
|
||||
@Override
|
||||
public Void doInTransaction(TransactionStatus status) {
|
||||
stepExecution.getExecutionContext().put("foo", value);
|
||||
dao.saveExecutionContext(stepExecution);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
ExecutionContext executionContext = dao.getExecutionContext(stepExecution);
|
||||
Assert.state(executionContext != null, "Lost insert: null executionContext at value=" + value);
|
||||
String foo = executionContext.getString("foo");
|
||||
Assert.state(value.equals(foo), "Lost update: wrong value=" + value + " (found " + foo + ") for id="
|
||||
+ stepExecution.getId());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
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.repository.JobExecutionAlreadyRunningException;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.test.AbstractIntegrationTests;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"/simple-job-launcher-context.xml"})
|
||||
public class JdbcJobRepositoryTests extends AbstractIntegrationTests {
|
||||
|
||||
private JobSupport job;
|
||||
|
||||
private Set<Long> jobExecutionIds = new HashSet<>();
|
||||
|
||||
private Set<Long> jobIds = new HashSet<>();
|
||||
|
||||
private List<Serializable> list = new ArrayList<>();
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private JobRepository repository;
|
||||
|
||||
/** Logger */
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void onSetUpInTransaction() throws Exception {
|
||||
super.setUp();
|
||||
job = new JobSupport("test-job");
|
||||
job.setRestartable(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindOrCreateJob() throws Exception {
|
||||
job.setName("foo");
|
||||
int before = 0;
|
||||
JobExecution execution = repository.createJobExecution(job.getName(), new JobParameters());
|
||||
int after = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM BATCH_JOB_INSTANCE", Integer.class);
|
||||
assertEquals(before + 1, after);
|
||||
assertNotNull(execution.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindOrCreateJobWithExecutionContext() throws Exception {
|
||||
job.setName("foo");
|
||||
int before = 0;
|
||||
JobExecution execution = repository.createJobExecution(job.getName(), new JobParameters());
|
||||
execution.getExecutionContext().put("foo", "bar");
|
||||
repository.updateExecutionContext(execution);
|
||||
int after = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM BATCH_JOB_EXECUTION_CONTEXT", Integer.class);
|
||||
assertEquals(before + 1, after);
|
||||
assertNotNull(execution.getId());
|
||||
JobExecution last = repository.getLastJobExecution(job.getName(), new JobParameters());
|
||||
assertEquals(execution, last);
|
||||
assertEquals(execution.getExecutionContext(), last.getExecutionContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindOrCreateJobConcurrently() throws Exception {
|
||||
|
||||
job.setName("bar");
|
||||
|
||||
int before = 0;
|
||||
assertEquals(0, before);
|
||||
|
||||
long t0 = System.currentTimeMillis();
|
||||
try {
|
||||
doConcurrentStart();
|
||||
fail("Expected JobExecutionAlreadyRunningException");
|
||||
}
|
||||
catch (JobExecutionAlreadyRunningException e) {
|
||||
// expected
|
||||
}
|
||||
long t1 = System.currentTimeMillis();
|
||||
|
||||
JobExecution execution = (JobExecution) list.get(0);
|
||||
|
||||
assertNotNull(execution);
|
||||
|
||||
int after = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM BATCH_JOB_INSTANCE", Integer.class);
|
||||
assertNotNull(execution.getId());
|
||||
assertEquals(before + 1, after);
|
||||
|
||||
logger.info("Duration: " + (t1 - t0)
|
||||
+ " - the second transaction did not block if this number is less than about 1000.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindOrCreateJobConcurrentlyWhenJobAlreadyExists() throws Exception {
|
||||
|
||||
job = new JobSupport("test-job");
|
||||
job.setRestartable(true);
|
||||
job.setName("spam");
|
||||
|
||||
JobExecution execution = repository.createJobExecution(job.getName(), new JobParameters());
|
||||
cacheJobIds(execution);
|
||||
execution.setEndTime(new Timestamp(System.currentTimeMillis()));
|
||||
repository.update(execution);
|
||||
execution.setStatus(BatchStatus.FAILED);
|
||||
|
||||
int before = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM BATCH_JOB_INSTANCE", Integer.class);
|
||||
assertEquals(1, before);
|
||||
|
||||
long t0 = System.currentTimeMillis();
|
||||
try {
|
||||
doConcurrentStart();
|
||||
fail("Expected JobExecutionAlreadyRunningException");
|
||||
}
|
||||
catch (JobExecutionAlreadyRunningException e) {
|
||||
// expected
|
||||
}
|
||||
long t1 = System.currentTimeMillis();
|
||||
|
||||
int after = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM BATCH_JOB_INSTANCE", Integer.class);
|
||||
assertNotNull(execution.getId());
|
||||
assertEquals(before, after);
|
||||
|
||||
logger.info("Duration: " + (t1 - t0)
|
||||
+ " - the second transaction did not block if this number is less than about 1000.");
|
||||
}
|
||||
|
||||
private void cacheJobIds(JobExecution execution) {
|
||||
if (execution == null) {
|
||||
return;
|
||||
}
|
||||
jobExecutionIds.add(execution.getId());
|
||||
jobIds.add(execution.getJobId());
|
||||
}
|
||||
|
||||
private JobExecution doConcurrentStart() throws Exception {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
JobExecution execution = repository.createJobExecution(job.getName(), new JobParameters());
|
||||
|
||||
//simulate running execution
|
||||
execution.setStartTime(new Date());
|
||||
repository.update(execution);
|
||||
|
||||
cacheJobIds(execution);
|
||||
list.add(execution);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (Exception e) {
|
||||
list.add(e);
|
||||
}
|
||||
|
||||
}
|
||||
}).start();
|
||||
|
||||
Thread.sleep(400);
|
||||
JobExecution execution = repository.createJobExecution(job.getName(), new JobParameters());
|
||||
cacheJobIds(execution);
|
||||
|
||||
int count = 0;
|
||||
while (list.size() == 0 && count++ < 100) {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
assertEquals("Timed out waiting for JobExecution to be created", 1, list.size());
|
||||
assertTrue("JobExecution not created in thread: " + list.get(0), list.get(0) instanceof JobExecution);
|
||||
return (JobExecution) list.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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.repository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParametersIncrementer;
|
||||
import org.springframework.batch.core.JobParametersValidator;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.UnexpectedJobExecutionException;
|
||||
import org.springframework.batch.core.job.DefaultJobParametersValidator;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Batch domain object representing a job. Job is an explicit abstraction
|
||||
* representing the configuration of a job specified by a developer. It should
|
||||
* be noted that restart policy is applied to the job as a whole and not to a
|
||||
* step.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class JobSupport implements BeanNameAware, Job {
|
||||
|
||||
private List<Step> steps = new ArrayList<>();
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean restartable = false;
|
||||
|
||||
private int startLimit = Integer.MAX_VALUE;
|
||||
|
||||
private JobParametersValidator jobParametersValidator = new DefaultJobParametersValidator();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public JobSupport() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor to immediately add name (which is mandatory but
|
||||
* not final).
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public JobSupport(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name property if it is not already set. Because of the order of
|
||||
* the callbacks in a Spring container the name property will be set first
|
||||
* if it is present. Care is needed with bean definition inheritance - if a
|
||||
* parent bean has a name, then its children need an explicit name as well,
|
||||
* otherwise they will not be unique.
|
||||
*
|
||||
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
if (this.name == null) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name property. Always overrides the default value if this object
|
||||
* is a Spring bean.
|
||||
*
|
||||
* @see #setBeanName(java.lang.String)
|
||||
* @param name the name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.domain.IJob#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jobParametersValidator the jobParametersValidator to set
|
||||
*/
|
||||
public void setJobParametersValidator(JobParametersValidator jobParametersValidator) {
|
||||
this.jobParametersValidator = jobParametersValidator;
|
||||
}
|
||||
|
||||
public void setSteps(List<Step> steps) {
|
||||
this.steps.clear();
|
||||
this.steps.addAll(steps);
|
||||
}
|
||||
|
||||
public void addStep(Step step) {
|
||||
this.steps.add(step);
|
||||
}
|
||||
|
||||
public List<Step> getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.domain.IJob#getStartLimit()
|
||||
*/
|
||||
public int getStartLimit() {
|
||||
return startLimit;
|
||||
}
|
||||
|
||||
public void setStartLimit(int startLimit) {
|
||||
this.startLimit = startLimit;
|
||||
}
|
||||
|
||||
public void setRestartable(boolean restartable) {
|
||||
this.restartable = restartable;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.domain.IJob#isRestartable()
|
||||
*/
|
||||
@Override
|
||||
public boolean isRestartable() {
|
||||
return restartable;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.Job#getJobParametersIncrementer()
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public JobParametersIncrementer getJobParametersIncrementer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobParametersValidator getJobParametersValidator() {
|
||||
return jobParametersValidator;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.domain.Job#run(org.springframework.batch.core.domain.JobExecution)
|
||||
*/
|
||||
@Override
|
||||
public void execute(JobExecution execution) throws UnexpectedJobExecutionException {
|
||||
throw new UnsupportedOperationException("JobSupport does not provide an implementation of run(). Use a smarter subclass.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ClassUtils.getShortName(getClass()) + ": [name=" + name + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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.repository;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.mysql.cj.jdbc.MysqlDataSource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.launch.support.SimpleJobOperator;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Mahmoud Ben Hassine
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@Ignore("Temporarily ignored until integration tests profile is created")
|
||||
public class MySQLJdbcJobRepositoryTests {
|
||||
|
||||
@ClassRule
|
||||
public static MySQLContainer mysql = new MySQLContainer<>();
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
@Autowired
|
||||
private JobOperator jobOperator;
|
||||
@Autowired
|
||||
private Job job;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
|
||||
databasePopulator.addScript(new ClassPathResource("/org/springframework/batch/core/schema-mysql.sql"));
|
||||
databasePopulator.execute(this.dataSource);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test is for issue https://github.com/spring-projects/spring-batch/issues/2202:
|
||||
* A round trip from a `java.util.Date` JobParameter to the database and back
|
||||
* again should preserve fractional seconds precision, otherwise a different
|
||||
* job instance is created while the existing one should be used.
|
||||
*
|
||||
* This test ensures that round trip to the database with a `java.util.Date`
|
||||
* parameter ends up with a single job instance (with two job executions)
|
||||
* being created and not two distinct job instances (with a job execution for
|
||||
* each one).
|
||||
*
|
||||
* Note the issue does not happen if the parameter is of type Long (when using
|
||||
* addLong("date", date.getTime()) for instance).
|
||||
*/
|
||||
@Test
|
||||
public void testDateMillisecondPrecision() throws Exception {
|
||||
// given
|
||||
Date date = new Date();
|
||||
JobParameters jobParameters = new JobParametersBuilder()
|
||||
.addDate("date", date)
|
||||
.toJobParameters();
|
||||
|
||||
// when
|
||||
JobExecution jobExecution = this.jobLauncher.run(this.job, jobParameters);
|
||||
this.jobOperator.restart(jobExecution.getId()); // should load the date parameter with fractional seconds precision here
|
||||
|
||||
// then
|
||||
List<Long> jobInstances = this.jobOperator.getJobInstances("job", 0, 100);
|
||||
Assert.assertEquals(1, jobInstances.size());
|
||||
List<Long> jobExecutions = this.jobOperator.getExecutions(jobInstances.get(0));
|
||||
Assert.assertEquals(2, jobExecutions.size());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
MysqlDataSource datasource = new MysqlDataSource();
|
||||
datasource.setURL(mysql.getJdbcUrl());
|
||||
datasource.setUser(mysql.getUsername());
|
||||
datasource.setPassword(mysql.getPassword());
|
||||
return datasource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
|
||||
return jobs.get("job")
|
||||
.start(steps.get("step")
|
||||
.tasklet((contribution, chunkContext) -> {
|
||||
throw new Exception("expected failure");
|
||||
})
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JobOperator jobOperator(
|
||||
JobLauncher jobLauncher,
|
||||
JobRegistry jobRegistry,
|
||||
JobExplorer jobExplorer,
|
||||
JobRepository jobRepository
|
||||
) {
|
||||
SimpleJobOperator jobOperator = new SimpleJobOperator();
|
||||
jobOperator.setJobExplorer(jobExplorer);
|
||||
jobOperator.setJobLauncher(jobLauncher);
|
||||
jobOperator.setJobRegistry(jobRegistry);
|
||||
jobOperator.setJobRepository(jobRepository);
|
||||
return jobOperator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
|
||||
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
|
||||
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
|
||||
return jobRegistryBeanPostProcessor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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
|
||||
*
|
||||
* https://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.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
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.step.factory.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.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
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 FaultTolerantStepFactoryBeanIntegrationTests {
|
||||
|
||||
private static final int MAX_COUNT = 1000;
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private FaultTolerantStepFactoryBean<String, String> factory;
|
||||
|
||||
private SkipProcessorStub processor;
|
||||
|
||||
private SkipWriterStub writer;
|
||||
|
||||
private JobExecution jobExecution;
|
||||
|
||||
private StepExecution stepExecution;
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private JobRepository repository;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
writer = new SkipWriterStub(dataSource);
|
||||
processor = new SkipProcessorStub(dataSource);
|
||||
|
||||
factory = new FaultTolerantStepFactoryBean<>();
|
||||
|
||||
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);
|
||||
|
||||
JdbcTestUtils.deleteFromTables(new JdbcTemplate(dataSource), "ERROR_LOG");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesNoRollback() throws Exception {
|
||||
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
|
||||
writer.write(Arrays.asList("foo", "bar"));
|
||||
processor.process("spam");
|
||||
assertEquals(3, JdbcTestUtils.countRowsInTable(jdbcTemplate, "ERROR_LOG"));
|
||||
|
||||
writer.clear();
|
||||
processor.clear();
|
||||
assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "ERROR_LOG"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultithreadedSunnyDay() throws Throwable {
|
||||
|
||||
jobExecution = repository.createJobExecution("vanillaJob", new JobParameters());
|
||||
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
|
||||
SkipReaderStub reader = new SkipReaderStub();
|
||||
reader.clear();
|
||||
reader.setItems("1", "2", "3", "4", "5");
|
||||
factory.setItemReader(reader);
|
||||
writer.clear();
|
||||
factory.setItemWriter(writer);
|
||||
processor.clear();
|
||||
factory.setItemProcessor(processor);
|
||||
|
||||
assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "ERROR_LOG"));
|
||||
|
||||
try {
|
||||
|
||||
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<>(writer.getCommitted());
|
||||
Collections.sort(committed);
|
||||
assertEquals("[1, 2, 3, 4, 5]", committed.toString());
|
||||
List<String> processed = new ArrayList<>(processor.getCommitted());
|
||||
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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
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 ArrayList<>();
|
||||
|
||||
private Collection<String> failures = Collections.emptySet();
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
public SkipWriterStub(DataSource dataSource) {
|
||||
jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
public List<String> getCommitted() {
|
||||
return jdbcTemplate.query("SELECT MESSAGE from ERROR_LOG where STEP_NAME='written'",
|
||||
new RowMapper<String>() {
|
||||
@Override
|
||||
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return rs.getString(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
written.clear();
|
||||
jdbcTemplate.update("DELETE FROM ERROR_LOG where STEP_NAME='written'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
for (String item : items) {
|
||||
written.add(item);
|
||||
jdbcTemplate.update("INSERT INTO ERROR_LOG (MESSAGE, STEP_NAME) VALUES (?, ?)", item, "written");
|
||||
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 ArrayList<>();
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* @param dataSource
|
||||
*/
|
||||
public SkipProcessorStub(DataSource dataSource) {
|
||||
jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
public List<String> getCommitted() {
|
||||
return jdbcTemplate.query("SELECT MESSAGE from ERROR_LOG where STEP_NAME='processed'",
|
||||
new RowMapper<String>() {
|
||||
@Override
|
||||
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return rs.getString(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
processed.clear();
|
||||
jdbcTemplate.update("DELETE FROM ERROR_LOG where STEP_NAME='processed'");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String process(String item) throws Exception {
|
||||
processed.add(item);
|
||||
logger.debug("Processed item: "+item);
|
||||
jdbcTemplate.update("INSERT INTO ERROR_LOG (MESSAGE, STEP_NAME) VALUES (?, ?)", item, "processed");
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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
|
||||
*
|
||||
* https://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 java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
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 javax.sql.DataSource;
|
||||
|
||||
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.step.factory.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.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link FaultTolerantStepFactoryBean}.
|
||||
*/
|
||||
@ContextConfiguration(locations = "/simple-job-launcher-context.xml")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FaultTolerantStepFactoryBeanRollbackIntegrationTests {
|
||||
|
||||
private static final int MAX_COUNT = 1000;
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private FaultTolerantStepFactoryBean<String, String> factory;
|
||||
|
||||
private SkipProcessorStub processor;
|
||||
|
||||
private SkipWriterStub writer;
|
||||
|
||||
private JobExecution jobExecution;
|
||||
|
||||
private StepExecution stepExecution;
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private JobRepository repository;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
writer = new SkipWriterStub(dataSource);
|
||||
processor = new SkipProcessorStub(dataSource);
|
||||
|
||||
factory = new FaultTolerantStepFactoryBean<>();
|
||||
|
||||
factory.setBeanName("stepName");
|
||||
factory.setTransactionManager(transactionManager);
|
||||
factory.setJobRepository(repository);
|
||||
factory.setCommitInterval(3);
|
||||
factory.setSkipLimit(10);
|
||||
|
||||
JdbcTestUtils.deleteFromTables(new JdbcTemplate(dataSource), "ERROR_LOG");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesNoRollback() throws Exception {
|
||||
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
|
||||
writer.write(Arrays.asList("foo", "bar"));
|
||||
processor.process("spam");
|
||||
assertEquals(3, JdbcTestUtils.countRowsInTable(jdbcTemplate, "ERROR_LOG"));
|
||||
|
||||
writer.clear();
|
||||
processor.clear();
|
||||
assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "ERROR_LOG"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultithreadedSkipInWriter() throws Throwable {
|
||||
|
||||
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
|
||||
taskExecutor.setCorePoolSize(3);
|
||||
taskExecutor.setMaxPoolSize(6);
|
||||
taskExecutor.setQueueCapacity(0);
|
||||
taskExecutor.afterPropertiesSet();
|
||||
factory.setTaskExecutor(taskExecutor);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Class<? extends Throwable>, Boolean> skippable = getExceptionMap(Exception.class);
|
||||
factory.setSkippableExceptionClasses(skippable);
|
||||
|
||||
jobExecution = repository.createJobExecution("skipJob", new JobParameters());
|
||||
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
|
||||
if (i % 100 == 0) {
|
||||
logger.info("Starting step: " + i);
|
||||
}
|
||||
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "ERROR_LOG"));
|
||||
|
||||
try {
|
||||
|
||||
SkipReaderStub reader = new SkipReaderStub();
|
||||
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");
|
||||
|
||||
Step step = factory.getObject();
|
||||
|
||||
stepExecution = jobExecution.createStepExecution(factory.getName());
|
||||
repository.add(stepExecution);
|
||||
step.execute(stepExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
|
||||
|
||||
assertEquals("[]", writer.getCommitted().toString());
|
||||
assertEquals("[]", processor.getCommitted().toString());
|
||||
List<String> processed = new ArrayList<>(processor.getProcessed());
|
||||
Collections.sort(processed);
|
||||
assertEquals("[1, 1, 2, 2, 3, 3, 4, 4, 5, 5]", processed.toString());
|
||||
assertEquals(5, stepExecution.getSkipCount());
|
||||
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logger.info("Failed on iteration " + i + " of " + MAX_COUNT);
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Class<? extends Throwable>, Boolean> getExceptionMap(Class<? extends Throwable>... args) {
|
||||
Map<Class<? extends Throwable>, Boolean> map = new HashMap<>();
|
||||
for (Class<? extends Throwable> arg : args) {
|
||||
map.put(arg, true);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
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<>();
|
||||
|
||||
private Collection<String> failures = Collections.emptySet();
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
public SkipWriterStub(DataSource dataSource) {
|
||||
jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
public void setFailures(String... failures) {
|
||||
this.failures = Arrays.asList(failures);
|
||||
}
|
||||
|
||||
public List<String> getCommitted() {
|
||||
return jdbcTemplate.query("SELECT MESSAGE from ERROR_LOG where STEP_NAME='written'",
|
||||
new RowMapper<String>() {
|
||||
@Override
|
||||
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return rs.getString(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
written.clear();
|
||||
jdbcTemplate.update("DELETE FROM ERROR_LOG where STEP_NAME='written'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
for (String item : items) {
|
||||
written.add(item);
|
||||
jdbcTemplate.update("INSERT INTO ERROR_LOG (MESSAGE, STEP_NAME) VALUES (?, ?)", item, "written");
|
||||
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<>();
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* @param dataSource
|
||||
*/
|
||||
public SkipProcessorStub(DataSource dataSource) {
|
||||
jdbcTemplate = new JdbcTemplate(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'",
|
||||
new RowMapper<String>() {
|
||||
@Override
|
||||
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return rs.getString(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
processed.clear();
|
||||
jdbcTemplate.update("DELETE FROM ERROR_LOG where STEP_NAME='processed'");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String process(String item) throws Exception {
|
||||
processed.add(item);
|
||||
logger.debug("Processed item: " + item);
|
||||
jdbcTemplate.update("INSERT INTO ERROR_LOG (MESSAGE, STEP_NAME) VALUES (?, ?)", item, "processed");
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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
|
||||
*
|
||||
* https://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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.builder.FaultTolerantStepBuilder;
|
||||
import org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy;
|
||||
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
|
||||
import org.springframework.batch.core.step.skip.SkipPolicy;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.support.ListItemReader;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for fault tolerant {@link org.springframework.batch.core.step.item.ChunkOrientedTasklet}.
|
||||
*/
|
||||
@ContextConfiguration(locations = "/simple-job-launcher-context.xml")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class FaultTolerantStepIntegrationTests {
|
||||
|
||||
private static final int TOTAL_ITEMS = 30;
|
||||
private static final int CHUNK_SIZE = TOTAL_ITEMS;
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
private SkipPolicy skipPolicy;
|
||||
|
||||
private FaultTolerantStepBuilder<Integer, Integer> stepBuilder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ItemReader<Integer> itemReader = new ListItemReader<>(createItems());
|
||||
ItemProcessor<Integer, Integer> itemProcessor = item -> item > 20 ? null : item;
|
||||
ItemWriter<Integer> itemWriter = chunk -> {
|
||||
if (chunk.contains(1)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
};
|
||||
skipPolicy = new SkipIllegalArgumentExceptionSkipPolicy();
|
||||
stepBuilder = new StepBuilderFactory(jobRepository, transactionManager).get("step")
|
||||
.<Integer, Integer>chunk(CHUNK_SIZE)
|
||||
.reader(itemReader)
|
||||
.processor(itemProcessor)
|
||||
.writer(itemWriter)
|
||||
.faultTolerant();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterCountWithTransactionalProcessorWhenSkipInWrite() throws Exception {
|
||||
// Given
|
||||
Step step = stepBuilder
|
||||
.skipPolicy(skipPolicy)
|
||||
.build();
|
||||
|
||||
// When
|
||||
StepExecution stepExecution = execute(step);
|
||||
|
||||
// Then
|
||||
assertEquals(TOTAL_ITEMS, stepExecution.getReadCount());
|
||||
assertEquals(10, stepExecution.getFilterCount());
|
||||
assertEquals(19, stepExecution.getWriteCount());
|
||||
assertEquals(1, stepExecution.getWriteSkipCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterCountWithNonTransactionalProcessorWhenSkipInWrite() throws Exception {
|
||||
// Given
|
||||
Step step = stepBuilder
|
||||
.skipPolicy(skipPolicy)
|
||||
.processorNonTransactional()
|
||||
.build();
|
||||
|
||||
// When
|
||||
StepExecution stepExecution = execute(step);
|
||||
|
||||
// Then
|
||||
assertEquals(TOTAL_ITEMS, stepExecution.getReadCount());
|
||||
assertEquals(10, stepExecution.getFilterCount());
|
||||
assertEquals(19, stepExecution.getWriteCount());
|
||||
assertEquals(1, stepExecution.getWriteSkipCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterCountOnRetryWithTransactionalProcessorWhenSkipInWrite() throws Exception {
|
||||
// Given
|
||||
Step step = stepBuilder
|
||||
.retry(IllegalArgumentException.class)
|
||||
.retryLimit(2)
|
||||
.skipPolicy(skipPolicy)
|
||||
.build();
|
||||
|
||||
// When
|
||||
StepExecution stepExecution = execute(step);
|
||||
|
||||
// Then
|
||||
assertEquals(TOTAL_ITEMS, stepExecution.getReadCount());
|
||||
// filter count is expected to be counted on each retry attempt
|
||||
assertEquals(20, stepExecution.getFilterCount());
|
||||
assertEquals(19, stepExecution.getWriteCount());
|
||||
assertEquals(1, stepExecution.getWriteSkipCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterCountOnRetryWithNonTransactionalProcessorWhenSkipInWrite() throws Exception {
|
||||
// Given
|
||||
Step step = stepBuilder
|
||||
.retry(IllegalArgumentException.class)
|
||||
.retryLimit(2)
|
||||
.skipPolicy(skipPolicy)
|
||||
.processorNonTransactional()
|
||||
.build();
|
||||
|
||||
// When
|
||||
StepExecution stepExecution = execute(step);
|
||||
|
||||
// Then
|
||||
assertEquals(TOTAL_ITEMS, stepExecution.getReadCount());
|
||||
// filter count is expected to be counted on each retry attempt
|
||||
assertEquals(20, stepExecution.getFilterCount());
|
||||
assertEquals(19, stepExecution.getWriteCount());
|
||||
assertEquals(1, stepExecution.getWriteSkipCount());
|
||||
}
|
||||
|
||||
@Test(timeout = 3000)
|
||||
public void testExceptionInProcessDuringChunkScan() throws Exception {
|
||||
// Given
|
||||
ListItemReader<Integer> itemReader = new ListItemReader<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
|
||||
|
||||
ItemProcessor<Integer, Integer> itemProcessor = new ItemProcessor<Integer, Integer>() {
|
||||
private int cpt;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer process(Integer item) throws Exception {
|
||||
cpt++;
|
||||
if (cpt == 7) { // item 2 succeeds the first time but fails during the scan
|
||||
throw new Exception("Error during process");
|
||||
}
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
ItemWriter<Integer> itemWriter = new ItemWriter<Integer>() {
|
||||
private int cpt;
|
||||
|
||||
@Override
|
||||
public void write(List<? extends Integer> items) throws Exception {
|
||||
cpt++;
|
||||
if (cpt == 1) {
|
||||
throw new Exception("Error during write");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Step step = new StepBuilderFactory(jobRepository, transactionManager).get("step")
|
||||
.<Integer, Integer>chunk(5)
|
||||
.reader(itemReader)
|
||||
.processor(itemProcessor)
|
||||
.writer(itemWriter)
|
||||
.faultTolerant()
|
||||
.skip(Exception.class)
|
||||
.skipLimit(3)
|
||||
.build();
|
||||
|
||||
// When
|
||||
StepExecution stepExecution = execute(step);
|
||||
|
||||
// Then
|
||||
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
|
||||
assertEquals(ExitStatus.COMPLETED, stepExecution.getExitStatus());
|
||||
assertEquals(7, stepExecution.getReadCount());
|
||||
assertEquals(6, stepExecution.getWriteCount());
|
||||
assertEquals(1, stepExecution.getProcessSkipCount());
|
||||
}
|
||||
|
||||
@Test(timeout = 3000)
|
||||
public void testExceptionInProcessAndWriteDuringChunkScan() throws Exception {
|
||||
// Given
|
||||
ListItemReader<Integer> itemReader = new ListItemReader<>(Arrays.asList(1, 2, 3));
|
||||
|
||||
ItemProcessor<Integer, Integer> itemProcessor = new ItemProcessor<Integer, Integer>() {
|
||||
@Override
|
||||
public Integer process(Integer item) throws Exception {
|
||||
if (item.equals(2)) {
|
||||
throw new Exception("Error during process item " + item);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
ItemWriter<Integer> itemWriter = new ItemWriter<Integer>() {
|
||||
@Override
|
||||
public void write(List<? extends Integer> items) throws Exception {
|
||||
if (items.contains(3)) {
|
||||
throw new Exception("Error during write");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Step step = new StepBuilderFactory(jobRepository, transactionManager).get("step")
|
||||
.<Integer, Integer>chunk(5)
|
||||
.reader(itemReader)
|
||||
.processor(itemProcessor)
|
||||
.writer(itemWriter)
|
||||
.faultTolerant()
|
||||
.skipPolicy(new AlwaysSkipItemSkipPolicy())
|
||||
.build();
|
||||
|
||||
// When
|
||||
StepExecution stepExecution = execute(step);
|
||||
|
||||
// Then
|
||||
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
|
||||
assertEquals(ExitStatus.COMPLETED, stepExecution.getExitStatus());
|
||||
assertEquals(3, stepExecution.getReadCount());
|
||||
assertEquals(1, stepExecution.getWriteCount());
|
||||
assertEquals(1, stepExecution.getWriteSkipCount());
|
||||
assertEquals(1, stepExecution.getProcessSkipCount());
|
||||
assertEquals(3, stepExecution.getRollbackCount());
|
||||
assertEquals(2, stepExecution.getCommitCount());
|
||||
}
|
||||
|
||||
private List<Integer> createItems() {
|
||||
List<Integer> items = new ArrayList<>(TOTAL_ITEMS);
|
||||
for (int i = 1; i <= TOTAL_ITEMS; i++) {
|
||||
items.add(i);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private StepExecution execute(Step step) throws Exception {
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(
|
||||
"job" + Math.random(), new JobParameters());
|
||||
StepExecution stepExecution = jobExecution.createStepExecution("step");
|
||||
jobRepository.add(stepExecution);
|
||||
step.execute(stepExecution);
|
||||
return stepExecution;
|
||||
}
|
||||
|
||||
private class SkipIllegalArgumentExceptionSkipPolicy implements SkipPolicy {
|
||||
|
||||
@Override
|
||||
public boolean shouldSkip(Throwable throwable, int skipCount)
|
||||
throws SkipLimitExceededException {
|
||||
return throwable instanceof IllegalArgumentException;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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
|
||||
*
|
||||
* https://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 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.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.factory.FaultTolerantStepFactoryBean;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link FaultTolerantStepFactoryBean}.
|
||||
*/
|
||||
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;
|
||||
|
||||
private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
reader = new SkipReaderStub();
|
||||
writer = new SkipWriterStub();
|
||||
processor = new SkipProcessorStub();
|
||||
|
||||
factory = new FaultTolerantStepFactoryBean<>();
|
||||
|
||||
factory.setTransactionManager(transactionManager);
|
||||
factory.setBeanName("stepName");
|
||||
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(transactionManager).getObject();
|
||||
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 = 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<>(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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized String read() throws Exception {
|
||||
counter++;
|
||||
if (counter >= items.length) {
|
||||
return null;
|
||||
}
|
||||
return items[counter];
|
||||
}
|
||||
}
|
||||
|
||||
private static class SkipWriterStub implements ItemWriter<String> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private List<String> written = new CopyOnWriteArrayList<>();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
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<>();
|
||||
|
||||
public List<String> getProcessed() {
|
||||
return processed;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
processed.clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String process(String item) throws Exception {
|
||||
processed.add(item);
|
||||
logger.debug("Processed item: "+item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Class<? extends Throwable>, Boolean> getExceptionMap(Class<? extends Throwable>... args) {
|
||||
Map<Class<? extends Throwable>, Boolean> map = new HashMap<>();
|
||||
for (Class<? extends Throwable> arg : args) {
|
||||
map.put(arg, true);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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
|
||||
*
|
||||
* https://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.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.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.factory.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.batch.support.transaction.ResourcelessTransactionManager;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Tests for {@link FaultTolerantStepFactoryBean}.
|
||||
*/
|
||||
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;
|
||||
|
||||
private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
reader = new SkipReaderStub();
|
||||
writer = new SkipWriterStub();
|
||||
processor = new SkipProcessorStub();
|
||||
|
||||
factory = new FaultTolerantStepFactoryBean<>();
|
||||
|
||||
factory.setBeanName("stepName");
|
||||
factory.setTransactionManager(transactionManager);
|
||||
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(transactionManager).getObject();
|
||||
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 = factory.getObject();
|
||||
|
||||
stepExecution = jobExecution.createStepExecution(factory.getName());
|
||||
repository.add(stepExecution);
|
||||
step.execute(stepExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
|
||||
|
||||
List<String> committed = new ArrayList<>(writer.getWritten());
|
||||
Collections.sort(committed);
|
||||
assertEquals("[1, 2, 3, 4, 5]", committed.toString());
|
||||
List<String> processed = new ArrayList<>(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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
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<>();
|
||||
|
||||
private Collection<String> failures = Collections.emptySet();
|
||||
|
||||
public List<String> getWritten() {
|
||||
return written;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
written.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
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<>();
|
||||
|
||||
public List<String> getProcessed() {
|
||||
return processed;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
processed.clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String process(String item) throws Exception {
|
||||
processed.add(item);
|
||||
logger.debug("Processed item: "+item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2006-2019 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
|
||||
*
|
||||
* https://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;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SplitJobMapRepositoryIntegrationTests {
|
||||
|
||||
private static final int MAX_COUNT = 1000;
|
||||
|
||||
/** Logger */
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Test
|
||||
public void testMultithreadedSplit() throws Throwable {
|
||||
|
||||
JobLauncher jobLauncher = null;
|
||||
Job job = null;
|
||||
|
||||
ClassPathXmlApplicationContext context = null;
|
||||
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
|
||||
if (i % 100 == 0) {
|
||||
if (context!=null) {
|
||||
context.close();
|
||||
}
|
||||
logger.info("Starting job: " + i);
|
||||
context = new ClassPathXmlApplicationContext(getClass().getSimpleName()
|
||||
+ "-context.xml", getClass());
|
||||
jobLauncher = context.getBean("jobLauncher", JobLauncher.class);
|
||||
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);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
contribution.incrementReadCount();
|
||||
contribution.incrementWriteCount(1);
|
||||
return RepeatStatus.continueIf(count.incrementAndGet() < maxCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2006-2014 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
|
||||
*
|
||||
* https://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.util.SerializationUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Michael Minella
|
||||
*/
|
||||
public class StepExecutionSerializationUtilsTests {
|
||||
|
||||
@Test
|
||||
public void testCycle() throws Exception {
|
||||
StepExecution stepExecution = new StepExecution("step", new JobExecution(new JobInstance(123L,
|
||||
"job"), 321L, new JobParameters(), null), 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<>(executor);
|
||||
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
final JobExecution jobExecution = new JobExecution(new JobInstance(123L, "job"), 321L, new JobParameters(), null);
|
||||
for (int j = 0; j < threads; j++) {
|
||||
completionService.submit(new Callable<StepExecution>() {
|
||||
@Override
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2014 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
|
||||
*
|
||||
* https://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.timeout;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
public class LoggingItemWriter<T> implements ItemWriter<T> {
|
||||
|
||||
protected Log logger = LogFactory.getLog(LoggingItemWriter.class);
|
||||
|
||||
@Override
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
logger.info(items);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014-2019 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
|
||||
*
|
||||
* https://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.timeout;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class SleepingItemProcessor<I> implements ItemProcessor<I, I> {
|
||||
|
||||
private long millisToSleep;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public I process(I item) throws Exception {
|
||||
Thread.sleep(millisToSleep);
|
||||
return item;
|
||||
}
|
||||
|
||||
public void setMillisToSleep(long millisToSleep) {
|
||||
this.millisToSleep = millisToSleep;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2014-2019 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
|
||||
*
|
||||
* https://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.timeout;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
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.lang.Nullable;
|
||||
|
||||
public class SleepingTasklet implements Tasklet {
|
||||
|
||||
private long millisToSleep;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution,
|
||||
ChunkContext chunkContext) throws Exception {
|
||||
Thread.sleep(millisToSleep);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
public void setMillisToSleep(long millisToSleep) {
|
||||
this.millisToSleep = millisToSleep;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2014 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
|
||||
*
|
||||
* https://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.timeout;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.launch.JobLauncher;
|
||||
import org.springframework.batch.core.test.AbstractIntegrationTests;
|
||||
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(locations = {"/simple-job-launcher-context.xml", "/META-INF/batch/timeoutJob.xml"})
|
||||
public class TimeoutJobIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
/** Logger */
|
||||
@SuppressWarnings("unused")
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("chunkTimeoutJob")
|
||||
private Job chunkTimeoutJob;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("taskletTimeoutJob")
|
||||
private Job taskletTimeoutJob;
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkTimeoutShouldFail() throws Exception {
|
||||
JobExecution execution = jobLauncher.run(chunkTimeoutJob, new JobParametersBuilder().addLong("id", System.currentTimeMillis())
|
||||
.toJobParameters());
|
||||
assertEquals(BatchStatus.FAILED, execution.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskletTimeoutShouldFail() throws Exception {
|
||||
JobExecution execution = jobLauncher.run(taskletTimeoutJob, new JobParametersBuilder().addLong("id", System.currentTimeMillis())
|
||||
.toJobParameters());
|
||||
assertEquals(BatchStatus.FAILED, execution.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2009-2018 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
|
||||
*
|
||||
* https://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 test.jdbc.datasource;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.derby.jdbc.EmbeddedDataSource;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
|
||||
public class DerbyDataSourceFactoryBean extends AbstractFactoryBean<DataSource> {
|
||||
|
||||
private String dataDirectory = "build/derby-home";
|
||||
|
||||
public void setDataDirectory(String dataDirectory) {
|
||||
this.dataDirectory = dataDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource createInstance() throws Exception {
|
||||
File directory = new File(dataDirectory);
|
||||
System.setProperty("derby.system.home", directory.getCanonicalPath());
|
||||
System.setProperty("derby.storage.fileSyncTransactionLog", "true");
|
||||
System.setProperty("derby.storage.pageCacheSize", "100");
|
||||
|
||||
final EmbeddedDataSource ds = new EmbeddedDataSource();
|
||||
ds.setDatabaseName("build/derbydb");
|
||||
ds.setCreateDatabase("create");
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<DataSource> getObjectType() {
|
||||
return DataSource.class;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user