From bb8d267b5d17f71b0ab94a39f61b875d3d273ade Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Tue, 26 Sep 2023 10:40:45 +0200 Subject: [PATCH] Add java configuration for the XML file import sample Issue #3663 --- spring-batch-samples/README.md | 18 ++-- spring-batch-samples/pom.xml | 22 ++--- .../batch/sample/file/xml/README.md | 52 ++++++++++ .../sample/file/xml/XmlJobConfiguration.java | 97 +++++++++++++++++++ .../batch/sample/file/xml/data}/input.xml | 0 .../batch/sample/file/xml/job}/xml.xml | 26 +++-- .../sample/file/xml/XmlFunctionalTests.java | 81 ++++++++++++++++ .../sample/iosample/XmlFunctionalTests.java | 44 --------- 8 files changed, 265 insertions(+), 75 deletions(-) create mode 100644 spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/README.md create mode 100644 spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/XmlJobConfiguration.java rename spring-batch-samples/src/main/resources/{data/iosample/input => org/springframework/batch/sample/file/xml/data}/input.xml (100%) rename spring-batch-samples/src/main/resources/{jobs/iosample => org/springframework/batch/sample/file/xml/job}/xml.xml (64%) create mode 100644 spring-batch-samples/src/test/java/org/springframework/batch/sample/file/xml/XmlFunctionalTests.java delete mode 100644 spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/XmlFunctionalTests.java diff --git a/spring-batch-samples/README.md b/spring-batch-samples/README.md index 969ec78dd1..42b71c0c24 100644 --- a/spring-batch-samples/README.md +++ b/spring-batch-samples/README.md @@ -176,6 +176,13 @@ 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) +### XML Input Output + +The goal here is to show the use of XML input and output through +streaming and Spring OXM marshallers and unmarshallers. + +[XML Input Output](./src/main/java/org/springframework/batch/sample/file/xml/README.md) + ### Football Job This is a (American) Football statistics loading job. It loads two files containing players and games @@ -634,17 +641,6 @@ file to database. Second, the trades are read from the database and credit on customer accounts is decreased appropriately. Last, a report about customers is exported to a file. -### XML Input Output - -The goal here is to show the use of XML input and output through -streaming and Spring OXM marshallers and unmarshallers. - -The job has a single step that copies `Trade` data from one XML -file to another. It uses XStream for the object XML conversion, -because this is simple to configure for basic use cases like this -one. See -[Spring OXM documentation](https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#oxm) for details of other options. - ### Batch metrics with Micrometer This sample shows how to use [Micrometer](https://micrometer.io) to collect batch metrics in Spring Batch. diff --git a/spring-batch-samples/pom.xml b/spring-batch-samples/pom.xml index 4f85457072..980bd63f63 100644 --- a/spring-batch-samples/pom.xml +++ b/spring-batch-samples/pom.xml @@ -174,6 +174,16 @@ slf4j-simple ${slf4j.version} + + org.springframework + spring-oxm + ${spring-framework.version} + + + com.thoughtworks.xstream + xstream + ${xstream.version} + @@ -223,24 +233,12 @@ ${spring-framework.version} test - - org.springframework - spring-oxm - ${spring-framework.version} - test - jakarta.el jakarta.el-api ${jakarta.el-api.version} test - - com.thoughtworks.xstream - xstream - ${xstream.version} - test - org.hsqldb hsqldb diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/README.md b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/README.md new file mode 100644 index 0000000000..7f07af07bb --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/README.md @@ -0,0 +1,52 @@ +### XML Input Output + +## About + +The goal here is to show the use of XML input and output through +streaming and Spring OXM marshallers and unmarshallers. + +The job has a single step that copies `CustomerCredit` data from one XML +file to another: + +```xml + + + + customer1 + 10 + + + customer2 + 20 + + + customer3 + 30 + + + customer4 + 40 + + + customer5 + 50 + + +``` + + +It uses XStream for the object XML conversion, +because this is simple to configure for basic use cases like this +one. See [Spring OXM documentation](https://docs.spring.io/spring-framework/reference/data-access/oxm.html) for details of other options. + +## 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=XmlFunctionalTests#testLaunchJobWithXmlConfig test +# Launch the sample using the Java configuration +$>../mvnw -Dtest=XmlFunctionalTests#testLaunchJobWithJavaConfig test +``` \ No newline at end of file diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/XmlJobConfiguration.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/XmlJobConfiguration.java new file mode 100644 index 0000000000..907bae30e3 --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/file/xml/XmlJobConfiguration.java @@ -0,0 +1,97 @@ +package org.springframework.batch.sample.file.xml; + +import java.math.BigDecimal; +import java.util.Map; + +import javax.sql.DataSource; + +import com.thoughtworks.xstream.security.ExplicitTypePermission; + +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.xml.StaxEventItemReader; +import org.springframework.batch.item.xml.StaxEventItemWriter; +import org.springframework.batch.item.xml.builder.StaxEventItemReaderBuilder; +import org.springframework.batch.item.xml.builder.StaxEventItemWriterBuilder; +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; +import org.springframework.oxm.xstream.XStreamMarshaller; + +@Configuration +@EnableBatchProcessing +public class XmlJobConfiguration { + + @Bean + public XStreamMarshaller customerCreditMarshaller() { + XStreamMarshaller marshaller = new XStreamMarshaller(); + marshaller + .setAliases(Map.of("customer", CustomerCredit.class, "credit", BigDecimal.class, "name", String.class)); + marshaller.setTypePermissions(new ExplicitTypePermission(new Class[] { CustomerCredit.class })); + return marshaller; + } + + @Bean + @StepScope + public StaxEventItemReader itemReader(@Value("#{jobParameters[inputFile]}") Resource resource) { + return new StaxEventItemReaderBuilder().name("itemReader") + .resource(resource) + .addFragmentRootElements("customer") + .unmarshaller(customerCreditMarshaller()) + .build(); + } + + @Bean + @StepScope + public StaxEventItemWriter itemWriter( + @Value("#{jobParameters[outputFile]}") WritableResource resource) { + return new StaxEventItemWriterBuilder().name("itemWriter") + .resource(resource) + .marshaller(customerCreditMarshaller()) + .rootTagName("customers") + .overwriteOutput(true) + .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/resources/data/iosample/input/input.xml b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/xml/data/input.xml similarity index 100% rename from spring-batch-samples/src/main/resources/data/iosample/input/input.xml rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/xml/data/input.xml diff --git a/spring-batch-samples/src/main/resources/jobs/iosample/xml.xml b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/xml/job/xml.xml similarity index 64% rename from spring-batch-samples/src/main/resources/jobs/iosample/xml.xml rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/xml/job/xml.xml index 41a5f00c39..2a612dc815 100644 --- a/spring-batch-samples/src/main/resources/jobs/iosample/xml.xml +++ b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/xml/job/xml.xml @@ -1,19 +1,32 @@ - + + + + + + + + + + + - + - - + + @@ -24,7 +37,7 @@ - + @@ -39,7 +52,4 @@ - - - diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/xml/XmlFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/xml/XmlFunctionalTests.java new file mode 100644 index 0000000000..e9bb777998 --- /dev/null +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/file/xml/XmlFunctionalTests.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.xml; + +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/xml/job/xml.xml", + "/simple-job-launcher-context.xml", "/job-runner-context.xml" }) +class XmlFunctionalTests { + + @Autowired + private JobLauncherTestUtils jobLauncherTestUtils; + + @Test + void testLaunchJobWithXmlConfig() throws Exception { + // given + JobParameters jobParameters = new JobParametersBuilder() + .addString("inputFile", "org/springframework/batch/sample/file/xml/data/input.xml") + .addString("outputFile", "file:./target/test-outputs/output.xml") + .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(XmlJobConfiguration.class); + JobLauncher jobLauncher = context.getBean(JobLauncher.class); + Job job = context.getBean(Job.class); + JobParameters jobParameters = new JobParametersBuilder() + .addString("inputFile", "org/springframework/batch/sample/file/xml/data/input.xml") + .addString("outputFile", "file:./target/test-outputs/output.xml") + .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/XmlFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/XmlFunctionalTests.java deleted file mode 100644 index bee6b9cbf0..0000000000 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/iosample/XmlFunctionalTests.java +++ /dev/null @@ -1,44 +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.item.ItemReader; -import org.springframework.batch.item.xml.StaxEventItemReader; -import org.springframework.batch.sample.domain.trade.CustomerCredit; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; -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/xml.xml") -class XmlFunctionalTests extends AbstractIoSampleTests { - - @Autowired - private Resource outputResource; - - @Override - protected void pointReaderToOutput(ItemReader reader) { - StaxEventItemReader xmlReader = (StaxEventItemReader) reader; - xmlReader.setResource(outputResource); - } - -}