IN PROGRESS - issue BATCH-127: Allow job configuration to control re-entrant behavior

http://opensource.atlassian.com/projects/spring/browse/BATCH-127

Refactor JobRepository.findOrCreateJob() to return JobExecution.
This commit is contained in:
dsyer
2007-12-11 18:10:20 +00:00
parent cb1a02bb9f
commit 5fa4ffc3b2
31 changed files with 296 additions and 157 deletions

View File

@@ -234,4 +234,12 @@ public class JobExecution extends Entity {
public String toString() {
return super.toString()+", job=["+job+"]";
}
/**
* Test if this {@link JobExecution} indicates that it is running.
* @return true if the end time is null
*/
public boolean isRunning() {
return endTime==null;
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.execution.launch;
package org.springframework.batch.core.repository;
import org.springframework.batch.core.executor.JobExecutionException;

View File

@@ -47,7 +47,7 @@ import org.springframework.batch.core.domain.StepInstance;
public interface JobRepository {
/**
* Find or create a job for a given {@link JobIdentifier} and configuration.
* Find or create a job execution for a given {@link JobIdentifier} and configuration.
* If the job that is uniquely identified by {@link JobIdentifier} already
* exists, its persisted values (including ID) will be returned in a new
* {@link JobInstance} object. If no previous run is found, a new job will
@@ -59,11 +59,15 @@ public interface JobRepository {
* identifies this particular run of the configuration across
* possible restarts
*
* @return a valid job
* @return a valid job execution for the identifier provided
* @throws JobExecutionAlreadyRunningException
* if there is a {@link JobExecution} alrady running for the
* job instance that would otherwise be returned
*
*/
public JobInstance findOrCreateJob(JobConfiguration jobConfiguration,
JobIdentifier jobIdentifier);
public JobExecution findOrCreateJob(JobConfiguration jobConfiguration,
JobIdentifier jobIdentifier)
throws JobExecutionAlreadyRunningException;
/**
* Update a Job.

View File

@@ -48,6 +48,15 @@ public class JobExecutionTests extends TestCase {
assertEquals(100L, execution.getEndTime().getTime());
}
/**
* Test method for {@link org.springframework.batch.core.domain.JobExecution#getEndTime()}.
*/
public void testIsRunning() {
assertTrue(execution.isRunning());
execution.setEndTime(new Timestamp(100L));
assertFalse(execution.isRunning());
}
/**
* Test method for {@link org.springframework.batch.core.domain.JobExecution#getStartTime()}.
*/

View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.execution.launch;
package org.springframework.batch.core.repository;
import org.springframework.batch.execution.AbstractExceptionTests;
import org.springframework.batch.core.AbstractExceptionTests;
/**
* @author Dave Syer

View File

@@ -19,6 +19,7 @@ package org.springframework.batch.execution.launch;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
/**
* Interface which defines a facade for running jobs. The interface is

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.execution.launch;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
/**
* Simple interface for controlling jobs, including possible ad-hoc executions,

View File

@@ -29,8 +29,8 @@ import org.springframework.batch.core.configuration.JobConfigurationLocator;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.executor.JobExecutor;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.execution.job.DefaultJobExecutor;
import org.springframework.batch.repeat.RepeatContext;
@@ -164,15 +164,9 @@ class SimpleJobExecutorFacade implements JobExecutorFacade,
JobConfiguration jobConfiguration = jobConfigurationLocator
.getJobConfiguration(jobIdentifier.getName());
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration,
return jobRepository.findOrCreateJob(jobConfiguration,
jobIdentifier);
JobExecution execution = job.createNewJobExecution();
// Save the JobExecution so that it picks up an ID (useful for clients
// monitoring asynchronous executions):
jobRepository.saveOrUpdate(execution);
return execution;
}
/**

View File

@@ -33,6 +33,7 @@ import org.springframework.batch.core.configuration.NoSuchJobConfigurationExcept
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.executor.JobExecutor;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.execution.job.DefaultJobExecutor;

View File

@@ -29,6 +29,7 @@ import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.core.domain.StepInstance;
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.StepDao;
@@ -36,18 +37,18 @@ import org.springframework.batch.restart.GenericRestartData;
import org.springframework.util.Assert;
/**
*
*
* <p>
* Simple Job Repository that stores Jobs, JobExecutions, Steps, and
* StepExecutions using the provided JobDao and StepDao.
* <p>
*
*
* @author Lucas Ward
* @author Dave Syer
* @see JobRepository
* @see StepDao
* @see JobDao
*
*
*/
public class SimpleJobRepository implements JobRepository {
@@ -71,7 +72,7 @@ public class SimpleJobRepository implements JobRepository {
* either creating a new job or finding an existing one, which will ensure
* that the id field of the job is populated with the correct value.
* </p>
*
*
* <p>
* There are two ways in which the method determines if a job should be
* created or an existing one should be returned. The first is
@@ -82,20 +83,23 @@ public class SimpleJobRepository implements JobRepository {
* (there must be at least 1) and it will be returned. If no job is found, a
* new one will be created based on the configuration.
* </p>
*
* @throws JobExecutionAlreadyRunningException
*
* @see JobRepository#findOrCreateJob(JobConfiguration, JobIdentifier)
* @throws BatchRestartException if more than one JobInstance if found
* or if JobInstance.getJobExecutionCount() is greater than JobConfiguration.getStartLimit()
* @throws BatchRestartException
* if more than one JobInstance if found or if
* JobInstance.getJobExecutionCount() is greater than
* JobConfiguration.getStartLimit()
*/
public JobInstance findOrCreateJob(JobConfiguration jobConfiguration, JobIdentifier runtimeInformation) {
public JobExecution findOrCreateJob(JobConfiguration jobConfiguration,
JobIdentifier runtimeInformation) throws JobExecutionAlreadyRunningException {
List jobs;
// Check if a job is restartable, if not, create and return a new job
if (jobConfiguration.isRestartable() == false) {
return createJob(jobConfiguration, runtimeInformation);
}
else {
} else {
// find all jobs matching the runtime information.
jobs = jobDao.findJobs(runtimeInformation);
}
@@ -103,24 +107,48 @@ public class SimpleJobRepository implements JobRepository {
if (jobs.size() == 1) {
// One job was found
JobInstance job = (JobInstance) jobs.get(0);
job.setSteps(findSteps(jobConfiguration.getStepConfigurations(), job));
job.setSteps(findSteps(jobConfiguration.getStepConfigurations(),
job));
job.setJobExecutionCount(jobDao.getJobExecutionCount(job.getId()));
if (job.getJobExecutionCount() > jobConfiguration.getStartLimit()) {
throw new BatchRestartException("Restart Max exceeded for Job: " + job.toString());
throw new BatchRestartException(
"Restart Max exceeded for Job: " + job.toString());
}
return job;
}
else if (jobs.size() == 0) {
List executions = jobDao.findJobExecutions(job);
for (Iterator iterator = executions.iterator(); iterator.hasNext();) {
JobExecution execution = (JobExecution) iterator.next();
if (execution.isRunning()) {
throw new JobExecutionAlreadyRunningException("A job execution for this job is already running: "+job);
}
}
/*
* Update the job, then if this method is transactional, and the
* isolation level is SERIALIZABLE, another launcher trying to start
* the same job in another thread or process will lose.
*/
jobDao.update(job);
return generateJobExecution(job);
} else if (jobs.size() == 0) {
// no job found, create one
return createJob(jobConfiguration, runtimeInformation);
}
else {
} else {
// More than one job found, throw exception
throw new BatchRestartException("Error restarting job, more than one JobInstance found for: "
+ jobConfiguration.toString());
throw new BatchRestartException(
"Error restarting job, more than one JobInstance found for: "
+ jobConfiguration.toString());
}
}
private JobExecution generateJobExecution(JobInstance job) {
JobExecution execution = job.createNewJobExecution();
// Save the JobExecution so that it picks up an ID (useful for clients
// monitoring asynchronous executions):
saveOrUpdate(execution);
return execution;
}
/**
* Save or Update a JobExecution. A JobExecution is considered one
* 'execution' of a particular job. Therefore, it must have it's jobId field
@@ -128,20 +156,22 @@ public class SimpleJobRepository implements JobRepository {
* identifer, because it must be updatable separately. If an id isn't found,
* a new JobExecution is created, if one is found, the current row is
* updated.
*
* @param JobExecution to be stored.
* @throws IllegalArgumentException if jobExecution is null.
*
* @param JobExecution
* to be stored.
* @throws IllegalArgumentException
* if jobExecution is null.
*/
public void saveOrUpdate(JobExecution jobExecution) {
Assert.notNull(jobExecution, "JobExecution cannot be null.");
Assert.notNull(jobExecution.getJobId(), "JobExecution must have a Job ID set.");
Assert.notNull(jobExecution.getJobId(),
"JobExecution must have a Job ID set.");
if (jobExecution.getId() == null) {
// existing instance
jobDao.save(jobExecution);
}
else {
} else {
// new execution
jobDao.update(jobExecution);
}
@@ -151,15 +181,20 @@ public class SimpleJobRepository implements JobRepository {
* Update an existing job. A job must have been obtained from the
* findOrCreateJob method, otherwise it is likely that the id is incorrect
* or non-existant.
*
* @param job to be updated.
* @throws IllegalArgumentException if Job or it's Id is null.
*
* @param job
* to be updated.
* @throws IllegalArgumentException
* if Job or it's Id is null.
*/
public void update(JobInstance job) {
Assert.notNull(job, "Job cannot be null.");
Assert.notNull(job.getId(), "Job cannot be updated if it's ID is null. It must be obtained"
+ "from SimpleJobRepository.findOrCreateJob to be considered valid.");
Assert
.notNull(
job.getId(),
"Job cannot be updated if it's ID is null. It must be obtained"
+ "from SimpleJobRepository.findOrCreateJob to be considered valid.");
jobDao.update(job);
}
@@ -169,20 +204,22 @@ public class SimpleJobRepository implements JobRepository {
* saved and an id will be set, otherwise it will be updated. It should be
* noted that assigning an ID randomly will likely cause an exception
* depending on the StepDao implementation.
*
* @param StepExecution to be saved.
* @throws IllegalArgumentException if stepExecution is null.
*
* @param StepExecution
* to be saved.
* @throws IllegalArgumentException
* if stepExecution is null.
*/
public void saveOrUpdate(StepExecution stepExecution) {
Assert.notNull(stepExecution, "StepExecution cannot be null.");
Assert.notNull(stepExecution.getStepId(), "StepExecution's Step Id cannot be null.");
Assert.notNull(stepExecution.getStepId(),
"StepExecution's Step Id cannot be null.");
if (stepExecution.getId() == null) {
// new execution, obtain id and insert
stepDao.save(stepExecution);
}
else {
} else {
// existing execution, update
stepDao.update(stepExecution);
}
@@ -190,15 +227,20 @@ public class SimpleJobRepository implements JobRepository {
/**
* Update the given step.
*
* @param StepInstance to be updated.
* @throws IllegalArgumentException if step or it's id is null.
*
* @param StepInstance
* to be updated.
* @throws IllegalArgumentException
* if step or it's id is null.
*/
public void update(StepInstance step) {
Assert.notNull(step, "Step cannot be null.");
Assert.notNull(step.getId(), "Step cannot be updated if it's ID is null. It must be obtained"
+ "from SimpleJobRepository.findOrCreateJob to be considered valid.");
Assert
.notNull(
step.getId(),
"Step cannot be updated if it's ID is null. It must be obtained"
+ "from SimpleJobRepository.findOrCreateJob to be considered valid.");
stepDao.update(step);
@@ -209,11 +251,15 @@ public class SimpleJobRepository implements JobRepository {
* calling {@link JobDao#createJob(JobRuntimeInformation)} and then it's
* list of StepConfigurations is passed to the createSteps method.
*/
private JobInstance createJob(JobConfiguration jobConfiguration, JobIdentifier runtimeInformation) {
private JobExecution createJob(JobConfiguration jobConfiguration,
JobIdentifier runtimeInformation) {
JobInstance job = jobDao.createJob(runtimeInformation);
job.setSteps(createSteps(job, jobConfiguration.getStepConfigurations()));
return job;
job
.setSteps(createSteps(job, jobConfiguration
.getStepConfigurations()));
JobExecution execution = generateJobExecution(job);
return execution;
}
/*
@@ -225,9 +271,11 @@ public class SimpleJobRepository implements JobRepository {
Iterator i = stepConfigurations.iterator();
while (i.hasNext()) {
StepConfiguration stepConfiguration = (StepConfiguration) i.next();
StepInstance step = stepDao.createStep(job, stepConfiguration.getName());
//Ensure valid restart data is being returned.
if(step.getRestartData() == null || step.getRestartData().getProperties() == null){
StepInstance step = stepDao.createStep(job, stepConfiguration
.getName());
// Ensure valid restart data is being returned.
if (step.getRestartData() == null
|| step.getRestartData().getProperties() == null) {
step.setRestartData(new GenericRestartData(new Properties()));
}
steps.add(step);
@@ -245,13 +293,18 @@ public class SimpleJobRepository implements JobRepository {
while (i.hasNext()) {
StepConfiguration stepConfiguration = (StepConfiguration) i.next();
StepInstance step = stepDao.findStep(job, stepConfiguration.getName());
StepInstance step = stepDao.findStep(job, stepConfiguration
.getName());
if (step != null) {
step.setStepExecutionCount(stepDao.getStepExecutionCount(step.getId()));
//Ensure valid restart data is being returned.
if(step.getRestartData() == null || step.getRestartData().getProperties() == null){
step.setRestartData(new GenericRestartData(new Properties()));
step.setStepExecutionCount(stepDao.getStepExecutionCount(step
.getId()));
// Ensure valid restart data is being returned.
if (step.getRestartData() == null
|| step.getRestartData().getProperties() == null) {
step
.setRestartData(new GenericRestartData(
new Properties()));
}
steps.add(step);
}

View File

@@ -26,14 +26,15 @@ import org.springframework.batch.core.domain.JobInstance;
* Data Access Object for jobs.
*
* @author Lucas Ward
*
*
*/
public interface JobDao {
/**
* Create a job using the provided JobIdentifier as the natural key.
*
* PostConditions: A valid job will be returned which contains an unique Id.
* PostConditions: A valid job will be returned which has been persisted and
* contains an unique Id.
*
* @param jobIdentifier
* @return Job
@@ -45,7 +46,8 @@ public interface JobDao {
* Identifier are found, then a list of size 0 will be returned.
*
* @param jobIdentifier
* @return List of {@link JobInstance} objects matching {@link JobIdentifier}
* @return List of {@link JobInstance} objects matching
* {@link JobIdentifier}
*/
public List findJobs(JobIdentifier jobIdentifier);
@@ -85,7 +87,7 @@ public interface JobDao {
* @param job
*/
public int getJobExecutionCount(Long jobId);
/**
* Return list of JobExecutions for given job.
*

View File

@@ -128,9 +128,8 @@ public class DefaultJobExecutorTests extends TestCase {
jobIdentifer = new SimpleJobIdentifier("TestJob");
job = jobRepository.findOrCreateJob(jobConfiguration, jobIdentifer);
jobExecution = new JobExecution(job);
jobExecution = jobRepository.findOrCreateJob(jobConfiguration, jobIdentifer);
job = jobExecution.getJob();
List steps = job.getSteps();
step1 = (StepInstance) steps.get(0);

View File

@@ -24,7 +24,6 @@ import java.util.Properties;
import junit.framework.TestCase;
import org.easymock.AbstractMatcher;
import org.easymock.MockControl;
import org.springframework.batch.core.configuration.JobConfiguration;
import org.springframework.batch.core.configuration.JobConfigurationLocator;
@@ -32,6 +31,7 @@ import org.springframework.batch.core.configuration.NoSuchJobConfigurationExcept
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.executor.JobExecutor;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.runtime.SimpleJobIdentifier;
import org.springframework.batch.io.exception.BatchCriticalException;
@@ -97,7 +97,7 @@ public class SimpleJobExecutorFacadeTests extends TestCase {
}
private JobInstance setUpFacadeForNormalStart()
throws NoSuchJobConfigurationException {
throws Exception {
jobIdentifier = new SimpleJobIdentifier("bar");
jobExecutor = new JobExecutor() {
public ExitStatus run(JobConfiguration configuration,
@@ -110,18 +110,7 @@ public class SimpleJobExecutorFacadeTests extends TestCase {
JobInstance job = new JobInstance(jobIdentifier);
jobExecution = new JobExecution(job);
jobRepository.findOrCreateJob(jobConfiguration, jobIdentifier);
jobRepositoryControl.setReturnValue(job);
jobRepository.saveOrUpdate(jobExecution);
jobRepositoryControl.setMatcher(new AbstractMatcher() {
protected boolean argumentMatches(Object expected, Object actual) {
if (actual instanceof JobExecution) {
jobExecution = (JobExecution) actual;
return true;
} else {
return super.argumentMatches(expected, actual);
}
}
});
jobRepositoryControl.setReturnValue(jobExecution);
jobRepositoryControl.replay();
jobExecutorFacade
.setJobConfigurationLocator(new JobConfigurationLocator() {

View File

@@ -118,7 +118,7 @@ public class SimpleJobTests extends TestCase {
jobConfiguration.addStep(new SimpleStepConfiguration(getTasklet("foo", "bar")));
jobConfiguration.addStep(new SimpleStepConfiguration(getTasklet("spam")));
JobInstance job = repository.findOrCreateJob(jobConfiguration, runtimeInformation);
JobInstance job = repository.findOrCreateJob(jobConfiguration, runtimeInformation).getJob();
assertEquals(job.getName(), "real.job");
@@ -169,8 +169,8 @@ public class SimpleJobTests extends TestCase {
});
jobConfiguration.addStep(step);
JobInstance job = repository.findOrCreateJob(jobConfiguration, runtimeInformation);
JobExecution jobExecution = new JobExecution(job);
JobExecution jobExecution = repository.findOrCreateJob(jobConfiguration, runtimeInformation);
JobInstance job = jobExecution.getJob();
jobExecutor.run(jobConfiguration, jobExecution);
assertEquals(BatchStatus.COMPLETED, job.getStatus());
@@ -193,10 +193,10 @@ public class SimpleJobTests extends TestCase {
});
jobConfiguration.addStep(step);
JobInstance job = repository.findOrCreateJob(jobConfiguration, runtimeInformation);
JobExecution jobExecutionContext = new JobExecution(job);
JobExecution jobExecution = repository.findOrCreateJob(jobConfiguration, runtimeInformation);
JobInstance job = jobExecution.getJob();
try {
jobExecutor.run(jobConfiguration, jobExecutionContext);
jobExecutor.run(jobConfiguration, jobExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {

View File

@@ -30,6 +30,7 @@ import org.springframework.batch.core.configuration.NoSuchJobConfigurationExcept
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.runtime.SimpleJobIdentifier;
import org.springframework.batch.core.runtime.SimpleJobIdentifierFactory;
import org.springframework.batch.repeat.ExitStatus;

View File

@@ -17,11 +17,13 @@
package org.springframework.batch.execution.repository;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import junit.framework.TestCase;
import org.easymock.ArgumentsMatcher;
import org.easymock.MockControl;
import org.springframework.batch.core.configuration.JobConfiguration;
import org.springframework.batch.core.configuration.StepConfiguration;
@@ -74,6 +76,8 @@ public class SimpleJobRepositoryTests extends TestCase {
List steps;
private JobExecution jobExecution;
public void setUp() throws Exception {
jobDao = (JobDao) jobDaoControl.getMock();
@@ -96,8 +100,13 @@ public class SimpleJobRepositoryTests extends TestCase {
stepConfigurations.add(stepConfiguration2);
jobConfiguration.setSteps(stepConfigurations);
databaseJob = new JobInstance(jobRuntimeInformation, new Long(1));
databaseJob = new JobInstance(jobRuntimeInformation, new Long(1)) {
public JobExecution createNewJobExecution() {
jobExecution = super.createNewJobExecution();
return jobExecution;
}
};
databaseStep1 = new StepInstance(new Long(1));
databaseStep2 = new StepInstance(new Long(2));
@@ -110,7 +119,7 @@ public class SimpleJobRepositoryTests extends TestCase {
/*
* Test a restartable job, that has not been run before.
*/
public void testCreateRestartableJob(){
public void testCreateRestartableJob() throws Exception {
List jobs = new ArrayList();
@@ -122,9 +131,18 @@ public class SimpleJobRepositoryTests extends TestCase {
stepDaoControl.setReturnValue(databaseStep1);
stepDao.createStep(databaseJob, "TestStep2");
stepDaoControl.setReturnValue(databaseStep2);
jobDao.save(new JobExecution(databaseJob));
jobDaoControl.setMatcher(new ArgumentsMatcher(){
public boolean matches(Object[] expected, Object[] actual) {
return ((JobExecution) actual[0]).getJob().equals(databaseJob);
}
public String toString(Object[] arguments) {
return ""+arguments[0];
}
});
stepDaoControl.replay();
jobDaoControl.replay();
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation);
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation).getJob();
assertTrue(job.equals(databaseJob));
List jobSteps = job.getSteps();
Iterator it = jobSteps.iterator();
@@ -134,7 +152,7 @@ public class SimpleJobRepositoryTests extends TestCase {
assertTrue(step.equals(databaseStep2));
}
public void testRestartedJob(){
public void testRestartedJob() throws Exception{
List jobs = new ArrayList();
jobDao.findJobs(jobRuntimeInformation);
jobs.add(databaseJob);
@@ -150,8 +168,24 @@ public class SimpleJobRepositoryTests extends TestCase {
stepDaoControl.replay();
jobDao.getJobExecutionCount(databaseJob.getId());
jobDaoControl.setReturnValue(1);
jobDao.findJobExecutions(databaseJob);
final List executions = new ArrayList();
jobDaoControl.setReturnValue(executions);
jobDao.update(databaseJob);
jobDao.save(new JobExecution(databaseJob));
jobDaoControl.setMatcher(new ArgumentsMatcher(){
public boolean matches(Object[] expected, Object[] actual) {
JobExecution execution = (JobExecution) actual[0];
executions.add(execution);
return execution.getJob().equals(databaseJob);
}
public String toString(Object[] arguments) {
return ""+arguments[0];
}
});
jobDaoControl.setVoidCallable();
jobDaoControl.replay();
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation);
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation).getJob();
assertTrue(job.equals(databaseJob));
List jobSteps = job.getSteps();
Iterator it = jobSteps.iterator();
@@ -164,7 +198,7 @@ public class SimpleJobRepositoryTests extends TestCase {
}
//Test that a restartable job that has multiple instances throws an exception.
public void testFindRestartableJobWithMultipleInstances(){
public void testFindRestartableJobWithMultipleInstances() throws Exception{
List jobs = new ArrayList();
jobs.add(databaseJob);
@@ -183,7 +217,7 @@ public class SimpleJobRepositoryTests extends TestCase {
jobDaoControl.verify();
}
public void testRestartJobStartLimitExceeded(){
public void testRestartJobStartLimitExceeded() throws Exception{
jobConfiguration.setStartLimit(1);
@@ -216,7 +250,7 @@ public class SimpleJobRepositoryTests extends TestCase {
stepDaoControl.verify();
}
public void testCreateNonRestartableJob(){
public void testCreateNonRestartableJob() throws Exception{
List jobs = new ArrayList();
jobConfiguration.setRestartable(false);
@@ -229,9 +263,18 @@ public class SimpleJobRepositoryTests extends TestCase {
stepDaoControl.setReturnValue(databaseStep1);
stepDao.createStep(databaseJob, "TestStep2");
stepDaoControl.setReturnValue(databaseStep2);
jobDao.save(new JobExecution(databaseJob));
jobDaoControl.setMatcher(new ArgumentsMatcher(){
public boolean matches(Object[] expected, Object[] actual) {
return ((JobExecution) actual[0]).getJob().equals(databaseJob);
}
public String toString(Object[] arguments) {
return ""+arguments[0];
}
});
stepDaoControl.replay();
jobDaoControl.replay();
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation);
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation).getJob();
assertTrue(job.equals(databaseJob));
List jobSteps = job.getSteps();
Iterator it = jobSteps.iterator();
@@ -347,7 +390,7 @@ public class SimpleJobRepositoryTests extends TestCase {
* Test to ensure that if a StepDao returns invalid
* restart data, it is corrected.
*/
public void testCreateStepsFixesInvalidRestartData(){
public void testCreateStepsFixesInvalidRestartData() throws Exception{
List jobs = new ArrayList();
@@ -361,9 +404,18 @@ public class SimpleJobRepositoryTests extends TestCase {
stepDao.createStep(databaseJob, "TestStep2");
databaseStep2.setRestartData(new GenericRestartData(null));
stepDaoControl.setReturnValue(databaseStep2);
jobDao.save(new JobExecution(databaseJob));
jobDaoControl.setMatcher(new ArgumentsMatcher(){
public boolean matches(Object[] expected, Object[] actual) {
return ((JobExecution) actual[0]).getJob().equals(databaseJob);
}
public String toString(Object[] arguments) {
return ""+arguments[0];
}
});
stepDaoControl.replay();
jobDaoControl.replay();
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation);
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation).getJob();
List jobSteps = job.getSteps();
Iterator it = jobSteps.iterator();
StepInstance step = (StepInstance) it.next();
@@ -374,7 +426,7 @@ public class SimpleJobRepositoryTests extends TestCase {
assertTrue(step.getRestartData().getProperties().isEmpty());
}
public void testFindStepsFixesInvalidRestartData(){
public void testFindStepsFixesInvalidRestartData() throws Exception{
List jobs = new ArrayList();
jobDao.findJobs(jobRuntimeInformation);
jobs.add(databaseJob);
@@ -392,8 +444,21 @@ public class SimpleJobRepositoryTests extends TestCase {
stepDaoControl.replay();
jobDao.getJobExecutionCount(databaseJob.getId());
jobDaoControl.setReturnValue(1);
jobDao.findJobExecutions(databaseJob);
jobDaoControl.setReturnValue(new ArrayList());
jobDao.update(databaseJob);
jobDaoControl.setVoidCallable();
jobDao.save(new JobExecution(databaseJob));
jobDaoControl.setMatcher(new ArgumentsMatcher(){
public boolean matches(Object[] expected, Object[] actual) {
return ((JobExecution) actual[0]).getJob().equals(databaseJob);
}
public String toString(Object[] arguments) {
return ""+arguments[0];
}
});
jobDaoControl.replay();
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation);
JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation).getJob();
assertTrue(job.equals(databaseJob));
List jobSteps = job.getSteps();
Iterator it = jobSteps.iterator();

View File

@@ -32,7 +32,7 @@ public class JobRepositorySupport implements JobRepository {
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#findOrCreateJob(org.springframework.batch.container.common.domain.JobConfiguration)
*/
public JobInstance findOrCreateJob(JobConfiguration jobConfiguration, JobIdentifier runtimeInformation) {
public JobExecution findOrCreateJob(JobConfiguration jobConfiguration, JobIdentifier runtimeInformation) {
return null;
}

View File

@@ -56,7 +56,7 @@ public class StepExecutorInterruptionTests extends TestCase {
private SimpleStepExecutor executor;
public void setUp() {
public void setUp() throws Exception {
jobRepository = new SimpleJobRepository(jobDao, stepDao);
@@ -65,7 +65,7 @@ public class StepExecutorInterruptionTests extends TestCase {
jobConfiguration.addStep(stepConfiguration);
JobIdentifier runtimeInformation = new SimpleJobIdentifier("TestJob");
jobConfiguration.setBeanName("testJob");
job = jobRepository.findOrCreateJob(jobConfiguration, runtimeInformation);
job = jobRepository.findOrCreateJob(jobConfiguration, runtimeInformation).getJob();
executor = new SimpleStepExecutor();
}

View File

@@ -4,7 +4,7 @@
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/samples"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="samples"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="spring-batch-samples"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="--url jdbc:hsqldb:hsql://localhost:9005/samples"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>

View File

@@ -4,7 +4,7 @@
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/samples"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="samples"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="spring-batch-samples"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<stringAttribute key="bad_container_name" value="\spring-batch-samples\.settings\j"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/spring-batch-samples/src/test/java/org/springframework/batch/sample/TaskExecutorLauncher.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.springframework.batch.sample.TaskExecutorLauncher"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="spring-batch-samples"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dcom.sun.management.jmxremote"/>
</launchConfiguration>

View File

@@ -10,6 +10,6 @@
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;core&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;classpathContainer path=&amp;quot;org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.classpathContainer&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;execution&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;infrastructure&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncher"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="samples"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="spring-batch-samples"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djob.configuration.path=jobs/footballJob.xml -Djob.name=footballjob"/>
</launchConfiguration>

View File

@@ -1,9 +1,9 @@
# Placeholders batch.*
# for HSQLDB:
batch.jdbc.driver=org.hsqldb.jdbcDriver
batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true
# batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true
# use this one for a separate server process (so you can inspect the results)
# batch.jdbc.url=jdbc:hsqldb:hsql://localhost:9005/samples
batch.jdbc.url=jdbc:hsqldb:hsql://localhost:9005/samples
batch.jdbc.user=sa
batch.jdbc.password=
batch.schema=

View File

@@ -33,17 +33,17 @@ INSERT INTO customer (id, version, name, credit) VALUES (2, 0, 'customer2', 1000
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);
YEAR_DRAFTED BIGINT not null
);
CREATE TABLE GAMES (
PLAYER_ID char(8) not null PRIMARY KEY,
PLAYER_ID char(8) not null,
YEAR BIGINT not null,
TEAM char(3) not null,
WEEK BIGINT not null,
@@ -61,7 +61,7 @@ CREATE TABLE GAMES (
);
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL PRIMARY KEY,
ID CHAR(8) NOT NULL,
YEAR BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,
@@ -72,4 +72,5 @@ CREATE TABLE PLAYER_SUMMARY (
RUSH_YARDS BIGINT NOT NULL ,
RECEPTIONS BIGINT NOT NULL ,
RECEPTIONS_YARDS BIGINT NOT NULL ,
TOTAL_TD BIGINT NOT NULL );
TOTAL_TD BIGINT NOT NULL
);

View File

@@ -33,17 +33,17 @@ INSERT INTO customer (id, version, name, credit) VALUES (2, 0, 'customer2', 1000
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);
YEAR_DRAFTED BIGINT not null
);
CREATE TABLE GAMES (
PLAYER_ID char(8) not null PRIMARY KEY,
PLAYER_ID char(8) not null,
YEAR BIGINT not null,
TEAM char(3) not null,
WEEK BIGINT not null,
@@ -61,7 +61,7 @@ CREATE TABLE GAMES (
);
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL PRIMARY KEY,
ID CHAR(8) NOT NULL,
YEAR BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,
@@ -72,4 +72,5 @@ CREATE TABLE PLAYER_SUMMARY (
RUSH_YARDS BIGINT NOT NULL ,
RECEPTIONS BIGINT NOT NULL ,
RECEPTIONS_YARDS BIGINT NOT NULL ,
TOTAL_TD BIGINT NOT NULL );
TOTAL_TD BIGINT NOT NULL
);

View File

@@ -37,7 +37,6 @@ INSERT INTO customer (id, version, name, credit) VALUES (2, 0, 'customer2', 1000
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,
@@ -48,7 +47,7 @@ CREATE TABLE PLAYERS (
);
CREATE TABLE GAMES (
PLAYER_ID char(8) not null PRIMARY KEY,
PLAYER_ID char(8) not null,
YEAR BIGINT not null,
TEAM char(3) not null,
WEEK BIGINT not null,
@@ -66,7 +65,7 @@ CREATE TABLE GAMES (
);
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL PRIMARY KEY,
ID CHAR(8) NOT NULL,
YEAR BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,

View File

@@ -33,17 +33,17 @@ INSERT INTO customer (id, version, name, credit) VALUES (2, 0, 'customer2', 1000
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 NUMBER(38) not null,
YEAR_DRAFTED NUMBER(38) not null);
YEAR_DRAFTED NUMBER(38) not null
);
CREATE TABLE GAMES (
PLAYER_ID char(8) not null PRIMARY KEY,
PLAYER_ID char(8) not null,
YEAR NUMBER(38) not null,
TEAM char(3) not null,
WEEK NUMBER(38) not null,
@@ -61,7 +61,7 @@ CREATE TABLE GAMES (
);
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL PRIMARY KEY,
ID CHAR(8) NOT NULL,
YEAR NUMBER(38) NOT NULL,
COMPLETES NUMBER(38) NOT NULL ,
ATTEMPTS NUMBER(38) NOT NULL ,
@@ -72,4 +72,5 @@ CREATE TABLE PLAYER_SUMMARY (
RUSH_YARDS NUMBER(38) NOT NULL ,
RECEPTIONS NUMBER(38) NOT NULL ,
RECEPTIONS_YARDS NUMBER(38) NOT NULL ,
TOTAL_TD NUMBER(38) NOT NULL );
TOTAL_TD NUMBER(38) NOT NULL
);

View File

@@ -33,17 +33,17 @@ INSERT INTO customer (id, version, name, credit) VALUES (2, 0, 'customer2', 1000
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);
YEAR_DRAFTED BIGINT not null
);
CREATE TABLE GAMES (
PLAYER_ID char(8) not null PRIMARY KEY,
PLAYER_ID char(8) not null,
YEAR BIGINT not null,
TEAM char(3) not null,
WEEK BIGINT not null,
@@ -61,7 +61,7 @@ CREATE TABLE GAMES (
);
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL PRIMARY KEY,
ID CHAR(8) NOT NULL,
YEAR BIGINT NOT NULL,
COMPLETES BIGINT NOT NULL ,
ATTEMPTS BIGINT NOT NULL ,
@@ -72,4 +72,5 @@ CREATE TABLE PLAYER_SUMMARY (
RUSH_YARDS BIGINT NOT NULL ,
RECEPTIONS BIGINT NOT NULL ,
RECEPTIONS_YARDS BIGINT NOT NULL ,
TOTAL_TD BIGINT NOT NULL );
TOTAL_TD BIGINT NOT NULL
);

View File

@@ -114,13 +114,9 @@
</bean>
<bean id="playerFileInputSource"
class="org.springframework.batch.io.file.support.DefaultFlatFileInputSource">
<property name="resource">
<bean
class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="data/footballjob/input/player.csv" />
</bean>
</property>
class="org.springframework.batch.io.file.support.DefaultFlatFileInputSource" scope="step">
<aop:scoped-proxy/>
<property name="resource" value="classpath:data/footballjob/input/player.csv" />
<property name="tokenizer">
<bean
class="org.springframework.batch.io.file.support.transform.DelimitedLineTokenizer">
@@ -135,13 +131,9 @@
</bean>
<bean id="gameFileInputSource"
class="org.springframework.batch.io.file.support.DefaultFlatFileInputSource">
<property name="resource">
<bean
class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="data/footballjob/input/games.csv" />
</bean>
</property>
class="org.springframework.batch.io.file.support.DefaultFlatFileInputSource" scope="step">
<aop:scoped-proxy/>
<property name="resource" value="classpath:data/footballjob/input/games.csv"/>
<property name="tokenizer">
<bean
class="org.springframework.batch.io.file.support.transform.DelimitedLineTokenizer">
@@ -156,7 +148,8 @@
</bean>
<bean id="playerSummarizationSource"
class="org.springframework.batch.io.cursor.JdbcCursorInputSource">
class="org.springframework.batch.io.cursor.JdbcCursorInputSource" scope="step">
<aop:scoped-proxy/>
<property name="dataSource" ref="dataSource" />
<property name="mapper">
<bean

View File

@@ -34,7 +34,7 @@ CREATE TABLE PLAYERS (
);
CREATE TABLE GAMES (
PLAYER_ID char(8) not null PRIMARY KEY,
PLAYER_ID char(8) not null,
YEAR ${BIGINT} not null,
TEAM char(3) not null,
WEEK ${BIGINT} not null,
@@ -52,7 +52,7 @@ CREATE TABLE GAMES (
);
CREATE TABLE PLAYER_SUMMARY (
ID CHAR(8) NOT NULL PRIMARY KEY,
ID CHAR(8) NOT NULL,
YEAR ${BIGINT} NOT NULL,
COMPLETES ${BIGINT} NOT NULL ,
ATTEMPTS ${BIGINT} NOT NULL ,

View File

@@ -50,7 +50,9 @@ public class TaskExecutorLauncher {
};
}).start();
System.out.println("Started application. Please connect using JMX.");
System.out
.println("Started application. "
+ "Please connect using JMX (remember to use -Dcom.sun.management.jmxremote if you can't see anything in Jconsole).");
System.in.read();
}