2.2.224
- 3.44.0.0
+ 3.45.1.010.16.1.12.18.132.31.2
- 4.0.4
- 2.22.0
+ 4.0.5
+ 2.22.18.0.1.Final5.0.14.0.2
@@ -119,15 +119,15 @@
4.0.12.0.26.5.1
- 1.9.20.1
- 8.2.0
- 3.3.0
- 42.7.0
+ 1.9.21.1
+ 8.3.0
+ 3.3.2
+ 42.7.211.5.8.0
- 19.21.0.0
+ 19.22.0.011.2.3.jre171.3.1
- 1.19.3
+ 1.19.51.5.1
diff --git a/spring-batch-bom/pom.xml b/spring-batch-bom/pom.xml
index a79875a89d..0ce9333623 100644
--- a/spring-batch-bom/pom.xml
+++ b/spring-batch-bom/pom.xml
@@ -4,7 +4,7 @@
org.springframework.batchspring-batch
- 5.1.1-SNAPSHOT
+ 5.1.4-SNAPSHOTspring-batch-bompom
diff --git a/spring-batch-core/pom.xml b/spring-batch-core/pom.xml
index 62b15ce662..c3499ed214 100644
--- a/spring-batch-core/pom.xml
+++ b/spring-batch-core/pom.xml
@@ -4,7 +4,7 @@
org.springframework.batchspring-batch
- 5.1.1-SNAPSHOT
+ 5.1.4-SNAPSHOTspring-batch-corejar
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/JobKeyGenerator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/JobKeyGenerator.java
index 589434b97f..147a26a37c 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/JobKeyGenerator.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/JobKeyGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2022 the original author or authors.
+ * Copyright 2013-2024 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.
@@ -21,9 +21,11 @@
*
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Taeik Lim
* @param The type of the source data used to calculate the key.
* @since 2.2
*/
+@FunctionalInterface
public interface JobKeyGenerator {
/**
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/JobParameters.java b/spring-batch-core/src/main/java/org/springframework/batch/core/JobParameters.java
index 36cc3a1d44..8f001e84c7 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/JobParameters.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/JobParameters.java
@@ -141,7 +141,7 @@ public String getString(String key, @Nullable String defaultValue) {
}
/**
- * Typesafe getter for the {@link Long} represented by the provided key.
+ * Typesafe getter for the {@link Double} represented by the provided key.
* @param key The key for which to get a value.
* @return The {@link Double} value or {@code null} if the key is absent.
*/
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java
index 3450f4894a..9727fa52a5 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java
@@ -105,6 +105,7 @@ public JobParametersBuilder addString(String key, @NonNull String parameter) {
* @return a reference to this object.
*/
public JobParametersBuilder addString(String key, @NonNull String parameter, boolean identifying) {
+ Assert.notNull(parameter, "Value for parameter '" + key + "' must not be null");
this.parameterMap.put(key, new JobParameter<>(parameter, String.class, identifying));
return this;
}
@@ -128,6 +129,7 @@ public JobParametersBuilder addDate(String key, @NonNull Date parameter) {
* @return a reference to this object.
*/
public JobParametersBuilder addDate(String key, @NonNull Date parameter, boolean identifying) {
+ Assert.notNull(parameter, "Value for parameter '" + key + "' must not be null");
this.parameterMap.put(key, new JobParameter<>(parameter, Date.class, identifying));
return this;
}
@@ -151,6 +153,7 @@ public JobParametersBuilder addLocalDate(String key, @NonNull LocalDate paramete
* @return a reference to this object.
*/
public JobParametersBuilder addLocalDate(String key, @NonNull LocalDate parameter, boolean identifying) {
+ Assert.notNull(parameter, "Value for parameter '" + key + "' must not be null");
this.parameterMap.put(key, new JobParameter<>(parameter, LocalDate.class, identifying));
return this;
}
@@ -174,6 +177,7 @@ public JobParametersBuilder addLocalTime(String key, @NonNull LocalTime paramete
* @return a reference to this object.
*/
public JobParametersBuilder addLocalTime(String key, @NonNull LocalTime parameter, boolean identifying) {
+ Assert.notNull(parameter, "Value for parameter '" + key + "' must not be null");
this.parameterMap.put(key, new JobParameter<>(parameter, LocalTime.class, identifying));
return this;
}
@@ -197,6 +201,7 @@ public JobParametersBuilder addLocalDateTime(String key, @NonNull LocalDateTime
* @return a reference to this object.
*/
public JobParametersBuilder addLocalDateTime(String key, @NonNull LocalDateTime parameter, boolean identifying) {
+ Assert.notNull(parameter, "Value for parameter '" + key + "' must not be null");
this.parameterMap.put(key, new JobParameter<>(parameter, LocalDateTime.class, identifying));
return this;
}
@@ -220,6 +225,7 @@ public JobParametersBuilder addLong(String key, @NonNull Long parameter) {
* @return a reference to this object.
*/
public JobParametersBuilder addLong(String key, @NonNull Long parameter, boolean identifying) {
+ Assert.notNull(parameter, "Value for parameter '" + key + "' must not be null");
this.parameterMap.put(key, new JobParameter<>(parameter, Long.class, identifying));
return this;
}
@@ -243,6 +249,7 @@ public JobParametersBuilder addDouble(String key, @NonNull Double parameter) {
* @return a reference to this object.
*/
public JobParametersBuilder addDouble(String key, @NonNull Double parameter, boolean identifying) {
+ Assert.notNull(parameter, "Value for parameter '" + key + "' must not be null");
this.parameterMap.put(key, new JobParameter<>(parameter, Double.class, identifying));
return this;
}
@@ -285,27 +292,28 @@ public JobParametersBuilder addJobParameter(String key, JobParameter> jobParam
/**
* Add a job parameter.
* @param name the name of the parameter
- * @param value the value of the parameter
+ * @param value the value of the parameter. Must not be {@code null}.
* @param type the type of the parameter
* @param identifying true if the parameter is identifying. false otherwise
* @return a reference to this object.
* @param the type of the parameter
* @since 5.0
*/
- public JobParametersBuilder addJobParameter(String name, T value, Class type, boolean identifying) {
+ public JobParametersBuilder addJobParameter(String name, @NonNull T value, Class type, boolean identifying) {
+ Assert.notNull(value, "Value for parameter '" + name + "' must not be null");
return addJobParameter(name, new JobParameter<>(value, type, identifying));
}
/**
* Add an identifying job parameter.
* @param name the name of the parameter
- * @param value the value of the parameter
+ * @param value the value of the parameter. Must not be {@code null}.
* @param type the type of the parameter
* @return a reference to this object.
* @param the type of the parameter
* @since 5.0
*/
- public JobParametersBuilder addJobParameter(String name, T value, Class type) {
+ public JobParametersBuilder addJobParameter(String name, @NonNull T value, Class type) {
return addJobParameter(name, value, type, true);
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java
index 0670560c94..69ffe907e9 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -40,6 +40,10 @@
* {@link JobRegistry}. Include a bean of this type along with your job configuration and
* use the same {@link JobRegistry} as a {@link JobLocator} when you need to locate a
* {@link Job} to launch.
+ *
+ * An alternative to this class is {@link JobRegistrySmartInitializingSingleton}, which is
+ * recommended in cases where this class may cause early bean initializations. You must
+ * include at most one of either of them as a bean.
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistrySmartInitializingSingleton.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistrySmartInitializingSingleton.java
new file mode 100644
index 0000000000..ede418cf23
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistrySmartInitializingSingleton.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2024 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.core.configuration.support;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.configuration.DuplicateJobException;
+import org.springframework.batch.core.configuration.JobLocator;
+import org.springframework.batch.core.configuration.JobRegistry;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.util.Assert;
+
+/**
+ * A {@link SmartInitializingSingleton} that registers {@link Job} beans with a
+ * {@link JobRegistry}. Include a bean of this type along with your job configuration and
+ * use the same {@link JobRegistry} as a {@link JobLocator} when you need to locate a
+ * {@link Job} to launch.
+ *
+ * This class is an alternative to {@link JobRegistryBeanPostProcessor} and prevents early
+ * bean initializations. You must include at most one of either of them as a bean.
+ *
+ * @author Henning Pöttker
+ * @since 5.1.1
+ */
+public class JobRegistrySmartInitializingSingleton
+ implements SmartInitializingSingleton, BeanFactoryAware, InitializingBean, DisposableBean {
+
+ private static final Log logger = LogFactory.getLog(JobRegistrySmartInitializingSingleton.class);
+
+ // It doesn't make sense for this to have a default value...
+ private JobRegistry jobRegistry = null;
+
+ private final Collection jobNames = new HashSet<>();
+
+ private String groupName = null;
+
+ private ListableBeanFactory beanFactory;
+
+ /**
+ * Default constructor.
+ */
+ public JobRegistrySmartInitializingSingleton() {
+ }
+
+ /**
+ * Convenience constructor for setting the {@link JobRegistry}.
+ * @param jobRegistry the {@link JobRegistry} to register the {@link Job}s with
+ */
+ public JobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) {
+ this.jobRegistry = jobRegistry;
+ }
+
+ /**
+ * The group name for jobs registered by this component. Optional (defaults to null,
+ * which means that jobs are registered with their bean names). Useful where there is
+ * a hierarchy of application contexts all contributing to the same
+ * {@link JobRegistry}: child contexts can then define an instance with a unique group
+ * name to avoid clashes between job names.
+ * @param groupName the groupName to set
+ */
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ /**
+ * Injection setter for {@link JobRegistry}.
+ * @param jobRegistry the {@link JobRegistry} to register the {@link Job}s with
+ */
+ public void setJobRegistry(JobRegistry jobRegistry) {
+ this.jobRegistry = jobRegistry;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ if (beanFactory instanceof ListableBeanFactory listableBeanFactory) {
+ this.beanFactory = listableBeanFactory;
+ }
+ }
+
+ /**
+ * Make sure the registry is set before use.
+ */
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ Assert.state(jobRegistry != null, "JobRegistry must not be null");
+ }
+
+ /**
+ * Unregister all the {@link Job} instances that were registered by this smart
+ * initializing singleton.
+ */
+ @Override
+ public void destroy() throws Exception {
+ for (String name : jobNames) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Unregistering job: " + name);
+ }
+ jobRegistry.unregister(name);
+ }
+ jobNames.clear();
+ }
+
+ @Override
+ public void afterSingletonsInstantiated() {
+ if (beanFactory == null) {
+ return;
+ }
+ Map jobs = beanFactory.getBeansOfType(Job.class, false, false);
+ for (var entry : jobs.entrySet()) {
+ postProcessAfterInitialization(entry.getValue(), entry.getKey());
+ }
+ }
+
+ private void postProcessAfterInitialization(Job job, String beanName) {
+ try {
+ String groupName = this.groupName;
+ if (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory
+ && beanFactory.containsBean(beanName)) {
+ groupName = getGroupName(defaultListableBeanFactory.getBeanDefinition(beanName), job);
+ }
+ job = groupName == null ? job : new GroupAwareJob(groupName, job);
+ ReferenceJobFactory jobFactory = new ReferenceJobFactory(job);
+ String name = jobFactory.getJobName();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Registering job: " + name);
+ }
+ jobRegistry.register(jobFactory);
+ jobNames.add(name);
+ }
+ catch (DuplicateJobException e) {
+ throw new FatalBeanException("Cannot register job configuration", e);
+ }
+ }
+
+ /**
+ * Determine a group name for the job to be registered. The default implementation
+ * returns the {@link #setGroupName(String) groupName} configured. Provides an
+ * extension point for specialised subclasses.
+ * @param beanDefinition the bean definition for the job
+ * @param job the job
+ * @return a group name for the job (or null if not needed)
+ */
+ protected String getGroupName(BeanDefinition beanDefinition, Job job) {
+ return groupName;
+ }
+
+}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java
index 7ea6394697..02eea1c0f1 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -414,8 +414,7 @@ protected static Collection createTransition(FlowExecutionStatus
endBuilder.addConstructorArgValue(abandon);
- String nextOnEnd = exitCodeExists ? null : next;
- endState = getStateTransitionReference(parserContext, endBuilder.getBeanDefinition(), null, nextOnEnd);
+ endState = getStateTransitionReference(parserContext, endBuilder.getBeanDefinition(), null, next);
next = endName;
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java
index 522175f62c..a9f671ca56 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -169,7 +169,11 @@ protected JobParameter> decode(String encodedJobParameter) {
}
private String parseValue(String encodedJobParameter) {
- return StringUtils.commaDelimitedListToStringArray(encodedJobParameter)[0];
+ String[] tokens = StringUtils.commaDelimitedListToStringArray(encodedJobParameter);
+ if (tokens.length == 0) {
+ return "";
+ }
+ return tokens[0];
}
private Class> parseType(String encodedJobParameter) {
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/SplitState.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/SplitState.java
index 0de11a3489..c9ab0c0152 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/SplitState.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/SplitState.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -19,6 +19,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
@@ -119,7 +120,7 @@ public FlowExecutionStatus handle(final FlowExecutor executor) throws Exception
FlowExecutionStatus parentSplitStatus = parentSplit == null ? null : parentSplit.handle(executor);
Collection results = new ArrayList<>();
-
+ List exceptions = new ArrayList<>();
// Could use a CompletionService here?
for (Future task : tasks) {
try {
@@ -129,14 +130,18 @@ public FlowExecutionStatus handle(final FlowExecutor executor) throws Exception
// Unwrap the expected exceptions
Throwable cause = e.getCause();
if (cause instanceof Exception) {
- throw (Exception) cause;
+ exceptions.add((Exception) cause);
}
else {
- throw e;
+ exceptions.add(e);
}
}
}
+ if (!exceptions.isEmpty()) {
+ throw exceptions.get(0);
+ }
+
FlowExecutionStatus flowExecutionStatus = doAggregation(results, executor);
if (parentSplitStatus != null) {
return Collections.max(Arrays.asList(flowExecutionStatus, parentSplitStatus));
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobOperatorFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobOperatorFactoryBean.java
index ce2ef8e4f2..a3c07375ec 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobOperatorFactoryBean.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobOperatorFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2023 the original author or authors.
+ * Copyright 2022-2024 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.
@@ -71,7 +71,7 @@ public class JobOperatorFactoryBean implements FactoryBean, Initial
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.transactionManager, "TransactionManager must not be null");
Assert.notNull(this.jobLauncher, "JobLauncher must not be null");
- Assert.notNull(this.jobRegistry, "JobLocator must not be null");
+ Assert.notNull(this.jobRegistry, "JobRegistry must not be null");
Assert.notNull(this.jobExplorer, "JobExplorer must not be null");
Assert.notNull(this.jobRepository, "JobRepository must not be null");
if (this.transactionAttributeSource == null) {
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java
index 5c53a6f4a0..037ad7706c 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;
@@ -97,7 +98,6 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implement
FROM %PREFIX%JOB_EXECUTION JE
JOIN %PREFIX%STEP_EXECUTION SE ON SE.JOB_EXECUTION_ID = JE.JOB_EXECUTION_ID
WHERE JE.JOB_INSTANCE_ID = ? AND SE.STEP_NAME = ?
- ORDER BY SE.CREATE_TIME DESC, SE.STEP_EXECUTION_ID DESC
""";
private static final String CURRENT_VERSION_STEP_EXECUTION = """
@@ -117,6 +117,10 @@ SELECT COUNT(*)
WHERE STEP_EXECUTION_ID = ?
""";
+ private static final Comparator BY_CREATE_TIME_DESC_ID_DESC = Comparator
+ .comparing(StepExecution::getCreateTime, Comparator.reverseOrder())
+ .thenComparing(StepExecution::getId, Comparator.reverseOrder());
+
private int exitMessageLength = DEFAULT_EXIT_MESSAGE_LENGTH;
private DataFieldMaxValueIncrementer stepExecutionIncrementer;
@@ -277,10 +281,9 @@ public void updateStepExecution(StepExecution stepExecution) {
stepExecution.getWriteSkipCount(), stepExecution.getRollbackCount(), lastUpdated,
stepExecution.getId(), stepExecution.getVersion() };
int count = getJdbcTemplate().update(getQuery(UPDATE_STEP_EXECUTION), parameters,
- new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.INTEGER,
- Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.INTEGER,
- Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.BIGINT,
- Types.INTEGER });
+ new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.BIGINT, Types.BIGINT,
+ Types.BIGINT, Types.BIGINT, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.BIGINT,
+ Types.BIGINT, Types.BIGINT, Types.BIGINT, Types.TIMESTAMP, Types.BIGINT, Types.INTEGER });
// Avoid concurrent modifications...
if (count == 0) {
@@ -348,6 +351,7 @@ public StepExecution getLastStepExecution(JobInstance jobInstance, String stepNa
jobExecution.setVersion(rs.getInt(27));
return new StepExecutionRowMapper(jobExecution).mapRow(rs, rowNum);
}, jobInstance.getInstanceId(), stepName);
+ executions.sort(BY_CREATE_TIME_DESC_ID_DESC);
if (executions.isEmpty()) {
return null;
}
@@ -390,15 +394,15 @@ public StepExecution mapRow(ResultSet rs, int rowNum) throws SQLException {
stepExecution.setStartTime(rs.getTimestamp(3) == null ? null : rs.getTimestamp(3).toLocalDateTime());
stepExecution.setEndTime(rs.getTimestamp(4) == null ? null : rs.getTimestamp(4).toLocalDateTime());
stepExecution.setStatus(BatchStatus.valueOf(rs.getString(5)));
- stepExecution.setCommitCount(rs.getInt(6));
- stepExecution.setReadCount(rs.getInt(7));
- stepExecution.setFilterCount(rs.getInt(8));
- stepExecution.setWriteCount(rs.getInt(9));
+ stepExecution.setCommitCount(rs.getLong(6));
+ stepExecution.setReadCount(rs.getLong(7));
+ stepExecution.setFilterCount(rs.getLong(8));
+ stepExecution.setWriteCount(rs.getLong(9));
stepExecution.setExitStatus(new ExitStatus(rs.getString(10), rs.getString(11)));
- stepExecution.setReadSkipCount(rs.getInt(12));
- stepExecution.setWriteSkipCount(rs.getInt(13));
- stepExecution.setProcessSkipCount(rs.getInt(14));
- stepExecution.setRollbackCount(rs.getInt(15));
+ stepExecution.setReadSkipCount(rs.getLong(12));
+ stepExecution.setWriteSkipCount(rs.getLong(13));
+ stepExecution.setProcessSkipCount(rs.getLong(14));
+ stepExecution.setRollbackCount(rs.getLong(15));
stepExecution.setLastUpdated(rs.getTimestamp(16) == null ? null : rs.getTimestamp(16).toLocalDateTime());
stepExecution.setVersion(rs.getInt(17));
stepExecution.setCreateTime(rs.getTimestamp(18) == null ? null : rs.getTimestamp(18).toLocalDateTime());
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java
index 44f8bf6eca..e98752c987 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -328,7 +328,7 @@ public void deleteJobExecution(JobExecution jobExecution) {
@Override
public void deleteJobInstance(JobInstance jobInstance) {
- List jobExecutions = this.jobExecutionDao.findJobExecutions(jobInstance);
+ List jobExecutions = findJobExecutions(jobInstance);
for (JobExecution jobExecution : jobExecutions) {
deleteJobExecution(jobExecution);
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java
index 05d9f13906..36ce5918a6 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java
@@ -49,6 +49,7 @@
* @author Dave Syer
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Ilpyo Yang
* @since 2.2
* @param the type of builder represented
*/
@@ -74,6 +75,23 @@ public AbstractTaskletStepBuilder(StepBuilderHelper> parent) {
super(parent);
}
+ /**
+ * Create a new builder initialized with any properties in the parent. The parent is
+ * copied, so it can be re-used.
+ * @param parent a parent helper containing common step properties
+ */
+ public AbstractTaskletStepBuilder(AbstractTaskletStepBuilder> parent) {
+ super(parent);
+ this.chunkListeners = parent.chunkListeners;
+ this.stepOperations = parent.stepOperations;
+ this.transactionManager = parent.transactionManager;
+ this.transactionAttribute = parent.transactionAttribute;
+ this.streams.addAll(parent.streams);
+ this.exceptionHandler = parent.exceptionHandler;
+ this.throttleLimit = parent.throttleLimit;
+ this.taskExecutor = parent.taskExecutor;
+ }
+
protected abstract Tasklet createTasklet();
/**
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/SimpleStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/SimpleStepBuilder.java
index aa51c34e54..088b17ca5f 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/SimpleStepBuilder.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/SimpleStepBuilder.java
@@ -114,7 +114,6 @@ protected SimpleStepBuilder(SimpleStepBuilder parent) {
this.itemListeners = parent.itemListeners;
this.readerTransactionalQueue = parent.readerTransactionalQueue;
this.meterRegistry = parent.meterRegistry;
- this.transactionManager(parent.getTransactionManager());
}
public FaultTolerantStepBuilder faultTolerant() {
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java
index f1eb7321f5..ecb797111c 100755
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -306,7 +306,9 @@ else if (shouldSkip(itemProcessSkipPolicy, e, contribution.getStepSkipCount()))
break;
}
}
-
+ if (inputs.isEnd()) {
+ outputs.setEnd();
+ }
return outputs;
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java
index 813b6eb403..101945bb22 100755
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2022 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -340,6 +340,9 @@ protected Chunk transform(StepContribution contribution, Chunk inputs) thr
iterator.remove();
}
}
+ if (inputs.isEnd()) {
+ outputs.setEnd();
+ }
return outputs;
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java
index b35a228a4d..4499279e8e 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -60,6 +60,7 @@
* @author Robert Kasanicky
* @author Will Schipp
* @author Mahmoud Ben Hassine
+ * @author Injae Kim
*/
public class SystemCommandTasklet implements StepExecutionListener, StoppableTasklet, InitializingBean {
@@ -121,8 +122,15 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon
}
if (systemCommandTask.isDone()) {
- contribution.setExitStatus(systemProcessExitCodeMapper.getExitStatus(systemCommandTask.get()));
- return RepeatStatus.FINISHED;
+ Integer exitCode = systemCommandTask.get();
+ ExitStatus exitStatus = systemProcessExitCodeMapper.getExitStatus(exitCode);
+ contribution.setExitStatus(exitStatus);
+ if (ExitStatus.FAILED.equals(exitStatus)) {
+ throw new SystemCommandException("Execution of system command failed with exit code " + exitCode);
+ }
+ else {
+ return RepeatStatus.FINISHED;
+ }
}
else if (System.currentTimeMillis() - t0 > timeout) {
systemCommandTask.cancel(interruptOnCancel);
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java
index b4ede49ed4..446daf7490 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java
@@ -430,7 +430,9 @@ public RepeatStatus doInTransaction(TransactionStatus status) {
try {
// Going to attempt a commit. If it fails this flag will
// stay false and we can use that later.
- getJobRepository().updateExecutionContext(stepExecution);
+ if (stepExecution.getExecutionContext().isDirty()) {
+ getJobRepository().updateExecutionContext(stepExecution);
+ }
stepExecution.incrementCommitCount();
if (logger.isDebugEnabled()) {
logger.debug("Saving step execution before commit: " + stepExecution);
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java
index 2f5cdb5d6e..220dfc4724 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2023 the original author or authors.
+ * Copyright 2008-2024 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.
@@ -22,6 +22,7 @@
import java.util.Map;
import java.util.Set;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -85,6 +86,13 @@ void testAddingExistingJobParameters() {
assertEquals(finalParams.getString("baz"), "quix");
}
+ @Test
+ void testAddingNullJobParameters() {
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+ () -> new JobParametersBuilder().addString("foo", null).toJobParameters());
+ Assertions.assertEquals("Value for parameter 'foo' must not be null", exception.getMessage());
+ }
+
@Test
void testNonIdentifyingParameters() {
this.parametersBuilder.addDate("SCHEDULE_DATE", date, false);
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/JobRegistrySmartInitializingSingletonTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/JobRegistrySmartInitializingSingletonTests.java
new file mode 100644
index 0000000000..f6db1e0187
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/JobRegistrySmartInitializingSingletonTests.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2024 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.core.configuration.support;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.configuration.DuplicateJobException;
+import org.springframework.batch.core.configuration.JobRegistry;
+import org.springframework.batch.core.job.JobSupport;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+
+/**
+ * @author Henning Pöttker
+ * @author Mahmoud Ben Hassine
+ */
+class JobRegistrySmartInitializingSingletonTests {
+
+ private final JobRegistry jobRegistry = new MapJobRegistry();
+
+ private final JobRegistrySmartInitializingSingleton singleton = new JobRegistrySmartInitializingSingleton(
+ jobRegistry);
+
+ private final ListableBeanFactory beanFactory = mock(ListableBeanFactory.class);
+
+ @BeforeEach
+ void setUp() {
+ var job = new JobSupport();
+ job.setName("foo");
+ lenient().when(beanFactory.getBeansOfType(Job.class, false, false)).thenReturn(Map.of("bar", job));
+ singleton.setBeanFactory(beanFactory);
+ }
+
+ @Test
+ void testInitializationFails() {
+ singleton.setJobRegistry(null);
+ var exception = assertThrows(IllegalStateException.class, singleton::afterPropertiesSet);
+ assertEquals("JobRegistry must not be null", exception.getMessage());
+ }
+
+ @Test
+ void testAfterSingletonsInstantiated() {
+ singleton.afterSingletonsInstantiated();
+ Collection jobNames = jobRegistry.getJobNames();
+ assertEquals(1, jobNames.size());
+ assertEquals("foo", jobNames.iterator().next());
+ }
+
+ @Test
+ void testAfterSingletonsInstantiatedWithGroupName() {
+ singleton.setGroupName("jobs");
+ singleton.afterSingletonsInstantiated();
+ Collection jobNames = jobRegistry.getJobNames();
+ assertEquals(1, jobNames.size());
+ assertEquals("jobs.foo", jobNames.iterator().next());
+ }
+
+ @Test
+ void testAfterSingletonsInstantiatedWithDuplicate() {
+ singleton.afterSingletonsInstantiated();
+ var exception = assertThrows(FatalBeanException.class, singleton::afterSingletonsInstantiated);
+ assertInstanceOf(DuplicateJobException.class, exception.getCause());
+ }
+
+ @Test
+ void testUnregisterOnDestroy() throws Exception {
+ singleton.afterSingletonsInstantiated();
+ singleton.destroy();
+ assertTrue(jobRegistry.getJobNames().isEmpty());
+ }
+
+ @Test
+ void testExecutionWithApplicationContext() throws Exception {
+ var context = new ClassPathXmlApplicationContext("test-context-with-smart-initializing-singleton.xml",
+ getClass());
+ var registry = context.getBean("registry", JobRegistry.class);
+ Collection jobNames = registry.getJobNames();
+ String[] names = context.getBeanNamesForType(JobSupport.class);
+ int count = names.length;
+ // Each concrete bean of type JobConfiguration is registered...
+ assertEquals(count, jobNames.size());
+ // N.B. there is a failure / wonky mode where a parent bean is given an
+ // explicit name or beanName (using property setter): in this case then
+ // child beans will have the same name and will be re-registered (and
+ // override, if the registry supports that).
+ assertNotNull(registry.getJob("test-job"));
+ assertEquals(context.getBean("test-job-with-name"), registry.getJob("foo"));
+ assertEquals(context.getBean("test-job-with-bean-name"), registry.getJob("bar"));
+ assertEquals(context.getBean("test-job-with-parent-and-name"), registry.getJob("spam"));
+ assertEquals(context.getBean("test-job-with-parent-and-bean-name"), registry.getJob("bucket"));
+ assertEquals(context.getBean("test-job-with-concrete-parent"), registry.getJob("maps"));
+ assertEquals(context.getBean("test-job-with-concrete-parent-and-name"), registry.getJob("oof"));
+ assertEquals(context.getBean("test-job-with-concrete-parent-and-bean-name"), registry.getJob("rab"));
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests.java
index b8fe6dedfb..525e2ba2a1 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2022 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -33,8 +33,6 @@
*
*/
@SpringJUnitConfig
-// FIXME this test fails when upgrading the batch xsd from 2.2 to 3.0:
-// https://github.com/spring-projects/spring-batch/issues/1287
class StopAndRestartFailedJobParserTests extends AbstractJobParserTests {
@Test
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests.java
index 1702b6f1a3..6f30120f30 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2022 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -29,8 +29,6 @@
*
*/
@SpringJUnitConfig
-// FIXME this test fails when upgrading the batch xsd from 2.2 to 3.0:
-// https://github.com/spring-projects/spring-batch/issues/1287
class StopAndRestartJobParserTests extends AbstractJobParserTests {
@Test
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartWithCustomExitCodeJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartWithCustomExitCodeJobParserTests.java
new file mode 100644
index 0000000000..375e21bb27
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopAndRestartWithCustomExitCodeJobParserTests.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 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.core.configuration.xml;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.BatchStatus;
+import org.springframework.batch.core.ExitStatus;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.StepExecution;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Henning Pöttker
+ */
+@SpringJUnitConfig
+class StopAndRestartWithCustomExitCodeJobParserTests extends AbstractJobParserTests {
+
+ @Test
+ void testStopIncomplete() throws Exception {
+
+ //
+ // First Launch
+ //
+ JobExecution jobExecution = createJobExecution();
+ job.execute(jobExecution);
+ assertEquals(1, stepNamesList.size());
+ assertEquals("[s1]", stepNamesList.toString());
+
+ assertEquals(BatchStatus.STOPPED, jobExecution.getStatus());
+ assertEquals("CUSTOM", jobExecution.getExitStatus().getExitCode());
+
+ StepExecution stepExecution1 = getStepExecution(jobExecution, "s1");
+ assertEquals(BatchStatus.COMPLETED, stepExecution1.getStatus());
+ assertEquals(ExitStatus.COMPLETED.getExitCode(), stepExecution1.getExitStatus().getExitCode());
+
+ //
+ // Second Launch
+ //
+ stepNamesList.clear();
+ jobExecution = createJobExecution();
+ job.execute(jobExecution);
+ assertEquals(1, stepNamesList.size()); // step1 is not executed
+ assertEquals("[s2]", stepNamesList.toString());
+
+ assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
+ assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
+
+ StepExecution stepExecution2 = getStepExecution(jobExecution, "s2");
+ assertEquals(BatchStatus.COMPLETED, stepExecution2.getStatus());
+ assertEquals(ExitStatus.COMPLETED, stepExecution2.getExitStatus());
+
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests.java
index f0fe245e14..e824cd75d9 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2022 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -29,8 +29,6 @@
*
*/
@SpringJUnitConfig
-// FIXME this test fails when upgrading the batch xsd from 2.2 to 3.0:
-// https://github.com/spring-projects/spring-batch/issues/1287
class StopCustomStatusJobParserTests extends AbstractJobParserTests {
@Test
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests.java
index e6ddfee766..b5e4b8183a 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2022 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -29,8 +29,6 @@
*
*/
@SpringJUnitConfig
-// FIXME this test fails when upgrading the batch xsd from 2.2 to 3.0:
-// https://github.com/spring-projects/spring-batch/issues/1287
class StopIncompleteJobParserTests extends AbstractJobParserTests {
@Test
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopJobParserTests.java
index b2f0d75c71..f05c0b6cba 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopJobParserTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StopJobParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2022 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -34,8 +34,6 @@
*
*/
@SpringJUnitConfig
-// FIXME this test fails when upgrading the batch xsd from 2.2 to 3.0:
-// https://github.com/spring-projects/spring-batch/issues/1287
class StopJobParserTests extends AbstractJobParserTests {
@Test
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java
index e413162a41..d39b9ad5eb 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -20,6 +20,7 @@
import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.util.StringUtils;
@@ -129,6 +130,22 @@ void testGetParametersWithBogusLong() {
}
}
+ @Test
+ void testGetParametersWithEmptyValue() {
+ // given
+ String[] args = new String[] { "parameter=" };
+
+ // when
+ JobParameters jobParameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
+
+ // then
+ assertEquals(1, jobParameters.getParameters().size());
+ JobParameter> parameter = jobParameters.getParameters().get("parameter");
+ assertEquals("", parameter.getValue());
+ assertEquals(String.class, parameter.getType());
+ assertTrue(parameter.isIdentifying());
+ }
+
@Test
void testGetParametersWithDoubleValueDeclaredAsLong() {
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/FlowJobBuilderTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/FlowJobBuilderTests.java
index 14e61a58a0..c3dea9e369 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/FlowJobBuilderTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/FlowJobBuilderTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2023 the original author or authors.
+ * Copyright 2012-2024 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.
@@ -16,11 +16,14 @@
package org.springframework.batch.core.job.builder;
import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.BatchStatus;
@@ -45,6 +48,8 @@
import org.springframework.batch.core.step.StepSupport;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.batch.repeat.RepeatStatus;
+import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -369,4 +374,38 @@ public JdbcTransactionManager transactionManager(DataSource dataSource) {
}
+ @Test
+ public void testBuildSplitWithParallelFlow() throws InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ Step longExecutingStep = new StepBuilder("longExecutingStep", jobRepository).tasklet((stepContribution, b) -> {
+ Thread.sleep(500L);
+ return RepeatStatus.FINISHED;
+ }, new ResourcelessTransactionManager()).build();
+
+ Step interruptedStep = new StepBuilder("interruptedStep", jobRepository).tasklet((stepContribution, b) -> {
+ stepContribution.getStepExecution().setTerminateOnly();
+ return RepeatStatus.FINISHED;
+ }, new ResourcelessTransactionManager()).build();
+
+ Step nonExecutableStep = new StepBuilder("nonExecutableStep", jobRepository).tasklet((stepContribution, b) -> {
+ countDownLatch.countDown();
+ return RepeatStatus.FINISHED;
+ }, new ResourcelessTransactionManager()).build();
+
+ Flow twoStepFlow = new FlowBuilder("twoStepFlow").start(longExecutingStep)
+ .next(nonExecutableStep)
+ .build();
+ Flow interruptedFlow = new FlowBuilder("interruptedFlow").start(interruptedStep).build();
+
+ Flow splitFlow = new FlowBuilder("splitFlow").split(new SimpleAsyncTaskExecutor())
+ .add(interruptedFlow, twoStepFlow)
+ .build();
+ FlowJobBuilder jobBuilder = new JobBuilder("job", jobRepository).start(splitFlow).build();
+ jobBuilder.preventRestart().build().execute(execution);
+
+ boolean isExecutedNonExecutableStep = countDownLatch.await(1, TimeUnit.SECONDS);
+ assertEquals(BatchStatus.STOPPED, execution.getStatus());
+ Assertions.assertFalse(isExecutedNonExecutableStep);
+ }
+
}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java
index 53c33b178e..eb81dbd246 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2022 the original author or authors.
+ * Copyright 2008-2024 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.
@@ -32,12 +32,13 @@
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
-import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.fail;
/**
* Repository tests using JDBC DAOs (rather than mocks).
@@ -152,11 +153,7 @@ void testGetStepExecutionCountAndLastStepExecution() throws Exception {
@Transactional
@Test
void testSaveExecutionContext() throws Exception {
- ExecutionContext ctx = new ExecutionContext() {
- {
- putLong("crashedPosition", 7);
- }
- };
+ ExecutionContext ctx = new ExecutionContext(Map.of("crashedPosition", 7));
JobExecution jobExec = jobRepository.createJobExecution(job.getName(), jobParameters);
jobExec.setStartTime(LocalDateTime.now());
jobExec.setExecutionContext(ctx);
@@ -169,11 +166,6 @@ void testSaveExecutionContext() throws Exception {
StepExecution retrievedStepExec = jobRepository.getLastStepExecution(jobExec.getJobInstance(), step.getName());
assertEquals(stepExec, retrievedStepExec);
assertEquals(ctx, retrievedStepExec.getExecutionContext());
-
- // JobExecution retrievedJobExec =
- // jobRepository.getLastJobExecution(jobExec.getJobInstance());
- // assertEquals(jobExec, retrievedJobExec);
- // assertEquals(ctx, retrievedJobExec.getExecutionContext());
}
/*
@@ -205,7 +197,7 @@ void testGetLastJobExecution() throws Exception {
jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
StepExecution stepExecution = new StepExecution("step1", jobExecution);
jobRepository.add(stepExecution);
- jobExecution.addStepExecutions(Arrays.asList(stepExecution));
+ jobExecution.addStepExecutions(List.of(stepExecution));
assertEquals(jobExecution, jobRepository.getLastJobExecution(job.getName(), jobParameters));
assertEquals(stepExecution, jobExecution.getStepExecutions().iterator().next());
}
@@ -233,42 +225,41 @@ void testReExecuteWithSameJobParameters() throws Exception {
*/
@Transactional
@Test
- public void testReExecuteWithSameJobParametersWhenRunning() throws Exception {
+ void testReExecuteWithSameJobParametersWhenRunning() throws Exception {
JobParameters jobParameters = new JobParametersBuilder().addString("stringKey", "stringValue")
.toJobParameters();
// jobExecution with status STARTING
JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
- try {
- jobRepository.createJobExecution(job.getName(), jobParameters);
- fail();
- }
- catch (JobExecutionAlreadyRunningException e) {
- // expected
- }
+ assertThrows(JobExecutionAlreadyRunningException.class,
+ () -> jobRepository.createJobExecution(job.getName(), jobParameters));
// jobExecution with status STARTED
jobExecution.setStatus(BatchStatus.STARTED);
jobExecution.setStartTime(LocalDateTime.now());
jobRepository.update(jobExecution);
- try {
- jobRepository.createJobExecution(job.getName(), jobParameters);
- fail();
- }
- catch (JobExecutionAlreadyRunningException e) {
- // expected
- }
+ assertThrows(JobExecutionAlreadyRunningException.class,
+ () -> jobRepository.createJobExecution(job.getName(), jobParameters));
// jobExecution with status STOPPING
jobExecution.setStatus(BatchStatus.STOPPING);
jobRepository.update(jobExecution);
- try {
- jobRepository.createJobExecution(job.getName(), jobParameters);
- fail();
- }
- catch (JobExecutionAlreadyRunningException e) {
- // expected
- }
+ assertThrows(JobExecutionAlreadyRunningException.class,
+ () -> jobRepository.createJobExecution(job.getName(), jobParameters));
+ }
+
+ @Transactional
+ @Test
+ void testDeleteJobInstance() throws Exception {
+ var jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters();
+ var jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
+ var stepExecution = new StepExecution("step", jobExecution);
+ jobRepository.add(stepExecution);
+
+ jobRepository.deleteJobInstance(jobExecution.getJobInstance());
+
+ assertEquals(0, jobRepository.findJobInstancesByName(job.getName(), 0, 1).size());
+ assertNull(jobRepository.getLastJobExecution(job.getName(), jobParameters));
}
}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilderTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilderTests.java
new file mode 100644
index 0000000000..6cd6f2374e
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilderTests.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2024 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.core.step.builder;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.batch.core.repository.JobRepository;
+import org.springframework.batch.core.step.tasklet.TaskletStep;
+import org.springframework.batch.item.ItemProcessor;
+import org.springframework.batch.item.ItemReader;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate;
+import org.springframework.core.task.SimpleAsyncTaskExecutor;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.transaction.PlatformTransactionManager;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test cases for verifying the {@link AbstractTaskletStepBuilder} and faultTolerant()
+ * functionality.
+ *
+ * Issue: https://github.com/spring-projects/spring-batch/issues/4438
+ *
+ * @author Ilpyo Yang
+ * @author Mahmoud Ben Hassine
+ */
+public class AbstractTaskletStepBuilderTests {
+
+ private final JobRepository jobRepository = mock(JobRepository.class);
+
+ private final PlatformTransactionManager transactionManager = mock(PlatformTransactionManager.class);
+
+ private final int chunkSize = 10;
+
+ private final ItemReader itemReader = mock(ItemReader.class);
+
+ private final ItemProcessor itemProcessor = mock(ItemProcessor.class);
+
+ private final ItemWriter itemWriter = mock(ItemWriter.class);
+
+ private final SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
+
+ @Test
+ void testSetTaskExecutorBeforeFaultTolerant() {
+ TaskletStep step = new StepBuilder("step-name", jobRepository)
+ .chunk(chunkSize, transactionManager)
+ .taskExecutor(taskExecutor)
+ .reader(itemReader)
+ .processor(itemProcessor)
+ .writer(itemWriter)
+ .faultTolerant()
+ .build();
+
+ Object stepOperations = ReflectionTestUtils.getField(step, "stepOperations");
+ assertInstanceOf(TaskExecutorRepeatTemplate.class, stepOperations);
+ }
+
+ @Test
+ void testSetTaskExecutorAfterFaultTolerant() {
+ TaskletStep step = new StepBuilder("step-name", jobRepository)
+ .chunk(chunkSize, transactionManager)
+ .reader(itemReader)
+ .processor(itemProcessor)
+ .writer(itemWriter)
+ .faultTolerant()
+ .taskExecutor(taskExecutor)
+ .build();
+
+ Object stepOperations = ReflectionTestUtils.getField(step, "stepOperations");
+ assertInstanceOf(TaskExecutorRepeatTemplate.class, stepOperations);
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java
index 5070b277a4..d30e06917e 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2023 the original author or authors.
+ * Copyright 2008-2024 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.
@@ -18,6 +18,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.ArrayList;
@@ -97,6 +98,16 @@ public String process(String item) throws Exception {
assertEquals(1, contribution.getFilterCount());
}
+ @Test
+ void testTransformChunkEnd() throws Exception {
+ Chunk inputs = new Chunk<>(Arrays.asList("1", "2"));
+ inputs.setEnd();
+ processor.initializeUserData(inputs);
+ Chunk outputs = processor.transform(contribution, inputs);
+ assertEquals(Arrays.asList("1", "2"), outputs.getItems());
+ assertTrue(outputs.isEnd());
+ }
+
@Test
void testFilterCountOnSkip() throws Exception {
processor.setProcessSkipPolicy(new AlwaysSkipItemSkipPolicy());
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java
index 84cb8a0449..9f5ec22ea7 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -61,10 +61,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
/**
* @author Dave Syer
* @author Mahmoud Ben Hassine
+ * @author jojoldu
*
*/
class FaultTolerantStepFactoryBeanRetryTests {
@@ -134,7 +136,7 @@ void testType() {
@SuppressWarnings("cast")
@Test
void testDefaultValue() throws Exception {
- assertTrue(factory.getObject() instanceof Step);
+ assertInstanceOf(Step.class, factory.getObject());
}
@Test
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/RepeatOperationsStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/RepeatOperationsStepFactoryBeanTests.java
index 88c2982aa3..f476e4e72c 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/RepeatOperationsStepFactoryBeanTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/RepeatOperationsStepFactoryBeanTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -35,10 +35,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
/**
* @author Dave Syer
* @author Mahmoud Ben Hassine
+ * @author jojoldu
*
*/
class RepeatOperationsStepFactoryBeanTests {
@@ -66,7 +68,7 @@ void testType() {
@Test
@SuppressWarnings("cast")
void testDefaultValue() throws Exception {
- assertTrue(factory.getObject() instanceof Step);
+ assertInstanceOf(Step.class, factory.getObject());
}
@Test
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleChunkProcessorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleChunkProcessorTests.java
index e9a7e0e678..5ebcb49ced 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleChunkProcessorTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleChunkProcessorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2023 the original author or authors.
+ * Copyright 2008-2024 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.
@@ -16,6 +16,7 @@
package org.springframework.batch.core.step.item;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
@@ -76,4 +77,15 @@ void testProcess() throws Exception {
assertEquals(2, contribution.getWriteCount());
}
+ @Test
+ void testTransform() throws Exception {
+ Chunk inputs = new Chunk<>();
+ inputs.add("foo");
+ inputs.add("bar");
+ inputs.setEnd();
+ Chunk outputs = processor.transform(contribution, inputs);
+ assertEquals(Arrays.asList("foo", "bar"), outputs.getItems());
+ assertTrue(outputs.isEnd());
+ }
+
}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java
index 006d9ed877..b703d91ee3 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2023 the original author or authors.
+ * Copyright 2008-2024 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.
@@ -323,9 +323,8 @@ public void testExecuteWithFailedCommandRunnerMockExecution() throws Exception {
tasklet.setCommand(command);
tasklet.afterPropertiesSet();
- RepeatStatus exitStatus = tasklet.execute(stepContribution, null);
-
- assertEquals(RepeatStatus.FINISHED, exitStatus);
+ Exception exception = assertThrows(SystemCommandException.class, () -> tasklet.execute(stepContribution, null));
+ assertTrue(exception.getMessage().contains("failed with exit code"));
assertEquals(ExitStatus.FAILED, stepContribution.getExitStatus());
}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/test/repository/SQLServerJobRepositoryIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/test/repository/SQLServerJobRepositoryIntegrationTests.java
index 304c7abc66..b8373688f1 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/test/repository/SQLServerJobRepositoryIntegrationTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/test/repository/SQLServerJobRepositoryIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2023 the original author or authors.
+ * Copyright 2020-2024 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.
@@ -58,7 +58,7 @@ class SQLServerJobRepositoryIntegrationTests {
// TODO find the best way to externalize and manage image versions
private static final DockerImageName SQLSERVER_IMAGE = DockerImageName
- .parse("mcr.microsoft.com/mssql/server:2019-CU11-ubuntu-20.04");
+ .parse("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04");
@Container
public static MSSQLServerContainer> sqlserver = new MSSQLServerContainer<>(SQLSERVER_IMAGE).acceptLicense();
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/test-context-with-smart-initializing-singleton.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/test-context-with-smart-initializing-singleton.xml
new file mode 100644
index 0000000000..64ae6eed68
--- /dev/null
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/support/test-context-with-smart-initializing-singleton.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests-context.xml
index 189d63ce13..97df1bef5e 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests-context.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests-context.xml
@@ -2,7 +2,7 @@
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests-context.xml
index fe7ed075ed..de0face964 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests-context.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartJobParserTests-context.xml
@@ -1,7 +1,7 @@
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartWithCustomExitCodeJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartWithCustomExitCodeJobParserTests-context.xml
new file mode 100644
index 0000000000..dba05231c4
--- /dev/null
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartWithCustomExitCodeJobParserTests-context.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml
index fbb7f4c6a0..93b0a1b4ea 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml
@@ -1,7 +1,7 @@
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests-context.xml
index ca269dec17..080f44a374 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests-context.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopIncompleteJobParserTests-context.xml
@@ -1,7 +1,7 @@
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopJobParserTests-context.xml
index 0f67bf801d..5be5d43f6b 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopJobParserTests-context.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopJobParserTests-context.xml
@@ -1,7 +1,7 @@
diff --git a/spring-batch-docs/antora-playbook.yml b/spring-batch-docs/antora-playbook.yml
index 037f9e80d6..bbf8fac2a9 100644
--- a/spring-batch-docs/antora-playbook.yml
+++ b/spring-batch-docs/antora-playbook.yml
@@ -4,9 +4,10 @@
antora:
extensions:
- '@springio/antora-extensions/partial-build-extension'
+ - '@antora/atlas-extension'
+ - require: '@springio/antora-extensions/latest-version-extension'
- require: '@springio/antora-extensions/inject-collector-cache-config-extension'
- '@antora/collector-extension'
- - '@antora/atlas-extension'
- require: '@springio/antora-extensions/root-component-extension'
root_component_name: 'batch'
- '@springio/antora-extensions/static-page-extension'
@@ -37,5 +38,5 @@ runtime:
format: pretty
ui:
bundle:
- url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.7/ui-bundle.zip
+ url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip
snapshot: true
\ No newline at end of file
diff --git a/spring-batch-docs/modules/ROOT/pages/appendix.adoc b/spring-batch-docs/modules/ROOT/pages/appendix.adoc
index 12de632126..5d2151f077 100644
--- a/spring-batch-docs/modules/ROOT/pages/appendix.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/appendix.adoc
@@ -42,7 +42,7 @@ This reader stores message offsets in the execution context to support restart c
|`ItemReaderAdapter`|Adapts any class to the
`ItemReader` interface.|Yes
|`JdbcCursorItemReader`|Reads from a database cursor over JDBC. See
- link:readersAndWriters.html#cursorBasedItemReaders["`Cursor-based ItemReaders`"].|No
+ link:readers-and-writers/database.html#cursorBasedItemReaders["`Cursor-based ItemReaders`"].|No
|`JdbcPagingItemReader`|Given an SQL statement, pages through the rows,
such that large datasets can be read without running out of
memory.|Yes
diff --git a/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc b/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc
index 94fc236f5c..bd41a5d941 100644
--- a/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc
@@ -173,9 +173,9 @@ The following example shows how to include a `JobRegistry` for a job defined in
====
-You can populate a `JobRegistry` in either of two ways: by using
-a bean post processor or by using a registrar lifecycle component. The coming
-sections describe these two mechanisms.
+You can populate a `JobRegistry` in one of the following ways: by using
+a bean post processor, or by using a smart initializing singleton or by using
+a registrar lifecycle component. The coming sections describe these mechanisms.
[[jobregistrybeanpostprocessor]]
=== JobRegistryBeanPostProcessor
@@ -224,6 +224,40 @@ there to also be registered automatically.
As of version 5.1, the `@EnableBatchProcessing` annotation automatically registers a `jobRegistryBeanPostProcessor` bean in the application context.
+[[jobregistrysmartinitializingsingleton]]
+=== JobRegistrySmartInitializingSingleton
+
+This is a `SmartInitializingSingleton` that registers all singleton jobs within the job registry.
+
+[tabs]
+====
+Java::
++
+The following example shows how to define a `JobRegistrySmartInitializingSingleton` in Java:
++
+.Java Configuration
+[source, java]
+----
+@Bean
+public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) {
+ return new JobRegistrySmartInitializingSingleton(jobRegistry);
+}
+----
+
+XML::
++
+The following example shows how to define a `JobRegistrySmartInitializingSingleton` in XML:
++
+.XML Configuration
+[source, xml]
+----
+
+
+
+----
+
+====
+
[[automaticjobregistrar]]
=== AutomaticJobRegistrar
diff --git a/spring-batch-docs/modules/ROOT/pages/job/java-config.adoc b/spring-batch-docs/modules/ROOT/pages/job/java-config.adoc
index 3cbcb727cc..472650b763 100644
--- a/spring-batch-docs/modules/ROOT/pages/job/java-config.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/job/java-config.adoc
@@ -42,6 +42,7 @@ public class MyJobConfiguration {
return new JdbcTransactionManager(dataSource);
}
+ @Bean
public Job job(JobRepository jobRepository) {
return new JobBuilder("myJob", jobRepository)
//define job flow as needed
diff --git a/spring-batch-docs/modules/ROOT/pages/readers-and-writers/multi-file-input.adoc b/spring-batch-docs/modules/ROOT/pages/readers-and-writers/multi-file-input.adoc
index 08307e720d..cf81b7a417 100644
--- a/spring-batch-docs/modules/ROOT/pages/readers-and-writers/multi-file-input.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/readers-and-writers/multi-file-input.adoc
@@ -24,10 +24,10 @@ The following example shows how to read files with wildcards in Java:
[source, java]
----
@Bean
-public MultiResourceItemReader multiResourceReader() {
+public MultiResourceItemReader multiResourceReader(@Value("classpath:data/input/file-*.txt") Resource[] resources) {
return new MultiResourceItemReaderBuilder()
.delegate(flatFileItemReader())
- .resources(resources())
+ .resources(resources)
.build();
}
----
diff --git a/spring-batch-docs/modules/ROOT/pages/repeat.adoc b/spring-batch-docs/modules/ROOT/pages/repeat.adoc
index 4836d338b2..7836d11043 100644
--- a/spring-batch-docs/modules/ROOT/pages/repeat.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/repeat.adoc
@@ -207,7 +207,7 @@ Java::
The following example uses Java configuration to
repeat a service call to a method called `processMessage` (for more detail on how to
configure AOP interceptors, see the
-<>):
+https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop[Spring User Guide]):
+
[source, java]
----
@@ -234,7 +234,7 @@ XML::
The following example shows declarative iteration that uses the Spring AOP namespace to
repeat a service call to a method called `processMessage` (for more detail on how to
configure AOP interceptors, see the
-<>):
+https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop[Spring User Guide]):
+
[source, xml]
----
diff --git a/spring-batch-docs/modules/ROOT/pages/scalability.adoc b/spring-batch-docs/modules/ROOT/pages/scalability.adoc
index 5836fddf56..b00353c4e6 100644
--- a/spring-batch-docs/modules/ROOT/pages/scalability.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/scalability.adoc
@@ -346,8 +346,8 @@ configuration:
[source, java]
----
@Bean
-public Step step1Manager() {
- return stepBuilderFactory.get("step1.manager")
+public Step step1Manager(JobRepository jobRepository) {
+ return new StepBuilder("step1.manager", jobRepository)
.partitioner("step1", partitioner())
.step(step1())
.gridSize(10)
diff --git a/spring-batch-docs/modules/ROOT/pages/spring-batch-architecture.adoc b/spring-batch-docs/modules/ROOT/pages/spring-batch-architecture.adoc
index 75e5ab926f..ea0d35f7c9 100644
--- a/spring-batch-docs/modules/ROOT/pages/spring-batch-architecture.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/spring-batch-architecture.adoc
@@ -252,7 +252,7 @@ advisable). The following image illustrates the partitioning approach:
image::partitioned.png[Figure 1.2: Partitioned Process, scaledwidth="60%"]
The architecture should be flexible enough to allow dynamic configuration of the number
-of partitions. You shoul consider both automatic and user controlled configuration.
+of partitions. You should consider both automatic and user controlled configuration.
Automatic configuration may be based on such parameters as the input file size and the
number of input records.
diff --git a/spring-batch-docs/modules/ROOT/pages/spring-batch-integration/sub-elements.adoc b/spring-batch-docs/modules/ROOT/pages/spring-batch-integration/sub-elements.adoc
index eac14f4e7c..205a8669e2 100644
--- a/spring-batch-docs/modules/ROOT/pages/spring-batch-integration/sub-elements.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/spring-batch-integration/sub-elements.adoc
@@ -182,7 +182,7 @@ The following example shows the how to add a step-level listener in XML:
Asynchronous Processors help you scale the processing of items. In the asynchronous
processor use case, an `AsyncItemProcessor` serves as a dispatcher, executing the logic of
the `ItemProcessor` for an item on a new thread. Once the item completes, the `Future` is
-passed to the `AsynchItemWriter` to be written.
+passed to the `AsyncItemWriter` to be written.
Therefore, you can increase performance by using asynchronous item processing, basically
letting you implement fork-join scenarios. The `AsyncItemWriter` gathers the results and
diff --git a/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/intercepting-execution.adoc b/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/intercepting-execution.adoc
index bdb7f57b61..142cc7772a 100644
--- a/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/intercepting-execution.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/intercepting-execution.adoc
@@ -207,9 +207,10 @@ public interface ItemWriteListener extends StepListener {
----
The `beforeWrite` method is called before `write` on the `ItemWriter` and is handed the
-list of items that is written. The `afterWrite` method is called after the item has been
-successfully written. If there was an error while writing, the `onWriteError` method is
-called. The exception encountered and the item that was attempted to be written are
+list of items that is written. The `afterWrite` method is called after the items have been
+successfully written, but before committing the transaction associated with the chunk's processing.
+If there was an error while writing, the `onWriteError` method is called.
+The exception encountered and the item that was attempted to be written are
provided, so that they can be logged.
The annotations corresponding to this interface are:
diff --git a/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/restart.adoc b/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/restart.adoc
index 8f4af6f71e..20e80bd72d 100644
--- a/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/restart.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/step/chunk-oriented-processing/restart.adoc
@@ -9,7 +9,7 @@ require some specific configuration.
== Setting a Start Limit
There are many scenarios where you may want to control the number of times a `Step` can
-be started. For example, you might need to configure a particular `Step` might so that it
+be started. For example, you might need to configure a particular `Step` so that it
runs only once because it invalidates some resource that must be fixed manually before it can
be run again. This is configurable on the step level, since different steps may have
different requirements. A `Step` that can be executed only once can exist as part of the
diff --git a/spring-batch-docs/modules/ROOT/pages/step/controlling-flow.adoc b/spring-batch-docs/modules/ROOT/pages/step/controlling-flow.adoc
index 7d3e70ab23..03670bc31b 100644
--- a/spring-batch-docs/modules/ROOT/pages/step/controlling-flow.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/step/controlling-flow.adoc
@@ -294,14 +294,14 @@ the condition of the execution having skipped records, as the following example
[source, java]
----
-public class SkipCheckingListener extends StepExecutionListenerSupport {
+public class SkipCheckingListener implements StepExecutionListener {
+ @Override
public ExitStatus afterStep(StepExecution stepExecution) {
String exitCode = stepExecution.getExitStatus().getExitCode();
if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) &&
- stepExecution.getSkipCount() > 0) {
+ stepExecution.getSkipCount() > 0) {
return new ExitStatus("COMPLETED WITH SKIPS");
- }
- else {
+ } else {
return null;
}
}
diff --git a/spring-batch-docs/modules/ROOT/pages/step/late-binding.adoc b/spring-batch-docs/modules/ROOT/pages/step/late-binding.adoc
index ceb0d390aa..879464ef21 100644
--- a/spring-batch-docs/modules/ROOT/pages/step/late-binding.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/step/late-binding.adoc
@@ -201,8 +201,8 @@ The following example shows how to access the `ExecutionContext` in XML:
NOTE: Any bean that uses late binding must be declared with `scope="step"`. See
xref:step/late-binding.adoc#step-scope[Step Scope] for more information.
-A `Step` bean should not be step-scoped. If late binding is needed in a step
-definition, the components of that step (tasklet, item reader or writer, and so on)
+A `Step` bean should not be step-scoped or job-scoped. If late binding is needed in a step
+definition, then the components of that step (tasklet, item reade/writer, completion policy, and so on)
are the ones that should be scoped instead.
NOTE: If you use Spring 3.0 (or above), the expressions in step-scoped beans are in the
diff --git a/spring-batch-docs/modules/ROOT/pages/step/tasklet.adoc b/spring-batch-docs/modules/ROOT/pages/step/tasklet.adoc
index 2613e34878..7ad23b8dae 100644
--- a/spring-batch-docs/modules/ROOT/pages/step/tasklet.adoc
+++ b/spring-batch-docs/modules/ROOT/pages/step/tasklet.adoc
@@ -122,7 +122,7 @@ public class FileDeletingTasklet implements Tasklet, InitializingBean {
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
File dir = directory.getFile();
- Assert.state(dir.isDirectory());
+ Assert.state(dir.isDirectory(), "The resource must be a directory");
File[] files = dir.listFiles();
for (int i = 0; i < files.length; i++) {
@@ -140,7 +140,7 @@ public class FileDeletingTasklet implements Tasklet, InitializingBean {
}
public void afterPropertiesSet() throws Exception {
- Assert.state(directory != null, "directory must be set");
+ Assert.state(directory != null, "Directory must be set");
}
}
----
diff --git a/spring-batch-docs/pom.xml b/spring-batch-docs/pom.xml
index 6c338c613d..e47753796e 100644
--- a/spring-batch-docs/pom.xml
+++ b/spring-batch-docs/pom.xml
@@ -4,7 +4,7 @@
org.springframework.batchspring-batch
- 5.1.1-SNAPSHOT
+ 5.1.4-SNAPSHOTspring-batch-docsSpring Batch Docs
@@ -26,7 +26,7 @@
@antora/atlas-extension@1.0.0-alpha.1@antora/collector-extension@1.0.0-alpha.3@asciidoctor/tabs@1.0.0-beta.3
- @springio/antora-extensions@1.7.0
+ @springio/antora-extensions@1.11.1@springio/asciidoctor-extensions@1.0.0-alpha.9
diff --git a/spring-batch-infrastructure/pom.xml b/spring-batch-infrastructure/pom.xml
index 4658581ac3..fe1199d193 100644
--- a/spring-batch-infrastructure/pom.xml
+++ b/spring-batch-infrastructure/pom.xml
@@ -4,7 +4,7 @@
org.springframework.batchspring-batch
- 5.1.1-SNAPSHOT
+ 5.1.4-SNAPSHOTspring-batch-infrastructurejar
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/Chunk.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/Chunk.java
index 4cdfa1c7e6..aababa36c2 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/Chunk.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/Chunk.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2024 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.
@@ -154,6 +154,13 @@ public int size() {
/**
* Flag to indicate if the source data is exhausted.
+ *
+ *
+ * Note: This may return false if the last chunk has the same number of items as the
+ * configured commit interval. Consequently, in such cases,there will be a last empty
+ * chunk that won't be processed. It is recommended to consider this behavior when
+ * utilizing this method.
+ *
* @return true if there is no more data to process
*/
public boolean isEnd() {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractPaginatedDataItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractPaginatedDataItemReader.java
index c7982e506d..043e54b7ba 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractPaginatedDataItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractPaginatedDataItemReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2023 the original author or authors.
+ * Copyright 2013-2024 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.
@@ -103,6 +103,14 @@ protected void doOpen() throws Exception {
@Override
protected void doClose() throws Exception {
+ this.lock.lock();
+ try {
+ this.page = 0;
+ this.results = null;
+ }
+ finally {
+ this.lock.unlock();
+ }
}
@Override
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoPagingItemReader.java
index 442d6956e2..5c2278cacc 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoPagingItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoPagingItemReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2023 the original author or authors.
+ * Copyright 2012-2024 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.
@@ -15,8 +15,14 @@
*/
package org.springframework.batch.item.data;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.ClassUtils;
@@ -70,4 +76,69 @@ public MongoPagingItemReader() {
setName(ClassUtils.getShortName(MongoPagingItemReader.class));
}
+ @Override
+ public void setTemplate(MongoOperations template) {
+ super.setTemplate(template);
+ }
+
+ @Override
+ public void setQuery(Query query) {
+ super.setQuery(query);
+ }
+
+ @Override
+ public void setQuery(String queryString) {
+ super.setQuery(queryString);
+ }
+
+ @Override
+ public void setTargetType(Class extends T> type) {
+ super.setTargetType(type);
+ }
+
+ @Override
+ public void setParameterValues(List