BATCH-1295:
*Provide ability to pass job's ExecutionContext into AbstractJobTests.launchStep() and StepRunner.launchStep() *Modified SimpleMethodInvoker.hashCode() so that it is not dependent on commons-lang
This commit is contained in:
@@ -34,7 +34,6 @@ package org.springframework.batch.support;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -145,6 +144,9 @@ public class SimpleMethodInvoker implements MethodInvoker {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(25, 37).append(object.hashCode()).append(method.hashCode()).toHashCode();
|
||||
int result = 25;
|
||||
result = 31 * result + object.hashCode();
|
||||
result = 31 * result + method.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.batch.core.job.SimpleJob;
|
||||
import org.springframework.batch.core.job.flow.FlowJob;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -81,14 +82,14 @@ public abstract class AbstractJobTests implements ApplicationContextAware {
|
||||
private StepRunner stepRunner;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the applicationContext
|
||||
*/
|
||||
@@ -162,27 +163,58 @@ public abstract class AbstractJobTests implements ApplicationContextAware {
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step in the job. An IllegalStateException is
|
||||
* thrown if there is no Step with the given name.
|
||||
* Launch just the specified step in the job. A unique set of JobParameters
|
||||
* will automatically be generated. An IllegalStateException is thrown if
|
||||
* there is no Step with the given name.
|
||||
*
|
||||
* @param stepName
|
||||
* @param stepName The name of the step to launch
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(String stepName) {
|
||||
return this.launchStep(stepName, this.getUniqueJobParameters(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step in the job. A unique set of JobParameters
|
||||
* will automatically be generated. An IllegalStateException is thrown if
|
||||
* there is no Step with the given name.
|
||||
*
|
||||
* @param stepName The name of the step to launch
|
||||
* @param jobExecutionContext An ExecutionContext whose values will be
|
||||
* loaded into the Job ExecutionContext prior to launching the step.
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(String stepName, ExecutionContext jobExecutionContext) {
|
||||
return this.launchStep(stepName, this.getUniqueJobParameters(), jobExecutionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step in the job. An IllegalStateException is
|
||||
* thrown if there is no Step with the given name.
|
||||
*
|
||||
* @param stepName The name of the step to launch
|
||||
* @param jobParameters The JobParameters to use during the launch
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(String stepName, JobParameters jobParameters) {
|
||||
return this.launchStep(stepName, jobParameters, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step in the job. An IllegalStateException is
|
||||
* thrown if there is no Step with the given name.
|
||||
*
|
||||
* @param stepName The name of the step to launch
|
||||
* @param jobParameters The JobParameters to use during the launch
|
||||
* @param jobExecutionContext An ExecutionContext whose values will be
|
||||
* loaded into the Job ExecutionContext prior to launching the step.
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(String stepName, JobParameters jobParameters, ExecutionContext jobExecutionContext) {
|
||||
Step step = this.job.getStep(stepName);
|
||||
if (step == null) {
|
||||
throw new IllegalStateException("No Step found with name: [" + stepName + "]");
|
||||
}
|
||||
return getStepRunner().launchStep(step);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step in the job.
|
||||
*
|
||||
* @param stepName
|
||||
* @param jobParameters
|
||||
*/
|
||||
public JobExecution launchStep(String stepName, JobParameters jobParameters) {
|
||||
return getStepRunner().launchStep(this.job.getStep(stepName), jobParameters);
|
||||
return getStepRunner().launchStep(step, jobParameters, jobExecutionContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,66 +26,105 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionListener;
|
||||
import org.springframework.batch.core.JobParameter;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.SimpleJob;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.listener.JobExecutionListenerSupport;
|
||||
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
|
||||
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.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Utility class for executing steps outside of a {@link Job}. This is useful
|
||||
* in end to end testing in order to allow for the testing of a step individually
|
||||
* Utility class for executing steps outside of a {@link Job}. This is useful in
|
||||
* end to end testing in order to allow for the testing of a step individually
|
||||
* without running every Step in a job.
|
||||
*
|
||||
* <ul>
|
||||
* <li><b>launchStep(Step step)</b>: Launch the step with new parameters each time. (The current system
|
||||
* time will be used)
|
||||
* <li><b>launchStep(Step step, JobParameters jobParameters)</b>: Launch the specified step with the provided
|
||||
* JobParameters. This may be useful if your step requires a certain parameter during runtime.
|
||||
* <li><b>launchStep(Step step)</b>: Launch the step with new parameters each
|
||||
* time. (The current system time will be used)
|
||||
* <li><b>launchStep(Step step, JobParameters jobParameters)</b>: Launch the
|
||||
* specified step with the provided JobParameters. This may be useful if your
|
||||
* step requires a certain parameter during runtime.
|
||||
* </ul>
|
||||
*
|
||||
* It should be noted that any checked exceptions encountered while running the Step will wrapped with
|
||||
* RuntimeException. Any checked exception thrown will be due to a framework error, not the logic of the
|
||||
* step, and thus requiring a throws declaration in clients of this class is unnecessary.
|
||||
* It should be noted that any checked exceptions encountered while running the
|
||||
* Step will wrapped with RuntimeException. Any checked exception thrown will be
|
||||
* due to a framework error, not the logic of the step, and thus requiring a
|
||||
* throws declaration in clients of this class is unnecessary.
|
||||
*
|
||||
* @author Dan Garrette
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
* @see SimpleJob
|
||||
*/
|
||||
public class StepRunner{
|
||||
public class StepRunner {
|
||||
|
||||
/** Logger */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private JobLauncher launcher;
|
||||
|
||||
private JobRepository jobRepository;
|
||||
|
||||
public StepRunner(JobLauncher launcher, JobRepository jobRepository) {
|
||||
this.launcher = launcher;
|
||||
this.jobRepository = jobRepository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Launcher
|
||||
* Launch just the specified step as its own job. A unique set of
|
||||
* JobParameters will automatically be generated. An IllegalStateException
|
||||
* is thrown if there is no Step with the given name.
|
||||
*
|
||||
* @param stepName
|
||||
* @param stepName The name of the step to launch
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(Step step) {
|
||||
return this.launchStep(step, this.makeUniqueJobParameters());
|
||||
return this.launchStep(step, this.makeUniqueJobParameters(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step in the job.
|
||||
* Launch just the specified step as its own job. A unique set of
|
||||
* JobParameters will automatically be generated. An IllegalStateException
|
||||
* is thrown if there is no Step with the given name.
|
||||
*
|
||||
* @param stepName
|
||||
* @param jobParameters
|
||||
* @param stepName The name of the step to launch
|
||||
* @param jobExecutionContext An ExecutionContext whose values will be
|
||||
* loaded into the Job ExecutionContext prior to launching the step.
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(Step step, ExecutionContext jobExecutionContext) {
|
||||
return this.launchStep(step, this.makeUniqueJobParameters(), jobExecutionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step as its own job. An IllegalStateException
|
||||
* is thrown if there is no Step with the given name.
|
||||
*
|
||||
* @param stepName The name of the step to launch
|
||||
* @param jobParameters The JobParameters to use during the launch
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(Step step, JobParameters jobParameters) {
|
||||
return this.launchStep(step, jobParameters, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch just the specified step as its own job. An IllegalStateException
|
||||
* is thrown if there is no Step with the given name.
|
||||
*
|
||||
* @param stepName The name of the step to launch
|
||||
* @param jobParameters The JobParameters to use during the launch
|
||||
* @param jobExecutionContext An ExecutionContext whose values will be
|
||||
* loaded into the Job ExecutionContext prior to launching the step.
|
||||
* @return JobExecution
|
||||
*/
|
||||
public JobExecution launchStep(Step step, JobParameters jobParameters, final ExecutionContext jobExecutionContext) {
|
||||
//
|
||||
// Create a fake job
|
||||
//
|
||||
@@ -97,6 +136,20 @@ public class StepRunner{
|
||||
stepsToExecute.add(step);
|
||||
job.setSteps(stepsToExecute);
|
||||
|
||||
//
|
||||
// Dump the given Job ExecutionContext using a listener
|
||||
//
|
||||
if (jobExecutionContext != null && !jobExecutionContext.isEmpty()) {
|
||||
job.setJobExecutionListeners(new JobExecutionListener[] { new JobExecutionListenerSupport() {
|
||||
public void beforeJob(JobExecution jobExecution) {
|
||||
ExecutionContext jobContext = jobExecution.getExecutionContext();
|
||||
for (Map.Entry<String, Object> entry : jobExecutionContext.entrySet()) {
|
||||
jobContext.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
} });
|
||||
}
|
||||
|
||||
//
|
||||
// Launch the job
|
||||
//
|
||||
@@ -112,18 +165,21 @@ public class StepRunner{
|
||||
private JobExecution launchJob(Job job, JobParameters jobParameters) {
|
||||
try {
|
||||
return this.launcher.run(job, jobParameters);
|
||||
} catch (JobExecutionAlreadyRunningException e) {
|
||||
}
|
||||
catch (JobExecutionAlreadyRunningException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (JobRestartException e) {
|
||||
}
|
||||
catch (JobRestartException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (JobInstanceAlreadyCompleteException e) {
|
||||
}
|
||||
catch (JobInstanceAlreadyCompleteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new JobParameters object containing only a parameter for the
|
||||
* current timestamp, to ensure that the job instance will be unique
|
||||
* current timestamp, to ensure that the job instance will be unique
|
||||
*/
|
||||
private JobParameters makeUniqueJobParameters() {
|
||||
Map<String, JobParameter> parameters = new HashMap<String, JobParameter>();
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package org.springframework.batch.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.test.sample.SampleTasklet;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
|
||||
/**
|
||||
@@ -20,6 +24,10 @@ public abstract class AbstractSampleJobTests extends AbstractJobTests {
|
||||
|
||||
private SimpleJdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("tasklet2")
|
||||
private SampleTasklet tasklet2;
|
||||
|
||||
@Autowired
|
||||
public void setJdbcTemplate(SimpleJdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
@@ -28,6 +36,7 @@ public abstract class AbstractSampleJobTests extends AbstractJobTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))");
|
||||
tasklet2.jobContextEntryFound = false;
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -59,6 +68,15 @@ public abstract class AbstractSampleJobTests extends AbstractJobTests {
|
||||
this.verifyTasklet(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStepLaunchJobContextEntry() {
|
||||
ExecutionContext jobContext = new ExecutionContext();
|
||||
jobContext.put("key1", "value1");
|
||||
assertEquals(BatchStatus.COMPLETED, this.launchStep("step2", jobContext).getStatus());
|
||||
this.verifyTasklet(2);
|
||||
assertTrue(tasklet2.jobContextEntryFound);
|
||||
}
|
||||
|
||||
private void verifyTasklet(int id) {
|
||||
assertEquals(id, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet" + id + "'"));
|
||||
}
|
||||
|
||||
@@ -20,20 +20,21 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sample-steps.xml" })
|
||||
public class SampleStepTests implements ApplicationContextAware{
|
||||
public class SampleStepTests implements ApplicationContextAware {
|
||||
|
||||
@Autowired
|
||||
private SimpleJdbcTemplate jdbcTemplate;
|
||||
|
||||
|
||||
private StepRunner stepRunner;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))");
|
||||
@@ -47,13 +48,12 @@ public class SampleStepTests implements ApplicationContextAware{
|
||||
|
||||
@Test
|
||||
public void testTasklet() {
|
||||
Step step = (Step)context.getBean("step2");
|
||||
Step step = (Step) context.getBean("s2");
|
||||
assertEquals(BatchStatus.COMPLETED, stepRunner.launchStep(step).getStatus());
|
||||
assertEquals(2, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet2'"));
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package org.springframework.batch.test.sample;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.annotation.BeforeStep;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
@@ -12,14 +16,31 @@ public class SampleTasklet implements Tasklet {
|
||||
@Autowired
|
||||
private SimpleJdbcTemplate jdbcTemplate;
|
||||
|
||||
private JobExecution jobExecution;
|
||||
|
||||
private int id = 0;
|
||||
|
||||
public boolean jobContextEntryFound = false;
|
||||
|
||||
public SampleTasklet(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
this.jdbcTemplate.update("insert into TESTS(ID, NAME) values (?, 'SampleTasklet" + id + "')", id);
|
||||
|
||||
if (jobExecution != null) {
|
||||
ExecutionContext jobContext = jobExecution.getExecutionContext();
|
||||
if (jobContext.containsKey("key1")) {
|
||||
jobContextEntryFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
@BeforeStep
|
||||
public void storeJobExecution(StepExecution stepExecution) {
|
||||
this.jobExecution = stepExecution.getJobExecution();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:batch="http://www.springframework.org/schema/batch"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
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">
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
|
||||
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.0.xsd">
|
||||
|
||||
<bean id="step1" parent="taskletStep">
|
||||
<property name="tasklet">
|
||||
<bean class="org.springframework.batch.test.sample.SampleTasklet">
|
||||
<constructor-arg value="1" />
|
||||
</bean>
|
||||
</property>
|
||||
<batch:step id="s1" parent="taskletStep">
|
||||
<batch:tasklet ref="tasklet1">
|
||||
<batch:listeners>
|
||||
<batch:listener ref="tasklet1"/>
|
||||
</batch:listeners>
|
||||
</batch:tasklet>
|
||||
</batch:step>
|
||||
|
||||
<bean id="tasklet1" class="org.springframework.batch.test.sample.SampleTasklet">
|
||||
<constructor-arg value="1" />
|
||||
</bean>
|
||||
|
||||
<bean id="step2" parent="taskletStep">
|
||||
<property name="tasklet">
|
||||
<bean class="org.springframework.batch.test.sample.SampleTasklet">
|
||||
<constructor-arg value="2" />
|
||||
</bean>
|
||||
</property>
|
||||
<batch:step id="s2" parent="taskletStep">
|
||||
<batch:tasklet ref="tasklet2">
|
||||
<batch:listeners>
|
||||
<batch:listener ref="tasklet2"/>
|
||||
</batch:listeners>
|
||||
</batch:tasklet>
|
||||
</batch:step>
|
||||
|
||||
<bean id="tasklet2" class="org.springframework.batch.test.sample.SampleTasklet">
|
||||
<constructor-arg value="2" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -12,25 +12,11 @@
|
||||
http://www.springframework.org/schema/aop
|
||||
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
|
||||
|
||||
<beans:import resource="sample-steps.xml" />
|
||||
|
||||
<job id="sampleFlowJob">
|
||||
<step id="step1" parent="s1" next="step2"/>
|
||||
<step id="step2" parent="s2"/>
|
||||
</job>
|
||||
|
||||
<beans:bean id="s1" parent="taskletStep">
|
||||
<beans:property name="tasklet">
|
||||
<beans:bean class="org.springframework.batch.test.sample.SampleTasklet">
|
||||
<beans:constructor-arg value="1" />
|
||||
</beans:bean>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="s2" parent="taskletStep">
|
||||
<beans:property name="tasklet">
|
||||
<beans:bean class="org.springframework.batch.test.sample.SampleTasklet">
|
||||
<beans:constructor-arg value="2" />
|
||||
</beans:bean>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
@@ -12,8 +12,8 @@
|
||||
<bean id="sampleJob" parent="simpleJob">
|
||||
<property name="steps">
|
||||
<list>
|
||||
<ref bean="step1" />
|
||||
<ref bean="step2" />
|
||||
<bean id="step1" parent="s1"/>
|
||||
<bean id="step2" parent="s2"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
Reference in New Issue
Block a user