/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.test;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.repository.dao.AbstractJdbcBatchMetadataDao;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.Assert;
/**
* Convenience class for creating and removing {@link JobExecution} instances
* from a database. Typical usage in test case would be to create instances
* before a transaction, save the result, and then use it to remove them after
* the transaction.
*
* @author Dave Syer
*/
public class JobRepositoryTestUtils extends AbstractJdbcBatchMetadataDao implements InitializingBean {
private JobRepository jobRepository;
private JobParametersIncrementer jobParametersIncrementer = new JobParametersIncrementer() {
Long count = 0L;
@Override
public JobParameters getNext(JobParameters parameters) {
return new JobParameters(Collections.singletonMap("count", new JobParameter(count++)));
}
};
private JdbcOperations jdbcTemplate;
/**
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(jobRepository, "JobRepository must be set");
Assert.notNull(jdbcTemplate, "DataSource must be set");
}
/**
* Default constructor.
*/
public JobRepositoryTestUtils() {
}
/**
* Create a {@link JobRepositoryTestUtils} with all its mandatory
* properties.
*
* @param jobRepository a {@link JobRepository} backed by a database
* @param dataSource a {@link DataSource}
*/
public JobRepositoryTestUtils(JobRepository jobRepository, DataSource dataSource) {
super();
this.jobRepository = jobRepository;
setDataSource(dataSource);
}
public final void setDataSource(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* @param jobParametersIncrementer the jobParametersIncrementer to set
*/
public void setJobParametersIncrementer(JobParametersIncrementer jobParametersIncrementer) {
this.jobParametersIncrementer = jobParametersIncrementer;
}
/**
* @param jobRepository the jobRepository to set
*/
public void setJobRepository(JobRepository jobRepository) {
this.jobRepository = jobRepository;
}
/**
* Use the {@link JobRepository} to create some {@link JobExecution}
* instances each with the given job name and each having step executions
* with the given step names.
*
* @param jobName the name of the job
* @param stepNames the names of the step executions
* @param count the required number of instances of {@link JobExecution} to
* create
* @return a collection of {@link JobExecution}
*/
public List<JobExecution> createJobExecutions(String jobName, String[] stepNames, int count)
throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
List<JobExecution> list = new ArrayList<JobExecution>();
JobParameters jobParameters = new JobParameters();
for (int i = 0; i < count; i++) {
JobExecution jobExecution = jobRepository.createJobExecution(jobName, jobParametersIncrementer
.getNext(jobParameters));
list.add(jobExecution);
for (String stepName : stepNames) {
jobRepository.add(jobExecution.createStepExecution(stepName));
}
}
return list;
}
/**
* Use the {@link JobRepository} to create some {@link JobExecution}
* instances each with a single step execution.
*
* @param count the required number of instances of {@link JobExecution} to
* create
* @return a collection of {@link JobExecution}
*/
public List<JobExecution> createJobExecutions(int count) throws JobExecutionAlreadyRunningException,
JobRestartException, JobInstanceAlreadyCompleteException {
return createJobExecutions("job", new String[] { "step" }, count);
}
/**
* Remove the {@link JobExecution} instances, and all associated
* {@link JobInstance} and {@link StepExecution} instances from the standard
* RDBMS locations used by Spring Batch.
*
* @param list a list of {@link JobExecution}
* @throws DataAccessException if there is a problem
*/
public void removeJobExecutions(Collection<JobExecution> list) throws DataAccessException {
for (JobExecution jobExecution : list) {
List<Long> stepExecutionIds = jdbcTemplate.query(
getQuery("select STEP_EXECUTION_ID from %PREFIX%STEP_EXECUTION where JOB_EXECUTION_ID=?"),
new RowMapper<Long>() {
@Override
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getLong(1);
}
}, jobExecution.getId());
for (Long stepExecutionId : stepExecutionIds) {
jdbcTemplate.update(getQuery("delete from %PREFIX%STEP_EXECUTION_CONTEXT where STEP_EXECUTION_ID=?"),
stepExecutionId);
jdbcTemplate.update(getQuery("delete from %PREFIX%STEP_EXECUTION where STEP_EXECUTION_ID=?"),
stepExecutionId);
}
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_EXECUTION_CONTEXT where JOB_EXECUTION_ID=?"),
jobExecution.getId());
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_EXECUTION_PARAMS where JOB_EXECUTION_ID=?"), jobExecution
.getId());
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_EXECUTION where JOB_EXECUTION_ID=?"), jobExecution
.getId());
}
for (JobExecution jobExecution : list) {
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_INSTANCE where JOB_INSTANCE_ID=?"), jobExecution
.getJobId());
}
}
/**
* Remove all the {@link JobExecution} instances, and all associated
* {@link JobInstance} and {@link StepExecution} instances from the standard
* RDBMS locations used by Spring Batch.
*
* @throws DataAccessException if there is a problem
*/
public void removeJobExecutions() throws DataAccessException {
jdbcTemplate.update(getQuery("delete from %PREFIX%STEP_EXECUTION_CONTEXT"));
jdbcTemplate.update(getQuery("delete from %PREFIX%STEP_EXECUTION"));
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_EXECUTION_CONTEXT"));
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_EXECUTION_PARAMS"));
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_EXECUTION"));
jdbcTemplate.update(getQuery("delete from %PREFIX%JOB_INSTANCE"));
}
}