Refactored launching to allow exit codes to be returned to the launcher.

This commit is contained in:
lucasward
2007-08-21 05:12:03 +00:00
parent ace8b6dddd
commit d9132be8be
49 changed files with 310 additions and 308 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.execution;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.repeat.ExitStatus;
/**
* Interface which defines a facade for running jobs. The interface is
@@ -40,7 +41,7 @@ public interface JobExecutorFacade {
*
* @throws NoSuchJobConfigurationException
*/
void start(JobIdentifier runtimeInformation) throws NoSuchJobConfigurationException;
ExitStatus start(JobIdentifier runtimeInformation) throws NoSuchJobConfigurationException;
/**
* Stop the job execution that was started with this runtime information.

View File

@@ -35,7 +35,7 @@ public class BatchCommandLineLauncher {
private ConfigurableApplicationContext parent;
private JobLauncher launcher;
private SynchronousJobLauncher launcher;
/**
* Default constructor for the launcher. Sets up the parent context to use
@@ -51,7 +51,7 @@ public class BatchCommandLineLauncher {
*
* @param launcher the launcher to set
*/
public void setLauncher(JobLauncher launcher) {
public void setLauncher(SynchronousJobLauncher launcher) {
this.launcher = launcher;
}
@@ -70,10 +70,10 @@ public class BatchCommandLineLauncher {
try {
if (!launcher.isRunning()) {
if (jobName == null) {
launcher.start();
launcher.run();
}
else {
launcher.start(jobName);
launcher.run(jobName);
}
}
}

View File

@@ -16,10 +16,17 @@
package org.springframework.batch.execution.bootstrap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.configuration.JobConfiguration;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.execution.JobExecutorFacade;
import org.springframework.batch.execution.runtime.ScheduledJobIdentifierFactory;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.context.Lifecycle;
import org.springframework.util.Assert;
/**
* Simple bootstrapping mechanism for running a single job execution in a
@@ -36,11 +43,29 @@ import org.springframework.context.Lifecycle;
* @author Dave Syer
* @since 2.1
*/
public class SimpleJobLauncher extends AbstractJobLauncher {
public class SimpleJobLauncher implements SynchronousJobLauncher {
private static final Log logger = LogFactory.getLog(SimpleJobLauncher.class);
private volatile Thread processingThread;
private volatile boolean running = false;
private JobExecutorFacade jobExecutorFacade;
private JobIdentifierFactory jobIdentifierFactory = new ScheduledJobIdentifierFactory();;
private String jobConfigurationName;
/**
* Check that mandatory properties are set.
*
* @see #setBatchContainer(JobExecutorFacade)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(jobExecutorFacade);
}
/**
* Return whether or not the container is currently running. This is done by
@@ -51,7 +76,7 @@ public class SimpleJobLauncher extends AbstractJobLauncher {
}
/**
* Start the provided container. The current thread will first be saved.
* Start the provided facade. The current thread will first be saved.
* This may seem odd at first, however, this simple bootstrap requires that
* only one thread can kick off a container, and that the first thread that
* calls start is the 'processing thread'. If the container has already been
@@ -61,26 +86,64 @@ public class SimpleJobLauncher extends AbstractJobLauncher {
*
* @throws IllegalStateException if JobConfiguration is null.
*/
protected void doStart(JobIdentifier jobIdentifier) throws NoSuchJobConfigurationException {
public ExitStatus run(JobIdentifier jobIdentifier) throws NoSuchJobConfigurationException {
Assert.notNull(jobIdentifier, "JobIdentifier must not be null.");
Assert.isTrue(!isRunning(), "SynchronousLaunchers can run only one job at at time.");
/*
* There is no reason to kick off a new thread, since only one thread
* should be processing at once. However, a handle to the thread should
* be maintained to allow for interrupt
*/
processingThread = Thread.currentThread();
// TODO: push this out to a method call in parent inside synchronized
// block?
running = true;
try {
batchContainer.start(jobIdentifier);
return jobExecutorFacade.start(jobIdentifier);
}
finally {
running = false;
unregister(jobIdentifier);
}
}
/**
* Start a job execution with the given name. If a job is already running
* has no effect.
*
* @param name the name to assign to the job
* @throws NoSuchJobConfigurationException
*/
public ExitStatus run(String name) throws NoSuchJobConfigurationException {
JobIdentifier runtimeInformation = jobIdentifierFactory.getJobIdentifier(name);
return this.run(runtimeInformation);
}
/**
* Start a job execution with default name and other runtime information
* provided by the factory. If a job is already running has no effect. The
* default name is taken from the enclosed {@link JobConfiguration}.
* @throws NoSuchJobConfigurationException if the job configuration cannot be located
*
* @see #setJobRuntimeInformationFactory(JobIdentifierFactory)
* @see org.springframework.context.Lifecycle#start()
*/
public ExitStatus run(){
if (jobConfigurationName==null) {
return new ExitStatus(false, "JOB_CONFIGURATION_NOT_PROVIDED", "No JobConfiguration was " +
"provided to the launcher.");
}
try {
return this.run(jobConfigurationName);
}
catch (NoSuchJobConfigurationException e) {
logger.error("JobExecutorFacade failed to find a JobConfiguration" +
" for the provided JobIdentifier", e);
return new ExitStatus(false, "NO_SUCH_JOB_CONFIGURATION", "JobExecutor Facade failed" +
"to find a JobConfiguration for the provided JobIdentifier");
}
}
/**
* Stop the job if it is running by interrupting its thread. If no job is
@@ -89,22 +152,24 @@ public class SimpleJobLauncher extends AbstractJobLauncher {
* (non-Javadoc)
* @see org.springframework.context.Lifecycle#stop()
*/
protected void doStop() {
public void stop() {
if (isRunning()) {
processingThread.interrupt();
running = false;
}
}
/**
* Delegates to {@link #doStop()}. Since there is only one job running in
* this launcher this is OK.
*
* (non-Javadoc)
* @see org.springframework.context.Lifecycle#stop()
*/
protected void doStop(JobIdentifier runtimeInformation) {
doStop();
public void setJobExecutorFacade(JobExecutorFacade jobExecutorFacade) {
this.jobExecutorFacade = jobExecutorFacade;
}
public void setJobIdentifierFactory(
JobIdentifierFactory jobIdentifierFactory) {
this.jobIdentifierFactory = jobIdentifierFactory;
}
public void setJobConfigurationName(String jobConfigurationName) {
this.jobConfigurationName = jobConfigurationName;
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.execution.JobExecutorFacade;
import org.springframework.batch.execution.NoSuchJobExecutionException;
import org.springframework.batch.execution.job.DefaultJobExecutor;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.statistics.StatisticsProvider;
import org.springframework.util.Assert;
@@ -113,7 +114,7 @@ public class SimpleJobExecutorFacade implements JobExecutorFacade, StatisticsPro
* null
*
*/
public void start(JobIdentifier jobRuntimeInformation) throws NoSuchJobConfigurationException {
public ExitStatus start(JobIdentifier jobRuntimeInformation) throws NoSuchJobConfigurationException {
Assert.notNull(jobRuntimeInformation, "JobRuntimeInformation must not be null.");
Assert.notNull(jobRuntimeInformation.getName(), "JobRuntimeInformation name must not be null.");
@@ -129,11 +130,13 @@ public class SimpleJobExecutorFacade implements JobExecutorFacade, StatisticsPro
final JobInstance job = jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation);
JobExecutionContext jobExecutionContext = jobExecutionRegistry.register(jobRuntimeInformation, job);
ExitStatus exitStatus = ExitStatus.FAILED;
try {
synchronized (mutex) {
running++;
}
jobExecutor.run(jobConfiguration, jobExecutionContext);
exitStatus = jobExecutor.run(jobConfiguration, jobExecutionContext);
}
finally {
synchronized (mutex) {
@@ -144,6 +147,7 @@ public class SimpleJobExecutorFacade implements JobExecutorFacade, StatisticsPro
jobExecutionRegistry.unregister(jobRuntimeInformation);
}
return exitStatus;
}
/*

View File

@@ -51,7 +51,7 @@ public class DefaultJobExecutor implements JobExecutor {
private StepExecutorFactory stepExecutorResolver = new DefaultStepExecutorFactory();
public void run(JobConfiguration configuration, JobExecutionContext jobExecutionContext)
public ExitStatus run(JobConfiguration configuration, JobExecutionContext jobExecutionContext)
throws BatchCriticalException {
JobInstance job = jobExecutionContext.getJob();
@@ -90,6 +90,8 @@ public class DefaultJobExecutor implements JobExecutor {
jobExecution.setExitCode(status.getExitCode());
jobRepository.saveOrUpdate(jobExecution);
}
return status;
}
private void updateStatus(JobExecutionContext jobExecutionContext, BatchStatus status) {

View File

@@ -227,7 +227,7 @@ public class SqlStepDao implements StepDao, InitializingBean {
Object[] parameters = new Object[] { stepExecution.getId(), new Long(0), stepExecution.getStepId(), stepExecution.getJobExecutionId(),
stepExecution.getStartTime(), stepExecution.getEndTime(), stepExecution.getStatus().toString(),
stepExecution.getCommitCount(), stepExecution.getTaskCount(),
PropertiesConverter.propertiesToString(stepExecution.getStatistics()), new Integer(stepExecution.getExitCode()) };
PropertiesConverter.propertiesToString(stepExecution.getStatistics()), stepExecution.getExitCode() };
jdbcTemplate.update(SAVE_STEP_EXECUTION, parameters);
}
@@ -252,7 +252,7 @@ public class SqlStepDao implements StepDao, InitializingBean {
Object[] parameters = new Object[] { stepExecution.getStartTime(), stepExecution.getEndTime(),
stepExecution.getStatus().toString(), stepExecution.getCommitCount(),
stepExecution.getTaskCount(), PropertiesConverter.propertiesToString(stepExecution.getStatistics()),
new Integer(stepExecution.getExitCode()),
stepExecution.getExitCode(),
stepExecution.getId() };
jdbcTemplate.update(UPDATE_STEP_EXECUTION, parameters);
@@ -292,7 +292,7 @@ public class SqlStepDao implements StepDao, InitializingBean {
stepExecution.setCommitCount(rs.getInt(6));
stepExecution.setTaskCount(rs.getInt(7));
stepExecution.setStatistics(PropertiesConverter.stringToProperties(rs.getString(8)));
stepExecution.setExitCode(rs.getInt(9));
stepExecution.setExitCode(rs.getString(9));
return stepExecution;
}
};

View File

@@ -20,6 +20,7 @@ import org.springframework.batch.core.domain.StepInstance;
import org.springframework.batch.core.tasklet.Tasklet;
import org.springframework.batch.core.tasklet.Recoverable;
import org.springframework.batch.io.Skippable;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@@ -50,13 +51,13 @@ public class DefaultStepExecutor extends SimpleStepExecutor {
* @see org.springframework.batch.execution.step.simple.SimpleStepExecutor#doTaskletProcessing(org.springframework.batch.core.tasklet.Tasklet,
* org.springframework.batch.core.domain.StepInstance)
*/
protected boolean doTaskletProcessing(Tasklet module, final StepInstance step) throws Exception {
protected ExitStatus doTaskletProcessing(Tasklet module, final StepInstance step) throws Exception {
boolean result = true;
ExitStatus exitStatus = ExitStatus.CONTINUABLE;
try {
result = super.doTaskletProcessing(module, step);
exitStatus = super.doTaskletProcessing(module, step);
}
catch (final Exception e) {
@@ -80,7 +81,7 @@ public class DefaultStepExecutor extends SimpleStepExecutor {
}
return result;
return exitStatus;
}
}

View File

@@ -247,10 +247,13 @@ public class SimpleStepExecutor implements StepExecutor {
});
stepExecution.setExitCode(status.getExitCode());
stepExecution.setExitDescription(status.getExitDescription());
updateStatus(stepExecutionContext, BatchStatus.COMPLETED);
return status;
}
catch (RuntimeException e) {
stepExecution.setException(e);
if (e.getCause() instanceof StepInterruptedException) {
updateStatus(stepExecutionContext, BatchStatus.STOPPED);
throw (StepInterruptedException) e.getCause();
@@ -320,11 +323,11 @@ public class SimpleStepExecutor implements StepExecutor {
});
// check for interruption before each item as well
interruptionPolicy.checkInterrupted(context);
boolean result = doTaskletProcessing(configuration.getTasklet(), stepExecutionContext.getStep());
ExitStatus exitStatus = doTaskletProcessing(configuration.getTasklet(), stepExecutionContext.getStep());
stepExecutionContext.getStepExecution().incrementTaskCount();
// check for interruption after each item as well
interruptionPolicy.checkInterrupted(context);
return new ExitStatus(result);
return exitStatus;
}
});
}
@@ -338,7 +341,7 @@ public class SimpleStepExecutor implements StepExecutor {
* @return boolean if there is more processing to do
* @throws Exception if there is an error
*/
protected boolean doTaskletProcessing(Tasklet tasklet, StepInstance step) throws Exception {
protected ExitStatus doTaskletProcessing(Tasklet tasklet, StepInstance step) throws Exception {
return tasklet.execute();
}

View File

@@ -27,6 +27,7 @@ import org.springframework.batch.core.tasklet.Tasklet;
import org.springframework.batch.io.Skippable;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemProvider;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.synch.RepeatSynchronizationManager;
import org.springframework.batch.retry.RetryOperations;
@@ -132,14 +133,14 @@ public class ItemProviderProcessTasklet implements Tasklet, Recoverable, Skippab
*
* @see org.springframework.batch.core.tasklet.Tasklet#execute()
*/
public boolean execute() throws Exception {
public ExitStatus execute() throws Exception {
if (retryOperations != null) {
return retryOperations.execute(new ItemProviderRetryCallback(itemProvider, itemProcessor)) != null;
return new ExitStatus(retryOperations.execute(new ItemProviderRetryCallback(itemProvider, itemProcessor)) != null);
}
else {
Object data = itemProvider.next();
if (data == null) {
return false;
return ExitStatus.FINISHED;
}
RepeatContext context = RepeatSynchronizationManager.getContext();
Assert.state(context != null,
@@ -149,7 +150,7 @@ public class ItemProviderProcessTasklet implements Tasklet, Recoverable, Skippab
// No exception so clear context (we can't recover directly because
// the current transaction is going to roll back)
context.removeAttribute(ITEM_KEY);
return true;
return ExitStatus.CONTINUABLE;
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.batch.execution.tasklet;
import org.springframework.batch.core.tasklet.Tasklet;
import org.springframework.batch.repeat.ExitStatus;
/**
* Provides the basic batch module for reading and processing data.
@@ -39,12 +40,12 @@ public abstract class ReadProcessTasklet implements Tasklet {
* from the abstract read method will be returned to the {@link Tasklet}, to indicate
* whether or not processing should continue.
*/
public final boolean execute() throws Exception {
public final ExitStatus execute() throws Exception {
if (!read()) {
return false;
return ExitStatus.FINISHED;
}
process();
return true;
return ExitStatus.CONTINUABLE;
}
/**

View File

@@ -26,7 +26,7 @@ CREATE TABLE BATCH_JOB_EXECUTION (
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP ,
STATUS VARCHAR(10),
EXIT_CODE BIGINT);
EXIT_CODE VARCHAR(250));
CREATE TABLE BATCH_STEP (
ID BIGINT PRIMARY KEY ,
@@ -47,7 +47,7 @@ CREATE TABLE BATCH_STEP_EXECUTION (
COMMIT_COUNT BIGINT ,
TASK_COUNT BIGINT ,
TASK_STATISTICS VARCHAR(250),
EXIT_CODE BIGINT,
EXIT_CODE VARCHAR(250),
EXIT_MESSAGE VARCHAR(250));
CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;

View File

@@ -26,7 +26,7 @@ CREATE TABLE BATCH_JOB_EXECUTION (
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP ,
STATUS VARCHAR(10),
EXIT_CODE BIGINT);
EXIT_CODE VARCHAR(250));
CREATE TABLE BATCH_STEP (
ID BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
@@ -47,7 +47,7 @@ CREATE TABLE BATCH_STEP_EXECUTION (
COMMIT_COUNT BIGINT ,
TASK_COUNT BIGINT ,
TASK_STATISTICS VARCHAR(250),
EXIT_CODE BIGINT,
EXIT_CODE VARCHAR(250),
EXIT_MESSAGE VARCHAR(250));
CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;

View File

@@ -26,7 +26,7 @@ CREATE TABLE BATCH_JOB_EXECUTION (
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP ,
STATUS VARCHAR(10),
EXIT_CODE BIGINT);
EXIT_CODE VARCHAR(250));
CREATE TABLE BATCH_STEP (
ID BIGINT IDENTITY PRIMARY KEY ,
@@ -47,7 +47,7 @@ CREATE TABLE BATCH_STEP_EXECUTION (
COMMIT_COUNT BIGINT ,
TASK_COUNT BIGINT ,
TASK_STATISTICS VARCHAR(250),
EXIT_CODE BIGINT,
EXIT_CODE VARCHAR(250),
EXIT_MESSAGE VARCHAR(250));
CREATE TABLE BATCH_STEP_EXECUTION_SEQ (

View File

@@ -26,7 +26,7 @@ CREATE TABLE BATCH_JOB_EXECUTION (
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP ,
STATUS VARCHAR(10),
EXIT_CODE INT);
EXIT_CODE VARCHAR(250));
CREATE TABLE BATCH_STEP (
ID INT PRIMARY KEY ,
@@ -47,7 +47,7 @@ CREATE TABLE BATCH_STEP_EXECUTION (
COMMIT_COUNT INT ,
TASK_COUNT INT ,
TASK_STATISTICS VARCHAR(250),
EXIT_CODE INT,
EXIT_CODE VARCHAR(250),
EXIT_MESSAGE VARCHAR(250));
CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;

View File

@@ -26,7 +26,7 @@ CREATE TABLE BATCH_JOB_EXECUTION (
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP ,
STATUS VARCHAR(10),
EXIT_CODE BIGINT);
EXIT_CODE VARCHAR(250));
CREATE TABLE BATCH_STEP (
ID BIGINT PRIMARY KEY ,
@@ -47,7 +47,7 @@ CREATE TABLE BATCH_STEP_EXECUTION (
COMMIT_COUNT BIGINT ,
TASK_COUNT BIGINT ,
TASK_STATISTICS VARCHAR(250),
EXIT_CODE BIGINT,
EXIT_CODE VARCHAR(250),
EXIT_MESSAGE VARCHAR(250));
CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;

View File

@@ -15,7 +15,7 @@ CREATE TABLE BATCH_JOB_EXECUTION (
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP ,
STATUS VARCHAR(10),
EXIT_CODE ${BIGINT});
EXIT_CODE VARCHAR(250));
CREATE TABLE BATCH_STEP (
ID ${BIGINT} $!{IDENTITY} PRIMARY KEY $!{GENERATED},
@@ -36,7 +36,7 @@ CREATE TABLE BATCH_STEP_EXECUTION (
COMMIT_COUNT ${BIGINT} ,
TASK_COUNT ${BIGINT} ,
TASK_STATISTICS VARCHAR(250),
EXIT_CODE ${BIGINT},
EXIT_CODE VARCHAR(250),
EXIT_MESSAGE VARCHAR(250));
#sequence( "BATCH_STEP_EXECUTION_SEQ" )

View File

@@ -20,70 +20,24 @@ import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.core.configuration.JobConfiguration;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.core.runtime.SimpleJobIdentifier;
import org.springframework.batch.execution.JobExecutorFacade;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
public class SimpleJobLauncherTests extends TestCase {
public void testAutoStartContainer() throws Exception {
MockControl containerControl = MockControl.createControl(JobExecutorFacade.class);
JobExecutorFacade mockContainer;
AbstractJobLauncher bootstrap = new SimpleJobLauncher();
final SimpleJobIdentifier runtimeInformation = new SimpleJobIdentifier("foo");
bootstrap.setJobRuntimeInformationFactory(new JobIdentifierFactory() {
public JobIdentifier getJobIdentifier(String name) {
return runtimeInformation;
}
});
mockContainer = (JobExecutorFacade) containerControl.getMock();
bootstrap.setBatchContainer(mockContainer);
JobConfiguration jobConfiguration = new JobConfiguration("foo");
bootstrap.setJobConfigurationName(jobConfiguration.getName());
mockContainer.start(runtimeInformation);
containerControl.replay();
bootstrap.setAutoStart(true);
bootstrap.onApplicationEvent(new ContextRefreshedEvent(new GenericApplicationContext()));
// It ran and then stopped...
assertFalse(bootstrap.isRunning());
containerControl.verify();
}
public void testApplicationEventNotContextRefresh() throws Exception {
MockControl containerControl = MockControl.createControl(JobExecutorFacade.class);
JobExecutorFacade mockContainer;
AbstractJobLauncher bootstrap = new SimpleJobLauncher();
mockContainer = (JobExecutorFacade) containerControl.getMock();
bootstrap.setBatchContainer(mockContainer);
containerControl.replay();
bootstrap.setAutoStart(true);
bootstrap.onApplicationEvent(new ApplicationEvent(new GenericApplicationContext()) {
});
assertFalse(bootstrap.isRunning());
containerControl.verify();
}
public void testStartWithNoConfiguration() throws Exception {
final AbstractJobLauncher bootstrap = new SimpleJobLauncher();
final SimpleJobLauncher launcher = new SimpleJobLauncher();
try {
bootstrap.afterPropertiesSet();
launcher.afterPropertiesSet();
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException e) {
@@ -93,9 +47,9 @@ public class SimpleJobLauncherTests extends TestCase {
}
public void testInitializeWithNoConfiguration() throws Exception {
final AbstractJobLauncher bootstrap = new SimpleJobLauncher();
final SimpleJobLauncher launcher = new SimpleJobLauncher();
try {
bootstrap.start();
launcher.run();
// should do nothing
}
catch (Exception e) {
@@ -103,83 +57,93 @@ public class SimpleJobLauncherTests extends TestCase {
}
}
public void testStartTwiceNotFatal() throws Exception {
AbstractJobLauncher bootstrap = new SimpleJobLauncher();
public void testRunTwiceNotFatal() throws Exception {
SimpleJobLauncher launcher = new SimpleJobLauncher();
final SimpleJobIdentifier runtimeInformation = new SimpleJobIdentifier("foo");
bootstrap.setJobRuntimeInformationFactory(new JobIdentifierFactory() {
launcher.setJobIdentifierFactory(new JobIdentifierFactory() {
public JobIdentifier getJobIdentifier(String name) {
return runtimeInformation;
}
});
InterruptibleContainer container = new InterruptibleContainer();
bootstrap.setBatchContainer(container);
bootstrap.setJobConfigurationName(new JobConfiguration("foo").getName());
bootstrap.start();
bootstrap.start();
InterruptibleFacade jobExecutorFacade = new InterruptibleFacade();
launcher.setJobExecutorFacade(jobExecutorFacade);
launcher.setJobConfigurationName(new JobConfiguration("foo").getName());
launcher.run();
launcher.run();
// Both jobs finished running because they were not launched in a new
// Thread
assertFalse(bootstrap.isRunning());
assertFalse(launcher.isRunning());
}
public void testInterruptContainer() throws Exception {
final AbstractJobLauncher bootstrap = new SimpleJobLauncher();
final SimpleJobLauncher launcher = new SimpleJobLauncher();
final SimpleJobIdentifier runtimeInformation = new SimpleJobIdentifier("foo");
bootstrap.setJobRuntimeInformationFactory(new JobIdentifierFactory() {
launcher.setJobIdentifierFactory(new JobIdentifierFactory() {
public JobIdentifier getJobIdentifier(String name) {
return runtimeInformation;
}
});
InterruptibleContainer container = new InterruptibleContainer();
bootstrap.setBatchContainer(container);
bootstrap.setJobConfigurationName(new JobConfiguration("foo").getName());
Thread bootstrapThread = new Thread() {
InterruptibleFacade jobExecutorFacade = new InterruptibleFacade();
launcher.setJobExecutorFacade(jobExecutorFacade);
launcher.setJobConfigurationName(new JobConfiguration("foo").getName());
TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
Runnable launcherRunnable = new Runnable() {
public void run() {
bootstrap.start();
System.out.println("run called");
launcher.run();
System.out.println("run finished.");
}
};
bootstrapThread.start();
taskExecutor.execute(launcherRunnable);
// give the thread a second to start up
System.out.println("first sleep");
Thread.sleep(100);
assertTrue(bootstrap.isRunning());
bootstrap.stop();
assertTrue(launcher.isRunning());
launcher.stop();
Thread.sleep(100);
assertFalse(bootstrap.isRunning());
assertFalse(launcher.isRunning());
}
public void testStopOnUnstartedContainer() {
public void testStopOnUnranLauncher() {
AbstractJobLauncher bootstrap = new SimpleJobLauncher();
SimpleJobLauncher launcher = new SimpleJobLauncher();
assertFalse(bootstrap.isRunning());
// no exception should be thrown if stop is called on unstarted
assertFalse(launcher.isRunning());
// no exception should be thrown if stop is called on unran
// container
// this is to fullfill the contract outlined in Lifecycle#stop().
bootstrap.stop();
launcher.stop();
}
private class InterruptibleContainer implements JobExecutorFacade {
private class InterruptibleFacade implements JobExecutorFacade {
/*
* (non-Javadoc)
* @see org.springframework.batch.container.BatchContainer#start()
* @see org.springframework.batch.container.BatchContainer#run()
*/
public void start() {
public void run() {
try {
// 1 seconds should be long enough to allow the thread to be
// started and
// for interrupt to be called;
// run and for interrupt to be called;
System.out.println("Facade sleep called.");
Thread.sleep(300);
//return ExitStatus.FAILED;
}
catch (InterruptedException ex) {
// thread interrupted, allow to exit normally
//return ExitStatus.FAILED;
}
}
public void start(JobIdentifier runtimeInformation) {
start();
public ExitStatus start(JobIdentifier runtimeInformation) {
run();
return ExitStatus.FAILED;
}
public void stop(JobIdentifier runtimeInformation) {

View File

@@ -31,6 +31,7 @@ import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.core.runtime.SimpleJobIdentifier;
import org.springframework.batch.execution.JobExecutorFacade;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.interceptor.RepeatOperationsApplicationEvent;
import org.springframework.batch.statistics.StatisticsProvider;
import org.springframework.batch.support.PropertiesConverter;
@@ -142,8 +143,9 @@ public class TaskExecutorJobLauncherTests extends TestCase {
}
}
public void start(JobIdentifier runtimeInformation) {
public ExitStatus start(JobIdentifier runtimeInformation) {
start();
return ExitStatus.FAILED;
}
public void stop(JobIdentifier runtimeInformation) {

View File

@@ -80,8 +80,9 @@ public class SimpleJobExecutorFacaderTests extends TestCase {
final SimpleJobIdentifier jobRuntimeInformation = new SimpleJobIdentifier("bar");
jobRepository.findOrCreateJob(jobConfiguration, jobRuntimeInformation);
jobExecutor = new JobExecutor() {
public void run(JobConfiguration configuration, JobExecutionContext jobExecutionContext) throws BatchCriticalException {
public ExitStatus run(JobConfiguration configuration, JobExecutionContext jobExecutionContext) throws BatchCriticalException {
jobExecutionContext.getJob().setIdentifier(jobRuntimeInformation);
return ExitStatus.FINISHED;
}
};
JobInstance job = new JobInstance();
@@ -104,7 +105,7 @@ public class SimpleJobExecutorFacaderTests extends TestCase {
public void testIsRunning() throws Exception {
simpleContainer.setJobExecutor(new JobExecutor() {
public void run(JobConfiguration configuration, JobExecutionContext jobExecutionContext)
public ExitStatus run(JobConfiguration configuration, JobExecutionContext jobExecutionContext)
throws BatchCriticalException {
while (running) {
try {
@@ -113,7 +114,11 @@ public class SimpleJobExecutorFacaderTests extends TestCase {
catch (InterruptedException e) {
throw new BatchCriticalException("Interrupted unexpectedly!");
}
}
return ExitStatus.FINISHED;
}
});
simpleContainer.setJobConfigurationLocator(new JobConfigurationLocator() {

View File

@@ -241,7 +241,5 @@ public class DefaultJobExecutorTests extends TestCase {
JobExecution jobExecution = (JobExecution) jobDao.findJobExecutions(job).get(0);
assertEquals(job.getId(), jobExecution.getJobId());
assertEquals(status, jobExecution.getStatus());
int exitCode = status==BatchStatus.STOPPED || status==BatchStatus.FAILED ? -1 : 0;
assertEquals(exitCode, jobExecution.getExitCode());
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.execution.step.simple;
import junit.framework.TestCase;
import org.springframework.batch.core.tasklet.Tasklet;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.support.RepeatTemplate;
/**
@@ -42,8 +43,8 @@ public class ChunkOperationsStepConfigurationTests extends TestCase {
*/
public void testStepExecutorStepConfigurationTasklet() {
Tasklet tasklet = new Tasklet() {
public boolean execute() throws Exception {
return false;
public ExitStatus execute() throws Exception {
return ExitStatus.FINISHED;
}
};
configuration = new ChunkOperationsStepConfiguration(tasklet);

View File

@@ -36,6 +36,7 @@ import org.springframework.batch.execution.tasklet.ItemProviderProcessTasklet;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemProvider;
import org.springframework.batch.item.provider.ListItemProvider;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
import org.springframework.batch.repeat.support.RepeatTemplate;
@@ -129,13 +130,13 @@ public class DefaultStepExecutorTests extends TestCase {
final StepExecutionContext stepExecutionContext = new StepExecutionContext(jobExecutionContext, step);
stepConfiguration.setTasklet(new Tasklet() {
public boolean execute() throws Exception {
public ExitStatus execute() throws Exception {
assertEquals(step, stepExecutionContext.getStep());
assertEquals(1, jobExecutionContext.getChunkContexts().size());
assertEquals(1, jobExecutionContext.getStepContexts().size());
assertNotNull(StepSynchronizationManager.getContext().getJobIdentifier());
processed.add("foo");
return true;
return ExitStatus.CONTINUABLE;
}
});
@@ -166,7 +167,7 @@ public class DefaultStepExecutorTests extends TestCase {
Tasklet module = new Tasklet(){
public boolean execute() throws Exception {
public ExitStatus execute() throws Exception {
int counter = 0;
counter++;
@@ -174,7 +175,7 @@ public class DefaultStepExecutorTests extends TestCase {
throw new Exception();
}
return true;
return ExitStatus.CONTINUABLE;
}
};

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.execution.step.simple;
import junit.framework.TestCase;
import org.springframework.batch.core.tasklet.Tasklet;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.exception.handler.DefaultExceptionHandler;
/**
@@ -42,8 +43,8 @@ public class SimpleStepConfigurationTests extends TestCase {
*/
public void testSimpleStepConfigurationTasklet() {
Tasklet tasklet = new Tasklet() {
public boolean execute() throws Exception {
return false;
public ExitStatus execute() throws Exception {
return ExitStatus.FINISHED;
}
};
configuration = new SimpleStepConfiguration(tasklet);

View File

@@ -36,6 +36,7 @@ import org.springframework.batch.execution.repository.dao.JobDao;
import org.springframework.batch.execution.repository.dao.MapJobDao;
import org.springframework.batch.execution.repository.dao.MapStepDao;
import org.springframework.batch.execution.repository.dao.StepDao;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
import org.springframework.batch.repeat.support.RepeatTemplate;
@@ -75,14 +76,14 @@ public class StepExecutorInterruptionTests extends TestCase {
JobExecutionContext jobExecutionContext = new JobExecutionContext(null, new JobInstance(new Long(0)));
final StepExecutionContext stepExecutionContext = new StepExecutionContext(jobExecutionContext, step);
stepConfiguration.setTasklet(new Tasklet() {
public boolean execute() throws Exception {
public ExitStatus execute() throws Exception {
// do something non-trivial (and not Thread.sleep())
double foo = 1;
for (int i = 2; i < 250; i++) {
foo = foo * i;
}
// always return true, so processing always continues
return foo != 1;
return new ExitStatus(foo != 1);
}
});

View File

@@ -100,7 +100,7 @@ public class ItemProviderProcessTaskletTests extends TestCase {
items = Collections.singletonList("foo");
// call execute
assertTrue(module.execute());
assertTrue(module.execute().isContinuable());
// verify method calls
assertEquals(1, list.size());
@@ -112,7 +112,7 @@ public class ItemProviderProcessTaskletTests extends TestCase {
// TEST2: data provider returns null (nothing to read)
// call read
assertFalse(module.execute());
assertFalse(module.execute().isContinuable());
}

View File

@@ -15,7 +15,7 @@ CREATE TABLE BATCH_JOB_EXECUTION (
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP ,
STATUS VARCHAR(10),
EXIT_CODE BIGINT);
EXIT_CODE VARCHAR(250));
CREATE TABLE BATCH_STEP (
ID BIGINT IDENTITY PRIMARY KEY ,
@@ -36,7 +36,7 @@ CREATE TABLE BATCH_STEP_EXECUTION (
COMMIT_COUNT BIGINT ,
TASK_COUNT BIGINT ,
TASK_STATISTICS VARCHAR(250),
EXIT_CODE BIGINT,
EXIT_CODE VARCHAR(250),
EXIT_MESSAGE VARCHAR(250));
CREATE TABLE BATCH_STEP_EXECUTION_SEQ (

View File

@@ -7,15 +7,15 @@
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="simpleContainer" class="org.springframework.batch.execution.facade.SimpleJobExecutorFacade">
<bean id="simpleFacade" class="org.springframework.batch.execution.facade.SimpleJobExecutorFacade">
<property name="jobRepository" ref="simpleJobRepository" />
<property name="jobConfigurationLocator" ref="jobConfigurationRegistry"/>
<property name="jobExecutor" ref="jobLifecycle" />
</bean>
<bean id="simpleContainerLauncher" class="org.springframework.batch.execution.bootstrap.SimpleJobLauncher">
<property name="batchContainer" ref="simpleContainer" />
<property name="jobRuntimeInformationFactory" ref="jobRuntimeInformationFactory"/>
<property name="jobExecutorFacade" ref="simpleFacade" />
<property name="jobIdentifierFactory" ref="jobRuntimeInformationFactory"/>
</bean>
<bean id="jobConfigurationRegistry" class="org.springframework.batch.execution.configuration.MapJobConfigurationRegistry"/>