diff --git a/spring-batch-samples/README.md b/spring-batch-samples/README.md index 42b71c0c24..a645afa3a2 100644 --- a/spring-batch-samples/README.md +++ b/spring-batch-samples/README.md @@ -183,6 +183,13 @@ streaming and Spring OXM marshallers and unmarshallers. [XML Input Output](./src/main/java/org/springframework/batch/sample/file/xml/README.md) +### MultiResource Input Output Job + +This sample shows how to use the `MultiResourceItemReader` and `MultiResourceItemWriter` +to read and write multiple files in the same step. + +[MultiResource Input Output Job Sample](./src/main/java/org/springframework/batch/sample/file/multiresource/README.md) + ### Football Job This is a (American) Football statistics loading job. It loads two files containing players and games diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/multiresource/MultiResourceJobConfiguration.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/multiresource/MultiResourceJobConfiguration.java new file mode 100644 index 0000000000..9b9513702f --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/multiresource/MultiResourceJobConfiguration.java @@ -0,0 +1,120 @@ +/* + * Copyright 2023 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 + * + * https://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.sample.file.multiresource; + +import javax.sql.DataSource; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.file.FlatFileItemReader; +import org.springframework.batch.item.file.FlatFileItemWriter; +import org.springframework.batch.item.file.MultiResourceItemReader; +import org.springframework.batch.item.file.MultiResourceItemWriter; +import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; +import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder; +import org.springframework.batch.item.file.builder.MultiResourceItemReaderBuilder; +import org.springframework.batch.item.file.builder.MultiResourceItemWriterBuilder; +import org.springframework.batch.sample.domain.trade.CustomerCredit; +import org.springframework.batch.sample.domain.trade.internal.CustomerCreditIncreaseProcessor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.core.io.WritableResource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.jdbc.support.JdbcTransactionManager; + +/** + * @author Mahmoud Ben Hassine + */ +@Configuration +@EnableBatchProcessing +public class MultiResourceJobConfiguration { + + @Bean + @StepScope + public MultiResourceItemReader itemReader( + @Value("#{jobParameters[inputFiles]}") Resource[] resources) { + return new MultiResourceItemReaderBuilder().name("itemReader") + .resources(resources) + .delegate(delegateReader()) + .build(); + } + + @Bean + public FlatFileItemReader delegateReader() { + return new FlatFileItemReaderBuilder().name("delegateItemReader") + .delimited() + .names("name", "credit") + .targetType(CustomerCredit.class) + .build(); + } + + @Bean + @StepScope + public MultiResourceItemWriter itemWriter( + @Value("#{jobParameters[outputFiles]}") WritableResource resource) { + return new MultiResourceItemWriterBuilder().name("itemWriter") + .delegate(delegateWriter()) + .resource(resource) + .itemCountLimitPerResource(6) + .build(); + } + + @Bean + public FlatFileItemWriter delegateWriter() { + return new FlatFileItemWriterBuilder().name("delegateItemWriter") + .delimited() + .names("name", "credit") + .build(); + } + + @Bean + public Job job(JobRepository jobRepository, JdbcTransactionManager transactionManager, + ItemReader itemReader, ItemWriter itemWriter) { + return new JobBuilder("ioSampleJob", jobRepository) + .start(new StepBuilder("step1", jobRepository).chunk(2, transactionManager) + .reader(itemReader) + .processor(new CustomerCreditIncreaseProcessor()) + .writer(itemWriter) + .build()) + .build(); + } + + // Infrastructure beans + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL) + .addScript("/org/springframework/batch/core/schema-drop-hsqldb.sql") + .addScript("/org/springframework/batch/core/schema-hsqldb.sql") + .generateUniqueName(true) + .build(); + } + + @Bean + public JdbcTransactionManager transactionManager(DataSource dataSource) { + return new JdbcTransactionManager(dataSource); + } + +} diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/multiresource/README.md b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/multiresource/README.md new file mode 100644 index 0000000000..050991e8b3 --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/multiresource/README.md @@ -0,0 +1,19 @@ +### MultiResource Input Output Job + +## About + +This sample shows how to use the `MultiResourceItemReader` and `MultiResourceItemWriter` +to read and write multiple files in the same step. + +## Run the sample + +You can run the sample from the command line as following: + +``` +$>cd spring-batch-samples +# Launch the sample using the XML configuration +$>../mvnw -Dtest=MultiResourceFunctionalTests#testLaunchJobWithXmlConfig test +# Launch the sample using the Java configuration +$>../mvnw -Dtest=MultiResourceFunctionalTests#testLaunchJobWithJavaConfig test +``` + diff --git a/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/data/delimited.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/data/delimited.csv new file mode 100644 index 0000000000..95a077e6b4 --- /dev/null +++ b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/data/delimited.csv @@ -0,0 +1,6 @@ +customer1,10 +customer2,20 +customer3,30 +customer4,40 +customer5,50 +customer6,60 \ No newline at end of file diff --git a/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/data/delimited2.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/data/delimited2.csv new file mode 100644 index 0000000000..46f251216a --- /dev/null +++ b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/data/delimited2.csv @@ -0,0 +1,2 @@ +customer7,70 +customer8,80 \ No newline at end of file diff --git a/spring-batch-samples/src/main/resources/jobs/iosample/multiResource.xml b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/job/multiResource.xml similarity index 71% rename from spring-batch-samples/src/main/resources/jobs/iosample/multiResource.xml rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/job/multiResource.xml index 727c5eb412..3473e37b11 100644 --- a/spring-batch-samples/src/main/resources/jobs/iosample/multiResource.xml +++ b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/multiresource/job/multiResource.xml @@ -1,9 +1,22 @@ + + + + + + + + + + @@ -29,13 +42,13 @@ - + + value="#{jobParameters['outputFiles']}" /> diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/multiresource/MultiResourceFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/multiresource/MultiResourceFunctionalTests.java new file mode 100644 index 0000000000..acc111b987 --- /dev/null +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/multiresource/MultiResourceFunctionalTests.java @@ -0,0 +1,81 @@ +/* + * Copyright 2006-2023 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 + * + * https://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.sample.file.multiresource; + +import org.junit.jupiter.api.Test; + +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.launch.JobLauncher; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Dan Garrette + * @author Glenn Renfro + * @author Mahmoud Ben Hassine + * @since 2.0 + */ +@SpringJUnitConfig(locations = { "/org/springframework/batch/sample/file/multiresource/job/multiResource.xml", + "/simple-job-launcher-context.xml", "/job-runner-context.xml" }) +class MultiResourceFunctionalTests { + + @Autowired + private JobLauncherTestUtils jobLauncherTestUtils; + + @Test + void testLaunchJobWithXmlConfig() throws Exception { + // given + JobParameters jobParameters = new JobParametersBuilder() + .addString("inputFiles", "org/springframework/batch/sample/file/multiresource/data/delimited*.csv") + .addString("outputFiles", "file:./target/test-outputs/multiResourceOutput.csv") + .toJobParameters(); + + // when + JobExecution jobExecution = this.jobLauncherTestUtils.launchJob(jobParameters); + + // then + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + } + + @Test + public void testLaunchJobWithJavaConfig() throws Exception { + // given + ApplicationContext context = new AnnotationConfigApplicationContext(MultiResourceJobConfiguration.class); + JobLauncher jobLauncher = context.getBean(JobLauncher.class); + Job job = context.getBean(Job.class); + JobParameters jobParameters = new JobParametersBuilder() + .addString("inputFiles", "org/springframework/batch/sample/file/multiresource/data/delimited*.csv") + .addString("outputFiles", "file:./target/test-outputs/multiResourceOutput.csv") + .toJobParameters(); + + // when + JobExecution jobExecution = jobLauncher.run(job, jobParameters); + + // then + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + } + +} diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/MultiResourceFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/MultiResourceFunctionalTests.java deleted file mode 100644 index 8c24a534e4..0000000000 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/MultiResourceFunctionalTests.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2006-2022 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 - * - * https://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.sample.iosample; - -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.junit.jupiter.SpringJUnitConfig; - -/** - * @author Dan Garrette - * @author Glenn Renfro - * @author Mahmoud Ben Hassine - * @since 2.0 - */ -@SpringJUnitConfig(locations = "/jobs/iosample/multiResource.xml") -class MultiResourceFunctionalTests extends AbstractIoSampleTests { - - @Override - protected void pointReaderToOutput(ItemReader reader) { - JobParameters jobParameters = super.getUniqueJobParametersBuilder() - .addString("input.file.path", "file:target/test-outputs/multiResourceOutput.csv.*") - .toJobParameters(); - StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(jobParameters); - StepSynchronizationManager.close(); - StepSynchronizationManager.register(stepExecution); - } - - @Override - protected JobParameters getUniqueJobParameters() { - JobParametersBuilder builder = super.getUniqueJobParametersBuilder(); - return builder.addString("input.file.path", "classpath:data/iosample/input/delimited*.csv") - .addString("output.file.path", "file:target/test-outputs/multiResourceOutput.csv") - .toJobParameters(); - } - -}