-
Notifications
You must be signed in to change notification settings - Fork 2.4k
BATCH-2009 - stoppable tasklet #173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e23eb35
5d27967
0ebda8f
cfa15bf
f9c8017
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.springframework.batch.core.step.tasklet; | ||
|
||
/** | ||
* | ||
* Stoppable tasklet | ||
* | ||
* @author Will Schipp | ||
* | ||
*/ | ||
public interface StoppableTasklet extends Tasklet { | ||
|
||
/** | ||
* method to signal a long running/looping tasklet to stop | ||
* | ||
*/ | ||
void stop(); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,15 +15,17 @@ | |
*/ | ||
package org.springframework.batch.core.launch.support; | ||
|
||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.assertTrue; | ||
import static org.junit.Assert.fail; | ||
import static org.mockito.Matchers.anyString; | ||
import static org.mockito.Mockito.doThrow; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
|
@@ -36,12 +38,17 @@ | |
import org.springframework.batch.core.BatchStatus; | ||
import org.springframework.batch.core.Job; | ||
import org.springframework.batch.core.JobExecution; | ||
import org.springframework.batch.core.JobExecutionException; | ||
import org.springframework.batch.core.JobInstance; | ||
import org.springframework.batch.core.JobParameters; | ||
import org.springframework.batch.core.JobParametersIncrementer; | ||
import org.springframework.batch.core.Step; | ||
import org.springframework.batch.core.StepContribution; | ||
import org.springframework.batch.core.configuration.JobRegistry; | ||
import org.springframework.batch.core.configuration.support.MapJobRegistry; | ||
import org.springframework.batch.core.converter.DefaultJobParametersConverter; | ||
import org.springframework.batch.core.explore.JobExplorer; | ||
import org.springframework.batch.core.job.AbstractJob; | ||
import org.springframework.batch.core.job.JobSupport; | ||
import org.springframework.batch.core.launch.JobInstanceAlreadyExistsException; | ||
import org.springframework.batch.core.launch.JobLauncher; | ||
|
@@ -52,6 +59,10 @@ | |
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; | ||
import org.springframework.batch.core.repository.JobRepository; | ||
import org.springframework.batch.core.repository.JobRestartException; | ||
import org.springframework.batch.core.scope.context.ChunkContext; | ||
import org.springframework.batch.core.step.tasklet.StoppableTasklet; | ||
import org.springframework.batch.core.step.tasklet.TaskletStep; | ||
import org.springframework.batch.repeat.RepeatStatus; | ||
import org.springframework.batch.support.PropertiesConverter; | ||
|
||
/** | ||
|
@@ -349,6 +360,67 @@ public void testStop() throws Exception{ | |
assertEquals(BatchStatus.STOPPING, jobExecution.getStatus()); | ||
} | ||
|
||
@Test | ||
public void testStopTasklet() throws Exception { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This tests the rosey scenario. What about when something goes wrong with stopping a Tasklet (exception being thrown in the StoppableTasklet#stop() method at a minimum). |
||
JobInstance jobInstance = new JobInstance(123L, job.getName()); | ||
JobExecution jobExecution = new JobExecution(jobInstance, 111L, jobParameters); | ||
StoppableTasklet tasklet = mock(StoppableTasklet.class); | ||
TaskletStep taskletStep = new TaskletStep(); | ||
taskletStep.setTasklet(tasklet); | ||
MockJob job = new MockJob(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same not as above... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. allowed test construction of the job to test the retrieval of the tasklet |
||
job.taskletStep = taskletStep; | ||
|
||
JobRegistry jobRegistry = mock(JobRegistry.class); | ||
TaskletStep step = mock(TaskletStep.class); | ||
|
||
when(step.getTasklet()).thenReturn(tasklet); | ||
when(step.getName()).thenReturn("test_job.step1"); | ||
when(jobRegistry.getJob(anyString())).thenReturn(job); | ||
when(jobExplorer.getJobExecution(111L)).thenReturn(jobExecution); | ||
|
||
jobOperator.setJobRegistry(jobRegistry); | ||
jobExplorer.getJobExecution(111L); | ||
jobRepository.update(jobExecution); | ||
jobOperator.stop(111L); | ||
assertEquals(BatchStatus.STOPPING, jobExecution.getStatus()); | ||
} | ||
|
||
@Test | ||
public void testStopTaskletException() throws Exception { | ||
JobInstance jobInstance = new JobInstance(123L, job.getName()); | ||
JobExecution jobExecution = new JobExecution(jobInstance, 111L, jobParameters); | ||
StoppableTasklet tasklet = new StoppableTasklet() { | ||
|
||
@Override | ||
public RepeatStatus execute(StepContribution contribution, | ||
ChunkContext chunkContext) throws Exception { | ||
return null; | ||
} | ||
|
||
@Override | ||
public void stop() { | ||
throw new IllegalStateException(); | ||
}}; | ||
TaskletStep taskletStep = new TaskletStep(); | ||
taskletStep.setTasklet(tasklet); | ||
MockJob job = new MockJob(); | ||
job.taskletStep = taskletStep; | ||
|
||
JobRegistry jobRegistry = mock(JobRegistry.class); | ||
TaskletStep step = mock(TaskletStep.class); | ||
|
||
when(step.getTasklet()).thenReturn(tasklet); | ||
when(step.getName()).thenReturn("test_job.step1"); | ||
when(jobRegistry.getJob(anyString())).thenReturn(job); | ||
when(jobExplorer.getJobExecution(111L)).thenReturn(jobExecution); | ||
|
||
jobOperator.setJobRegistry(jobRegistry); | ||
jobExplorer.getJobExecution(111L); | ||
jobRepository.update(jobExecution); | ||
jobOperator.stop(111L); | ||
assertEquals(BatchStatus.STOPPING, jobExecution.getStatus()); | ||
} | ||
|
||
@Test | ||
public void testAbort() throws Exception { | ||
JobInstance jobInstance = new JobInstance(123L, job.getName()); | ||
|
@@ -370,4 +442,27 @@ public void testAbortNonStopping() throws Exception { | |
jobRepository.update(jobExecution); | ||
jobOperator.abandon(123L); | ||
} | ||
|
||
|
||
class MockJob extends AbstractJob { | ||
|
||
private TaskletStep taskletStep; | ||
|
||
@Override | ||
public Step getStep(String stepName) { | ||
return taskletStep; | ||
} | ||
|
||
@Override | ||
public Collection<String> getStepNames() { | ||
return Arrays.asList("test_job.step1"); | ||
} | ||
|
||
@Override | ||
protected void doExecute(JobExecution execution) throws JobExecutionException { | ||
|
||
} | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -216,6 +216,24 @@ public void testWorkingDirectory() throws Exception { | |
// no error expected now | ||
tasklet.setWorkingDirectory(directory.getCanonicalPath()); | ||
} | ||
|
||
/* | ||
* test stopping a tasklet | ||
*/ | ||
@Test | ||
public void testStopped() throws Exception { | ||
String command = "sleep 5"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple things here:
|
||
tasklet.setCommand(command); | ||
tasklet.setTerminationCheckInterval(10); | ||
tasklet.afterPropertiesSet(); | ||
|
||
StepContribution contribution = stepExecution.createStepContribution(); | ||
//send stop | ||
tasklet.stop(); | ||
tasklet.execute(contribution, null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is stop called before execute? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only to support the test in the sequencing |
||
|
||
assertEquals(contribution.getExitStatus().getExitCode(),ExitStatus.STOPPED.getExitCode()); | ||
} | ||
|
||
/** | ||
* Exit code mapper containing mapping logic expected by the tests. 0 means | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this one. At the very least, we would need to add in the documentation that the underlying command will not be interrupted unless interuptOnCancel has been set to true. Even with that though, we can't guarantee the state of things once that has occurred.