Merge spring-batch-core-tests in spring-batch-core

This commit is contained in:
Mahmoud Ben Hassine
2020-12-04 16:03:58 +01:00
parent bf612650ea
commit 236ee88dcf
72 changed files with 1602 additions and 2125 deletions

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}
}
}

View File

@@ -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());
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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 + "]";
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<job id="footballJob" xmlns="http://www.springframework.org/schema/batch">
<step id="playerload" next="gameLoad">
<tasklet>
<chunk reader="playerFileItemReader" writer="playerWriter"
commit-interval="#{jobParameters['commit.interval']}"/>
</tasklet>
</step>
<step id="gameLoad" next="playerSummarization">
<tasklet>
<chunk reader="gameFileItemReader" writer="gameWriter"
commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<step id="playerSummarization" parent="summarizationStep" />
</job>
<step id="summarizationStep" xmlns="http://www.springframework.org/schema/batch">
<tasklet>
<chunk reader="playerSummarizationSource" writer="summaryWriter"
commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<bean id="playerWriter" class="org.springframework.batch.core.test.football.internal.PlayerItemWriter">
<property name="playerDao">
<bean class="org.springframework.batch.core.test.football.internal.JdbcPlayerDao">
<property name="dataSource" ref="dataSource" />
</bean>
</property>
</bean>
<bean id="gameWriter" class="org.springframework.batch.core.test.football.internal.JdbcGameDao">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="summaryWriter" class="org.springframework.batch.core.test.football.internal.JdbcPlayerSummaryDao">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="playerFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:data/football/${player.file.name}" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="ID,lastName,firstName,position,birthYear,debutYear" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.core.test.football.internal.PlayerFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<bean id="gameFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:data/football/${games.file.name}" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="id,year,team,week,opponent,completes,attempts,passingYards,passingTd,interceptions,rushes,rushYards,receptions,receptionYards,totalTd" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.core.test.football.internal.GameFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<bean id="playerSummarizationSource" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="verifyCursorPosition" value="${batch.verify.cursor.position}" />
<property name="rowMapper">
<bean class="org.springframework.batch.core.test.football.internal.PlayerSummaryMapper" />
</property>
<property name="sql">
<value>
SELECT GAMES.player_id, GAMES.year_no, SUM(COMPLETES),
SUM(ATTEMPTS), SUM(PASSING_YARDS), SUM(PASSING_TD),
SUM(INTERCEPTIONS), SUM(RUSHES), SUM(RUSH_YARDS),
SUM(RECEPTIONS), SUM(RECEPTIONS_YARDS), SUM(TOTAL_TD)
from GAMES, PLAYERS where PLAYERS.player_id =
GAMES.player_id group by GAMES.player_id, GAMES.year_no
</value>
</property>
</bean>
<bean id="footballProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<value>
games.file.name=games-small.csv
player.file.name=player-small.csv
job.commit.interval=2
</value>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="1" />
</bean>
</beans>

View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<job id="footballSkipJob" xmlns="http://www.springframework.org/schema/batch">
<step id="playerload" next="gameLoad">
<tasklet>
<chunk reader="playerFileItemReader" writer="playerWriter" commit-interval="${job.commit.interval}"
skip-policy="skipPolicy" retry-policy="retryPolicy"/>
</tasklet>
</step>
<step id="gameLoad" next="playerSummarization">
<tasklet>
<chunk reader="gameFileItemReader" writer="gameWriter" commit-interval="${job.commit.interval}" skip-limit="100000">
<skippable-exception-classes>
<include class="org.springframework.dao.DataAccessException" />
</skippable-exception-classes>
</chunk>
</tasklet>
</step>
<step id="playerSummarization" parent="summarizationStep" />
</job>
<bean id="skipPolicy" class="org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy" scope="step">
<property name="skipLimit" value="#{jobParameters['skip.limit']}" />
<property name="skippableExceptionMap">
<map key-type="java.lang.Class">
<entry key="org.springframework.dao.DataAccessException" value="true"/>
</map>
</property>
</bean>
<bean id="retryPolicy" class="org.springframework.retry.policy.SimpleRetryPolicy" scope="step">
<constructor-arg value="#{jobParameters['retry.limit']}"/>
<constructor-arg>
<map key-type="java.lang.Class">
<entry key="org.springframework.dao.DataAccessException" value="true"/>
</map>
</constructor-arg>
</bean>
<step id="summarizationStep" xmlns="http://www.springframework.org/schema/batch">
<tasklet>
<chunk reader="playerSummarizationSource" writer="summaryWriter" commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<bean id="playerWriter" class="org.springframework.batch.core.test.football.internal.PlayerItemWriter">
<property name="playerDao">
<bean class="org.springframework.batch.core.test.football.internal.JdbcPlayerDao">
<property name="dataSource" ref="dataSource" />
</bean>
</property>
</bean>
<bean id="gameWriter" class="org.springframework.batch.core.test.football.internal.JdbcGameDao">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="summaryWriter" class="org.springframework.batch.core.test.football.internal.JdbcPlayerSummaryDao">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="playerFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:data/football/${player.file.name}" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="ID,lastName,firstName,position,birthYear,debutYear" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.core.test.football.internal.PlayerFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<bean id="gameFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:data/football/${games.file.name}" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names"
value="id,year,team,week,opponent,completes,attempts,passingYards,passingTd,interceptions,rushes,rushYards,receptions,receptionYards,totalTd" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.core.test.football.internal.GameFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<bean id="playerSummarizationSource" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="verifyCursorPosition" value="${batch.verify.cursor.position}" />
<property name="rowMapper">
<bean class="org.springframework.batch.core.test.football.internal.PlayerSummaryMapper" />
</property>
<property name="sql">
<value>
SELECT GAMES.player_id, GAMES.year_no, SUM(COMPLETES),
SUM(ATTEMPTS), SUM(PASSING_YARDS), SUM(PASSING_TD),
SUM(INTERCEPTIONS), SUM(RUSHES), SUM(RUSH_YARDS),
SUM(RECEPTIONS), SUM(RECEPTIONS_YARDS), SUM(TOTAL_TD)
from GAMES,
PLAYERS where PLAYERS.player_id =
GAMES.player_id group by GAMES.player_id, GAMES.year_no
</value>
</property>
</bean>
<bean id="footballProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<value>
games.file.name=games-small.csv
player.file.name=player-small.csv
job.commit.interval=2
</value>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="1" />
</bean>
</beans>

