Consistent use of @Nullable across the codebase (even for internals)

Beyond just formally declaring the current behavior, this revision actually enforces non-null behavior in selected signatures now, not tolerating null values anymore when not explicitly documented. It also changes some utility methods with historic null-in/null-out tolerance towards enforced non-null return values, making them a proper citizen in non-null assignments.

Some issues are left as to-do: in particular a thorough revision of spring-test, and a few tests with unclear failures (ignored as "TODO: NULLABLE") to be sorted out in a follow-up commit.

Issue: SPR-15540
This commit is contained in:
Juergen Hoeller
2017-06-07 14:17:48 +02:00
parent ffc3f6d87d
commit f813712f5b
1493 changed files with 10670 additions and 9172 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -37,8 +37,9 @@ public class MockHttpInputMessage implements HttpInputMessage {
private final InputStream body;
public MockHttpInputMessage(byte[] contents) {
this.body = (contents != null ? new ByteArrayInputStream(contents) : null);
public MockHttpInputMessage(byte[] content) {
Assert.notNull(content, "Byte array must not be null");
this.body = new ByteArrayInputStream(content);
}
public MockHttpInputMessage(InputStream body) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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,7 +19,6 @@ package org.springframework.mock.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
@@ -77,13 +76,7 @@ public class MockHttpOutputMessage implements HttpOutputMessage {
*/
public String getBodyAsString(Charset charset) {
byte[] bytes = getBodyAsBytes();
try {
return new String(bytes, charset.name());
}
catch (UnsupportedEncodingException ex) {
// should not occur
throw new IllegalStateException(ex);
}
return new String(bytes, charset);
}
}

View File

@@ -22,6 +22,7 @@ import java.util.List;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -61,6 +62,7 @@ public class DefaultRequestExpectation implements RequestExpectation {
return this.requestMatchers;
}
@Nullable
protected ResponseCreator getResponseCreator() {
return this.responseCreator;
}
@@ -86,7 +88,7 @@ public class DefaultRequestExpectation implements RequestExpectation {
}
@Override
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
public ClientHttpResponse createResponse(@Nullable ClientHttpRequest request) throws IOException {
ResponseCreator responseCreator = getResponseCreator();
Assert.state(responseCreator != null, "createResponse() called before ResponseCreator was set");
getRequestCount().incrementAndValidate();

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.test.web.client.response.MockRestResponseCreators;
/**
@@ -36,6 +37,6 @@ public interface ResponseCreator {
* Create a response for the given request.
* @param request the request
*/
ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException;
ClientHttpResponse createResponse(@Nullable ClientHttpRequest request) throws IOException;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -27,6 +27,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.test.web.client.ResponseCreator;
import org.springframework.util.Assert;
@@ -41,7 +42,7 @@ public class DefaultResponseCreator implements ResponseCreator {
private HttpStatus statusCode;
private byte[] content;
private byte[] content = new byte[0];
private Resource contentResource;
@@ -86,9 +87,7 @@ public class DefaultResponseCreator implements ResponseCreator {
* Set the {@code Content-Type} header.
*/
public DefaultResponseCreator contentType(MediaType mediaType) {
if (mediaType != null) {
this.headers.setContentType(mediaType);
}
this.headers.setContentType(mediaType);
return this;
}
@@ -104,17 +103,13 @@ public class DefaultResponseCreator implements ResponseCreator {
* Copy all given headers.
*/
public DefaultResponseCreator headers(HttpHeaders headers) {
for (String headerName : headers.keySet()) {
for (String headerValue : headers.get(headerName)) {
this.headers.add(headerName, headerValue);
}
}
this.headers.putAll(headers);
return this;
}
@Override
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
public ClientHttpResponse createResponse(@Nullable ClientHttpRequest request) throws IOException {
MockClientHttpResponse response;
if (this.contentResource != null) {
InputStream stream = this.contentResource.getInputStream();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.web.client.response;
import java.net.URI;
@@ -44,28 +45,31 @@ public abstract class MockRestResponseCreators {
/**
* {@code ResponseCreator} for a 200 response (OK) with String body.
* @param body the response body, a "UTF-8" string
* @param mediaType the type of the content, may be {@code null}
* @param contentType the type of the content (may be {@code null})
*/
public static DefaultResponseCreator withSuccess(String body, @Nullable MediaType mediaType) {
return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(mediaType);
public static DefaultResponseCreator withSuccess(String body, @Nullable MediaType contentType) {
DefaultResponseCreator creator = new DefaultResponseCreator(HttpStatus.OK).body(body);
return (contentType != null ? creator.contentType(contentType) : creator);
}
/**
* {@code ResponseCreator} for a 200 response (OK) with byte[] body.
* @param body the response body
* @param contentType the type of the content, may be {@code null}
* @param contentType the type of the content (may be {@code null})
*/
public static DefaultResponseCreator withSuccess(byte[] body, @Nullable MediaType contentType) {
return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(contentType);
DefaultResponseCreator creator = new DefaultResponseCreator(HttpStatus.OK).body(body);
return (contentType != null ? creator.contentType(contentType) : creator);
}
/**
* {@code ResponseCreator} for a 200 response (OK) content with {@link Resource}-based body.
* @param body the response body
* @param contentType the type of the content, may be {@code null}
* @param contentType the type of the content (may be {@code null})
*/
public static DefaultResponseCreator withSuccess(Resource body, @Nullable MediaType contentType) {
return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(contentType);
DefaultResponseCreator creator = new DefaultResponseCreator(HttpStatus.OK).body(body);
return (contentType != null ? creator.contentType(contentType) : creator);
}
/**

View File

@@ -32,7 +32,6 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.mock.web.MockServletContext;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -496,16 +495,11 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
public StaticStringValueResolver(final Map<String, String> values) {
this.helper = new PropertyPlaceholderHelper("${", "}", ":", false);
this.resolver = new PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return values.get(placeholderName);
}
};
this.resolver = values::get;
}
@Override
public String resolveStringValue(@Nullable String strVal) throws BeansException {
public String resolveStringValue(String strVal) throws BeansException {
return this.helper.replacePlaceholders(strVal, this.resolver);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -141,13 +141,12 @@ class StubWebApplicationContext implements WebApplicationContext {
this.beanFactory.addBean(name, bean);
}
public void addBeans(List<?> beans) {
if (beans == null) {
return;
}
for (Object bean : beans) {
String name = bean.getClass().getName() + "#" + ObjectUtils.getIdentityHexString(bean);
this.beanFactory.addBean(name, bean);
public void addBeans(@Nullable List<?> beans) {
if (beans != null) {
for (Object bean : beans) {
String name = bean.getClass().getName() + "#" + ObjectUtils.getIdentityHexString(bean);
this.beanFactory.addBean(name, bean);
}
}
}
@@ -167,13 +166,13 @@ class StubWebApplicationContext implements WebApplicationContext {
}
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return this.beanFactory.getBean(requiredType);
public Object getBean(String name, Object... args) throws BeansException {
return this.beanFactory.getBean(name, args);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return this.beanFactory.getBean(name, args);
public <T> T getBean(Class<T> requiredType) throws BeansException {
return this.beanFactory.getBean(requiredType);
}
@Override
@@ -202,7 +201,7 @@ class StubWebApplicationContext implements WebApplicationContext {
}
@Override
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
public boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
return this.beanFactory.isTypeMatch(name, typeToMatch);
}
@@ -405,7 +404,7 @@ class StubWebApplicationContext implements WebApplicationContext {
}
@Override
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {
throw new UnsupportedOperationException("Dependency resolution not supported");
}