Skip to content

GH-10083: Migrate spring-integration-ws module to Jspecify #10098

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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<String, Expression> uriVariableExpressions = new HashMap<>();

@SuppressWarnings("NullAway.Init")
private StandardEvaluationContext evaluationContext;

private WebServiceMessageCallback requestCallback;
private @Nullable WebServiceMessageCallback requestCallback;

private WebServiceTemplate webServiceTemplate;

Expand All @@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct.
The WebServiceTemplate expects from us a WebServiceMessageFactory as a not null.
If you see somewhere it is, let's mitigate it over there!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then.. when ctors pass null for WebServiceMessageFactory, I'll replace null with SaajSoapMessageFactory as the default. Does this sound good?

Based on the WebServiceTemplate.properties, SaajSoapMessageFactory is indeed the default strategy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we allow to pass null at all?
The WebServiceTemplate does not expect that from us, so let's comeback to not-null for this ctors!

Copy link
Contributor Author

@anthologia anthologia Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I meant that I'll remove @Nullable from the constructor parameters and fix the calling sites to pass SaajSoapMessageFactory instance instead of null.

For example:

// Instead of
super(uri, null);

// I'll change to  
super(uri, new SaajSoapMessageFactory());

Am I still misunderstanding something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see:

	public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider,
			WebServiceTemplate webServiceTemplate) {
		super(destinationProvider, null);
		doSetWebServiceTemplate(webServiceTemplate);
	}

I think it is better to introduce a protected ctor in the AbstractWebServiceOutboundGateway based on the WebServiceTemplate then.

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);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 " +
Expand All @@ -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();
}
Expand All @@ -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;
}
Expand All @@ -266,7 +271,7 @@ protected abstract class ResponseMessageExtractor extends TransformerObjectSuppo
implements WebServiceMessageExtractor<Object> {

@Override
public Object extractData(WebServiceMessage message)
public @Nullable Object extractData(WebServiceMessage message)
throws IOException, TransformerException {

Object resultObject = this.doExtractData(message);
Expand All @@ -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;

}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -53,6 +53,7 @@
* @author Mauro Molinari
* @author Artem Bilan
* @author Gary Russell
* @author Jooyoung Pyoung
*
* @since 2.0
*/
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -35,8 +35,10 @@
*/
public class MarshallingWebServiceInboundGateway extends AbstractWebServiceInboundGateway {

@SuppressWarnings("NullAway.Init")
private Marshaller marshaller;

@SuppressWarnings("NullAway.Init")
private Unmarshaller unmarshaller;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not very OK with these @SuppressWarnings("NullAway"), but we can look into that separately if that is too much for this change cycle.

public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller,
Unmarshaller unmarshaller) {
this(destinationProvider, marshaller, unmarshaller, null);
Expand All @@ -60,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);
}
Expand All @@ -80,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);
}
Expand All @@ -90,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);
Expand All @@ -102,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);
Expand Down Expand Up @@ -139,15 +144,15 @@ 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));
}

private final class PassThroughRequestMessageCallback extends RequestMessageCallback {

PassThroughRequestMessageCallback(WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
PassThroughRequestMessageCallback(@Nullable WebServiceMessageCallback requestCallback, Message<?> requestMessage) {
super(requestCallback, requestMessage);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -49,6 +49,7 @@
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
* @author Jooyoung Pyoung
*/
public class SimpleWebServiceOutboundGateway extends AbstractWebServiceOutboundGateway {

Expand All @@ -68,7 +69,7 @@ public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider,

public SimpleWebServiceOutboundGateway(DestinationProvider destinationProvider,
@Nullable SourceExtractor<?> sourceExtractor,
WebServiceMessageFactory messageFactory) {
@Nullable WebServiceMessageFactory messageFactory) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to fix those places to not provide a null for the WebServiceMessageFactory.


super(destinationProvider, messageFactory);
this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor();
Expand All @@ -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();
Expand All @@ -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;
Expand All @@ -126,21 +127,24 @@ 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);
}

@Override
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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/**
* Contains parser classes for the Web Services namespace support.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.integration.ws.config;
Loading