From 69331c516dbb95cc23d4340fe083460fc376551e Mon Sep 17 00:00:00 2001 From: khj68 Date: Sun, 17 Aug 2025 17:53:11 +0900 Subject: [PATCH] Improve error message when using ResourcelessJobRepository with partitioned steps Issue spring-projects/spring-batch#4732 The ResourcelessJobRepository cannot be used with partitioned steps as it does not support execution context, which is required for partition metadata sharing between manager and worker steps. This commit adds an explicit check in SimpleStepExecutionSplitter to detect usage of ResourcelessJobRepository and throw a clear, informative error message instead of the confusing 'Cannot restart step from STARTING status' message that was previously displayed. Signed-off-by: khj68 --- .../support/SimpleStepExecutionSplitter.java | 7 ++++++ .../SimpleStepExecutionSplitterTests.java | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitter.java index c31f85b62f..a42e8c9dec 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitter.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitter.java @@ -33,6 +33,7 @@ import org.springframework.batch.core.partition.Partitioner; import org.springframework.batch.core.partition.StepExecutionSplitter; import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.repository.support.ResourcelessJobRepository; import org.springframework.batch.item.ExecutionContext; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; @@ -148,6 +149,12 @@ public String getStepName() { @Override public Set split(StepExecution stepExecution, int gridSize) throws JobExecutionException { + if (jobRepository instanceof ResourcelessJobRepository) { + throw new JobExecutionException("ResourcelessJobRepository cannot be used with partitioned steps " + + "as it does not support execution context. Please use a different JobRepository implementation " + + "that supports batch meta-data storage for partitioned steps."); + } + JobExecution jobExecution = stepExecution.getJobExecution(); Map contexts = getContexts(stepExecution, gridSize); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitterTests.java index 17536392fe..88f59b6fe7 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitterTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/partition/support/SimpleStepExecutionSplitterTests.java @@ -36,6 +36,7 @@ import org.springframework.batch.core.partition.Partitioner; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.support.JdbcJobRepositoryFactoryBean; +import org.springframework.batch.core.repository.support.ResourcelessJobRepository; import org.springframework.batch.core.step.tasklet.TaskletStep; import org.springframework.batch.item.ExecutionContext; import org.springframework.jdbc.support.JdbcTransactionManager; @@ -44,6 +45,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class SimpleStepExecutionSplitterTests { @@ -219,6 +221,26 @@ void testAbandonedStatus() throws Exception { } } + @Test + void testResourcelessJobRepositoryThrowsException() throws Exception { + // Create a ResourcelessJobRepository for testing + ResourcelessJobRepository resourcelessRepo = new ResourcelessJobRepository(); + JobExecution jobExecution = resourcelessRepo.createJobExecution("job", new JobParameters()); + StepExecution stepExecution = jobExecution.createStepExecution("bar"); + + // Create splitter with ResourcelessJobRepository + SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(resourcelessRepo, true, step.getName(), + new SimplePartitioner()); + + // Verify that attempting to split with ResourcelessJobRepository throws + // appropriate exception + JobExecutionException exception = assertThrows(JobExecutionException.class, + () -> splitter.split(stepExecution, 2)); + + assertTrue(exception.getMessage().contains("ResourcelessJobRepository cannot be used with partitioned steps")); + assertTrue(exception.getMessage().contains("does not support execution context")); + } + private StepExecution update(Set split, StepExecution stepExecution, BatchStatus status) throws Exception { return update(split, stepExecution, status, true);