View File

@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<job id="parallelJob" xmlns="http://www.springframework.org/schema/batch">
<step id="playerload" next="gameLoadParallel">
<tasklet>
<chunk reader="playerFileItemReader" writer="playerWriter"
commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<step id="gameLoadParallel" next="playerSummarization">
<tasklet task-executor="taskExecutor">
<chunk reader="gameFileItemReader" writer="gameWriter"
commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<step id="playerSummarization" parent="summarizationStep" />
</job>
<step id="summarizationStep" xmlns="http://www.springframework.org/schema/batch">
<tasklet>
<chunk reader="playerSummarizationSource" writer="summaryWriter"
commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10"/>
<property name="maxPoolSize" value="20"/>
<property name="queueCapacity" value="0"/>
</bean>
<bean id="playerWriter" class="org.springframework.batch.core.test.football.internal.PlayerItemWriter">
<property name="playerDao">
<bean class="org.springframework.batch.core.test.football.internal.JdbcPlayerDao">
<property name="dataSource" ref="dataSource" />
</bean>
</property>
</bean>
<bean id="gameWriter" class="org.springframework.batch.core.test.football.internal.JdbcGameDao">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="summaryWriter" class="org.springframework.batch.core.test.football.internal.JdbcPlayerSummaryDao">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="playerFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:data/football/${player.file.name}" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="ID,lastName,firstName,position,birthYear,debutYear" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.core.test.football.internal.PlayerFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<bean id="gameFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:data/football/${games.file.name}" />
<property name="saveState" value="false" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="id,year,team,week,opponent,completes,attempts,passingYards,passingTd,interceptions,rushes,rushYards,receptions,receptionYards,totalTd" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.core.test.football.internal.GameFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<bean id="playerSummarizationSource" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="verifyCursorPosition" value="${batch.verify.cursor.position}" />
<property name="rowMapper">
<bean class="org.springframework.batch.core.test.football.internal.PlayerSummaryMapper" />
</property>
<property name="sql">
<value>
SELECT GAMES.player_id, GAMES.year_no, SUM(COMPLETES),
SUM(ATTEMPTS), SUM(PASSING_YARDS), SUM(PASSING_TD),
SUM(INTERCEPTIONS), SUM(RUSHES), SUM(RUSH_YARDS),
SUM(RECEPTIONS), SUM(RECEPTIONS_YARDS), SUM(TOTAL_TD)
from GAMES, PLAYERS where PLAYERS.player_id =
GAMES.player_id group by GAMES.player_id, GAMES.year_no
</value>
</property>
</bean>
<bean id="footballProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<value>
games.file.name=games-small.csv
player.file.name=player-small.csv
job.commit.interval=2
</value>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="1" />
</bean>
</beans>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<job id="chunkTimeoutJob" xmlns="http://www.springframework.org/schema/batch">
<step id="chunk-step">
<tasklet>
<chunk reader="dummyReader" processor="sleepingProcessor" writer="loggingWriter"
commit-interval="5" />
<transaction-attributes timeout="1"/>
</tasklet>
</step>
</job>
<job id="taskletTimeoutJob" xmlns="http://www.springframework.org/schema/batch">
<step id="tasklet-step">
<tasklet ref="sleepingTasklet">
<transaction-attributes timeout="1"/>
</tasklet>
</step>
</job>
<bean id="dummyReader" class="org.springframework.batch.item.support.ListItemReader">
<constructor-arg>
<list>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value>5</value>
<value>6</value>
<value>7</value>
<value>8</value>
<value>9</value>
<value>10</value>
</list>
</constructor-arg>
</bean>
<bean id="sleepingProcessor" class="org.springframework.batch.core.test.timeout.SleepingItemProcessor">
<property name="millisToSleep" value="500" />
</bean>
<bean id="loggingWriter" class="org.springframework.batch.core.test.timeout.LoggingItemWriter" />
<bean id="sleepingTasklet" class="org.springframework.batch.core.test.timeout.SleepingTasklet">
<property name="millisToSleep" value="2000" />
</bean>
</beans>

View File

@@ -1 +1 @@
http\://www.springframework.org/schema/batch/test=org.springframework.batch.test.namespace.config.DummyNamespaceHandler
http\://www.springframework.org/schema/batch/test=org.springframework.batch.core.test.namespace.config.DummyNamespaceHandler

View File

