Skip to content

Commit bb8d267

Browse files
committed
Add java configuration for the XML file import sample
Issue #3663
1 parent 43e7c8c commit bb8d267

File tree

8 files changed

+265
-75
lines changed

8 files changed

+265
-75
lines changed

spring-batch-samples/README.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ from a fixed length file, processing it and writing it to another file.
176176

177177
[Fixed Length Import Job sample](./src/main/java/org/springframework/batch/sample/file/fixed/README.md)
178178

179+
### XML Input Output
180+
181+
The goal here is to show the use of XML input and output through
182+
streaming and Spring OXM marshallers and unmarshallers.
183+
184+
[XML Input Output](./src/main/java/org/springframework/batch/sample/file/xml/README.md)
185+
179186
### Football Job
180187

181188
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
634641
credit on customer accounts is decreased appropriately. Last, a
635642
report about customers is exported to a file.
636643

637-
### XML Input Output
638-
639-
The goal here is to show the use of XML input and output through
640-
streaming and Spring OXM marshallers and unmarshallers.
641-
642-
The job has a single step that copies `Trade` data from one XML
643-
file to another. It uses XStream for the object XML conversion,
644-
because this is simple to configure for basic use cases like this
645-
one. See
646-
[Spring OXM documentation](https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#oxm) for details of other options.
647-
648644
### Batch metrics with Micrometer
649645

650646
This sample shows how to use [Micrometer](https://micrometer.io) to collect batch metrics in Spring Batch.

spring-batch-samples/pom.xml

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,16 @@
174174
<artifactId>slf4j-simple</artifactId>
175175
<version>${slf4j.version}</version>
176176
</dependency>
177+
<dependency>
178+
<groupId>org.springframework</groupId>
179+
<artifactId>spring-oxm</artifactId>
180+
<version>${spring-framework.version}</version>
181+
</dependency>
182+
<dependency>
183+
<groupId>com.thoughtworks.xstream</groupId>
184+
<artifactId>xstream</artifactId>
185+
<version>${xstream.version}</version>
186+
</dependency>
177187

178188
<!-- test dependencies -->
179189
<dependency>
@@ -223,24 +233,12 @@
223233
<version>${spring-framework.version}</version>
224234
<scope>test</scope>
225235
</dependency>
226-
<dependency>
227-
<groupId>org.springframework</groupId>
228-
<artifactId>spring-oxm</artifactId>
229-
<version>${spring-framework.version}</version>
230-
<scope>test</scope>
231-
</dependency>
232236
<dependency>
233237
<groupId>jakarta.el</groupId>
234238
<artifactId>jakarta.el-api</artifactId>
235239
<version>${jakarta.el-api.version}</version>
236240
<scope>test</scope>
237241
</dependency>
238-
<dependency>
239-
<groupId>com.thoughtworks.xstream</groupId>
240-
<artifactId>xstream</artifactId>
241-
<version>${xstream.version}</version>
242-
<scope>test</scope>
243-
</dependency>
244242
<dependency>
245243
<groupId>org.hsqldb</groupId>
246244
<artifactId>hsqldb</artifactId>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
### XML Input Output
2+
3+
## About
4+
5+
The goal here is to show the use of XML input and output through
6+
streaming and Spring OXM marshallers and unmarshallers.
7+
8+
The job has a single step that copies `CustomerCredit` data from one XML
9+
file to another:
10+
11+
```xml
12+
<?xml version="1.0" encoding="UTF-8"?>
13+
<customers>
14+
<customer>
15+
<name>customer1</name>
16+
<credit>10</credit>
17+
</customer>
18+
<customer>
19+
<name>customer2</name>
20+
<credit>20</credit>
21+
</customer>
22+
<customer>
23+
<name>customer3</name>
24+
<credit>30</credit>
25+
</customer>
26+
<customer>
27+
<name>customer4</name>
28+
<credit>40</credit>
29+
</customer>
30+
<customer>
31+
<name>customer5</name>
32+
<credit>50</credit>
33+
</customer>
34+
</customers>
35+
```
36+
37+
38+
It uses XStream for the object XML conversion,
39+
because this is simple to configure for basic use cases like this
40+
one. See [Spring OXM documentation](https://docs.spring.io/spring-framework/reference/data-access/oxm.html) for details of other options.
41+
42+
## Run the sample
43+
44+
You can run the sample from the command line as following:
45+
46+
```
47+
$>cd spring-batch-samples
48+
# Launch the sample using the XML configuration
49+
$>../mvnw -Dtest=XmlFunctionalTests#testLaunchJobWithXmlConfig test
50+
# Launch the sample using the Java configuration
51+
$>../mvnw -Dtest=XmlFunctionalTests#testLaunchJobWithJavaConfig test
52+
```
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.springframework.batch.sample.file.xml;
2+
3+
import java.math.BigDecimal;
4+
import java.util.Map;
5+
6+
import javax.sql.DataSource;
7+
8+
import com.thoughtworks.xstream.security.ExplicitTypePermission;
9+
10+
import org.springframework.batch.core.Job;
11+
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
12+
import org.springframework.batch.core.configuration.annotation.StepScope;
13+
import org.springframework.batch.core.job.builder.JobBuilder;
14+
import org.springframework.batch.core.repository.JobRepository;
15+
import org.springframework.batch.core.step.builder.StepBuilder;
16+
import org.springframework.batch.item.ItemReader;
17+
import org.springframework.batch.item.ItemWriter;
18+
import org.springframework.batch.item.xml.StaxEventItemReader;
19+
import org.springframework.batch.item.xml.StaxEventItemWriter;
20+
import org.springframework.batch.item.xml.builder.StaxEventItemReaderBuilder;
21+
import org.springframework.batch.item.xml.builder.StaxEventItemWriterBuilder;
22+
import org.springframework.batch.sample.domain.trade.CustomerCredit;
23+
import org.springframework.batch.sample.domain.trade.internal.CustomerCreditIncreaseProcessor;
24+
import org.springframework.beans.factory.annotation.Value;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.core.io.Resource;
28+
import org.springframework.core.io.WritableResource;
29+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
30+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
31+
import org.springframework.jdbc.support.JdbcTransactionManager;
32+
import org.springframework.oxm.xstream.XStreamMarshaller;
33+
34+
@Configuration
35+
@EnableBatchProcessing
36+
public class XmlJobConfiguration {
37+
38+
@Bean
39+
public XStreamMarshaller customerCreditMarshaller() {
40+
XStreamMarshaller marshaller = new XStreamMarshaller();
41+
marshaller
42+
.setAliases(Map.of("customer", CustomerCredit.class, "credit", BigDecimal.class, "name", String.class));
43+
marshaller.setTypePermissions(new ExplicitTypePermission(new Class[] { CustomerCredit.class }));
44+
return marshaller;
45+
}
46+
47+
@Bean
48+
@StepScope
49+
public StaxEventItemReader<CustomerCredit> itemReader(@Value("#{jobParameters[inputFile]}") Resource resource) {
50+
return new StaxEventItemReaderBuilder<CustomerCredit>().name("itemReader")
51+
.resource(resource)
52+
.addFragmentRootElements("customer")
53+
.unmarshaller(customerCreditMarshaller())
54+
.build();
55+
}
56+
57+
@Bean
58+
@StepScope
59+
public StaxEventItemWriter<CustomerCredit> itemWriter(
60+
@Value("#{jobParameters[outputFile]}") WritableResource resource) {
61+
return new StaxEventItemWriterBuilder<CustomerCredit>().name("itemWriter")
62+
.resource(resource)
63+
.marshaller(customerCreditMarshaller())
64+
.rootTagName("customers")
65+
.overwriteOutput(true)
66+
.build();
67+
}
68+
69+
@Bean
70+
public Job job(JobRepository jobRepository, JdbcTransactionManager transactionManager,
71+
ItemReader<CustomerCredit> itemReader, ItemWriter<CustomerCredit> itemWriter) {
72+
return new JobBuilder("ioSampleJob", jobRepository)
73+
.start(new StepBuilder("step1", jobRepository).<CustomerCredit, CustomerCredit>chunk(2, transactionManager)
74+
.reader(itemReader)
75+
.processor(new CustomerCreditIncreaseProcessor())
76+
.writer(itemWriter)
77+
.build())
78+
.build();
79+
}
80+
81+
// Infrastructure beans
82+
83+
@Bean
84+
public DataSource dataSource() {
85+
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL)
86+
.addScript("/org/springframework/batch/core/schema-drop-hsqldb.sql")
87+
.addScript("/org/springframework/batch/core/schema-hsqldb.sql")
88+
.generateUniqueName(true)
89+
.build();
90+
}
91+
92+
@Bean
93+
public JdbcTransactionManager transactionManager(DataSource dataSource) {
94+
return new JdbcTransactionManager(dataSource);
95+
}
96+
97+
}

spring-batch-samples/src/main/resources/jobs/iosample/xml.xml renamed to spring-batch-samples/src/main/resources/org/springframework/batch/sample/file/xml/job/xml.xml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<beans xmlns="http://www.springframework.org/schema/beans"
33
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:batch="http://www.springframework.org/schema/batch"
45
xmlns:util="http://www.springframework.org/schema/util"
56
xsi:schemaLocation="
67
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
8+
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd
79
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
810

9-
<bean id="itemReader" class="org.springframework.batch.item.xml.StaxEventItemReader">
11+
<batch:job id="ioSampleJob" xmlns="http://www.springframework.org/schema/batch">
12+
<batch:step id="step1">
13+
<batch:tasklet>
14+
<batch:chunk reader="itemReader" processor="itemProcessor" writer="itemWriter"
15+
commit-interval="2"/>
16+
</batch:tasklet>
17+
</batch:step>
18+
</batch:job>
19+
20+
<bean id="itemProcessor" class="org.springframework.batch.sample.domain.trade.internal.CustomerCreditIncreaseProcessor" />
21+
22+
<bean id="itemReader" class="org.springframework.batch.item.xml.StaxEventItemReader" scope="step">
1023
<property name="fragmentRootElementName" value="customer" />
11-
<property name="resource" value="data/iosample/input/input.xml" />
24+
<property name="resource" value="#{jobParameters[inputFile]}" />
1225
<property name="unmarshaller" ref="customerCreditMarshaller" />
1326
</bean>
1427

15-
<bean id="itemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
16-
<property name="resource" ref="outputResource" />
28+
<bean id="itemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter" scope="step">
29+
<property name="resource" value="#{jobParameters[outputFile]}" />
1730
<property name="marshaller" ref="customerCreditMarshaller" />
1831
<property name="rootTagName" value="customers" />
1932
<property name="overwriteOutput" value="true" />
@@ -24,7 +37,7 @@
2437
<util:map id="aliases">
2538
<entry key="customer"
2639
value="org.springframework.batch.sample.domain.trade.CustomerCredit" />
27-
<entry key="price" value="java.math.BigDecimal" />
40+
<entry key="credit" value="java.math.BigDecimal" />
2841
<entry key="name" value="java.lang.String" />
2942
</util:map>
3043
</property>
@@ -39,7 +52,4 @@
3952
</property>
4053
</bean>
4154

42-
<bean id="outputResource" class="org.springframework.core.io.FileSystemResource">
43-
<constructor-arg value="target/test-outputs/output.xml" />
44-
</bean>
4555
</beans>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2006-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.batch.sample.file.xml;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.batch.core.BatchStatus;
22+
import org.springframework.batch.core.Job;
23+
import org.springframework.batch.core.JobExecution;
24+
import org.springframework.batch.core.JobParameters;
25+
import org.springframework.batch.core.JobParametersBuilder;
26+
import org.springframework.batch.core.launch.JobLauncher;
27+
import org.springframework.batch.test.JobLauncherTestUtils;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.context.ApplicationContext;
30+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
31+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
32+
33+
import static org.junit.jupiter.api.Assertions.assertEquals;
34+
35+
/**
36+
* @author Dan Garrette
37+
* @author Glenn Renfro
38+
* @author Mahmoud Ben Hassine
39+
* @since 2.0
40+
*/
41+
@SpringJUnitConfig(locations = { "/org/springframework/batch/sample/file/xml/job/xml.xml",
42+
"/simple-job-launcher-context.xml", "/job-runner-context.xml" })
43+
class XmlFunctionalTests {
44+
45+
@Autowired
46+
private JobLauncherTestUtils jobLauncherTestUtils;
47+
48+
@Test
49+
void testLaunchJobWithXmlConfig() throws Exception {
50+
// given
51+
JobParameters jobParameters = new JobParametersBuilder()
52+
.addString("inputFile", "org/springframework/batch/sample/file/xml/data/input.xml")
53+
.addString("outputFile", "file:./target/test-outputs/output.xml")
54+
.toJobParameters();
55+
56+
// when
57+
JobExecution jobExecution = this.jobLauncherTestUtils.launchJob(jobParameters);
58+
59+
// then
60+
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
61+
}
62+
63+
@Test
64+
public void testLaunchJobWithJavaConfig() throws Exception {
65+
// given
66+
ApplicationContext context = new AnnotationConfigApplicationContext(XmlJobConfiguration.class);
67+
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
68+
Job job = context.getBean(Job.class);
69+
JobParameters jobParameters = new JobParametersBuilder()
70+
.addString("inputFile", "org/springframework/batch/sample/file/xml/data/input.xml")
71+
.addString("outputFile", "file:./target/test-outputs/output.xml")
72+
.toJobParameters();
73+
74+
// when
75+
JobExecution jobExecution = jobLauncher.run(job, jobParameters);
76+
77+
// then
78+
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
79+
}
80+
81+
}

0 commit comments

Comments
 (0)