Skip to content

Commit 2374a5a

Browse files
committed
Document thread-safety of item readers and writers
This commit also adds a sample of how to use the synchronized decorators of item readers and writers. Resolves #3646
1 parent 118be5e commit 2374a5a

22 files changed

+187
-61
lines changed

spring-batch-docs/src/main/asciidoc/appendix.adoc

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ include::attributes.adoc[]
1313
.Available Item Readers
1414
[options="header"]
1515
|===============
16-
|Item Reader|Description
16+
|Item Reader|Description|Thread-safe
17+
|`AbstractItemStreamItemReader`|Abstract base class that combines the `ItemStream` and `ItemReader` interfaces.|Yes
1718
|`AbstractItemCountingItemStreamItemReader`|Abstract base class that provides basic
1819
restart capabilities by counting the number of items returned from
19-
an `ItemReader`.
20+
an `ItemReader`.|No
21+
|`AbstractPagingItemReader`|Abstract base class that provides basic paging features|No
22+
|`AbstractPaginatedDataItemReader`|Abstract base class that provides basic paging features based on Spring Data's
23+
paginated facilities|No
2024
|`AggregateItemReader`|An `ItemReader` that delivers a list as its
2125
item, storing up objects from the injected `ItemReader` until they
2226
are ready to be packed out as a collection. This class must be used
@@ -25,46 +29,49 @@ include::attributes.adoc[]
2529
records by returning an `AggregateItem` which responds `true` to its
2630
query methods (`isHeader()` and `isFooter()`). Note that this reader
2731
is not part of the library of readers provided by Spring Batch
28-
but given as a sample in `spring-batch-samples`.
32+
but given as a sample in `spring-batch-samples`.|Yes
2933
|`AmqpItemReader`|Given a Spring `AmqpTemplate`, it provides
3034
synchronous receive methods. The `receiveAndConvert()` method
31-
lets you receive POJO objects.
35+
lets you receive POJO objects.|Yes
3236
|`KafkaItemReader`|An `ItemReader` that reads messages from an Apache Kafka topic.
3337
It can be configured to read messages from multiple partitions of the same topic.
34-
This reader stores message offsets in the execution context to support restart capabilities.
38+
This reader stores message offsets in the execution context to support restart capabilities.|No
3539
|`FlatFileItemReader`|Reads from a flat file. Includes `ItemStream`
36-
and `Skippable` functionality. See link:readersAndWriters.html#flatFileItemReader["`FlatFileItemReader`"].
40+
and `Skippable` functionality. See link:readersAndWriters.html#flatFileItemReader["`FlatFileItemReader`"].|No
3741
|`HibernateCursorItemReader`|Reads from a cursor based on an HQL query. See
38-
link:readersAndWriters.html#cursorBasedItemReaders[`Cursor-based ItemReaders`].
39-
|`HibernatePagingItemReader`|Reads from a paginated HQL query.
42+
link:readersAndWriters.html#cursorBasedItemReaders[`Cursor-based ItemReaders`].|No
43+
|`HibernatePagingItemReader`|Reads from a paginated HQL query.|Yes
4044
|`ItemReaderAdapter`|Adapts any class to the
41-
`ItemReader` interface.
45+
`ItemReader` interface.|Yes
4246
|`JdbcCursorItemReader`|Reads from a database cursor over JDBC. See
43-
link:readersAndWriters.html#cursorBasedItemReaders["`Cursor-based ItemReaders`"].
47+
link:readersAndWriters.html#cursorBasedItemReaders["`Cursor-based ItemReaders`"].|No
4448
|`JdbcPagingItemReader`|Given an SQL statement, pages through the rows,
4549
such that large datasets can be read without running out of
46-
memory.
50+
memory.|Yes
4751
|`JmsItemReader`|Given a Spring `JmsOperations` object and a JMS
4852
destination or destination name to which to send errors, provides items
4953
received through the injected `JmsOperations#receive()`
50-
method.
51-
|`JpaPagingItemReader`|Given a JPQL statement, pages through the
54+
method.|Yes
55+
|`JpaCursorItemReader`|Executes a JPQL query and iterates over the returned result set|No
56+
|`JpaPagingItemReader`|Given a JPQL query, pages through the
5257
rows, such that large datasets can be read without running out of
53-
memory.
54-
|`ListItemReader`|Provides the items from a list, one at a
55-
time.
58+
memory.|Yes
59+
|`ListItemReader`|Provides the items from a list, one at a time.|No
5660
|`MongoItemReader`|Given a `MongoOperations` object and a JSON-based MongoDB
57-
query, provides items received from the `MongoOperations#find()` method.
61+
query, provides items received from the `MongoOperations#find()` method.|Yes
5862
|`Neo4jItemReader`|Given a `Neo4jOperations` object and the components of a
5963
Cyhper query, items are returned as the result of the Neo4jOperations.query
60-
method.
64+
method.|Yes
6165
|`RepositoryItemReader`|Given a Spring Data `PagingAndSortingRepository` object,
6266
a `Sort`, and the name of method to execute, returns items provided by the
63-
Spring Data repository implementation.
67+
Spring Data repository implementation.|Yes
6468
|`StoredProcedureItemReader`|Reads from a database cursor resulting from the
65-
execution of a database stored procedure. See link:readersAndWriters.html#StoredProcedureItemReader[`StoredProcedureItemReader`]
66-
|`StaxEventItemReader`|Reads over StAX. see link:readersAndWriters.html#StaxEventItemReader[`StaxEventItemReader`].
67-
|`JsonItemReader`|Reads items from a Json document. see link:readersAndWriters.html#JsonItemReader[`JsonItemReader`].
69+
execution of a database stored procedure. See link:readersAndWriters.html#StoredProcedureItemReader[`StoredProcedureItemReader`]|No
70+
|`StaxEventItemReader`|Reads over StAX. see link:readersAndWriters.html#StaxEventItemReader[`StaxEventItemReader`].|No
71+
|`JsonItemReader`|Reads items from a Json document. see link:readersAndWriters.html#JsonItemReader[`JsonItemReader`].|No
72+
|`AvroItemReader`|Reads items from a resource containing serialized Avro objects.|No
73+
|`LdifReader`|Reads items from a LDIF resource and returns them as `LdapAttributes`|No
74+
|`MappingLdifReader`|Reads items from a LDIF resource and uses a `RecordMapper` to map them to domain objects|No
6875