@@ -1 +1 @@
http\://www.springframework.org/schema/batch/test/test.xsd=org/springframework/batch/test/namespace/config/test.xsd
http\://www.springframework.org/schema/batch/test/test.xsd=org/springframework/batch/core/test/namespace/config/test.xsd

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd">
<job id="job1" xmlns="http://www.springframework.org/schema/batch">
<step id="step1">
<tasklet>
<chunk reader="itemReader1" writer="itemWriter" commit-interval="2" skip-limit="1">
<skippable-exception-classes>
<include class="org.springframework.ldap.ldif.InvalidAttributeFormatException"/>
</skippable-exception-classes>
</chunk>
</tasklet>
</step>
</job>
<job id="job2" xmlns="http://www.springframework.org/schema/batch">
<step id="step2">
<tasklet>
<chunk reader="itemReader2" writer="itemWriter" commit-interval="2" />
</tasklet>
</step>
</job>
<bean id="itemReader1" class="org.springframework.batch.item.ldif.LdifReader">
<property name="resource" value="classpath:/test.ldif" />
<property name="recordsToSkip" value="1" />
</bean>
<bean id="itemReader2" class="org.springframework.batch.item.ldif.LdifReader">
<property name="resource" value="file:src/test/resources/missing.ldif" />
<property name="recordsToSkip" value="1" />
</bean>
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:target/test-outputs/output.ldif" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
</property>
</bean>
</beans>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd">
<job id="job1" xmlns="http://www.springframework.org/schema/batch">
<step id="step1">
<tasklet transaction-manager="transactionManager">
<chunk reader="itemReader1" writer="itemWriter" commit-interval="2" skip-limit="1">
<skippable-exception-classes>
<include class="org.springframework.ldap.ldif.InvalidAttributeFormatException"/>
</skippable-exception-classes>
</chunk>
</tasklet>
</step>
</job>
<job id="job2" xmlns="http://www.springframework.org/schema/batch">
<step id="step2">
<tasklet transaction-manager="transactionManager">
<chunk reader="itemReader2" writer="itemWriter" commit-interval="2" />
</tasklet>
</step>
</job>
<bean id="itemReader1" class="org.springframework.batch.item.ldif.MappingLdifReader">
<property name="resource" value="file:src/test/resources/test.ldif" />
<property name="recordsToSkip" value="1" />
<property name="recordMapper" ref="recordMapper" />
</bean>
<bean id="itemReader2" class="org.springframework.batch.item.ldif.MappingLdifReader">
<property name="resource" value="file:src/test/resources/missing.ldif" />
<property name="recordsToSkip" value="1" />
<property name="recordMapper" ref="recordMapper" />
</bean>
<bean id="recordMapper" class="org.springframework.batch.core.test.ldif.MyMapper" />
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:target/test-outputs/output.ldif" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
</property>
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor" ref="taskExecutor" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="taskExecutor" class="org.springframework.core.task.SyncTaskExecutor" />
</beans>

View File

@@ -0,0 +1,17 @@
# Placeholders batch.*
# for Derby:
batch.jdbc.driver=org.apache.derby.jdbc.EmbeddedDriver
batch.jdbc.url=jdbc:derby:build/derby-home/test;create=true
batch.jdbc.user=sa
batch.jdbc.password=
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-derby.sql
batch.schema.script=classpath:/org/springframework/batch/core/schema-derby.sql
batch.business.schema.script=business-schema-derby.sql
batch.data.source.init=true
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer
batch.database.incrementer.parent=columnIncrementerParent
batch.grid.size=2
batch.verify.cursor.position=false
batch.jdbc.validationQuery=values 1

View File

@@ -0,0 +1,21 @@
# Placeholders batch.*
# for HSQLDB:
batch.jdbc.driver=org.hsqldb.jdbcDriver
batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true;hsqldb.tx=mvcc
# use this one for a separate server process so you can inspect the results
# (or add it to system properties with -D to override at run time).
# batch.jdbc.url=jdbc:hsqldb:hsql://localhost:9005/samples
batch.jdbc.user=sa
batch.jdbc.password=
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-hsqldb.sql
batch.schema.script=classpath:/org/springframework/batch/core/schema-hsqldb.sql
batch.business.schema.script=classpath:/business-schema-hsqldb.sql
batch.data.source.init=true
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer
batch.database.incrementer.parent=columnIncrementerParent
batch.grid.size=2
batch.verify.cursor.position=true
batch.jdbc.validationQuery=SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS
batch.table.prefix=BATCH_

View File

@@ -0,0 +1,14 @@
# Placeholders batch.*
# for MySQL:
batch.jdbc.driver=com.mysql.jdbc.Driver
batch.jdbc.url=jdbc:mysql://localhost/test
batch.jdbc.user=root
batch.jdbc.password=root
batch.schema.script=classpath:/org/springframework/batch/core/schema-mysql.sql
batch.business.schema.script=classpath:/business-schema-mysql.sql
batch.data.source.init=false
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer
batch.database.incrementer.parent=columnIncrementerParent
batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler
batch.verify.cursor.position=true
batch.jdbc.validationQuery=SELECT 1

View File

@@ -0,0 +1,17 @@
# Placeholders batch.*
# for Postgres:
batch.jdbc.driver=org.postgresql.Driver
batch.jdbc.url=jdbc:postgresql://localhost/test
batch.jdbc.user=test
batch.jdbc.password=test
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.schema.script=classpath:/org/springframework/batch/core/schema-postgresql.sql
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-postgresql.sql
batch.business.schema.script=classpath:/business-schema-postgresql.sql
batch.data.source.init=true
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.PostgresSequenceMaxValueIncrementer
batch.database.incrementer.parent=sequenceIncrementerParent
batch.grid.size=2
batch.verify.cursor.position=true
batch.jdbc.validationQuery=SELECT 1

