From e327eb7b45c527faaea7840eff124c65fabe68be Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 6 Aug 2025 13:36:11 +0200 Subject: [PATCH] Add nullability annotations to module/spring-boot-undertow See gh-46587 --- .../undertow/AccessLogHttpHandlerFactory.java | 19 +++-- .../CompressionHttpHandlerFactory.java | 3 +- .../ConfigurableUndertowWebServerFactory.java | 17 +++-- .../boot/undertow/HttpHandlerFactory.java | 3 +- .../boot/undertow/SslBuilderCustomizer.java | 7 +- .../boot/undertow/UndertowWebServer.java | 75 +++++++++++++------ .../undertow/UndertowWebServerFactory.java | 41 +++++----- .../UndertowServerProperties.java | 37 ++++----- .../server/UndertowAccessLogCustomizer.java | 6 +- .../actuate/web/server/package-info.java | 3 + .../undertow/autoconfigure/package-info.java | 3 + .../autoconfigure/reactive/package-info.java | 3 + .../autoconfigure/servlet/package-info.java | 3 + .../boot/undertow/package-info.java | 3 + .../boot/undertow/reactive/package-info.java | 3 + .../servlet/CompositeResourceManager.java | 3 +- .../DeploymentManagerHttpHandlerFactory.java | 3 +- .../servlet/FileSessionPersistence.java | 4 +- .../undertow/servlet/JarResourceManager.java | 3 +- .../servlet/UndertowServletWebServer.java | 9 ++- .../UndertowServletWebServerFactory.java | 30 +++++--- .../boot/undertow/servlet/package-info.java | 3 + 22 files changed, 176 insertions(+), 105 deletions(-) diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/AccessLogHttpHandlerFactory.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/AccessLogHttpHandlerFactory.java index 7663bb21641..2456b3e95bc 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/AccessLogHttpHandlerFactory.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/AccessLogHttpHandlerFactory.java @@ -25,13 +25,12 @@ import io.undertow.Undertow; import io.undertow.server.HttpHandler; import io.undertow.server.handlers.accesslog.AccessLogHandler; import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver; +import org.jspecify.annotations.Nullable; import org.xnio.OptionMap; import org.xnio.Options; import org.xnio.Xnio; import org.xnio.XnioWorker; -import org.springframework.util.Assert; - /** * An {@link HttpHandlerFactory} for an {@link AccessLogHandler}. * @@ -41,15 +40,16 @@ class AccessLogHttpHandlerFactory implements HttpHandlerFactory { private final File directory; - private final String pattern; + private final @Nullable String pattern; - private final String prefix; + private final @Nullable String prefix; - private final String suffix; + private final @Nullable String suffix; private final boolean rotate; - AccessLogHttpHandlerFactory(File directory, String pattern, String prefix, String suffix, boolean rotate) { + AccessLogHttpHandlerFactory(File directory, @Nullable String pattern, @Nullable String prefix, + @Nullable String suffix, boolean rotate) { this.directory = directory; this.pattern = pattern; this.prefix = prefix; @@ -58,7 +58,7 @@ class AccessLogHttpHandlerFactory implements HttpHandlerFactory { } @Override - public HttpHandler getHandler(HttpHandler next) { + public HttpHandler getHandler(@Nullable HttpHandler next) { try { createAccessLogDirectoryIfNecessary(); XnioWorker worker = createWorker(); @@ -74,7 +74,6 @@ class AccessLogHttpHandlerFactory implements HttpHandlerFactory { } private void createAccessLogDirectoryIfNecessary() { - Assert.state(this.directory != null, "Access log directory is not set"); if (!this.directory.isDirectory() && !this.directory.mkdirs()) { throw new IllegalStateException("Failed to create access log directory '" + this.directory + "'"); } @@ -94,8 +93,8 @@ class AccessLogHttpHandlerFactory implements HttpHandlerFactory { private final XnioWorker worker; - ClosableAccessLogHandler(HttpHandler next, XnioWorker worker, DefaultAccessLogReceiver accessLogReceiver, - String formatString) { + ClosableAccessLogHandler(@Nullable HttpHandler next, XnioWorker worker, + DefaultAccessLogReceiver accessLogReceiver, String formatString) { super(next, accessLogReceiver, formatString, Undertow.class.getClassLoader()); this.worker = worker; this.accessLogReceiver = accessLogReceiver; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/CompressionHttpHandlerFactory.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/CompressionHttpHandlerFactory.java index 21ac8256dd1..4feb1c30c72 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/CompressionHttpHandlerFactory.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/CompressionHttpHandlerFactory.java @@ -29,6 +29,7 @@ import io.undertow.server.handlers.encoding.EncodingHandler; import io.undertow.server.handlers.encoding.GzipEncodingProvider; import io.undertow.util.Headers; import io.undertow.util.HttpString; +import org.jspecify.annotations.Nullable; import org.springframework.boot.web.server.Compression; import org.springframework.http.HttpHeaders; @@ -51,7 +52,7 @@ class CompressionHttpHandlerFactory implements HttpHandlerFactory { } @Override - public HttpHandler getHandler(HttpHandler next) { + public @Nullable HttpHandler getHandler(@Nullable HttpHandler next) { if (!this.compression.getEnabled()) { return next; } diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/ConfigurableUndertowWebServerFactory.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/ConfigurableUndertowWebServerFactory.java index e30a9af2450..0b83b98d0ea 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/ConfigurableUndertowWebServerFactory.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/ConfigurableUndertowWebServerFactory.java @@ -20,6 +20,7 @@ import java.io.File; import java.util.Collection; import io.undertow.Undertow.Builder; +import org.jspecify.annotations.Nullable; import org.springframework.boot.web.server.ConfigurableWebServerFactory; @@ -50,49 +51,49 @@ public interface ConfigurableUndertowWebServerFactory extends ConfigurableWebSer * Set the buffer size. * @param bufferSize buffer size */ - void setBufferSize(Integer bufferSize); + void setBufferSize(@Nullable Integer bufferSize); /** * Set the number of IO Threads. * @param ioThreads number of IO Threads */ - void setIoThreads(Integer ioThreads); + void setIoThreads(@Nullable Integer ioThreads); /** * Set the number of Worker Threads. * @param workerThreads number of Worker Threads */ - void setWorkerThreads(Integer workerThreads); + void setWorkerThreads(@Nullable Integer workerThreads); /** * Set whether direct buffers should be used. * @param useDirectBuffers whether direct buffers should be used */ - void setUseDirectBuffers(Boolean useDirectBuffers); + void setUseDirectBuffers(@Nullable Boolean useDirectBuffers); /** * Set the access log directory. * @param accessLogDirectory access log directory */ - void setAccessLogDirectory(File accessLogDirectory); + void setAccessLogDirectory(@Nullable File accessLogDirectory); /** * Set the access log pattern. * @param accessLogPattern access log pattern */ - void setAccessLogPattern(String accessLogPattern); + void setAccessLogPattern(@Nullable String accessLogPattern); /** * Set the access log prefix. * @param accessLogPrefix log prefix */ - void setAccessLogPrefix(String accessLogPrefix); + void setAccessLogPrefix(@Nullable String accessLogPrefix); /** * Set the access log suffix. * @param accessLogSuffix access log suffix */ - void setAccessLogSuffix(String accessLogSuffix); + void setAccessLogSuffix(@Nullable String accessLogSuffix); /** * Set whether access logs are enabled. diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/HttpHandlerFactory.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/HttpHandlerFactory.java index 3ff96a373c9..225757b7829 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/HttpHandlerFactory.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/HttpHandlerFactory.java @@ -20,6 +20,7 @@ import java.io.Closeable; import io.undertow.server.HttpHandler; import io.undertow.server.handlers.GracefulShutdownHandler; +import org.jspecify.annotations.Nullable; import org.springframework.boot.undertow.servlet.UndertowServletWebServer; @@ -43,6 +44,6 @@ public interface HttpHandlerFactory { * @param next the next handler in the chain * @return the new HTTP handler instance */ - HttpHandler getHandler(HttpHandler next); + @Nullable HttpHandler getHandler(@Nullable HttpHandler next); } diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/SslBuilderCustomizer.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/SslBuilderCustomizer.java index d52d936ba97..8f0f402d247 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/SslBuilderCustomizer.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/SslBuilderCustomizer.java @@ -24,6 +24,7 @@ import javax.net.ssl.SSLContext; import io.undertow.Undertow; import io.undertow.protocols.ssl.SNIContextMatcher; import io.undertow.protocols.ssl.SNISSLContext; +import org.jspecify.annotations.Nullable; import org.xnio.Options; import org.xnio.Sequence; import org.xnio.SslClientAuthMode; @@ -44,15 +45,15 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer { private final int port; - private final InetAddress address; + private final @Nullable InetAddress address; - private final ClientAuth clientAuth; + private final @Nullable ClientAuth clientAuth; private final SslBundle sslBundle; private final Map serverNameSslBundles; - SslBuilderCustomizer(int port, InetAddress address, ClientAuth clientAuth, SslBundle sslBundle, + SslBuilderCustomizer(int port, @Nullable InetAddress address, @Nullable ClientAuth clientAuth, SslBundle sslBundle, Map serverNameSslBundles) { this.port = port; this.address = address; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServer.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServer.java index 84edb62ccdd..58f6c6634d8 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServer.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServer.java @@ -32,6 +32,7 @@ import io.undertow.server.HttpServerExchange; import io.undertow.server.handlers.GracefulShutdownHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.xnio.channels.BoundChannel; import org.springframework.aot.hint.RuntimeHints; @@ -61,7 +62,7 @@ public class UndertowWebServer implements WebServer { private static final Log logger = LogFactory.getLog(UndertowWebServer.class); - private final AtomicReference gracefulShutdownCallback = new AtomicReference<>(); + private final AtomicReference<@Nullable GracefulShutdownCallback> gracefulShutdownCallback = new AtomicReference<>(); private final Object monitor = new Object(); @@ -71,13 +72,13 @@ public class UndertowWebServer implements WebServer { private final boolean autoStart; - private Undertow undertow; + private @Nullable Undertow undertow; private volatile boolean started = false; - private volatile GracefulShutdownHandler gracefulShutdown; + private volatile @Nullable GracefulShutdownHandler gracefulShutdown; - private volatile List closeables; + private volatile @Nullable List closeables; /** * Create a new {@link UndertowWebServer} instance. @@ -142,6 +143,7 @@ public class UndertowWebServer implements WebServer { try { if (this.undertow != null) { this.undertow.stop(); + Assert.state(this.closeables != null, "'closeables' must not be null"); this.closeables.forEach(this::closeSilently); } } @@ -167,11 +169,12 @@ public class UndertowWebServer implements WebServer { return this.builder.build(); } - protected HttpHandler createHttpHandler() { + protected @Nullable HttpHandler createHttpHandler() { HttpHandler handler = null; for (HttpHandlerFactory factory : this.httpHandlerFactories) { handler = factory.getHandler(handler); if (handler instanceof Closeable closeable) { + Assert.state(this.closeables != null, "'closeables' must not be null"); this.closeables.add(closeable); } if (handler instanceof GracefulShutdownHandler shutdownHandler) { @@ -220,11 +223,14 @@ public class UndertowWebServer implements WebServer { @SuppressWarnings("unchecked") private List extractChannels() { Field channelsField = ReflectionUtils.findField(Undertow.class, "channels"); + Assert.state(channelsField != null, "'channelsField' must not be null"); ReflectionUtils.makeAccessible(channelsField); - return (List) ReflectionUtils.getField(channelsField, this.undertow); + List channels = (List) ReflectionUtils.getField(channelsField, this.undertow); + Assert.state(channels != null, "'channels' must not be null"); + return channels; } - private UndertowWebServer.Port getPortFromChannel(BoundChannel channel) { + private UndertowWebServer.@Nullable Port getPortFromChannel(BoundChannel channel) { SocketAddress socketAddress = channel.getLocalAddress(); if (socketAddress instanceof InetSocketAddress inetSocketAddress) { Field sslField = ReflectionUtils.findField(channel.getClass(), "ssl"); @@ -253,20 +259,37 @@ public class UndertowWebServer implements WebServer { @SuppressWarnings("unchecked") private List extractListeners() { Field listenersField = ReflectionUtils.findField(Undertow.class, "listeners"); + Assert.state(listenersField != null, "'listenersField' must not be null"); ReflectionUtils.makeAccessible(listenersField); - return (List) ReflectionUtils.getField(listenersField, this.undertow); + List listeners = (List) ReflectionUtils.getField(listenersField, this.undertow); + Assert.state(listeners != null, "'listeners' must not be null"); + return listeners; } private UndertowWebServer.Port getPortFromListener(Object listener) { Field typeField = ReflectionUtils.findField(listener.getClass(), "type"); - ReflectionUtils.makeAccessible(typeField); - String protocol = ReflectionUtils.getField(typeField, listener).toString(); + Assert.state(typeField != null, "'typeField' must not be null"); + String protocol = getProtocol(listener, typeField); Field portField = ReflectionUtils.findField(listener.getClass(), "port"); - ReflectionUtils.makeAccessible(portField); - int port = (Integer) ReflectionUtils.getField(portField, listener); + Assert.state(portField != null, "'portField' must not be null"); + int port = getPort(listener, portField); return new UndertowWebServer.Port(port, protocol); } + private static Integer getPort(Object listener, Field portField) { + ReflectionUtils.makeAccessible(portField); + Integer value = (Integer) ReflectionUtils.getField(portField, listener); + Assert.state(value != null, "'value' must not be null"); + return value; + } + + private String getProtocol(Object listener, Field typeField) { + ReflectionUtils.makeAccessible(typeField); + Object value = ReflectionUtils.getField(typeField, listener); + Assert.state(value != null, "'value' must not be null"); + return value.toString(); + } + @Override public void stop() throws WebServerException { synchronized (this.monitor) { @@ -278,9 +301,12 @@ public class UndertowWebServer implements WebServer { notifyGracefulCallback(false); } try { - this.undertow.stop(); - for (Closeable closeable : this.closeables) { - closeable.close(); + if (this.undertow != null) { + this.undertow.stop(); + Assert.state(this.closeables != null, "'closeables' must not be null"); + for (Closeable closeable : this.closeables) { + closeable.close(); + } } } catch (Exception ex) { @@ -304,7 +330,7 @@ public class UndertowWebServer implements WebServer { * @return the Undertow server or {@code null} if the server hasn't been started yet * @since 4.0.0 */ - public Undertow getUndertow() { + public @Nullable Undertow getUndertow() { return this.undertow; } @@ -396,27 +422,30 @@ public class UndertowWebServer implements WebServer { */ private static final class CloseableHttpHandlerFactory implements HttpHandlerFactory { - private final Closeable closeable; + private final @Nullable Closeable closeable; - private CloseableHttpHandlerFactory(Closeable closeable) { + private CloseableHttpHandlerFactory(@Nullable Closeable closeable) { this.closeable = closeable; } @Override - public HttpHandler getHandler(HttpHandler next) { - if (this.closeable == null) { + public @Nullable HttpHandler getHandler(@Nullable HttpHandler next) { + Closeable closeable = this.closeable; + if (closeable == null) { return next; } return new CloseableHttpHandler() { @Override public void handleRequest(HttpServerExchange exchange) throws Exception { - next.handleRequest(exchange); + if (next != null) { + next.handleRequest(exchange); + } } @Override public void close() throws IOException { - CloseableHttpHandlerFactory.this.closeable.close(); + closeable.close(); } }; @@ -438,7 +467,7 @@ public class UndertowWebServer implements WebServer { static class UndertowWebServerRuntimeHints implements RuntimeHintsRegistrar { @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { hints.reflection() .registerTypeIfPresent(classLoader, "io.undertow.Undertow", (hint) -> hint.withField("listeners").withField("channels")); diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServerFactory.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServerFactory.java index e3f6ed95796..d1dba06ec39 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServerFactory.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/UndertowWebServerFactory.java @@ -31,6 +31,7 @@ import io.undertow.Handlers; import io.undertow.Undertow; import io.undertow.Undertow.Builder; import io.undertow.UndertowOptions; +import org.jspecify.annotations.Nullable; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory; @@ -52,21 +53,21 @@ public abstract class UndertowWebServerFactory extends AbstractConfigurableWebSe private Set builderCustomizers = new LinkedHashSet<>(); - private Integer bufferSize; + private @Nullable Integer bufferSize; - private Integer ioThreads; + private @Nullable Integer ioThreads; - private Integer workerThreads; + private @Nullable Integer workerThreads; - private Boolean directBuffers; + private @Nullable Boolean directBuffers; - private File accessLogDirectory; + private @Nullable File accessLogDirectory; - private String accessLogPattern; + private @Nullable String accessLogPattern; - private String accessLogPrefix; + private @Nullable String accessLogPrefix; - private String accessLogSuffix; + private @Nullable String accessLogSuffix; private boolean accessLogEnabled; @@ -98,46 +99,46 @@ public abstract class UndertowWebServerFactory extends AbstractConfigurableWebSe } @Override - public void setBufferSize(Integer bufferSize) { + public void setBufferSize(@Nullable Integer bufferSize) { this.bufferSize = bufferSize; } @Override - public void setIoThreads(Integer ioThreads) { + public void setIoThreads(@Nullable Integer ioThreads) { this.ioThreads = ioThreads; } @Override - public void setWorkerThreads(Integer workerThreads) { + public void setWorkerThreads(@Nullable Integer workerThreads) { this.workerThreads = workerThreads; } @Override - public void setUseDirectBuffers(Boolean directBuffers) { + public void setUseDirectBuffers(@Nullable Boolean directBuffers) { this.directBuffers = directBuffers; } @Override - public void setAccessLogDirectory(File accessLogDirectory) { + public void setAccessLogDirectory(@Nullable File accessLogDirectory) { this.accessLogDirectory = accessLogDirectory; } @Override - public void setAccessLogPattern(String accessLogPattern) { + public void setAccessLogPattern(@Nullable String accessLogPattern) { this.accessLogPattern = accessLogPattern; } @Override - public void setAccessLogPrefix(String accessLogPrefix) { + public void setAccessLogPrefix(@Nullable String accessLogPrefix) { this.accessLogPrefix = accessLogPrefix; } - public String getAccessLogPrefix() { + public @Nullable String getAccessLogPrefix() { return this.accessLogPrefix; } @Override - public void setAccessLogSuffix(String accessLogSuffix) { + public void setAccessLogSuffix(@Nullable String accessLogSuffix) { this.accessLogSuffix = accessLogSuffix; } @@ -207,14 +208,16 @@ public abstract class UndertowWebServerFactory extends AbstractConfigurableWebSe this.useForwardHeaders, webServerFactory.getServerHeader(), webServerFactory.getShutdown(), initialHttpHandlerFactories); if (isAccessLogEnabled()) { + Assert.state(this.accessLogDirectory != null, "Access log directory is not set"); factories.add(new AccessLogHttpHandlerFactory(this.accessLogDirectory, this.accessLogPattern, this.accessLogPrefix, this.accessLogSuffix, this.accessLogRotate)); } return factories; } - static List createHttpHandlerFactories(Compression compression, boolean useForwardHeaders, - String serverHeader, Shutdown shutdown, HttpHandlerFactory... initialHttpHandlerFactories) { + static List createHttpHandlerFactories(@Nullable Compression compression, + boolean useForwardHeaders, @Nullable String serverHeader, Shutdown shutdown, + HttpHandlerFactory... initialHttpHandlerFactories) { List factories = new ArrayList<>(Arrays.asList(initialHttpHandlerFactories)); if (compression != null && compression.getEnabled()) { factories.add(new CompressionHttpHandlerFactory(compression)); diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/UndertowServerProperties.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/UndertowServerProperties.java index 9aecceb6234..84d467c5635 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/UndertowServerProperties.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/UndertowServerProperties.java @@ -24,6 +24,7 @@ import java.util.LinkedHashMap; import java.util.Map; import io.undertow.UndertowOptions; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.util.unit.DataSize; @@ -69,13 +70,13 @@ public class UndertowServerProperties { * Size of each buffer. The default is derived from the maximum amount of memory that * is available to the JVM. */ - private DataSize bufferSize; + private @Nullable DataSize bufferSize; /** * Whether to allocate buffers outside the Java heap. The default is derived from the * maximum amount of memory that is available to the JVM. */ - private Boolean directBuffers; + private @Nullable Boolean directBuffers; /** * Whether servlet filters should be initialized on startup. @@ -106,7 +107,7 @@ public class UndertowServerProperties { * enable this if you have a legacy application that requires it. When set, * server.undertow.allow-encoded-slash has no effect. */ - private Boolean decodeSlash; + private @Nullable Boolean decodeSlash; /** * Whether the URL should be decoded. When disabled, percent-encoded characters in the @@ -129,7 +130,7 @@ public class UndertowServerProperties { * Amount of time a connection can sit idle without processing a request, before it is * closed by the server. */ - private Duration noRequestTimeout; + private @Nullable Duration noRequestTimeout; /** * Whether to preserve the path of a request when it is forwarded. @@ -153,19 +154,19 @@ public class UndertowServerProperties { this.maxHttpPostSize = maxHttpPostSize; } - public DataSize getBufferSize() { + public @Nullable DataSize getBufferSize() { return this.bufferSize; } - public void setBufferSize(DataSize bufferSize) { + public void setBufferSize(@Nullable DataSize bufferSize) { this.bufferSize = bufferSize; } - public Boolean getDirectBuffers() { + public @Nullable Boolean getDirectBuffers() { return this.directBuffers; } - public void setDirectBuffers(Boolean directBuffers) { + public void setDirectBuffers(@Nullable Boolean directBuffers) { this.directBuffers = directBuffers; } @@ -201,11 +202,11 @@ public class UndertowServerProperties { this.maxCookies = maxCookies; } - public Boolean getDecodeSlash() { + public @Nullable Boolean getDecodeSlash() { return this.decodeSlash; } - public void setDecodeSlash(Boolean decodeSlash) { + public void setDecodeSlash(@Nullable Boolean decodeSlash) { this.decodeSlash = decodeSlash; } @@ -233,11 +234,11 @@ public class UndertowServerProperties { this.alwaysSetKeepAlive = alwaysSetKeepAlive; } - public Duration getNoRequestTimeout() { + public @Nullable Duration getNoRequestTimeout() { return this.noRequestTimeout; } - public void setNoRequestTimeout(Duration noRequestTimeout) { + public void setNoRequestTimeout(@Nullable Duration noRequestTimeout) { this.noRequestTimeout = noRequestTimeout; } @@ -355,26 +356,26 @@ public class UndertowServerProperties { * Number of I/O threads to create for the worker. The default is derived from the * number of available processors. */ - private Integer io; + private @Nullable Integer io; /** * Number of worker threads. The default is 8 times the number of I/O threads. */ - private Integer worker; + private @Nullable Integer worker; - public Integer getIo() { + public @Nullable Integer getIo() { return this.io; } - public void setIo(Integer io) { + public void setIo(@Nullable Integer io) { this.io = io; } - public Integer getWorker() { + public @Nullable Integer getWorker() { return this.worker; } - public void setWorker(Integer worker) { + public void setWorker(@Nullable Integer worker) { this.worker = worker; } diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/UndertowAccessLogCustomizer.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/UndertowAccessLogCustomizer.java index d6c3b4340af..f709977dc78 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/UndertowAccessLogCustomizer.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/UndertowAccessLogCustomizer.java @@ -18,6 +18,8 @@ package org.springframework.boot.undertow.autoconfigure.actuate.web.server; import java.util.function.Function; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.actuate.autoconfigure.web.server.AccessLogCustomizer; import org.springframework.boot.undertow.ConfigurableUndertowWebServerFactory; @@ -29,10 +31,10 @@ import org.springframework.boot.undertow.ConfigurableUndertowWebServerFactory; */ class UndertowAccessLogCustomizer extends AccessLogCustomizer { - private final Function accessLogPrefixExtractor; + private final Function accessLogPrefixExtractor; UndertowAccessLogCustomizer(UndertowManagementServerProperties properties, - Function accessLogPrefixExtractor) { + Function accessLogPrefixExtractor) { super(properties.getAccesslog().getPrefix()); this.accessLogPrefixExtractor = accessLogPrefixExtractor; } diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/package-info.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/package-info.java index bb5d1467d8a..63f89d3bc9e 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/package-info.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/actuate/web/server/package-info.java @@ -17,4 +17,7 @@ /** * Actuator Undertow actuator web concerns. */ +@NullMarked package org.springframework.boot.undertow.autoconfigure.actuate.web.server; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/package-info.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/package-info.java index cc63f9a246e..987f5fff791 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/package-info.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/package-info.java @@ -18,4 +18,7 @@ * Classes related to the auto-configuration of a servlet or reactive web server using * Undertow. */ +@NullMarked package org.springframework.boot.undertow.autoconfigure; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/reactive/package-info.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/reactive/package-info.java index 9378b2cfed1..14d12b37961 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/reactive/package-info.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/reactive/package-info.java @@ -17,4 +17,7 @@ /** * Classes related to the auto-configuration of a reactive web server using Undertow. */ +@NullMarked package org.springframework.boot.undertow.autoconfigure.reactive; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/servlet/package-info.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/servlet/package-info.java index 9539702d0d1..e7c5fd2982f 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/servlet/package-info.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/autoconfigure/servlet/package-info.java @@ -17,4 +17,7 @@ /** * Classes related to the auto-configuration of a servlet web server using Undertow. */ +@NullMarked package org.springframework.boot.undertow.autoconfigure.servlet; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/package-info.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/package-info.java index cff413d9484..42a96fed990 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/package-info.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/package-info.java @@ -20,4 +20,7 @@ * @see org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory * @see org.springframework.boot.undertow.reactive.UndertowReactiveWebServerFactory */ +@NullMarked package org.springframework.boot.undertow; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/reactive/package-info.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/reactive/package-info.java index 33979942ccd..cdf63cc51a1 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/reactive/package-info.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/reactive/package-info.java @@ -17,4 +17,7 @@ /** * Reactive web server implementation backed by Undertow. */ +@NullMarked package org.springframework.boot.undertow.reactive; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/CompositeResourceManager.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/CompositeResourceManager.java index c99681bcc1a..b774bd9abdb 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/CompositeResourceManager.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/CompositeResourceManager.java @@ -24,6 +24,7 @@ import io.undertow.UndertowMessages; import io.undertow.server.handlers.resource.Resource; import io.undertow.server.handlers.resource.ResourceChangeListener; import io.undertow.server.handlers.resource.ResourceManager; +import org.jspecify.annotations.Nullable; /** * A {@link ResourceManager} that delegates to multiple {@code ResourceManager} instances. @@ -46,7 +47,7 @@ class CompositeResourceManager implements ResourceManager { } @Override - public Resource getResource(String path) throws IOException { + public @Nullable Resource getResource(String path) throws IOException { for (ResourceManager resourceManager : this.resourceManagers) { Resource resource = resourceManager.getResource(path); if (resource != null) { diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/DeploymentManagerHttpHandlerFactory.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/DeploymentManagerHttpHandlerFactory.java index 2175b70214f..16116a63e8d 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/DeploymentManagerHttpHandlerFactory.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/DeploymentManagerHttpHandlerFactory.java @@ -23,6 +23,7 @@ import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.servlet.api.DeploymentManager; import jakarta.servlet.ServletException; +import org.jspecify.annotations.Nullable; import org.springframework.boot.undertow.HttpHandlerFactory; import org.springframework.util.Assert; @@ -42,7 +43,7 @@ class DeploymentManagerHttpHandlerFactory implements HttpHandlerFactory { } @Override - public HttpHandler getHandler(HttpHandler next) { + public @Nullable HttpHandler getHandler(@Nullable HttpHandler next) { Assert.state(next == null, "DeploymentManagerHttpHandlerFactory must be first"); return new DeploymentManagerHandler(this.deploymentManager); } diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/FileSessionPersistence.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/FileSessionPersistence.java index c30d38dfca7..d3e4c7f74b9 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/FileSessionPersistence.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/FileSessionPersistence.java @@ -29,6 +29,7 @@ import java.util.Map; import io.undertow.servlet.UndertowServletLogger; import io.undertow.servlet.api.SessionPersistenceManager; +import org.jspecify.annotations.Nullable; import org.springframework.core.ConfigurableObjectInputStream; @@ -70,7 +71,8 @@ class FileSessionPersistence implements SessionPersistenceManager { } @Override - public Map loadSessionAttributes(String deploymentName, final ClassLoader classLoader) { + public @Nullable Map loadSessionAttributes(String deploymentName, + final ClassLoader classLoader) { try { File file = getSessionFile(deploymentName); if (file.exists()) { diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/JarResourceManager.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/JarResourceManager.java index 5fb6c7a6135..e1e5cfa64a9 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/JarResourceManager.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/JarResourceManager.java @@ -26,6 +26,7 @@ import io.undertow.server.handlers.resource.Resource; import io.undertow.server.handlers.resource.ResourceChangeListener; import io.undertow.server.handlers.resource.ResourceManager; import io.undertow.server.handlers.resource.URLResource; +import org.jspecify.annotations.Nullable; import org.springframework.util.StringUtils; @@ -49,7 +50,7 @@ class JarResourceManager implements ResourceManager { } @Override - public Resource getResource(String path) throws IOException { + public @Nullable Resource getResource(String path) throws IOException { URL url = new URL("jar:" + this.jarPath + "!" + (path.startsWith("/") ? path : "/" + path)); URLResource resource = new URLResource(url, path); if (StringUtils.hasText(path) && !"/".equals(path) && resource.getContentLength() < 0) { diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServer.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServer.java index 94f511bafcd..2ee17fb7e2b 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServer.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServer.java @@ -20,6 +20,7 @@ import io.undertow.Handlers; import io.undertow.Undertow.Builder; import io.undertow.server.HttpHandler; import io.undertow.servlet.api.DeploymentManager; +import org.jspecify.annotations.Nullable; import org.springframework.boot.undertow.HttpHandlerFactory; import org.springframework.boot.undertow.UndertowWebServer; @@ -43,7 +44,7 @@ public class UndertowServletWebServer extends UndertowWebServer { private final String contextPath; - private final DeploymentManager manager; + private final @Nullable DeploymentManager manager; /** * Create a new {@link UndertowServletWebServer} instance. @@ -60,7 +61,7 @@ public class UndertowServletWebServer extends UndertowWebServer { this.manager = findManager(httpHandlerFactories); } - private DeploymentManager findManager(Iterable httpHandlerFactories) { + private @Nullable DeploymentManager findManager(Iterable httpHandlerFactories) { for (HttpHandlerFactory httpHandlerFactory : httpHandlerFactories) { if (httpHandlerFactory instanceof DeploymentManagerHttpHandlerFactory deploymentManagerFactory) { return deploymentManagerFactory.getDeploymentManager(); @@ -70,7 +71,7 @@ public class UndertowServletWebServer extends UndertowWebServer { } @Override - protected HttpHandler createHttpHandler() { + protected @Nullable HttpHandler createHttpHandler() { HttpHandler handler = super.createHttpHandler(); if (StringUtils.hasLength(this.contextPath)) { handler = Handlers.path().addPrefixPath(this.contextPath, handler); @@ -88,7 +89,7 @@ public class UndertowServletWebServer extends UndertowWebServer { return message.toString(); } - public DeploymentManager getDeploymentManager() { + public @Nullable DeploymentManager getDeploymentManager() { return this.manager; } diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerFactory.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerFactory.java index 0698d38e2dd..a69f309daec 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerFactory.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerFactory.java @@ -61,6 +61,7 @@ import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.undertow.HttpHandlerFactory; @@ -110,6 +111,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory private Set deploymentInfoCustomizers = new LinkedHashSet<>(); + @SuppressWarnings("NullAway.Init") private ResourceLoader resourceLoader; private boolean eagerFilterInit = true; @@ -273,10 +275,12 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory @SuppressWarnings("unchecked") private Class loadWebListenerClass(String className) throws ClassNotFoundException { - return (Class) getServletClassLoader().loadClass(className); + ClassLoader classLoader = getServletClassLoader(); + Assert.state(classLoader != null, "'classLoader' must not be null"); + return (Class) classLoader.loadClass(className); } - private boolean isZeroOrLess(Duration timeoutDuration) { + private boolean isZeroOrLess(@Nullable Duration timeoutDuration) { return timeoutDuration == null || timeoutDuration.isZero() || timeoutDuration.isNegative(); } @@ -293,7 +297,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory new ImmediateInstanceFactory<>(initializer), NO_CLASSES)); } - private ClassLoader getServletClassLoader() { + private @Nullable ClassLoader getServletClassLoader() { if (this.resourceLoader != null) { return this.resourceLoader.getClassLoader(); } @@ -337,7 +341,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory return new CompositeResourceManager(managers.toArray(new ResourceManager[0])); } - private File getCanonicalDocumentRoot(File docBase) { + private File getCanonicalDocumentRoot(@Nullable File docBase) { try { File root = (docBase != null) ? docBase : createTempDir("undertow-docbase"); return root.getCanonicalFile(); @@ -402,7 +406,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory port >= 0); } - private HttpHandlerFactory getCookieHandlerFactory(Deployment deployment) { + private @Nullable HttpHandlerFactory getCookieHandlerFactory(Deployment deployment) { SameSite sessionSameSite = getSettings().getSession().getCookie().getSameSite(); List suppliers = new ArrayList<>(); if (sessionSameSite != null) { @@ -458,7 +462,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory } @Override - public Resource getResource(String path) { + public @Nullable Resource getResource(String path) { for (URL url : this.metaInfResourceJarUrls) { URLResource resource = getMetaInfResource(url, path); if (resource != null) { @@ -482,7 +486,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory } - private URLResource getMetaInfResource(URL resourceJar, String path) { + private @Nullable URLResource getMetaInfResource(URL resourceJar, String path) { try { String urlPath = URLEncoder.encode(ENCODED_SLASH.matcher(path).replaceAll("/"), StandardCharsets.UTF_8); URL resourceUrl = new URL(resourceJar + "META-INF/resources" + urlPath); @@ -511,7 +515,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory } @Override - public Resource getResource(String path) throws IOException { + public @Nullable Resource getResource(String path) throws IOException { if (path.startsWith("/org/springframework/boot")) { return null; } @@ -546,11 +550,11 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory */ private static class SuppliedSameSiteCookieHandler implements HttpHandler { - private final HttpHandler next; + private final @Nullable HttpHandler next; private final List suppliers; - SuppliedSameSiteCookieHandler(HttpHandler next, List suppliers) { + SuppliedSameSiteCookieHandler(@Nullable HttpHandler next, List suppliers) { this.next = next; this.suppliers = suppliers; } @@ -558,7 +562,9 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory @Override public void handleRequest(HttpServerExchange exchange) throws Exception { exchange.addResponseCommitListener(this::beforeCommit); - this.next.handleRequest(exchange); + if (this.next != null) { + this.next.handleRequest(exchange); + } } private void beforeCommit(HttpServerExchange exchange) { @@ -587,7 +593,7 @@ public class UndertowServletWebServerFactory extends UndertowWebServerFactory return result; } - private SameSite getSameSite(jakarta.servlet.http.Cookie cookie) { + private @Nullable SameSite getSameSite(jakarta.servlet.http.Cookie cookie) { for (CookieSameSiteSupplier supplier : this.suppliers) { SameSite sameSite = supplier.getSameSite(cookie); if (sameSite != null) { diff --git a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/package-info.java b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/package-info.java index e00b16f8075..54f54b26062 100644 --- a/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/package-info.java +++ b/module/spring-boot-undertow/src/main/java/org/springframework/boot/undertow/servlet/package-info.java @@ -17,4 +17,7 @@ /** * Servlet web server implementation backed by Undertow. */ +@NullMarked package org.springframework.boot.undertow.servlet; + +import org.jspecify.annotations.NullMarked;