Skip to content

Commit 2b949cb

Browse files
authored
GH-10083: Migrate spring-integration-ws module to Jspecify
Related to: #10083 - Replace `org.springframework.lang.Nullable` with `org.jspecify.annotations.Nullable` - Migrate `package-info.java` files to use `@NullMarked` annotation - Add `@SuppressWarnings("NullAway.Init")` for fields initialized in lifecycle methods * Apply JSpecify null safety annotation improvements - Use proper array nullability: `WebServiceMessageSender @nullable []` - Fix inner class annotation placement: `DefaultUriBuilderFactory.@nullable EncodingMode` - Set default Jaxb2Marshaller for gatewayMarshaller field * Add `@NullMarked` to all the `ws` packages * Add `@Nullable` or `@SuppressWarnings("NullAway.Init")` whenever it is requested * Add defensive null checks for better safety (`DefaultSoapHeaderMapper`, `SimpleWebServiceOutboundGateway`) * Fix WebServiceMessageFactory null handling * Add constructor overloads without messageFactory parameter * Clean up unnecessary @SuppressWarnings("NullAway") annotations * Apply review feedback to simplify nullable handling and clean up suppressions * Use `@Nullable` `WebServiceMessageFactory` with conditional `webServiceTemplate` creation * Mark `uri` and webServiceMessageFactory fields as `@Nullable`, keep template as final * Remove all `@SuppressWarnings("NullAway")` annotations * Remove unnecessary ctor overloads from previous approach Signed-off-by: Jooyoung Pyoung <[email protected]>
1 parent 806ca20 commit 2b949cb

12 files changed

+91
-61
lines changed

spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@
2525

2626
import javax.xml.transform.TransformerException;
2727

28+
import org.jspecify.annotations.Nullable;
29+
2830
import org.springframework.expression.Expression;
2931
import org.springframework.expression.spel.support.StandardEvaluationContext;
3032
import org.springframework.integration.expression.ExpressionEvalMap;
@@ -59,22 +61,24 @@
5961
* @author Artem Bilan
6062
* @author Christian Tzolov
6163
* @author Ngoc Nhan
64+
* @author Jooyoung Pyoung
6265
*/
6366
public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyProducingMessageHandler {
6467

6568
private final Lock lock = new ReentrantLock();
6669

6770
protected final DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); // NOSONAR - final
6871

69-
private final String uri;
72+
private final @Nullable String uri;
7073

71-
private final DestinationProvider destinationProvider;
74+
private final @Nullable DestinationProvider destinationProvider;
7275

7376
private final Map<String, Expression> uriVariableExpressions = new HashMap<>();
7477

78+
@SuppressWarnings("NullAway.Init")
7579
private StandardEvaluationContext evaluationContext;
7680

77-
private WebServiceMessageCallback requestCallback;
81+
private @Nullable WebServiceMessageCallback requestCallback;
7882

7983
private WebServiceTemplate webServiceTemplate;
8084

@@ -84,18 +88,20 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
8488

8589
private boolean webServiceTemplateExplicitlySet;
8690

