diff --git a/spring-batch-samples/src/main/resources/jobs/iosample/delimited.xml b/spring-batch-samples/src/main/resources/jobs/iosample/delimited.xml index 990bb0d6e..3027fec6b 100644 --- a/spring-batch-samples/src/main/resources/jobs/iosample/delimited.xml +++ b/spring-batch-samples/src/main/resources/jobs/iosample/delimited.xml @@ -11,7 +11,7 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> - + @@ -27,14 +27,11 @@ + - - - - - - + + diff --git a/spring-batch-samples/src/main/resources/jobs/iosample/fixedLength.xml b/spring-batch-samples/src/main/resources/jobs/iosample/fixedLength.xml index dc919e833..9253f7e70 100644 --- a/spring-batch-samples/src/main/resources/jobs/iosample/fixedLength.xml +++ b/spring-batch-samples/src/main/resources/jobs/iosample/fixedLength.xml @@ -9,8 +9,8 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> - - + + @@ -31,8 +31,8 @@ - - + + @@ -46,9 +46,5 @@ - - - - diff --git a/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml b/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml index 611917d9d..4ecff3485 100644 --- a/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml +++ b/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml @@ -1,7 +1,6 @@ - - - - - - - - - - - + - + - + - + - + @@ -47,15 +32,12 @@ - + - + - + diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/AbstractIoSampleTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/AbstractIoSampleTests.java index b1ef02e80..09239912b 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/AbstractIoSampleTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/AbstractIoSampleTests.java @@ -10,12 +10,14 @@ import org.junit.Test; import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.StepExecution; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemStream; import org.springframework.batch.sample.domain.trade.CustomerCredit; import org.springframework.batch.sample.domain.trade.internal.CustomerCreditIncreaseProcessor; import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.batch.test.MetaDataInstanceFactory; import org.springframework.batch.test.StepScopeTestExecutionListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; @@ -29,8 +31,9 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution * * @author Robert Kasanicky */ -@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/job-runner-context.xml", "/jobs/ioSampleJob.xml" }) -@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, StepScopeTestExecutionListener.class}) +@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/job-runner-context.xml", + "/jobs/ioSampleJob.xml" }) +@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, StepScopeTestExecutionListener.class }) public abstract class AbstractIoSampleTests { @Autowired @@ -111,4 +114,14 @@ public abstract class AbstractIoSampleTests { } } + /** + * Create a {@link StepExecution} that can be used to satisfy step scoped + * dependencies in the test itself (not in the job it launches). + * + * @return a {@link StepExecution} + */ + protected StepExecution getStepExecution() { + return MetaDataInstanceFactory.createStepExecution(getUniqueJobParameters()); + } + } diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/DelimitedFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/DelimitedFunctionalTests.java index 460b18894..489603268 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/DelimitedFunctionalTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/DelimitedFunctionalTests.java @@ -16,37 +16,40 @@ package org.springframework.batch.sample.iosample; -import java.util.Collections; -import java.util.Map; - import org.junit.runner.RunWith; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.scope.context.StepSynchronizationManager; import org.springframework.batch.item.ItemReader; import org.springframework.batch.sample.domain.trade.CustomerCredit; +import org.springframework.batch.test.MetaDataInstanceFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Dan Garrette + * @author Dave Syer * @since 2.0 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "/jobs/iosample/delimited.xml") public class DelimitedFunctionalTests extends AbstractIoSampleTests { - @SuppressWarnings("unused") - private Map jobParameters = Collections.singletonMap("fileName", - "file:./target/test-outputs/delimitedOutput.csv"); - @Override protected void pointReaderToOutput(ItemReader reader) { + JobParameters jobParameters = new JobParametersBuilder(super.getUniqueJobParameters()).addString("inputFile", + "file:./target/test-outputs/delimitedOutput.csv").toJobParameters(); + StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(jobParameters); + StepSynchronizationManager.close(); + StepSynchronizationManager.register(stepExecution); } @Override protected JobParameters getUniqueJobParameters() { - return new JobParametersBuilder(super.getUniqueJobParameters()).addString("fileName", - "data/iosample/input/delimited.csv").toJobParameters(); + return new JobParametersBuilder(super.getUniqueJobParameters()).addString("inputFile", + "data/iosample/input/delimited.csv").addString("outputFile", + "file:./target/test-outputs/delimitedOutput.csv").toJobParameters(); } } \ No newline at end of file diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/FixedLengthFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/FixedLengthFunctionalTests.java index a70d54865..375554822 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/FixedLengthFunctionalTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/FixedLengthFunctionalTests.java @@ -17,11 +17,13 @@ package org.springframework.batch.sample.iosample; import org.junit.runner.RunWith; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.scope.context.StepSynchronizationManager; import org.springframework.batch.item.ItemReader; -import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.sample.domain.trade.CustomerCredit; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; +import org.springframework.batch.test.MetaDataInstanceFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -29,12 +31,20 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration(locations = "/jobs/iosample/fixedLength.xml") public class FixedLengthFunctionalTests extends AbstractIoSampleTests { - @Autowired - private Resource outputResource; - @Override protected void pointReaderToOutput(ItemReader reader) { - FlatFileItemReader fileReader = (FlatFileItemReader) reader; - fileReader.setResource(outputResource); + JobParameters jobParameters = new JobParametersBuilder(super.getUniqueJobParameters()).addString("inputFile", + "file:./target/test-outputs/fixedLengthOutput.txt").toJobParameters(); + StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(jobParameters); + StepSynchronizationManager.close(); + StepSynchronizationManager.register(stepExecution); } + + @Override + protected JobParameters getUniqueJobParameters() { + return new JobParametersBuilder(super.getUniqueJobParameters()).addString("inputFile", + "data/iosample/input/fixedLength.txt").addString("outputFile", + "file:./target/test-outputs/fixedLengthOutput.txt").toJobParameters(); + } + } diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/JdbcPagingFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/JdbcPagingFunctionalTests.java index 4d3da92e8..57d57ae6e 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/JdbcPagingFunctionalTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/JdbcPagingFunctionalTests.java @@ -19,13 +19,17 @@ package org.springframework.batch.sample.iosample; import org.junit.runner.RunWith; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.scope.context.StepSynchronizationManager; import org.springframework.batch.item.ItemReader; import org.springframework.batch.sample.domain.trade.CustomerCredit; +import org.springframework.batch.test.MetaDataInstanceFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Dan Garrette + * @author Dave Syer * @since 2.0 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -34,9 +38,13 @@ public class JdbcPagingFunctionalTests extends AbstractIoSampleTests { @Override protected void pointReaderToOutput(ItemReader reader) { - // no-op + JobParameters jobParameters = new JobParametersBuilder(super.getUniqueJobParameters()).addDouble("credit", 0.) + .toJobParameters(); + StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(jobParameters); + StepSynchronizationManager.close(); + StepSynchronizationManager.register(stepExecution); } - + @Override protected JobParameters getUniqueJobParameters() { return new JobParametersBuilder(super.getUniqueJobParameters()).addDouble("credit", 10000.).toJobParameters(); diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesDelimitedFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesDelimitedFunctionalTests.java index bbc3d5d3a..480c75a84 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesDelimitedFunctionalTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesDelimitedFunctionalTests.java @@ -19,20 +19,25 @@ package org.springframework.batch.sample.iosample; import static org.junit.Assert.assertEquals; import java.util.Date; +import java.util.concurrent.Callable; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.core.job.AbstractJob; +import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemStream; import org.springframework.batch.sample.domain.trade.CustomerCredit; +import org.springframework.batch.test.MetaDataInstanceFactory; +import org.springframework.batch.test.StepScopeTestUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -49,13 +54,14 @@ public class TwoJobInstancesDelimitedFunctionalTests { private JobLauncher launcher; @Autowired - private AbstractJob job; + private Job job; @Autowired - private FlatFileItemReader reader; + private ItemReader reader; @Autowired - private Resource outputResource; + @Qualifier("itemReader") + private ItemStream readerStream; @Test public void testLaunchJobTwice() throws Exception { @@ -69,18 +75,31 @@ public class TwoJobInstancesDelimitedFunctionalTests { private void verifyOutput(int expected) throws Exception { - reader.setResource(outputResource); - reader.open(new ExecutionContext()); + JobParameters jobParameters = new JobParametersBuilder().addString("fileName", + "file:./target/test-outputs/delimitedOutput.csv").toJobParameters(); + StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(jobParameters); + + int count = StepScopeTestUtils.doInStepScope(stepExecution, new Callable() { + + public Integer call() throws Exception { + + int count = 0; + + readerStream.open(new ExecutionContext()); + + try { + while (reader.read() != null) { + count++; + } + } + finally { + readerStream.close(); + } + return count; - int count = 0; - try { - while (reader.read() != null) { - count++; } - } - finally { - reader.close(); - } + + }); assertEquals(expected, count); diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesPagingFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesPagingFunctionalTests.java index 42baeb3b8..2b7f65f99 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesPagingFunctionalTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/TwoJobInstancesPagingFunctionalTests.java @@ -26,10 +26,10 @@ import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.core.job.AbstractJob; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; @@ -49,7 +49,7 @@ public class TwoJobInstancesPagingFunctionalTests { private JobLauncher launcher; @Autowired - private AbstractJob job; + private Job job; private SimpleJdbcTemplate jdbcTemplate; diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/MetaDataInstanceFactory.java b/spring-batch-test/src/main/java/org/springframework/batch/test/MetaDataInstanceFactory.java index 8ae631672..68ee1551d 100644 --- a/spring-batch-test/src/main/java/org/springframework/batch/test/MetaDataInstanceFactory.java +++ b/spring-batch-test/src/main/java/org/springframework/batch/test/MetaDataInstanceFactory.java @@ -22,6 +22,7 @@ import org.springframework.batch.core.JobInstance; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.converter.DefaultJobParametersConverter; +import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.support.PropertiesConverter; /** @@ -33,6 +34,31 @@ import org.springframework.batch.support.PropertiesConverter; */ public class MetaDataInstanceFactory { + /** + * The default name for a job ("job") + */ + public static final String DEFAULT_JOB_NAME = "job"; + + /** + * The default id for a job instance (12L) + */ + public static final long DEFAULT_JOB_INSTANCE_ID = 12L; + + /** + * The default id for a job execution (123L) + */ + public static final long DEFAULT_JOB_EXECUTION_ID = 123L; + + /** + * The default name for a step ("step") + */ + public static final String DEFAULT_STEP_NAME = "step"; + + /** + * The default id for a step execution (1234L) + */ + public static final long DEFAULT_STEP_EXECUTION_ID = 1234L; + /** * Create a {@link JobInstance} with the parameters provided. * @@ -73,20 +99,20 @@ public class MetaDataInstanceFactory { /** * Create a {@link JobInstance} with default parameters. * - * @return a {@link JobInstance} with name="job", id=12L and empty - * parameters + * @return a {@link JobInstance} with name=DEFAULT_JOB_NAME, + * id=DEFAULT_JOB_INSTANCE_ID and empty parameters */ public static JobInstance createJobInstance() { - return new JobInstance(12L, new JobParameters(), "job"); + return new JobInstance(DEFAULT_JOB_INSTANCE_ID, new JobParameters(), DEFAULT_JOB_NAME); } /** * Create a {@link JobExecution} with default parameters. * - * @return a {@link JobExecution} with id=123L + * @return a {@link JobExecution} with id=DEFAULT_JOB_EXECUTION_ID */ public static JobExecution createJobExecution() { - return createJobExecution(123L); + return createJobExecution(DEFAULT_JOB_EXECUTION_ID); } /** @@ -96,7 +122,7 @@ public class MetaDataInstanceFactory { * @return a {@link JobExecution} with valid {@link JobInstance} */ public static JobExecution createJobExecution(Long executionId) { - return createJobExecution("job", 12L, executionId); + return createJobExecution(DEFAULT_JOB_NAME, DEFAULT_JOB_INSTANCE_ID, executionId); } /** @@ -144,10 +170,11 @@ public class MetaDataInstanceFactory { /** * Create a {@link StepExecution} with default parameters. * - * @return a {@link StepExecution} with stepName="step" and id=1234L + * @return a {@link StepExecution} with stepName="step" and + * id=DEFAULT_STEP_EXECUTION_ID */ public static StepExecution createStepExecution() { - return createStepExecution("step", 1234L); + return createStepExecution(DEFAULT_STEP_NAME, DEFAULT_STEP_EXECUTION_ID); } /** @@ -185,8 +212,8 @@ public class MetaDataInstanceFactory { * with a unique id */ public static JobExecution createJobExecutionWithStepExecutions(Long executionId, Collection stepNames) { - JobExecution jobExecution = createJobExecution("job", 12L, executionId); - Long stepExecutionId = 1234L; + JobExecution jobExecution = createJobExecution(DEFAULT_JOB_NAME, DEFAULT_JOB_INSTANCE_ID, executionId); + Long stepExecutionId = DEFAULT_STEP_EXECUTION_ID; for (String stepName : stepNames) { createStepExecution(jobExecution, stepName, stepExecutionId); stepExecutionId++; @@ -194,4 +221,46 @@ public class MetaDataInstanceFactory { return jobExecution; } + /** + * Create a {@link StepExecution} and all its parent entities with default + * values, but using the {@link ExecutionContext} and {@link JobParameters} + * provided. + * + * @param jobParameters come {@link JobParameters} + * @param executionContext some {@link ExecutionContext} + * + * @return a {@link StepExecution} with the execution context provided + */ + public static StepExecution createStepExecution(JobParameters jobParameters, ExecutionContext executionContext) { + StepExecution stepExecution = createStepExecution(jobParameters); + stepExecution.setExecutionContext(executionContext); + return stepExecution; + } + + /** + * Create a {@link StepExecution} and all its parent entities with default + * values, but using the {@link JobParameters} provided. + * + * @param jobParameters some {@link JobParameters} + * @return a {@link StepExecution} with the job parameters provided + */ + public static StepExecution createStepExecution(JobParameters jobParameters) { + JobExecution jobExecution = createJobExecution(DEFAULT_JOB_NAME, DEFAULT_JOB_INSTANCE_ID, + DEFAULT_JOB_EXECUTION_ID, jobParameters); + return jobExecution.createStepExecution(DEFAULT_STEP_NAME); + } + + /** + * Create a {@link StepExecution} and all its parent entities with default + * values, but using the {@link ExecutionContext} provided. + * + * @param executionContext some {@link ExecutionContext} + * @return a {@link StepExecution} with the execution context provided + */ + public static StepExecution createStepExecution(ExecutionContext executionContext) { + StepExecution stepExecution = createStepExecution(); + stepExecution.setExecutionContext(executionContext); + return stepExecution; + } + } diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/StepScopeTestUtils.java b/spring-batch-test/src/main/java/org/springframework/batch/test/StepScopeTestUtils.java new file mode 100644 index 000000000..5232bfebe --- /dev/null +++ b/spring-batch-test/src/main/java/org/springframework/batch/test/StepScopeTestUtils.java @@ -0,0 +1,45 @@ +/* + * Copyright 2006-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.test; + +import java.util.concurrent.Callable; + +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.scope.StepScope; +import org.springframework.batch.core.scope.context.StepSynchronizationManager; + +/** + * Utility class for creating and manipulating {@link StepScope} in unit tests. + * This is useful when you want to use the Spring test support and inject + * dependencies into your test case that happen to be step scoped in the + * application context. + * + * @author Dave Syer + * + */ +public class StepScopeTestUtils { + + public static T doInStepScope(StepExecution stepExecution, Callable callable) throws Exception { + try { + StepSynchronizationManager.register(stepExecution); + return callable.call(); + } + finally { + StepSynchronizationManager.close(); + } + } + +}