View File

@@ -0,0 +1,96 @@
-- Autogenerated: do not edit this file
DROP TABLE BATCH_STAGING_SEQ ;
DROP TABLE TRADE_SEQ ;
DROP TABLE CUSTOMER_SEQ ;
DROP TABLE BATCH_STAGING ;
DROP TABLE TRADE ;
DROP TABLE CUSTOMER ;
DROP TABLE PLAYERS ;
DROP TABLE GAMES ;
DROP TABLE PLAYER_SUMMARY ;
DROP TABLE ERROR_LOG ;
-- Autogenerated: do not edit this file
CREATE TABLE CUSTOMER_SEQ (ID BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, DUMMY VARCHAR(1));
INSERT INTO CUSTOMER_SEQ (ID) values (5);
CREATE TABLE BATCH_STAGING_SEQ (ID BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, DUMMY VARCHAR(1));
INSERT INTO BATCH_STAGING_SEQ (ID) values (0);
CREATE TABLE TRADE_SEQ (ID BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, DUMMY VARCHAR(1));
INSERT INTO TRADE_SEQ (ID) values (0);
CREATE TABLE BATCH_STAGING (
ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
JOB_ID BIGINT NOT NULL,
VALUE BLOB NOT NULL,
PROCESSED CHAR(1) NOT NULL
) ;
CREATE TABLE TRADE (
ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
VERSION BIGINT ,
ISIN VARCHAR(45) NOT NULL,
QUANTITY BIGINT ,
PRICE DECIMAL(8,2) ,
CUSTOMER VARCHAR(45)
) ;
CREATE TABLE CUSTOMER (
ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
VERSION BIGINT ,
NAME VARCHAR(45) ,
CREDIT DECIMAL(10,2)
) ;
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (1, 0, 'customer1', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (2, 0, 'customer2', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (3, 0, 'customer3', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (4, 0, 'customer4', 100000);
CREATE TABLE PLAYERS (
PLAYER_ID CHAR(8) NOT NULL PRIMARY KEY,
LAST_NAME VARCHAR(35) NOT NULL,
FIRST_NAME VARCHAR(25) NOT NULL,
POS VARCHAR(10) ,
YEAR_OF_BIRTH BIGINT NOT NULL,
YEAR_DRAFTED BIGINT NOT NULL
) ;
CREATE TABLE GAMES (
PLAYER_ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
TEAM CHAR(3) NOT NULL,
WEEK BIGINT NOT NULL,
OPPONENT CHAR(3) ,
COMPLETES BIGINT ,
ATTEMPTS BIGINT ,
PASSING_YARDS BIGINT ,
PASSING_TD BIGINT ,
INTERCEPTIONS BIGINT ,
RUSHES BIGINT ,
RUSH_YARDS BIGINT ,
RECEPTIONS BIGINT ,
RECEPTIONS_YARDS BIGINT ,
TOTAL_TD BIGINT
) ;
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,
PASSING_YARDS BIGINT NOT NULL ,
PASSING_TD BIGINT NOT NULL ,
INTERCEPTIONS BIGINT NOT NULL ,
RUSHES BIGINT NOT NULL ,
RUSH_YARDS BIGINT NOT NULL ,
RECEPTIONS BIGINT NOT NULL ,
RECEPTIONS_YARDS BIGINT NOT NULL ,
TOTAL_TD BIGINT NOT NULL
) ;
CREATE TABLE ERROR_LOG (
JOB_NAME CHAR(20) ,
STEP_NAME CHAR(20) ,
MESSAGE VARCHAR(300) NOT NULL
) ;

View File

@@ -0,0 +1,53 @@
-- Autogenerated: do not edit this file
DROP TABLE PLAYERS IF EXISTS;
DROP TABLE GAMES IF EXISTS;
DROP TABLE PLAYER_SUMMARY IF EXISTS;
DROP TABLE ERROR_LOG IF EXISTS;
CREATE TABLE PLAYERS (
PLAYER_ID CHAR(8) NOT NULL PRIMARY KEY,
LAST_NAME VARCHAR(35) NOT NULL,
FIRST_NAME VARCHAR(25) NOT NULL,
POS VARCHAR(10) ,
YEAR_OF_BIRTH BIGINT NOT NULL,
YEAR_DRAFTED BIGINT NOT NULL
) ;
CREATE TABLE GAMES (
PLAYER_ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
TEAM CHAR(3) NOT NULL,
WEEK BIGINT NOT NULL,
OPPONENT CHAR(3) ,
COMPLETES BIGINT ,
ATTEMPTS BIGINT ,
PASSING_YARDS BIGINT ,
PASSING_TD BIGINT ,
INTERCEPTIONS BIGINT ,
RUSHES BIGINT ,
RUSH_YARDS BIGINT ,
RECEPTIONS BIGINT ,
RECEPTIONS_YARDS BIGINT ,
TOTAL_TD BIGINT
) ;
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,
PASSING_YARDS BIGINT NOT NULL ,
PASSING_TD BIGINT NOT NULL ,
INTERCEPTIONS BIGINT NOT NULL ,
RUSHES BIGINT NOT NULL ,
RUSH_YARDS BIGINT NOT NULL ,
RECEPTIONS BIGINT NOT NULL ,
RECEPTIONS_YARDS BIGINT NOT NULL ,
TOTAL_TD BIGINT NOT NULL
) ;
CREATE TABLE ERROR_LOG (
JOB_NAME CHAR(20) ,
STEP_NAME CHAR(20) ,
MESSAGE VARCHAR(300) NOT NULL
) ;

View File

@@ -0,0 +1,96 @@
-- Autogenerated: do not edit this file
DROP TABLE IF EXISTS BATCH_STAGING_SEQ ;
DROP TABLE IF EXISTS TRADE_SEQ ;
DROP TABLE IF EXISTS CUSTOMER_SEQ ;
DROP TABLE IF EXISTS BATCH_STAGING ;
DROP TABLE IF EXISTS TRADE ;
DROP TABLE IF EXISTS CUSTOMER ;
DROP TABLE IF EXISTS PLAYERS ;
DROP TABLE IF EXISTS GAMES ;
DROP TABLE IF EXISTS PLAYER_SUMMARY ;
DROP TABLE IF EXISTS ERROR_LOG ;
-- Autogenerated: do not edit this file
CREATE TABLE CUSTOMER_SEQ (ID BIGINT NOT NULL) type=MYISAM;
INSERT INTO CUSTOMER_SEQ values(5);
CREATE TABLE BATCH_STAGING_SEQ (ID BIGINT NOT NULL) type=MYISAM;
INSERT INTO BATCH_STAGING_SEQ values(0);
CREATE TABLE TRADE_SEQ (ID BIGINT NOT NULL) type=MYISAM;
INSERT INTO TRADE_SEQ values(0);
CREATE TABLE BATCH_STAGING (
ID BIGINT NOT NULL PRIMARY KEY ,
JOB_ID BIGINT NOT NULL,
VALUE BLOB NOT NULL,
PROCESSED CHAR(1) NOT NULL
) type=InnoDB;
CREATE TABLE TRADE (
ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
ISIN VARCHAR(45) NOT NULL,
QUANTITY BIGINT ,
PRICE DECIMAL(8,2) ,
CUSTOMER VARCHAR(45)
) type=InnoDB;
CREATE TABLE CUSTOMER (
ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
NAME VARCHAR(45) ,
CREDIT DECIMAL(10,2)
) type=InnoDB;
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (1, 0, 'customer1', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (2, 0, 'customer2', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (3, 0, 'customer3', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (4, 0, 'customer4', 100000);
CREATE TABLE PLAYERS (
PLAYER_ID CHAR(8) NOT NULL PRIMARY KEY,
LAST_NAME VARCHAR(35) NOT NULL,
FIRST_NAME VARCHAR(25) NOT NULL,
POS VARCHAR(10) ,
YEAR_OF_BIRTH BIGINT NOT NULL,
YEAR_DRAFTED BIGINT NOT NULL
) type=InnoDB;
CREATE TABLE GAMES (
PLAYER_ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
TEAM CHAR(3) NOT NULL,
WEEK BIGINT NOT NULL,
OPPONENT CHAR(3) ,
COMPLETES BIGINT ,
ATTEMPTS BIGINT ,
PASSING_YARDS BIGINT ,
PASSING_TD BIGINT ,
INTERCEPTIONS BIGINT ,
RUSHES BIGINT ,
RUSH_YARDS BIGINT ,
RECEPTIONS BIGINT ,
RECEPTIONS_YARDS BIGINT ,
TOTAL_TD BIGINT
) type=InnoDB;
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,
PASSING_YARDS BIGINT NOT NULL ,
PASSING_TD BIGINT NOT NULL ,
INTERCEPTIONS BIGINT NOT NULL ,
RUSHES BIGINT NOT NULL ,
RUSH_YARDS BIGINT NOT NULL ,
RECEPTIONS BIGINT NOT NULL ,
RECEPTIONS_YARDS BIGINT NOT NULL ,
TOTAL_TD BIGINT NOT NULL
) type=InnoDB;
CREATE TABLE ERROR_LOG (
JOB_NAME CHAR(20) ,
STEP_NAME CHAR(20) ,
MESSAGE VARCHAR(300) NOT NULL
) type=InnoDB;

View File

@@ -0,0 +1,94 @@
-- Autogenerated: do not edit this file
DROP SEQUENCE BATCH_STAGING_SEQ ;
DROP SEQUENCE TRADE_SEQ ;
DROP SEQUENCE CUSTOMER_SEQ ;
DROP TABLE BATCH_STAGING ;
DROP TABLE TRADE ;
DROP TABLE CUSTOMER ;
DROP TABLE PLAYERS ;
DROP TABLE GAMES ;
DROP TABLE PLAYER_SUMMARY ;
DROP TABLE ERROR_LOG ;
-- Autogenerated: do not edit this file
CREATE SEQUENCE CUSTOMER_SEQ;
CREATE SEQUENCE BATCH_STAGING_SEQ;
CREATE SEQUENCE TRADE_SEQ;
CREATE TABLE BATCH_STAGING (
ID BIGINT NOT NULL PRIMARY KEY ,
JOB_ID BIGINT NOT NULL,
VALUE BYTEA NOT NULL,
PROCESSED CHAR(1) NOT NULL
) ;
CREATE TABLE TRADE (
ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
ISIN VARCHAR(45) NOT NULL,
QUANTITY BIGINT ,
PRICE DECIMAL(8,2) ,
CUSTOMER VARCHAR(45)
) ;
CREATE TABLE CUSTOMER (
ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
NAME VARCHAR(45) ,
CREDIT DECIMAL(10,2)
) ;
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (1, 0, 'customer1', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (2, 0, 'customer2', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (3, 0, 'customer3', 100000);
INSERT INTO CUSTOMER (ID, VERSION, NAME, CREDIT) VALUES (4, 0, 'customer4', 100000);
CREATE TABLE PLAYERS (
PLAYER_ID CHAR(8) NOT NULL PRIMARY KEY,
LAST_NAME VARCHAR(35) NOT NULL,
FIRST_NAME VARCHAR(25) NOT NULL,
POS VARCHAR(10) ,
YEAR_OF_BIRTH BIGINT NOT NULL,
YEAR_DRAFTED BIGINT NOT NULL
) ;
CREATE TABLE GAMES (
PLAYER_ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
TEAM CHAR(3) NOT NULL,
WEEK BIGINT NOT NULL,
OPPONENT CHAR(3) ,
COMPLETES BIGINT ,
ATTEMPTS BIGINT ,
PASSING_YARDS BIGINT ,
PASSING_TD BIGINT ,
INTERCEPTIONS BIGINT ,
RUSHES BIGINT ,
RUSH_YARDS BIGINT ,
RECEPTIONS BIGINT ,
RECEPTIONS_YARDS BIGINT ,
TOTAL_TD BIGINT
) ;
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL,
YEAR_NO BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,
PASSING_YARDS BIGINT NOT NULL ,
PASSING_TD BIGINT NOT NULL ,
INTERCEPTIONS BIGINT NOT NULL ,
RUSHES BIGINT NOT NULL ,
RUSH_YARDS BIGINT NOT NULL ,
RECEPTIONS BIGINT NOT NULL ,
RECEPTIONS_YARDS BIGINT NOT NULL ,
TOTAL_TD BIGINT NOT NULL
) ;
CREATE TABLE ERROR_LOG (
JOB_NAME CHAR(20) ,
STEP_NAME CHAR(20) ,
MESSAGE VARCHAR(300) NOT NULL
);

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-3.1.xsd">
<!-- Initialise the database before every test case: -->
<bean id="dataSourceInitializer" class="test.jdbc.datasource.DataSourceInitializer">
<property name="dataSource" ref="dataSource"/>
<property name="initScripts">
<list>
<value>${batch.drop.script}</value>
<value>${batch.schema.script}</value>
<value>${batch.business.schema.script}</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${batch.jdbc.driver}" />
<property name="url" value="${batch.jdbc.url}" />
<property name="username" value="${batch.jdbc.user}" />
<property name="password" value="${batch.jdbc.password}" />
<property name="validationQuery" value="${batch.jdbc.validationQuery}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Set up or detect a System property called "ENVIRONMENT" used to construct a properties file on the classpath. The default is "hsql". -->
<bean id="environment"
class="org.springframework.batch.support.SystemPropertyInitializer">
<property name="defaultValue" value="hsql"/>
<property name="keyName" value="ENVIRONMENT"/>
</bean>
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
depends-on="environment">
<property name="location" value="classpath:batch-${ENVIRONMENT}.properties" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="1" />
</bean>
<bean id="sequenceIncrementerParent" class="${batch.database.incrementer.class}" abstract="true">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="columnIncrementerParent" class="${batch.database.incrementer.class}" abstract="true" parent="sequenceIncrementerParent">
<property name="columnName" value="ID" />
</bean>
<bean id="incrementerParent" parent="${batch.database.incrementer.parent}">
<property name="incrementerName" value="DUMMY" />
</bean>
</beans>

View File

@@ -0,0 +1,5 @@
AbduKa00,1996,mia,10,nwe,0,0,0,0,0,29,104,,16,2
AbduKa00,1996,mia,11,clt,0,0,0,0,0,18,70,,11,2
AbduKa00,1996,mia,12,oti,0,0,0,0,0,18,59,,0,0
AbduKa00,1996,mia,13,pit,0,0,0,0,0,16,57,,0,0
AbduKa00,1996,mia,14,rai,0,0,0,0,0,18,39,,7,0
1 AbduKa00 1996 mia 10 nwe 0 0 0 0 0 29 104 16 2
2 AbduKa00 1996 mia 11 clt 0 0 0 0 0 18 70 11 2
3 AbduKa00 1996 mia 12 oti 0 0 0 0 0 18 59 0 0
4 AbduKa00 1996 mia 13 pit 0 0 0 0 0 16 57 0 0
5 AbduKa00 1996 mia 14 rai 0 0 0 0 0 18 39 7 0

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
AbduKa00,Abdul-Jabbar,Karim,rb,1974,1996
AbduRa00,Abdullah,Rabih,rb,1975,1999
AberWa00,Abercrombie,Walter,rb,1959,1982
AbraDa00,Abramowicz,Danny,wr,1945,1967
AdamBo00,Adams,Bob,te,1946,1969
AdamCh00,Adams,Charlie,wr,1979,2003
AdamCu00,Adams,Curtis,rb,1962,1985
AdamGe00,Adams,George,rb,1962,1985
AdamGr00,Adams,Grant,wr,2000,2005
AdamJo00,Adams,John,rb,1937,1959
AdamMi00,Adams,Michael,wr,1974,1997
AdamMi01,Adamle,Mike,rb,1949,1971
AdamTo00,Adams,Tony,qb,1950,1975
AdamTo01,Adams,Tom,wr,1940,1962
AdamTo02,Adamle,Tony,rb,1924,1950
AdamWi00,Adams,Willie,wr,1956,1979
AddaJo00,Addai,Joseph,rb,1983,2006
AdkiJa00,Adkisson,James,te,1980,2005
AdkiMa00,Adkins,Margene,wr,1947,1970
AdkiSa00,Adkins,Sam,qb,1955,1977
1 AbduKa00 Abdul-Jabbar Karim rb 1974 1996
2 AbduRa00 Abdullah Rabih rb 1975 1999
3 AberWa00 Abercrombie Walter rb 1959 1982
4 AbraDa00 Abramowicz Danny wr 1945 1967
5 AdamBo00 Adams Bob te 1946 1969
6 AdamCh00 Adams Charlie wr 1979 2003
7 AdamCu00 Adams Curtis rb 1962 1985
8 AdamGe00 Adams George rb 1962 1985
9 AdamGr00 Adams Grant wr 2000 2005
10 AdamJo00 Adams John rb 1937 1959
11 AdamMi00 Adams Michael wr 1974 1997
12 AdamMi01 Adamle Mike rb 1949 1971
13 AdamTo00 Adams Tony qb 1950 1975
14 AdamTo01 Adams Tom wr 1940 1962
15 AdamTo02 Adamle Tony rb 1924 1950
16 AdamWi00 Adams Willie wr 1956 1979
17 AddaJo00 Addai Joseph rb 1983 2006
18 AdkiJa00 Adkisson James te 1980 2005
19 AdkiMa00 Adkins Margene wr 1947 1970
20 AdkiSa00 Adkins Sam qb 1955 1977

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
dn: cn=Bjorn Jensen,ou=Accounting,dc=airius,dc=com
telephonenumber: +1 408 555 1212
objectclass: top
objectclass: person
objectclass: organizationalPerson
sn: Jensen
cn: Bjorn Jensen
dn: cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com
telephonenumber: +1 408 555 1212
uid: bjensen
description: Babs is a big sailing fan, and travels extensively in search of perfect sailing conditions.
objectclass: top
objectclass: person
objectclass: organizationalPerson
title: Product Manager, Rod and Reel Division
sn: Jensen
cn: Barbara Jensen
cn: Barbara J Jensen
cn: Babs Jensen
dn: cn=Gern Jensen,ou=Product Testing,dc=airius,dc=com
telephonenumber: +1 408 555 1212
uid: gernj
description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVlIGlzIGJhc2UtNjQtZW5j
b2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdGVyIGluIGl0IChhIENSKS4NICBC
eSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQgb3V0IG1vcmUu
objectclass: top
objectclass: person
objectclass: organizationalPerson
sn: Jensen
cn: Gern Jensen
cn: Gern O Jensen
dn:: b3U95Za25qWt6YOoLG89QWlyaXVz
ou:: 5Za25qWt6YOo
ou:: 44GI44GE44GO44KH44GG44G2
ou: Sales
description: Japanese office
objectclass: top
objectclass: organizationalUnit
dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz
givenname:: 44Ot44OJ44OL44O8
givenname:: 44KN44Gp44Gr44O8
givenname: Rodney
sn:: 5bCP56yg5Y6f
sn:: 44GK44GM44GV44KP44KJ
sn: Ogasawara
userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM=
mail: rogasawara@airius.co.jp
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
uid: rogasawara
preferredlanguage: ja
cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
cn:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA==
cn: Rodney Ogasawara
title:: 5Za25qWt6YOoIOmDqOmVtw==
title:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg==
title: Sales, Director
dn: cn=Horatio Jensen,ou=Product Testing,dc=airius,dc=com
telephonenumber: +1 408 555 1212
uid: hjensen
jpegphoto:< file:///usr/local/directory/photos/hjensen.jpg
objectclass: top
objectclass: person
objectclass: organizationalPerson
sn: Jensen
cn: Horatio Jensen
cn: Horatio N Jensen

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd">
<job id="job" xmlns="http://www.springframework.org/schema/batch">
<split id="split" task-executor="taskExecutor">
<flow>
<step id="step1" parent="step" />
</flow>
<flow>
<step id="step2" parent="step" />
</flow>
</split>
</job>
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="maxPoolSize" value="6"/>
<property name="queueCapacity" value="0"/>
</bean>
<step id="step" xmlns="http://www.springframework.org/schema/batch">
<tasklet>
<beans:bean class="org.springframework.batch.core.test.step.SplitJobMapRepositoryIntegrationTests$CountingTasklet" />
</tasklet>
</step>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
</beans>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<import resource="data-source-context.xml" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager" />
<bean id="mapJobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"
lazy-init="true" autowire-candidate="false" />
<bean id="jobOperator"
class="org.springframework.batch.core.launch.support.SimpleJobOperator"
p:jobLauncher-ref="jobLauncher" p:jobExplorer-ref="jobExplorer"
p:jobRepository-ref="jobRepository" p:jobRegistry-ref="jobRegistry" />
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
p:dataSource-ref="dataSource" />
<bean id="jobRegistry"
class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>

View File

@@ -0,0 +1,235 @@
version: 1
#
#Examples of LDAP Data Interchange Format
#
#
#Example 1: An simple LDAP file with two entries
#
dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Barbara Jensen
cn: Barbara J Jensen
cn: Babs Jensen
sn: Jensen
uid: bjensen
telephonenumber: +1 408 555 1212
description: A big sailing fan.
dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Bjorn Jensen
sn: Jensen
telephonenumber: +1 408 555 1212
#
# Example 2: A file containing an entry with a folded attribute value
#
dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
objectclass:top
objectclass:person
objectclass:organizationalPerson
cn:Barbara Jensen
cn:Barbara J Jensen
cn:Babs Jensen
sn:Jensen
uid:bjensen
telephonenumber:+1 408 555 1212
description:Babs is a big sailing fan, and travels extensively in sea
rch of perfect sailing conditions.
title:Product Manager, Rod and Reel Division
#
# Example 3: A file containing a base-64-encoded value
#
dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Gern Jensen
cn: Gern O Jensen
sn: Jensen
uid: gernj
telephonenumber: +1 408 555 1212
description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl
IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG
VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg
b3V0IG1vcmUu
#
# Example 4: A file containing an entries with UTF-8-encoded attribute
# values, including language tags. Comments indicate the contents
# of UTF-8-encoded attributes and distinguished names.
#
dn:: b3U95Za25qWt6YOoLG89QWlyaXVz
# dn:: ou=<JapaneseOU>,o=Airius
objectclass: top
objectclass: organizationalUnit
ou:: 5Za25qWt6YOo
# ou:: <JapaneseOU>
ou;lang-ja:: 5Za25qWt6YOo
# ou;lang-ja:: <JapaneseOU>
ou;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2
# ou;lang-ja:: <JapaneseOU_in_phonetic_representation>
ou;lang-en: Sales
description: Japanese office
dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz
# dn:: uid=<uid>,ou=<JapaneseOU>,o=Airius
userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM=
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
uid: rogasawara
mail: rogasawara@airius.co.jp
givenname;lang-ja:: 44Ot44OJ44OL44O8
# givenname;lang-ja:: <JapaneseGivenname>
sn;lang-ja:: 5bCP56yg5Y6f
# sn;lang-ja:: <JapaneseSn>
cn;lang-ja:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
# cn;lang-ja:: <JapaneseCn>
title;lang-ja:: 5Za25qWt6YOoIOmDqOmVtw==
# title;lang-ja:: <JapaneseTitle>
preferredlanguage: ja
givenname:: 44Ot44OJ44OL44O8
# givenname:: <JapaneseGivenname>
sn:: 5bCP56yg5Y6f
# sn:: <JapaneseSn>
cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
# cn:: <JapaneseCn>
title:: 5Za25qWt6YOoIOmDqOmVtw==
# title:: <JapaneseTitle>
givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8
# givenname;lang-ja;phonetic::
# <JapaneseGivenname_in_phonetic_representation_kana>
sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ
# sn;lang-ja;phonetic:: <JapaneseSn_in_phonetic_representation_kana>
cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA==
# cn;lang-ja;phonetic:: <JapaneseCn_in_phonetic_representation_kana>
title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg==
# title;lang-ja;phonetic::
# <JapaneseTitle_in_phonetic_representation_kana>
givenname;lang-en: Rodney
sn;lang-en: Ogasawara
cn;lang-en: Rodney Ogasawara
title;lang-en: Sales, Director
#
# Example 5: An LDIF file containing an invalid attribute
#
dn: cn=Harry Jacobs, ou=Product Development, dc=airius, dc=com
objectClass: top
objectClass: person
cn: Harry Jacobs
description: :A big sailing fan.
#
# Example 6: A file containing a reference to an external file
#
dn: cn=Horatio Jensen, ou=Product Testing, dc=airius, dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Horatio Jensen
cn: Horatio N Jensen
sn: Jensen
uid: hjensen
telephonenumber: +1 408 555 1212
jpegphoto:< file:///usr/local/directory/photos/hjensen.jpg
#
# Example 7: A file containing a series of change records and comments
#
# Add a new entry
dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
changetype: add
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Fiona Jensen
sn: Jensen
uid: fiona
telephonenumber: +1 408 555 1212
jpegphoto:< file:///usr/local/directory/photos/fiona.jpg
# Delete an existing entry
dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com
changetype: delete
# Modify an entry's relative distinguished name
dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
changetype: modrdn
newrdn: cn=Paula Jensen
deleteoldrdn: 1
# Rename an entry and move all of its children to a new location in
# the directory tree (only implemented by LDAPv3 servers).
dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
changetype: modrdn
newrdn: ou=Product Development Accountants
deleteoldrdn: 0
newsuperior: ou=Accounting, dc=airius, dc=com
# Modify an entry: add an additional value to the postaladdress
# attribute, completely delete the description attribute, replace
# the telephonenumber attribute with two values, and delete a specific
# value from the facsimiletelephonenumber attribute
dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com
changetype: modify
add: postaladdress
postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
-
delete: description
-
replace: telephonenumber
telephonenumber: +1 408 555 1234
telephonenumber: +1 408 555 5678
-
delete: facsimiletelephonenumber
facsimiletelephonenumber: +1 408 555 9876
-
# Modify an entry: replace the postaladdress attribute with an empty
# set of values (which will cause the attribute to be removed), and
# delete the entire description attribute. Note that the first will
# always succeed, while the second will only succeed if at least
# one value for the description attribute is present.
dn: cn=Ingrid Jensen, ou=Product Support, dc=airius, dc=com
changetype: modify
replace: postaladdress
-
delete: description
-
#
# Example 8: An LDIF file containing a change record with a control
#
# Delete an entry. The operation will attach the LDAPv3
# Tree Delete Control defined in [9]. The criticality
# field is "true" and the controlValue field is
# absent, as required by [9].
dn: ou=Product Development, dc=airius, dc=com
control: 1.2.840.113556.1.4.805 true
changetype: delete