diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/SimpleMethodInvoker.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/SimpleMethodInvoker.java
index e23766710..83432d3ed 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/SimpleMethodInvoker.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/SimpleMethodInvoker.java
@@ -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;
}
}
diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java b/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java
index edee01cf6..4083e0553 100644
--- a/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java
+++ b/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java
@@ -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);
}
}
diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/StepRunner.java b/spring-batch-test/src/main/java/org/springframework/batch/test/StepRunner.java
index 3e239c1c8..47ee22b28 100755
--- a/spring-batch-test/src/main/java/org/springframework/batch/test/StepRunner.java
+++ b/spring-batch-test/src/main/java/org/springframework/batch/test/StepRunner.java
@@ -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.
*
*
- * - launchStep(Step step): Launch the step with new parameters each time. (The current system
- * time will be used)
- *
- launchStep(Step step, JobParameters jobParameters): Launch the specified step with the provided
- * JobParameters. This may be useful if your step requires a certain parameter during runtime.
+ *
- launchStep(Step step): Launch the step with new parameters each
+ * time. (The current system time will be used)
+ *
- launchStep(Step step, JobParameters jobParameters): Launch the
+ * specified step with the provided JobParameters. This may be useful if your
+ * step requires a certain parameter during runtime.
*
*
- * 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 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 parameters = new HashMap();
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/AbstractSampleJobTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/AbstractSampleJobTests.java
index b7f97e8ee..6200adcf5 100644
--- a/spring-batch-test/src/test/java/org/springframework/batch/test/AbstractSampleJobTests.java
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/AbstractSampleJobTests.java
@@ -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 + "'"));
}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java
index bc4f37414..440147a22 100755
--- a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java
@@ -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;
}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/sample/SampleTasklet.java b/spring-batch-test/src/test/java/org/springframework/batch/test/sample/SampleTasklet.java
index e155cbea3..b973d566f 100755
--- a/spring-batch-test/src/test/java/org/springframework/batch/test/sample/SampleTasklet.java
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/sample/SampleTasklet.java
@@ -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();
+ }
}
diff --git a/spring-batch-test/src/test/resources/jobs/sample-steps.xml b/spring-batch-test/src/test/resources/jobs/sample-steps.xml
index 67460d000..f0939b0f0 100644
--- a/spring-batch-test/src/test/resources/jobs/sample-steps.xml
+++ b/spring-batch-test/src/test/resources/jobs/sample-steps.xml
@@ -1,26 +1,36 @@
+ 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">
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml b/spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml
index ced2f3512..425156109 100644
--- a/spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml
+++ b/spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml
@@ -12,25 +12,11 @@
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml b/spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml
index 613251c65..cd07407a8 100644
--- a/spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml
+++ b/spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml
@@ -12,8 +12,8 @@
-
-
+
+