6976
|===============
7077

@@ -75,55 +82,55 @@ This reader stores message offsets in the execution context to support restart c
7582
.Available Item Writers
7683
[options="header"]
7784
|===============
78-
|Item Writer|Description
79-
|`AbstractItemStreamItemWriter`|Abstract base class that combines the
80-
`ItemStream` and
81-
`ItemWriter` interfaces.
85+
|Item Writer|Description|Thread-safe
86+
|`AbstractItemStreamItemWriter`|Abstract base class that combines the`ItemStream` and`ItemWriter` interfaces.|Yes
8287
|`AmqpItemWriter`|Given a Spring `AmqpTemplate`, provides
8388
for a synchronous `send` method. The `convertAndSend(Object)`
84-
method lets you send POJO objects.
89+
method lets you send POJO objects.|Yes
8590
|`CompositeItemWriter`|Passes an item to the `write` method of each item
86-
in an injected `List` of `ItemWriter` objects.
91+
in an injected `List` of `ItemWriter` objects.|Yes
8792
|`FlatFileItemWriter`|Writes to a flat file. Includes `ItemStream` and
88-
Skippable functionality. See link:readersAndWriters.html#flatFileItemWriter["`FlatFileItemWriter`"].
93+
Skippable functionality. See link:readersAndWriters.html#flatFileItemWriter["`FlatFileItemWriter`"].|No
8994
|`HibernateItemWriter`|This item writer is Hibernate-session aware
9095
and handles some transaction-related work that a non-"`hibernate-aware`"
9196
item writer would not need to know about and then delegates
92-
to another item writer to do the actual writing.
97+
to another item writer to do the actual writing.|Yes
9398
|`ItemWriterAdapter`|Adapts any class to the
94-
`ItemWriter` interface.
99+
`ItemWriter` interface.|Yes
95100
|`JdbcBatchItemWriter`|Uses batching features from a
96101
`PreparedStatement`, if available, and can
97102
take rudimentary steps to locate a failure during a
98-
`flush`.
103+
`flush`.|Yes
99104
|`JmsItemWriter`|Using a `JmsOperations` object, items are written
100-
to the default queue through the `JmsOperations#convertAndSend()` method.
105+
to the default queue through the `JmsOperations#convertAndSend()` method.|Yes
101106
|`JpaItemWriter`|This item writer is JPA `EntityManager`-aware
102107
and handles some transaction-related work that a non-"`JPA-aware`"
103108
`ItemWriter` would not need to know about and
104-
then delegates to another writer to do the actual writing.
109+
then delegates to another writer to do the actual writing.|Yes
105110
|`KafkaItemWriter`|Using a `KafkaTemplate` object, items are written to the default topic through the
106111
`KafkaTemplate#sendDefault(Object, Object)` method by using a `Converter` to map the key from the item.
107-
A delete flag can also be configured to send delete events to the topic.
112+
A delete flag can also be configured to send delete events to the topic.|No
108113
|`MimeMessageItemWriter`|Using Spring's `JavaMailSender`, items of type `MimeMessage`
109-
are sent as mail messages.
114+
are sent as mail messages.|Yes
110115
|`MongoItemWriter`|Given a `MongoOperations` object, items are written
111116
through the `MongoOperations.save(Object)` method. The actual write is delayed
112-
until the last possible moment before the transaction commits.
117+
until the last possible moment before the transaction commits.|Yes
113118
|`Neo4jItemWriter`|Given a `Neo4jOperations` object, items are persisted through the
114119
`save(Object)` method or deleted through the `delete(Object)`, as dictated by the
115-
`ItemWriter's` configuration
120+
`ItemWriter's` configuration|Yes
116121
|`PropertyExtractingDelegatingItemWriter`|Extends `AbstractMethodInvokingDelegator`
117122
creating arguments on the fly. Arguments are created by retrieving
118123
the values from the fields in the item to be processed (through a
119124
`SpringBeanWrapper`), based on an injected array of field
120-
names.
125+
names.|Yes
121126
|`RepositoryItemWriter`|Given a Spring Data `CrudRepository` implementation,
122-
items are saved through the method specified in the configuration.
127+
items are saved through the method specified in the configuration.|Yes
123128
|`StaxEventItemWriter`|Uses a `Marshaller` implementation to
124129
convert each item to XML and then writes it to an XML file by using
125-
StAX.
130+
StAX.|No
126131
|`JsonFileItemWriter`|Uses a `JsonObjectMarshaller` implementation to
127-
convert each item to Json and then writes it to a Json file.
132+
convert each item to Json and then writes it to a Json file.|No
133+
|`AvroItemWriter`|Serializes data to an `WritableResource` using Avro|No
134+
|`ListItemWriter`|Item writer that writes items to a `List`.|No
128135