87-
public AbstractWebServiceOutboundGateway(final String uri, WebServiceMessageFactory messageFactory) {
91+
public AbstractWebServiceOutboundGateway(@Nullable final String uri, @Nullable WebServiceMessageFactory messageFactory) {
8892
Assert.hasText(uri, "URI must not be empty");
89-
this.webServiceTemplate = new WebServiceTemplate(messageFactory);
93+
this.webServiceTemplate = messageFactory != null ?
94+
new WebServiceTemplate(messageFactory) : new WebServiceTemplate();
9095
this.destinationProvider = null;
9196
this.uri = uri;
9297
}
9398

9499
public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider,
95-
WebServiceMessageFactory messageFactory) {
100+
@Nullable WebServiceMessageFactory messageFactory) {
96101

97102
Assert.notNull(destinationProvider, "DestinationProvider must not be null");
98-
this.webServiceTemplate = new WebServiceTemplate(messageFactory);
103+
this.webServiceTemplate = messageFactory != null ?
104+
new WebServiceTemplate(messageFactory) : new WebServiceTemplate();
99105
this.destinationProvider = destinationProvider;
100106
// we always call WebServiceTemplate methods with an explicit URI argument,
101107
// but in case the WebServiceTemplate is accessed directly we'll set this:
@@ -165,7 +171,7 @@ public void setMessageFactory(WebServiceMessageFactory messageFactory) {
165171
this.webServiceTemplate.setMessageFactory(messageFactory);
166172
}
167173

168-
public void setRequestCallback(WebServiceMessageCallback requestCallback) {
174+
public void setRequestCallback(@Nullable WebServiceMessageCallback requestCallback) {
169175
this.requestCallback = requestCallback;
170176
}
171177

@@ -199,7 +205,7 @@ protected WebServiceTemplate getWebServiceTemplate() {
199205
}
200206

201207
@Override
202-
public final Object handleRequestMessage(Message<?> requestMessage) {
208+
public final @Nullable Object handleRequestMessage(Message<?> requestMessage) {
203209
URI uriWithVariables = prepareUri(requestMessage);
204210
if (uriWithVariables == null) {
205211
throw new MessageDeliveryException(requestMessage, "Failed to determine URI for " +
@@ -215,7 +221,7 @@ public final Object handleRequestMessage(Message<?> requestMessage) {
215221
return null;
216222
}
217223

218-
private URI prepareUri(Message<?> requestMessage) {
224+
private @Nullable URI prepareUri(Message<?> requestMessage) {
219225
if (this.destinationProvider != null) {
220226
return this.destinationProvider.getDestination();
221227
}
@@ -226,20 +232,21 @@ private URI prepareUri(Message<?> requestMessage) {
226232
.withRoot(requestMessage)
227233
.build();
228234

235+
Assert.notNull(this.uri, "'uri' must not be null");
229236
return this.uriFactory.expand(this.uri, uriVariables);
230237
}
231238

232-
protected abstract Object doHandle(String theUri, Message<?> requestMessage,
233-
WebServiceMessageCallback reqCallback);
239+
protected abstract @Nullable Object doHandle(String theUri, Message<?> requestMessage,
240+
@Nullable WebServiceMessageCallback reqCallback);
234241

235242
protected abstract class RequestMessageCallback extends TransformerObjectSupport
236243
implements WebServiceMessageCallback {
237244

238-
private final WebServiceMessageCallback reqCallback;
245+
private final @Nullable WebServiceMessageCallback reqCallback;
239246

240247
private final Message<?> requestMessage;
241248

242-
public RequestMessageCallback(WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
249+
public RequestMessageCallback(@Nullable WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
243250
this.reqCallback = requestCallback;
244251
this.requestMessage = requestMessage;
245252
}
@@ -266,7 +273,7 @@ protected abstract class ResponseMessageExtractor extends TransformerObjectSuppo
266273
implements WebServiceMessageExtractor<Object> {
267274

268275
@Override
269-
public Object extractData(WebServiceMessage message)
276+
public @Nullable Object extractData(WebServiceMessage message)
270277
throws IOException, TransformerException {
271278

272279
Object resultObject = this.doExtractData(message);
@@ -284,7 +291,7 @@ public Object extractData(WebServiceMessage message)
284291
}
285292
}
286293

287-
public abstract Object doExtractData(WebServiceMessage message) throws IOException, TransformerException;
294+
public abstract @Nullable Object doExtractData(WebServiceMessage message) throws IOException, TransformerException;
288295

289296
}
290297

spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@
5353
* @author Mauro Molinari
5454
* @author Artem Bilan
5555
* @author Gary Russell
56+
* @author Jooyoung Pyoung
5657
*
5758
* @since 2.0
5859
*/
@@ -126,6 +127,10 @@ else if (!StringUtils.hasText(target.getSoapAction())) {
126127
@Override
127128
protected void populateUserDefinedHeader(String headerName, Object headerValue, SoapMessage target) {
128129
SoapHeader soapHeader = target.getSoapHeader();
130+
if (soapHeader == null) {
131+
return;
132+
}
133+
129134
if (headerValue instanceof String) {
130135
QName qname = QNameUtils.parseQNameString(headerName);
131136
soapHeader.addAttribute(qname, (String) headerValue);

spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,8 +35,10 @@
3535
*/
3636
public class MarshallingWebServiceInboundGateway extends AbstractWebServiceInboundGateway {
3737

38+
@SuppressWarnings("NullAway.Init")
3839
private Marshaller marshaller;
3940

41+
@SuppressWarnings("NullAway.Init")
4042
private Unmarshaller unmarshaller;
4143

4244
/**

spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818

1919
import java.io.IOException;
2020

21-
import org.springframework.lang.Nullable;
21+
import org.jspecify.annotations.Nullable;
22+
2223
import org.springframework.messaging.Message;
2324
import org.springframework.oxm.Marshaller;
2425
import org.springframework.oxm.Unmarshaller;
@@ -45,7 +46,7 @@ public class MarshallingWebServiceOutboundGateway extends AbstractWebServiceOutb
4546

4647
@SuppressWarnings("this-escape")
4748
public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller,
48-
Unmarshaller unmarshaller, WebServiceMessageFactory messageFactory) {
49+
@Nullable Unmarshaller unmarshaller, @Nullable WebServiceMessageFactory messageFactory) {
4950
super(destinationProvider, messageFactory);
5051
configureMarshallers(marshaller, unmarshaller);
5152
}
@@ -56,7 +57,7 @@ public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvi
5657
}
5758

5859
public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller,
59-
WebServiceMessageFactory messageFactory) {
60+
@Nullable WebServiceMessageFactory messageFactory) {
6061
this(destinationProvider, marshaller, null, messageFactory);
6162
}
6263

@@ -65,8 +66,8 @@ public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvi
6566
}
6667

6768
@SuppressWarnings("this-escape")
68-
public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, Unmarshaller unmarshaller,
69-
WebServiceMessageFactory messageFactory) {
69+
public MarshallingWebServiceOutboundGateway(@Nullable String uri, Marshaller marshaller, @Nullable Unmarshaller unmarshaller,
70+
@Nullable WebServiceMessageFactory messageFactory) {
7071
super(uri, messageFactory);
7172
configureMarshallers(marshaller, unmarshaller);
7273
}
@@ -76,7 +77,7 @@ public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, U
7677
}
7778

7879
public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller,
79-
WebServiceMessageFactory messageFactory) {
80+
@Nullable WebServiceMessageFactory messageFactory) {
8081
this(uri, marshaller, null, messageFactory);
8182
}
8283

@@ -91,7 +92,7 @@ public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller) {
9192
* @since 5.0
9293
*/
9394
@SuppressWarnings("this-escape")
94-
public MarshallingWebServiceOutboundGateway(String uri, WebServiceTemplate webServiceTemplate) {
95+
public MarshallingWebServiceOutboundGateway(@Nullable String uri, WebServiceTemplate webServiceTemplate) {
9596
super(uri, null);
9697
doSetWebServiceTemplate(webServiceTemplate);
9798
}
@@ -139,15 +140,15 @@ public String getComponentType() {
139140
}
140141

141142
@Override
142-
protected Object doHandle(String uri, Message<?> requestMessage, WebServiceMessageCallback requestCallback) {
143+
protected @Nullable Object doHandle(String uri, Message<?> requestMessage, @Nullable WebServiceMessageCallback requestCallback) {
143144
return getWebServiceTemplate()
144145
.marshalSendAndReceive(uri, requestMessage.getPayload(),
145146
new PassThroughRequestMessageCallback(requestCallback, requestMessage));
146147
}
147148

148149
private final class PassThroughRequestMessageCallback extends RequestMessageCallback {
149150

150-
PassThroughRequestMessageCallback(WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
151+
PassThroughRequestMessageCallback(@Nullable WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
151152
super(requestCallback, requestMessage);
152153
}
153154

spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,9 +25,9 @@
2525
import javax.xml.transform.dom.DOMResult;
2626
import javax.xml.transform.dom.DOMSource;
2727

28+
import org.jspecify.annotations.Nullable;
2829
import org.w3c.dom.Document;
2930

30-
import org.springframework.lang.Nullable;
3131
import org.springframework.messaging.Message;
3232
import org.springframework.messaging.MessagingException;
3333
import org.springframework.util.Assert;
@@ -49,6 +49,7 @@
4949
* @author Oleg Zhurakousky
5050
* @author Artem Bilan
5151
* @author Gary Russell
52+
* @author Jooyoung Pyoung
5253
*/
5354
public class SimpleWebServiceOutboundGateway extends AbstractWebServiceOutboundGateway {
5455

@@ -68,8 +69,7 @@ public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider,
6869

6970
public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider,
7071
@Nullable SourceExtractor<?> sourceExtractor,
71-
WebServiceMessageFactory messageFactory) {
72-
72+
@Nullable WebServiceMessageFactory messageFactory) {
7373
super(destinationProvider, messageFactory);
7474
this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor();
7575
}
@@ -82,8 +82,8 @@ public SimpleWebServiceOutboundGateway(String uri, SourceExtractor<?> sourceExtr
8282
this(uri, sourceExtractor, null);
8383
}
8484

85-
public SimpleWebServiceOutboundGateway(String uri, @Nullable SourceExtractor<?> sourceExtractor,
86-
WebServiceMessageFactory messageFactory) {
85+
public SimpleWebServiceOutboundGateway(@Nullable String uri, @Nullable SourceExtractor<?> sourceExtractor,
86+
@Nullable WebServiceMessageFactory messageFactory) {
8787

8888
super(uri, messageFactory);
8989
this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor();
@@ -107,8 +107,8 @@ public String getComponentType() {
107107
}
108108

109109
@Override
110-
protected Object doHandle(String uri, final Message<?> requestMessage,
111-
final WebServiceMessageCallback requestCallback) {
110+
protected @Nullable Object doHandle(String uri, final Message<?> requestMessage,
111+
final @Nullable WebServiceMessageCallback requestCallback) {
112112

113113
Object requestPayload = requestMessage.getPayload();
114114
Result responseResultInstance = null;
@@ -126,21 +126,24 @@ else if (requestPayload instanceof Document) {
126126

127127
private final class SimpleRequestMessageCallback extends RequestMessageCallback {
128128

129-
SimpleRequestMessageCallback(WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
129+
SimpleRequestMessageCallback(@Nullable WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
130130
super(requestCallback, requestMessage);
131131
}
132132

133133
@Override
134134
public void doWithMessageInternal(WebServiceMessage message, Object payload)
135135
throws IOException, TransformerException {
136136
Source source = this.extractSource(payload);
137+
if (source == null) {
138+
source = new DOMSource();
139+
}
137140
transform(source, message.getPayloadResult());
138141
if (message instanceof MimeMessage && payload instanceof MimeMessage) {
139142
copyAttachments((MimeMessage) payload, (MimeMessage) message);
140143
}
141144
}
142145

143-
private Source extractSource(Object requestPayload) throws IOException, TransformerException {
146+
private @Nullable Source extractSource(Object requestPayload) throws IOException, TransformerException {
144147
Source source = null;
145148

146149
if (requestPayload instanceof Source) {
@@ -181,14 +184,14 @@ private void copyAttachments(MimeMessage source, MimeMessage target) {
181184

182185
private final class SimpleResponseMessageExtractor extends ResponseMessageExtractor {
183186

184-
private final Result result;
187+
private final @Nullable Result result;
185188

186-
SimpleResponseMessageExtractor(Result result) {
189+
SimpleResponseMessageExtractor(@Nullable Result result) {
187190
this.result = result;
188191
}
189192

190193
@Override
191-
public Object doExtractData(WebServiceMessage message) throws TransformerException {
194+
public @Nullable Object doExtractData(WebServiceMessage message) throws TransformerException {
192195
if (!SimpleWebServiceOutboundGateway.this.extractPayload) {
193196
return message;
194197
}
@@ -220,10 +223,13 @@ private static class DefaultSourceExtractor extends TransformerObjectSupport imp
220223
}
221224

222225
@Override
223-
public DOMSource extractData(Source source) throws TransformerException {
226+
public @Nullable DOMSource extractData(@Nullable Source source) throws TransformerException {
224227
if (source instanceof DOMSource) {
225228
return (DOMSource) source;
226229
}
230+
else if (source == null) {
231+
return new DOMSource();
232+
}
227233
DOMResult result = new DOMResult();
228234
this.transform(source, result);
229235
return new DOMSource(result.getNode());
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/**
22
* Contains parser classes for the Web Services namespace support.
33
*/
4+
@org.jspecify.annotations.NullMarked
45
package org.springframework.integration.ws.config;

0 commit comments

Comments
 (0)