IN PROGRESS - issue BATCH-340: Refactor JobRepository for greater clarity and consistency.

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

JobDao split into JobInstanceDao and JobExecutionDao
This commit is contained in:
robokaso
2008-02-12 10:15:52 +00:00
parent 1c620b1b02
commit 12cce44e67
22 changed files with 711 additions and 671 deletions

View File

@@ -32,6 +32,8 @@ import org.springframework.batch.core.repository.BatchRestartException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.execution.repository.dao.JobDao;
import org.springframework.batch.execution.repository.dao.JobExecutionDao;
import org.springframework.batch.execution.repository.dao.JobInstanceDao;
import org.springframework.batch.execution.repository.dao.StepExecutionDao;
import org.springframework.batch.execution.repository.dao.StepInstanceDao;
import org.springframework.batch.item.ExecutionAttributes;
@@ -55,7 +57,9 @@ import org.springframework.util.Assert;
*/
public class SimpleJobRepository implements JobRepository {
private JobDao jobDao;
private JobInstanceDao jobDao;
private JobExecutionDao jobExecutionDao;
private StepInstanceDao stepInstanceDao;
@@ -68,9 +72,11 @@ public class SimpleJobRepository implements JobRepository {
SimpleJobRepository() {
}
public SimpleJobRepository(JobDao jobDao, StepInstanceDao stepInstanceDao, StepExecutionDao stepExecutionDao) {
public SimpleJobRepository(JobInstanceDao jobDao, JobExecutionDao jobExecutionDao, StepInstanceDao stepInstanceDao,
StepExecutionDao stepExecutionDao) {
super();
this.jobDao = jobDao;
this.jobExecutionDao = jobExecutionDao;
this.stepInstanceDao = stepInstanceDao;
this.stepExecutionDao = stepExecutionDao;
}
@@ -166,11 +172,11 @@ public class SimpleJobRepository implements JobRepository {
// One job was found
jobInstance = (JobInstance) jobs.get(0);
jobInstance.setStepInstances(findStepInstances(job.getSteps(), jobInstance));
jobInstance.setJobExecutionCount(jobDao.getJobExecutionCount(jobInstance.getId()));
jobInstance.setJobExecutionCount(jobExecutionDao.getJobExecutionCount(jobInstance.getId()));
if (jobInstance.getJobExecutionCount() > job.getStartLimit()) {
throw new BatchRestartException("Restart Max exceeded for Job: " + jobInstance.toString());
}
List executions = jobDao.findJobExecutions(jobInstance);
List executions = jobExecutionDao.findJobExecutions(jobInstance);
for (Iterator iterator = executions.iterator(); iterator.hasNext();) {
JobExecution execution = (JobExecution) iterator.next();
if (execution.isRunning()) {
@@ -219,11 +225,11 @@ public class SimpleJobRepository implements JobRepository {
if (jobExecution.getId() == null) {
// existing instance
jobDao.saveJobExecution(jobExecution);
jobExecutionDao.saveJobExecution(jobExecution);
}
else {
// new execution
jobDao.updateJobExecution(jobExecution);
jobExecutionDao.updateJobExecution(jobExecution);
}
}

View File

@@ -1,504 +0,0 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.execution.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.repository.NoSuchBatchDomainObjectException;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link JobDao}. Uses sequences (via Spring's
* {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
* before inserting a new row. Objects are checked to ensure all mandatory
* fields to be stored are not null. If any are found to be null, an
* IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
* however, the exception will be fairly vague, and fails to highlight which
* field caused the exception.
*
* @author Lucas Ward
* @author Dave Syer
*/
public class JdbcJobDao extends AbstractJdbcBatchMetadataDao implements JobDao, InitializingBean {
private static final String CHECK_JOB_EXECUTION_EXISTS = "SELECT COUNT(*) FROM %PREFIX%JOB_EXECUTION WHERE JOB_EXECUTION_ID = ?";
// Job SQL statements
private static final String CREATE_JOB = "INSERT into %PREFIX%JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY)"
+ " values (?, ?, ?)";
private static final String CREATE_JOB_PARAMETERS = "INSERT into %PREFIX%JOB_PARAMS(JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, " +
"STRING_VAL, DATE_VAL, LONG_VAL) values (?, ?, ?, ?, ?, ?)";
private static final int EXIT_MESSAGE_LENGTH = 250;
private static final String FIND_JOBS = "SELECT JOB_INSTANCE_ID, LAST_JOB_EXECUTION_ID from %PREFIX%JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?";
private static final String GET_JOB_EXECUTION_COUNT = "SELECT count(JOB_EXECUTION_ID) from %PREFIX%JOB_EXECUTION "
+ "where JOB_INSTANCE_ID = ?";
protected static final Log logger = LogFactory.getLog(JdbcJobDao.class);
private static final String SAVE_JOB_EXECUTION = "INSERT into %PREFIX%JOB_EXECUTION(JOB_EXECUTION_ID, JOB_INSTANCE_ID, START_TIME, "
+ "END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE) values (?, ?, ?, ?, ?, ?, ?, ?)";
private static final String UPDATE_JOB = "UPDATE %PREFIX%JOB_INSTANCE set LAST_JOB_EXECUTION_ID = ? where JOB_INSTANCE_ID = ?";
// Job Execution SqlStatements
private static final String UPDATE_JOB_EXECUTION = "UPDATE %PREFIX%JOB_EXECUTION set START_TIME = ?, END_TIME = ?, "
+ " STATUS = ?, CONTINUABLE = ?, EXIT_CODE = ?, EXIT_MESSAGE = ? where JOB_EXECUTION_ID = ?";
private DataFieldMaxValueIncrementer jobExecutionIncrementer;
private DataFieldMaxValueIncrementer jobIncrementer;
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*
* Ensure jdbcTemplate and incrementers have been provided.
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(jobIncrementer, "JobIncrementor cannot be null");
Assert.notNull(jobExecutionIncrementer,
"JobExecutionIncrementer cannot be null");
}
/**
* In this sql implementation a job id is obtained by asking the
* jobIncrementer (which is likely a sequence) for the nextLong, and then
* passing the Id and identifier values (job name, jobKey, schedule date)
* into an INSERT statement.
*
* @see JobDao#createJob(JobIdentifier)
* @throws IllegalArgumentException
* if any {@link JobIdentifier} fields are null.
*/
public JobInstance createJobInstance(String jobName, JobParameters jobParameters) {
Assert.notNull(jobName, "Job Name must not be null.");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Long jobId = new Long(jobIncrementer.nextLongValue());
Object[] parameters = new Object[] { jobId, jobName, createJobKey(jobParameters) };
getJdbcTemplate().update(getCreateJobQuery(), parameters, new int[] {
Types.INTEGER, Types.VARCHAR, Types.VARCHAR});
insertJobParameters(jobId, jobParameters);
JobInstance jobInstance = new JobInstance(jobId, jobParameters);
return jobInstance;
}
private String createJobKey(JobParameters jobParameters){
Map props = jobParameters.getParameters();
StringBuilder stringBuilder = new StringBuilder();
for(Iterator it = props.entrySet().iterator();it.hasNext();){
Entry entry = (Entry)it.next();
stringBuilder.append(entry.toString() + ";");
}
return stringBuilder.toString();
}
public List findJobExecutions(final JobInstance job) {
Assert.notNull(job, "Job cannot be null.");
Assert.notNull(job.getId(), "Job Id cannot be null.");
return getJdbcTemplate().query(
getQuery(JobExecutionRowMapper.FIND_JOB_EXECUTIONS),
new Object[] { job.getId() }, new JobExecutionRowMapper(job));
}
public JobExecution getJobExecution(Long jobExecutionId) {
Assert.notNull(jobExecutionId, "Job Execution id must not be null.");
List executions = getJdbcTemplate().query(
getQuery(JobExecutionRowMapper.GET_JOB_EXECUTION),
new Object[] { jobExecutionId }, new JobExecutionRowMapper(null));
JobExecution jobExecution;
if(executions.size() == 1){
jobExecution = (JobExecution)executions.get(0);
}
else if(executions.size() == 0){
jobExecution = null;
}
else{
throw new IncorrectResultSizeDataAccessException("Only one JobExecution may exist for given id: [" +
jobExecutionId + "]", 1, executions.size());
}
return jobExecution;
}
/**
* The job table is queried for <strong>any</strong> jobs that match the
* given identifier, adding them to a list via the RowMapper callback.
*
* @see JobDao#findJobInstances(JobIdentifier)
* @throws IllegalArgumentException
* if any {@link JobIdentifier} fields are null.
*/
public List findJobInstances(final String jobName, final JobParameters jobParameters) {
Assert.notNull(jobName, "Job Name must not be null.");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Object[] parameters = new Object[] { jobName,
createJobKey(jobParameters) };
RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobInstance jobInstance = new JobInstance(new Long(rs.getLong(1)), jobParameters);
long lastExecutionId = rs.getLong(2);
JobExecution lastExecution = getJobExecution(new Long(lastExecutionId));
if(lastExecution != null){
lastExecution.setJobInstance(jobInstance);
}
jobInstance.setLastExecution(lastExecution);
return jobInstance;
}
};
return getJdbcTemplate().query(getFindJobsQuery(), parameters, rowMapper);
}
private String getCheckJobExecutionExistsQuery() {
return getQuery(CHECK_JOB_EXECUTION_EXISTS);
}
private String getCreateJobQuery() {
return getQuery(CREATE_JOB);
}
private String getFindJobsQuery() {
return getQuery(FIND_JOBS);
}
private String getCreateJobParamsQuery(){
return getQuery(CREATE_JOB_PARAMETERS);
}
/**
* @see JobDao#getJobExecutionCount(JobInstance)
* @throws IllegalArgumentException
* if jobId is null.
*/
public int getJobExecutionCount(Long jobId) {
Assert.notNull(jobId, "JobId cannot be null");
Object[] parameters = new Object[] { jobId };
return getJdbcTemplate()
.queryForInt(getJobExecutionCountQuery(), parameters);
}
private String getJobExecutionCountQuery() {
return getQuery(GET_JOB_EXECUTION_COUNT);
}
private String getSaveJobExecutionQuery() {
return getQuery(SAVE_JOB_EXECUTION);
}
private String getUpdateJobExecutionQuery() {
return getQuery(UPDATE_JOB_EXECUTION);
}
private String getUpdateJobQuery() {
return getQuery(UPDATE_JOB);
}
/*
* Convenience method that inserts all parameters from the provided JobParameters.
*
*/
private void insertJobParameters(Long jobId, JobParameters jobParameters){
Map parameters = jobParameters.getStringParameters();
if(!parameters.isEmpty()){
for(Iterator it = parameters.entrySet().iterator(); it.hasNext();){
Entry entry = (Entry)it.next();
insertParameter(jobId, ParameterType.STRING, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getLongParameters();
if(!parameters.isEmpty()){
for(Iterator it = parameters.entrySet().iterator(); it.hasNext();){
Entry entry = (Entry)it.next();
insertParameter(jobId, ParameterType.LONG, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getDateParameters();
if(!parameters.isEmpty()){
for(Iterator it = parameters.entrySet().iterator(); it.hasNext();){
Entry entry = (Entry)it.next();
insertParameter(jobId, ParameterType.DATE, entry.getKey().toString(), entry.getValue());
}
}
}
/*
* Convenience method that inserts an individual records into the JobParameters table.
*/
private void insertParameter(Long jobId, ParameterType type, String key, Object value){
Object[] args = new Object[0];
int[] argTypes = new int[]{Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.INTEGER};
if(type == ParameterType.STRING){
args = new Object[]{jobId, key, type, value, new Timestamp(0L), new Long(0)};
}
else if(type == ParameterType.LONG){
args = new Object[]{jobId, key, type, "", new Timestamp(0L), value};
}
else if(type == ParameterType.DATE){
args = new Object[]{jobId, key, type, "", value, new Long(0)};
}
getJdbcTemplate().update(getCreateJobParamsQuery(), args, argTypes);
}
/**
*
* SQL implementation using Sequences via the Spring incrementer
* abstraction. Once a new id has been obtained, the JobExecution is saved
* via a SQL INSERT statement.
*
* @see JobDao#saveJobExecution(JobExecution)
* @throws IllegalArgumentException
* if jobExecution is null, as well as any of it's fields to be
* persisted.
*/
public void saveJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
jobExecution.setId(new Long(jobExecutionIncrementer.nextLongValue()));
Object[] parameters = new Object[] { jobExecution.getId(),
jobExecution.getJobId(), jobExecution.getStartTime(),
jobExecution.getEndTime(), jobExecution.getStatus().toString(),
jobExecution.getExitStatus().isContinuable() ? "Y" : "N",
jobExecution.getExitStatus().getExitCode(),
jobExecution.getExitStatus().getExitDescription() };
getJdbcTemplate().update(getSaveJobExecutionQuery(), parameters, new int[] {
Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.TIMESTAMP,
Types.VARCHAR, Types.CHAR, Types.VARCHAR, Types.VARCHAR });
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobExecution} instances.
*
* @param jobExecutionIncrementer
* the {@link DataFieldMaxValueIncrementer}
*/
public void setJobExecutionIncrementer(
DataFieldMaxValueIncrementer jobExecutionIncrementer) {
this.jobExecutionIncrementer = jobExecutionIncrementer;
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobInstance} instances.
*
* @param jobIncrementer
* the {@link DataFieldMaxValueIncrementer}
*/
public void setJobIncrementer(DataFieldMaxValueIncrementer jobIncrementer) {
this.jobIncrementer = jobIncrementer;
}
/**
* Update given JobExecution using a SQL UPDATE statement. The JobExecution
* is first checked to ensure all fields are not null, and that it has an
* ID. The database is then queried to ensure that the ID exists, which
* ensures that it is valid.
*
* @see JobDao#updateJobExecution(JobExecution)
*/
public void updateJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
String exitDescription = jobExecution.getExitStatus()
.getExitDescription();
if (exitDescription != null
&& exitDescription.length() > EXIT_MESSAGE_LENGTH) {
exitDescription = exitDescription.substring(0, EXIT_MESSAGE_LENGTH);
logger
.debug("Truncating long message before update of JobExecution: "
+ jobExecution);
}
Object[] parameters = new Object[] { jobExecution.getStartTime(),
jobExecution.getEndTime(), jobExecution.getStatus().toString(),
jobExecution.getExitStatus().isContinuable() ? "Y" : "N",
jobExecution.getExitStatus().getExitCode(), exitDescription,
jobExecution.getId() };
if (jobExecution.getId() == null) {
throw new IllegalArgumentException(
"JobExecution ID cannot be null. JobExecution must be saved "
+ "before it can be updated.");
}
// Check if given JobExecution's Id already exists, if none is found it
// is invalid and
// an exception should be thrown.
if (getJdbcTemplate().queryForInt(getCheckJobExecutionExistsQuery(),
new Object[] { jobExecution.getId() }) != 1) {
throw new NoSuchBatchDomainObjectException(
"Invalid JobExecution, ID " + jobExecution.getId()
+ " not found.");
}
getJdbcTemplate()
.update(getUpdateJobExecutionQuery(), parameters,
new int[] { Types.TIMESTAMP, Types.TIMESTAMP,
Types.VARCHAR, Types.CHAR, Types.VARCHAR,
Types.VARCHAR, Types.INTEGER });
}
/**
* @see JobDao#updateJobInstance(JobInstance)
* @throws IllegalArgumentException
* if Job, Job.status, or job.id is null
*/
public void updateJobInstance(JobInstance jobInstance) {
Assert.notNull(jobInstance, "Job Cannot be Null");
Assert.notNull(jobInstance.getId(), "Job ID cannot be null");
Long lastExecutionId = jobInstance.getLastExecution() == null ? null : jobInstance.getLastExecution().getId();
Object[] parameters = new Object[] { lastExecutionId, jobInstance.getId() };
getJdbcTemplate().update(getUpdateJobQuery(), parameters, new int[] {
Types.INTEGER, Types.INTEGER});
}
/*
* Validate JobExecution. At a minimum, JobId, StartTime, EndTime, and
* Status cannot be null.
*
* @param jobExecution @throws IllegalArgumentException
*/
private void validateJobExecution(JobExecution jobExecution) {
Assert.notNull(jobExecution);
Assert.notNull(jobExecution.getJobId(),
"JobExecution Job-Id cannot be null.");
Assert.notNull(jobExecution.getStartTime(),
"JobExecution start time cannot be null.");
Assert.notNull(jobExecution.getStatus(),
"JobExecution status cannot be null.");
}
/**
* Re-usable mapper for {@link JobExecution} instances.
*
* @author Dave Syer
*
*/
public static class JobExecutionRowMapper implements RowMapper {
public static final String FIND_JOB_EXECUTIONS = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_INSTANCE_ID = ?";
public static final String GET_JOB_EXECUTION = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_EXECUTION_ID = ?";
private JobInstance job;
public JobExecutionRowMapper(JobInstance job) {
super();
this.job = job;
}
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobExecution jobExecution = new JobExecution(job);
jobExecution.setId(new Long(rs.getLong(1)));
jobExecution.setStartTime(rs.getTimestamp(2));
jobExecution.setEndTime(rs.getTimestamp(3));
jobExecution.setStatus(BatchStatus.getStatus(rs.getString(4)));
jobExecution.setExitStatus(new ExitStatus("Y".equals(rs
.getString(5)), rs.getString(6), rs.getString(7)));
return jobExecution;
}
}
private static class ParameterType {
private final String type;
private ParameterType(String type) {
this.type = type;
}
public String toString(){
return type;
}
public static final ParameterType STRING = new ParameterType("STRING");
public static final ParameterType DATE = new ParameterType("DATE");
public static final ParameterType LONG = new ParameterType("LONG");
private static final ParameterType[] VALUES = {STRING, DATE, LONG};
public static ParameterType getType(String typeAsString){
for(int i = 0; i < VALUES.length; i++){
if(VALUES[i].toString().equals(typeAsString)){
return (ParameterType)VALUES[i];
}
}
return null;
}
}
}

View File

@@ -0,0 +1,243 @@
package org.springframework.batch.execution.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.repository.NoSuchBatchDomainObjectException;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link JobExecutionDao}. Uses sequences (via Spring's
* {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
* before inserting a new row. Objects are checked to ensure all mandatory
* fields to be stored are not null. If any are found to be null, an
* IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
* however, the exception will be fairly vague, and fails to highlight which
* field caused the exception.
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*/
public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements JobExecutionDao, InitializingBean {
private static final Log logger = LogFactory.getLog(JdbcJobExecutionDao.class);
private static final int EXIT_MESSAGE_LENGTH = 250;
private static final String GET_JOB_EXECUTION_COUNT = "SELECT count(JOB_EXECUTION_ID) from %PREFIX%JOB_EXECUTION "
+ "where JOB_INSTANCE_ID = ?";
private static final String SAVE_JOB_EXECUTION = "INSERT into %PREFIX%JOB_EXECUTION(JOB_EXECUTION_ID, JOB_INSTANCE_ID, START_TIME, "
+ "END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE) values (?, ?, ?, ?, ?, ?, ?, ?)";
private static final String CHECK_JOB_EXECUTION_EXISTS = "SELECT COUNT(*) FROM %PREFIX%JOB_EXECUTION WHERE JOB_EXECUTION_ID = ?";
private static final String UPDATE_JOB_EXECUTION = "UPDATE %PREFIX%JOB_EXECUTION set START_TIME = ?, END_TIME = ?, "
+ " STATUS = ?, CONTINUABLE = ?, EXIT_CODE = ?, EXIT_MESSAGE = ? where JOB_EXECUTION_ID = ?";
private DataFieldMaxValueIncrementer jobExecutionIncrementer;
public List findJobExecutions(final JobInstance job) {
Assert.notNull(job, "Job cannot be null.");
Assert.notNull(job.getId(), "Job Id cannot be null.");
return getJdbcTemplate().query(getQuery(JobExecutionRowMapper.FIND_JOB_EXECUTIONS),
new Object[] { job.getId() }, new JobExecutionRowMapper(job));
}
public JobExecution getJobExecution(Long jobExecutionId) {
Assert.notNull(jobExecutionId, "Job Execution id must not be null.");
List executions = getJdbcTemplate().query(getQuery(JobExecutionRowMapper.GET_JOB_EXECUTION),
new Object[] { jobExecutionId }, new JobExecutionRowMapper(null));
JobExecution jobExecution;
if (executions.size() == 1) {
jobExecution = (JobExecution) executions.get(0);
}
else if (executions.size() == 0) {
jobExecution = null;
}
else {
throw new IncorrectResultSizeDataAccessException("Only one JobExecution may exist for given id: ["
+ jobExecutionId + "]", 1, executions.size());
}
return jobExecution;
}
/**
* @see JobDao#getJobExecutionCount(JobInstance)
* @throws IllegalArgumentException
* if jobId is null.
*/
public int getJobExecutionCount(Long jobId) {
Assert.notNull(jobId, "JobId cannot be null");
Object[] parameters = new Object[] { jobId };
return getJdbcTemplate()
.queryForInt(getQuery(GET_JOB_EXECUTION_COUNT), parameters);
}
/**
*
* SQL implementation using Sequences via the Spring incrementer
* abstraction. Once a new id has been obtained, the JobExecution is saved
* via a SQL INSERT statement.
*
* @see JobDao#saveJobExecution(JobExecution)
* @throws IllegalArgumentException
* if jobExecution is null, as well as any of it's fields to be
* persisted.
*/
public void saveJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
jobExecution.setId(new Long(jobExecutionIncrementer.nextLongValue()));
Object[] parameters = new Object[] { jobExecution.getId(),
jobExecution.getJobId(), jobExecution.getStartTime(),
jobExecution.getEndTime(), jobExecution.getStatus().toString(),
jobExecution.getExitStatus().isContinuable() ? "Y" : "N",
jobExecution.getExitStatus().getExitCode(),
jobExecution.getExitStatus().getExitDescription() };
getJdbcTemplate().update(getQuery(SAVE_JOB_EXECUTION), parameters, new int[] {
Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.TIMESTAMP,
Types.VARCHAR, Types.CHAR, Types.VARCHAR, Types.VARCHAR });
}
/**
* Validate JobExecution. At a minimum, JobId, StartTime, EndTime, and
* Status cannot be null.
*
* @param jobExecution @throws IllegalArgumentException
*/
private void validateJobExecution(JobExecution jobExecution) {
Assert.notNull(jobExecution);
Assert.notNull(jobExecution.getJobId(),
"JobExecution Job-Id cannot be null.");
Assert.notNull(jobExecution.getStartTime(),
"JobExecution start time cannot be null.");
Assert.notNull(jobExecution.getStatus(),
"JobExecution status cannot be null.");
}
/**
* Update given JobExecution using a SQL UPDATE statement. The JobExecution
* is first checked to ensure all fields are not null, and that it has an
* ID. The database is then queried to ensure that the ID exists, which
* ensures that it is valid.
*
* @see JobDao#updateJobExecution(JobExecution)
*/
public void updateJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
String exitDescription = jobExecution.getExitStatus()
.getExitDescription();
if (exitDescription != null
&& exitDescription.length() > EXIT_MESSAGE_LENGTH) {
exitDescription = exitDescription.substring(0, EXIT_MESSAGE_LENGTH);
logger
.debug("Truncating long message before update of JobExecution: "
+ jobExecution);
}
Object[] parameters = new Object[] { jobExecution.getStartTime(),
jobExecution.getEndTime(), jobExecution.getStatus().toString(),
jobExecution.getExitStatus().isContinuable() ? "Y" : "N",
jobExecution.getExitStatus().getExitCode(), exitDescription,
jobExecution.getId() };
if (jobExecution.getId() == null) {
throw new IllegalArgumentException(
"JobExecution ID cannot be null. JobExecution must be saved "
+ "before it can be updated.");
}
// Check if given JobExecution's Id already exists, if none is found it
// is invalid and
// an exception should be thrown.
if (getJdbcTemplate().queryForInt(getQuery(CHECK_JOB_EXECUTION_EXISTS),
new Object[] { jobExecution.getId() }) != 1) {
throw new NoSuchBatchDomainObjectException(
"Invalid JobExecution, ID " + jobExecution.getId()
+ " not found.");
}
getJdbcTemplate()
.update(getQuery(UPDATE_JOB_EXECUTION), parameters,
new int[] { Types.TIMESTAMP, Types.TIMESTAMP,
Types.VARCHAR, Types.CHAR, Types.VARCHAR,
Types.VARCHAR, Types.INTEGER });
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobExecution} instances.
*
* @param jobExecutionIncrementer
* the {@link DataFieldMaxValueIncrementer}
*/
public void setJobExecutionIncrementer(
DataFieldMaxValueIncrementer jobExecutionIncrementer) {
this.jobExecutionIncrementer = jobExecutionIncrementer;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notNull(jobExecutionIncrementer);
}
/**
* Re-usable mapper for {@link JobExecution} instances.
*
* @author Dave Syer
*
*/
public static class JobExecutionRowMapper implements RowMapper {
public static final String FIND_JOB_EXECUTIONS = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_INSTANCE_ID = ?";
public static final String GET_JOB_EXECUTION = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_EXECUTION_ID = ?";
private JobInstance job;
public JobExecutionRowMapper(JobInstance job) {
super();
this.job = job;
}
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobExecution jobExecution = new JobExecution(job);
jobExecution.setId(new Long(rs.getLong(1)));
jobExecution.setStartTime(rs.getTimestamp(2));
jobExecution.setEndTime(rs.getTimestamp(3));
jobExecution.setStatus(BatchStatus.getStatus(rs.getString(4)));
jobExecution.setExitStatus(new ExitStatus("Y".equals(rs.getString(5)), rs.getString(6), rs.getString(7)));
return jobExecution;
}
}
}

View File

@@ -0,0 +1,245 @@
package org.springframework.batch.execution.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link JobInstanceDao}. Uses sequences (via Spring's
* {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
* before inserting a new row. Objects are checked to ensure all mandatory
* fields to be stored are not null. If any are found to be null, an
* IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
* however, the exception will be fairly vague, and fails to highlight which
* field caused the exception.
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*/
public class JdbcJobInstanceDao extends AbstractJdbcBatchMetadataDao implements JobInstanceDao, InitializingBean {
private static final String CREATE_JOB = "INSERT into %PREFIX%JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY)"
+ " values (?, ?, ?)";
private static final String CREATE_JOB_PARAMETERS = "INSERT into %PREFIX%JOB_PARAMS(JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, "
+ "STRING_VAL, DATE_VAL, LONG_VAL) values (?, ?, ?, ?, ?, ?)";
private static final String FIND_JOBS = "SELECT JOB_INSTANCE_ID, LAST_JOB_EXECUTION_ID from %PREFIX%JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?";
private static final String UPDATE_JOB = "UPDATE %PREFIX%JOB_INSTANCE set LAST_JOB_EXECUTION_ID = ? where JOB_INSTANCE_ID = ?";
private DataFieldMaxValueIncrementer jobIncrementer;
private JobExecutionDao jobExecutionDao;
/**
* In this jdbc implementation a job id is obtained by asking the
* jobIncrementer (which is likely a sequence) for the nextLong, and then
* passing the Id and parameter values into an INSERT statement.
*
* @see JobDao#createJob(JobIdentifier)
* @throws IllegalArgumentException if any {@link JobIdentifier} fields are
* null.
*/
public JobInstance createJobInstance(String jobName, JobParameters jobParameters) {
Assert.notNull(jobName, "Job Name must not be null.");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Long jobId = new Long(jobIncrementer.nextLongValue());
Object[] parameters = new Object[] { jobId, jobName, createJobKey(jobParameters) };
getJdbcTemplate().update(getQuery(CREATE_JOB), parameters,
new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR });
insertJobParameters(jobId, jobParameters);
JobInstance jobInstance = new JobInstance(jobId, jobParameters);
return jobInstance;
}
private String createJobKey(JobParameters jobParameters) {
Map props = jobParameters.getParameters();
StringBuilder stringBuilder = new StringBuilder();
for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
stringBuilder.append(entry.toString() + ";");
}
return stringBuilder.toString();
}
/**
* Convenience method that inserts all parameters from the provided
* JobParameters.
*
*/
private void insertJobParameters(Long jobId, JobParameters jobParameters) {
Map parameters = jobParameters.getStringParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.STRING, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getLongParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.LONG, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getDateParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.DATE, entry.getKey().toString(), entry.getValue());
}
}
}
/**
* Convenience method that inserts an individual records into the
* JobParameters table.
*/
private void insertParameter(Long jobId, ParameterType type, String key, Object value) {
Object[] args = new Object[0];
int[] argTypes = new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP,
Types.INTEGER };
if (type == ParameterType.STRING) {
args = new Object[] { jobId, key, type, value, new Timestamp(0L), new Long(0) };
}
else if (type == ParameterType.LONG) {
args = new Object[] { jobId, key, type, "", new Timestamp(0L), value };
}
else if (type == ParameterType.DATE) {
args = new Object[] { jobId, key, type, "", value, new Long(0) };
}
getJdbcTemplate().update(getQuery(CREATE_JOB_PARAMETERS), args, argTypes);
}
/**
* The job table is queried for <strong>any</strong> jobs that match the
* given identifier, adding them to a list via the RowMapper callback.
*
* @see JobDao#findJobInstances(JobIdentifier)
* @throws IllegalArgumentException
* if any {@link JobIdentifier} fields are null.
*/
public List findJobInstances(final String jobName, final JobParameters jobParameters) {
Assert.notNull(jobName, "Job Name must not be null.");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Object[] parameters = new Object[] { jobName,
createJobKey(jobParameters) };
RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobInstance jobInstance = new JobInstance(new Long(rs.getLong(1)), jobParameters);
long lastExecutionId = rs.getLong(2);
JobExecution lastExecution = jobExecutionDao.getJobExecution(new Long(lastExecutionId));
if(lastExecution != null){
lastExecution.setJobInstance(jobInstance);
}
jobInstance.setLastExecution(lastExecution);
return jobInstance;
}
};
return getJdbcTemplate().query(getQuery(FIND_JOBS), parameters, rowMapper);
}
/**
* @see JobDao#updateJobInstance(JobInstance)
* @throws IllegalArgumentException
* if Job, Job.status, or job.id is null
*/
public void updateJobInstance(JobInstance jobInstance) {
Assert.notNull(jobInstance, "Job Cannot be Null");
Assert.notNull(jobInstance.getId(), "Job ID cannot be null");
Long lastExecutionId = jobInstance.getLastExecution() == null ? null : jobInstance.getLastExecution().getId();
Object[] parameters = new Object[] { lastExecutionId, jobInstance.getId() };
getJdbcTemplate().update(getQuery(UPDATE_JOB), parameters, new int[] {
Types.INTEGER, Types.INTEGER});
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobInstance} instances.
*
* @param jobIncrementer the {@link DataFieldMaxValueIncrementer}
*/
public void setJobIncrementer(DataFieldMaxValueIncrementer jobIncrementer) {
this.jobIncrementer = jobIncrementer;
}
public void setJobExecutionDao(JobExecutionDao jobExecutionDao) {
this.jobExecutionDao = jobExecutionDao;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notNull(jobExecutionDao);
Assert.notNull(jobIncrementer);
}
private static class ParameterType {
private final String type;
private ParameterType(String type) {
this.type = type;
}
public String toString() {
return type;
}
public static final ParameterType STRING = new ParameterType("STRING");
public static final ParameterType DATE = new ParameterType("DATE");
public static final ParameterType LONG = new ParameterType("LONG");
private static final ParameterType[] VALUES = { STRING, DATE, LONG };
public static ParameterType getType(String typeAsString) {
for (int i = 0; i < VALUES.length; i++) {
if (VALUES[i].toString().equals(typeAsString)) {
return (ParameterType) VALUES[i];
}
}
return null;
}
}
}

