* Basic interface for generic output operations. Class implementing this interface will
@@ -48,6 +46,6 @@ public interface ItemWriter {
* @throws Exception if there are errors. The framework will catch the exception and
* convert or rethrow it as appropriate.
*/
- void write(@NonNull Chunk extends T> chunk) throws Exception;
+ void write(Chunk extends T> chunk) throws Exception;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/KeyValueItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/KeyValueItemWriter.java
index 6354fb1358..fdbc445c78 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/KeyValueItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/KeyValueItemWriter.java
@@ -12,6 +12,7 @@
*/
package org.springframework.batch.item;
+import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.Assert;
@@ -22,21 +23,20 @@
*
* @author David Turanski
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 2.2
*
*/
public abstract class KeyValueItemWriter implements ItemWriter, InitializingBean {
- protected Converter itemKeyMapper;
+ protected @Nullable Converter itemKeyMapper;
protected boolean delete;
@Override
- public void write(Chunk extends V> items) throws Exception {
- if (items == null) {
- return;
- }
- for (V item : items) {
+ public void write(Chunk extends V> chunk) throws Exception {
+ for (V item : chunk) {
+ @SuppressWarnings("DataFlowIssue")
K key = itemKeyMapper.convert(item);
writeKeyValue(key, item);
}
@@ -55,7 +55,7 @@ protected void flush() throws Exception {
* @param key the key
* @param value the item
*/
- protected abstract void writeKeyValue(K key, V value);
+ protected abstract void writeKeyValue(@Nullable K key, V value);
/**
* afterPropertiesSet() hook
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/PeekableItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/PeekableItemReader.java
index 4bf053738a..088c6688b0 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/PeekableItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/PeekableItemReader.java
@@ -15,7 +15,7 @@
*/
package org.springframework.batch.item;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
*
@@ -45,7 +45,6 @@ public interface PeekableItemReader extends ItemReader {
* @return the next item or {@code null} if the data source is exhausted
* @throws Exception if there is a problem
*/
- @Nullable
- T peek() throws Exception, UnexpectedInputException, ParseException;
+ @Nullable T peek() throws Exception, UnexpectedInputException, ParseException;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SkipWrapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SkipWrapper.java
index fe94f012e4..ea3673c884 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SkipWrapper.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SkipWrapper.java
@@ -16,21 +16,22 @@
package org.springframework.batch.item;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* Wrapper for an item and its exception if it failed processing.
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @deprecated since 6.0 with no replacement. Scheduled for removal in 7.0.
*/
@Deprecated(since = "6.0", forRemoval = true)
public class SkipWrapper {
- final private Throwable exception;
+ private final @Nullable Throwable exception;
- final private T item;
+ private final @Nullable T item;
/**
* @param item the item being wrapped.
@@ -39,7 +40,7 @@ public SkipWrapper(T item) {
this(item, null);
}
- public SkipWrapper(T item, @Nullable Throwable e) {
+ public SkipWrapper(@Nullable T item, @Nullable Throwable e) {
this.item = item;
this.exception = e;
}
@@ -48,8 +49,7 @@ public SkipWrapper(T item, @Nullable Throwable e) {
* Public getter for the exception.
* @return the exception
*/
- @Nullable
- public Throwable getException() {
+ public @Nullable Throwable getException() {
return exception;
}
@@ -57,7 +57,7 @@ public Throwable getException() {
* Public getter for the item.
* @return the item
*/
- public T getItem() {
+ public @Nullable T getItem() {
return item;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SpELItemKeyMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SpELItemKeyMapper.java
index 13c4f49539..ac4a802ca1 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SpELItemKeyMapper.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SpELItemKeyMapper.java
@@ -12,30 +12,29 @@
*/
package org.springframework.batch.item;
+import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.converter.Converter;
import org.springframework.expression.Expression;
-import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* An implementation of {@link Converter} that uses SpEL to map a Value to a key
*
* @author David Turanski
+ * @author Stefano Cordio
* @since 2.2
*/
public class SpELItemKeyMapper implements Converter {
- private final ExpressionParser parser = new SpelExpressionParser();
-
private final Expression parsedExpression;
public SpELItemKeyMapper(String keyExpression) {
- parsedExpression = parser.parseExpression(keyExpression);
+ parsedExpression = new SpelExpressionParser().parseExpression(keyExpression);
}
@SuppressWarnings("unchecked")
@Override
- public K convert(V item) {
+ public @Nullable K convert(V item) {
return (K) parsedExpression.getValue(item);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/AbstractMethodInvokingDelegator.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/AbstractMethodInvokingDelegator.java
index 4a0665ab12..f214c76c64 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/AbstractMethodInvokingDelegator.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/AbstractMethodInvokingDelegator.java
@@ -22,6 +22,7 @@
import java.util.Arrays;
import java.util.List;
+import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -41,21 +42,22 @@
* @author Robert Kasanicky
* @author Mahmoud Ben Hassine
* @author Glenn Renfro
+ * @author Stefano Cordio
*/
public abstract class AbstractMethodInvokingDelegator implements InitializingBean {
- private Object targetObject;
+ private @Nullable Object targetObject;
- private String targetMethod;
+ private @Nullable String targetMethod;
- private Object[] arguments;
+ private @Nullable Object @Nullable [] arguments;
/**
* Invoker the target method with arguments set by {@link #setArguments(Object[])}.
* @return object returned by invoked method
* @throws Exception exception thrown when executing the delegate method.
*/
- protected T invokeDelegateMethod() throws Exception {
+ protected @Nullable T invokeDelegateMethod() throws Exception {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
invoker.setArguments(arguments);
return doInvoke(invoker);
@@ -67,7 +69,7 @@ protected T invokeDelegateMethod() throws Exception {
* @return object returned by target method
* @throws Exception exception thrown when executing the delegate method.
*/
- protected T invokeDelegateMethodWithArgument(Object object) throws Exception {
+ protected @Nullable T invokeDelegateMethodWithArgument(Object object) throws Exception {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
invoker.setArguments(object);
return doInvoke(invoker);
@@ -79,7 +81,7 @@ protected T invokeDelegateMethodWithArgument(Object object) throws Exception {
* @return object returned by invoked method
* @throws Exception exception thrown when executing the delegate method.
*/
- protected T invokeDelegateMethodWithArguments(Object[] args) throws Exception {
+ protected @Nullable T invokeDelegateMethodWithArguments(@Nullable Object[] args) throws Exception {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
invoker.setArguments(args);
return doInvoke(invoker);
@@ -88,7 +90,7 @@ protected T invokeDelegateMethodWithArguments(Object[] args) throws Exception {
/**
* Create a new configured instance of {@link MethodInvoker}.
*/
- private MethodInvoker createMethodInvoker(Object targetObject, String targetMethod) {
+ private MethodInvoker createMethodInvoker(@Nullable Object targetObject, @Nullable String targetMethod) {
HippyMethodInvoker invoker = new HippyMethodInvoker();
invoker.setTargetObject(targetObject);
invoker.setTargetMethod(targetMethod);
@@ -102,7 +104,7 @@ private MethodInvoker createMethodInvoker(Object targetObject, String targetMeth
* @return return value of the invoked method
*/
@SuppressWarnings("unchecked")
- private T doInvoke(MethodInvoker invoker) throws Exception {
+ private @Nullable T doInvoke(MethodInvoker invoker) throws Exception {
try {
invoker.prepare();
}
@@ -141,6 +143,7 @@ public void afterPropertiesSet() throws Exception {
private boolean targetClassDeclaresTargetMethod() {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
+ @SuppressWarnings("DataFlowIssue")
Method[] memberMethods = invoker.getTargetClass().getMethods();
Method[] declaredMethods = invoker.getTargetClass().getDeclaredMethods();
@@ -200,11 +203,11 @@ public void setTargetMethod(String targetMethod) {
* providing explicit argument values.
*
* If arguments are set to not-null value {@link #afterPropertiesSet()} will check the
- * values are compatible with target method's signature. In case arguments are null
- * (not set) method signature will not be checked and it is assumed correct values
- * will be supplied at runtime.
+ * values are compatible with target method's signature. In case arguments are
+ * {@code null} (not set), the method signature will not be checked, and it is assumed
+ * correct values will be supplied at runtime.
*/
- public void setArguments(Object[] arguments) {
+ public void setArguments(Object @Nullable [] arguments) {
this.arguments = arguments == null ? null : arguments.clone();
}
@@ -212,7 +215,7 @@ public void setArguments(Object[] arguments) {
* Return arguments.
* @return arguments
*/
- protected Object[] getArguments() {
+ protected @Nullable Object @Nullable [] getArguments() {
return arguments;
}
@@ -220,7 +223,7 @@ protected Object[] getArguments() {
* @return the object on which the method will be invoked.
* @since 5.1
*/
- protected Object getTargetObject() {
+ protected @Nullable Object getTargetObject() {
return targetObject;
}
@@ -228,7 +231,7 @@ protected Object getTargetObject() {
* @return the name of the method to be invoked.
* @since 5.1
*/
- protected String getTargetMethod() {
+ protected @Nullable String getTargetMethod() {
return targetMethod;
}
@@ -240,7 +243,7 @@ protected String getTargetMethod() {
*/
public static class InvocationTargetThrowableWrapper extends RuntimeException {
- public InvocationTargetThrowableWrapper(Throwable cause) {
+ public InvocationTargetThrowableWrapper(@Nullable Throwable cause) {
super(cause);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/HippyMethodInvoker.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/HippyMethodInvoker.java
index 452c60ddcf..8673a916b5 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/HippyMethodInvoker.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/HippyMethodInvoker.java
@@ -17,14 +17,16 @@
import java.lang.reflect.Method;
+import org.jspecify.annotations.Nullable;
+import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MethodInvoker;
import org.springframework.util.ReflectionUtils;
/**
* A {@link MethodInvoker} that is a bit relaxed about its arguments. You can give it
- * arguments in the wrong order or you can give it too many arguments and it will try and
- * find a method that matches a subset.
+ * arguments in the wrong order, or you can give it too many arguments, and it will try
+ * and find a method that matches a subset.
*
* @author Dave Syer
* @since 2.1
@@ -32,20 +34,23 @@
public class HippyMethodInvoker extends MethodInvoker {
@Override
- protected Method findMatchingMethod() {
+ protected @Nullable Method findMatchingMethod() {
String targetMethod = getTargetMethod();
- Object[] arguments = getArguments();
- Method[] candidates = ReflectionUtils.getAllDeclaredMethods(getTargetClass());
+ @Nullable Object[] arguments = getArguments();
+
+ Class> targetClass = getTargetClass();
+ Assert.state(targetClass != null, "No target class set");
+ Method[] candidates = ReflectionUtils.getAllDeclaredMethods(targetClass);
int minTypeDiffWeight = Integer.MAX_VALUE;
Method matchingMethod = null;
- Object[] transformedArguments = null;
+ @Nullable Object[] transformedArguments = null;
for (Method candidate : candidates) {
if (candidate.getName().equals(targetMethod)) {
Class>[] paramTypes = candidate.getParameterTypes();
- Object[] candidateArguments = new Object[paramTypes.length];
+ @Nullable Object[] candidateArguments = new Object[paramTypes.length];
int assignedParameterCount = 0;
for (Object argument : arguments) {
for (int i = 0; i < paramTypes.length; i++) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemProcessorAdapter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemProcessorAdapter.java
index 1a640383c7..3d58930164 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemProcessorAdapter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemProcessorAdapter.java
@@ -16,8 +16,9 @@
package org.springframework.batch.item.adapter;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ItemProcessor;
-import org.springframework.lang.Nullable;
/**
* Invokes a custom method on a delegate plain old Java object which itself processes an
@@ -32,9 +33,8 @@ public class ItemProcessorAdapter extends AbstractMethodInvokingDelegator<
*
* @see ItemProcessor#process(Object)
*/
- @Nullable
@Override
- public O process(I item) throws Exception {
+ public @Nullable O process(I item) throws Exception {
return invokeDelegateMethodWithArgument(item);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemReaderAdapter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemReaderAdapter.java
index 862d793d21..6d7e70bfce 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemReaderAdapter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/ItemReaderAdapter.java
@@ -16,8 +16,9 @@
package org.springframework.batch.item.adapter;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ItemReader;
-import org.springframework.lang.Nullable;
/**
* Invokes a custom method on a delegate plain old Java object which itself provides an
@@ -35,9 +36,8 @@ public class ItemReaderAdapter extends AbstractMethodInvokingDelegator imp
/**
* @return return value of the target method.
*/
- @Nullable
@Override
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
return invokeDelegateMethod();
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/PropertyExtractingDelegatingItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/PropertyExtractingDelegatingItemWriter.java
index 545d44e888..1e01fb29ea 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/PropertyExtractingDelegatingItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/PropertyExtractingDelegatingItemWriter.java
@@ -16,6 +16,8 @@
package org.springframework.batch.item.adapter;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.BeanWrapper;
@@ -39,12 +41,13 @@
public class PropertyExtractingDelegatingItemWriter extends AbstractMethodInvokingDelegator
implements ItemWriter {
- private String[] fieldsUsedAsTargetMethodArguments;
+ private @Nullable String @Nullable [] fieldsUsedAsTargetMethodArguments;
/**
* Extracts values from item's fields named in fieldsUsedAsTargetMethodArguments and
* passes them as arguments to the delegate method.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends T> items) throws Exception {
for (T item : items) {
@@ -52,7 +55,8 @@ public void write(Chunk extends T> items) throws Exception {
// helper for extracting property values from a bean
BeanWrapper beanWrapper = new BeanWrapperImpl(item);
- Object[] methodArguments = new Object[fieldsUsedAsTargetMethodArguments.length];
+ @Nullable Object[] methodArguments = new Object[fieldsUsedAsTargetMethodArguments.length];
+
for (int i = 0; i < fieldsUsedAsTargetMethodArguments.length; i++) {
methodArguments[i] = beanWrapper.getPropertyValue(fieldsUsedAsTargetMethodArguments[i]);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/package-info.java
index db2847283f..cc768b5f1e 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/package-info.java
@@ -3,7 +3,7 @@
* Adapters for Plain Old Java Objects.
*
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.adapter;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemReader.java
index 8e1e4a654c..b1a33a0182 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemReader.java
@@ -16,10 +16,11 @@
package org.springframework.batch.item.amqp;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.batch.item.ItemReader;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -35,27 +36,27 @@
*
* @author Chris Schaefer
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public class AmqpItemReader implements ItemReader {
private final AmqpTemplate amqpTemplate;
- private Class extends T> itemType;
+ private @Nullable Class extends T> itemType;
/**
* Initialize the AmqpItemReader.
* @param amqpTemplate the template to be used. Must not be null.
*/
- public AmqpItemReader(final AmqpTemplate amqpTemplate) {
+ public AmqpItemReader(AmqpTemplate amqpTemplate) {
Assert.notNull(amqpTemplate, "AmqpTemplate must not be null");
this.amqpTemplate = amqpTemplate;
}
- @Nullable
@Override
@SuppressWarnings("unchecked")
- public T read() {
+ public @Nullable T read() {
if (itemType != null && itemType.isAssignableFrom(Message.class)) {
return (T) amqpTemplate.receive();
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemWriter.java
index c825635cf8..1e591d7ee9 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/AmqpItemWriter.java
@@ -45,14 +45,14 @@ public class AmqpItemWriter implements ItemWriter {
private final Log log = LogFactory.getLog(getClass());
- public AmqpItemWriter(final AmqpTemplate amqpTemplate) {
+ public AmqpItemWriter(AmqpTemplate amqpTemplate) {
Assert.notNull(amqpTemplate, "AmqpTemplate must not be null");
this.amqpTemplate = amqpTemplate;
}
@Override
- public void write(final Chunk extends T> items) throws Exception {
+ public void write(Chunk extends T> items) throws Exception {
if (log.isDebugEnabled()) {
log.debug("Writing to AMQP with " + items.size() + " items.");
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemReaderBuilder.java
index 6f1619ad79..03eda92a92 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemReaderBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.amqp.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.batch.item.amqp.AmqpItemReader;
import org.springframework.util.Assert;
@@ -24,14 +25,15 @@
* A builder implementation for the {@link AmqpItemReader}
*
* @author Glenn Renfro
+ * @author Stefano Cordio
* @since 4.0
* @see AmqpItemReader
*/
public class AmqpItemReaderBuilder {
- private AmqpTemplate amqpTemplate;
+ private @Nullable AmqpTemplate amqpTemplate;
- private Class extends T> itemType;
+ private @Nullable Class extends T> itemType;
/**
* Establish the amqpTemplate to be used by the AmqpItemReader.
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemWriterBuilder.java
index 979a6ad993..a349a54a8e 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/AmqpItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.amqp.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.batch.item.amqp.AmqpItemWriter;
import org.springframework.util.Assert;
@@ -24,12 +25,13 @@
* A builder implementation for the {@link AmqpItemWriter}
*
* @author Glenn Renfro
+ * @author Stefano Cordio
* @since 4.0
* @see AmqpItemWriter
*/
public class AmqpItemWriterBuilder {
- private AmqpTemplate amqpTemplate;
+ private @Nullable AmqpTemplate amqpTemplate;
/**
* Establish the amqpTemplate to be used by the AmqpItemWriter.
@@ -50,9 +52,7 @@ public AmqpItemWriterBuilder amqpTemplate(AmqpTemplate amqpTemplate) {
public AmqpItemWriter build() {
Assert.notNull(this.amqpTemplate, "amqpTemplate is required.");
- AmqpItemWriter writer = new AmqpItemWriter<>(this.amqpTemplate);
-
- return writer;
+ return new AmqpItemWriter<>(this.amqpTemplate);
}
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/package-info.java
index 250e32146f..573347cc1f 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/builder/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 the original author or authors.
+ * Copyright 2018-2025 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,9 +18,9 @@
* Builders for AMQP item reader and writer.
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.amqp.builder;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/package-info.java
index aa419383c0..8724014cca 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/amqp/package-info.java
@@ -3,8 +3,9 @@
*
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.amqp;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemReader.java
index ab39d81571..34cc2cb45f 100755
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemReader.java
@@ -28,12 +28,12 @@
import org.apache.avro.reflect.ReflectDatumReader;
import org.apache.avro.specific.SpecificDatumReader;
import org.apache.avro.specific.SpecificRecordBase;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -54,9 +54,9 @@ public class AvroItemReader extends AbstractItemCountingItemStreamItemReader<
private boolean embeddedSchema = true;
- private InputStreamReader inputStreamReader;
+ private @Nullable InputStreamReader inputStreamReader;
- private DataFileStream dataFileReader;
+ private @Nullable DataFileStream dataFileReader;
private final InputStream inputStream;
@@ -104,15 +104,16 @@ public AvroItemReader(Resource data, Resource schema) {
/**
* Disable or enable reading an embedded Avro schema. True by default.
- * @param embeddedSchema set to false to if the input does not embed an Avro schema.
+ * @param embeddedSchema set to {@code false} if the input does not embed an Avro
+ * schema.
*/
public void setEmbeddedSchema(boolean embeddedSchema) {
this.embeddedSchema = embeddedSchema;
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- protected T doRead() throws Exception {
+ protected @Nullable T doRead() throws Exception {
if (this.inputStreamReader != null) {
return this.inputStreamReader.read();
}
@@ -124,6 +125,7 @@ protected void doOpen() throws Exception {
initializeReader();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void doClose() throws Exception {
if (this.inputStreamReader != null) {
@@ -171,7 +173,7 @@ private InputStreamReader(InputStream inputStream, DatumReader datumReader) {
this.binaryDecoder = DecoderFactory.get().binaryDecoder(inputStream, null);
}
- private T read() throws Exception {
+ private @Nullable T read() throws Exception {
if (!this.binaryDecoder.isEnd()) {
return this.datumReader.read(null, this.binaryDecoder);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemWriter.java
index 99a63b04bf..0ebbee5b2c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/AvroItemWriter.java
@@ -30,6 +30,7 @@
import org.apache.avro.specific.SpecificDatumWriter;
import org.apache.avro.specific.SpecificRecordBase;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
@@ -54,13 +55,13 @@
*/
public class AvroItemWriter extends AbstractItemStreamItemWriter {
- private DataFileWriter dataFileWriter;
+ private @Nullable DataFileWriter dataFileWriter;
- private OutputStreamWriter outputStreamWriter;
+ private @Nullable OutputStreamWriter outputStreamWriter;
private final WritableResource resource;
- private final Resource schemaResource;
+ private final @Nullable Resource schemaResource;
private final Class clazz;
@@ -71,7 +72,7 @@ public class AvroItemWriter extends AbstractItemStreamItemWriter {
* @param schema a {@link Resource} containing the Avro schema.
* @param clazz the data type to be serialized.
*/
- public AvroItemWriter(WritableResource resource, Resource schema, Class clazz) {
+ public AvroItemWriter(WritableResource resource, @Nullable Resource schema, Class clazz) {
this.schemaResource = schema;
this.resource = resource;
this.clazz = clazz;
@@ -87,6 +88,7 @@ public AvroItemWriter(WritableResource resource, Class clazz) {
embedSchema = false;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends T> items) throws Exception {
items.forEach(item -> {
@@ -118,6 +120,7 @@ public void open(ExecutionContext executionContext) {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() {
try {
@@ -155,7 +158,6 @@ private void initializeWriter() throws IOException {
this.outputStreamWriter = createOutputStreamWriter(this.resource.getOutputStream(),
datumWriterForClass(this.clazz));
}
-
}
private static DatumWriter datumWriterForClass(Class clazz) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemReaderBuilder.java
index c9803e2590..eab6ee661e 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemReaderBuilder.java
@@ -18,6 +18,7 @@
import org.apache.avro.Schema;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.avro.AvroItemReader;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
@@ -29,6 +30,7 @@
*
* @author David Turanski
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 4.2
*/
public class AvroItemReaderBuilder {
@@ -41,11 +43,11 @@ public class AvroItemReaderBuilder {
private int currentItemCount;
- private Resource schema;
+ private @Nullable Resource schema;
- private Resource resource;
+ private @Nullable Resource resource;
- private Class type;
+ private @Nullable Class type;
private boolean embeddedSchema = true;
@@ -162,10 +164,12 @@ public AvroItemReader build() {
Assert.notNull(this.resource, "A 'resource' is required.");
if (this.type != null) {
- avroItemReader = buildForType();
+ Assert.isNull(this.schema, "You cannot specify a schema and 'type'.");
+ avroItemReader = new AvroItemReader<>(this.resource, this.type);
}
else {
- avroItemReader = buildForSchema();
+ Assert.notNull(this.schema, "'schema' is required.");
+ avroItemReader = new AvroItemReader<>(this.resource, this.schema);
}
avroItemReader.setSaveState(this.saveState);
@@ -182,14 +186,4 @@ public AvroItemReader build() {
return avroItemReader;
}
- private AvroItemReader buildForType() {
- Assert.isNull(this.schema, "You cannot specify a schema and 'type'.");
- return new AvroItemReader<>(this.resource, this.type);
- }
-
- private AvroItemReader buildForSchema() {
- Assert.notNull(this.schema, "'schema' is required.");
- return new AvroItemReader<>(this.resource, this.schema);
- }
-
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemWriterBuilder.java
index 69c9eb85cc..7bd72ba3c8 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/AvroItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.avro.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.avro.AvroItemWriter;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
@@ -27,15 +28,16 @@
*
* @author David Turanski
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 4.2
*/
public class AvroItemWriterBuilder {
- private Class type;
+ private @Nullable Class type;
- private WritableResource resource;
+ private @Nullable WritableResource resource;
- private Resource schema;
+ private @Nullable Resource schema;
private String name = AvroItemWriter.class.getSimpleName();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/package-info.java
new file mode 100644
index 0000000000..a24fd63cab
--- /dev/null
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/builder/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2025 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.
+ */
+
+/**
+ * Builders for Avro item reader and writer.
+ *
+ * @author Stefano Cordio
+ */
+@NullMarked
+package org.springframework.batch.item.avro.builder;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/package-info.java
new file mode 100644
index 0000000000..78781b2c36
--- /dev/null
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/avro/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * Avro related reader and writer.
+ *
+ * @author Stefano Cordio
+ */
+@NullMarked
+package org.springframework.batch.item.avro;
+
+import org.jspecify.annotations.NullMarked;
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 043e54b7ba..eca601e1ad 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
@@ -18,13 +18,14 @@
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import java.util.Iterator;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import org.jspecify.annotations.Nullable;
+
/**
* A base class that handles basic reading logic based on the paginated semantics of
* Spring Data's paginated facilities. It also handles the semantics required for
@@ -35,6 +36,7 @@
* @author Michael Minella
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 2.2
* @param Type of item to be read
*/
@@ -44,7 +46,7 @@ public abstract class AbstractPaginatedDataItemReader extends AbstractItemCou
protected int pageSize = 10;
- protected Iterator results;
+ protected @Nullable Iterator results;
private final Lock lock = new ReentrantLock();
@@ -57,9 +59,8 @@ public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
- @Nullable
@Override
- protected T doRead() throws Exception {
+ protected @Nullable T doRead() throws Exception {
this.lock.lock();
try {
@@ -69,17 +70,12 @@ protected T doRead() throws Exception {
page++;
- if (results == null || !results.hasNext()) {
+ if (!results.hasNext()) {
return null;
}
}
- if (results.hasNext()) {
- return results.next();
- }
- else {
- return null;
- }
+ return results.next();
}
finally {
this.lock.unlock();
@@ -91,8 +87,8 @@ protected T doRead() throws Exception {
* page. Each time this method is called, the resulting {@link Iterator} should
* contain the items read within the next page.
*
- * If the {@link Iterator} is empty or null when it is returned, this
- * {@link ItemReader} will assume that the input has been exhausted.
+ * If the {@link Iterator} is empty when it is returned, this {@link ItemReader} will
+ * assume that the input has been exhausted.
* @return an {@link Iterator} containing the items within a page.
*/
protected abstract Iterator doPageRead();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoCursorItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoCursorItemReader.java
index 1759557d61..535c157f2b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoCursorItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoCursorItemReader.java
@@ -25,6 +25,7 @@
import org.bson.Document;
import org.bson.codecs.DecoderContext;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.beans.factory.InitializingBean;
@@ -48,37 +49,36 @@
*/
public class MongoCursorItemReader extends AbstractItemCountingItemStreamItemReader implements InitializingBean {
- private MongoOperations template;
+ private @Nullable MongoOperations template;
- private Class extends T> targetType;
+ private @Nullable Class extends T> targetType;
- private String collection;
+ private @Nullable String collection;
- private Query query;
+ private @Nullable Query query;
- private String queryString;
+ private @Nullable String queryString;
private List
*
*
- * By default the cursor will be opened using a separate connection. The ResultSet for the
- * cursor is held open regardless of commits or roll backs in a surrounding transaction.
- * Clients of this reader are responsible for buffering the items in the case that they
- * need to be re-presented on a rollback. This buffering is handled by the step
+ * By default, the cursor will be opened using a separate connection. The ResultSet for
+ * the cursor is held open regardless of commits or rollbacks in a surrounding
+ * transaction. Clients of this reader are responsible for buffering the items in the case
+ * that they need to be re-presented on a rollback. This buffering is handled by the step
* implementations provided and is only a concern for anyone writing their own step
* implementations.
*
@@ -111,6 +112,7 @@
* @author Thomas Risberg
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public abstract class AbstractCursorItemReader extends AbstractItemCountingItemStreamItemReader
implements InitializingBean {
@@ -120,11 +122,11 @@ public abstract class AbstractCursorItemReader extends AbstractItemCountingIt
public static final int VALUE_NOT_SET = -1;
- private Connection con;
+ private @Nullable Connection con;
- protected ResultSet rs;
+ protected @Nullable ResultSet rs;
- private DataSource dataSource;
+ private @Nullable DataSource dataSource;
private int fetchSize = VALUE_NOT_SET;
@@ -136,7 +138,7 @@ public abstract class AbstractCursorItemReader extends AbstractItemCountingIt
private boolean verifyCursorPosition = true;
- private SQLExceptionTranslator exceptionTranslator;
+ private @Nullable SQLExceptionTranslator exceptionTranslator;
private boolean initialized = false;
@@ -144,14 +146,10 @@ public abstract class AbstractCursorItemReader extends AbstractItemCountingIt
private boolean useSharedExtendedConnection = false;
- private Boolean connectionAutoCommit;
+ private @Nullable Boolean connectionAutoCommit;
private boolean initialConnectionAutoCommit;
- public AbstractCursorItemReader() {
- super();
- }
-
/**
* Assert that mandatory properties are set.
* @throws IllegalArgumentException if either data source or SQL properties not set.
@@ -173,7 +171,7 @@ public void setDataSource(DataSource dataSource) {
* Public getter for the data source.
* @return the dataSource
*/
- public DataSource getDataSource() {
+ public @Nullable DataSource getDataSource() {
return this.dataSource;
}
@@ -261,6 +259,7 @@ protected void handleWarnings(Statement statement) throws SQLWarningException, S
* traversing the ResultSet.
* @param row The index of the row to move to
*/
+ @SuppressWarnings("DataFlowIssue")
private void moveCursorToRow(int row) {
try {
int count = 0;
@@ -377,6 +376,7 @@ public void setConnectionAutoCommit(boolean autoCommit) {
* Check the result set is in sync with the currentRow attribute. This is important to
* ensure that the user hasn't modified the current row.
*/
+ @SuppressWarnings("DataFlowIssue")
private void verifyCursorPosition(long expectedCurrentRow) throws SQLException {
if (verifyCursorPosition) {
if (expectedCurrentRow != this.rs.getRow()) {
@@ -389,6 +389,7 @@ private void verifyCursorPosition(long expectedCurrentRow) throws SQLException {
* Close the cursor and database connection. Make call to cleanupOnClose so sub
* classes can cleanup any resources they have allocated.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void doClose() throws Exception {
initialized = false;
@@ -421,18 +422,18 @@ protected void doClose() throws Exception {
/**
* Execute the statement to open the cursor.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void doOpen() throws Exception {
-
Assert.state(!initialized, "Stream is already initialized. Close before re-opening.");
Assert.isNull(rs, "ResultSet still open! Close before re-opening.");
initializeConnection();
openCursor(con);
initialized = true;
-
}
+ @SuppressWarnings("DataFlowIssue")
protected void initializeConnection() {
Assert.state(getDataSource() != null, "DataSource must not be null.");
@@ -468,9 +469,8 @@ protected void initializeConnection() {
* Read next row and map it to item, verify cursor position if
* {@link #setVerifyCursorPosition(boolean)} is true.
*/
- @Nullable
@Override
- protected T doRead() throws Exception {
+ protected @Nullable T doRead() throws Exception {
if (rs == null) {
throw new ReaderNotOpenException("Reader must be open before it can be read.");
}
@@ -497,13 +497,13 @@ protected T doRead() throws Exception {
* @return the mapped object at the cursor position
* @throws SQLException if interactions with the current result set fail
*/
- @Nullable
- protected abstract T readCursor(ResultSet rs, int currentRow) throws SQLException;
+ protected abstract @Nullable T readCursor(ResultSet rs, int currentRow) throws SQLException;
/**
* Use {@link ResultSet#absolute(int)} if possible, otherwise scroll by calling
* {@link ResultSet#next()}.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void jumpToItem(int itemIndex) throws Exception {
if (driverSupportsAbsolute) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java
index e66dc2bc28..060aa5b9eb 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java
@@ -21,9 +21,10 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -43,6 +44,7 @@
* @author Thomas Risberg
* @author Dave Syer
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 2.0
*/
public abstract class AbstractPagingItemReader extends AbstractItemCountingItemStreamItemReader
@@ -58,7 +60,7 @@ public abstract class AbstractPagingItemReader extends AbstractItemCountingIt
private volatile int page = 0;
- protected volatile List results;
+ protected volatile @Nullable List results;
private final Lock lock = new ReentrantLock();
@@ -99,9 +101,9 @@ public void afterPropertiesSet() throws Exception {
Assert.state(pageSize > 0, "pageSize must be greater than zero");
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- protected T doRead() throws Exception {
+ protected @Nullable T doRead() throws Exception {
this.lock.lock();
try {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java
index fad274bcfc..b85b5e2b37 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -31,12 +31,13 @@
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.jdbc.datasource.ConnectionProxy;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.SmartDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
-import org.springframework.util.MethodInvoker;
/**
* Implementation of {@link SmartDataSource} that is capable of keeping a single JDBC
@@ -72,23 +73,24 @@
* The connection returned will be a close-suppressing proxy instead of the physical
* {@link Connection}. Be aware that you will not be able to cast this to a native
* OracleConnection or the like anymore; you'd be required to use
- * {@link java.sql.Connection#unwrap(Class)}.
+ * {@link Connection#unwrap(Class)}.
*
* @author Thomas Risberg
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @see #getConnection()
- * @see java.sql.Connection#close()
+ * @see Connection#close()
* @see DataSourceUtils#releaseConnection
- * @see java.sql.Connection#unwrap(Class)
+ * @see Connection#unwrap(Class)
* @since 2.0
*/
public class ExtendedConnectionDataSourceProxy implements SmartDataSource, InitializingBean {
/** Provided DataSource */
- private DataSource dataSource;
+ private @Nullable DataSource dataSource;
/** The connection to suppress close calls for */
- private Connection closeSuppressedConnection = null;
+ private @Nullable Connection closeSuppressedConnection;
/** The connection to suppress close calls for */
private boolean borrowedConnection = false;
@@ -123,11 +125,10 @@ public void setDataSource(DataSource dataSource) {
*/
@Override
public boolean shouldClose(Connection connection) {
- boolean shouldClose = !isCloseSuppressionActive(connection);
- if (borrowedConnection && closeSuppressedConnection.equals(connection)) {
+ if (borrowedConnection && isCloseSuppressionActive(connection)) {
borrowedConnection = false;
}
- return shouldClose;
+ return !isCloseSuppressionActive(connection);
}
/**
@@ -138,7 +139,7 @@ public boolean shouldClose(Connection connection) {
* @return true or false
*/
public boolean isCloseSuppressionActive(Connection connection) {
- return connection != null && connection.equals(closeSuppressedConnection);
+ return connection.equals(closeSuppressedConnection);
}
/**
@@ -194,14 +195,8 @@ public Connection getConnection(String username, String password) throws SQLExce
}
}
- private boolean completeCloseCall(Connection connection) {
- if (borrowedConnection && closeSuppressedConnection.equals(connection)) {
- borrowedConnection = false;
- }
- return isCloseSuppressionActive(connection);
- }
-
- private Connection initConnection(String username, String password) throws SQLException {
+ @SuppressWarnings("DataFlowIssue")
+ private Connection initConnection(@Nullable String username, @Nullable String password) throws SQLException {
if (closeSuppressedConnection != null) {
if (!borrowedConnection) {
borrowedConnection = true;
@@ -219,21 +214,25 @@ private Connection initConnection(String username, String password) throws SQLEx
return getCloseSuppressingConnectionProxy(target);
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public PrintWriter getLogWriter() throws SQLException {
return dataSource.getLogWriter();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public int getLoginTimeout() throws SQLException {
return dataSource.getLoginTimeout();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
dataSource.setLogWriter(out);
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void setLoginTimeout(int seconds) throws SQLException {
dataSource.setLoginTimeout(seconds);
@@ -267,7 +266,7 @@ public CloseSuppressingInvocationHandler(Connection target, ExtendedConnectionDa
}
@Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on ConnectionProxy interface coming in...
switch (method.getName()) {
@@ -282,13 +281,10 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
case "close" -> {
// Handle close method: don't pass the call on if we are
// suppressing close calls.
- if (dataSource.completeCloseCall((Connection) proxy)) {
- return null;
- }
- else {
- target.close();
- return null;
+ if (dataSource.shouldClose((Connection) proxy)) {
+ this.target.close();
}
+ return null;
}
case "getTargetConnection" -> {
// Handle getTargetConnection method: return underlying
@@ -312,8 +308,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
* Performs only a 'shallow' non-recursive check of self's and delegate's class to
* retain Java 5 compatibility.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
- public boolean isWrapperFor(Class> iface) throws SQLException {
+ public boolean isWrapperFor(Class> iface) {
return iface.isAssignableFrom(SmartDataSource.class) || iface.isAssignableFrom(dataSource.getClass());
}
@@ -322,6 +319,7 @@ public boolean isWrapperFor(Class> iface) throws SQLException {
* supplied parameter class. Does *not* support recursive unwrapping of the delegate
* to retain Java 5 compatibility.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public T unwrap(Class iface) throws SQLException {
if (iface.isAssignableFrom(SmartDataSource.class)) {
@@ -342,23 +340,10 @@ public void afterPropertiesSet() throws Exception {
Assert.state(dataSource != null, "DataSource is required");
}
- /**
- * Added due to JDK 7 compatibility.
- */
+ @SuppressWarnings("DataFlowIssue")
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- MethodInvoker invoker = new MethodInvoker();
- invoker.setTargetObject(dataSource);
- invoker.setTargetMethod("getParentLogger");
-
- try {
- invoker.prepare();
- return (Logger) invoker.invoke();
- }
- catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
- | InvocationTargetException nsme) {
- throw new SQLFeatureNotSupportedException(nsme);
- }
+ return dataSource.getParentLogger();
}
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java
index be5d04eeb3..70151f55a0 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcBatchItemWriter.java
@@ -23,6 +23,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
@@ -58,19 +59,20 @@
* @author Thomas Risberg
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 2.0
*/
public class JdbcBatchItemWriter implements ItemWriter, InitializingBean {
protected static final Log logger = LogFactory.getLog(JdbcBatchItemWriter.class);
- protected NamedParameterJdbcOperations namedParameterJdbcTemplate;
+ protected @Nullable NamedParameterJdbcOperations namedParameterJdbcTemplate;
- protected ItemPreparedStatementSetter itemPreparedStatementSetter;
+ protected @Nullable ItemPreparedStatementSetter itemPreparedStatementSetter;
- protected ItemSqlParameterSourceProvider itemSqlParameterSourceProvider;
+ protected @Nullable ItemSqlParameterSourceProvider itemSqlParameterSourceProvider;
- protected String sql;
+ protected @Nullable String sql;
protected boolean assertUpdates = true;
@@ -143,7 +145,7 @@ public void afterPropertiesSet() {
Assert.state(sql != null, "An SQL statement is required.");
List namedParameters = new ArrayList<>();
parameterCount = JdbcParameterUtils.countParameterPlaceholders(sql, namedParameters);
- if (namedParameters.size() > 0) {
+ if (!namedParameters.isEmpty()) {
if (parameterCount != namedParameters.size()) {
throw new InvalidDataAccessApiUsageException(
"You can't use both named parameters and classic \"?\" placeholders: " + sql);
@@ -156,9 +158,9 @@ public void afterPropertiesSet() {
}
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "DataFlowIssue" })
@Override
- public void write(final Chunk extends T> chunk) throws Exception {
+ public void write(Chunk extends T> chunk) throws Exception {
if (!chunk.isEmpty()) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java
index 3bf215430f..0292046e90 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java
@@ -20,11 +20,13 @@
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.Objects;
import org.springframework.jdbc.core.PreparedStatementSetter;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -37,7 +39,7 @@
*
* The statement used to open the cursor is created with the 'READ_ONLY' option since a
* non read-only cursor may unnecessarily lock tables or rows. It is also opened with
- * 'TYPE_FORWARD_ONLY' option. By default the cursor will be opened using a separate
+ * 'TYPE_FORWARD_ONLY' option. By default, the cursor will be opened using a separate
* connection which means that it will not participate in any transactions created as part
* of the step processing.
*
@@ -56,19 +58,19 @@
* @author Robert Kasanicky
* @author Thomas Risberg
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public class JdbcCursorItemReader extends AbstractCursorItemReader {
- private PreparedStatement preparedStatement;
+ private @Nullable PreparedStatement preparedStatement;
- private PreparedStatementSetter preparedStatementSetter;
+ private @Nullable PreparedStatementSetter preparedStatementSetter;
- private String sql;
+ private @Nullable String sql;
- private RowMapper rowMapper;
+ private @Nullable RowMapper rowMapper;
public JdbcCursorItemReader() {
- super();
setName(ClassUtils.getShortName(JdbcCursorItemReader.class));
}
@@ -135,9 +137,9 @@ protected void openCursor(Connection con) {
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- protected T readCursor(ResultSet rs, int currentRow) throws SQLException {
+ protected @Nullable T readCursor(ResultSet rs, int currentRow) throws SQLException {
return rowMapper.mapRow(rs, currentRow);
}
@@ -153,7 +155,7 @@ protected void cleanupOnClose(Connection connection) {
@Override
public String getSql() {
- return this.sql;
+ return Objects.requireNonNull(this.sql);
}
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java
index 547c8e3ff9..8904123335 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java
@@ -29,6 +29,7 @@
import javax.sql.DataSource;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.beans.factory.InitializingBean;
@@ -49,14 +50,14 @@
* data. The query is executed using paged requests of a size specified in
* {@link #setPageSize(int)}. Additional pages are requested when needed as
* {@link #read()} method is called, returning an object corresponding to current
- * position. On restart it uses the last sort key value to locate the first page to read
+ * position. On restart, it uses the last sort key value to locate the first page to read
* (so it doesn't matter if the successfully processed items have been removed or
* modified). It is important to have a unique key constraint on the sort key to guarantee
* that no data is lost between executions.
*
*
*
- * The performance of the paging depends on the database specific features available to
+ * The performance of the paging depends on the database-specific features available to
* limit the number of returned rows. Setting a fairly large page size and using a commit
* interval that matches the page size should provide better performance.
*
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.database;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java
index b60cdfadf9..efa0729dc9 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java
@@ -22,6 +22,7 @@
import java.util.Map;
import javax.sql.DataSource;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.database.JdbcParameterUtils;
import org.springframework.batch.item.database.Order;
import org.springframework.batch.item.database.PagingQueryProvider;
@@ -50,19 +51,20 @@
* @author Michael Minella
* @author Mahmoud Ben Hassine
* @author Benjamin Hetz
+ * @author Stefano Cordio
* @since 2.0
*/
public abstract class AbstractSqlPagingQueryProvider implements PagingQueryProvider {
- private String selectClause;
+ private @Nullable String selectClause;
- private String fromClause;
+ private @Nullable String fromClause;
- private String whereClause;
+ private @Nullable String whereClause;
private Map sortKeys = new LinkedHashMap<>();
- private String groupClause;
+ private @Nullable String groupClause;
private int parameterCount;
@@ -72,20 +74,15 @@ public abstract class AbstractSqlPagingQueryProvider implements PagingQueryProvi
* The setter for the group by clause
* @param groupClause SQL GROUP BY clause part of the SQL query string
*/
- public void setGroupClause(String groupClause) {
- if (StringUtils.hasText(groupClause)) {
- this.groupClause = removeKeyWord("group by", groupClause);
- }
- else {
- this.groupClause = null;
- }
+ public void setGroupClause(@Nullable String groupClause) {
+ this.groupClause = StringUtils.hasText(groupClause) ? removeKeyWord("group by", groupClause) : null;
}
/**
* The getter for the group by clause
* @return SQL GROUP BY clause part of the SQL query string
*/
- public String getGroupClause() {
+ public @Nullable String getGroupClause() {
return this.groupClause;
}
@@ -99,7 +96,7 @@ public void setSelectClause(String selectClause) {
/**
* @return SQL SELECT clause part of SQL query string
*/
- protected String getSelectClause() {
+ protected @Nullable String getSelectClause() {
return selectClause;
}
@@ -113,14 +110,14 @@ public void setFromClause(String fromClause) {
/**
* @return SQL FROM clause part of SQL query string
*/
- protected String getFromClause() {
+ protected @Nullable String getFromClause() {
return fromClause;
}
/**
* @param whereClause WHERE clause part of SQL query string
*/
- public void setWhereClause(String whereClause) {
+ public void setWhereClause(@Nullable String whereClause) {
if (StringUtils.hasText(whereClause)) {
this.whereClause = removeKeyWord("where", whereClause);
}
@@ -132,7 +129,7 @@ public void setWhereClause(String whereClause) {
/**
* @return SQL WHERE clause part of SQL query string
*/
- protected String getWhereClause() {
+ protected @Nullable String getWhereClause() {
return whereClause;
}
@@ -166,7 +163,7 @@ public boolean isUsingNamedParameters() {
/**
* The sort key placeholder will vary depending on whether named parameters or
* traditional placeholders are used in query strings.
- * @return place holder for sortKey.
+ * @return placeholder for sortKey.
*/
@Override
public String getSortKeyPlaceHolder(String keyName) {
@@ -205,7 +202,7 @@ public void init(DataSource dataSource) throws Exception {
/**
* Method generating the query string to be used for retrieving the first page. This
- * method must be implemented in sub classes.
+ * method must be implemented in subclasses.
* @param pageSize number of rows to read per page
* @return query string
*/
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java
index 608e0f2e5b..bf51c4bfdc 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java
@@ -37,6 +37,7 @@
import javax.sql.DataSource;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.database.Order;
import org.springframework.batch.item.database.PagingQueryProvider;
import org.springframework.batch.support.DatabaseType;
@@ -56,19 +57,19 @@
*/
public class SqlPagingQueryProviderFactoryBean implements FactoryBean {
- private DataSource dataSource;
+ private @Nullable DataSource dataSource;
- private String databaseType;
+ private @Nullable String databaseType;
- private String fromClause;
+ private @Nullable String fromClause;
- private String whereClause;
+ private @Nullable String whereClause;
- private String selectClause;
+ private @Nullable String selectClause;
- private String groupClause;
+ private @Nullable String groupClause;
- private Map sortKeys;
+ private @Nullable Map sortKeys;
private final Map providers = new HashMap<>();
@@ -141,11 +142,7 @@ public void setSortKeys(Map sortKeys) {
public void setSortKey(String key) {
Assert.doesNotContain(key, ",", "String setter is valid for a single ASC key only");
-
- Map keys = new LinkedHashMap<>();
- keys.put(key, Order.ASCENDING);
-
- this.sortKeys = keys;
+ this.sortKeys = Map.of(key, Order.ASCENDING);
}
/**
@@ -154,6 +151,7 @@ public void setSortKey(String key) {
*
* @see FactoryBean#getObject()
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public PagingQueryProvider getObject() throws Exception {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java
index 265c6275c3..43cc12bdc2 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java
@@ -21,6 +21,7 @@
import java.util.Map;
import java.util.Map.Entry;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.database.Order;
import org.springframework.util.StringUtils;
@@ -155,7 +156,7 @@ public static String generateRowNumSqlQuery(AbstractSqlPagingQueryProvider provi
* @param rowNumClause the implementation specific row num clause to be used
* @return the generated query
*/
- public static String generateRowNumSqlQuery(AbstractSqlPagingQueryProvider provider, String selectClause,
+ public static String generateRowNumSqlQuery(AbstractSqlPagingQueryProvider provider, @Nullable String selectClause,
boolean remainingPageQuery, String rowNumClause) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM (SELECT ").append(selectClause);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/package-info.java
index 67d58af49e..d12a3ee574 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/package-info.java
@@ -4,7 +4,7 @@
* @author Michael Minella
* @author Mahmoud Ben Hassine
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.database.support;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java
index c990d97708..eb7cc3421b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java
@@ -22,6 +22,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ReaderNotOpenException;
@@ -30,7 +31,6 @@
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -49,6 +49,7 @@
*
* @author Robert Kasanicky
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public class FlatFileItemReader extends AbstractItemCountingItemStreamItemReader
implements ResourceAwareItemReaderItemStream, InitializingBean {
@@ -61,9 +62,9 @@ public class FlatFileItemReader extends AbstractItemCountingItemStreamItemRea
private RecordSeparatorPolicy recordSeparatorPolicy = new SimpleRecordSeparatorPolicy();
- private Resource resource;
+ private @Nullable Resource resource;
- private BufferedReader reader;
+ private @Nullable BufferedReader reader;
private int lineCount = 0;
@@ -73,11 +74,11 @@ public class FlatFileItemReader extends AbstractItemCountingItemStreamItemRea
private String encoding = DEFAULT_CHARSET;
- private LineMapper lineMapper;
+ private @Nullable LineMapper lineMapper;
private int linesToSkip = 0;
- private LineCallbackHandler skippedLinesCallback;
+ private @Nullable LineCallbackHandler skippedLinesCallback;
private boolean strict = true;
@@ -158,7 +159,7 @@ public void setComments(String[] comments) {
* Public setter for the input resource.
*/
@Override
- public void setResource(Resource resource) {
+ public void setResource(@Nullable Resource resource) {
this.resource = resource;
}
@@ -177,9 +178,9 @@ public void setRecordSeparatorPolicy(RecordSeparatorPolicy recordSeparatorPolicy
* {@link #setRecordSeparatorPolicy(RecordSeparatorPolicy)} (might span multiple lines
* in file).
*/
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- protected T doRead() throws Exception {
+ protected @Nullable T doRead() throws Exception {
if (noInput) {
return null;
}
@@ -203,8 +204,7 @@ protected T doRead() throws Exception {
/**
* @return next line (skip comments).getCurrentResource
*/
- @Nullable
- private String readLine() {
+ private @Nullable String readLine() {
if (reader == null) {
throw new ReaderNotOpenException("Reader must be open before it can be read.");
@@ -213,18 +213,14 @@ private String readLine() {
String line = null;
try {
- line = this.reader.readLine();
- if (line == null) {
- return null;
- }
- lineCount++;
- while (isComment(line)) {
+ do {
line = reader.readLine();
if (line == null) {
return null;
}
lineCount++;
}
+ while (isComment(line));
line = applyRecordSeparatorPolicy(line);
}
@@ -281,7 +277,7 @@ protected void doOpen() throws Exception {
reader = bufferedReaderFactory.create(resource, encoding);
for (int i = 0; i < linesToSkip; i++) {
String line = readLine();
- if (skippedLinesCallback != null) {
+ if (skippedLinesCallback != null && line != null) {
skippedLinesCallback.handleLine(line);
}
}
@@ -300,10 +296,11 @@ protected void jumpToItem(int itemIndex) throws Exception {
}
}
+ @SuppressWarnings("DataFlowIssue")
private String applyRecordSeparatorPolicy(String line) throws IOException {
String record = line;
- while (line != null && !recordSeparatorPolicy.isEndOfRecord(record)) {
+ while (!recordSeparatorPolicy.isEndOfRecord(record)) {
line = this.reader.readLine();
if (line == null) {
if (StringUtils.hasText(record)) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java
index 889e4ff5dc..6cd5970580 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.file;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.file.transform.LineAggregator;
import org.springframework.batch.item.support.AbstractFileItemWriter;
@@ -38,10 +39,11 @@
* @author Dave Syer
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public class FlatFileItemWriter extends AbstractFileItemWriter {
- protected LineAggregator lineAggregator;
+ protected @Nullable LineAggregator lineAggregator;
public FlatFileItemWriter() {
this.setExecutionContextName(ClassUtils.getShortName(FlatFileItemWriter.class));
@@ -69,6 +71,7 @@ public void setLineAggregator(LineAggregator lineAggregator) {
this.lineAggregator = lineAggregator;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public String doWrite(Chunk extends T> items) {
StringBuilder lines = new StringBuilder();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java
index 63ebf7617e..610882dc8e 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java
@@ -21,6 +21,8 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemStreamException;
@@ -29,7 +31,6 @@
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.batch.item.support.AbstractItemStreamItemReader;
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -51,9 +52,9 @@ public class MultiResourceItemReader extends AbstractItemStreamItemReader
private static final String RESOURCE_KEY = "resourceIndex";
- private ResourceAwareItemReaderItemStream extends T> delegate;
+ private @Nullable ResourceAwareItemReaderItemStream extends T> delegate;
- private Resource[] resources;
+ private Resource @Nullable [] resources;
private boolean saveState = true;
@@ -79,6 +80,7 @@ public void setStrict(boolean strict) {
/**
* Compares resource filenames.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public int compare(Resource r1, Resource r2) {
return r1.getFilename().compareTo(r2.getFilename());
@@ -93,9 +95,9 @@ public MultiResourceItemReader() {
/**
* Reads the next item, jumping to next resource if necessary.
*/
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- public T read() throws Exception, UnexpectedInputException, ParseException {
+ public @Nullable T read() throws Exception {
if (noInput) {
return null;
@@ -117,7 +119,8 @@ public T read() throws Exception, UnexpectedInputException, ParseException {
* exhausted. Items are appended to the buffer.
* @return next item from input
*/
- private T readNextItem() throws Exception {
+ @SuppressWarnings("DataFlowIssue")
+ private @Nullable T readNextItem() throws Exception {
T item = readFromDelegate();
@@ -139,7 +142,8 @@ private T readNextItem() throws Exception {
return item;
}
- private T readFromDelegate() throws Exception {
+ @SuppressWarnings("DataFlowIssue")
+ private @Nullable T readFromDelegate() throws Exception {
T item = delegate.read();
if (item instanceof ResourceAware resourceAware) {
resourceAware.setResource(resources[currentResource]);
@@ -151,6 +155,7 @@ private T readFromDelegate() throws Exception {
* Close the {@link #setDelegate(ResourceAwareItemReaderItemStream)} reader and reset
* instance variable values.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() throws ItemStreamException {
super.close();
@@ -166,6 +171,7 @@ public void close() throws ItemStreamException {
* Figure out which resource to start with in case of restart, open the delegate and
* restore delegate's position in the resource.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
super.open(executionContext);
@@ -205,6 +211,7 @@ public void open(ExecutionContext executionContext) throws ItemStreamException {
/**
* Store the current resource index and position in the resource.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
super.update(executionContext);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java
index d07cbda99d..989127c6e4 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java
@@ -19,6 +19,7 @@
import java.io.File;
import java.io.IOException;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
@@ -50,9 +51,9 @@ public class MultiResourceItemWriter extends AbstractItemStreamItemWriter
final static private String CURRENT_RESOURCE_ITEM_COUNT = "resource.item.count";
- private Resource resource;
+ private @Nullable Resource resource;
- private ResourceAwareItemWriterItemStream super T> delegate;
+ private @Nullable ResourceAwareItemWriterItemStream super T> delegate;
private int itemCountLimitPerResource = Integer.MAX_VALUE;
@@ -70,6 +71,7 @@ public MultiResourceItemWriter() {
this.setExecutionContextName(ClassUtils.getShortName(MultiResourceItemWriter.class));
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends T> items) throws Exception {
int writtenItems = 0;
@@ -144,6 +146,7 @@ public void setSaveState(boolean saveState) {
this.saveState = saveState;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() throws ItemStreamException {
super.close();
@@ -154,6 +157,7 @@ public void close() throws ItemStreamException {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
super.open(executionContext);
@@ -177,6 +181,7 @@ public void open(ExecutionContext executionContext) throws ItemStreamException {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
super.update(executionContext);
@@ -192,6 +197,7 @@ public void update(ExecutionContext executionContext) throws ItemStreamException
/**
* Create output resource (if necessary) and point the delegate to it.
*/
+ @SuppressWarnings("DataFlowIssue")
private File setResourceToDelegate() throws IOException {
String path = resource.getFile().getAbsolutePath() + suffixCreator.getSuffix(resourceIndex);
File file = new File(path);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/NonTransientFlatFileException.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/NonTransientFlatFileException.java
index 0e322610c1..9fff9e7768 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/NonTransientFlatFileException.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/NonTransientFlatFileException.java
@@ -15,6 +15,7 @@
*/
package org.springframework.batch.item.file;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.NonTransientResourceException;
/**
@@ -25,7 +26,7 @@
*/
public class NonTransientFlatFileException extends NonTransientResourceException {
- private final String input;
+ private final @Nullable String input;
private int lineNumber;
@@ -40,13 +41,13 @@ public NonTransientFlatFileException(String message, String input, int lineNumbe
this.lineNumber = lineNumber;
}
- public NonTransientFlatFileException(String message, Throwable cause, String input, int lineNumber) {
+ public NonTransientFlatFileException(String message, Throwable cause, @Nullable String input, int lineNumber) {
super(message, cause);
this.input = input;
this.lineNumber = lineNumber;
}
- public String getInput() {
+ public @Nullable String getInput() {
return input;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/ResourcesItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/ResourcesItemReader.java
index 9f675de38b..209c07cd27 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/ResourcesItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/ResourcesItemReader.java
@@ -21,11 +21,12 @@
import org.springframework.batch.item.support.AbstractItemStreamItemReader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceArrayPropertyEditor;
-import org.springframework.lang.Nullable;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
+import org.jspecify.annotations.Nullable;
+
/**
* {@link ItemReader} which produces {@link Resource} instances from an array. This can be
* used conveniently with a configuration entry that injects a pattern (e.g.
@@ -72,8 +73,7 @@ public void setResources(Resource[] resources) {
* or {@code null} if none remain.
*/
@Override
- @Nullable
- public synchronized Resource read() throws Exception {
+ public synchronized @Nullable Resource read() throws Exception {
int index = counter.incrementAndGet() - 1;
if (index >= resources.length) {
return null;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/SimpleBinaryBufferedReaderFactory.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/SimpleBinaryBufferedReaderFactory.java
index fe6f2e3f7c..5555182c8b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/SimpleBinaryBufferedReaderFactory.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/SimpleBinaryBufferedReaderFactory.java
@@ -24,6 +24,8 @@
import org.springframework.core.io.Resource;
+import org.jspecify.annotations.Nullable;
+
/**
* A {@link BufferedReaderFactory} useful for reading simple binary (or text) files with
* no line endings, such as those produced by mainframe copy books. The reader splits a
@@ -76,7 +78,7 @@ private BinaryBufferedReader(Reader in, String ending) {
}
@Override
- public String readLine() throws IOException {
+ public @Nullable String readLine() throws IOException {
StringBuilder buffer;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemReaderBuilder.java
index e52d4dbde9..a38ae322bb 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemReaderBuilder.java
@@ -29,6 +29,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.BufferedReaderFactory;
import org.springframework.batch.item.file.DefaultBufferedReaderFactory;
import org.springframework.batch.item.file.FlatFileItemReader;
@@ -60,6 +61,7 @@
* @author Drummond Dawson
* @author Patrick Baumgartner
* @author François Martin
+ * @author Stefano Cordio
* @since 4.0
* @see FlatFileItemReader
*/
@@ -75,29 +77,29 @@ public class FlatFileItemReaderBuilder {
private BufferedReaderFactory bufferedReaderFactory = new DefaultBufferedReaderFactory();
- private Resource resource;
+ private @Nullable Resource resource;
private List comments = new ArrayList<>(Arrays.asList(FlatFileItemReader.DEFAULT_COMMENT_PREFIXES));
private int linesToSkip = 0;
- private LineCallbackHandler skippedLinesCallback;
+ private @Nullable LineCallbackHandler skippedLinesCallback;
- private LineMapper lineMapper;
+ private @Nullable LineMapper lineMapper;
- private FieldSetMapper fieldSetMapper;
+ private @Nullable FieldSetMapper fieldSetMapper;
- private LineTokenizer lineTokenizer;
+ private @Nullable LineTokenizer lineTokenizer;
- private DelimitedBuilder delimitedBuilder;
+ private @Nullable DelimitedBuilder delimitedBuilder;
- private FixedLengthBuilder fixedLengthBuilder;
+ private @Nullable FixedLengthBuilder fixedLengthBuilder;
- private Class targetType;
+ private @Nullable Class targetType;
- private String prototypeBeanName;
+ private @Nullable String prototypeBeanName;
- private BeanFactory beanFactory;
+ private @Nullable BeanFactory beanFactory;
private final Map, PropertyEditor> customEditors = new HashMap<>();
@@ -109,7 +111,7 @@ public class FlatFileItemReaderBuilder {
private boolean saveState = true;
- private String name;
+ private @Nullable String name;
private int maxItemCount = Integer.MAX_VALUE;
@@ -299,8 +301,7 @@ public FlatFileItemReaderBuilder fieldSetMapper(FieldSetMapper mapper) {
* @see DefaultLineMapper#setLineTokenizer(LineTokenizer)
*/
public FlatFileItemReaderBuilder lineTokenizer(LineTokenizer tokenizer) {
- updateTokenizerValidation(tokenizer, 0);
-
+ this.tokenizerValidator = this.tokenizerValidator.flipBit(0);
this.lineTokenizer = tokenizer;
return this;
}
@@ -315,7 +316,7 @@ public FlatFileItemReaderBuilder lineTokenizer(LineTokenizer tokenizer) {
*/
public DelimitedBuilder delimited() {
this.delimitedBuilder = new DelimitedBuilder<>(this);
- updateTokenizerValidation(this.delimitedBuilder, 1);
+ this.tokenizerValidator = this.tokenizerValidator.flipBit(1);
return this.delimitedBuilder;
}
@@ -328,15 +329,15 @@ public DelimitedBuilder delimited() {
*/
public FixedLengthBuilder fixedLength() {
this.fixedLengthBuilder = new FixedLengthBuilder<>(this);
- updateTokenizerValidation(this.fixedLengthBuilder, 2);
+ this.tokenizerValidator = this.tokenizerValidator.flipBit(2);
return this.fixedLengthBuilder;
}
/**
* The class that will represent the "item" to be returned from the reader. This class
* is used via the {@link BeanWrapperFieldSetMapper}. If more complex logic is
- * required, providing your own {@link FieldSetMapper} via
- * {@link FlatFileItemReaderBuilder#fieldSetMapper} is required.
+ * required, providing your own {@link FieldSetMapper} via {@link #fieldSetMapper} is
+ * required.
* @param targetType The class to map to
* @return The current instance of the builder.
* @see BeanWrapperFieldSetMapper#setTargetType(Class)
@@ -377,10 +378,7 @@ public FlatFileItemReaderBuilder beanFactory(BeanFactory beanFactory) {
* @see BeanWrapperFieldSetMapper#setCustomEditors(Map)
*/
public FlatFileItemReaderBuilder customEditors(Map, PropertyEditor> customEditors) {
- if (customEditors != null) {
- this.customEditors.putAll(customEditors);
- }
-
+ this.customEditors.putAll(customEditors);
return this;
}
@@ -471,10 +469,16 @@ else if (this.delimitedBuilder != null) {
}
else {
BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper<>();
- mapper.setTargetType(this.targetType);
- mapper.setPrototypeBeanName(this.prototypeBeanName);
+ if (this.prototypeBeanName != null) {
+ mapper.setPrototypeBeanName(this.prototypeBeanName);
+ }
+ if (this.beanFactory != null) {
+ mapper.setBeanFactory(this.beanFactory);
+ }
+ if (this.targetType != null) {
+ mapper.setTargetType(this.targetType);
+ }
mapper.setStrict(this.beanMapperStrict);
- mapper.setBeanFactory(this.beanFactory);
mapper.setDistanceLimit(this.distanceLimit);
mapper.setCustomEditors(this.customEditors);
try {
@@ -497,9 +501,11 @@ else if (this.fieldSetMapper != null) {
}
reader.setLinesToSkip(this.linesToSkip);
- reader.setComments(this.comments.toArray(new String[this.comments.size()]));
+ reader.setComments(this.comments.toArray(new String[0]));
- reader.setSkippedLinesCallback(this.skippedLinesCallback);
+ if (this.skippedLinesCallback != null) {
+ reader.setSkippedLinesCallback(this.skippedLinesCallback);
+ }
reader.setRecordSeparatorPolicy(this.recordSeparatorPolicy);
reader.setBufferedReaderFactory(this.bufferedReaderFactory);
reader.setMaxItemCount(this.maxItemCount);
@@ -510,15 +516,6 @@ else if (this.fieldSetMapper != null) {
return reader;
}
- private void updateTokenizerValidation(Object tokenizer, int index) {
- if (tokenizer != null) {
- this.tokenizerValidator = this.tokenizerValidator.flipBit(index);
- }
- else {
- this.tokenizerValidator = this.tokenizerValidator.clearBit(index);
- }
- }
-
/**
* A builder for constructing a {@link DelimitedLineTokenizer}
*
@@ -530,9 +527,9 @@ public static class DelimitedBuilder {
private final List names = new ArrayList<>();
- private String delimiter;
+ private @Nullable String delimiter;
- private Character quoteCharacter;
+ private @Nullable Character quoteCharacter;
private final List includedFields = new ArrayList<>();
@@ -638,7 +635,7 @@ public DelimitedLineTokenizer build() {
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
- tokenizer.setNames(this.names.toArray(new String[this.names.size()]));
+ tokenizer.setNames(this.names.toArray(new String[0]));
if (StringUtils.hasLength(this.delimiter)) {
tokenizer.setDelimiter(this.delimiter);
@@ -780,8 +777,8 @@ public FixedLengthTokenizer build() {
FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
- tokenizer.setNames(this.names.toArray(new String[this.names.size()]));
- tokenizer.setColumns(this.ranges.toArray(new Range[this.ranges.size()]));
+ tokenizer.setNames(this.names.toArray(new String[0]));
+ tokenizer.setColumns(this.ranges.toArray(new Range[0]));
tokenizer.setFieldSetFactory(this.fieldSetFactory);
tokenizer.setStrict(this.strict);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemWriterBuilder.java
index 7de7de5301..0ffc9262a4 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/FlatFileItemWriterBuilder.java
@@ -23,6 +23,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.FlatFileFooterCallback;
import org.springframework.batch.item.file.FlatFileHeaderCallback;
import org.springframework.batch.item.file.FlatFileItemWriter;
@@ -43,6 +44,7 @@
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
* @author Drummond Dawson
+ * @author Stefano Cordio
* @since 4.0
* @see FlatFileItemWriter
*/
@@ -50,13 +52,13 @@ public class FlatFileItemWriterBuilder {
protected Log logger = LogFactory.getLog(getClass());
- private WritableResource resource;
+ private @Nullable WritableResource resource;
private boolean forceSync = false;
private String lineSeparator = FlatFileItemWriter.DEFAULT_LINE_SEPARATOR;
- private LineAggregator lineAggregator;
+ private @Nullable LineAggregator lineAggregator;
private String encoding = FlatFileItemWriter.DEFAULT_CHARSET;
@@ -66,19 +68,19 @@ public class FlatFileItemWriterBuilder {
private boolean shouldDeleteIfEmpty = false;
- private FlatFileHeaderCallback headerCallback;
+ private @Nullable FlatFileHeaderCallback headerCallback;
- private FlatFileFooterCallback footerCallback;
+ private @Nullable FlatFileFooterCallback footerCallback;
private boolean transactional = FlatFileItemWriter.DEFAULT_TRANSACTIONAL;
private boolean saveState = true;
- private String name;
+ private @Nullable String name;
- private DelimitedBuilder delimitedBuilder;
+ private @Nullable DelimitedBuilder delimitedBuilder;
- private FormattedBuilder formattedBuilder;
+ private @Nullable FormattedBuilder formattedBuilder;
/**
* Configure if the state of the
@@ -280,7 +282,7 @@ public static class FormattedBuilder {
private final FlatFileItemWriterBuilder parent;
- private String format;
+ private @Nullable String format;
private Locale locale = Locale.getDefault();
@@ -288,11 +290,11 @@ public static class FormattedBuilder {
private int minimumLength = 0;
- private FieldExtractor fieldExtractor;
+ private @Nullable FieldExtractor fieldExtractor;
private final List names = new ArrayList<>();
- private Class sourceType;
+ private @Nullable Class sourceType;
protected FormattedBuilder(FlatFileItemWriterBuilder parent) {
this.parent = parent;
@@ -380,7 +382,7 @@ public FlatFileItemWriterBuilder names(String... names) {
public FormatterLineAggregator build() {
Assert.notNull(this.format, "A format is required");
- Assert.isTrue((this.names != null && !this.names.isEmpty()) || this.fieldExtractor != null,
+ Assert.isTrue(!this.names.isEmpty() || this.fieldExtractor != null,
"A list of field names or a field extractor is required");
FormatterLineAggregator formatterLineAggregator = new FormatterLineAggregator<>();
@@ -395,7 +397,7 @@ public FormatterLineAggregator build() {
}
else {
BeanWrapperFieldExtractor beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<>();
- beanWrapperFieldExtractor.setNames(this.names.toArray(new String[this.names.size()]));
+ beanWrapperFieldExtractor.setNames(this.names.toArray(new String[0]));
try {
beanWrapperFieldExtractor.afterPropertiesSet();
this.fieldExtractor = beanWrapperFieldExtractor;
@@ -427,9 +429,9 @@ public static class DelimitedBuilder {
private String quoteCharacter = "";
- private FieldExtractor fieldExtractor;
+ private @Nullable FieldExtractor fieldExtractor;
- private Class sourceType;
+ private @Nullable Class sourceType;
protected DelimitedBuilder(FlatFileItemWriterBuilder parent) {
this.parent = parent;
@@ -497,13 +499,12 @@ public FlatFileItemWriterBuilder fieldExtractor(FieldExtractor fieldExtrac
}
public DelimitedLineAggregator build() {
- Assert.isTrue((this.names != null && !this.names.isEmpty()) || this.fieldExtractor != null,
+ Assert.isTrue(!this.names.isEmpty() || this.fieldExtractor != null,
"A list of field names or a field extractor is required");
DelimitedLineAggregator delimitedLineAggregator = new DelimitedLineAggregator<>();
- if (this.delimiter != null) {
- delimitedLineAggregator.setDelimiter(this.delimiter);
- }
+ delimitedLineAggregator.setDelimiter(this.delimiter);
+
if (StringUtils.hasLength(this.quoteCharacter)) {
delimitedLineAggregator.setQuoteCharacter(this.quoteCharacter);
}
@@ -514,7 +515,7 @@ public DelimitedLineAggregator build() {
}
else {
BeanWrapperFieldExtractor beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<>();
- beanWrapperFieldExtractor.setNames(this.names.toArray(new String[this.names.size()]));
+ beanWrapperFieldExtractor.setNames(this.names.toArray(new String[0]));
try {
beanWrapperFieldExtractor.afterPropertiesSet();
this.fieldExtractor = beanWrapperFieldExtractor;
@@ -551,12 +552,18 @@ public FlatFileItemWriter build() {
FlatFileItemWriter writer = new FlatFileItemWriter<>();
- writer.setName(this.name);
+ if (this.name != null) {
+ writer.setName(this.name);
+ }
writer.setAppendAllowed(this.append);
writer.setEncoding(this.encoding);
- writer.setFooterCallback(this.footerCallback);
+ if (this.footerCallback != null) {
+ writer.setFooterCallback(this.footerCallback);
+ }
writer.setForceSync(this.forceSync);
- writer.setHeaderCallback(this.headerCallback);
+ if (this.headerCallback != null) {
+ writer.setHeaderCallback(this.headerCallback);
+ }
if (this.lineAggregator == null) {
Assert.state(this.delimitedBuilder == null || this.formattedBuilder == null,
"Either a DelimitedLineAggregator or a FormatterLineAggregator should be provided, but not both");
@@ -564,12 +571,15 @@ public FlatFileItemWriter build() {
this.lineAggregator = this.delimitedBuilder.build();
}
else {
+ Assert.state(this.formattedBuilder != null, "A FormattedBuilder is required");
this.lineAggregator = this.formattedBuilder.build();
}
}
writer.setLineAggregator(this.lineAggregator);
writer.setLineSeparator(this.lineSeparator);
- writer.setResource(this.resource);
+ if (this.resource != null) {
+ writer.setResource(this.resource);
+ }
writer.setSaveState(this.saveState);
writer.setShouldDeleteIfEmpty(this.shouldDeleteIfEmpty);
writer.setShouldDeleteIfExists(this.shouldDeleteIfExists);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemReaderBuilder.java
index bfb6927518..4ee65dd80c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemReaderBuilder.java
@@ -18,6 +18,7 @@
import java.util.Comparator;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.MultiResourceItemReader;
import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream;
import org.springframework.core.io.Resource;
@@ -29,22 +30,23 @@
*
* @author Glenn Renfro
* @author Drummond Dawson
+ * @author Stefano Cordio
* @since 4.0
* @see MultiResourceItemReader
*/
public class MultiResourceItemReaderBuilder {
- private ResourceAwareItemReaderItemStream extends T> delegate;
+ private @Nullable ResourceAwareItemReaderItemStream extends T> delegate;
- private Resource[] resources;
+ private Resource @Nullable [] resources;
private boolean strict = false;
- private Comparator comparator;
+ private @Nullable Comparator comparator;
private boolean saveState = true;
- private String name;
+ private @Nullable String name;
/**
* Configure if the state of the
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemWriterBuilder.java
index 101d283a7d..ecae5a112c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/MultiResourceItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.file.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.MultiResourceItemWriter;
import org.springframework.batch.item.file.ResourceAwareItemWriterItemStream;
import org.springframework.batch.item.file.ResourceSuffixCreator;
@@ -32,17 +33,17 @@
*/
public class MultiResourceItemWriterBuilder {
- private Resource resource;
+ private @Nullable Resource resource;
- private ResourceAwareItemWriterItemStream super T> delegate;
+ private @Nullable ResourceAwareItemWriterItemStream super T> delegate;
private int itemCountLimitPerResource = Integer.MAX_VALUE;
- private ResourceSuffixCreator suffixCreator;
+ private @Nullable ResourceSuffixCreator suffixCreator;
private boolean saveState = true;
- private String name;
+ private @Nullable String name;
/**
* Configure if the state of the
@@ -142,7 +143,9 @@ public MultiResourceItemWriter build() {
writer.setResourceSuffixCreator(this.suffixCreator);
}
writer.setSaveState(this.saveState);
- writer.setName(this.name);
+ if (this.name != null) {
+ writer.setName(this.name);
+ }
return writer;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/package-info.java
index 6ceabb3b91..4e4ccc71e1 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/builder/package-info.java
@@ -18,8 +18,9 @@
* Builders for file item readers and writers.
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.file.builder;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java
index 81bfa97739..1ab6612cb8 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2025 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.
@@ -26,6 +26,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.support.DefaultPropertyEditorRegistrar;
import org.springframework.beans.BeanWrapperImpl;
@@ -88,16 +89,16 @@
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
- *
+ * @author Stefano Cordio
*/
public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar
implements FieldSetMapper, BeanFactoryAware, InitializingBean {
- private String name;
+ private @Nullable String name;
- private Class extends T> type;
+ private @Nullable Class extends T> type;
- private BeanFactory beanFactory;
+ private @Nullable BeanFactory beanFactory;
private final ConcurrentMap> propertiesMatched = new ConcurrentHashMap<>();
@@ -105,7 +106,7 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar
private boolean strict = true;
- private ConversionService conversionService;
+ private @Nullable ConversionService conversionService;
private boolean isCustomEditorsSet;
@@ -152,7 +153,7 @@ public void setTargetType(Class extends T> type) {
* Check that precisely one of type or prototype bean name is specified.
* @throws IllegalStateException if neither is set or both properties are set.
*
- * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+ * @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
@@ -169,7 +170,7 @@ public void afterPropertiesSet() throws Exception {
* {@link DataBinder} from {@link #createBinder(Object)} has errors after binding).
* @throws NotWritablePropertyException if the {@link FieldSet} contains a field that
* cannot be mapped to a bean property.
- * @see org.springframework.batch.item.file.mapping.FieldSetMapper#mapFieldSet(FieldSet)
+ * @see FieldSetMapper#mapFieldSet(FieldSet)
*/
@Override
public T mapFieldSet(FieldSet fs) throws BindException {
@@ -215,7 +216,7 @@ protected DataBinder createBinder(Object target) {
protected void initBinder(DataBinder binder) {
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "DataFlowIssue" })
private T getBean() {
if (name != null) {
return (T) beanFactory.getBean(name);
@@ -275,11 +276,7 @@ private Properties getBeanProperties(Object bean, Properties properties) {
return properties;
}
- private String findPropertyName(Object bean, String key) {
-
- if (bean == null) {
- return null;
- }
+ private @Nullable String findPropertyName(Object bean, String key) {
Class> cls = bean.getClass();
@@ -333,6 +330,7 @@ private String findPropertyName(Object bean, String key) {
return name;
}
+ @SuppressWarnings("DataFlowIssue")
private Object getPropertyValue(Object bean, String nestedName) {
BeanWrapperImpl wrapper = new BeanWrapperImpl(bean);
wrapper.setAutoGrowNestedPaths(true);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/DefaultLineMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/DefaultLineMapper.java
index 5392887fa7..c501a609db 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/DefaultLineMapper.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/DefaultLineMapper.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.file.mapping;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.item.file.transform.LineTokenizer;
@@ -33,10 +34,11 @@
*/
public class DefaultLineMapper implements LineMapper, InitializingBean {
- private LineTokenizer tokenizer;
+ private @Nullable LineTokenizer tokenizer;
- private FieldSetMapper fieldSetMapper;
+ private @Nullable FieldSetMapper fieldSetMapper;
+ @SuppressWarnings("DataFlowIssue")
@Override
public T mapLine(String line, int lineNumber) throws Exception {
return fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PatternMatchingCompositeLineMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PatternMatchingCompositeLineMapper.java
index ef91240350..662ba3b6f3 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PatternMatchingCompositeLineMapper.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/PatternMatchingCompositeLineMapper.java
@@ -18,6 +18,7 @@
import java.util.Map;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.batch.item.file.transform.PatternMatchingCompositeLineTokenizer;
@@ -48,8 +49,9 @@ public class PatternMatchingCompositeLineMapper implements LineMapper, Ini
private final PatternMatchingCompositeLineTokenizer tokenizer = new PatternMatchingCompositeLineTokenizer();
- private PatternMatcher> patternMatcher;
+ private @Nullable PatternMatcher> patternMatcher;
+ @SuppressWarnings("DataFlowIssue")
@Override
public T mapLine(String line, int lineNumber) throws Exception {
return patternMatcher.match(line).mapFieldSet(this.tokenizer.tokenize(line));
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/RecordFieldSetMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/RecordFieldSetMapper.java
index 0eb449dab4..4684763e0f 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/RecordFieldSetMapper.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/RecordFieldSetMapper.java
@@ -17,6 +17,7 @@
import java.lang.reflect.Constructor;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.SimpleTypeConverter;
@@ -40,9 +41,9 @@ public class RecordFieldSetMapper implements FieldSetMapper {
private final Constructor mappedConstructor;
- private String[] constructorParameterNames;
+ private final @Nullable String[] constructorParameterNames;
- private Class>[] constructorParameterTypes;
+ private final Class>[] constructorParameterTypes;
/**
* Create a new {@link RecordFieldSetMapper}.
@@ -75,14 +76,11 @@ public T mapFieldSet(FieldSet fieldSet) {
Assert.isTrue(fieldSet.getFieldCount() == this.constructorParameterNames.length,
"Fields count must be equal to record components count");
Assert.isTrue(fieldSet.hasNames(), "Field names must be specified");
- Object[] args = new Object[0];
- if (this.constructorParameterNames != null && this.constructorParameterTypes != null) {
- args = new Object[this.constructorParameterNames.length];
- for (int i = 0; i < args.length; i++) {
- String name = this.constructorParameterNames[i];
- Class> type = this.constructorParameterTypes[i];
- args[i] = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);
- }
+ @Nullable Object[] args = new Object[this.constructorParameterNames.length];
+ for (int i = 0; i < args.length; i++) {
+ String name = this.constructorParameterNames[i];
+ Class> type = this.constructorParameterTypes[i];
+ args[i] = this.typeConverter.convertIfNecessary(fieldSet.readRawString(name), type);
}
return BeanUtils.instantiateClass(this.mappedConstructor, args);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/package-info.java
index 3405d55d18..0fc1065610 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/package-info.java
@@ -3,7 +3,7 @@
* Infrastructure implementations of io file support mapping concerns.
*
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.file.mapping;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/package-info.java
index 4365cd486d..ef31af1d82 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/package-info.java
@@ -3,7 +3,7 @@
* Infrastructure implementations of io file concerns.
*
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.file;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/DefaultRecordSeparatorPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/DefaultRecordSeparatorPolicy.java
index e188044f5c..918ad80848 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/DefaultRecordSeparatorPolicy.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/DefaultRecordSeparatorPolicy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the original author or authors.
+ * Copyright 2006-2025 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.item.file.separator;
+import org.jspecify.annotations.Nullable;
import org.springframework.util.StringUtils;
/**
@@ -24,7 +25,7 @@
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
- *
+ * @author Stefano Cordio
*/
public class DefaultRecordSeparatorPolicy extends SimpleRecordSeparatorPolicy {
@@ -71,7 +72,7 @@ public void setQuoteCharacter(String quoteCharacter) {
}
/**
- * Public setter for the continuation. Defaults to back slash.
+ * Public setter for the continuation. Defaults to backslash.
* @param continuation the continuation to set
*/
public void setContinuation(String continuation) {
@@ -79,22 +80,22 @@ public void setContinuation(String continuation) {
}
/**
- * Return true if the line does not have unterminated quotes (delimited by "), and
- * does not end with a continuation marker ('\'). The test for the continuation marker
- * ignores whitespace at the end of the line.
+ * Return true if the line does not have unterminated quotes (delimited by {@code "}),
+ * and does not end with a continuation marker ({@code \}). The test for the
+ * continuation marker ignores whitespace at the end of the line.
*
- * @see org.springframework.batch.item.file.separator.RecordSeparatorPolicy#isEndOfRecord(java.lang.String)
+ * @see RecordSeparatorPolicy#isEndOfRecord(String)
*/
@Override
- public boolean isEndOfRecord(String line) {
+ public boolean isEndOfRecord(@Nullable String line) {
return !isQuoteUnterminated(line) && !isContinued(line);
}
/**
- * If we are in an unterminated quote, add a line separator. Otherwise remove the
+ * If we are in an unterminated quote, add a line separator. Otherwise, remove the
* continuation marker (plus whitespace at the end) if it is there.
*
- * @see org.springframework.batch.item.file.separator.SimpleRecordSeparatorPolicy#preProcess(java.lang.String)
+ * @see SimpleRecordSeparatorPolicy#preProcess(String)
*/
@Override
public String preProcess(String line) {
@@ -113,8 +114,8 @@ public String preProcess(String line) {
* @param line the line to check
* @return true if the quote is unterminated, false otherwise
*/
- private boolean isQuoteUnterminated(String line) {
- return StringUtils.countOccurrencesOf(line, quoteCharacter) % 2 != 0;
+ private boolean isQuoteUnterminated(@Nullable String line) {
+ return line != null && StringUtils.countOccurrencesOf(line, quoteCharacter) % 2 != 0;
}
/**
@@ -123,11 +124,8 @@ private boolean isQuoteUnterminated(String line) {
* @param line the line to check
* @return true if the line ends with the continuation marker, false otherwise
*/
- private boolean isContinued(String line) {
- if (line == null) {
- return false;
- }
- return line.trim().endsWith(continuation);
+ private boolean isContinued(@Nullable String line) {
+ return line != null && line.trim().endsWith(continuation);
}
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/JsonRecordSeparatorPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/JsonRecordSeparatorPolicy.java
index 1571685396..ac8f301193 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/JsonRecordSeparatorPolicy.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/JsonRecordSeparatorPolicy.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.file.separator;
+import org.jspecify.annotations.Nullable;
import org.springframework.util.StringUtils;
/**
@@ -42,8 +43,8 @@ public class JsonRecordSeparatorPolicy extends SimpleRecordSeparatorPolicy {
* @see RecordSeparatorPolicy#isEndOfRecord(String)
*/
@Override
- public boolean isEndOfRecord(String line) {
- return StringUtils.countOccurrencesOf(line, "{") == StringUtils.countOccurrencesOf(line, "}")
+ public boolean isEndOfRecord(@Nullable String line) {
+ return line != null && StringUtils.countOccurrencesOf(line, "{") == StringUtils.countOccurrencesOf(line, "}")
&& line.trim().endsWith("}");
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/RecordSeparatorPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/RecordSeparatorPolicy.java
index db1135d2c4..72742d8b07 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/RecordSeparatorPolicy.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/RecordSeparatorPolicy.java
@@ -16,6 +16,8 @@
package org.springframework.batch.item.file.separator;
+import org.jspecify.annotations.Nullable;
+
import java.io.BufferedReader;
/**
@@ -36,14 +38,14 @@ public interface RecordSeparatorPolicy {
* @param record a String without a newline character at the end.
* @return true if this line is a complete record.
*/
- boolean isEndOfRecord(String record);
+ boolean isEndOfRecord(@Nullable String record);
/**
* Give the policy a chance to post-process a complete record, e.g. remove a suffix.
* @param record the complete record.
- * @return a modified version of the record if desired.
+ * @return a modified version of the record if desired, potentially null.
*/
- String postProcess(String record);
+ @Nullable String postProcess(String record);
/**
* Pre-process a record before another line is appended, in the case of a multi-line
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SimpleRecordSeparatorPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SimpleRecordSeparatorPolicy.java
index b26e32e559..6affe64a4d 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SimpleRecordSeparatorPolicy.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SimpleRecordSeparatorPolicy.java
@@ -16,6 +16,8 @@
package org.springframework.batch.item.file.separator;
+import org.jspecify.annotations.Nullable;
+
/**
* Simplest possible {@link RecordSeparatorPolicy} - treats all lines as record endings.
*
@@ -30,7 +32,7 @@ public class SimpleRecordSeparatorPolicy implements RecordSeparatorPolicy {
* @see org.springframework.batch.item.file.separator.RecordSeparatorPolicy#isEndOfRecord(java.lang.String)
*/
@Override
- public boolean isEndOfRecord(String line) {
+ public boolean isEndOfRecord(@Nullable String line) {
return true;
}
@@ -39,7 +41,7 @@ public boolean isEndOfRecord(String line) {
* @see org.springframework.batch.item.file.separator.RecordSeparatorPolicy#postProcess(java.lang.String)
*/
@Override
- public String postProcess(String record) {
+ public @Nullable String postProcess(String record) {
return record;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SuffixRecordSeparatorPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SuffixRecordSeparatorPolicy.java
index d8c1bb227a..e49b332624 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SuffixRecordSeparatorPolicy.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/SuffixRecordSeparatorPolicy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2007 the original author or authors.
+ * Copyright 2006-2025 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,12 +16,14 @@
package org.springframework.batch.item.file.separator;
+import org.jspecify.annotations.Nullable;
+
/**
* A {@link RecordSeparatorPolicy} that looks for an exact match for a String at the end
* of a line (e.g. a semicolon).
*
* @author Dave Syer
- *
+ * @author Stefano Cordio
*/
public class SuffixRecordSeparatorPolicy extends DefaultRecordSeparatorPolicy {
@@ -52,14 +54,13 @@ public void setIgnoreWhitespace(boolean ignoreWhitespace) {
}
/**
- * Return true if the line ends with the specified substring. By default whitespace is
- * trimmed before the comparison. Also returns true if the line is null, but not if it
- * is empty.
+ * Return true if the line ends with the specified substring. By default, whitespace
+ * is trimmed before the comparison.
*
- * @see org.springframework.batch.item.file.separator.RecordSeparatorPolicy#isEndOfRecord(java.lang.String)
+ * @see RecordSeparatorPolicy#isEndOfRecord(String)
*/
@Override
- public boolean isEndOfRecord(String line) {
+ public boolean isEndOfRecord(@Nullable String line) {
if (line == null) {
return true;
}
@@ -70,10 +71,10 @@ public boolean isEndOfRecord(String line) {
/**
* Remove the suffix from the end of the record.
*
- * @see org.springframework.batch.item.file.separator.SimpleRecordSeparatorPolicy#postProcess(java.lang.String)
+ * @see SimpleRecordSeparatorPolicy#postProcess(String)
*/
@Override
- public String postProcess(String record) {
+ public @Nullable String postProcess(@Nullable String record) {
if (record == null) {
return null;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/package-info.java
index 87f48e90e3..c37d9f9ef8 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/separator/package-info.java
@@ -3,7 +3,7 @@
* Infrastructure implementations of io file support separator concerns.
*
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.file.separator;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/AbstractLineTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/AbstractLineTokenizer.java
index b4fa1572fe..603e8ff2e9 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/AbstractLineTokenizer.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/AbstractLineTokenizer.java
@@ -17,12 +17,12 @@
package org.springframework.batch.item.file.transform;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
+import org.jspecify.annotations.Nullable;
+
/**
* Abstract class handling common concerns of various {@link LineTokenizer}
* implementations such as dealing with names and actual construction of {@link FieldSet}
@@ -32,15 +32,16 @@
* @author Lucas Ward
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public abstract class AbstractLineTokenizer implements LineTokenizer {
+ private static final String EMPTY_TOKEN = "";
+
protected String[] names = new String[0];
private boolean strict = true;
- private final String emptyToken = "";
-
private FieldSetFactory fieldSetFactory = new DefaultFieldSetFactory();
/**
@@ -77,21 +78,16 @@ public void setFieldSetFactory(FieldSetFactory fieldSetFactory) {
* @param names names of each column
*/
public void setNames(String... names) {
- if (names == null) {
- this.names = null;
- }
- else {
- boolean valid = false;
- for (String name : names) {
- if (StringUtils.hasText(name)) {
- valid = true;
- break;
- }
+ boolean valid = false;
+ for (String name : names) {
+ if (StringUtils.hasText(name)) {
+ valid = true;
+ break;
}
+ }
- if (valid) {
- this.names = Arrays.asList(names).toArray(new String[names.length]);
- }
+ if (valid) {
+ this.names = names.clone();
}
}
@@ -100,10 +96,7 @@ public void setNames(String... names) {
* @see #setNames(String[])
*/
public boolean hasNames() {
- if (names != null && names.length > 0) {
- return true;
- }
- return false;
+ return names.length > 0;
}
/**
@@ -125,7 +118,7 @@ public FieldSet tokenize(@Nullable String line) {
adjustTokenCountIfNecessary(tokens);
}
- String[] values = tokens.toArray(new String[tokens.size()]);
+ String[] values = tokens.toArray(new String[0]);
if (names.length == 0) {
return fieldSetFactory.create(values);
@@ -156,7 +149,7 @@ private void adjustTokenCountIfNecessary(List tokens) {
// add empty tokens until the token list size matches
// the expected number of tokens
for (int i = 0; i < (nameLength - tokensSize); i++) {
- tokens.add(emptyToken);
+ tokens.add(EMPTY_TOKEN);
}
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractor.java
index a7986901fa..f7c5142d48 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractor.java
@@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.List;
+import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.InitializingBean;
@@ -33,7 +34,7 @@
*/
public class BeanWrapperFieldExtractor implements FieldExtractor, InitializingBean {
- private String[] names;
+ private String @Nullable [] names;
/**
* @param names field names to be extracted by the {@link #extract(Object)} method.
@@ -43,9 +44,10 @@ public void setNames(String[] names) {
this.names = names.clone();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public Object[] extract(T item) {
- List values = new ArrayList<>();
+ List<@Nullable Object> values = new ArrayList<>();
BeanWrapper bw = new BeanWrapperImpl(item);
for (String propertyName : this.names) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSet.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSet.java
index c1c1d1b489..fbc4c0adf7 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSet.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2024 the original author or authors.
+ * Copyright 2006-2025 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.
@@ -26,10 +26,12 @@
import java.util.Date;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Properties;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.util.StringUtils;
/**
@@ -40,6 +42,7 @@
* @author Rob Harrop
* @author Dave Syer
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public class DefaultFieldSet implements FieldSet {
@@ -49,77 +52,61 @@ public class DefaultFieldSet implements FieldSet {
private NumberFormat numberFormat;
- private String grouping;
+ private @Nullable String grouping;
- private String decimal;
+ private @Nullable String decimal;
/**
* The fields wrapped by this 'FieldSet' instance.
*/
- private final String[] tokens;
-
- private List names;
-
- /**
- * The {@link NumberFormat} to use for parsing numbers. If unset the {@link Locale#US}
- * will be used ('.' as decimal place).
- * @param numberFormat the {@link NumberFormat} to use for number parsing
- */
- public final void setNumberFormat(NumberFormat numberFormat) {
- this.numberFormat = numberFormat;
- if (numberFormat instanceof DecimalFormat decimalFormat) {
- grouping = String.valueOf(decimalFormat.getDecimalFormatSymbols().getGroupingSeparator());
- decimal = String.valueOf(decimalFormat.getDecimalFormatSymbols().getDecimalSeparator());
- }
- }
+ private final @Nullable String[] tokens;
- private static NumberFormat getDefaultNumberFormat() {
- return NumberFormat.getInstance(Locale.US);
- }
+ private @Nullable List names;
/**
- * The {@link DateFormat} to use for parsing dates. If unset the default pattern is
- * ISO standard yyyy-MM-dd.
- * @param dateFormat the {@link DateFormat} to use for date parsing
+ * Create a FieldSet with anonymous tokens.
+ *
+ * They can only be retrieved by column number.
+ * @param tokens the token values
+ * @see FieldSet#readString(int)
*/
- public void setDateFormat(DateFormat dateFormat) {
- this.dateFormat = dateFormat;
- }
-
- private static DateFormat getDefaultDateFormat() {
- DateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
- dateFormat.setLenient(false);
- return dateFormat;
+ public DefaultFieldSet(@Nullable String @Nullable [] tokens) {
+ this(tokens, null, null);
}
/**
- * Create a FieldSet with anonymous tokens. They can only be retrieved by column
- * number.
+ * Create a FieldSet with anonymous tokens.
+ *
+ * They can only be retrieved by column number.
* @param tokens the token values
* @param dateFormat the {@link DateFormat} to use
* @param numberFormat the {@link NumberFormat} to use
* @see FieldSet#readString(int)
* @since 5.2
*/
- public DefaultFieldSet(String[] tokens, @Nullable DateFormat dateFormat, @Nullable NumberFormat numberFormat) {
- this.tokens = tokens == null ? null : tokens.clone();
- setDateFormat(dateFormat == null ? getDefaultDateFormat() : dateFormat);
- setNumberFormat(numberFormat == null ? getDefaultNumberFormat() : numberFormat);
+ public DefaultFieldSet(@Nullable String @Nullable [] tokens, @Nullable DateFormat dateFormat,
+ @Nullable NumberFormat numberFormat) {
+ this.tokens = tokens != null ? tokens.clone() : new String[0];
+ this.dateFormat = dateFormat != null ? dateFormat : getDefaultDateFormat();
+ setNumberFormat(numberFormat != null ? numberFormat : getDefaultNumberFormat());
}
/**
- * Create a FieldSet with anonymous tokens. They can only be retrieved by column
- * number.
+ * Create a FieldSet with named tokens.
+ *
+ * The token values can then be retrieved either by name or by column number.
* @param tokens the token values
- * @see FieldSet#readString(int)
+ * @param names the names of the tokens
+ * @see FieldSet#readString(String)
*/
- public DefaultFieldSet(String[] tokens) {
- this(tokens, null, null);
+ public DefaultFieldSet(@Nullable String[] tokens, String[] names) {
+ this(tokens, names, getDefaultDateFormat(), getDefaultNumberFormat());
}
/**
- * Create a FieldSet with named tokens. The token values can then be retrieved either
- * by name or by column number.
+ * Create a FieldSet with named tokens.
+ *
+ * The token values can then be retrieved either by name or by column number.
* @param tokens the token values
* @param names the names of the tokens
* @param dateFormat the {@link DateFormat} to use
@@ -127,7 +114,7 @@ public DefaultFieldSet(String[] tokens) {
* @see FieldSet#readString(String)
* @since 5.2
*/
- public DefaultFieldSet(String[] tokens, String[] names, @Nullable DateFormat dateFormat,
+ public DefaultFieldSet(@Nullable String[] tokens, String[] names, @Nullable DateFormat dateFormat,
@Nullable NumberFormat numberFormat) {
Assert.notNull(tokens, "Tokens must not be null");
Assert.notNull(names, "Names must not be null");
@@ -137,19 +124,42 @@ public DefaultFieldSet(String[] tokens, String[] names, @Nullable DateFormat dat
}
this.tokens = tokens.clone();
this.names = Arrays.asList(names);
- setDateFormat(dateFormat == null ? getDefaultDateFormat() : dateFormat);
- setNumberFormat(numberFormat == null ? getDefaultNumberFormat() : numberFormat);
+ this.dateFormat = dateFormat != null ? dateFormat : getDefaultDateFormat();
+ setNumberFormat(numberFormat != null ? numberFormat : getDefaultNumberFormat());
+ }
+
+ private static DateFormat getDefaultDateFormat() {
+ DateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
+ dateFormat.setLenient(false);
+ return dateFormat;
+ }
+
+ private static NumberFormat getDefaultNumberFormat() {
+ return NumberFormat.getInstance(Locale.US);
}
/**
- * Create a FieldSet with named tokens. The token values can then be retrieved either
- * by name or by column number.
- * @param tokens the token values
- * @param names the names of the tokens
- * @see FieldSet#readString(String)
+ * The {@link DateFormat} to use for parsing dates.
+ *
+ * If unset, the default pattern is ISO standard yyyy-MM-dd.
+ * @param dateFormat the {@link DateFormat} to use for date parsing
*/
- public DefaultFieldSet(String[] tokens, String[] names) {
- this(tokens, names, null, null);
+ public void setDateFormat(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
+ /**
+ * The {@link NumberFormat} to use for parsing numbers.
+ *
+ * If unset, {@link Locale#US} will be used ('.' as decimal place).
+ * @param numberFormat the {@link NumberFormat} to use for number parsing
+ */
+ public final void setNumberFormat(NumberFormat numberFormat) {
+ this.numberFormat = numberFormat;
+ if (numberFormat instanceof DecimalFormat decimalFormat) {
+ this.grouping = String.valueOf(decimalFormat.getDecimalFormatSymbols().getGroupingSeparator());
+ this.decimal = String.valueOf(decimalFormat.getDecimalFormatSymbols().getDecimalSeparator());
+ }
}
@Override
@@ -166,27 +176,27 @@ public boolean hasNames() {
}
@Override
- public String[] getValues() {
+ public @Nullable String[] getValues() {
return tokens.clone();
}
@Override
- public String readString(int index) {
+ public @Nullable String readString(int index) {
return readAndTrim(index);
}
@Override
- public String readString(String name) {
+ public @Nullable String readString(String name) {
return readString(indexOf(name));
}
@Override
- public String readRawString(int index) {
+ public @Nullable String readRawString(int index) {
return tokens[index];
}
@Override
- public String readRawString(String name) {
+ public @Nullable String readRawString(String name) {
return readRawString(indexOf(name));
}
@@ -203,10 +213,7 @@ public boolean readBoolean(String name) {
@Override
public boolean readBoolean(int index, String trueValue) {
Assert.notNull(trueValue, "'trueValue' cannot be null.");
-
- String value = readAndTrim(index);
-
- return trueValue.equals(value);
+ return trueValue.equals(readAndTrim(index));
}
@Override
@@ -216,10 +223,8 @@ public boolean readBoolean(String name, String trueValue) {
@Override
public char readChar(int index) {
- String value = readAndTrim(index);
-
+ String value = Objects.requireNonNull(readAndTrim(index));
Assert.isTrue(value.length() == 1, "Cannot convert field value '" + value + "' to char.");
-
return value.charAt(0);
}
@@ -230,7 +235,7 @@ public char readChar(String name) {
@Override
public byte readByte(int index) {
- return Byte.parseByte(readAndTrim(index));
+ return Byte.parseByte(Objects.requireNonNull(readAndTrim(index)));
}
@Override
@@ -240,7 +245,7 @@ public byte readByte(String name) {
@Override
public short readShort(int index) {
- return Short.parseShort(readAndTrim(index));
+ return Short.parseShort(Objects.requireNonNull(readAndTrim(index)));
}
@Override
@@ -250,7 +255,7 @@ public short readShort(String name) {
@Override
public int readInt(int index) {
- return parseNumber(readAndTrim(index)).intValue();
+ return parseNumber(Objects.requireNonNull(readAndTrim(index))).intValue();
}
@Override
@@ -272,7 +277,7 @@ public int readInt(String name, int defaultValue) {
@Override
public long readLong(int index) {
- return parseNumber(readAndTrim(index)).longValue();
+ return parseNumber(Objects.requireNonNull(readAndTrim(index))).longValue();
}
@Override
@@ -283,7 +288,6 @@ public long readLong(String name) {
@Override
public long readLong(int index, long defaultValue) {
String value = readAndTrim(index);
-
return StringUtils.hasLength(value) ? Long.parseLong(value) : defaultValue;
}
@@ -294,7 +298,7 @@ public long readLong(String name, long defaultValue) {
@Override
public float readFloat(int index) {
- return parseNumber(readAndTrim(index)).floatValue();
+ return parseNumber(Objects.requireNonNull(readAndTrim(index))).floatValue();
}
@Override
@@ -304,7 +308,7 @@ public float readFloat(String name) {
@Override
public double readDouble(int index) {
- return parseNumber(readAndTrim(index)).doubleValue();
+ return parseNumber(Objects.requireNonNull(readAndTrim(index))).doubleValue();
}
@Override
@@ -313,17 +317,17 @@ public double readDouble(String name) {
}
@Override
- public BigDecimal readBigDecimal(int index) {
+ public @Nullable BigDecimal readBigDecimal(int index) {
return readBigDecimal(index, null);
}
@Override
- public BigDecimal readBigDecimal(String name) {
+ public @Nullable BigDecimal readBigDecimal(String name) {
return readBigDecimal(name, null);
}
@Override
- public BigDecimal readBigDecimal(int index, BigDecimal defaultValue) {
+ public @Nullable BigDecimal readBigDecimal(int index, @Nullable BigDecimal defaultValue) {
String candidate = readAndTrim(index);
if (!StringUtils.hasText(candidate)) {
@@ -331,8 +335,7 @@ public BigDecimal readBigDecimal(int index, BigDecimal defaultValue) {
}
try {
- String result = removeSeparators(candidate);
- return new BigDecimal(result);
+ return new BigDecimal(removeSeparators(candidate));
}
catch (NumberFormatException e) {
throw new NumberFormatException("Unparseable number: " + candidate);
@@ -344,7 +347,7 @@ private String removeSeparators(String candidate) {
}
@Override
- public BigDecimal readBigDecimal(String name, BigDecimal defaultValue) {
+ public @Nullable BigDecimal readBigDecimal(String name, @Nullable BigDecimal defaultValue) {
try {
return readBigDecimal(indexOf(name), defaultValue);
}
@@ -358,7 +361,7 @@ public BigDecimal readBigDecimal(String name, BigDecimal defaultValue) {
@Override
public Date readDate(int index) {
- return parseDate(readAndTrim(index), dateFormat);
+ return parseDate(Objects.requireNonNull(readAndTrim(index)), dateFormat);
}
@Override
@@ -391,7 +394,7 @@ public Date readDate(String name, Date defaultValue) {
public Date readDate(int index, String pattern) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
sdf.setLenient(false);
- return parseDate(readAndTrim(index), sdf);
+ return parseDate(Objects.requireNonNull(readAndTrim(index)), sdf);
}
@Override
@@ -430,16 +433,9 @@ public int getFieldCount() {
* @param index the offset in the token array to obtain the value to be trimmed.
* @return null if the field value is null.
*/
- @Nullable
- protected String readAndTrim(int index) {
+ protected @Nullable String readAndTrim(int index) {
String value = tokens[index];
-
- if (value != null) {
- return value.trim();
- }
- else {
- return null;
- }
+ return value != null ? value.trim() : null;
}
/**
@@ -466,22 +462,16 @@ public String toString() {
return getProperties().toString();
}
- return tokens == null ? "" : Arrays.asList(tokens).toString();
+ return Arrays.toString(tokens);
}
/**
- * @see java.lang.Object#equals(java.lang.Object)
+ * @see Object#equals(Object)
*/
@Override
public boolean equals(Object object) {
if (object instanceof DefaultFieldSet fs) {
-
- if (this.tokens == null) {
- return fs.tokens == null;
- }
- else {
- return Arrays.equals(this.tokens, fs.tokens);
- }
+ return Arrays.equals(this.tokens, fs.tokens);
}
return false;
@@ -490,7 +480,7 @@ public boolean equals(Object object) {
@Override
public int hashCode() {
// this algorithm was taken from java 1.5 jdk Arrays.hashCode(Object[])
- if (tokens == null) {
+ if (tokens.length == 0) {
return 0;
}
@@ -518,27 +508,21 @@ public Properties getProperties() {
return props;
}
- private Number parseNumber(String candidate) {
+ private Number parseNumber(String input) {
try {
- return numberFormat.parse(candidate);
+ return numberFormat.parse(input);
}
catch (ParseException e) {
- throw new NumberFormatException("Unparseable number: " + candidate);
+ throw new NumberFormatException("Unparseable number: " + input);
}
}
- private Date parseDate(String readAndTrim, DateFormat dateFormat) {
+ private Date parseDate(String input, DateFormat dateFormat) {
try {
- return dateFormat.parse(readAndTrim);
+ return dateFormat.parse(input);
}
catch (ParseException e) {
- String pattern;
- if (dateFormat instanceof SimpleDateFormat simpleDateFormat) {
- pattern = simpleDateFormat.toPattern();
- }
- else {
- pattern = dateFormat.toString();
- }
+ String pattern = dateFormat instanceof SimpleDateFormat sdf ? sdf.toPattern() : dateFormat.toString();
throw new IllegalArgumentException(e.getMessage() + ", format: [" + pattern + "]");
}
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSetFactory.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSetFactory.java
index fe3dd0989c..d3da651b9f 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSetFactory.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DefaultFieldSetFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2024 the original author or authors.
+ * Copyright 2009-2025 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,7 +18,7 @@
import java.text.DateFormat;
import java.text.NumberFormat;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* Default implementation of {@link FieldSetFactory} with no special knowledge of the
@@ -26,13 +26,13 @@
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
- *
+ * @author Stefano Cordio
*/
public class DefaultFieldSetFactory implements FieldSetFactory {
- private DateFormat dateFormat;
+ private @Nullable DateFormat dateFormat;
- private NumberFormat numberFormat;
+ private @Nullable NumberFormat numberFormat;
/**
* Default constructor.
@@ -46,27 +46,29 @@ public DefaultFieldSetFactory() {
* @param numberFormat the {@link NumberFormat} to use for parsing numbers
* @since 5.2
*/
- public DefaultFieldSetFactory(@Nullable DateFormat dateFormat, @Nullable NumberFormat numberFormat) {
+ public DefaultFieldSetFactory(DateFormat dateFormat, NumberFormat numberFormat) {
this.dateFormat = dateFormat;
this.numberFormat = numberFormat;
}
/**
- * The {@link NumberFormat} to use for parsing numbers. If unset then
- * {@link java.util.Locale#US} will be used.
- * @param numberFormat the {@link NumberFormat} to use for number parsing
+ * The {@link DateFormat} to use for parsing dates.
+ *
+ * If unset the default pattern is ISO standard yyyy-MM-dd.
+ * @param dateFormat the {@link DateFormat} to use for date parsing
*/
- public void setNumberFormat(NumberFormat numberFormat) {
- this.numberFormat = numberFormat;
+ public void setDateFormat(DateFormat dateFormat) {
+ this.dateFormat = dateFormat;
}
/**
- * The {@link DateFormat} to use for parsing dates. If unset the default pattern is
- * ISO standard yyyy-MM-dd.
- * @param dateFormat the {@link DateFormat} to use for date parsing
+ * The {@link NumberFormat} to use for parsing numbers.
+ *
+ * If unset, {@link java.util.Locale#US} will be used.
+ * @param numberFormat the {@link NumberFormat} to use for number parsing
*/
- public void setDateFormat(DateFormat dateFormat) {
- this.dateFormat = dateFormat;
+ public void setNumberFormat(NumberFormat numberFormat) {
+ this.numberFormat = numberFormat;
}
/**
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java
index bb14e462dd..56259edb43 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java
@@ -17,9 +17,9 @@
package org.springframework.batch.item.file.transform;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@@ -63,7 +63,7 @@ public class DelimitedLineTokenizer extends AbstractLineTokenizer implements Ini
private String escapedQuoteString;
- private Collection includedFields = null;
+ private final Set includedFields = new HashSet<>();
/**
* Create a new instance of the {@link DelimitedLineTokenizer} class for the common
@@ -80,6 +80,7 @@ public DelimitedLineTokenizer() {
* Create a new instance of the {@link DelimitedLineTokenizer} class.
* @param delimiter the desired delimiter. This is required
*/
+ @SuppressWarnings("NullAway")
public DelimitedLineTokenizer(String delimiter) {
Assert.notNull(delimiter, "A delimiter is required");
Assert.state(!delimiter.equals(String.valueOf(DEFAULT_QUOTE_CHARACTER)),
@@ -98,14 +99,16 @@ public void setDelimiter(String delimiter) {
}
/**
- * The fields to include in the output by position (starting at 0). By default all
+ * The fields to include in the output by position (starting at 0). By default, all
* fields are included, but this property can be set to pick out only a few fields
* from a larger set. Note that if field names are provided, their number must match
* the number of included fields.
* @param includedFields the included fields to set
*/
public void setIncludedFields(int... includedFields) {
- this.includedFields = new HashSet<>();
+ if (!this.includedFields.isEmpty()) {
+ this.includedFields.clear();
+ }
for (int i : includedFields) {
this.includedFields.add(i);
}
@@ -161,7 +164,7 @@ else if (!isEnd) {
endPosition = (endPosition - delimiter.length()) + 1;
}
- if (includedFields == null || includedFields.contains(fieldCount)) {
+ if (includedFields.isEmpty() || includedFields.contains(fieldCount)) {
String value = substringWithTrimmedWhitespaceAndQuotesIfQuotesPresent(line, lastCut, endPosition);
tokens.add(value);
}
@@ -169,7 +172,7 @@ else if (!isEnd) {
fieldCount++;
if (isEnd && isDelimiter) {
- if (includedFields == null || includedFields.contains(fieldCount)) {
+ if (includedFields.isEmpty() || includedFields.contains(fieldCount)) {
tokens.add("");
}
fieldCount++;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldExtractor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldExtractor.java
index b9c48ce0f8..6886ea49c5 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldExtractor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldExtractor.java
@@ -15,6 +15,8 @@
*/
package org.springframework.batch.item.file.transform;
+import org.jspecify.annotations.Nullable;
+
/**
* This class will convert an object to an array of its parts.
*
@@ -28,6 +30,6 @@ public interface FieldExtractor {
* @param item the object that contains the information to be extracted.
* @return an array containing item's parts
*/
- Object[] extract(T item);
+ @Nullable Object[] extract(T item);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldSet.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldSet.java
index 42c9153f52..86b8307a5b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldSet.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FieldSet.java
@@ -15,6 +15,8 @@
*/
package org.springframework.batch.item.file.transform;
+import org.jspecify.annotations.Nullable;
+
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.util.Date;
@@ -47,7 +49,7 @@ public interface FieldSet {
/**
* @return fields wrapped by this 'FieldSet' instance as String values.
*/
- String[] getValues();
+ @Nullable String[] getValues();
/**
* Read the {@link String} value at index 'index'.
@@ -55,14 +57,14 @@ public interface FieldSet {
* @return {@link String} containing the value at the index.
* @throws IndexOutOfBoundsException if the {@code index} is out of bounds.
*/
- String readString(int index);
+ @Nullable String readString(int index);
/**
* Read the {@link String} value from column with given 'name'.
* @param name the field {@code name}.
* @return {@link String} containing the value from the specified {@code name}.
*/
- String readString(String name);
+ @Nullable String readString(String name);
/**
* Read the {@link String} value at index 'index' including trailing
@@ -71,7 +73,7 @@ public interface FieldSet {
* @return {@link String} containing the value from the specified {@code index}.
* @throws IndexOutOfBoundsException if the {@code index} is out of bounds.
*/
- String readRawString(int index);
+ @Nullable String readRawString(int index);
/**
* Read the {@link String} value from column with given 'name' including
@@ -79,7 +81,7 @@ public interface FieldSet {
* @param name the field {@code name}.
* @return {@link String} containing the value from the specified {@code name}.
*/
- String readRawString(String name);
+ @Nullable String readRawString(String name);
/**
* Read the 'boolean' value at index 'index'.
@@ -105,7 +107,7 @@ public interface FieldSet {
* case-sensitive.
* @return boolean containing the value from the specified {@code index}.
* @throws IndexOutOfBoundsException if the index is out of bounds, or if the supplied
- * trueValue is null.
+ * {@code trueValue} is {@code null}.
*/
boolean readBoolean(int index, String trueValue);
@@ -116,7 +118,7 @@ public interface FieldSet {
* case-sensitive.
* @return boolean containing the value from the specified {@code name}.
* @throws IllegalArgumentException if a column with given {@code name} is not
- * defined, or if the supplied trueValue is null.
+ * defined, or if the supplied {@code trueValue} is {@code null}.
*/
boolean readBoolean(String name, String trueValue);
@@ -285,7 +287,7 @@ public interface FieldSet {
* @return {@link BigDecimal} containing the value from the specified index.
* @throws IndexOutOfBoundsException if the index is out of bounds.
*/
- BigDecimal readBigDecimal(int index);
+ @Nullable BigDecimal readBigDecimal(int index);
/**
* Read the {@link java.math.BigDecimal} value from column with given
@@ -295,7 +297,7 @@ public interface FieldSet {
* @throws IllegalArgumentException if a column with given {@code name} is not
* defined.
*/
- BigDecimal readBigDecimal(String name);
+ @Nullable BigDecimal readBigDecimal(String name);
/**
* Read the {@link BigDecimal} value at index 'index', returning the
@@ -306,7 +308,7 @@ public interface FieldSet {
* @return {@link BigDecimal} containing the value from the specified index.
* @throws IndexOutOfBoundsException if the index is out of bounds.
*/
- BigDecimal readBigDecimal(int index, BigDecimal defaultValue);
+ @Nullable BigDecimal readBigDecimal(int index, BigDecimal defaultValue);
/**
* Read the {@link BigDecimal} value from column with given 'name,
@@ -318,7 +320,7 @@ public interface FieldSet {
* @throws IllegalArgumentException if a column with given {@code name} is not
* defined.
*/
- BigDecimal readBigDecimal(String name, BigDecimal defaultValue);
+ @Nullable BigDecimal readBigDecimal(String name, BigDecimal defaultValue);
/**
* Read the java.util.Date value in default format at designated column
@@ -425,7 +427,7 @@ public interface FieldSet {
* Construct name-value pairs from the field names and string values. Null values are
* omitted.
* @return some properties representing the field set.
- * @throws IllegalStateException if the field name meta data is not available.
+ * @throws IllegalStateException if the field name metadata is not available.
*/
Properties getProperties();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FixedLengthTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FixedLengthTokenizer.java
index 8d27812a72..a35cc69520 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FixedLengthTokenizer.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FixedLengthTokenizer.java
@@ -16,6 +16,9 @@
package org.springframework.batch.item.file.transform;
+import org.jspecify.annotations.Nullable;
+import org.springframework.util.Assert;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -30,10 +33,11 @@
* @author Lucas Ward
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
public class FixedLengthTokenizer extends AbstractLineTokenizer {
- private Range[] ranges;
+ private Range @Nullable [] ranges;
private int maxRange = 0;
@@ -50,7 +54,7 @@ public class FixedLengthTokenizer extends AbstractLineTokenizer {
* @param ranges the column ranges expected in the input
*/
public void setColumns(Range... ranges) {
- this.ranges = Arrays.asList(ranges).toArray(new Range[ranges.length]);
+ this.ranges = ranges.clone();
calculateMaxRange(ranges);
}
@@ -60,7 +64,7 @@ public void setColumns(Range... ranges) {
* always a min and max, such as: "1,4-20, 22"
*/
private void calculateMaxRange(Range[] ranges) {
- if (ranges == null || ranges.length == 0) {
+ if (ranges.length == 0) {
maxRange = 0;
return;
}
@@ -95,6 +99,7 @@ private void calculateMaxRange(Range[] ranges) {
*/
@Override
protected List doTokenize(String line) {
+ Assert.state(ranges != null, "ranges must not be null");
List tokens = new ArrayList<>(ranges.length);
int lineLength;
String token;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FlatFileFormatException.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FlatFileFormatException.java
index 6a3b0a48ba..4cf48cb8ff 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FlatFileFormatException.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FlatFileFormatException.java
@@ -15,6 +15,8 @@
*/
package org.springframework.batch.item.file.transform;
+import org.jspecify.annotations.Nullable;
+
/**
* Exception indicating that some type of error has occurred while attempting to parse a
* line of input into tokens.
@@ -26,7 +28,7 @@
*/
public class FlatFileFormatException extends RuntimeException {
- private String input;
+ private @Nullable String input;
/**
* Create a new {@link FlatFileFormatException} based on a message.
@@ -61,7 +63,7 @@ public FlatFileFormatException(String message, Throwable cause) {
* Retrieve the input that caused this exception.
* @return String containing the input.
*/
- public String getInput() {
+ public @Nullable String getInput() {
return input;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FormatterLineAggregator.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FormatterLineAggregator.java
index aa37bcfda2..94b77df311 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FormatterLineAggregator.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/FormatterLineAggregator.java
@@ -19,6 +19,7 @@
import java.util.Formatter;
import java.util.Locale;
+import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -27,10 +28,11 @@
*
* @see Formatter
* @author Dave Syer
+ * @author Stefano Cordio
*/
public class FormatterLineAggregator extends ExtractorLineAggregator {
- private String format;
+ private @Nullable String format;
private Locale locale = Locale.getDefault();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/IncorrectTokenCountException.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/IncorrectTokenCountException.java
index 3879948148..6a9db0cb14 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/IncorrectTokenCountException.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/IncorrectTokenCountException.java
@@ -22,6 +22,7 @@
* @author Lucas Ward
* @author "Michael Minella"
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 1.1
*/
public class IncorrectTokenCountException extends FlatFileFormatException {
@@ -30,13 +31,10 @@ public class IncorrectTokenCountException extends FlatFileFormatException {
private final int expectedCount;
- private String input;
-
public IncorrectTokenCountException(String message, int expectedCount, int actualCount, String input) {
- super(message);
+ super(message, input);
this.expectedCount = expectedCount;
this.actualCount = actualCount;
- this.input = input;
}
public IncorrectTokenCountException(String message, int expectedCount, int actualCount) {
@@ -46,10 +44,10 @@ public IncorrectTokenCountException(String message, int expectedCount, int actua
}
public IncorrectTokenCountException(int expectedCount, int actualCount, String input) {
- super("Incorrect number of tokens found in record: expected " + expectedCount + " actual " + actualCount);
+ super("Incorrect number of tokens found in record: expected " + expectedCount + " actual " + actualCount,
+ input);
this.expectedCount = expectedCount;
this.actualCount = actualCount;
- this.input = input;
}
public IncorrectTokenCountException(int expectedCount, int actualCount) {
@@ -66,13 +64,4 @@ public int getExpectedCount() {
return expectedCount;
}
- /**
- * @return the line that caused the exception
- * @since 2.2.6
- */
- @Override
- public String getInput() {
- return input;
- }
-
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/LineTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/LineTokenizer.java
index 7157876c30..b92660b3fd 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/LineTokenizer.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/LineTokenizer.java
@@ -16,7 +16,7 @@
package org.springframework.batch.item.file.transform;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* Interface that is used by framework to split string obtained typically from a file into
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PatternMatchingCompositeLineTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PatternMatchingCompositeLineTokenizer.java
index eff7d28f64..75a570acbf 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PatternMatchingCompositeLineTokenizer.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/PatternMatchingCompositeLineTokenizer.java
@@ -19,8 +19,9 @@
import java.util.Map;
import org.springframework.batch.support.PatternMatcher;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -36,8 +37,9 @@
*/
public class PatternMatchingCompositeLineTokenizer implements LineTokenizer, InitializingBean {
- private PatternMatcher tokenizers = null;
+ private @Nullable PatternMatcher tokenizers;
+ @SuppressWarnings("DataFlowIssue")
@Override
public FieldSet tokenize(@Nullable String line) {
return tokenizers.match(line).tokenize(line);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RangeArrayPropertyEditor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RangeArrayPropertyEditor.java
index e382bfca8c..6e7f16e92c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RangeArrayPropertyEditor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RangeArrayPropertyEditor.java
@@ -111,7 +111,7 @@ public String getAsText() {
return sb.toString();
}
- private void setMaxValues(final Range[] ranges) {
+ private void setMaxValues(Range[] ranges) {
// Array of integers to track range values by index
Integer[] c = new Integer[ranges.length];
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RecordFieldExtractor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RecordFieldExtractor.java
index b9de4da2d0..a0b609b5e6 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RecordFieldExtractor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RecordFieldExtractor.java
@@ -21,10 +21,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
+import org.jspecify.annotations.Nullable;
+
/**
* This is a field extractor for a Java record. By default, it will extract all record
* components, unless a subset is selected using {@link #setNames(String...)}.
@@ -66,7 +68,7 @@ public void setNames(String... names) {
public Object[] extract(T item) {
List values = new ArrayList<>();
for (String componentName : this.names) {
- RecordComponent recordComponent = getRecordComponentByName(componentName);
+ RecordComponent recordComponent = getRecordComponentByName(componentName).orElseThrow();
Object value;
try {
value = recordComponent.getAccessor().invoke(item);
@@ -85,19 +87,17 @@ private List getRecordComponentNames() {
private void validate(String[] names) {
for (String name : names) {
- if (getRecordComponentByName(name) == null) {
+ if (getRecordComponentByName(name).isEmpty()) {
throw new IllegalArgumentException(
"Component '" + name + "' is not defined in record " + targetType.getName());
}
}
}
- @Nullable
- private RecordComponent getRecordComponentByName(String name) {
+ private Optional getRecordComponentByName(String name) {
return Arrays.stream(this.recordComponents)
.filter(recordComponent -> recordComponent.getName().equals(name))
- .findFirst()
- .orElse(null);
+ .findFirst();
}
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RegexLineTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RegexLineTokenizer.java
index 6128c84dec..23c347edec 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RegexLineTokenizer.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/RegexLineTokenizer.java
@@ -21,6 +21,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -46,13 +47,15 @@
*
* @see Matcher#group(int)
* @author Costin Leau
+ * @author Stefano Cordio
*/
public class RegexLineTokenizer extends AbstractLineTokenizer {
- private Pattern pattern;
+ private @Nullable Pattern pattern;
@Override
protected List doTokenize(String line) {
+ Assert.state(pattern != null, "pattern must be initialized");
Matcher matcher = pattern.matcher(line);
boolean matchFound = matcher.find();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/package-info.java
index 4c5085445f..cf1e0786d4 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/package-info.java
@@ -3,7 +3,7 @@
* Infrastructure implementations of io file support transform concerns.
*
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.file.transform;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/FunctionItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/FunctionItemProcessor.java
index 07b5c1f5ee..63a6061c86 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/FunctionItemProcessor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/FunctionItemProcessor.java
@@ -18,7 +18,8 @@
import java.util.function.Function;
import org.springframework.batch.item.ItemProcessor;
-import org.springframework.lang.Nullable;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -39,9 +40,8 @@ public FunctionItemProcessor(Function function) {
this.function = function;
}
- @Nullable
@Override
- public O process(I item) throws Exception {
+ public @Nullable O process(I item) throws Exception {
return this.function.apply(item);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/PredicateFilteringItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/PredicateFilteringItemProcessor.java
index 553c85a797..b1d4a57196 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/PredicateFilteringItemProcessor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/PredicateFilteringItemProcessor.java
@@ -18,6 +18,8 @@
import java.util.function.Predicate;
import org.springframework.batch.item.ItemProcessor;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -42,7 +44,7 @@ public PredicateFilteringItemProcessor(Predicate predicate) {
}
@Override
- public T process(T item) throws Exception {
+ public @Nullable T process(T item) throws Exception {
return this.predicate.test(item) ? null : item;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/package-info.java
index f71c35ba6b..96c106a7a7 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/function/package-info.java
@@ -19,7 +19,7 @@
*
* @author Mahmoud Ben Hassine
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.function;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemReader.java
index 60d5083b37..98fc02a1d6 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemReader.java
@@ -18,11 +18,12 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.core.JmsTemplate;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import jakarta.jms.Message;
@@ -43,9 +44,9 @@ public class JmsItemReader implements ItemReader, InitializingBean {
protected Log logger = LogFactory.getLog(getClass());
- protected Class extends T> itemType;
+ protected @Nullable Class extends T> itemType;
- protected JmsOperations jmsTemplate;
+ protected @Nullable JmsOperations jmsTemplate;
/**
* Setter for JMS template.
@@ -72,10 +73,9 @@ public void setItemType(Class extends T> itemType) {
this.itemType = itemType;
}
- @Nullable
@Override
- @SuppressWarnings("unchecked")
- public T read() {
+ @SuppressWarnings({ "unchecked", "DataFlowIssue" })
+ public @Nullable T read() {
if (itemType != null && itemType.isAssignableFrom(Message.class)) {
return (T) jmsTemplate.receive();
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemWriter.java
index f5576ae23a..91c9c1ead3 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsItemWriter.java
@@ -19,6 +19,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.jms.core.JmsOperations;
@@ -41,7 +42,7 @@ public class JmsItemWriter implements ItemWriter {
protected Log logger = LogFactory.getLog(getClass());
- private JmsOperations jmsTemplate;
+ private @Nullable JmsOperations jmsTemplate;
/**
* Setter for JMS template.
@@ -60,6 +61,7 @@ public void setJmsTemplate(JmsOperations jmsTemplate) {
*
* @see org.springframework.batch.item.ItemWriter#write(Chunk)
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends T> items) throws Exception {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsMethodInvocationRecoverer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsMethodInvocationRecoverer.java
index dd9dcd6b3e..5c54602b44 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsMethodInvocationRecoverer.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/JmsMethodInvocationRecoverer.java
@@ -17,8 +17,8 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
-import org.springframework.lang.Nullable;
import org.springframework.retry.interceptor.MethodInvocationRecoverer;
import org.springframework.jms.JmsException;
import org.springframework.jms.core.JmsOperations;
@@ -32,7 +32,7 @@ public class JmsMethodInvocationRecoverer implements MethodInvocationRecovere
protected Log logger = LogFactory.getLog(getClass());
- private JmsOperations jmsTemplate;
+ private @Nullable JmsOperations jmsTemplate;
/**
* Setter for jms template.
@@ -48,9 +48,9 @@ public void setJmsTemplate(JmsOperations jmsTemplate) {
*
* @see MethodInvocationRecoverer#recover(Object[], Throwable)
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
- @Nullable
- public T recover(Object[] items, Throwable cause) {
+ public @Nullable T recover(Object[] items, Throwable cause) {
try {
for (Object item : items) {
jmsTemplate.convertAndSend(item);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemReaderBuilder.java
index bec28d06c6..69cb4f9423 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemReaderBuilder.java
@@ -18,6 +18,7 @@
import jakarta.jms.Message;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.jms.JmsItemReader;
import org.springframework.jms.core.JmsOperations;
import org.springframework.util.Assert;
@@ -27,13 +28,14 @@
*
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 4.0
*/
public class JmsItemReaderBuilder {
- protected Class extends T> itemType;
+ protected @Nullable Class extends T> itemType;
- protected JmsOperations jmsTemplate;
+ protected @Nullable JmsOperations jmsTemplate;
/**
* Establish the JMS template that will be used by the JmsItemReader.
@@ -70,7 +72,9 @@ public JmsItemReader build() {
Assert.notNull(this.jmsTemplate, "jmsTemplate is required.");
JmsItemReader jmsItemReader = new JmsItemReader<>();
- jmsItemReader.setItemType(this.itemType);
+ if (this.itemType != null) {
+ jmsItemReader.setItemType(this.itemType);
+ }
jmsItemReader.setJmsTemplate(this.jmsTemplate);
return jmsItemReader;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemWriterBuilder.java
index de6ca57394..fa71e8b934 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/JmsItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.jms.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.jms.JmsItemWriter;
import org.springframework.jms.core.JmsOperations;
import org.springframework.util.Assert;
@@ -28,7 +29,7 @@
*/
public class JmsItemWriterBuilder {
- private JmsOperations jmsTemplate;
+ private @Nullable JmsOperations jmsTemplate;
/**
* Establish the JMS template that will be used by the {@link JmsItemWriter}.
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/package-info.java
index ba5a0aa85a..b7df19842c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/builder/package-info.java
@@ -18,8 +18,9 @@
* Builders for JMS item reader and writer.
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.jms.builder;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/package-info.java
index 2e0e156aa6..e16f615749 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/jms/package-info.java
@@ -3,8 +3,9 @@
*
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.jms;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/GsonJsonObjectReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/GsonJsonObjectReader.java
index c1b49fb4bd..5a2835da29 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/GsonJsonObjectReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/GsonJsonObjectReader.java
@@ -25,10 +25,10 @@
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ParseException;
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -44,11 +44,11 @@ public class GsonJsonObjectReader implements JsonObjectReader {
private final Class extends T> itemType;
- private JsonReader jsonReader;
-
private Gson mapper;
- private InputStream inputStream;
+ private @Nullable JsonReader jsonReader;
+
+ private @Nullable InputStream inputStream;
/**
* Create a new {@link GsonJsonObjectReader} instance.
@@ -83,9 +83,9 @@ public void open(Resource resource) throws Exception {
this.jsonReader.beginArray();
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
try {
if (this.jsonReader.hasNext()) {
return this.mapper.fromJson(this.jsonReader, this.itemType);
@@ -97,12 +97,14 @@ public T read() throws Exception {
return null;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() throws Exception {
this.inputStream.close();
this.jsonReader.close();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void jumpToItem(int itemIndex) throws Exception {
for (int i = 0; i < itemIndex; i++) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JacksonJsonObjectReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JacksonJsonObjectReader.java
index df1879240c..12b7369409 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JacksonJsonObjectReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JacksonJsonObjectReader.java
@@ -22,10 +22,10 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ParseException;
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -41,11 +41,11 @@ public class JacksonJsonObjectReader implements JsonObjectReader {
private final Class extends T> itemType;
- private JsonParser jsonParser;
-
private ObjectMapper mapper;
- private InputStream inputStream;
+ private @Nullable JsonParser jsonParser;
+
+ private @Nullable InputStream inputStream;
/**
* Create a new {@link JacksonJsonObjectReader} instance.
@@ -79,9 +79,9 @@ public void open(Resource resource) throws Exception {
"The Json input stream must start with an array of Json objects");
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
try {
if (this.jsonParser.nextToken() == JsonToken.START_OBJECT) {
return this.mapper.readValue(this.jsonParser, this.itemType);
@@ -93,12 +93,14 @@ public T read() throws Exception {
return null;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() throws Exception {
this.inputStream.close();
this.jsonParser.close();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void jumpToItem(int itemIndex) throws Exception {
for (int i = 0; i < itemIndex; i++) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonFileItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonFileItemWriter.java
index 9e849ca14c..7f23e166ee 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonFileItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonFileItemWriter.java
@@ -18,6 +18,7 @@
import java.util.Iterator;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.support.AbstractFileItemWriter;
import org.springframework.core.io.WritableResource;
@@ -56,7 +57,7 @@ public class JsonFileItemWriter extends AbstractFileItemWriter {
private static final char JSON_ARRAY_STOP = ']';
- private JsonObjectMarshaller jsonObjectMarshaller;
+ private @Nullable JsonObjectMarshaller jsonObjectMarshaller;
/**
* Create a new {@link JsonFileItemWriter} instance.
@@ -93,6 +94,7 @@ public void setJsonObjectMarshaller(JsonObjectMarshaller jsonObjectMarshaller
this.jsonObjectMarshaller = jsonObjectMarshaller;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public String doWrite(Chunk extends T> items) {
StringBuilder lines = new StringBuilder();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonItemReader.java
index a7fdc830f1..0b3d8fec10 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonItemReader.java
@@ -18,12 +18,12 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream;
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -55,9 +55,9 @@ public class JsonItemReader extends AbstractItemCountingItemStreamItemReader<
private static final Log LOGGER = LogFactory.getLog(JsonItemReader.class);
- private Resource resource;
+ private @Nullable Resource resource;
- private JsonObjectReader jsonObjectReader;
+ private @Nullable JsonObjectReader jsonObjectReader;
private boolean strict = true;
@@ -105,9 +105,9 @@ public void setResource(Resource resource) {
this.resource = resource;
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- protected T doRead() throws Exception {
+ protected @Nullable T doRead() throws Exception {
return jsonObjectReader.read();
}
@@ -132,11 +132,13 @@ protected void doOpen() throws Exception {
this.jsonObjectReader.open(this.resource);
}
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void doClose() throws Exception {
this.jsonObjectReader.close();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void jumpToItem(int itemIndex) throws Exception {
this.jsonObjectReader.jumpToItem(itemIndex);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonObjectReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonObjectReader.java
index d143b71c8d..75a3685502 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonObjectReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/JsonObjectReader.java
@@ -16,8 +16,9 @@
package org.springframework.batch.item.json;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
/**
* Strategy interface for Json readers. Implementations are expected to use a streaming
@@ -44,8 +45,7 @@ default void open(Resource resource) throws Exception {
* @return the next object or {@code null} if the resource is exhausted
* @throws Exception if unable to read the next object
*/
- @Nullable
- T read() throws Exception;
+ @Nullable T read() throws Exception;
/**
* Close the input resource.
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonFileItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonFileItemWriterBuilder.java
index e92dec952c..2b456673f6 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonFileItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonFileItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.json.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.file.FlatFileFooterCallback;
import org.springframework.batch.item.file.FlatFileHeaderCallback;
import org.springframework.batch.item.json.JsonFileItemWriter;
@@ -32,15 +33,15 @@
*/
public class JsonFileItemWriterBuilder {
- private WritableResource resource;
+ private @Nullable WritableResource resource;
- private JsonObjectMarshaller jsonObjectMarshaller;
+ private @Nullable JsonObjectMarshaller jsonObjectMarshaller;
- private FlatFileHeaderCallback headerCallback;
+ private @Nullable FlatFileHeaderCallback headerCallback;
- private FlatFileFooterCallback footerCallback;
+ private @Nullable FlatFileFooterCallback footerCallback;
- private String name;
+ private @Nullable String name;
private String encoding = JsonFileItemWriter.DEFAULT_CHARSET;
@@ -237,7 +238,9 @@ public JsonFileItemWriter build() {
JsonFileItemWriter jsonFileItemWriter = new JsonFileItemWriter<>(this.resource, this.jsonObjectMarshaller);
- jsonFileItemWriter.setName(this.name);
+ if (this.name != null) {
+ jsonFileItemWriter.setName(this.name);
+ }
jsonFileItemWriter.setAppendAllowed(this.append);
jsonFileItemWriter.setEncoding(this.encoding);
if (this.headerCallback != null) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonItemReaderBuilder.java
index 9c1e7c6bcb..77b0d134e8 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/JsonItemReaderBuilder.java
@@ -19,6 +19,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.json.JsonItemReader;
import org.springframework.batch.item.json.JsonObjectReader;
import org.springframework.core.io.Resource;
@@ -36,11 +37,11 @@ public class JsonItemReaderBuilder {
protected Log logger = LogFactory.getLog(getClass());
- private JsonObjectReader jsonObjectReader;
+ private @Nullable JsonObjectReader jsonObjectReader;
- private Resource resource;
+ private @Nullable Resource resource;
- private String name;
+ private @Nullable String name;
private boolean strict = true;
@@ -149,15 +150,18 @@ public JsonItemReader build() {
Assert.state(StringUtils.hasText(this.name), "A name is required when saveState is set to true.");
}
- if (this.resource == null) {
+ JsonItemReader reader = new JsonItemReader<>();
+ if (this.resource != null) {
+ reader.setResource(this.resource);
+ }
+ else {
logger.debug("The resource is null. This is only a valid scenario when "
+ "injecting it later as in when using the MultiResourceItemReader");
}
-
- JsonItemReader reader = new JsonItemReader<>();
- reader.setResource(this.resource);
reader.setJsonObjectReader(this.jsonObjectReader);
- reader.setName(this.name);
+ if (this.name != null) {
+ reader.setName(this.name);
+ }
reader.setStrict(this.strict);
reader.setSaveState(this.saveState);
reader.setMaxItemCount(this.maxItemCount);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/package-info.java
index 0b03185ebc..6d533e4b2c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/builder/package-info.java
@@ -18,8 +18,9 @@
* Builders for JSON item reader and writer.
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.json.builder;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/package-info.java
index 667ea71817..413dbbd8dc 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/json/package-info.java
@@ -4,8 +4,9 @@
*
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.json;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemReader.java
index bde6dc4513..d3d8c255a6 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemReader.java
@@ -29,10 +29,10 @@
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.support.AbstractItemStreamItemReader;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -58,13 +58,13 @@ public class KafkaItemReader extends AbstractItemStreamItemReader {
private final List topicPartitions;
- private Map partitionOffsets;
+ private @Nullable Map partitionOffsets;
- private KafkaConsumer kafkaConsumer;
+ private @Nullable KafkaConsumer kafkaConsumer;
private final Properties consumerProperties;
- private Iterator> consumerRecords;
+ private @Nullable Iterator> consumerRecords;
private Duration pollTimeout = Duration.ofSeconds(DEFAULT_POLL_TIMEOUT);
@@ -163,7 +163,7 @@ public void setPartitionOffsets(Map partitionOffsets) {
this.partitionOffsets = partitionOffsets;
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "DataFlowIssue" })
@Override
public void open(ExecutionContext executionContext) {
this.kafkaConsumer = new KafkaConsumer<>(this.consumerProperties);
@@ -184,9 +184,9 @@ public void open(ExecutionContext executionContext) {
this.partitionOffsets.forEach(this.kafkaConsumer::seek);
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- public V read() {
+ public @Nullable V read() {
if (this.consumerRecords == null || !this.consumerRecords.hasNext()) {
this.consumerRecords = this.kafkaConsumer.poll(this.pollTimeout).iterator();
}
@@ -200,6 +200,7 @@ public V read() {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void update(ExecutionContext executionContext) {
if (this.saveState) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemWriter.java
index a542bd87c2..ad9218ce90 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/KafkaItemWriter.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.kafka;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.KeyValueItemWriter;
import org.springframework.kafka.core.KafkaTemplate;
@@ -39,19 +40,21 @@
*
* @author Mathieu Ouellet
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 4.2
*
*/
public class KafkaItemWriter extends KeyValueItemWriter {
- protected KafkaTemplate kafkaTemplate;
+ protected @Nullable KafkaTemplate kafkaTemplate;
protected final List>> completableFutures = new ArrayList<>();
private long timeout = -1;
+ @SuppressWarnings("DataFlowIssue")
@Override
- protected void writeKeyValue(K key, T value) {
+ protected void writeKeyValue(@Nullable K key, T value) {
if (this.delete) {
this.completableFutures.add(this.kafkaTemplate.sendDefault(key, null));
}
@@ -60,6 +63,7 @@ protected void writeKeyValue(K key, T value) {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void flush() throws Exception {
this.kafkaTemplate.flush();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemReaderBuilder.java
index ae8c7fb354..df50ba93e1 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemReaderBuilder.java
@@ -26,6 +26,7 @@
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.TopicPartition;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.kafka.KafkaItemReader;
import org.springframework.util.Assert;
@@ -39,19 +40,19 @@
*/
public class KafkaItemReaderBuilder {
- private Properties consumerProperties;
+ private @Nullable Properties consumerProperties;
- private String topic;
+ private @Nullable String topic;
private List partitions = new ArrayList<>();
- private Map partitionOffsets;
+ private @Nullable Map partitionOffsets;
private Duration pollTimeout = Duration.ofSeconds(30L);
private boolean saveState = true;
- private String name;
+ private @Nullable String name;
/**
* Configure if the state of the
@@ -174,8 +175,12 @@ public KafkaItemReader build() {
KafkaItemReader reader = new KafkaItemReader<>(this.consumerProperties, this.topic, this.partitions);
reader.setPollTimeout(this.pollTimeout);
reader.setSaveState(this.saveState);
- reader.setName(this.name);
- reader.setPartitionOffsets(this.partitionOffsets);
+ if (this.name != null) {
+ reader.setName(this.name);
+ }
+ if (this.partitionOffsets != null) {
+ reader.setPartitionOffsets(this.partitionOffsets);
+ }
return reader;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemWriterBuilder.java
index adc6dc37b6..9db4cd324a 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/KafkaItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.kafka.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.kafka.KafkaItemWriter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.kafka.core.KafkaTemplate;
@@ -30,9 +31,9 @@
*/
public class KafkaItemWriterBuilder {
- private KafkaTemplate kafkaTemplate;
+ private @Nullable KafkaTemplate kafkaTemplate;
- private Converter itemKeyMapper;
+ private @Nullable Converter itemKeyMapper;
private boolean delete;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/package-info.java
index 45fb008395..7657b3593e 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/builder/package-info.java
@@ -19,7 +19,7 @@
*
* @author Mathieu Ouellet
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.kafka.builder;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/package-info.java
index c0e084822b..439ed117a0 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/kafka/package-info.java
@@ -19,7 +19,7 @@
*
* @author Mathieu Ouellet
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.kafka;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/LdifReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/LdifReader.java
index e619376332..49bb5e8e03 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/LdifReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/LdifReader.java
@@ -16,13 +16,13 @@
package org.springframework.batch.item.ldif;
+import org.jspecify.annotations.Nullable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream;
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
-import org.springframework.lang.Nullable;
import org.springframework.ldap.core.LdapAttributes;
import org.springframework.ldap.ldif.parser.LdifParser;
import org.springframework.util.Assert;
@@ -68,9 +68,9 @@ public class LdifReader extends AbstractItemCountingItemStreamItemReader extends AbstractItemCountingItemStreamItemRead
private static final Log LOG = LogFactory.getLog(MappingLdifReader.class);
- private Resource resource;
+ private @Nullable Resource resource;
- private LdifParser ldifParser;
+ private @Nullable LdifParser ldifParser;
private int recordCount = 0;
@@ -69,9 +69,9 @@ public class MappingLdifReader extends AbstractItemCountingItemStreamItemRead
private boolean strict = true;
- private RecordCallbackHandler skippedRecordsCallback;
+ private @Nullable RecordCallbackHandler skippedRecordsCallback;
- private RecordMapper recordMapper;
+ private @Nullable RecordMapper recordMapper;
public MappingLdifReader() {
setName(ClassUtils.getShortName(MappingLdifReader.class));
@@ -123,6 +123,7 @@ protected void doClose() throws Exception {
this.recordCount = 0;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void doOpen() throws Exception {
if (resource == null)
@@ -148,9 +149,9 @@ protected void doOpen() throws Exception {
}
}
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- protected T doRead() throws Exception {
+ protected @Nullable T doRead() throws Exception {
LdapAttributes attributes = null;
try {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/RecordMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/RecordMapper.java
index 3d824e1c99..1c7ebde486 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/RecordMapper.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/RecordMapper.java
@@ -15,7 +15,8 @@
*/
package org.springframework.batch.item.ldif;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.ldap.core.LdapAttributes;
/**
@@ -34,7 +35,6 @@ public interface RecordMapper {
* @param attributes attributes
* @return object of type T or {@code null} if unable to map the record to an object.
*/
- @Nullable
- T mapRecord(LdapAttributes attributes);
+ @Nullable T mapRecord(LdapAttributes attributes);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/LdifReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/LdifReaderBuilder.java
index 5bda30d642..2f1ce5fe4b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/LdifReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/LdifReaderBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.ldif.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ldif.LdifReader;
import org.springframework.batch.item.ldif.RecordCallbackHandler;
import org.springframework.core.io.Resource;
@@ -29,17 +30,17 @@
*/
public class LdifReaderBuilder {
- private Resource resource;
+ private @Nullable Resource resource;
private int recordsToSkip = 0;
private boolean strict = true;
- private RecordCallbackHandler skippedRecordsCallback;
+ private @Nullable RecordCallbackHandler skippedRecordsCallback;
private boolean saveState = true;
- private String name;
+ private @Nullable String name;
private int maxItemCount = Integer.MAX_VALUE;
@@ -163,7 +164,9 @@ public LdifReader build() {
reader.setResource(this.resource);
reader.setRecordsToSkip(this.recordsToSkip);
reader.setSaveState(this.saveState);
- reader.setName(this.name);
+ if (this.name != null) {
+ reader.setName(this.name);
+ }
reader.setCurrentItemCount(this.currentItemCount);
reader.setMaxItemCount(this.maxItemCount);
if (this.skippedRecordsCallback != null) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/MappingLdifReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/MappingLdifReaderBuilder.java
index e56a723a75..46ac8c7e7d 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/MappingLdifReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/MappingLdifReaderBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.ldif.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ldif.MappingLdifReader;
import org.springframework.batch.item.ldif.RecordCallbackHandler;
import org.springframework.batch.item.ldif.RecordMapper;
@@ -30,19 +31,19 @@
*/
public class MappingLdifReaderBuilder {
- private Resource resource;
+ private @Nullable Resource resource;
private int recordsToSkip = 0;
private boolean strict = true;
- private RecordCallbackHandler skippedRecordsCallback;
+ private @Nullable RecordCallbackHandler skippedRecordsCallback;
- private RecordMapper recordMapper;
+ private @Nullable RecordMapper recordMapper;
private boolean saveState = true;
- private String name;
+ private @Nullable String name;
private int maxItemCount = Integer.MAX_VALUE;
@@ -181,7 +182,9 @@ public MappingLdifReader build() {
reader.setCurrentItemCount(this.currentItemCount);
reader.setMaxItemCount(this.maxItemCount);
reader.setRecordMapper(this.recordMapper);
- reader.setName(this.name);
+ if (this.name != null) {
+ reader.setName(this.name);
+ }
if (this.skippedRecordsCallback != null) {
reader.setSkippedRecordsCallback(this.skippedRecordsCallback);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/package-info.java
index 2bcd72c032..90ea20093d 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/builder/package-info.java
@@ -18,8 +18,9 @@
* Builders for LDIF related components.
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.ldif.builder;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/package-info.java
index f50cc0e797..9fcfa0e641 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ldif/package-info.java
@@ -5,8 +5,9 @@
*
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.ldif;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/SimpleMailMessageItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/SimpleMailMessageItemWriter.java
index 65cf843acb..7e839453fb 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/SimpleMailMessageItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/SimpleMailMessageItemWriter.java
@@ -18,6 +18,7 @@
import java.util.Map;
import java.util.Map.Entry;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
@@ -56,7 +57,7 @@
*/
public class SimpleMailMessageItemWriter implements ItemWriter, InitializingBean {
- private MailSender mailSender;
+ private @Nullable MailSender mailSender;
private MailErrorHandler mailErrorHandler = new DefaultMailErrorHandler();
@@ -91,6 +92,7 @@ public void afterPropertiesSet() throws IllegalStateException {
* @param chunk the chunk of items to send
* @see ItemWriter#write(Chunk)
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends SimpleMailMessage> chunk) throws MailException {
try {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/SimpleMailMessageItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/SimpleMailMessageItemWriterBuilder.java
index 247f0d1c3a..cb56c9ccb6 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/SimpleMailMessageItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/SimpleMailMessageItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.mail.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.mail.DefaultMailErrorHandler;
import org.springframework.batch.item.mail.MailErrorHandler;
@@ -30,10 +31,9 @@
* @author Mahmoud Ben Hassine
* @since 4.0
*/
-
public class SimpleMailMessageItemWriterBuilder {
- private MailSender mailSender;
+ private @Nullable MailSender mailSender;
private MailErrorHandler mailErrorHandler = new DefaultMailErrorHandler();
@@ -69,9 +69,7 @@ public SimpleMailMessageItemWriter build() {
SimpleMailMessageItemWriter writer = new SimpleMailMessageItemWriter();
writer.setMailSender(this.mailSender);
- if (mailErrorHandler != null) {
- writer.setMailErrorHandler(this.mailErrorHandler);
- }
+ writer.setMailErrorHandler(this.mailErrorHandler);
return writer;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/package-info.java
index 42a7de1483..542dda3fd4 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/builder/package-info.java
@@ -18,8 +18,9 @@
* Builders for JavaMail related components.
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.mail.builder;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/MimeMessageItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/MimeMessageItemWriter.java
index a900e7d276..c1a0262af3 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/MimeMessageItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/MimeMessageItemWriter.java
@@ -20,6 +20,7 @@
import jakarta.mail.internet.MimeMessage;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.mail.DefaultMailErrorHandler;
@@ -60,7 +61,7 @@
*/
public class MimeMessageItemWriter implements ItemWriter {
- private JavaMailSender mailSender;
+ private @Nullable JavaMailSender mailSender;
private MailErrorHandler mailErrorHandler = new DefaultMailErrorHandler();
@@ -94,6 +95,7 @@ public void afterPropertiesSet() throws IllegalStateException {
* @param chunk the chunk of items to send
* @see ItemWriter#write(Chunk)
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends MimeMessage> chunk) throws MailException {
try {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/package-info.java
index c1811df0f2..3ba7f21e39 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/javamail/package-info.java
@@ -18,8 +18,9 @@
* JavaMail related components.
*
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.mail.javamail;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/package-info.java
index 413f4b3833..ee3457634c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/mail/package-info.java
@@ -3,8 +3,9 @@
*
* @author Michael Minella
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item.mail;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/package-info.java
index ae555480b6..b0992cd366 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/package-info.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/package-info.java
@@ -3,7 +3,7 @@
* Infrastructure interfaces and primary dependencies for item concerns.
*
*/
-@NonNullApi
+@NullMarked
package org.springframework.batch.item;
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/queue/BlockingQueueItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/queue/BlockingQueueItemReader.java
index e5e411045b..ceb9e5d6cc 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/queue/BlockingQueueItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/queue/BlockingQueueItemReader.java
@@ -22,7 +22,7 @@
/**
* This is an {@link ItemReader} that reads items from a {@link BlockingQueue}. It stops
- * reading (ie returns {@code null}) if no items are available in the queue after a
+ * reading (i.e., returns {@code null}) if no items are available in the queue after a
* configurable timeout.
*
* @param type of items to read.
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemReader.java
index f5142fa39a..dd6074a5e5 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemReader.java
@@ -15,6 +15,8 @@
*/
package org.springframework.batch.item.redis;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamReader;
@@ -43,7 +45,7 @@ public class RedisItemReader implements ItemStreamReader {
private final ScanOptions scanOptions;
- private Cursor cursor;
+ private @Nullable Cursor cursor;
public RedisItemReader(RedisTemplate redisTemplate, ScanOptions scanOptions) {
Assert.notNull(redisTemplate, "redisTemplate must not be null");
@@ -57,8 +59,9 @@ public void open(ExecutionContext executionContext) throws ItemStreamException {
this.cursor = this.redisTemplate.scan(this.scanOptions);
}
+ @SuppressWarnings("DataFlowIssue")
@Override
- public V read() throws Exception {
+ public @Nullable V read() throws Exception {
if (this.cursor.hasNext()) {
K nextKey = this.cursor.next();
return this.redisTemplate.opsForValue().get(nextKey);
@@ -68,6 +71,7 @@ public V read() throws Exception {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() throws ItemStreamException {
this.cursor.close();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemWriter.java
index c9b0ae3ee3..306c8d3a34 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/RedisItemWriter.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.redis;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.KeyValueItemWriter;
import org.springframework.data.redis.core.RedisTemplate;
@@ -28,12 +29,14 @@
*
* @author Santiago Molano
* @author Mahmoud Ben Hassine
+ * @author Stefano Cordio
* @since 5.1
*/
public class RedisItemWriter extends KeyValueItemWriter {
- private RedisTemplate redisTemplate;
+ private @Nullable RedisTemplate redisTemplate;
+ @SuppressWarnings("DataFlowIssue")
@Override
protected void writeKeyValue(K key, T value) {
if (this.delete) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/package-info.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/package-info.java
new file mode 100644
index 0000000000..7eb3c84f7e
--- /dev/null
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/redis/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 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.
+ */
+
+/**
+ * Redis related readers and writers
+ *
+ * @author Stefano Cordio
+ */
+@NullMarked
+package org.springframework.batch.item.redis;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java
index a726680f99..c15a9e9f30 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java
@@ -30,6 +30,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
@@ -72,7 +73,7 @@ public abstract class AbstractFileItemWriter extends AbstractItemStreamItemWr
protected static final Log logger = LogFactory.getLog(AbstractFileItemWriter.class);
- public static final String DEFAULT_LINE_SEPARATOR = System.getProperty("line.separator");
+ public static final String DEFAULT_LINE_SEPARATOR = System.lineSeparator();
public static final String DEFAULT_CHARSET = StandardCharsets.UTF_8.name();
@@ -80,9 +81,9 @@ public abstract class AbstractFileItemWriter extends AbstractItemStreamItemWr
private static final String RESTART_DATA_NAME = "current.count";
- private WritableResource resource;
+ private @Nullable WritableResource resource;
- protected OutputState state = null;
+ protected @Nullable OutputState state;
private boolean saveState = true;
@@ -94,9 +95,9 @@ public abstract class AbstractFileItemWriter extends AbstractItemStreamItemWr
private String encoding = DEFAULT_CHARSET;
- private FlatFileHeaderCallback headerCallback;
+ private @Nullable FlatFileHeaderCallback headerCallback;
- private FlatFileFooterCallback footerCallback;
+ private @Nullable FlatFileFooterCallback footerCallback;
protected String lineSeparator = DEFAULT_LINE_SEPARATOR;
@@ -253,6 +254,7 @@ public void write(Chunk extends T> items) throws Exception {
/**
* @see ItemStream#close()
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() {
super.close();
@@ -298,6 +300,7 @@ public void open(ExecutionContext executionContext) throws ItemStreamException {
}
}
+ @SuppressWarnings("DataFlowIssue")
private void doOpen(ExecutionContext executionContext) throws ItemStreamException {
OutputState outputState = getOutputState();
if (executionContext.containsKey(getExecutionContextKey(RESTART_DATA_NAME))) {
@@ -348,6 +351,7 @@ public void update(ExecutionContext executionContext) {
}
// Returns object representing state.
+ @SuppressWarnings("DataFlowIssue")
protected OutputState getOutputState() {
if (state == null) {
File file;
@@ -372,12 +376,12 @@ protected OutputState getOutputState() {
*/
protected class OutputState {
- private FileOutputStream os;
+ private @Nullable FileOutputStream os;
// The bufferedWriter over the file channel that is actually written
- Writer outputBufferedWriter;
+ @Nullable Writer outputBufferedWriter;
- FileChannel fileChannel;
+ @Nullable FileChannel fileChannel;
// this represents the charset encoding (if any is needed) for the
// output file
@@ -403,6 +407,7 @@ protected class OutputState {
* @return the byte offset position of the cursor in the output file
* @throws IOException If unable to get the offset position
*/
+ @SuppressWarnings("DataFlowIssue")
public long position() throws IOException {
if (fileChannel == null) {
return 0;
@@ -511,6 +516,7 @@ private void closeStream() {
* @param line String to be written to the file
* @throws IOException If unable to write the String to the file
*/
+ @SuppressWarnings("DataFlowIssue")
public void write(String line) throws IOException {
if (!initialized) {
initializeBufferedWriter();
@@ -524,6 +530,7 @@ public void write(String line) throws IOException {
* Truncate the output at the last known good point.
* @throws IOException if unable to work with file
*/
+ @SuppressWarnings("DataFlowIssue")
public void truncate() throws IOException {
fileChannel.truncate(lastMarkedByteOffsetPosition);
fileChannel.position(lastMarkedByteOffsetPosition);
@@ -534,6 +541,7 @@ public void truncate() throws IOException {
* information.
* @throws IOException if unable to initialize buffer
*/
+ @SuppressWarnings("DataFlowIssue")
private void initializeBufferedWriter() throws IOException {
File file = resource.getFile();
@@ -608,6 +616,7 @@ public void flush() throws IOException {
* beginning.
* @throws IOException if there is an IO problem
*/
+ @SuppressWarnings("DataFlowIssue")
private void checkFileSize() throws IOException {
long size;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java
index d289404c47..35d7a7790c 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java
@@ -16,11 +16,12 @@
package org.springframework.batch.item.support;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemCountAware;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStreamException;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -52,8 +53,7 @@ public abstract class AbstractItemCountingItemStreamItemReader extends Abstra
* @throws Exception Allows subclasses to throw checked exceptions for interpretation
* by the framework
*/
- @Nullable
- protected abstract T doRead() throws Exception;
+ protected abstract @Nullable T doRead() throws Exception;
/**
* Open resources necessary to start reading input.
@@ -83,9 +83,8 @@ protected void jumpToItem(int itemIndex) throws Exception {
}
}
- @Nullable
@Override
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
if (currentItemCount >= maxItemCount) {
return null;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessor.java
index da0d1d4736..221c3e87c7 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ClassifierCompositeItemProcessor.java
@@ -16,10 +16,11 @@
package org.springframework.batch.item.support;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ItemProcessor;
import org.springframework.classify.Classifier;
import org.springframework.classify.ClassifierSupport;
-import org.springframework.lang.Nullable;
/**
* Calls one of a collection of ItemProcessors, based on a router pattern implemented
@@ -47,9 +48,8 @@ public void setClassifier(Classifier super I, ItemProcessor, ? extends O>> c
* Delegates to injected {@link ItemProcessor} instances according to the
* classification by the {@link Classifier}.
*/
- @Nullable
@Override
- public O process(I item) throws Exception {
+ public @Nullable O process(I item) throws Exception {
return processItem(classifier.classify(item), item);
}
@@ -60,7 +60,7 @@ public O process(I item) throws Exception {
* extends O> is not applicable for the arguments (I)
*/
@SuppressWarnings("unchecked")
- private O processItem(ItemProcessor processor, I input) throws Exception {
+ private @Nullable O processItem(ItemProcessor processor, I input) throws Exception {
return processor.process((T) input);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java
index 767d7703dc..5cb6202a9d 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java
@@ -18,12 +18,13 @@
import org.springframework.batch.item.ItemProcessor;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.List;
+import org.jspecify.annotations.Nullable;
+
/**
* Composite {@link ItemProcessor} that passes the item through a sequence of injected
* ItemTransformers (return value of previous transformation is the entry
@@ -37,7 +38,7 @@
*/
public class CompositeItemProcessor implements ItemProcessor, InitializingBean {
- private List extends ItemProcessor, ?>> delegates;
+ private @Nullable List extends ItemProcessor, ?>> delegates;
/**
* Default constructor
@@ -64,10 +65,9 @@ public CompositeItemProcessor(List extends ItemProcessor, ?>> delegates) {
setDelegates(delegates);
}
- @Nullable
@Override
- @SuppressWarnings("unchecked")
- public O process(I item) throws Exception {
+ @SuppressWarnings({ "unchecked", "DataFlowIssue" })
+ public @Nullable O process(I item) throws Exception {
Object result = item;
for (ItemProcessor, ?> delegate : delegates) {
@@ -87,7 +87,7 @@ public O process(I item) throws Exception {
* not applicable for the arguments (Object)
*/
@SuppressWarnings("unchecked")
- private Object processItem(ItemProcessor processor, Object input) throws Exception {
+ private @Nullable Object processItem(ItemProcessor processor, Object input) throws Exception {
return processor.process((T) input);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemReader.java
index 73a92aa57a..78f66ba3da 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemReader.java
@@ -20,6 +20,8 @@
import java.util.List;
import org.springframework.batch.item.ExecutionContext;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamReader;
@@ -38,7 +40,7 @@ public class CompositeItemReader implements ItemStreamReader {
private final Iterator> delegatesIterator;
- private ItemStreamReader extends T> currentDelegate;
+ private @Nullable ItemStreamReader extends T> currentDelegate;
/**
* Create a new {@link CompositeItemReader}.
@@ -60,7 +62,7 @@ public void open(ExecutionContext executionContext) throws ItemStreamException {
}
@Override
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
if (this.currentDelegate == null) {
return null;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java
index 74b5c64878..242a4905a1 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.support;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
@@ -42,7 +43,7 @@
*/
public class CompositeItemWriter implements ItemStreamWriter, InitializingBean {
- private List> delegates;
+ private @Nullable List> delegates;
private boolean ignoreItemStream = false;
@@ -82,6 +83,7 @@ public void setIgnoreItemStream(boolean ignoreItemStream) {
this.ignoreItemStream = ignoreItemStream;
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends T> chunk) throws Exception {
for (ItemWriter super T> writer : delegates) {
@@ -111,6 +113,7 @@ public void setDelegates(List> delegates) {
* exceptions thrown by delegates are added as suppressed exceptions into this one, in
* the same order as delegates were registered.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() throws ItemStreamException {
List exceptions = new ArrayList<>();
@@ -134,6 +137,7 @@ public void close() throws ItemStreamException {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
for (ItemWriter super T> writer : delegates) {
@@ -143,6 +147,7 @@ public void open(ExecutionContext executionContext) throws ItemStreamException {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
for (ItemWriter super T> writer : delegates) {
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/IteratorItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/IteratorItemReader.java
index 95cb7d3fe3..6cf8b77100 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/IteratorItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/IteratorItemReader.java
@@ -19,7 +19,8 @@
import java.util.Iterator;
import org.springframework.batch.item.ItemReader;
-import org.springframework.lang.Nullable;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -62,9 +63,8 @@ public IteratorItemReader(Iterator iterator) {
* Implementation of {@link ItemReader#read()} that just iterates over the iterator
* provided.
*/
- @Nullable
@Override
- public T read() {
+ public @Nullable T read() {
if (iterator.hasNext())
return iterator.next();
else
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ListItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ListItemReader.java
index 23a18da31e..38c927062b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ListItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ListItemReader.java
@@ -20,8 +20,9 @@
import java.util.List;
import org.springframework.aop.support.AopUtils;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemReader;
-import org.springframework.lang.Nullable;
/**
* An {@link ItemReader} that pulls data from a list. Useful for testing.
@@ -50,9 +51,8 @@ public ListItemReader(List list) {
}
}
- @Nullable
@Override
- public T read() {
+ public @Nullable T read() {
if (!list.isEmpty()) {
return list.remove(0);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/PassThroughItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/PassThroughItemProcessor.java
index 6387ea5d2a..2219108215 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/PassThroughItemProcessor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/PassThroughItemProcessor.java
@@ -16,8 +16,9 @@
package org.springframework.batch.item.support;
+import org.jspecify.annotations.Nullable;
+
import org.springframework.batch.item.ItemProcessor;
-import org.springframework.lang.Nullable;
/**
* Simple {@link ItemProcessor} that does nothing - simply passes its argument through to
@@ -34,9 +35,8 @@ public class PassThroughItemProcessor implements ItemProcessor {
* @return the item
* @see ItemProcessor#process(Object)
*/
- @Nullable
@Override
- public T process(T item) throws Exception {
+ public @Nullable T process(T item) throws Exception {
return item;
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ScriptItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ScriptItemProcessor.java
index 8b86d78f13..603687b70b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ScriptItemProcessor.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/ScriptItemProcessor.java
@@ -15,7 +15,6 @@
*/
package org.springframework.batch.item.support;
-import org.springframework.lang.Nullable;
import org.springframework.scripting.support.StaticScriptSource;
import org.springframework.util.StringUtils;
import org.springframework.batch.item.ItemProcessor;
@@ -30,17 +29,16 @@
import java.util.HashMap;
import java.util.Map;
+import org.jspecify.annotations.Nullable;
+
/**
*
- * {@link org.springframework.batch.item.ItemProcessor} implementation that passes the
- * current item to process to the provided script. Exposes the current item for processing
- * via the
- * {@link org.springframework.batch.item.support.ScriptItemProcessor#ITEM_BINDING_VARIABLE_NAME}
- * key name ("item"). A custom key name can be set by invoking:
- * {@link org.springframework.batch.item.support.ScriptItemProcessor#setItemBindingVariableName}
- * with the desired key name. The thread safety of this
- * {@link org.springframework.batch.item.ItemProcessor} depends on the implementation of
- * the {@link org.springframework.scripting.ScriptEvaluator} used.
+ * {@link ItemProcessor} implementation that passes the current item to process to the
+ * provided script. Exposes the current item for processing via the
+ * {@link ScriptItemProcessor#ITEM_BINDING_VARIABLE_NAME} key name ("item"). A custom key
+ * name can be set by invoking: {@link ScriptItemProcessor#setItemBindingVariableName}
+ * with the desired key name. The thread safety of this {@link ItemProcessor} depends on
+ * the implementation of the {@link org.springframework.scripting.ScriptEvaluator} used.
*
*
* @author Chris Schaefer
@@ -50,20 +48,19 @@ public class ScriptItemProcessor implements ItemProcessor, Initializ
public static final String ITEM_BINDING_VARIABLE_NAME = "item";
- private String language;
+ private @Nullable String language;
- private ScriptSource script;
+ private @Nullable ScriptSource script;
- private ScriptSource scriptSource;
+ private @Nullable ScriptSource scriptSource;
- private ScriptEvaluator scriptEvaluator;
+ private @Nullable ScriptEvaluator scriptEvaluator;
private String itemBindingVariableName = ITEM_BINDING_VARIABLE_NAME;
- @Nullable
@Override
- @SuppressWarnings("unchecked")
- public O process(I item) throws Exception {
+ @SuppressWarnings({ "unchecked", "DataFlowIssue" })
+ public @Nullable O process(I item) throws Exception {
Map arguments = new HashMap<>();
arguments.put(itemBindingVariableName, item);
@@ -103,8 +100,7 @@ public void setScriptSource(String scriptSource, String language) {
*
* Provides the ability to change the key name that scripts use to obtain the current
* item to process if the variable represented by:
- * {@link org.springframework.batch.item.support.ScriptItemProcessor#ITEM_BINDING_VARIABLE_NAME}
- * is not suitable ("item").
+ * {@link ScriptItemProcessor#ITEM_BINDING_VARIABLE_NAME} is not suitable ("item").
*
* @param itemBindingVariableName the desired binding variable name
*/
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SingleItemPeekableItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SingleItemPeekableItemReader.java
index e1d25c8f0b..bf9f189f16 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SingleItemPeekableItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SingleItemPeekableItemReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2024 the original author or authors.
+ * Copyright 2006-2025 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,12 +18,13 @@
import java.util.Map.Entry;
import org.springframework.batch.item.ExecutionContext;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.PeekableItemReader;
-import org.springframework.lang.Nullable;
/**
*
@@ -40,13 +41,12 @@
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
- *
*/
public class SingleItemPeekableItemReader implements ItemStreamReader, PeekableItemReader {
- private ItemReader delegate;
+ private @Nullable ItemReader delegate;
- private T next;
+ private @Nullable T next;
private ExecutionContext executionContext = new ExecutionContext();
@@ -64,9 +64,9 @@ public void setDelegate(ItemReader delegate) {
*
* @see ItemReader#read()
*/
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
if (next != null) {
T item = next;
next = null;
@@ -82,9 +82,9 @@ public T read() throws Exception {
*
* @see PeekableItemReader#peek()
*/
- @Nullable
+ @SuppressWarnings("DataFlowIssue")
@Override
- public T peek() throws Exception {
+ public @Nullable T peek() throws Exception {
if (next == null) {
updateDelegate(executionContext);
next = delegate.read();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemReader.java
index 69655a50e2..7e534d43ad 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemReader.java
@@ -19,7 +19,8 @@
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.batch.item.ItemReader;
-import org.springframework.lang.Nullable;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -47,8 +48,7 @@ public SynchronizedItemReader(ItemReader delegate) {
* synchronized with a lock.
*/
@Override
- @Nullable
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
this.lock.lock();
try {
return this.delegate.read();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamReader.java
index a2909b9228..7c08699313 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamReader.java
@@ -19,9 +19,10 @@
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.batch.item.ExecutionContext;
+
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -29,7 +30,7 @@
* This is a simple ItemStreamReader decorator with a synchronized ItemReader.read()
* method - which makes a non-thread-safe ItemReader thread-safe.
*
- * However, if reprocessing an item is problematic then using this will make a job not
+ * However, if reprocessing an item is problematic, then using this will make a job not
* restartable.
*
* Here is the motivation behind this class: https://stackoverflow.com/a/20002493/2910265
@@ -41,7 +42,7 @@
*/
public class SynchronizedItemStreamReader implements ItemStreamReader, InitializingBean {
- private ItemStreamReader delegate;
+ private @Nullable ItemStreamReader delegate;
private final Lock lock = new ReentrantLock();
@@ -52,9 +53,9 @@ public void setDelegate(ItemStreamReader delegate) {
/**
* This delegates to the read method of the delegate
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
- @Nullable
- public T read() throws Exception {
+ public @Nullable T read() throws Exception {
this.lock.lock();
try {
return this.delegate.read();
@@ -64,16 +65,19 @@ public T read() throws Exception {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() {
this.delegate.close();
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void open(ExecutionContext executionContext) {
this.delegate.open(executionContext);
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void update(ExecutionContext executionContext) {
this.delegate.update(executionContext);
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamWriter.java
index ad69b89a5f..aef5a67a63 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/SynchronizedItemStreamWriter.java
@@ -18,6 +18,7 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
@@ -48,7 +49,7 @@
*/
public class SynchronizedItemStreamWriter implements ItemStreamWriter, InitializingBean {
- private ItemStreamWriter delegate;
+ private @Nullable ItemStreamWriter delegate;
private final Lock lock = new ReentrantLock();
@@ -63,6 +64,7 @@ public void setDelegate(ItemStreamWriter delegate) {
/**
* This method delegates to the {@code write} method of the {@code delegate}.
*/
+ @SuppressWarnings("DataFlowIssue")
@Override
public void write(Chunk extends T> items) throws Exception {
this.lock.lock();
@@ -74,16 +76,19 @@ public void write(Chunk extends T> items) throws Exception {
}
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
this.delegate.open(executionContext);
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
this.delegate.update(executionContext);
}
+ @SuppressWarnings("DataFlowIssue")
@Override
public void close() throws ItemStreamException {
this.delegate.close();
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemProcessorBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemProcessorBuilder.java
index fd5e47f8e8..fa463d1d71 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemProcessorBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemProcessorBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.support.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.support.ClassifierCompositeItemProcessor;
import org.springframework.classify.Classifier;
@@ -29,7 +30,7 @@
*/
public class ClassifierCompositeItemProcessorBuilder {
- private Classifier super I, ItemProcessor, ? extends O>> classifier;
+ private @Nullable Classifier super I, ItemProcessor, ? extends O>> classifier;
/**
* Establishes the classifier that will determine which {@link ItemProcessor} to use.
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemWriterBuilder.java
index dd0bccdaf0..d891161315 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ClassifierCompositeItemWriterBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.support.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ClassifierCompositeItemWriter;
import org.springframework.classify.Classifier;
@@ -30,7 +31,7 @@
*/
public class ClassifierCompositeItemWriterBuilder {
- private Classifier> classifier;
+ private @Nullable Classifier> classifier;
/**
* Establish the classifier to be used for the selection of which {@link ItemWriter}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemProcessorBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemProcessorBuilder.java
index c4d6291e4a..d3f713c7e9 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemProcessorBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemProcessorBuilder.java
@@ -19,6 +19,7 @@
import java.util.Arrays;
import java.util.List;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.support.CompositeItemProcessor;
import org.springframework.util.Assert;
@@ -32,7 +33,7 @@
*/
public class CompositeItemProcessorBuilder {
- private List extends ItemProcessor, ?>> delegates;
+ private @Nullable List extends ItemProcessor, ?>> delegates;
/**
* Establishes the {@link ItemProcessor} delegates that will work on the item to be
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemWriterBuilder.java
index adc067de12..e7c70df1ec 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemWriterBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/CompositeItemWriterBuilder.java
@@ -19,6 +19,7 @@
import java.util.Arrays;
import java.util.List;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.CompositeItemWriter;
import org.springframework.util.Assert;
@@ -33,7 +34,7 @@
*/
public class CompositeItemWriterBuilder {
- private List> delegates;
+ private @Nullable List> delegates;
private boolean ignoreItemStream = false;
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ScriptItemProcessorBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ScriptItemProcessorBuilder.java
index 7a79f5e808..c9eeaca03b 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ScriptItemProcessorBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/ScriptItemProcessorBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.support.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.support.ScriptItemProcessor;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
@@ -29,13 +30,13 @@
*/
public class ScriptItemProcessorBuilder {
- private String language;
+ private @Nullable String language;
- private Resource scriptResource;
+ private @Nullable Resource scriptResource;
- private String scriptSource;
+ private @Nullable String scriptSource;
- private String itemBindingVariableName;
+ private @Nullable String itemBindingVariableName;
/**
* Sets the {@link org.springframework.core.io.Resource} location of the script to
@@ -114,6 +115,7 @@ public ScriptItemProcessor build() {
}
if (this.scriptSource != null) {
+ Assert.hasText(language, "Language must contain the script language");
processor.setScriptSource(this.scriptSource, this.language);
}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/SingleItemPeekableItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/SingleItemPeekableItemReaderBuilder.java
index 2b9b5fc94b..dd4218f2ea 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/SingleItemPeekableItemReaderBuilder.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/builder/SingleItemPeekableItemReaderBuilder.java
@@ -16,6 +16,7 @@
package org.springframework.batch.item.support.builder;
+import org.jspecify.annotations.Nullable;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.support.SingleItemPeekableItemReader;
import org.springframework.util.Assert;
@@ -28,7 +29,7 @@
*/
public class SingleItemPeekableItemReaderBuilder {
- private ItemReader