129136
|===============

spring-batch-docs/src/main/asciidoc/readersAndWriters.adoc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2787,6 +2787,25 @@ When using an `ItemReader` that is not thread safe, Spring Batch offers the
27872787
thread safe. Spring Batch provides a `SynchronizedItemStreamReaderBuilder` to construct
27882788
an instance of the `SynchronizedItemStreamReader`.
27892789

2790+
For example, the `FlatFileItemReader` is *not* thread-safe and cannot be used in
2791+
a multi-threaded step. This reader can be decorated with a `SynchronizedItemStreamReader`
2792+
in order to use it safely in a multi-threaded step. Here is an example of how to decorate
2793+
such a reader:
2794+
2795+
[source, java]
2796+
----
2797+
@Bean
2798+
public SynchronizedItemStreamReader<Person> itemReader() {
2799+
FlatFileItemReader<Person> flatFileItemReader = new FlatFileItemReaderBuilder<Person>()
2800+
// set reader properties
2801+
.build();
2802+
2803+
return new SynchronizedItemStreamReaderBuilder<Person>()
2804+
.delegate(flatFileItemReader)
2805+
.build();
2806+
}
2807+
----
2808+
27902809
[[singleItemPeekableItemReader]]
27912810
===== `SingleItemPeekableItemReader`
27922811
Spring Batch includes a decorator that adds a peek method to an `ItemReader`. This peek
@@ -2806,6 +2825,25 @@ When using an `ItemWriter` that is not thread safe, Spring Batch offers the
28062825
thread safe. Spring Batch provides a `SynchronizedItemStreamWriterBuilder` to construct
28072826
an instance of the `SynchronizedItemStreamWriter`.
28082827