View File

@@ -16,7 +16,7 @@ import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.core.domain.StepInstance;
import org.springframework.batch.execution.repository.dao.JdbcJobDao.JobExecutionRowMapper;
import org.springframework.batch.execution.repository.dao.JdbcJobExecutionDao.JobExecutionRowMapper;
import org.springframework.batch.io.exception.BatchCriticalException;
import org.springframework.batch.item.ExecutionAttributes;
import org.springframework.batch.repeat.ExitStatus;
@@ -91,7 +91,7 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao
private DataFieldMaxValueIncrementer stepExecutionIncrementer;
private JobDao jobDao;
private JobExecutionDao jobExecutionDao;
public ExecutionAttributes findExecutionAttributes(final Long executionId) {
@@ -282,7 +282,7 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao
// assume already saved...
return;
}
jobDao.saveJobExecution(jobExecution);
jobExecutionDao.saveJobExecution(jobExecution);
}
/**
@@ -458,13 +458,13 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao
this.stepExecutionIncrementer = stepExecutionIncrementer;
}
public void setJobDao(JobDao jobDao) {
this.jobDao = jobDao;
public void setJobExecutionDao(JobExecutionDao jobExecutionDao) {
this.jobExecutionDao = jobExecutionDao;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(stepExecutionIncrementer, "StepExecutionIncrementer cannot be null.");
Assert.notNull(jobDao, "JobDao cannot be null");
Assert.notNull(jobExecutionDao, "JobDao cannot be null");
}
public static class AttributeType {

View File

@@ -16,12 +16,6 @@
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
/**
* Data Access Object for jobs.
@@ -29,84 +23,6 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
* @author Lucas Ward
*
*/
public interface JobDao {
public interface JobDao extends JobInstanceDao, JobExecutionDao {
/**
* Create a JobInstance with given name and parameters.
*
* PostConditions: A valid job will be returned which has been persisted and
* contains an unique Id.
*
* @param jobName
* @param jobParameters
* @return JobInstance
*/
JobInstance createJobInstance(String jobName, JobParameters jobParameters);
/**
* Find all job instances that match the given name and parameters. If no
* matching job instances are found, then a list of size 0 will be
* returned.
*
* @param jobName
* @param jobParameters
* @return List of {@link JobInstance} objects matching
* {@link JobIdentifier}
*/
List findJobInstances(String jobName, JobParameters jobParameters);
/**
* Update an existing JobInstance.
*
* Preconditions: jobInstance must have an ID.
*
* @param jobInstance
*/
void updateJobInstance(JobInstance jobInstance);
/**
* Save a new JobExecution.
*
* Preconditions: jobExecution must have a jobInstanceId.
*
* @param jobExecution
*/
void saveJobExecution(JobExecution jobExecution);
/**
* Update and existing JobExecution.
*
* Preconditions: jobExecution must have an Id (which can be obtained by the
* save method) and a jobInstanceId.
*
* @param jobExecution
*/
void updateJobExecution(JobExecution jobExecution);
/**
* Return the number of JobExecutions with the given jobInstanceId
*
* Preconditions: jobInstance must have an id.
*
* @param jobInstanceId
*/
int getJobExecutionCount(Long jobInstanceId);
/**
* Return list of JobExecutions for given JobInstance.
*
* @param jobInstance
* @return list of jobExecutions.
*/
List findJobExecutions(JobInstance jobInstance);
/**
* Given an id, return the matching JobExecution.
*
* @param jobExecutionId - id of the execution to be returned.
* @return {@link JobExecution} matching the id.
* @throws {@link IncorrectResultSizeDataAccessException} if more than one
* execution is found for the given id.
*/
JobExecution getJobExecution(Long jobExecutionId);
}

View File

@@ -0,0 +1,61 @@
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
/**
* Data Access Object for job executions.
*
* @author Lucas Ward
* @author Robert Kasanicky
*/
public interface JobExecutionDao {
/**
* Save a new JobExecution.
*
* Preconditions: jobExecution must have a jobInstanceId.
*
* @param jobExecution
*/
void saveJobExecution(JobExecution jobExecution);
/**
* Update and existing JobExecution.
*
* Preconditions: jobExecution must have an Id (which can be obtained by the
* save method) and a jobInstanceId.
*
* @param jobExecution
*/
void updateJobExecution(JobExecution jobExecution);
/**
* Return the number of JobExecutions with the given jobInstanceId
*
* Preconditions: jobInstance must have an id.
*
* @param jobInstanceId
*/
int getJobExecutionCount(Long jobInstanceId);
/**
* Return list of JobExecutions for given JobInstance.
*
* @param jobInstance
* @return list of jobExecutions.
*/
List findJobExecutions(JobInstance jobInstance);
/**
* Given an id, return the matching JobExecution.
*
* @param jobExecutionId - id of the execution to be returned.
* @return {@link JobExecution} matching the id.
* @throws {@link IncorrectResultSizeDataAccessException} if more than one
* execution is found for the given id.
*/
JobExecution getJobExecution(Long jobExecutionId);
}

View File

@@ -0,0 +1,49 @@
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
/**
* Data Access Object for job instances.
*
* @author Lucas Ward
* @author Robert Kasanicky
*
*/
public interface JobInstanceDao {
/**
* Create a JobInstance with given name and parameters.
*
* PostConditions: A valid job will be returned which has been persisted and
* contains an unique Id.
*
* @param jobName
* @param jobParameters
* @return JobInstance
*/
JobInstance createJobInstance(String jobName, JobParameters jobParameters);
/**
* Find all job instances that match the given name and parameters. If no
* matching job instances are found, then a list of size 0 will be
* returned.
*
* @param jobName
* @param jobParameters
* @return List of {@link JobInstance} objects matching
* {@link JobIdentifier}
*/
List findJobInstances(String jobName, JobParameters jobParameters);
/**
* Update an existing JobInstance.
*
* Preconditions: jobInstance must have an ID.
*
* @param jobInstance
*/
void updateJobInstance(JobInstance jobInstance);
}

View File

@@ -31,7 +31,8 @@ import org.springframework.batch.core.domain.StepInstance;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier;
import org.springframework.batch.execution.repository.SimpleJobRepository;
import org.springframework.batch.execution.repository.dao.JobDao;
import org.springframework.batch.execution.repository.dao.JobExecutionDao;
import org.springframework.batch.execution.repository.dao.JobInstanceDao;
import org.springframework.batch.execution.repository.dao.MapJobDao;
import org.springframework.batch.execution.repository.dao.MapStepDao;
import org.springframework.batch.execution.repository.dao.StepExecutionDao;
@@ -51,7 +52,9 @@ public class SimpleJobTests extends TestCase {
private JobRepository jobRepository;
private JobDao jobDao;
private JobInstanceDao jobInstanceDao;
private JobExecutionDao jobExecutionDao;
private StepInstanceDao stepInstanceDao;
@@ -84,10 +87,11 @@ public class SimpleJobTests extends TestCase {
MapJobDao.clear();
MapStepDao.clear();
jobDao = new MapJobDao();
jobInstanceDao = new MapJobDao();
jobExecutionDao = new MapJobDao();
stepInstanceDao = new MapStepDao();
stepExecutionDao = new MapStepDao();
jobRepository = new SimpleJobRepository(jobDao, stepInstanceDao, stepExecutionDao);
jobRepository = new SimpleJobRepository(jobInstanceDao, jobExecutionDao, stepInstanceDao, stepExecutionDao);
job = new SimpleJob();
job.setJobRepository(jobRepository);
@@ -254,9 +258,9 @@ public class SimpleJobTests extends TestCase {
* Check JobRepository to ensure status is being saved.
*/
private void checkRepository(BatchStatus status, ExitStatus exitStatus) {
assertEquals(jobInstance, jobDao.findJobInstances(jobInstance.getJobName(), jobParameters).get(0));
assertEquals(jobInstance, jobInstanceDao.findJobInstances(jobInstance.getJobName(), jobParameters).get(0));
// because map dao stores in memory, it can be checked directly
JobExecution jobExecution = (JobExecution) jobDao.findJobExecutions(jobInstance).get(0);
JobExecution jobExecution = (JobExecution) jobExecutionDao.findJobExecutions(jobInstance).get(0);
assertEquals(jobInstance.getId(), jobExecution.getJobId());
assertEquals(status, jobExecution.getStatus());
if (exitStatus != null) {

View File

@@ -49,7 +49,7 @@ public class SimpleJobTests extends TestCase {
private List recovered = new ArrayList();
private SimpleJobRepository repository = new SimpleJobRepository(new MapJobDao(), new MapStepDao(), new MapStepDao());
private SimpleJobRepository repository = new SimpleJobRepository(new MapJobDao(), new MapJobDao(), new MapStepDao(), new MapStepDao());
private List processed = new ArrayList();

View File

@@ -86,7 +86,7 @@ public class SimpleJobRepositoryTests extends TestCase {
jobDao = (JobDao) jobDaoControl.getMock();
stepDao = (StepDao) stepDaoControl.getMock();
jobRepository = new SimpleJobRepository(jobDao, stepDao, stepDao);
jobRepository = new SimpleJobRepository(jobDao, jobDao, stepDao, stepDao);
jobParameters = new JobParametersBuilder().toJobParameters();

View File

@@ -39,7 +39,9 @@ import org.springframework.util.ClassUtils;
public abstract class AbstractJobDaoTests extends
AbstractTransactionalDataSourceSpringContextTests {
protected JobDao jobDao;
protected JobInstanceDao jobInstanceDao;
protected JobExecutionDao jobExecutionDao;
protected JobParameters jobParameters = new JobParametersBuilder().addString("job.key", "jobKey").toJobParameters();
@@ -61,8 +63,12 @@ public abstract class AbstractJobDaoTests extends
* Because AbstractTransactionalSpringContextTests is used, this method will
* be called by Spring to set the JobRepository.
*/
public void setJobRepositoryDao(JobDao jobRepositoryDao) {
this.jobDao = jobRepositoryDao;
public void setJobInstanceDao(JobInstanceDao jobInstanceDao) {
this.jobInstanceDao = jobInstanceDao;
}
public void setJobExecutionDao(JobExecutionDao jobExecutionDao) {
this.jobExecutionDao = jobExecutionDao;
}
protected void onSetUpInTransaction() throws Exception {
@@ -72,16 +78,16 @@ public abstract class AbstractJobDaoTests extends
job = new JobSupport("Job1");
// Create job.
jobInstance = jobDao.createJobInstance(job.getName(), jobParameters);
jobInstance = jobInstanceDao.createJobInstance(job.getName(), jobParameters);
// Create an execution
jobExecutionStartTime = new Date(System.currentTimeMillis());
jobExecution = new JobExecution(jobInstance);
jobExecution.setStartTime(jobExecutionStartTime);
jobExecution.setStatus(BatchStatus.STARTED);
jobDao.saveJobExecution(jobExecution);
jobExecutionDao.saveJobExecution(jobExecution);
jobInstance.setLastExecution(jobExecution);
jobDao.updateJobInstance(jobInstance);
jobInstanceDao.updateJobInstance(jobInstance);
}
public void testVersionIsNotNullForJob() throws Exception {
@@ -100,13 +106,13 @@ public abstract class AbstractJobDaoTests extends
public void testFindNonExistentJob() {
// No job should be found since it hasn't been created.
List jobs = jobDao.findJobInstances("nonexistentJob", jobParameters);
List jobs = jobInstanceDao.findJobInstances("nonexistentJob", jobParameters);
assertTrue(jobs.size() == 0);
}
public void testFindJob() {
List jobs = jobDao.findJobInstances(job.getName(), jobParameters);
List jobs = jobInstanceDao.findJobInstances(job.getName(), jobParameters);
assertTrue(jobs.size() == 1);
JobInstance tempJob = (JobInstance) jobs.get(0);
assertTrue(jobInstance.equals(tempJob));
@@ -116,7 +122,7 @@ public abstract class AbstractJobDaoTests extends
public void testFindJobWithNullRuntime() {
try {
jobDao.findJobInstances(null, null);
jobInstanceDao.findJobInstances(null, null);
fail();
} catch (IllegalArgumentException ex) {
// expected
@@ -130,7 +136,7 @@ public abstract class AbstractJobDaoTests extends
*/
public void testCreateJobWithExistingName() {
jobDao.createJobInstance("ScheduledJob", jobParameters);
jobInstanceDao.createJobInstance("ScheduledJob", jobParameters);
// Modifying the key should bring back a completely different
// JobInstance
@@ -138,12 +144,12 @@ public abstract class AbstractJobDaoTests extends
.toJobParameters();
List jobs;
jobs = jobDao.findJobInstances("ScheduledJob", jobParameters);
jobs = jobInstanceDao.findJobInstances("ScheduledJob", jobParameters);
assertEquals(1, jobs.size());
JobInstance jobInstance = (JobInstance) jobs.get(0);
assertEquals(jobParameters, jobInstance.getJobParameters());
jobs = jobDao.findJobInstances("ScheduledJob", tempProps);
jobs = jobInstanceDao.findJobInstances("ScheduledJob", tempProps);
assertEquals(0, jobs.size());
}
@@ -151,12 +157,12 @@ public abstract class AbstractJobDaoTests extends
public void testUpdateJob() {
// Update the returned job with a new status
JobExecution newExecution = new JobExecution(jobInstance);
jobDao.saveJobExecution(newExecution);
jobExecutionDao.saveJobExecution(newExecution);
jobInstance.setLastExecution(newExecution);
jobDao.updateJobInstance(jobInstance);
jobInstanceDao.updateJobInstance(jobInstance);
// The job just updated should be found, with the saved status.
List jobs = jobDao.findJobInstances(job.getName(), jobParameters);
List jobs = jobInstanceDao.findJobInstances(job.getName(), jobParameters);
assertTrue(jobs.size() == 1);
JobInstance tempJob = (JobInstance) jobs.get(0);
assertTrue(jobInstance.equals(tempJob));
@@ -165,13 +171,13 @@ public abstract class AbstractJobDaoTests extends
public void testGetJobExecution(){
JobExecution tempExecution = jobDao.getJobExecution(jobExecution.getId());
JobExecution tempExecution = jobExecutionDao.getJobExecution(jobExecution.getId());
assertEquals(jobExecution, tempExecution);
}
public void testJobInstanceLastExecution(){
//ensure the last execution id is being stored
JobExecution lastJobExecution = jobDao.getJobExecution(jobInstance.getLastExecution().getId());
JobExecution lastJobExecution = jobExecutionDao.getJobExecution(jobInstance.getLastExecution().getId());
assertEquals(lastJobExecution, jobExecution);
}
@@ -180,7 +186,7 @@ public abstract class AbstractJobDaoTests extends
try {
JobInstance testJob = new JobInstance(null, null);
jobDao.updateJobInstance(testJob);
jobInstanceDao.updateJobInstance(testJob);
fail();
} catch (IllegalArgumentException ex) {
// expected
@@ -191,7 +197,7 @@ public abstract class AbstractJobDaoTests extends
JobInstance testJob = null;
try {
jobDao.updateJobInstance(testJob);
jobInstanceDao.updateJobInstance(testJob);
} catch (IllegalArgumentException ex) {
// expected
}
@@ -202,9 +208,9 @@ public abstract class AbstractJobDaoTests extends
jobExecution.setStatus(BatchStatus.COMPLETED);
jobExecution.setExitStatus(ExitStatus.FINISHED);
jobExecution.setEndTime(new Date(System.currentTimeMillis()));
jobDao.updateJobExecution(jobExecution);
jobExecutionDao.updateJobExecution(jobExecution);
List executions = jobDao.findJobExecutions(jobInstance);
List executions = jobExecutionDao.findJobExecutions(jobInstance);
assertEquals(executions.size(), 1);
validateJobExecution(jobExecution, (JobExecution) executions.get(0));
@@ -212,7 +218,7 @@ public abstract class AbstractJobDaoTests extends
public void testSaveJobExecution(){
List executions = jobDao.findJobExecutions(jobInstance);
List executions = jobExecutionDao.findJobExecutions(jobInstance);
assertEquals(executions.size(), 1);
validateJobExecution(jobExecution, (JobExecution) executions.get(0));
}
@@ -222,7 +228,7 @@ public abstract class AbstractJobDaoTests extends
// id is invalid
JobExecution execution = new JobExecution(jobInstance, new Long(29432));
try {
jobDao.updateJobExecution(execution);
jobExecutionDao.updateJobExecution(execution);
fail("Expected NoSuchBatchDomainObjectException");
} catch (NoSuchBatchDomainObjectException ex) {
// expected
@@ -233,7 +239,7 @@ public abstract class AbstractJobDaoTests extends
JobExecution execution = new JobExecution(jobInstance);
try {
jobDao.updateJobExecution(execution);
jobExecutionDao.updateJobExecution(execution);
fail();
} catch (IllegalArgumentException ex) {
// expected
@@ -243,26 +249,26 @@ public abstract class AbstractJobDaoTests extends
public void testIncrementExecutionCount() {
// 1 JobExection already added in setup
assertEquals(jobDao.getJobExecutionCount(jobInstance.getId()), 1);
assertEquals(jobExecutionDao.getJobExecutionCount(jobInstance.getId()), 1);
// Save new JobExecution for same job
JobExecution testJobExecution = new JobExecution(jobInstance);
jobDao.saveJobExecution(testJobExecution);
jobExecutionDao.saveJobExecution(testJobExecution);
// JobExecutionCount should be incremented by 1
assertEquals(jobDao.getJobExecutionCount(jobInstance.getId()), 2);
assertEquals(jobExecutionDao.getJobExecutionCount(jobInstance.getId()), 2);
}
public void testZeroExecutionCount() {
JobInstance testJob = jobDao.createJobInstance("test", new JobParameters());
JobInstance testJob = jobInstanceDao.createJobInstance("test", new JobParameters());
// no jobExecutions saved for new job, count should be 0
assertEquals(jobDao.getJobExecutionCount(testJob.getId()), 0);
assertEquals(jobExecutionDao.getJobExecutionCount(testJob.getId()), 0);
}
public void testJobWithSimpleJobIdentifier() throws Exception {
// Create job.
jobInstance = jobDao.createJobInstance("test", jobParameters);
jobInstance = jobInstanceDao.createJobInstance("test", jobParameters);
List jobs = jdbcTemplate.queryForList(
"SELECT * FROM BATCH_JOB_INSTANCE where JOB_INSTANCE_ID=?", new Object[] { jobInstance
@@ -274,9 +280,9 @@ public abstract class AbstractJobDaoTests extends
public void testJobWithDefaultJobIdentifier() throws Exception {
// Create job.
jobInstance = jobDao.createJobInstance("testDefault", jobParameters);
jobInstance = jobInstanceDao.createJobInstance("testDefault", jobParameters);
List jobs = jobDao.findJobInstances("testDefault", jobParameters);
List jobs = jobInstanceDao.findJobInstances("testDefault", jobParameters);
assertEquals(1, jobs.size());
assertEquals(jobParameters.getString("job.key"), ((JobInstance) jobs.get(0))
@@ -286,7 +292,7 @@ public abstract class AbstractJobDaoTests extends
public void testFindJobExecutions(){
List results = jobDao.findJobExecutions(jobInstance);
List results = jobExecutionDao.findJobExecutions(jobInstance);
assertEquals(results.size(), 1);
validateJobExecution(jobExecution, (JobExecution)results.get(0));
}

View File

@@ -44,7 +44,7 @@ import org.springframework.util.ClassUtils;
*/
public abstract class AbstractStepDaoTests extends AbstractTransactionalDataSourceSpringContextTests {
protected JobDao jobDao;
protected JobInstanceDao jobInstanceDao;
protected StepInstanceDao stepInstanceDao;
@@ -64,8 +64,8 @@ public abstract class AbstractStepDaoTests extends AbstractTransactionalDataSour
protected ExecutionAttributes executionAttributes;
public void setJobDao(JobDao jobDao) {
this.jobDao = jobDao;
public void setJobInstanceDao(JobInstanceDao jobInstanceDao) {
this.jobInstanceDao = jobInstanceDao;
}
public void setStepInstanceDao(StepInstanceDao stepInstanceDao) {
@@ -90,7 +90,7 @@ public abstract class AbstractStepDaoTests extends AbstractTransactionalDataSour
*/
protected void onSetUpInTransaction() throws Exception {
Job job = new JobSupport("TestJob");
jobInstance = jobDao.createJobInstance(job.getName(), jobParameters);
jobInstance = jobInstanceDao.createJobInstance(job.getName(), jobParameters);
step1 = stepInstanceDao.createStepInstance(jobInstance, "TestStep1");
step2 = stepInstanceDao.createStepInstance(jobInstance, "TestStep2");
jobExecution = new JobExecution(step2.getJobInstance());

View File

@@ -31,8 +31,8 @@ import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer
*
*/
public class JdbcJobDaoQueryTests extends TestCase {
JdbcJobDao sqlDao;
JdbcJobExecutionDao jobExecutionDao;
List list = new ArrayList();
@@ -41,8 +41,9 @@ public class JdbcJobDaoQueryTests extends TestCase {
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
sqlDao = new JdbcJobDao();
sqlDao.setJobExecutionIncrementer(new DataFieldMaxValueIncrementer() {
jobExecutionDao = new JdbcJobExecutionDao();
jobExecutionDao.setJobExecutionIncrementer(new DataFieldMaxValueIncrementer() {
public int nextIntValue() throws DataAccessException {
return 0;
@@ -60,14 +61,14 @@ public class JdbcJobDaoQueryTests extends TestCase {
}
public void testTablePrefix() throws Exception {
sqlDao.setTablePrefix("FOO_");
sqlDao.setJdbcTemplate(new JdbcTemplate() {
jobExecutionDao.setTablePrefix("FOO_");
jobExecutionDao.setJdbcTemplate(new JdbcTemplate() {
public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
list.add(sql);
return 1;
}
});
sqlDao.saveJobExecution(new JobInstance(new Long(11), new JobParameters()).createJobExecution());
jobExecutionDao.saveJobExecution(new JobInstance(new Long(11), new JobParameters()).createJobExecution());
assertEquals(1, list.size());
String query = (String) list.get(0);
assertTrue("Query did not contain FOO_:" + query, query.indexOf("FOO_") >= 0);

View File

@@ -10,7 +10,8 @@ public class JdbcJobDaoTests extends AbstractJobDaoTests {
public static final String LONG_STRING = "A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String ";
protected void onSetUpBeforeTransaction() throws Exception {
((JdbcJobDao) jobDao).setTablePrefix(JdbcJobDao.DEFAULT_TABLE_PREFIX);
((JdbcJobInstanceDao) jobInstanceDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
((JdbcJobExecutionDao) jobExecutionDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
}
public void testUpdateJobExecutionWithLongExitCode() {
@@ -18,7 +19,7 @@ public class JdbcJobDaoTests extends AbstractJobDaoTests {
assertTrue(LONG_STRING.length() > 250);
jobExecution.setExitStatus(ExitStatus.FINISHED
.addExitDescription(LONG_STRING));
jobDao.updateJobExecution(jobExecution);
jobExecutionDao.updateJobExecution(jobExecution);
List executions = jdbcTemplate.queryForList(
"SELECT * FROM BATCH_JOB_EXECUTION where JOB_INSTANCE_ID=?",

View File

@@ -46,7 +46,7 @@ public class JdbcStepDaoPrefixTests extends TestCase {
stepInstanceDao = new JdbcStepInstanceDao();
stepExecutionDao = new JdbcStepExecutionDao();
stepExecutionDao.setJobDao(new MapJobDao());
stepExecutionDao.setJobExecutionDao(new MapJobDao());
stepExecutionIncrementer = (DataFieldMaxValueIncrementer)stepExecutionIncrementerControl.getMock();
stepIncrementer = (DataFieldMaxValueIncrementer)stepIncrementerControl.getMock();

View File

@@ -11,8 +11,8 @@ public class JdbcStepDaoTests extends AbstractStepDaoTests {
private static final String LONG_STRING = JdbcJobDaoTests.LONG_STRING;
protected void onSetUpBeforeTransaction() throws Exception {
((JdbcStepInstanceDao) stepInstanceDao).setTablePrefix(JdbcJobDao.DEFAULT_TABLE_PREFIX);
((JdbcStepExecutionDao) stepExecutionDao).setTablePrefix(JdbcJobDao.DEFAULT_TABLE_PREFIX);
((JdbcStepInstanceDao) stepInstanceDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
((JdbcStepExecutionDao) stepExecutionDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
}
public void testTablePrefix() throws Exception {

View File

@@ -202,7 +202,7 @@ public class SimpleStepExecutorTests extends TestCase {
public void testRepository() throws Exception {
SimpleJobRepository repository = new SimpleJobRepository(new MapJobDao(), new MapStepDao(), new MapStepDao());
SimpleJobRepository repository = new SimpleJobRepository(new MapJobDao(), new MapJobDao(), new MapStepDao(), new MapStepDao());
stepExecutor.setRepository(repository);
StepInstance step = new StepInstance(new Long(1));

View File

@@ -55,7 +55,7 @@ public class StepExecutorInterruptionTests extends TestCase {
public void setUp() throws Exception {
jobRepository = new SimpleJobRepository(jobDao, stepDao, stepDao);
jobRepository = new SimpleJobRepository(jobDao, jobDao, stepDao, stepDao);
JobSupport jobConfiguration = new JobSupport();
step = new RepeatOperationsStep();

View File

@@ -5,10 +5,15 @@
<import resource="data-source-context.xml" />
<bean id="jobDao" class="org.springframework.batch.execution.repository.dao.JdbcJobDao" >
<bean id="jobInstanceDao" class="org.springframework.batch.execution.repository.dao.JdbcJobInstanceDao" >
<property name="jdbcTemplate" ref="jdbcTemplate" />
<property name="jobIncrementer" ref="jobIncrementer" />
<property name="jobExecutionIncrementer" ref="jobExecutionIncrementer" />
<property name="jobIncrementer" ref="jobIncrementer" />
<property name="jobExecutionDao" ref="jobExecutionDao" />
</bean>
<bean id="jobExecutionDao" class="org.springframework.batch.execution.repository.dao.JdbcJobExecutionDao" >
<property name="jdbcTemplate" ref="jdbcTemplate" />
<property name="jobExecutionIncrementer" ref="jobExecutionIncrementer" />
</bean>
<bean id="stepInstanceDao" class="org.springframework.batch.execution.repository.dao.JdbcStepInstanceDao" >
@@ -20,7 +25,7 @@
<bean id="stepExecutionDao" class="org.springframework.batch.execution.repository.dao.JdbcStepExecutionDao" >
<property name="jdbcTemplate" ref="jdbcTemplate" />
<property name="stepExecutionIncrementer" ref="stepExecutionIncrementer" />
<property name="jobDao" ref="jobDao"/>
<property name="jobExecutionDao" ref="jobExecutionDao"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >

View File

@@ -45,6 +45,7 @@
<bean id="simpleJobRepository"
class="org.springframework.batch.execution.repository.SimpleJobRepository">
<constructor-arg ref="jobDao" />
<constructor-arg ref="jobDao" />
<constructor-arg ref="stepDao" />
<constructor-arg ref="stepDao" />