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

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