Refactored launching to allow exit codes to be returned to the launcher.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user