Skip to content

Commit 4dccefd

Browse files
authored
Add JSpecify Nullability to the xml parsers package
* Tests were updated * Test XML files updated to handle non nullable attributes and parameters * In some cases I reformatted the whole xml file vs the changed section. If this is a problem I'll undo. * In many circumstances the chances of having null, should not happen, so `Objects.requireNonNull` was used. But wonder if we should use Assert.state and have a message. Thoughts? * Removed @nullable from requested locations * Added requested `@SuppressWarning("NullAway")` to specified locations * Removed format changes from XML file that was mistakingly pushed. * Resolved Nullability compile time errors from where the @nullable was removed * PointToPointChannelParser requires a null check on line 82 because builder because it may not have been set. With this assert we can throw away the previous bogus exception * Remove correlation and release strategy entries from context-xml files Remove CassandraParserUtilsTests. Other tests provide the coverage required. Readd method variable for readability * Remove @nullable from buildBeanDefinition in both AbstractChannelParser andk KafkaChannelParser Add dummy exceptions to be applied below calls to .error so that NullAway knows that it will not return null Make sure dummy exceptions have a consistent message describing why they are there * Add blank line to improve readability in `AbstractPollingInboundChannelAdapterParser` class. Replace stock exception for NullAway to signal that `error` will throw an exception with the message passed to the `error` method.
1 parent 2033d7e commit 4dccefd

29 files changed

+147
-204
lines changed

spring-integration-cassandra/src/main/java/org/springframework/integration/cassandra/config/xml/CassandraParserUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.List;
2020

21+
import org.jspecify.annotations.Nullable;
2122
import org.w3c.dom.Element;
2223

2324
import org.springframework.beans.factory.config.BeanDefinition;
@@ -91,7 +92,7 @@ public static void processOutboundTypeAttributes(Element element, ParserContext
9192

9293
}
9394