2828+
For example, the `FlatFileItemWriter` is *not* thread-safe and cannot be used in
2829+
a multi-threaded step. This writer can be decorated with a `SynchronizedItemStreamWriter`
2830+
in order to use it safely in a multi-threaded step. Here is an example of how to decorate
2831+
such a writer:
2832+
2833+
[source, java]
2834+
----
2835+
@Bean
2836+
public SynchronizedItemStreamWriter<Person> itemWriter() {
2837+
FlatFileItemWriter<Person> flatFileItemWriter = new FlatFileItemWriterBuilder<Person>()
2838+
// set writer properties
2839+
.build();
2840+
2841+
return new SynchronizedItemStreamWriterBuilder<Person>()
2842+
.delegate(flatFileItemWriter)
2843+
.build();
2844+
}
2845+
----
2846+
28092847
[[multiResourceItemWriter]]
28102848
===== `MultiResourceItemWriter`
28112849
The `MultiResourceItemWriter` wraps a `ResourceAwareItemWriterItemStream` and creates a new

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemReaderAdapter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2019 the original author or authors.
2+
* Copyright 2006-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,11 @@
2323
* Invokes a custom method on a delegate plain old Java object which itself provides an
2424
* item.
2525
*
26+
* <p>
27+
* This adapter is thread-safe as long as the delegate <code>ItemReader</code> is
28+
* thread-safe.
29+
* </p>
30+
*
2631
* @author Robert Kasanicky
2732
*/
2833
public class ItemReaderAdapter<T> extends AbstractMethodInvokingDelegator<T> implements ItemReader<T> {

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemWriterAdapter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2022 the original author or authors.
2+
* Copyright 2006-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,11 @@
2323
* Delegates item processing to a custom method - passes the item as an argument for the
2424
* delegate method.
2525
*
26+
* <p>
27+
* This adapter is thread-safe as long as the delegate <code>ItemWriter</code> is
28+
* thread-safe.
29+
* </p>
30+
*
2631
* @see PropertyExtractingDelegatingItemWriter
2732
* @author Robert Kasanicky
2833
* @author Mahmoud Ben Hassine

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/PropertyExtractingDelegatingItemWriter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2022 the original author or authors.
2+
* Copyright 2006-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,11 @@
2929
* Delegates processing to a custom method - extracts property values from item object and
3030
* uses them as arguments for the delegate method.
3131
*
32+
* <p>
33+
* This writer is thread-safe as long as the delegate <code>ItemWriter</code> is
34+
* thread-safe.
35+
* </p>
36+
*
3237
* @see ItemWriterAdapter
3338
* @author Robert Kasanicky
3439
* @author Mahmoud Ben Hassine

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemReader.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,6 +28,11 @@
2828
* convert messages.
2929
* </p>
3030
*
31+
* <p>
32+
* This reader is thread-safe as long as the delegate <code>AmqpTemplate</code>
33+
* implementation is thread-safe.
34+
* </p>
35+
*
3136
* @author Chris Schaefer
3237
* @author Mahmoud Ben Hassine
3338
*/

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemWriter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,11 @@
3131
* {@link AmqpTemplate}.
3232
* </p>
3333
*
34+
* <p>
35+
* This writer is thread-safe as long as the delegate <code>AmqpTemplate</code>
36+
* implementation is thread-safe.
37+
* </p>
38+
*
3439
* @author Chris Schaefer
3540
* @author Mahmoud Ben Hassine
3641
*/

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemReader.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
* An {@link ItemReader} that deserializes data from a {@link Resource} containing
4242
* serialized Avro objects.
4343
*
44+
* <p>
45+
* This reader is <b>not</b> thread-safe.
46+
* </p>
47+
*
4448
* @author David Turanski
4549
* @author Mahmoud Ben Hassine
4650
* @author Song JaeGeun

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemWriter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2022 the original author or authors.
2+
* Copyright 2019-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -44,6 +44,10 @@
4444
*
4545
* This does not support restart on failure.
4646
*
47+
* <p>
48+
* This writer is <b>not</b> thread-safe.
49+
* </p>
50+
*
4751
* @since 4.2
4852
* @author David Turanski
4953
* @author Mahmoud Ben Hassine

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractPaginatedDataItemReader.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2019 the original author or authors.
2+
* Copyright 2013-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,8 +28,11 @@
2828
* Spring Data's paginated facilities. It also handles the semantics required for
2929
* restartability based on those facilities.
3030
*
31+
* This reader is <b>not</b> thread-safe.
32+
*
3133
* @author Michael Minella
3234
* @author Glenn Renfro
35+
* @author Mahmoud Ben Hassine
3336
* @since 2.2
3437
* @param <T> Type of item to be read
3538
*/

0 commit comments

Comments
 (0)