diff --git a/spring-batch-samples/README.md b/spring-batch-samples/README.md index 1504259e0..969ec78dd 100644 --- a/spring-batch-samples/README.md +++ b/spring-batch-samples/README.md @@ -171,43 +171,10 @@ from a delimited file, processing it and writing it to another file. ### Fixed Length Import Job -The goal is to demonstrate a typical scenario of importing data -from a fixed-length file to database - -This job shows a typical scenario, when reading input data and -processing the data is cleanly separated. The data provider is -responsible for reading input and mapping each record to a domain -object, which is then passed to the module processor. The module -processor handles the processing of the domain objects, in this case -it only writes them to database. - -In this example we are using a simple fixed length record structure -that can be found in the project at -`data/iosample/input`. A considerable amount of -thought can go into designing the folder structures for batch file -management. The fixed length records look like this: - - UK21341EAH4597898.34customer1 - UK21341EAH4611218.12customer2 - UK21341EAH4724512.78customer2 - UK21341EAH48108109.25customer3 - UK21341EAH49854123.39customer4 - -Looking back to the configuration file you will see where this is -documented in the property of the `FixedLengthTokenizer`. You can -infer the following properties: - - -FieldName | Length ---------- | :----: -ISIN | 12 -Quantity | 3 -Price | 5 -Customer | 9 - -*Output target:* database - writes the data to database using a DAO -object +The goal is to demonstrate a typical scenario of reading data +from a fixed length file, processing it and writing it to another file. +[Fixed Length Import Job sample](./src/main/java/org/springframework/batch/sample/file/fixed/README.md) ### Football Job diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/fixed/FixedLengthJobConfiguration.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/fixed/FixedLengthJobConfiguration.java new file mode 100644 index 000000000..5d0d93dcd --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/fixed/FixedLengthJobConfiguration.java @@ -0,0 +1,85 @@ +package org.springframework.batch.sample.file.fixed; + +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.builder.FlatFileItemReaderBuilder; +import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder; +import org.springframework.batch.item.file.transform.Range; +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; + +@Configuration +@EnableBatchProcessing +public class FixedLengthJobConfiguration { + + @Bean + @StepScope + public FlatFileItemReader itemReader(@Value("#{jobParameters[inputFile]}") Resource resource) { + return new FlatFileItemReaderBuilder().name("itemReader") + .resource(resource) + .fixedLength() + .columns(new Range(1, 9), new Range(10, 11)) + .names("name", "credit") + .targetType(CustomerCredit.class) + .build(); + } + + @Bean + @StepScope + public FlatFileItemWriter itemWriter( + @Value("#{jobParameters[outputFile]}") WritableResource resource) { + return new FlatFileItemWriterBuilder().name("itemWriter") + .resource(resource) + .formatted() + .format("%-9s%-2.0f") + .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/fixed/README.md b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/fixed/README.md new file mode 100644 index 000000000..1140ead32 --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/fixed/README.md @@ -0,0 +1,41 @@ +### Fixed Length File Import Job + +## About + +The goal is to demonstrate a typical scenario of reading data +from a fixed length file, processing it and writing it to another file. + +In this example we are using a simple fixed length record structure +that can be found in the project at +`src/main/resources/org/springframework/batch/sample/file/fixed/data`. +The fixed length records look like this: + +``` +customer110 +customer220 +customer330 +customer440 +customer550 +customer660 +``` + +Looking back to the configuration of the reader you will this is +configured in the fixed column ranges: + +FieldName | Range +--------- | :----: +name | 1,9 +credit | 10,11 + +## 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=FixedLengthFunctionalTests#testLaunchJobWithXmlConfig test +# Launch the sample using the Java configuration +$>../mvnw -Dtest=FixedLengthFunctionalTests#testLaunchJobWithJavaConfig test +``` + diff --git a/spring-batch-samples/src/main/resources/data/iosample/input/fixedLength.txt b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/fixed/data/fixedLength.txt similarity index 100% rename from spring-batch-samples/src/main/resources/data/iosample/input/fixedLength.txt rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/fixed/data/fixedLength.txt diff --git a/spring-batch-samples/src/main/resources/jobs/iosample/fixedLength.xml b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/fixed/job/fixedLength.xml similarity index 71% rename from spring-batch-samples/src/main/resources/jobs/iosample/fixedLength.xml rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/fixed/job/fixedLength.xml index 5cb917bc3..be9cbf283 100644 --- a/spring-batch-samples/src/main/resources/jobs/iosample/fixedLength.xml +++ b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/fixed/job/fixedLength.xml @@ -1,9 +1,22 @@ + + + + + + + + + + diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/fixed/FixedLengthFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/fixed/FixedLengthFunctionalTests.java new file mode 100644 index 000000000..d03c052db --- /dev/null +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/fixed/FixedLengthFunctionalTests.java @@ -0,0 +1,75 @@ +/* + * 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.fixed; + +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; + +@SpringJUnitConfig(locations = { "/org/springframework/batch/sample/file/fixed/job/fixedLength.xml", + "/simple-job-launcher-context.xml", "/job-runner-context.xml" }) +class FixedLengthFunctionalTests { + + @Autowired + private JobLauncherTestUtils jobLauncherTestUtils; + + @Test + void testLaunchJobWithXmlConfig() throws Exception { + // given + JobParameters jobParameters = new JobParametersBuilder() + .addString("inputFile", "org/springframework/batch/sample/file/fixed/data/fixedLength.txt") + .addString("outputFile", "file:./target/test-outputs/fixedLengthOutput.txt") + .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(FixedLengthJobConfiguration.class); + JobLauncher jobLauncher = context.getBean(JobLauncher.class); + Job job = context.getBean(Job.class); + JobParameters jobParameters = new JobParametersBuilder() + .addString("inputFile", "org/springframework/batch/sample/file/fixed/data/fixedLength.txt") + .addString("outputFile", "file:./target/test-outputs/fixedLengthOutput.txt") + .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/FixedLengthFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/FixedLengthFunctionalTests.java deleted file mode 100644 index 0fb8325ff..000000000 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/FixedLengthFunctionalTests.java +++ /dev/null @@ -1,47 +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.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; - -@SpringJUnitConfig(locations = "/jobs/iosample/fixedLength.xml") -class FixedLengthFunctionalTests extends AbstractIoSampleTests { - - @Override - protected void pointReaderToOutput(ItemReader reader) { - JobParameters jobParameters = super.getUniqueJobParametersBuilder() - .addString("inputFile", "file:./target/test-outputs/fixedLengthOutput.txt") - .toJobParameters(); - StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(jobParameters); - StepSynchronizationManager.close(); - StepSynchronizationManager.register(stepExecution); - } - - @Override - protected JobParameters getUniqueJobParameters() { - return super.getUniqueJobParametersBuilder().addString("inputFile", "data/iosample/input/fixedLength.txt") - .addString("outputFile", "file:./target/test-outputs/fixedLengthOutput.txt") - .toJobParameters(); - } - -}