94-
public static boolean areMutuallyExclusive(String query, BeanDefinition statementExpressionDef,
95+
private static boolean areMutuallyExclusive(String query, @Nullable BeanDefinition statementExpressionDef,
9596
String ingestQuery) {
9697

9798
return !StringUtils.hasText(query) && statementExpressionDef == null && !StringUtils.hasText(ingestQuery)

spring-integration-cassandra/src/test/java/org/springframework/integration/cassandra/config/CassandraParserUtilsTests.java

Lines changed: 0 additions & 103 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/**
22
* Provides classes supporting annotation-based configuration.
33
*/
4+
@org.jspecify.annotations.NullMarked
45
package org.springframework.integration.config.annotation;

spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractChannelAdapterParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.config.xml;
1818

19+
import org.jspecify.annotations.Nullable;
1920
import org.w3c.dom.Element;
2021

2122
import org.springframework.beans.MutablePropertyValues;
@@ -61,6 +62,7 @@ else if (!StringUtils.hasText(id)) {
6162
return id;
6263
}
6364

65+
@SuppressWarnings("NullAway")
6466
@Override
6567
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
6668
String channelName = element.getAttribute("channel");
@@ -88,7 +90,7 @@ protected final AbstractBeanDefinition parseInternal(Element element, ParserCont
8890
return beanDefinition;
8991
}
9092

91-
private String createDirectChannel(Element element, ParserContext parserContext) {
93+
private @Nullable String createDirectChannel(Element element, ParserContext parserContext) {
9294
if (parserContext.isNested()) {
9395
return null;
9496
}

spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Collection;
2020
import java.util.List;
2121

22+
import org.jspecify.annotations.Nullable;
2223
import org.w3c.dom.Element;
2324

2425
import org.springframework.beans.factory.BeanDefinitionStoreException;
@@ -85,7 +86,7 @@ protected String getInputChannelAttributeName() {
8586
}
8687

8788
@Override
88-
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
89+
protected final @Nullable AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
8990
BeanDefinitionBuilder handlerBuilder = parseHandler(element, parserContext);
9091
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "output-channel",
9192
"outputChannelName");
@@ -178,6 +179,7 @@ private void poller(Element element, ParserContext parserContext, BeanDefinition
178179
}
179180
}
180181

182+
@SuppressWarnings("NullAway") // Dataflow analysis limitation
181183
private void registerChannelForCreation(ParserContext parserContext, String inputChannelName,
182184
BeanDefinitionBuilder consumerEndpointBuilder) {
183185

@@ -194,13 +196,11 @@ private void registerChannelForCreation(ParserContext parserContext, String inpu
194196
if (vh == null) { //although it should never happen if it does we can fix it
195197
caValues.addIndexedArgumentValue(0, new ManagedSet<String>());
196198
}
197-
198199
@SuppressWarnings("unchecked")
199200
Collection<String> channelCandidateNames =
200201
(Collection<String>) caValues.getArgumentValue(0, Collection.class)
201-
.getValue(); // NOSONAR see comment above
202-
channelCandidateNames.add(inputChannelName); // NOSONAR
203-
202+
.getValue();
203+
channelCandidateNames.add(inputChannelName);
204204
consumerEndpointBuilder.addDependsOn(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
205205
}
206206
else {

spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractCorrelatingMessageHandlerParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.config.xml;
1818

19+
import org.jspecify.annotations.Nullable;
1920
import org.w3c.dom.Element;
2021

2122
import org.springframework.beans.BeanMetadataElement;
@@ -65,7 +66,7 @@ public abstract class AbstractCorrelatingMessageHandlerParser extends AbstractCo
6566

6667
private static final String RELEASE_LOCK = "release-lock-before-send";
6768

68-
protected void doParse(BeanDefinitionBuilder builder, Element element, BeanMetadataElement processor,
69+
protected void doParse(BeanDefinitionBuilder builder, Element element, @Nullable BeanMetadataElement processor,
6970
ParserContext parserContext) {
7071
IntegrationNamespaceUtils.injectPropertyWithAdapter(CORRELATION_STRATEGY_REF_ATTRIBUTE,
7172
CORRELATION_STRATEGY_METHOD_ATTRIBUTE,

spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractDelegatingConsumerEndpointParser.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.config.xml;
1818

19+
import org.jspecify.annotations.Nullable;
1920
import org.w3c.dom.Element;
2021

2122
import org.springframework.beans.factory.config.BeanDefinition;
@@ -65,7 +66,6 @@ else if (hasRef && hasExpression) {
6566
parserContext.getReaderContext().error(
6667
"Only one of 'ref' or 'expression' is permitted, not both, on element " +
6768
IntegrationNamespaceUtils.createElementDescription(element) + ".", source);
68-
return null;
6969
}
7070
else if (hasRef) {
7171
builder.addPropertyReference("targetObject", ref);
@@ -77,7 +77,6 @@ else if (!this.hasDefaultOption()) {
7777
parserContext.getReaderContext().error("Exactly one of the 'ref' attribute, 'expression' attribute, " +
7878
"or inner bean (<bean/>) definition is required for element " +
7979
IntegrationNamespaceUtils.createElementDescription(element) + ".", source);
80-
return null;
8180
}
8281
methodAttribute(element, parserContext, source, builder, innerDefinition, hasRef, hasExpression,
8382
expressionElement);
@@ -87,9 +86,9 @@ else if (!this.hasDefaultOption()) {
8786
return builder;
8887
}
8988

90-
private void innerDefinition(Element element, ParserContext parserContext, Object source,
89+
private void innerDefinition(Element element, ParserContext parserContext, @Nullable Object source,
9190
BeanDefinitionBuilder builder, BeanComponentDefinition innerDefinition, boolean hasRef,
92-
boolean hasExpression, Element expressionElement) {
91+
boolean hasExpression, @Nullable Element expressionElement) {
9392
if (hasRef || hasExpression || expressionElement != null) {
9493
parserContext.getReaderContext().error(
9594
"Neither 'ref' nor 'expression' are permitted when an inner bean (<bean/>) is configured on element " +
@@ -98,9 +97,9 @@ private void innerDefinition(Element element, ParserContext parserContext, Objec
9897
builder.addPropertyValue("targetObject", innerDefinition);
9998
}
10099

101-
private void scriptElement(Element element, ParserContext parserContext, Object source,
100+
private void scriptElement(Element element, ParserContext parserContext, @Nullable Object source,
102101
BeanDefinitionBuilder builder, boolean hasRef, boolean hasExpression, Element scriptElement,
103-
Element expressionElement) {
102+
@Nullable Element expressionElement) {
104103
if (hasRef || hasExpression || expressionElement != null) {
105104
parserContext.getReaderContext().error(
106105
"Neither 'ref' nor 'expression' are permitted when an inner script element is configured on element " +
@@ -110,7 +109,7 @@ private void scriptElement(Element element, ParserContext parserContext, Object
110109
builder.addPropertyValue("targetObject", scriptBeanDefinition);
111110
}
112111

113-
private void expressionElement(Element element, ParserContext parserContext, Object source,
112+
private void expressionElement(Element element, ParserContext parserContext, @Nullable Object source,
114113
BeanDefinitionBuilder builder, boolean hasRef, boolean hasExpression, Element expressionElement) {
115114
if (hasRef || hasExpression) {
116115
parserContext.getReaderContext().error(
@@ -126,9 +125,9 @@ private void expressionElement(Element element, ParserContext parserContext, Obj
126125
builder.addPropertyValue("expression", dynamicExpressionBuilder.getBeanDefinition());
127126
}
128127

129-
private void methodAttribute(Element element, ParserContext parserContext, Object source,
130-
BeanDefinitionBuilder builder, BeanComponentDefinition innerDefinition, boolean hasRef,
131-
boolean hasExpression, Element expressionElement) {
128+
private void methodAttribute(Element element, ParserContext parserContext, @Nullable Object source,
129+
BeanDefinitionBuilder builder, @Nullable BeanComponentDefinition innerDefinition, boolean hasRef,
130+
boolean hasExpression, @Nullable Element expressionElement) {
132131
String method = element.getAttribute(METHOD_ATTRIBUTE);
133132
if (StringUtils.hasText(method)) {
134133
if (hasExpression || expressionElement != null) {

spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.concurrent.atomic.AtomicBoolean;
2020

21+
import org.jspecify.annotations.Nullable;
2122
import org.w3c.dom.Element;
2223

2324
import org.springframework.beans.factory.config.BeanDefinition;
@@ -47,7 +48,7 @@ public abstract class AbstractIntegrationNamespaceHandler extends NamespaceHandl
4748
private final AtomicBoolean initialized = new AtomicBoolean();
4849

4950
@Override
50-
public final BeanDefinition parse(Element element, ParserContext parserContext) {
51+
public final @Nullable BeanDefinition parse(Element element, ParserContext parserContext) {
5152
if (!this.initialized.getAndSet(true)) {
5253
BeanDefinitionRegistry registry = parserContext.getRegistry();
5354
new IntegrationRegistrar().registerBeanDefinitions(null, registry);

spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractOutboundChannelAdapterParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.config.xml;
1818

19+
import org.jspecify.annotations.Nullable;
1920
import org.w3c.dom.Element;
2021

2122
import org.springframework.beans.factory.config.BeanDefinition;
@@ -82,7 +83,7 @@ protected AbstractBeanDefinition doParse(Element element, ParserContext parserCo
8283
}
8384

8485
private void configureRequestHandlerAdviceChain(Element element, ParserContext parserContext,
85-
BeanDefinition handlerBeanDefinition, BeanDefinitionBuilder consumerBuilder) {
86+
BeanDefinition handlerBeanDefinition, @Nullable BeanDefinitionBuilder consumerBuilder) {
8687
Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
8788
Element adviceChainElement = DomUtils.getChildElementByTagName(element,
8889
IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN);

spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractPollingInboundChannelAdapterParser.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ else if (source instanceof RuntimeBeanReference runtimeBeanReference) {
5959
}
6060
else {
6161
parserContext.getReaderContext().error("Wrong 'source' type: must be 'BeanDefinition' or 'RuntimeBeanReference'", source);
62+
// This exception is meant to signal to NullAway that the error method throws an exception
63+
throw new IllegalStateException("Wrong 'source' type: must be 'BeanDefinition' or 'RuntimeBeanReference'");
6264
}
6365

6466
adapterBuilder.addPropertyReference("source", sourceBeanName);

0 commit comments

Comments
 (0)