diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteUrlPropertyExtractor.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteUrlPropertyExtractor.java index 4158d29a54d..e106f35813d 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteUrlPropertyExtractor.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteUrlPropertyExtractor.java @@ -21,6 +21,8 @@ import java.net.URISyntaxException; import java.util.Collections; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; @@ -59,7 +61,7 @@ class RemoteUrlPropertyExtractor implements ApplicationListener driverClassNames; - InMemoryDatabase(String urlPrefix, Set driverClassNames) { + InMemoryDatabase(@Nullable String urlPrefix, Set driverClassNames) { this(urlPrefix, driverClassNames, (dataSource) -> { try (Connection connection = dataSource.getConnection()) { try (Statement statement = connection.createStatement()) { @@ -152,7 +155,8 @@ public final class DevToolsDataSourceAutoConfiguration { }); } - InMemoryDatabase(String urlPrefix, Set driverClassNames, ShutdownHandler shutdownHandler) { + InMemoryDatabase(@Nullable String urlPrefix, Set driverClassNames, + ShutdownHandler shutdownHandler) { this.urlPrefix = urlPrefix; this.driverClassNames = driverClassNames; this.shutdownHandler = shutdownHandler; @@ -189,11 +193,13 @@ public final class DevToolsDataSourceAutoConfiguration { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("DevTools DataSource Condition"); - String[] dataSourceBeanNames = context.getBeanFactory().getBeanNamesForType(DataSource.class, true, false); + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + Assert.state(beanFactory != null, "'beanFactory' must not be null"); + String[] dataSourceBeanNames = beanFactory.getBeanNamesForType(DataSource.class, true, false); if (dataSourceBeanNames.length != 1) { return ConditionOutcome.noMatch(message.didNotFind("a single DataSource bean").atAll()); } - if (context.getBeanFactory().getBeanNamesForType(DataSourceProperties.class, true, false).length != 1) { + if (beanFactory.getBeanNamesForType(DataSourceProperties.class, true, false).length != 1) { return ConditionOutcome.noMatch(message.didNotFind("a single DataSourceProperties bean").atAll()); } BeanDefinition dataSourceDefinition = context.getRegistry().getBeanDefinition(dataSourceBeanNames[0]); diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsProperties.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsProperties.java index 6356c77b9eb..6c5b1aaadf8 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsProperties.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsProperties.java @@ -21,6 +21,8 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.util.StringUtils; @@ -76,7 +78,7 @@ public class DevToolsProperties { /** * Additional patterns that should be excluded from triggering a full restart. */ - private String additionalExclude; + private @Nullable String additionalExclude; /** * Amount of time to wait between polling for classpath changes. @@ -94,7 +96,7 @@ public class DevToolsProperties { * a simple name (without any path) of a file that appears on your classpath. If * not specified, any classpath file change triggers the restart. */ - private String triggerFile; + private @Nullable String triggerFile; /** * Additional paths to watch for changes. @@ -133,11 +135,11 @@ public class DevToolsProperties { this.exclude = exclude; } - public String getAdditionalExclude() { + public @Nullable String getAdditionalExclude() { return this.additionalExclude; } - public void setAdditionalExclude(String additionalExclude) { + public void setAdditionalExclude(@Nullable String additionalExclude) { this.additionalExclude = additionalExclude; } @@ -157,11 +159,11 @@ public class DevToolsProperties { this.quietPeriod = quietPeriod; } - public String getTriggerFile() { + public @Nullable String getTriggerFile() { return this.triggerFile; } - public void setTriggerFile(String triggerFile) { + public void setTriggerFile(@Nullable String triggerFile) { this.triggerFile = triggerFile; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java index d24587b9aec..d544d5d077a 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/DevToolsR2dbcAutoConfiguration.java @@ -18,12 +18,14 @@ package org.springframework.boot.devtools.autoconfigure; import io.r2dbc.spi.Connection; import io.r2dbc.spi.ConnectionFactory; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionMessage; @@ -40,6 +42,7 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.MethodMetadata; +import org.springframework.util.Assert; /** * {@link EnableAutoConfiguration Auto-configuration} for DevTools-specific R2DBC @@ -99,7 +102,7 @@ public final class DevToolsR2dbcAutoConfiguration { return closeConnection(connection, null); } - private Publisher closeConnection(Connection connection, Throwable ex) { + private Publisher closeConnection(Connection connection, @Nullable Throwable ex) { return connection.close(); } @@ -115,7 +118,9 @@ public final class DevToolsR2dbcAutoConfiguration { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("DevTools ConnectionFactory Condition"); - String[] beanNames = context.getBeanFactory().getBeanNamesForType(ConnectionFactory.class, true, false); + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + Assert.state(beanFactory != null, "'beanFactory' must not be null"); + String[] beanNames = beanFactory.getBeanNamesForType(ConnectionFactory.class, true, false); if (beanNames.length != 1) { return ConditionOutcome.noMatch(message.didNotFind("a single ConnectionFactory bean").atAll()); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java index 634634bfd78..e71bbba62f1 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -49,6 +50,7 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.GenericApplicationListener; import org.springframework.core.ResolvableType; import org.springframework.core.log.LogMessage; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -116,6 +118,7 @@ public final class LocalDevToolsAutoConfiguration { ClassPathFileSystemWatcher classPathFileSystemWatcher(FileSystemWatcherFactory fileSystemWatcherFactory, ClassPathRestartStrategy classPathRestartStrategy) { URL[] urls = Restarter.getInstance().getInitialUrls(); + Assert.state(urls != null, "'urls' must not be null"); ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(fileSystemWatcherFactory, classPathRestartStrategy, urls); watcher.setStopWatcherOnRestart(true); @@ -176,7 +179,7 @@ public final class LocalDevToolsAutoConfiguration { } @Override - public boolean supportsSourceType(Class sourceType) { + public boolean supportsSourceType(@Nullable Class sourceType) { return true; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OptionalLiveReloadServer.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OptionalLiveReloadServer.java index 9fabf911a2d..1931f4fab8f 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OptionalLiveReloadServer.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/OptionalLiveReloadServer.java @@ -18,6 +18,7 @@ package org.springframework.boot.devtools.autoconfigure; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.devtools.livereload.LiveReloadServer; @@ -34,13 +35,13 @@ public class OptionalLiveReloadServer implements InitializingBean { private static final Log logger = LogFactory.getLog(OptionalLiveReloadServer.class); - private LiveReloadServer server; + private @Nullable LiveReloadServer server; /** * Create a new {@link OptionalLiveReloadServer} instance. * @param server the server to manage or {@code null} */ - public OptionalLiveReloadServer(LiveReloadServer server) { + public OptionalLiveReloadServer(@Nullable LiveReloadServer server) { this.server = server; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java index 8214428c8fe..16085cae930 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsAutoConfiguration.java @@ -48,6 +48,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.log.LogMessage; import org.springframework.http.server.ServerHttpRequest; +import org.springframework.util.Assert; /** * {@link EnableAutoConfiguration Auto-configuration} for remote development support. @@ -78,7 +79,9 @@ public final class RemoteDevToolsAutoConfiguration { @ConditionalOnMissingBean AccessManager remoteDevToolsAccessManager() { RemoteDevToolsProperties remoteProperties = this.properties.getRemote(); - return new HttpHeaderAccessManager(remoteProperties.getSecretHeaderName(), remoteProperties.getSecret()); + String secret = remoteProperties.getSecret(); + Assert.state(secret != null, "'secret' must not be null"); + return new HttpHeaderAccessManager(remoteProperties.getSecretHeaderName(), secret); } @Bean diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsProperties.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsProperties.java index 7729dfa4738..5f02ff31976 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsProperties.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/RemoteDevToolsProperties.java @@ -16,6 +16,8 @@ package org.springframework.boot.devtools.autoconfigure; +import org.jspecify.annotations.Nullable; + /** * Configuration properties for remote Spring Boot applications. * @@ -39,7 +41,7 @@ public class RemoteDevToolsProperties { * A shared secret required to establish a connection (required to enable remote * support). */ - private String secret; + private @Nullable String secret; /** * HTTP header used to transfer the shared secret. @@ -58,11 +60,11 @@ public class RemoteDevToolsProperties { this.contextPath = contextPath; } - public String getSecret() { + public @Nullable String getSecret() { return this.secret; } - public void setSecret(String secret) { + public void setSecret(@Nullable String secret) { this.secret = secret; } @@ -104,26 +106,26 @@ public class RemoteDevToolsProperties { /** * The host of the proxy to use to connect to the remote application. */ - private String host; + private @Nullable String host; /** * The port of the proxy to use to connect to the remote application. */ - private Integer port; + private @Nullable Integer port; - public String getHost() { + public @Nullable String getHost() { return this.host; } - public void setHost(String host) { + public void setHost(@Nullable String host) { this.host = host; } - public Integer getPort() { + public @Nullable Integer getPort() { return this.port; } - public void setPort(Integer port) { + public void setPort(@Nullable Integer port) { this.port = port; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/package-info.java index de541bfc2eb..4420be79102 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/package-info.java @@ -17,4 +17,7 @@ /** * Auto-configuration for {@code spring-boot-devtools}. */ +@NullMarked package org.springframework.boot.devtools.autoconfigure; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathDirectories.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathDirectories.java index 96f8ff31003..e8bd1b19908 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathDirectories.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathDirectories.java @@ -25,6 +25,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.log.LogMessage; import org.springframework.util.ResourceUtils; @@ -41,7 +42,7 @@ public class ClassPathDirectories implements Iterable { private final List directories = new ArrayList<>(); - public ClassPathDirectories(URL[] urls) { + public ClassPathDirectories(URL @Nullable [] urls) { if (urls != null) { addUrls(urls); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileChangeListener.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileChangeListener.java index 244501905b1..d9daf6068d6 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileChangeListener.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileChangeListener.java @@ -18,6 +18,8 @@ package org.springframework.boot.devtools.classpath; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.devtools.filewatch.ChangedFile; import org.springframework.boot.devtools.filewatch.ChangedFiles; import org.springframework.boot.devtools.filewatch.FileChangeListener; @@ -39,7 +41,7 @@ class ClassPathFileChangeListener implements FileChangeListener { private final ClassPathRestartStrategy restartStrategy; - private final FileSystemWatcher fileSystemWatcherToStop; + private final @Nullable FileSystemWatcher fileSystemWatcherToStop; /** * Create a new {@link ClassPathFileChangeListener} instance. @@ -49,7 +51,7 @@ class ClassPathFileChangeListener implements FileChangeListener { * {@code null}) */ ClassPathFileChangeListener(ApplicationEventPublisher eventPublisher, ClassPathRestartStrategy restartStrategy, - FileSystemWatcher fileSystemWatcherToStop) { + @Nullable FileSystemWatcher fileSystemWatcherToStop) { Assert.notNull(eventPublisher, "'eventPublisher' must not be null"); Assert.notNull(restartStrategy, "'restartStrategy' must not be null"); this.eventPublisher = eventPublisher; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileSystemWatcher.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileSystemWatcher.java index 234173a932c..06101f49c2d 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileSystemWatcher.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathFileSystemWatcher.java @@ -18,6 +18,8 @@ package org.springframework.boot.devtools.classpath; import java.net.URL; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; @@ -39,8 +41,9 @@ public class ClassPathFileSystemWatcher implements InitializingBean, DisposableB private final FileSystemWatcher fileSystemWatcher; - private final ClassPathRestartStrategy restartStrategy; + private final @Nullable ClassPathRestartStrategy restartStrategy; + @SuppressWarnings("NullAway.Init") private ApplicationContext applicationContext; private boolean stopWatcherOnRestart; @@ -53,7 +56,7 @@ public class ClassPathFileSystemWatcher implements InitializingBean, DisposableB * @param urls the URLs to watch */ public ClassPathFileSystemWatcher(FileSystemWatcherFactory fileSystemWatcherFactory, - ClassPathRestartStrategy restartStrategy, URL[] urls) { + @Nullable ClassPathRestartStrategy restartStrategy, URL[] urls) { Assert.notNull(fileSystemWatcherFactory, "'fileSystemWatcherFactory' must not be null"); Assert.notNull(urls, "'urls' must not be null"); this.fileSystemWatcher = fileSystemWatcherFactory.getFileSystemWatcher(); diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/package-info.java index 28c966e2163..8b3def44e98 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/package-info.java @@ -17,4 +17,7 @@ /** * Support for classpath monitoring. */ +@NullMarked package org.springframework.boot.devtools.classpath; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java index ac09f887040..f20ff2ec5c9 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java @@ -29,6 +29,8 @@ import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.SpringApplication; import org.springframework.boot.devtools.system.DevToolsEnablementDeducer; import org.springframework.boot.env.EnvironmentPostProcessor; @@ -137,14 +139,14 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce .anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(name, fileExtension)); } - protected File getHomeDirectory() { + protected @Nullable File getHomeDirectory() { return getHomeDirectory(() -> this.environmentVariables.get("SPRING_DEVTOOLS_HOME"), () -> this.systemProperties.getProperty("spring.devtools.home"), () -> this.systemProperties.getProperty("user.home")); } @SafeVarargs - private File getHomeDirectory(Supplier... pathSuppliers) { + private @Nullable File getHomeDirectory(Supplier... pathSuppliers) { for (Supplier pathSupplier : pathSuppliers) { String path = pathSupplier.get(); if (StringUtils.hasText(path)) { diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java index 5d8774d2cda..42d37e4c623 100755 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; +import org.jspecify.annotations.Nullable; import org.springframework.boot.SpringApplication; import org.springframework.boot.devtools.logger.DevToolsLogFactory; @@ -123,7 +124,7 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro return false; } - private Class resolveClassName(String candidate, ClassLoader classLoader) { + private @Nullable Class resolveClassName(String candidate, ClassLoader classLoader) { try { return ClassUtils.resolveClassName(candidate, classLoader); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/package-info.java index f737861671d..dec034b0a76 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/package-info.java @@ -18,4 +18,7 @@ * DevTools classes relating to Spring Framework's * {@link org.springframework.core.env.Environment}. */ +@NullMarked package org.springframework.boot.devtools.env; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFile.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFile.java index 3ca8e1d9e88..aa83ab88933 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFile.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFile.java @@ -18,6 +18,8 @@ package org.springframework.boot.devtools.filewatch; import java.io.File; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -82,7 +84,7 @@ public final class ChangedFile { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFiles.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFiles.java index e305a0c9091..65f8328e3c2 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFiles.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/ChangedFiles.java @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.Set; +import org.jspecify.annotations.Nullable; + /** * A collections of files from a specific source directory that have changed. * @@ -62,7 +64,7 @@ public final class ChangedFiles implements Iterable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/DirectorySnapshot.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/DirectorySnapshot.java index 8f777a88869..2338f7a3936 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/DirectorySnapshot.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/DirectorySnapshot.java @@ -27,6 +27,8 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.devtools.filewatch.ChangedFile.Type; import org.springframework.util.Assert; @@ -73,7 +75,7 @@ class DirectorySnapshot { } } - ChangedFiles getChangedFiles(DirectorySnapshot snapshot, FileFilter triggerFilter) { + ChangedFiles getChangedFiles(DirectorySnapshot snapshot, @Nullable FileFilter triggerFilter) { Assert.notNull(snapshot, "'snapshot' must not be null"); File directory = this.directory; Assert.isTrue(snapshot.directory.equals(directory), @@ -99,7 +101,7 @@ class DirectorySnapshot { return new ChangedFiles(directory, changes); } - private boolean acceptChangedFile(FileFilter triggerFilter, FileSnapshot file) { + private boolean acceptChangedFile(@Nullable FileFilter triggerFilter, FileSnapshot file) { return (triggerFilter == null || !triggerFilter.accept(file.getFile())); } @@ -112,7 +114,7 @@ class DirectorySnapshot { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -125,7 +127,7 @@ class DirectorySnapshot { return super.equals(obj); } - boolean equals(DirectorySnapshot other, FileFilter filter) { + boolean equals(DirectorySnapshot other, @Nullable FileFilter filter) { if (this.directory.equals(other.directory)) { Set ourFiles = filter(this.files, filter); Set otherFiles = filter(other.files, filter); @@ -134,7 +136,7 @@ class DirectorySnapshot { return false; } - private Set filter(Set source, FileFilter filter) { + private Set filter(Set source, @Nullable FileFilter filter) { if (filter == null) { return source; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSnapshot.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSnapshot.java index 10ebffbbf4b..fe47b5bba56 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSnapshot.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSnapshot.java @@ -18,6 +18,8 @@ package org.springframework.boot.devtools.filewatch; import java.io.File; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -49,7 +51,7 @@ class FileSnapshot { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSystemWatcher.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSystemWatcher.java index c10e08de79b..ce969a9de62 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSystemWatcher.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSystemWatcher.java @@ -30,6 +30,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -58,11 +60,11 @@ public class FileSystemWatcher { private final AtomicInteger remainingScans = new AtomicInteger(-1); - private final Map directories = new HashMap<>(); + private final Map directories = new HashMap<>(); - private Thread watchThread; + private @Nullable Thread watchThread; - private FileFilter triggerFilter; + private @Nullable FileFilter triggerFilter; private final Object monitor = new Object(); @@ -94,7 +96,7 @@ public class FileSystemWatcher { * @since 2.4.0 */ public FileSystemWatcher(boolean daemon, Duration pollInterval, Duration quietPeriod, - SnapshotStateRepository snapshotStateRepository) { + @Nullable SnapshotStateRepository snapshotStateRepository) { Assert.notNull(pollInterval, "'pollInterval' must not be null"); Assert.notNull(quietPeriod, "'quietPeriod' must not be null"); Assert.isTrue(pollInterval.toMillis() > 0, "'pollInterval' must be positive"); @@ -151,7 +153,7 @@ public class FileSystemWatcher { * Set an optional {@link FileFilter} used to limit the files that trigger a change. * @param triggerFilter a trigger filter or null */ - public void setTriggerFilter(FileFilter triggerFilter) { + public void setTriggerFilter(@Nullable FileFilter triggerFilter) { synchronized (this.monitor) { this.triggerFilter = triggerFilter; } @@ -227,7 +229,7 @@ public class FileSystemWatcher { private final List listeners; - private final FileFilter triggerFilter; + private final @Nullable FileFilter triggerFilter; private final long pollInterval; @@ -237,9 +239,9 @@ public class FileSystemWatcher { private final SnapshotStateRepository snapshotStateRepository; - private Watcher(AtomicInteger remainingScans, List listeners, FileFilter triggerFilter, - long pollInterval, long quietPeriod, Map directories, - SnapshotStateRepository snapshotStateRepository) { + private Watcher(AtomicInteger remainingScans, List listeners, + @Nullable FileFilter triggerFilter, long pollInterval, long quietPeriod, + Map directories, SnapshotStateRepository snapshotStateRepository) { this.remainingScans = remainingScans; this.listeners = listeners; this.triggerFilter = triggerFilter; @@ -289,6 +291,7 @@ public class FileSystemWatcher { for (Map.Entry entry : previous.entrySet()) { DirectorySnapshot previousDirectory = entry.getValue(); DirectorySnapshot currentDirectory = current.get(entry.getKey()); + Assert.state(currentDirectory != null, "'currentDirectory' must not be null"); if (!previousDirectory.equals(currentDirectory, this.triggerFilter)) { return true; } @@ -310,6 +313,7 @@ public class FileSystemWatcher { for (DirectorySnapshot snapshot : snapshots) { DirectorySnapshot previous = this.directories.get(snapshot.getDirectory()); updated.put(snapshot.getDirectory(), snapshot); + Assert.state(previous != null, "'previous' must not be null"); ChangedFiles changedFiles = previous.getChangedFiles(snapshot, this.triggerFilter); if (!changedFiles.getFiles().isEmpty()) { changeSet.add(changedFiles); diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/SnapshotStateRepository.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/SnapshotStateRepository.java index 8960ba09102..079b43c4d50 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/SnapshotStateRepository.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/SnapshotStateRepository.java @@ -16,6 +16,8 @@ package org.springframework.boot.devtools.filewatch; +import org.jspecify.annotations.Nullable; + /** * Repository used by {@link FileSystemWatcher} to save file/directory snapshots across * restarts. @@ -35,7 +37,7 @@ public interface SnapshotStateRepository { } @Override - public Object restore() { + public @Nullable Object restore() { return null; } @@ -57,6 +59,6 @@ public interface SnapshotStateRepository { * Restore any previously saved state. * @return the previously saved state or {@code null} */ - Object restore(); + @Nullable Object restore(); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/StaticSnapshotStateRepository.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/StaticSnapshotStateRepository.java index adcea3ab4bf..40dcc8dcba9 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/StaticSnapshotStateRepository.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/StaticSnapshotStateRepository.java @@ -16,6 +16,8 @@ package org.springframework.boot.devtools.filewatch; +import org.jspecify.annotations.Nullable; + /** * {@link SnapshotStateRepository} that uses a single static instance. * @@ -25,7 +27,7 @@ class StaticSnapshotStateRepository implements SnapshotStateRepository { static final StaticSnapshotStateRepository INSTANCE = new StaticSnapshotStateRepository(); - private volatile Object state; + private volatile @Nullable Object state; @Override public void save(Object state) { @@ -33,7 +35,7 @@ class StaticSnapshotStateRepository implements SnapshotStateRepository { } @Override - public Object restore() { + public @Nullable Object restore() { return this.state; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/package-info.java index 7468af24731..ef3346641d4 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/package-info.java @@ -17,4 +17,7 @@ /** * Class to watch the local filesystem for changes. */ +@NullMarked package org.springframework.boot.devtools.filewatch; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java index d079555ef55..0e587afb3bd 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java @@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; @@ -63,9 +64,9 @@ public class LiveReloadServer { private final ThreadFactory threadFactory; - private ServerSocket serverSocket; + private @Nullable ServerSocket serverSocket; - private Thread listenThread; + private @Nullable Thread listenThread; /** * Create a new {@link LiveReloadServer} listening on the default port. @@ -140,6 +141,7 @@ public class LiveReloadServer { } private void acceptConnections() { + Assert.state(this.serverSocket != null, "'serverSocket' must not be null"); do { try { Socket socket = this.serverSocket.accept(); @@ -173,6 +175,7 @@ public class LiveReloadServer { catch (InterruptedException ex) { Thread.currentThread().interrupt(); } + Assert.state(this.serverSocket != null, "'serverSocket' must not be null"); this.serverSocket.close(); try { this.listenThread.join(); diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/package-info.java index 94c4ce45728..e1c09489e18 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/package-info.java @@ -17,4 +17,7 @@ /** * Support for the livereload protocol. */ +@NullMarked package org.springframework.boot.devtools.livereload; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/logger/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/logger/package-info.java index 88d380587e3..236022fcd8c 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/logger/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/logger/package-info.java @@ -17,4 +17,7 @@ /** * Devtools specific logging concerns. */ +@NullMarked package org.springframework.boot.devtools.logger; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/package-info.java index 3263f58d704..65309719968 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/package-info.java @@ -17,4 +17,7 @@ /** * Spring Boot developer tools. */ +@NullMarked package org.springframework.boot.devtools; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java index 8985ba8a88a..6878cf55891 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java @@ -155,6 +155,7 @@ public class ClassPathChangeUploader implements ApplicationListener elementClass = Class.forName(element.getClassName()); Method method = elementClass.getDeclaredMethod("main", String[].class); diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/OnInitializedRestarterCondition.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/OnInitializedRestarterCondition.java index 98d44a6d707..7c486f293be 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/OnInitializedRestarterCondition.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/OnInitializedRestarterCondition.java @@ -16,6 +16,8 @@ package org.springframework.boot.devtools.restart; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; @@ -44,7 +46,7 @@ class OnInitializedRestarterCondition extends SpringBootCondition { return ConditionOutcome.match(message.because("available and initialized")); } - private Restarter getRestarter() { + private @Nullable Restarter getRestarter() { try { return Restarter.getInstance(); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartInitializer.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartInitializer.java index 005c4cc450d..e1c011a328e 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartInitializer.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartInitializer.java @@ -18,6 +18,8 @@ package org.springframework.boot.devtools.restart; import java.net.URL; +import org.jspecify.annotations.Nullable; + /** * Strategy interface used to initialize a {@link Restarter}. * @@ -39,6 +41,6 @@ public interface RestartInitializer { * @param thread the source thread * @return initial URLs or {@code null} */ - URL[] getInitialUrls(Thread thread); + URL @Nullable [] getInitialUrls(Thread thread); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartLauncher.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartLauncher.java index 8d1d65355e6..aa996386f52 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartLauncher.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartLauncher.java @@ -18,6 +18,8 @@ package org.springframework.boot.devtools.restart; import java.lang.reflect.Method; +import org.jspecify.annotations.Nullable; + /** * Thread used to launch a restarted application. * @@ -29,7 +31,7 @@ class RestartLauncher extends Thread { private final String[] args; - private Throwable error; + private @Nullable Throwable error; RestartLauncher(ClassLoader classLoader, String mainClassName, String[] args, UncaughtExceptionHandler exceptionHandler) { @@ -55,7 +57,7 @@ class RestartLauncher extends Thread { } } - Throwable getError() { + @Nullable Throwable getError() { return this.error; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartScopeInitializer.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartScopeInitializer.java index d951fb7edef..f03bbc4b598 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartScopeInitializer.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartScopeInitializer.java @@ -16,6 +16,8 @@ package org.springframework.boot.devtools.restart; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; import org.springframework.context.ApplicationContextInitializer; @@ -54,12 +56,12 @@ public class RestartScopeInitializer implements ApplicationContextInitializer urls = new LinkedHashSet<>(); @@ -106,9 +107,9 @@ public class Restarter { private boolean enabled = true; - private final URL[] initialUrls; + private final URL @Nullable [] initialUrls; - private final String mainClassName; + private final @Nullable String mainClassName; private final ClassLoader applicationClassLoader; @@ -145,7 +146,7 @@ public class Restarter { this.leakSafeThreads.add(new LeakSafeThread()); } - private String getMainClassName(Thread thread) { + private @Nullable String getMainClassName(Thread thread) { try { return new MainMethod(thread).getDeclaringClassName(); } @@ -170,7 +171,7 @@ public class Restarter { getLeakSafeThread().callAndWait(() -> { start(FailureHandler.NONE); cleanupCaches(); - return null; + return new Object(); }); } catch (Exception ex) { @@ -249,7 +250,7 @@ public class Restarter { getLeakSafeThread().call(() -> { Restarter.this.stop(); Restarter.this.start(failureHandler); - return null; + return new Object(); }); } @@ -271,7 +272,7 @@ public class Restarter { while (true); } - private Throwable doStart() throws Exception { + private @Nullable Throwable doStart() throws Exception { Assert.state(this.mainClassName != null, "Unable to find the main class to restart"); URL[] urls = this.urls.toArray(new URL[0]); ClassLoaderFiles updatedFiles = new ClassLoaderFiles(this.classLoaderFiles); @@ -288,7 +289,8 @@ public class Restarter { * @return any exception that caused the launch to fail or {@code null} * @throws Exception in case of errors */ - protected Throwable relaunch(ClassLoader classLoader) throws Exception { + protected @Nullable Throwable relaunch(ClassLoader classLoader) throws Exception { + Assert.state(this.mainClassName != null, "'mainClassName' must not be null"); RestartLauncher launcher = new RestartLauncher(classLoader, this.mainClassName, this.args, this.exceptionHandler); launcher.start(); @@ -417,7 +419,7 @@ public class Restarter { this.rootContexts.add(applicationContext); } - void remove(ConfigurableApplicationContext applicationContext) { + void remove(@Nullable ConfigurableApplicationContext applicationContext) { if (applicationContext != null) { this.rootContexts.remove(applicationContext); } @@ -456,7 +458,7 @@ public class Restarter { * Return the initial set of URLs as configured by the {@link RestartInitializer}. * @return the initial URLs or {@code null} */ - public URL[] getInitialUrls() { + public URL @Nullable [] getInitialUrls() { return this.initialUrls; } @@ -575,9 +577,9 @@ public class Restarter { */ private class LeakSafeThread extends Thread { - private Callable callable; + private @Nullable Callable callable; - private Object result; + private @Nullable Object result; LeakSafeThread() { setDaemon(false); @@ -594,6 +596,7 @@ public class Restarter { start(); try { join(); + Assert.state(this.result != null, "'this.result' must not be null"); return (V) this.result; } catch (InterruptedException ex) { @@ -609,6 +612,7 @@ public class Restarter { // RestartClassLoader try { Restarter.this.leakSafeThreads.put(new LeakSafeThread()); + Assert.state(this.callable != null, "'callable' must not be null"); this.result = this.callable.call(); } catch (Exception ex) { diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/SilentExitExceptionHandler.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/SilentExitExceptionHandler.java index 7dbdf74abe7..f75367ef6da 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/SilentExitExceptionHandler.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/SilentExitExceptionHandler.java @@ -20,6 +20,8 @@ import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import org.jspecify.annotations.Nullable; + /** * {@link UncaughtExceptionHandler} decorator that allows a thread to exit silently. * @@ -28,9 +30,9 @@ import java.util.Arrays; */ class SilentExitExceptionHandler implements UncaughtExceptionHandler { - private final UncaughtExceptionHandler delegate; + private final @Nullable UncaughtExceptionHandler delegate; - SilentExitExceptionHandler(UncaughtExceptionHandler delegate) { + SilentExitExceptionHandler(@Nullable UncaughtExceptionHandler delegate) { this.delegate = delegate; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFile.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFile.java index 96d23bf020f..8ccf2a31a05 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFile.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFile.java @@ -16,8 +16,11 @@ package org.springframework.boot.devtools.restart.classloader; +import java.io.Serial; import java.io.Serializable; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -30,11 +33,12 @@ import org.springframework.util.Assert; */ public class ClassLoaderFile implements Serializable { + @Serial private static final long serialVersionUID = 1; private final Kind kind; - private final byte[] contents; + private final byte @Nullable [] contents; private final long lastModified; @@ -53,7 +57,7 @@ public class ClassLoaderFile implements Serializable { * @param lastModified the last modified time * @param contents the file contents */ - public ClassLoaderFile(Kind kind, long lastModified, byte[] contents) { + public ClassLoaderFile(Kind kind, long lastModified, byte @Nullable [] contents) { Assert.notNull(kind, "'kind' must not be null"); if (kind == Kind.DELETED) { Assert.isTrue(contents == null, "'contents' must be null"); @@ -87,7 +91,7 @@ public class ClassLoaderFile implements Serializable { * {@link #getKind()} is {@link Kind#DELETED}. * @return the contents or {@code null} */ - public byte[] getContents() { + public byte @Nullable [] getContents() { return this.contents; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFileRepository.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFileRepository.java index 2a3a23671ed..41c1586aac5 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFileRepository.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFileRepository.java @@ -16,6 +16,8 @@ package org.springframework.boot.devtools.restart.classloader; +import org.jspecify.annotations.Nullable; + /** * A container for files that may be served from a {@link ClassLoader}. Can be used to * represent files that have been added, modified or deleted since the original JAR was @@ -39,6 +41,6 @@ public interface ClassLoaderFileRepository { * @param name the name of the file * @return a {@link ClassLoaderFile} or {@code null} */ - ClassLoaderFile getFile(String name); + @Nullable ClassLoaderFile getFile(String name); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFiles.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFiles.java index 1def6fd05b1..a4d28604633 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFiles.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFiles.java @@ -16,6 +16,7 @@ package org.springframework.boot.devtools.restart.classloader; +import java.io.Serial; import java.io.Serializable; import java.util.Collection; import java.util.Collections; @@ -26,6 +27,8 @@ import java.util.Set; import javax.management.loading.ClassLoaderRepository; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -133,7 +136,7 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable } @Override - public ClassLoaderFile getFile(String name) { + public @Nullable ClassLoaderFile getFile(String name) { for (SourceDirectory sourceDirectory : this.sourceDirectories.values()) { ClassLoaderFile file = sourceDirectory.get(name); if (file != null) { @@ -148,6 +151,7 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable */ public static class SourceDirectory implements Serializable { + @Serial private static final long serialVersionUID = 1; private final String name; @@ -170,7 +174,7 @@ public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable this.files.remove(name); } - protected final ClassLoaderFile get(String name) { + protected final @Nullable ClassLoaderFile get(String name) { return this.files.get(name); } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoader.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoader.java index 806c1bb6a60..736e7837725 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoader.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoader.java @@ -23,6 +23,8 @@ import java.net.URLClassLoader; import java.security.ProtectionDomain; import java.util.Enumeration; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile.Kind; import org.springframework.core.SmartClassLoader; import org.springframework.util.Assert; @@ -80,7 +82,7 @@ public class RestartClassLoader extends URLClassLoader implements SmartClassLoad } @Override - public URL getResource(String name) { + public @Nullable URL getResource(String name) { ClassLoaderFile file = this.updatedFiles.getFile(name); if (file != null && file.getKind() == Kind.DELETED) { return null; @@ -93,7 +95,7 @@ public class RestartClassLoader extends URLClassLoader implements SmartClassLoad } @Override - public URL findResource(String name) { + public @Nullable URL findResource(String name) { final ClassLoaderFile file = this.updatedFiles.getFile(name); if (file == null) { return super.findResource(name); @@ -139,11 +141,12 @@ public class RestartClassLoader extends URLClassLoader implements SmartClassLoad throw new ClassNotFoundException(name); } byte[] bytes = file.getContents(); + Assert.state(bytes != null, "'bytes' must not be null"); return defineClass(name, bytes, 0, bytes.length); } @Override - public Class publicDefineClass(String name, byte[] b, ProtectionDomain protectionDomain) { + public Class publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) { return defineClass(name, b, 0, b.length, protectionDomain); } @@ -171,11 +174,11 @@ public class RestartClassLoader extends URLClassLoader implements SmartClassLoad */ private static class CompoundEnumeration implements Enumeration { - private E firstElement; + private @Nullable E firstElement; private final Enumeration enumeration; - CompoundEnumeration(E firstElement, Enumeration enumeration) { + CompoundEnumeration(@Nullable E firstElement, Enumeration enumeration) { this.firstElement = firstElement; this.enumeration = enumeration; } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/package-info.java index eeea229cde2..79c543c10bc 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/package-info.java @@ -17,4 +17,7 @@ /** * Classloaders used for reload support. */ +@NullMarked package org.springframework.boot.devtools.restart.classloader; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/package-info.java index 8da7a6f30ac..73a9c7bb863 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/package-info.java @@ -17,4 +17,7 @@ /** * Application restart support. */ +@NullMarked package org.springframework.boot.devtools.restart; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/DefaultSourceDirectoryUrlFilter.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/DefaultSourceDirectoryUrlFilter.java index f50563f0c25..7a29715e870 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/DefaultSourceDirectoryUrlFilter.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/DefaultSourceDirectoryUrlFilter.java @@ -20,6 +20,8 @@ import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; + import org.springframework.util.StringUtils; /** @@ -46,7 +48,7 @@ public class DefaultSourceDirectoryUrlFilter implements SourceDirectoryUrlFilter return isMatch(sourceDirectory, jarName); } - private String getJarName(URL url) { + private @Nullable String getJarName(URL url) { Matcher matcher = URL_MODULE_PATTERN.matcher(url.toString()); if (matcher.find()) { return matcher.group(1); diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/RestartServer.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/RestartServer.java index b388ff9c11a..21835b1ff9b 100755 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/RestartServer.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/RestartServer.java @@ -107,7 +107,9 @@ public class RestartServer { if (classLoaderFile.getKind() == Kind.DELETED) { return file.delete(); } - FileCopyUtils.copy(classLoaderFile.getContents(), file); + byte[] contents = classLoaderFile.getContents(); + Assert.state(contents != null, "'contents' must not be null"); + FileCopyUtils.copy(contents, file); return true; } } diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/package-info.java index 891d890174c..6b1c12c415f 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/server/package-info.java @@ -17,4 +17,7 @@ /** * Remote restart server. */ +@NullMarked package org.springframework.boot.devtools.restart.server; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/DevToolsSettings.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/DevToolsSettings.java index e71b69bc359..79e87cdbb8e 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/DevToolsSettings.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/DevToolsSettings.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.logging.Log; +import org.jspecify.annotations.Nullable; import org.springframework.boot.devtools.logger.DevToolsLogFactory; import org.springframework.core.io.UrlResource; @@ -45,7 +46,7 @@ public class DevToolsSettings { private static final Log logger = DevToolsLogFactory.getLog(DevToolsSettings.class); - private static DevToolsSettings settings; + private static @Nullable DevToolsSettings settings; private final List restartIncludePatterns = new ArrayList<>(); diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/package-info.java index 776afb0f98b..a4f299f52b4 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/settings/package-info.java @@ -17,4 +17,7 @@ /** * Classes for loading DevTools settings. */ +@NullMarked package org.springframework.boot.devtools.settings; + +import org.jspecify.annotations.NullMarked; diff --git a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/system/package-info.java b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/system/package-info.java index 138011c869b..31da63fe42f 100644 --- a/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/system/package-info.java +++ b/module/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/system/package-info.java @@ -17,4 +17,7 @@ /** * Devtools system support classes. */ +@NullMarked package org.springframework.boot.devtools.system; + +import org.jspecify.annotations.NullMarked;