Review nullability of spring-ws-test

See gh-1562
This commit is contained in:
Stéphane Nicoll
2025-05-16 15:10:26 +02:00
parent 69673bc962
commit 06eaa1dec9
23 changed files with 94 additions and 91 deletions

View File

@@ -40,8 +40,7 @@ class ErrorResponseCreator implements ResponseCreator {
@Override
public WebServiceMessage createResponse(URI uri, WebServiceMessage request, WebServiceMessageFactory factory)
throws IOException {
// Do nothing
return null;
throw new UnsupportedOperationException();
}
String getErrorMessage() {

View File

@@ -20,6 +20,9 @@ import java.io.IOException;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
@@ -38,11 +41,11 @@ class MockSenderConnection implements WebServiceConnection, ResponseActions {
private final List<RequestMatcher> requestMatchers = new LinkedList<>();
private URI uri;
private @Nullable URI uri;
private WebServiceMessage request;
private @Nullable WebServiceMessage request;
private ResponseCreator responseCreator;
private @Nullable ResponseCreator responseCreator;
void addRequestMatcher(RequestMatcher requestMatcher) {
Assert.notNull(requestMatcher, "'requestMatcher' must not be null");
@@ -74,7 +77,7 @@ class MockSenderConnection implements WebServiceConnection, ResponseActions {
public void send(WebServiceMessage message) throws IOException {
if (!this.requestMatchers.isEmpty()) {
for (RequestMatcher requestMatcher : this.requestMatchers) {
requestMatcher.match(this.uri, message);
requestMatcher.match(Objects.requireNonNull(this.uri), message);
}
}
else {
@@ -84,9 +87,10 @@ class MockSenderConnection implements WebServiceConnection, ResponseActions {
}
@Override
public WebServiceMessage receive(WebServiceMessageFactory messageFactory) throws IOException {
public @Nullable WebServiceMessage receive(WebServiceMessageFactory messageFactory) throws IOException {
if (this.responseCreator != null) {
return this.responseCreator.createResponse(this.uri, this.request, messageFactory);
return this.responseCreator.createResponse(Objects.requireNonNull(this.uri),
Objects.requireNonNull(this.request), messageFactory);
}
else {
return null;
@@ -94,7 +98,7 @@ class MockSenderConnection implements WebServiceConnection, ResponseActions {
}
@Override
public URI getUri() {
public @Nullable URI getUri() {
return this.uri;
}
@@ -104,7 +108,7 @@ class MockSenderConnection implements WebServiceConnection, ResponseActions {
}
@Override
public String getErrorMessage() throws IOException {
public @Nullable String getErrorMessage() throws IOException {
if (this.responseCreator instanceof ErrorResponseCreator) {
return ((ErrorResponseCreator) this.responseCreator).getErrorMessage();
}

View File

@@ -22,6 +22,8 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.ws.transport.WebServiceMessageSender;
@@ -38,7 +40,7 @@ public class MockWebServiceMessageSender implements WebServiceMessageSender {
private final List<MockSenderConnection> expectedConnections = new LinkedList<>();
private Iterator<MockSenderConnection> connectionIterator;
private @Nullable Iterator<MockSenderConnection> connectionIterator;
@Override
public MockSenderConnection createConnection(URI uri) throws IOException {

View File

@@ -18,6 +18,8 @@ package org.springframework.ws.test.client;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.ws.test.support.matcher.XPathExpectationsHelper;
/**
@@ -31,7 +33,7 @@ class XPathExpectationsHelperAdapter implements RequestXPathExpectations {
private final XPathExpectationsHelper helper;
XPathExpectationsHelperAdapter(String expression, Map<String, String> namespaces) {
XPathExpectationsHelperAdapter(String expression, @Nullable Map<String, String> namespaces) {
this.helper = new XPathExpectationsHelper(expression, namespaces);
}

View File

@@ -19,4 +19,7 @@
* the {@link org.springframework.ws.test.client.MockWebServiceServer}, and various
* related test interfaces.
*/
@NullMarked
package org.springframework.ws.test.client;
import org.jspecify.annotations.NullMarked;

View File

@@ -29,7 +29,6 @@ import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.soap.server.SoapMessageDispatcher;
import org.springframework.ws.test.support.AssertionErrors;
import org.springframework.ws.test.support.MockStrategiesHelper;
import org.springframework.ws.transport.WebServiceMessageReceiver;
@@ -185,8 +184,7 @@ public final class MockWebServiceClient {
}
catch (Exception ex) {
logger.error("Could not send request", ex);
AssertionErrors.fail(ex.getMessage());
return null;
throw new AssertionError(ex.getMessage());
}
}
@@ -206,8 +204,7 @@ public final class MockWebServiceClient {
WebServiceMessage request = this.messageContext.getRequest();
WebServiceMessage response = this.messageContext.getResponse();
if (response == null) {
AssertionErrors.fail("No response received");
return null;
throw new AssertionError("No response received");
}
try {
responseMatcher.match(request, response);
@@ -215,8 +212,7 @@ public final class MockWebServiceClient {
}
catch (IOException ex) {
logger.error("Could not match request", ex);
AssertionErrors.fail(ex.getMessage());
return null;
throw new AssertionError(ex.getMessage());
}
}

View File

@@ -23,6 +23,8 @@ import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import org.jspecify.annotations.Nullable;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.ws.FaultAwareWebServiceMessage;
@@ -160,7 +162,7 @@ public abstract class ResponseMatchers {
* {@code null} the fault string or reason text will not be verified
* @see org.springframework.ws.soap.SoapBody#addMustUnderstandFault(String, Locale)
*/
public static ResponseMatcher mustUnderstandFault(String faultStringOrReason) {
public static ResponseMatcher mustUnderstandFault(@Nullable String faultStringOrReason) {
return new SoapFaultResponseMatcher(faultStringOrReason) {
@Override
protected QName getExpectedFaultCode(SoapVersion version) {
@@ -184,7 +186,7 @@ public abstract class ResponseMatchers {
* {@code null} the fault string or reason text will not be verified
* @see org.springframework.ws.soap.SoapBody#addClientOrSenderFault(String, Locale)
*/
public static ResponseMatcher clientOrSenderFault(String faultStringOrReason) {
public static ResponseMatcher clientOrSenderFault(@Nullable String faultStringOrReason) {
return new SoapFaultResponseMatcher(faultStringOrReason) {
@Override
protected QName getExpectedFaultCode(SoapVersion version) {
@@ -209,7 +211,7 @@ public abstract class ResponseMatchers {
* {@code null} the fault string or reason text will not be verified
* @see org.springframework.ws.soap.SoapBody#addClientOrSenderFault(String, Locale)
*/
public static ResponseMatcher serverOrReceiverFault(String faultStringOrReason) {
public static ResponseMatcher serverOrReceiverFault(@Nullable String faultStringOrReason) {
return new SoapFaultResponseMatcher(faultStringOrReason) {
@Override
protected QName getExpectedFaultCode(SoapVersion version) {
@@ -233,7 +235,7 @@ public abstract class ResponseMatchers {
* {@code null} the fault string or reason text will not be verified
* @see org.springframework.ws.soap.SoapBody#addClientOrSenderFault(String, Locale)
*/
public static ResponseMatcher versionMismatchFault(String faultStringOrReason) {
public static ResponseMatcher versionMismatchFault(@Nullable String faultStringOrReason) {
return new SoapFaultResponseMatcher(faultStringOrReason) {
@Override
protected QName getExpectedFaultCode(SoapVersion version) {

View File

@@ -20,6 +20,8 @@ import java.io.IOException;
import javax.xml.namespace.QName;
import org.jspecify.annotations.Nullable;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapFault;
@@ -35,9 +37,9 @@ import org.springframework.ws.test.support.AssertionErrors;
*/
abstract class SoapFaultResponseMatcher implements ResponseMatcher {
private final String expectedFaultStringOrReason;
private final @Nullable String expectedFaultStringOrReason;
SoapFaultResponseMatcher(String expectedFaultStringOrReason) {
SoapFaultResponseMatcher(@Nullable String expectedFaultStringOrReason) {
this.expectedFaultStringOrReason = expectedFaultStringOrReason;
}
@@ -50,10 +52,11 @@ abstract class SoapFaultResponseMatcher implements ResponseMatcher {
AssertionErrors.assertTrue("Response has no SOAP Fault", responseBody.hasFault());
SoapFault soapFault = responseBody.getFault();
QName expectedFaultCode = getExpectedFaultCode(soapResponse.getVersion());
AssertionErrors.assertEquals("Invalid SOAP Fault code", expectedFaultCode, soapFault.getFaultCode());
AssertionErrors.assertEquals("Invalid SOAP Fault code", expectedFaultCode,
(soapFault != null) ? soapFault.getFaultCode() : null);
if (this.expectedFaultStringOrReason != null) {
AssertionErrors.assertEquals("Invalid SOAP Fault string/reason", this.expectedFaultStringOrReason,
soapFault.getFaultStringOrReason());
(soapFault != null) ? soapFault.getFaultStringOrReason() : null);
}
}

View File

@@ -18,6 +18,8 @@ package org.springframework.ws.test.server;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.ws.test.support.matcher.XPathExpectationsHelper;
/**
@@ -31,7 +33,7 @@ class XPathExpectationsHelperAdapter implements ResponseXPathExpectations {
private final XPathExpectationsHelper helper;
XPathExpectationsHelperAdapter(String expression, Map<String, String> namespaces) {
XPathExpectationsHelperAdapter(String expression, @Nullable Map<String, String> namespaces) {
this.helper = new XPathExpectationsHelper(expression, namespaces);
}

View File

@@ -19,4 +19,7 @@
* the {@link org.springframework.ws.test.server.MockWebServiceClient}, and various
* related test interfaces.
*/
@NullMarked
package org.springframework.ws.test.server;
import org.jspecify.annotations.NullMarked;

View File

@@ -18,6 +18,10 @@ package org.springframework.ws.test.support;
import javax.xml.transform.Source;
import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
/**
* JUnit independent assertion class.
*
@@ -34,7 +38,8 @@ public abstract class AssertionErrors {
* Fails a test with the given message.
* @param message the message
*/
public static void fail(String message) {
@Contract("_ -> fail")
public static void fail(@Nullable String message) {
throw new AssertionError(message);
}
@@ -43,7 +48,7 @@ public abstract class AssertionErrors {
* @param message the message
* @param source the source
*/
public static void fail(String message, String sourceLabel, Source source) {
public static void fail(String message, @Nullable String sourceLabel, @Nullable Source source) {
if (source != null) {
throw new SourceAssertionError(message, sourceLabel, source);
}
@@ -68,7 +73,8 @@ public abstract class AssertionErrors {
* @param message the message
* @param condition the condition to test for
*/
public static void assertTrue(String message, boolean condition, String sourceLabel, Source source) {
public static void assertTrue(String message, boolean condition, @Nullable String sourceLabel,
@Nullable Source source) {
if (!condition) {
fail(message, sourceLabel, source);
}
@@ -81,7 +87,7 @@ public abstract class AssertionErrors {
* @param expected the expected value
* @param actual the actual value
*/
public static void assertEquals(String message, Object expected, Object actual) {
public static void assertEquals(String message, @Nullable Object expected, @Nullable Object actual) {
assertEquals(message, expected, actual, null, null);
}
@@ -93,7 +99,8 @@ public abstract class AssertionErrors {
* @param actual the actual value
* @param source the source
*/
public static void assertEquals(String message, Object expected, Object actual, String sourceLabel, Source source) {
public static void assertEquals(String message, @Nullable Object expected, @Nullable Object actual,
@Nullable String sourceLabel, @Nullable Source source) {
if (expected == null && actual == null) {
return;
}

View File

@@ -20,6 +20,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanCreationException;
@@ -65,7 +66,7 @@ public class MockStrategiesHelper {
* @return the bean, or {@code null} if no bean of the given type can be found
* @throws BeanInitializationException if there is more than 1 beans of the given type
*/
public <T> T getStrategy(Class<T> type) {
public <T> @Nullable T getStrategy(Class<T> type) {
Assert.notNull(type, "'type' must not be null");
Map<String, T> map = this.applicationContext.getBeansOfType(type);
if (map.isEmpty()) {

View File

@@ -22,6 +22,8 @@ import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.jspecify.annotations.Nullable;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.TransformerHelper;
@@ -36,9 +38,9 @@ import org.springframework.xml.transform.TransformerHelper;
@SuppressWarnings("serial")
public class SourceAssertionError extends AssertionError {
private final String sourceLabel;
private final @Nullable String sourceLabel;
private final Source source;
private final @Nullable Source source;
private final TransformerHelper transformerHelper = new TransformerHelper();
@@ -46,7 +48,7 @@ public class SourceAssertionError extends AssertionError {
* Creates a new instance of the {@code SourceAssertionError} class with the given
* parameters.
*/
public SourceAssertionError(String detailMessage, String sourceLabel, Source source) {
public SourceAssertionError(String detailMessage, @Nullable String sourceLabel, @Nullable Source source) {
super(detailMessage);
this.sourceLabel = sourceLabel;
this.source = source;
@@ -56,7 +58,7 @@ public class SourceAssertionError extends AssertionError {
* Returns the source context of this error.
* @return the source
*/
public Source getSource() {
public @Nullable Source getSource() {
return this.source;
}
@@ -76,7 +78,7 @@ public class SourceAssertionError extends AssertionError {
return builder.toString();
}
private String getSourceString() {
private @Nullable String getSourceString() {
if (this.source != null) {
try {
StringResult result = new StringResult();

View File

@@ -19,4 +19,7 @@
* {@link org.springframework.ws.test.support.creator.WebServiceMessageCreator
* WebServiceMessageCreator} interface, and implementations.
*/
@NullMarked
package org.springframework.ws.test.support.creator;
import org.jspecify.annotations.NullMarked;

View File

@@ -18,6 +18,7 @@ package org.springframework.ws.test.support.matcher;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import org.xml.sax.SAXParseException;
@@ -57,7 +58,7 @@ public class SchemaValidatingMatcher implements WebServiceMessageMatcher {
@Override
public void match(WebServiceMessage message) throws IOException, AssertionError {
SAXParseException[] exceptions = this.xmlValidator.validate(message.getPayloadSource());
SAXParseException[] exceptions = this.xmlValidator.validate(Objects.requireNonNull(message.getPayloadSource()));
if (!ObjectUtils.isEmpty(exceptions)) {
AssertionErrors.fail("XML is not valid: " + Arrays.toString(exceptions), "Payload",
message.getPayloadSource());

View File

@@ -53,13 +53,15 @@ public class SoapHeaderMatcher extends AbstractSoapMessageMatcher {
AssertionErrors.assertTrue("SOAP message [" + soapMessage + "] does not contain SOAP header",
soapHeader != null, "Envelope", soapMessage.getEnvelope().getSource());
Iterator<SoapHeaderElement> soapHeaderElementIterator = soapHeader.examineAllHeaderElements();
boolean found = false;
while (soapHeaderElementIterator.hasNext()) {
SoapHeaderElement soapHeaderElement = soapHeaderElementIterator.next();
if (this.soapHeaderName.equals(soapHeaderElement.getName())) {
found = true;
break;
if (soapHeader != null) {
Iterator<SoapHeaderElement> soapHeaderElementIterator = soapHeader.examineAllHeaderElements();
while (soapHeaderElementIterator.hasNext()) {
SoapHeaderElement soapHeaderElement = soapHeaderElementIterator.next();
if (this.soapHeaderName.equals(soapHeaderElement.getName())) {
found = true;
break;
}
}
}
AssertionErrors.assertTrue("SOAP header [" + this.soapHeaderName + "] not found", found, "Envelope",

View File

@@ -18,10 +18,12 @@ package org.springframework.ws.test.support.matcher;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import org.jspecify.annotations.Nullable;
import org.w3c.dom.Node;
import org.springframework.util.Assert;
@@ -61,7 +63,7 @@ public class XPathExpectationsHelper {
* @param expression the XPath expression
* @param namespaces the namespaces, can be empty or {@code null}
*/
public XPathExpectationsHelper(String expression, Map<String, String> namespaces) {
public XPathExpectationsHelper(String expression, @Nullable Map<String, String> namespaces) {
Assert.hasLength(expression, "'expression' must not be empty");
this.expression = XPathExpressionFactory.createXPathExpression(expression, namespaces);
this.expressionString = expression;
@@ -139,12 +141,11 @@ public class XPathExpectationsHelper {
private Node transformToNode(WebServiceMessage request) {
DOMResult domResult = new DOMResult();
try {
this.transformerHelper.transform(request.getPayloadSource(), domResult);
this.transformerHelper.transform(Objects.requireNonNull(request.getPayloadSource()), domResult);
return domResult.getNode();
}
catch (TransformerException ex) {
AssertionErrors.fail("Could not transform request payload: " + ex.getMessage());
return null;
throw new AssertionError("Could not transform request payload: " + ex.getMessage());
}
}

View File

@@ -19,4 +19,7 @@
* {@link org.springframework.ws.test.support.matcher.WebServiceMessageMatcher
* WebServiceMessageMatcher} interface, and implementations.
*/
@NullMarked
package org.springframework.ws.test.support.matcher;
import org.jspecify.annotations.NullMarked;

View File

@@ -85,8 +85,7 @@ public class PayloadDiffMatcher extends DiffMatcher {
return (Document) result.getNode();
}
catch (TransformerException ex) {
AssertionErrors.fail("Could not transform source to DOMResult" + ex.getMessage());
return null;
throw new AssertionError("Could not transform source to DOMResult" + ex.getMessage());
}
}

View File

@@ -75,8 +75,7 @@ public class SoapEnvelopeDiffMatcher extends AbstractSoapMessageMatcher {
return (Document) result.getNode();
}
catch (TransformerException ex) {
AssertionErrors.fail("Could not transform source to DOMResult" + ex.getMessage());
return null;
throw new AssertionError("Could not transform source to DOMResult" + ex.getMessage());
}
}

View File

@@ -17,4 +17,7 @@
/**
* Matcher support for XMLUnit.
*/
@NullMarked
package org.springframework.ws.test.support.matcher.xmlunit;
import org.jspecify.annotations.NullMarked;

View File

@@ -19,4 +19,7 @@
* {@link org.springframework.ws.test.client} and
* {@link org.springframework.ws.test.server} packages.
*/
@NullMarked
package org.springframework.ws.test.support;
import org.jspecify.annotations.NullMarked;

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.test.client;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class ErrorResponseCreatorTests {
@Test
void callback() throws IOException {
String errorMessage = "Error message";
ErrorResponseCreator callback = new ErrorResponseCreator(errorMessage);
callback.createResponse(null, null, null);
assertThat(callback.getErrorMessage()).isEqualTo(errorMessage);
}
}