From 9c9405806fa985fe2eeab161d4f36b2984745ca2 Mon Sep 17 00:00:00 2001 From: Jooyoung Pyoung Date: Thu, 12 Jun 2025 00:03:41 +0900 Subject: [PATCH 1/4] GH-10083: Migrate `spring-integration-ws` module to Jspecify Related to: https://github.com/spring-projects/spring-integration/issues/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 Signed-off-by: Jooyoung Pyoung --- .../MarshallingWebServiceOutboundGateway.java | 3 ++- .../ws/SimpleWebServiceOutboundGateway.java | 4 ++-- .../ws/dsl/BaseWsOutboundGatewaySpec.java | 22 ++++++++++++++----- .../dsl/MarshallingWsOutboundGatewaySpec.java | 4 +++- .../ws/dsl/SimpleWsOutboundGatewaySpec.java | 6 +++-- .../integration/ws/dsl/package-info.java | 2 +- 6 files changed, 29 insertions(+), 12 deletions(-) diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java index 6a5329b9e3d..770b38fd117 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java @@ -18,7 +18,8 @@ import java.io.IOException; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; + import org.springframework.messaging.Message; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java index d5185e1a023..a9521683f2b 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -25,9 +25,9 @@ import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; +import org.jspecify.annotations.Nullable; import org.w3c.dom.Document; -import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java index c5d37417eba..0738a96b7c8 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-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. @@ -19,12 +19,14 @@ import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.expression.Expression; import org.springframework.integration.JavaUtils; import org.springframework.integration.dsl.MessageHandlerSpec; import org.springframework.integration.ws.AbstractWebServiceOutboundGateway; import org.springframework.integration.ws.SoapHeaderMapper; -import org.springframework.web.util.DefaultUriBuilderFactory; +import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.client.core.FaultMessageResolver; import org.springframework.ws.client.core.WebServiceMessageCallback; @@ -51,26 +53,36 @@ public abstract class BaseWsOutboundGatewaySpec< private final Map uriVariableExpressions = new HashMap<>(); + @Nullable protected WebServiceTemplate template; // NOSONAR + @Nullable protected DestinationProvider destinationProvider; // NOSONAR + @Nullable protected String uri; // NOSONAR + @Nullable protected WebServiceMessageFactory webServiceMessageFactory; // NOSONAR + @Nullable private SoapHeaderMapper headerMapper; - private DefaultUriBuilderFactory.EncodingMode encodingMode; + @Nullable + private EncodingMode encodingMode; private boolean ignoreEmptyResponses = true; + @Nullable private WebServiceMessageCallback requestCallback; + @Nullable protected FaultMessageResolver faultMessageResolver; // NOSONAR + @SuppressWarnings("NullAway.Init") protected WebServiceMessageSender[] messageSenders; // NOSONAR + @SuppressWarnings("NullAway.Init") protected ClientInterceptor[] gatewayInterceptors; // NOSONAR protected boolean extractPayload = true; // NOSONAR @@ -117,11 +129,11 @@ public S uriVariableExpressions(Map uriVariableExpressions) } /** - * Specify a {@link DefaultUriBuilderFactory.EncodingMode} for uri construction. + * Specify a {@link EncodingMode} for uri construction. * @param encodingMode to use for uri construction. * @return the spec */ - public S encodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) { + public S encodingMode(EncodingMode encodingMode) { this.encodingMode = encodingMode; return _this(); } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java index c78920ccd75..83d638347c2 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-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. @@ -63,8 +63,10 @@ public static class MarshallingWsOutboundGatewayNoTemplateSpec extends BaseWsOutboundGatewaySpec { + @SuppressWarnings("NullAway.Init") protected Marshaller gatewayMarshaller; // NOSONAR + @SuppressWarnings("NullAway.Init") protected Unmarshaller gatewayUnmarshaller; // NOSONAR /** diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java index fb584394ed2..9dd4539de82 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-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,10 @@ import java.util.Arrays; +import org.jspecify.annotations.Nullable; + import org.springframework.integration.JavaUtils; import org.springframework.integration.ws.SimpleWebServiceOutboundGateway; -import org.springframework.lang.Nullable; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.client.core.FaultMessageResolver; import org.springframework.ws.client.core.SourceExtractor; @@ -100,6 +101,7 @@ protected SimpleWebServiceOutboundGateway create() { public static class SimpleWsOutboundGatewayNoTemplateSpec extends BaseWsOutboundGatewaySpec { + @Nullable protected SourceExtractor sourceExtractor; // NOSONAR private boolean extractPayload = true; diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/package-info.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/package-info.java index b10046610d9..c77e7a4ab5a 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/package-info.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/package-info.java @@ -1,5 +1,5 @@ /** * Contains classes for DSL support. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.integration.ws.dsl; From 09840bb726a3c3f13aebe878d27636469e61b99f Mon Sep 17 00:00:00 2001 From: Jooyoung Pyoung Date: Thu, 12 Jun 2025 01:13:46 +0900 Subject: [PATCH 2/4] 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 Signed-off-by: Jooyoung Pyoung --- .../ws/dsl/BaseWsOutboundGatewaySpec.java | 36 +++++++------------ .../dsl/MarshallingWsOutboundGatewaySpec.java | 9 ++--- .../ws/dsl/SimpleWsOutboundGatewaySpec.java | 3 +- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java index 0738a96b7c8..224aa1bfe51 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java @@ -26,7 +26,7 @@ import org.springframework.integration.dsl.MessageHandlerSpec; import org.springframework.integration.ws.AbstractWebServiceOutboundGateway; import org.springframework.integration.ws.SoapHeaderMapper; -import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; +import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.client.core.FaultMessageResolver; import org.springframework.ws.client.core.WebServiceMessageCallback; @@ -53,37 +53,27 @@ public abstract class BaseWsOutboundGatewaySpec< private final Map uriVariableExpressions = new HashMap<>(); - @Nullable - protected WebServiceTemplate template; // NOSONAR + protected @Nullable WebServiceTemplate template; // NOSONAR - @Nullable - protected DestinationProvider destinationProvider; // NOSONAR + protected @Nullable DestinationProvider destinationProvider; // NOSONAR - @Nullable - protected String uri; // NOSONAR + protected @Nullable String uri; // NOSONAR - @Nullable - protected WebServiceMessageFactory webServiceMessageFactory; // NOSONAR + protected @Nullable WebServiceMessageFactory webServiceMessageFactory; // NOSONAR - @Nullable - private SoapHeaderMapper headerMapper; + private @Nullable SoapHeaderMapper headerMapper; - @Nullable - private EncodingMode encodingMode; + private DefaultUriBuilderFactory.@Nullable EncodingMode encodingMode; private boolean ignoreEmptyResponses = true; - @Nullable - private WebServiceMessageCallback requestCallback; + private @Nullable WebServiceMessageCallback requestCallback; - @Nullable - protected FaultMessageResolver faultMessageResolver; // NOSONAR + protected @Nullable FaultMessageResolver faultMessageResolver; // NOSONAR - @SuppressWarnings("NullAway.Init") - protected WebServiceMessageSender[] messageSenders; // NOSONAR + protected WebServiceMessageSender @Nullable [] messageSenders; // NOSONAR - @SuppressWarnings("NullAway.Init") - protected ClientInterceptor[] gatewayInterceptors; // NOSONAR + protected ClientInterceptor @Nullable [] gatewayInterceptors; // NOSONAR protected boolean extractPayload = true; // NOSONAR @@ -129,11 +119,11 @@ public S uriVariableExpressions(Map uriVariableExpressions) } /** - * Specify a {@link EncodingMode} for uri construction. + * Specify a {@link DefaultUriBuilderFactory.EncodingMode} for uri construction. * @param encodingMode to use for uri construction. * @return the spec */ - public S encodingMode(EncodingMode encodingMode) { + public S encodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) { this.encodingMode = encodingMode; return _this(); } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java index 83d638347c2..2d4c26521e3 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java @@ -18,10 +18,13 @@ import java.util.Arrays; +import org.jspecify.annotations.Nullable; + import org.springframework.integration.JavaUtils; import org.springframework.integration.ws.MarshallingWebServiceOutboundGateway; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.client.core.FaultMessageResolver; import org.springframework.ws.client.core.WebServiceTemplate; @@ -63,11 +66,9 @@ public static class MarshallingWsOutboundGatewayNoTemplateSpec extends BaseWsOutboundGatewaySpec { - @SuppressWarnings("NullAway.Init") - protected Marshaller gatewayMarshaller; // NOSONAR + protected Marshaller gatewayMarshaller = new Jaxb2Marshaller(); // NOSONAR - @SuppressWarnings("NullAway.Init") - protected Unmarshaller gatewayUnmarshaller; // NOSONAR + protected @Nullable Unmarshaller gatewayUnmarshaller; // NOSONAR /** * Configure the marshaller to use. diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java index 9dd4539de82..0fbc9b3b280 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/SimpleWsOutboundGatewaySpec.java @@ -41,8 +41,7 @@ public class SimpleWsOutboundGatewaySpec extends BaseWsOutboundGatewaySpec { - @Nullable - protected SourceExtractor sourceExtractor; // NOSONAR + protected @Nullable SourceExtractor sourceExtractor; // NOSONAR protected SimpleWsOutboundGatewaySpec(WebServiceTemplate template) { this.template = template; From 99bea966e189a96d479d5c07360fe1be48c39303 Mon Sep 17 00:00:00 2001 From: Jooyoung Pyoung Date: Fri, 13 Jun 2025 00:36:05 +0900 Subject: [PATCH 3/4] Apply Nullability to remaining `spring-integration-ws` module packages * 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`) Signed-off-by: Jooyoung Pyoung --- .../ws/AbstractWebServiceOutboundGateway.java | 33 +++++++++++-------- .../ws/DefaultSoapHeaderMapper.java | 7 +++- .../MarshallingWebServiceInboundGateway.java | 4 ++- .../MarshallingWebServiceOutboundGateway.java | 18 ++++++---- .../ws/SimpleWebServiceOutboundGateway.java | 27 +++++++++------ .../integration/ws/config/package-info.java | 1 + .../ws/dsl/BaseWsOutboundGatewaySpec.java | 6 ++-- .../dsl/MarshallingWsOutboundGatewaySpec.java | 2 +- .../integration/ws/package-info.java | 1 + 9 files changed, 63 insertions(+), 36 deletions(-) diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java index f053ad65bd7..411ba6edaab 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -25,6 +25,8 @@ import javax.xml.transform.TransformerException; +import org.jspecify.annotations.Nullable; + import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.expression.ExpressionEvalMap; @@ -68,13 +70,14 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro private final String uri; - private final DestinationProvider destinationProvider; + private final @Nullable DestinationProvider destinationProvider; private final Map uriVariableExpressions = new HashMap<>(); + @SuppressWarnings("NullAway.Init") private StandardEvaluationContext evaluationContext; - private WebServiceMessageCallback requestCallback; + private @Nullable WebServiceMessageCallback requestCallback; private WebServiceTemplate webServiceTemplate; @@ -84,15 +87,17 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro private boolean webServiceTemplateExplicitlySet; - public AbstractWebServiceOutboundGateway(final String uri, WebServiceMessageFactory messageFactory) { + @SuppressWarnings("NullAway") + public AbstractWebServiceOutboundGateway(final String uri, @Nullable WebServiceMessageFactory messageFactory) { Assert.hasText(uri, "URI must not be empty"); this.webServiceTemplate = new WebServiceTemplate(messageFactory); this.destinationProvider = null; this.uri = uri; } + @SuppressWarnings("NullAway") public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider, - WebServiceMessageFactory messageFactory) { + @Nullable WebServiceMessageFactory messageFactory) { Assert.notNull(destinationProvider, "DestinationProvider must not be null"); this.webServiceTemplate = new WebServiceTemplate(messageFactory); @@ -165,7 +170,7 @@ public void setMessageFactory(WebServiceMessageFactory messageFactory) { this.webServiceTemplate.setMessageFactory(messageFactory); } - public void setRequestCallback(WebServiceMessageCallback requestCallback) { + public void setRequestCallback(@Nullable WebServiceMessageCallback requestCallback) { this.requestCallback = requestCallback; } @@ -199,7 +204,7 @@ protected WebServiceTemplate getWebServiceTemplate() { } @Override - public final Object handleRequestMessage(Message requestMessage) { + public final @Nullable Object handleRequestMessage(Message requestMessage) { URI uriWithVariables = prepareUri(requestMessage); if (uriWithVariables == null) { throw new MessageDeliveryException(requestMessage, "Failed to determine URI for " + @@ -215,7 +220,7 @@ public final Object handleRequestMessage(Message requestMessage) { return null; } - private URI prepareUri(Message requestMessage) { + private @Nullable URI prepareUri(Message requestMessage) { if (this.destinationProvider != null) { return this.destinationProvider.getDestination(); } @@ -229,17 +234,17 @@ private URI prepareUri(Message requestMessage) { return this.uriFactory.expand(this.uri, uriVariables); } - protected abstract Object doHandle(String theUri, Message requestMessage, - WebServiceMessageCallback reqCallback); + protected abstract @Nullable Object doHandle(String theUri, Message requestMessage, + @Nullable WebServiceMessageCallback reqCallback); protected abstract class RequestMessageCallback extends TransformerObjectSupport implements WebServiceMessageCallback { - private final WebServiceMessageCallback reqCallback; + private final @Nullable WebServiceMessageCallback reqCallback; private final Message requestMessage; - public RequestMessageCallback(WebServiceMessageCallback requestCallback, Message requestMessage) { + public RequestMessageCallback(@Nullable WebServiceMessageCallback requestCallback, Message requestMessage) { this.reqCallback = requestCallback; this.requestMessage = requestMessage; } @@ -266,7 +271,7 @@ protected abstract class ResponseMessageExtractor extends TransformerObjectSuppo implements WebServiceMessageExtractor { @Override - public Object extractData(WebServiceMessage message) + public @Nullable Object extractData(WebServiceMessage message) throws IOException, TransformerException { Object resultObject = this.doExtractData(message); @@ -284,7 +289,7 @@ public Object extractData(WebServiceMessage message) } } - public abstract Object doExtractData(WebServiceMessage message) throws IOException, TransformerException; + public abstract @Nullable Object doExtractData(WebServiceMessage message) throws IOException, TransformerException; } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java index 39544c5c948..fa87c8f3590 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 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. @@ -53,6 +53,7 @@ * @author Mauro Molinari * @author Artem Bilan * @author Gary Russell + * @author Jooyoung Pyoung * * @since 2.0 */ @@ -126,6 +127,10 @@ else if (!StringUtils.hasText(target.getSoapAction())) { @Override protected void populateUserDefinedHeader(String headerName, Object headerValue, SoapMessage target) { SoapHeader soapHeader = target.getSoapHeader(); + if (soapHeader == null) { + return; + } + if (headerValue instanceof String) { QName qname = QNameUtils.parseQNameString(headerName); soapHeader.addAttribute(qname, (String) headerValue); diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java index d7f6ea52cb8..8800715270d 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -35,8 +35,10 @@ */ public class MarshallingWebServiceInboundGateway extends AbstractWebServiceInboundGateway { + @SuppressWarnings("NullAway.Init") private Marshaller marshaller; + @SuppressWarnings("NullAway.Init") private Unmarshaller unmarshaller; /** diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java index 770b38fd117..7db958a1874 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java @@ -46,11 +46,12 @@ public class MarshallingWebServiceOutboundGateway extends AbstractWebServiceOutb @SuppressWarnings("this-escape") public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller, - Unmarshaller unmarshaller, WebServiceMessageFactory messageFactory) { + @Nullable Unmarshaller unmarshaller, @Nullable WebServiceMessageFactory messageFactory) { super(destinationProvider, messageFactory); configureMarshallers(marshaller, unmarshaller); } + @SuppressWarnings("NullAway") public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller, Unmarshaller unmarshaller) { this(destinationProvider, marshaller, unmarshaller, null); @@ -61,17 +62,19 @@ public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvi this(destinationProvider, marshaller, null, messageFactory); } + @SuppressWarnings("NullAway") public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller) { this(destinationProvider, marshaller, (WebServiceMessageFactory) null); } @SuppressWarnings("this-escape") - public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, Unmarshaller unmarshaller, - WebServiceMessageFactory messageFactory) { + public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, @Nullable Unmarshaller unmarshaller, + @Nullable WebServiceMessageFactory messageFactory) { super(uri, messageFactory); configureMarshallers(marshaller, unmarshaller); } + @SuppressWarnings("NullAway") public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, Unmarshaller unmarshaller) { this(uri, marshaller, unmarshaller, null); } @@ -81,6 +84,7 @@ public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, this(uri, marshaller, null, messageFactory); } + @SuppressWarnings("NullAway") public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller) { this(uri, marshaller, (WebServiceMessageFactory) null); } @@ -91,7 +95,7 @@ public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller) { * @param webServiceTemplate the WebServiceTemplate * @since 5.0 */ - @SuppressWarnings("this-escape") + @SuppressWarnings({"this-escape", "NullAway"}) public MarshallingWebServiceOutboundGateway(String uri, WebServiceTemplate webServiceTemplate) { super(uri, null); doSetWebServiceTemplate(webServiceTemplate); @@ -103,7 +107,7 @@ public MarshallingWebServiceOutboundGateway(String uri, WebServiceTemplate webSe * @param webServiceTemplate the WebServiceTemplate * @since 5.0 */ - @SuppressWarnings("this-escape") + @SuppressWarnings({"this-escape", "NullAway"}) public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, WebServiceTemplate webServiceTemplate) { super(destinationProvider, null); @@ -140,7 +144,7 @@ public String getComponentType() { } @Override - protected Object doHandle(String uri, Message requestMessage, WebServiceMessageCallback requestCallback) { + protected @Nullable Object doHandle(String uri, Message requestMessage, @Nullable WebServiceMessageCallback requestCallback) { return getWebServiceTemplate() .marshalSendAndReceive(uri, requestMessage.getPayload(), new PassThroughRequestMessageCallback(requestCallback, requestMessage)); @@ -148,7 +152,7 @@ protected Object doHandle(String uri, Message requestMessage, WebServiceMessa private final class PassThroughRequestMessageCallback extends RequestMessageCallback { - PassThroughRequestMessageCallback(WebServiceMessageCallback requestCallback, Message requestMessage) { + PassThroughRequestMessageCallback(@Nullable WebServiceMessageCallback requestCallback, Message requestMessage) { super(requestCallback, requestMessage); } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java index a9521683f2b..7a8d68690dc 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java @@ -49,6 +49,7 @@ * @author Oleg Zhurakousky * @author Artem Bilan * @author Gary Russell + * @author Jooyoung Pyoung */ public class SimpleWebServiceOutboundGateway extends AbstractWebServiceOutboundGateway { @@ -68,7 +69,7 @@ public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider, public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider, @Nullable SourceExtractor sourceExtractor, - WebServiceMessageFactory messageFactory) { + @Nullable WebServiceMessageFactory messageFactory) { super(destinationProvider, messageFactory); this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); @@ -83,7 +84,7 @@ public SimpleWebServiceOutboundGateway(String uri, SourceExtractor sourceExtr } public SimpleWebServiceOutboundGateway(String uri, @Nullable SourceExtractor sourceExtractor, - WebServiceMessageFactory messageFactory) { + @Nullable WebServiceMessageFactory messageFactory) { super(uri, messageFactory); this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); @@ -107,8 +108,8 @@ public String getComponentType() { } @Override - protected Object doHandle(String uri, final Message requestMessage, - final WebServiceMessageCallback requestCallback) { + protected @Nullable Object doHandle(String uri, final Message requestMessage, + final @Nullable WebServiceMessageCallback requestCallback) { Object requestPayload = requestMessage.getPayload(); Result responseResultInstance = null; @@ -126,7 +127,7 @@ else if (requestPayload instanceof Document) { private final class SimpleRequestMessageCallback extends RequestMessageCallback { - SimpleRequestMessageCallback(WebServiceMessageCallback requestCallback, Message requestMessage) { + SimpleRequestMessageCallback(@Nullable WebServiceMessageCallback requestCallback, Message requestMessage) { super(requestCallback, requestMessage); } @@ -134,13 +135,16 @@ private final class SimpleRequestMessageCallback extends RequestMessageCallback public void doWithMessageInternal(WebServiceMessage message, Object payload) throws IOException, TransformerException { Source source = this.extractSource(payload); + if (source == null) { + source = new DOMSource(); + } transform(source, message.getPayloadResult()); if (message instanceof MimeMessage && payload instanceof MimeMessage) { copyAttachments((MimeMessage) payload, (MimeMessage) message); } } - private Source extractSource(Object requestPayload) throws IOException, TransformerException { + private @Nullable Source extractSource(Object requestPayload) throws IOException, TransformerException { Source source = null; if (requestPayload instanceof Source) { @@ -181,14 +185,14 @@ private void copyAttachments(MimeMessage source, MimeMessage target) { private final class SimpleResponseMessageExtractor extends ResponseMessageExtractor { - private final Result result; + private final @Nullable Result result; - SimpleResponseMessageExtractor(Result result) { + SimpleResponseMessageExtractor(@Nullable Result result) { this.result = result; } @Override - public Object doExtractData(WebServiceMessage message) throws TransformerException { + public @Nullable Object doExtractData(WebServiceMessage message) throws TransformerException { if (!SimpleWebServiceOutboundGateway.this.extractPayload) { return message; } @@ -220,10 +224,13 @@ private static class DefaultSourceExtractor extends TransformerObjectSupport imp } @Override - public DOMSource extractData(Source source) throws TransformerException { + public @Nullable DOMSource extractData(@Nullable Source source) throws TransformerException { if (source instanceof DOMSource) { return (DOMSource) source; } + else if (source == null) { + return new DOMSource(); + } DOMResult result = new DOMResult(); this.transform(source, result); return new DOMSource(result.getNode()); diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/package-info.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/package-info.java index d89a5edf0a6..d0742368f6c 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/package-info.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/package-info.java @@ -1,4 +1,5 @@ /** * Contains parser classes for the Web Services namespace support. */ +@org.jspecify.annotations.NullMarked package org.springframework.integration.ws.config; diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java index 224aa1bfe51..46723d347fe 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java @@ -53,11 +53,13 @@ public abstract class BaseWsOutboundGatewaySpec< private final Map uriVariableExpressions = new HashMap<>(); - protected @Nullable WebServiceTemplate template; // NOSONAR + @SuppressWarnings("NullAway.Init") + protected WebServiceTemplate template; // NOSONAR protected @Nullable DestinationProvider destinationProvider; // NOSONAR - protected @Nullable String uri; // NOSONAR + @SuppressWarnings("NullAway.Init") + protected String uri; // NOSONAR protected @Nullable WebServiceMessageFactory webServiceMessageFactory; // NOSONAR diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java index 2d4c26521e3..544efab8918 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/MarshallingWsOutboundGatewaySpec.java @@ -66,7 +66,7 @@ public static class MarshallingWsOutboundGatewayNoTemplateSpec extends BaseWsOutboundGatewaySpec { - protected Marshaller gatewayMarshaller = new Jaxb2Marshaller(); // NOSONAR + protected Marshaller gatewayMarshaller = new Jaxb2Marshaller(); protected @Nullable Unmarshaller gatewayUnmarshaller; // NOSONAR diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/package-info.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/package-info.java index 3c8070775dd..d78697a966e 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/package-info.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/package-info.java @@ -2,4 +2,5 @@ * Provides several inbound and outbound Web Service components. Also contains * support classes (e.g. Header Mapper) */ +@org.jspecify.annotations.NullMarked package org.springframework.integration.ws; From b36f1424040b3917c786d669af4d82f2143b62c0 Mon Sep 17 00:00:00 2001 From: Jooyoung Pyoung Date: Fri, 20 Jun 2025 20:20:38 +0900 Subject: [PATCH 4/4] Fix WebServiceMessageFactory null handling * Add constructor overloads without messageFactory parameter * Clean up unnecessary @SuppressWarnings("NullAway") annotations Signed-off-by: Jooyoung Pyoung --- .../ws/AbstractWebServiceOutboundGateway.java | 42 ++++++++++++++++--- .../MarshallingWebServiceOutboundGateway.java | 14 +++---- .../ws/SimpleWebServiceOutboundGateway.java | 20 ++++----- .../ws/dsl/BaseWsOutboundGatewaySpec.java | 3 +- .../WebServiceOutboundGatewayParserTests.java | 4 +- 5 files changed, 57 insertions(+), 26 deletions(-) diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java index 411ba6edaab..a9e9df4b464 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java @@ -61,6 +61,7 @@ * @author Artem Bilan * @author Christian Tzolov * @author Ngoc Nhan + * @author Jooyoung Pyoung */ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyProducingMessageHandler { @@ -87,17 +88,48 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro private boolean webServiceTemplateExplicitlySet; - @SuppressWarnings("NullAway") - public AbstractWebServiceOutboundGateway(final String uri, @Nullable WebServiceMessageFactory messageFactory) { + public AbstractWebServiceOutboundGateway(final String uri) { + Assert.hasText(uri, "URI must not be empty"); + this.webServiceTemplate = new WebServiceTemplate(); + this.destinationProvider = null; + this.uri = uri; + } + + public AbstractWebServiceOutboundGateway(final String uri, WebServiceMessageFactory messageFactory) { Assert.hasText(uri, "URI must not be empty"); this.webServiceTemplate = new WebServiceTemplate(messageFactory); this.destinationProvider = null; this.uri = uri; } - @SuppressWarnings("NullAway") + public AbstractWebServiceOutboundGateway(final String uri, WebServiceTemplate webServiceTemplate) { + Assert.hasText(uri, "URI must not be empty"); + doSetWebServiceTemplate(webServiceTemplate); + this.destinationProvider = null; + this.uri = uri; + } + + protected AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider, + WebServiceTemplate webServiceTemplate) { + + Assert.notNull(destinationProvider, "DestinationProvider must not be null"); + doSetWebServiceTemplate(webServiceTemplate); + this.destinationProvider = destinationProvider; + this.uri = ""; + } + + public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider) { + Assert.notNull(destinationProvider, "DestinationProvider must not be null"); + this.webServiceTemplate = new WebServiceTemplate(); + this.destinationProvider = destinationProvider; + // we always call WebServiceTemplate methods with an explicit URI argument, + // but in case the WebServiceTemplate is accessed directly we'll set this: + this.webServiceTemplate.setDestinationProvider(destinationProvider); + this.uri = ""; + } + public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider, - @Nullable WebServiceMessageFactory messageFactory) { + WebServiceMessageFactory messageFactory) { Assert.notNull(destinationProvider, "DestinationProvider must not be null"); this.webServiceTemplate = new WebServiceTemplate(messageFactory); @@ -105,7 +137,7 @@ public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider // we always call WebServiceTemplate methods with an explicit URI argument, // but in case the WebServiceTemplate is accessed directly we'll set this: this.webServiceTemplate.setDestinationProvider(destinationProvider); - this.uri = null; + this.uri = ""; } public void setHeaderMapper(SoapHeaderMapper headerMapper) { diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java index 7db958a1874..c6b044ceb79 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java @@ -46,7 +46,7 @@ public class MarshallingWebServiceOutboundGateway extends AbstractWebServiceOutb @SuppressWarnings("this-escape") public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller, - @Nullable Unmarshaller unmarshaller, @Nullable WebServiceMessageFactory messageFactory) { + @Nullable Unmarshaller unmarshaller, WebServiceMessageFactory messageFactory) { super(destinationProvider, messageFactory); configureMarshallers(marshaller, unmarshaller); } @@ -69,7 +69,7 @@ public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvi @SuppressWarnings("this-escape") public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, @Nullable Unmarshaller unmarshaller, - @Nullable WebServiceMessageFactory messageFactory) { + WebServiceMessageFactory messageFactory) { super(uri, messageFactory); configureMarshallers(marshaller, unmarshaller); } @@ -95,10 +95,9 @@ public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller) { * @param webServiceTemplate the WebServiceTemplate * @since 5.0 */ - @SuppressWarnings({"this-escape", "NullAway"}) + @SuppressWarnings("this-escape") public MarshallingWebServiceOutboundGateway(String uri, WebServiceTemplate webServiceTemplate) { - super(uri, null); - doSetWebServiceTemplate(webServiceTemplate); + super(uri, webServiceTemplate); } /** @@ -107,11 +106,10 @@ public MarshallingWebServiceOutboundGateway(String uri, WebServiceTemplate webSe * @param webServiceTemplate the WebServiceTemplate * @since 5.0 */ - @SuppressWarnings({"this-escape", "NullAway"}) + @SuppressWarnings("this-escape") public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, WebServiceTemplate webServiceTemplate) { - super(destinationProvider, null); - doSetWebServiceTemplate(webServiceTemplate); + super(destinationProvider, webServiceTemplate); } /** diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java index 7a8d68690dc..b38f4de4e0d 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java @@ -58,33 +58,33 @@ public class SimpleWebServiceOutboundGateway extends AbstractWebServiceOutboundG private boolean extractPayload = true; public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider) { - this(destinationProvider, null, null); + this(destinationProvider, null); } public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider, - SourceExtractor sourceExtractor) { - - this(destinationProvider, sourceExtractor, null); + @Nullable SourceExtractor sourceExtractor) { + super(destinationProvider); + this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); } public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider, @Nullable SourceExtractor sourceExtractor, - @Nullable WebServiceMessageFactory messageFactory) { - + WebServiceMessageFactory messageFactory) { super(destinationProvider, messageFactory); this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); } public SimpleWebServiceOutboundGateway(String uri) { - this(uri, null, null); + this(uri, null); } - public SimpleWebServiceOutboundGateway(String uri, SourceExtractor sourceExtractor) { - this(uri, sourceExtractor, null); + public SimpleWebServiceOutboundGateway(String uri, @Nullable SourceExtractor sourceExtractor) { + super(uri); + this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); } public SimpleWebServiceOutboundGateway(String uri, @Nullable SourceExtractor sourceExtractor, - @Nullable WebServiceMessageFactory messageFactory) { + WebServiceMessageFactory messageFactory) { super(uri, messageFactory); this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java index 46723d347fe..3f480368224 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/dsl/BaseWsOutboundGatewaySpec.java @@ -61,7 +61,8 @@ public abstract class BaseWsOutboundGatewaySpec< @SuppressWarnings("NullAway.Init") protected String uri; // NOSONAR - protected @Nullable WebServiceMessageFactory webServiceMessageFactory; // NOSONAR + @SuppressWarnings("NullAway.Init") + protected WebServiceMessageFactory webServiceMessageFactory; // NOSONAR private @Nullable SoapHeaderMapper headerMapper; diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java index f9559e64105..f9af4903a4f 100644 --- a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -382,7 +382,7 @@ public void simpleGatewayWithDestinationProvider() { DirectFieldAccessor accessor = new DirectFieldAccessor(gateway); assertThat(accessor.getPropertyValue("destinationProvider")).as("Wrong DestinationProvider") .isEqualTo(stubProvider); - assertThat(accessor.getPropertyValue("uri")).isNull(); + assertThat(accessor.getPropertyValue("uri")).isEqualTo(""); Object destinationProviderObject = new DirectFieldAccessor( accessor.getPropertyValue("webServiceTemplate")).getPropertyValue("destinationProvider"); assertThat(destinationProviderObject).as("Wrong DestinationProvider").isEqualTo(stubProvider);