Skip to content

Commit aa77031

Browse files
authored
Merge branch 'spring-projects:main' into feature/add-migration-script-integration-tests
2 parents b2c63d3 + ae5767a commit aa77031

File tree

32 files changed

+1686
-80
lines changed

32 files changed

+1686
-80
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Latest news
22

3+
* August 20, 2025: [Spring Batch 6.0.0 M2 available now](https://spring.io/blog/2025/08/20/spring-batch-6)
34
* July 23, 2025: [Spring Batch 6.0.0 M1 is out!](https://spring.io/blog/2025/07/23/spring-batch-6)
45
* March 19, 2025: [Spring Batch 5.2.2 available now](https://spring.io/blog/2025/03/19/spring-batch-5-2-2-available-now)
56
* December 18, 2024: [Spring Batch 5.1.3 and 5.2.1 available now](https://spring.io/blog/2024/12/18/spring-batch-5-1-3-and-5-2-1-available-now)
67
* November 24, 2024: [Bootiful Spring Boot 3.4: Spring Batch](https://spring.io/blog/2024/11/24/bootiful-34-batch)
7-
* November 20, 2024: [Spring Batch 5.2.0 goes GA!](https://spring.io/blog/2024/11/20/spring-batch-5-2-0-goes-ga)
88

99
<img align="right" src="spring-batch-docs/modules/ROOT/assets/images/spring-batch.png" width="200" height="200">
1010

pom.xml

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262

6363
<!-- production dependencies -->
6464
<spring-framework.version>7.0.0-SNAPSHOT</spring-framework.version>
65-
<spring-retry.version>2.0.12-SNAPSHOT</spring-retry.version>
65+
<spring-retry.version>2.0.13-SNAPSHOT</spring-retry.version>
6666
<spring-integration.version>7.0.0-SNAPSHOT</spring-integration.version>
6767
<micrometer.version>1.16.0-SNAPSHOT</micrometer.version>
6868

@@ -78,7 +78,7 @@
7878
<jackson.version>2.19.2</jackson.version>
7979
<avro.version>1.12.0</avro.version>
8080
<gson.version>2.13.1</gson.version>
81-
<hibernate-core.version>7.0.6.Final</hibernate-core.version>
81+
<hibernate-core.version>7.1.0.Final</hibernate-core.version>
8282
<jakarta.annotation-api.version>3.0.0</jakarta.annotation-api.version>
8383
<jakarta.mail-api.version>2.1.3</jakarta.mail-api.version>
8484
<jakarta.jms-api.version>3.1.0</jakarta.jms-api.version>
@@ -98,8 +98,8 @@
9898
<junit.version>4.13.2</junit.version>
9999
<junit-vintage-engine.version>${junit-jupiter.version}</junit-vintage-engine.version>
100100
<hamcrest.version>3.0</hamcrest.version>
101-
<assertj.version>3.27.3</assertj.version>
102-
<mockito.version>5.18.0</mockito.version>
101+
<assertj.version>3.27.4</assertj.version>
102+
<mockito.version>5.19.0</mockito.version>
103103
<xmlunit.version>2.10.3</xmlunit.version>
104104
<commons-io.version>2.20.0</commons-io.version>
105105
<commons-dbcp2.version>2.13.0</commons-dbcp2.version>
@@ -108,7 +108,7 @@
108108
<h2.version>2.3.232</h2.version>
109109
<sqlite.version>3.50.3.0</sqlite.version>
110110
<derby.version>10.16.1.1</derby.version> <!-- FIXME build failure with 10.17.1.0 -->
111-
<hana.version>2.25.9</hana.version>
111+
<hana.version>2.25.12</hana.version>
112112
<artemis.version>2.42.0</artemis.version>
113113
<jaxb-core.version>4.0.5</jaxb-core.version>
114114
<log4j.version>2.25.1</log4j.version>
@@ -117,25 +117,25 @@
117117
<jakarta.el.version>4.0.2</jakarta.el.version>
118118
<jakarta.inject-api.version>2.0.1</jakarta.inject-api.version>
119119
<jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version>
120-
<angus-mail.version>2.0.3</angus-mail.version>
120+
<angus-mail.version>2.0.4</angus-mail.version>
121121
<woodstox-core.version>7.1.1</woodstox-core.version>
122122
<aspectj.version>1.9.24</aspectj.version>
123-
<mysql-connector-j.version>9.2.0</mysql-connector-j.version>
124-
<mariadb-java-client.version>3.5.4</mariadb-java-client.version>
123+
<mysql-connector-j.version>9.4.0</mysql-connector-j.version>
124+
<mariadb-java-client.version>3.5.5</mariadb-java-client.version>
125125
<postgresql.version>42.7.7</postgresql.version>
126126
<db2.version>12.1.2.0</db2.version>
127-
<oracle.version>19.27.0.0</oracle.version>
127+
<oracle.version>19.28.0.0</oracle.version>
128128
<sqlserver.version>11.2.3.jre17</sqlserver.version>
129129
<jtds.version>1.3.1</jtds.version>
130130
<testcontainers.version>1.21.3</testcontainers.version>
131131
<testcontainers-redis.version>2.2.4</testcontainers-redis.version>
132132
<jsonassert.version>1.5.3</jsonassert.version>
133-
<groovy-jsr223.version>4.0.27</groovy-jsr223.version>
133+
<groovy-jsr223.version>4.0.28</groovy-jsr223.version>
134134
<nashorn.version>15.6</nashorn.version>
135135
<beanshell.version>2.0b6</beanshell.version>
136136
<jruby.version>9.4.13.0</jruby.version>
137-
<lettuce.version>6.7.1.RELEASE</lettuce.version>
138-
<jedis.version>6.0.0</jedis.version>
137+
<lettuce.version>6.8.0.RELEASE</lettuce.version>
138+
<jedis.version>6.1.0</jedis.version>
139139

140140
<!-- samples dependencies -->
141141
<spring-rabbit.version>${spring-amqp.version}</spring-rabbit.version>
@@ -150,14 +150,14 @@
150150
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
151151
<maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
152152
<maven-failsafe-plugin.version>3.5.3</maven-failsafe-plugin.version>
153-
<maven-javadoc-plugin.version>3.11.2</maven-javadoc-plugin.version>
153+
<maven-javadoc-plugin.version>3.11.3</maven-javadoc-plugin.version>
154154
<maven-source-plugin.version>3.3.1</maven-source-plugin.version>
155-
<flatten-maven-plugin.version>1.7.1</flatten-maven-plugin.version>
155+
<flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
156156
<maven-deploy-plugin.version>3.1.4</maven-deploy-plugin.version>
157157
<maven-assembly-plugin.version>3.7.1</maven-assembly-plugin.version>
158158
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
159159
<spring-javaformat-maven-plugin.version>0.0.47</spring-javaformat-maven-plugin.version>
160-
<error-prone.version>2.40.0</error-prone.version>
160+
<error-prone.version>2.41.0</error-prone.version>
161161
</properties>
162162

163163
<build>

spring-batch-core/src/main/java/org/springframework/batch/core/annotation/AfterChunk.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@
1616
package org.springframework.batch.core.annotation;
1717

1818
import org.springframework.batch.core.listener.ChunkListener;
19-
import org.springframework.batch.core.scope.context.ChunkContext;
19+
import org.springframework.batch.item.Chunk;
2020

2121
import java.lang.annotation.ElementType;
2222
import java.lang.annotation.Retention;
2323
import java.lang.annotation.RetentionPolicy;
2424
import java.lang.annotation.Target;
2525

2626
/**
27-
* Marks a method to be called after a chunk is executed.<br>
28-
* <br>
29-
* Expected signature: void afterChunk(ChunkContext context)
27+
* Marks a method to be called after a chunk is processed. <br>
28+
* Expected signature: void afterChunk(Chunk)
3029
*
3130
* @author Lucas Ward
31+
* @author Mahmoud Ben Hassine
3232
* @since 2.0
33-
* @see ChunkListener#afterChunk(ChunkContext context)
33+
* @see ChunkListener#afterChunk(Chunk)
3434
*/
3535
@Retention(RetentionPolicy.RUNTIME)
3636
@Target({ ElementType.METHOD })

spring-batch-core/src/main/java/org/springframework/batch/core/annotation/BeforeChunk.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@
2222

2323
import org.springframework.batch.core.listener.ChunkListener;
2424
import org.springframework.batch.core.scope.context.ChunkContext;
25+
import org.springframework.batch.item.Chunk;
2526

2627
/**
2728
* Marks a method to be called before a chunk is executed. <br>
2829
* <br>
29-
* Expected signature: void beforeChunk(ChunkContext context)
30+
* Expected signature: void beforeChunk(Chunk)
3031
*
3132
* @author Lucas Ward
33+
* @author Mahmoud Ben Hassine
3234
* @since 2.0
33-
* @see ChunkListener#beforeChunk(ChunkContext context)
35+
* @see ChunkListener#beforeChunk(Chunk)
3436
*/
3537
@Retention(RetentionPolicy.RUNTIME)
3638
@Target({ ElementType.METHOD })
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2025-present 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+
package org.springframework.batch.core.annotation;
17+
18+
import java.lang.annotation.ElementType;
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
import java.lang.annotation.Target;
22+
23+
import org.springframework.batch.core.listener.ChunkListener;
24+
import org.springframework.batch.item.Chunk;
25+
26+
/**
27+
* Marks a method to be called after a chunk has failed. <br>
28+
* Expected signature: void onChunkError(Exception, Chunk)
29+
*
30+
* @author Mahmoud Ben Hassine
31+
* @since 6.0
32+
* @see ChunkListener#onChunkError(Exception, Chunk)
33+
*/
34+
@Retention(RetentionPolicy.RUNTIME)
35+
@Target({ ElementType.METHOD })
36+
public @interface OnChunkError {
37+
38+
}

spring-batch-core/src/main/java/org/springframework/batch/core/aot/CoreRuntimeHints.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,18 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
9595
"java.util.concurrent.ConcurrentHashMap$Segment");
9696

9797
// resource hints
98-
hints.resources()
99-
.registerPattern(
100-
"org/springframework/batch/core/schema-{h2,derby,hsqldb,sqlite,db2,hana,mysql,mariadb,oracle,postgresql,sqlserver,sybase}.sql");
98+
hints.resources().registerPattern("org/springframework/batch/core/schema-h2.sql");
99+
hints.resources().registerPattern("org/springframework/batch/core/schema-derby.sql");
100+
hints.resources().registerPattern("org/springframework/batch/core/schema-hsqldb.sql");
101+
hints.resources().registerPattern("org/springframework/batch/core/schema-sqlite.sql");
102+
hints.resources().registerPattern("org/springframework/batch/core/schema-db2.sql");
103+
hints.resources().registerPattern("org/springframework/batch/core/schema-hana.sql");
104+
hints.resources().registerPattern("org/springframework/batch/core/schema-mysql.sql");
105+
hints.resources().registerPattern("org/springframework/batch/core/schema-mariadb.sql");
106+
hints.resources().registerPattern("org/springframework/batch/core/schema-oracle.sql");
107+
hints.resources().registerPattern("org/springframework/batch/core/schema-postgresql.sql");
108+
hints.resources().registerPattern("org/springframework/batch/core/schema-sqlserver.sql");
109+
hints.resources().registerPattern("org/springframework/batch/core/schema-sybase.sql");
101110

102111
// proxy hints
103112
hints.proxies()

spring-batch-core/src/main/java/org/springframework/batch/core/launch/JobOperator.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
*
4040
* @author Dave Syer
4141
* @author Mahmoud Ben Hassine
42+
* @author Yejeong Ham
4243
* @since 2.0
4344
*/
4445
@SuppressWarnings("removal")
@@ -248,6 +249,17 @@ JobExecution startNextInstance(Job job) throws JobRestartException, JobExecution
248249
*/
249250
JobExecution abandon(JobExecution jobExecution) throws JobExecutionAlreadyRunningException;
250251

252+
/**
253+
* Marks the given {@link JobExecution} as {@code FAILED} when it is stuck in a
254+
* {@code STARTED} state due to an abrupt shutdown or failure, in order to make it
255+
* restartable. This operation makes a previously non-restartable execution eligible
256+
* for restart by updating its execution context with the flag {@code recovered=true}.
257+
* @param jobExecution the {@link JobExecution} to recover
258+
* @return the {@link JobExecution} after it has been marked as recovered
259+
* @since 6.0
260+
*/
261+
JobExecution recover(JobExecution jobExecution);
262+
251263
/**
252264
* List the {@link JobExecution JobExecutions} associated with a particular
253265
* {@link JobInstance}, in reverse order of creation (and therefore usually of

spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobOperator.java

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737

3838
/**
3939
* A command-line utility to operate Spring Batch jobs using the {@link JobOperator}. It
40-
* allows starting, stopping, restarting, and abandoning jobs from the command line.
40+
* allows starting, stopping, restarting, abandoning and recovering jobs from the command
41+
* line.
4142
* <p>
4243
* This utility requires a Spring application context to be set up with the necessary
4344
* batch infrastructure, including a {@link JobOperator}, a {@link JobRepository}, and a
@@ -49,6 +50,7 @@
4950
* {@link #main(String[])} method explains the various operations and exit codes.
5051
*
5152
* @author Mahmoud Ben Hassine
53+
* @author Yejeong Ham
5254
* @since 6.0
5355
*/
5456
public class CommandLineJobOperator {
@@ -204,21 +206,61 @@ public int abandon(long jobExecutionId) {
204206
}
205207
}
206208

207-
/*
209+
/**
210+
* Recover the job execution with the given ID that is stuck in a {@code STARTED}
211+
* state due to an abrupt shutdown or failure, making it eligible for restart.
212+
* @param jobExecutionId the ID of the job execution to recover
213+
* @return the exit code of the recovered job execution, or JVM_EXITCODE_GENERIC_ERROR
214+
* if an error occurs
215+
*/
216+
public int recover(long jobExecutionId) {
217+
logger.info(() -> "Recovering job execution with ID: " + jobExecutionId);
218+
try {
219+
JobExecution jobExecution = this.jobRepository.getJobExecution(jobExecutionId);
220+
if (jobExecution == null) {
221+
logger.error(() -> "No job execution found with ID: " + jobExecutionId);
222+
return JVM_EXITCODE_GENERIC_ERROR;
223+
}
224+
JobExecution recoveredExecution = this.jobOperator.recover(jobExecution);
225+
return this.exitCodeMapper.intValue(recoveredExecution.getExitStatus().getExitCode());
226+
}
227+
catch (Exception e) {
228+
return JVM_EXITCODE_GENERIC_ERROR;
229+
}
230+
}
231+
232+
// @formatter:off
233+
/**
208234
* Main method to operate jobs from the command line.
209-
*
210-
* Usage: java org.springframework.batch.core.launch.support.CommandLineJobOperator \
211-
* fully.qualified.name.of.JobConfigurationClass \ operation \ parameters \
212-
*
213-
* where operation is one of the following: - start jobName [jobParameters] -
214-
* startNextInstance jobName - restart jobExecutionId - stop jobExecutionId - abandon
215-
* jobExecutionId
216-
*
217-
* and jobParameters are key-value pairs in the form name=value,type,identifying.
218-
*
219-
* Exit status: - 0: Job completed successfully - 1: Job failed to (re)start or an
220-
* error occurred - 2: Job configuration class not found
235+
* <p>
236+
* Usage:
237+
* <code>
238+
* java org.springframework.batch.core.launch.support.CommandLineJobOperator \
239+
* fully.qualified.name.of.JobConfigurationClass \
240+
* operation \
241+
* parameters
242+
* </code>
243+
* <p>
244+
* where <code>operation</code> is one of the following:
245+
* <ul>
246+
* <li>start jobName <code>[jobParameters]</code></li>
247+
* <li>startNextInstance jobName</li>
248+
* <li>restart jobExecutionId</li>
249+
* <li>stop jobExecutionId</li>
250+
* <li>abandon jobExecutionId</li>
251+
* <li>recover jobExecutionId</li>
252+
* </ul>
253+
* <p>
254+
* and <code>jobParameters</code> are key-value pairs in the form name=value,type,identifying.
255+
* <p>
256+
* Exit status:
257+
* <ul>
258+
* <li>0: Job completed successfully</li>
259+
* <li>1: Job failed to (re)start or an error occurred</li>
260+
* <li>2: Job configuration class not found</li>
261+
* </ul>
221262
*/
263+
// @formatter:on
222264
public static void main(String[] args) {
223265
if (args.length < 3) {
224266
String usage = """
@@ -229,6 +271,7 @@ public static void main(String[] args) {
229271
- restart jobExecutionId
230272
- stop jobExecutionId
231273
- abandon jobExecutionId
274+
- recover jobExecutionId
232275
and jobParameters are key-value pairs in the form name=value,type,identifying.
233276
""";
234277
System.err.printf(String.format(usage, CommandLineJobOperator.class.getName()));
@@ -287,6 +330,10 @@ public static void main(String[] args) {
287330
jobExecutionId = Long.parseLong(args[2]);
288331
exitCode = operator.abandon(jobExecutionId);
289332
break;
333+
case "recover":
334+
jobExecutionId = Long.parseLong(args[2]);
335+
exitCode = operator.recover(jobExecutionId);
336+
break;
290337
default:
291338
System.err.println("Unknown operation: " + operation);
292339
exitCode = JVM_EXITCODE_GENERIC_ERROR;

0 commit comments

Comments
 (0)