Add nullability annotations to core/spring-boot-docker-compose
See gh-46587
This commit is contained in:
parent
b6e4533296
commit
cb2a26ceec
|
@ -18,6 +18,8 @@ package org.springframework.boot.docker.compose.core;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides access to the ports that can be used to connect to a {@link RunningService}.
|
||||
*
|
||||
|
@ -52,6 +54,6 @@ public interface ConnectionPorts {
|
|||
* all host ports
|
||||
* @return a list of all host ports using the given protocol
|
||||
*/
|
||||
List<Integer> getAll(String protocol);
|
||||
List<Integer> getAll(@Nullable String protocol);
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.docker.compose.core.DockerCliInspectResponse.Config;
|
||||
import org.springframework.boot.docker.compose.core.DockerCliInspectResponse.HostConfig;
|
||||
import org.springframework.boot.docker.compose.core.DockerCliInspectResponse.HostPort;
|
||||
|
@ -57,7 +59,7 @@ class DefaultConnectionPorts implements ConnectionPorts {
|
|||
return (config != null) && "host".equals(config.networkMode());
|
||||
}
|
||||
|
||||
private Map<ContainerPort, Integer> buildMappingsForNetworkSettings(NetworkSettings networkSettings) {
|
||||
private Map<ContainerPort, Integer> buildMappingsForNetworkSettings(@Nullable NetworkSettings networkSettings) {
|
||||
if (networkSettings == null || CollectionUtils.isEmpty(networkSettings.ports())) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
@ -73,7 +75,7 @@ class DefaultConnectionPorts implements ConnectionPorts {
|
|||
return Collections.unmodifiableMap(mappings);
|
||||
}
|
||||
|
||||
private boolean isIpV4(HostPort hostPort) {
|
||||
private boolean isIpV4(@Nullable HostPort hostPort) {
|
||||
String ip = (hostPort != null) ? hostPort.hostIp() : null;
|
||||
return !StringUtils.hasLength(ip) || ip.contains(".");
|
||||
}
|
||||
|
@ -108,7 +110,7 @@ class DefaultConnectionPorts implements ConnectionPorts {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getAll(String protocol) {
|
||||
public List<Integer> getAll(@Nullable String protocol) {
|
||||
List<Integer> hostPorts = new ArrayList<>();
|
||||
this.mappings.forEach((containerPort, hostPort) -> {
|
||||
if (protocol == null || protocol.equalsIgnoreCase(containerPort.protocol())) {
|
||||
|
|
|
@ -25,6 +25,8 @@ import java.util.Map.Entry;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -41,7 +43,7 @@ class DefaultDockerCompose implements DockerCompose {
|
|||
|
||||
private final DockerHost hostname;
|
||||
|
||||
DefaultDockerCompose(DockerCli cli, String host) {
|
||||
DefaultDockerCompose(DockerCli cli, @Nullable String host) {
|
||||
this.cli = cli;
|
||||
this.hostname = DockerHost.get(host, () -> cli.run(new DockerCliCommand.Context()));
|
||||
}
|
||||
|
@ -114,7 +116,8 @@ class DefaultDockerCompose implements DockerCompose {
|
|||
return inspectResponses.stream().collect(Collectors.toMap(DockerCliInspectResponse::id, Function.identity()));
|
||||
}
|
||||
|
||||
private DockerCliInspectResponse inspectContainer(String id, Map<String, DockerCliInspectResponse> inspected) {
|
||||
private @Nullable DockerCliInspectResponse inspectContainer(String id,
|
||||
Map<String, DockerCliInspectResponse> inspected) {
|
||||
DockerCliInspectResponse inspect = inspected.get(id);
|
||||
if (inspect != null) {
|
||||
return inspect;
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.springframework.boot.docker.compose.core;
|
|||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.boot.origin.OriginProvider;
|
||||
|
||||
|
@ -45,10 +47,10 @@ class DefaultRunningService implements RunningService, OriginProvider {
|
|||
|
||||
private final DockerEnv env;
|
||||
|
||||
private final DockerComposeFile composeFile;
|
||||
private final @Nullable DockerComposeFile composeFile;
|
||||
|
||||
DefaultRunningService(DockerHost host, DockerComposeFile composeFile, DockerCliComposePsResponse composePsResponse,
|
||||
DockerCliInspectResponse inspectResponse) {
|
||||
DefaultRunningService(DockerHost host, @Nullable DockerComposeFile composeFile,
|
||||
DockerCliComposePsResponse composePsResponse, DockerCliInspectResponse inspectResponse) {
|
||||
this.origin = new DockerComposeOrigin(composeFile, composePsResponse.name());
|
||||
this.name = composePsResponse.name();
|
||||
this.image = ImageReference
|
||||
|
@ -101,7 +103,7 @@ class DefaultRunningService implements RunningService, OriginProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DockerComposeFile composeFile() {
|
||||
public @Nullable DockerComposeFile composeFile() {
|
||||
return this.composeFile;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.docker.compose.core.DockerCliCommand.ComposeVersion;
|
||||
import org.springframework.boot.docker.compose.core.DockerCliCommand.Type;
|
||||
|
@ -60,7 +61,7 @@ class DockerCli {
|
|||
* @param workingDirectory the working directory or {@code null}
|
||||
* @param dockerComposeOptions the Docker Compose options to use or {@code null}.
|
||||
*/
|
||||
DockerCli(File workingDirectory, DockerComposeOptions dockerComposeOptions) {
|
||||
DockerCli(@Nullable File workingDirectory, @Nullable DockerComposeOptions dockerComposeOptions) {
|
||||
this.processRunner = new ProcessRunner(workingDirectory);
|
||||
this.dockerCommands = dockerCommandsCache.computeIfAbsent(workingDirectory,
|
||||
(key) -> new DockerCommands(this.processRunner));
|
||||
|
@ -82,7 +83,7 @@ class DockerCli {
|
|||
return dockerCommand.deserialize(json);
|
||||
}
|
||||
|
||||
private Consumer<String> createOutputConsumer(LogLevel logLevel) {
|
||||
private @Nullable Consumer<String> createOutputConsumer(@Nullable LogLevel logLevel) {
|
||||
if (logLevel == null || logLevel == LogLevel.OFF) {
|
||||
return null;
|
||||
}
|
||||
|
@ -123,7 +124,7 @@ class DockerCli {
|
|||
* Return the {@link DockerComposeFile} being used by this CLI instance.
|
||||
* @return the Docker Compose file
|
||||
*/
|
||||
DockerComposeFile getDockerComposeFile() {
|
||||
@Nullable DockerComposeFile getDockerComposeFile() {
|
||||
return this.dockerComposeOptions.composeFile();
|
||||
}
|
||||
|
||||
|
@ -205,11 +206,14 @@ class DockerCli {
|
|||
* @param activeProfiles the profiles to activate
|
||||
* @param arguments the arguments to pass to Docker Compose
|
||||
*/
|
||||
record DockerComposeOptions(DockerComposeFile composeFile, Set<String> activeProfiles, List<String> arguments) {
|
||||
record DockerComposeOptions(@Nullable DockerComposeFile composeFile, Set<String> activeProfiles,
|
||||
List<String> arguments) {
|
||||
|
||||
DockerComposeOptions {
|
||||
activeProfiles = (activeProfiles != null) ? activeProfiles : Collections.emptySet();
|
||||
arguments = (arguments != null) ? arguments : Collections.emptyList();
|
||||
DockerComposeOptions(@Nullable DockerComposeFile composeFile, @Nullable Set<String> activeProfiles,
|
||||
@Nullable List<String> arguments) {
|
||||
this.composeFile = composeFile;
|
||||
this.activeProfiles = (activeProfiles != null) ? activeProfiles : Collections.emptySet();
|
||||
this.arguments = (arguments != null) ? arguments : Collections.emptyList();
|
||||
}
|
||||
|
||||
static DockerComposeOptions none() {
|
||||
|
|
|
@ -78,8 +78,8 @@ abstract sealed class DockerCliCommand<R> {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
R deserialize(String json) {
|
||||
if (this.responseType == Void.class) {
|
||||
return null;
|
||||
if (this.responseType == None.class) {
|
||||
return (R) None.INSTANCE;
|
||||
}
|
||||
return (R) ((!this.listResponse) ? DockerJson.deserialize(json, this.responseType)
|
||||
: DockerJson.deserializeToList(json, this.responseType));
|
||||
|
@ -175,10 +175,10 @@ abstract sealed class DockerCliCommand<R> {
|
|||
/**
|
||||
* The {@code docker compose up} command.
|
||||
*/
|
||||
static final class ComposeUp extends DockerCliCommand<Void> {
|
||||
static final class ComposeUp extends DockerCliCommand<None> {
|
||||
|
||||
ComposeUp(LogLevel logLevel, List<String> arguments) {
|
||||
super(Type.DOCKER_COMPOSE, logLevel, Void.class, false, getCommand(arguments));
|
||||
super(Type.DOCKER_COMPOSE, logLevel, None.class, false, getCommand(arguments));
|
||||
}
|
||||
|
||||
private static String[] getCommand(List<String> arguments) {
|
||||
|
@ -196,10 +196,10 @@ abstract sealed class DockerCliCommand<R> {
|
|||
/**
|
||||
* The {@code docker compose down} command.
|
||||
*/
|
||||
static final class ComposeDown extends DockerCliCommand<Void> {
|
||||
static final class ComposeDown extends DockerCliCommand<None> {
|
||||
|
||||
ComposeDown(Duration timeout, List<String> arguments) {
|
||||
super(Type.DOCKER_COMPOSE, Void.class, false, getCommand(timeout, arguments));
|
||||
super(Type.DOCKER_COMPOSE, None.class, false, getCommand(timeout, arguments));
|
||||
}
|
||||
|
||||
private static String[] getCommand(Duration timeout, List<String> arguments) {
|
||||
|
@ -216,10 +216,10 @@ abstract sealed class DockerCliCommand<R> {
|
|||
/**
|
||||
* The {@code docker compose start} command.
|
||||
*/
|
||||
static final class ComposeStart extends DockerCliCommand<Void> {
|
||||
static final class ComposeStart extends DockerCliCommand<None> {
|
||||
|
||||
ComposeStart(LogLevel logLevel, List<String> arguments) {
|
||||
super(Type.DOCKER_COMPOSE, logLevel, Void.class, false, getCommand(arguments));
|
||||
super(Type.DOCKER_COMPOSE, logLevel, None.class, false, getCommand(arguments));
|
||||
}
|
||||
|
||||
private static String[] getCommand(List<String> arguments) {
|
||||
|
@ -234,10 +234,10 @@ abstract sealed class DockerCliCommand<R> {
|
|||
/**
|
||||
* The {@code docker compose stop} command.
|
||||
*/
|
||||
static final class ComposeStop extends DockerCliCommand<Void> {
|
||||
static final class ComposeStop extends DockerCliCommand<None> {
|
||||
|
||||
ComposeStop(Duration timeout, List<String> arguments) {
|
||||
super(Type.DOCKER_COMPOSE, Void.class, false, getCommand(timeout, arguments));
|
||||
super(Type.DOCKER_COMPOSE, None.class, false, getCommand(timeout, arguments));
|
||||
}
|
||||
|
||||
private static String[] getCommand(Duration timeout, List<String> arguments) {
|
||||
|
@ -268,6 +268,15 @@ abstract sealed class DockerCliCommand<R> {
|
|||
|
||||
}
|
||||
|
||||
static final class None {
|
||||
|
||||
public static final None INSTANCE = new None();
|
||||
|
||||
private None() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Docker compose version.
|
||||
*
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.core;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Response from {@link DockerCliCommand.ComposePs docker compose ps}.
|
||||
*
|
||||
|
@ -27,6 +29,6 @@ package org.springframework.boot.docker.compose.core;
|
|||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
record DockerCliComposePsResponse(String id, String name, String image, String state) {
|
||||
record DockerCliComposePsResponse(String id, String name, @Nullable String image, String state) {
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.springframework.boot.docker.compose.core;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Response from {@link DockerCliCommand.Inspect docker inspect}.
|
||||
*
|
||||
|
@ -31,7 +33,8 @@ import java.util.Map;
|
|||
* @author Phillip Webb
|
||||
*/
|
||||
record DockerCliInspectResponse(String id, DockerCliInspectResponse.Config config,
|
||||
DockerCliInspectResponse.NetworkSettings networkSettings, DockerCliInspectResponse.HostConfig hostConfig) {
|
||||
DockerCliInspectResponse.NetworkSettings networkSettings,
|
||||
DockerCliInspectResponse.@Nullable HostConfig hostConfig) {
|
||||
|
||||
/**
|
||||
* Configuration for the container that is portable between hosts.
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.docker.compose.core.DockerCli.DockerComposeOptions;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
|
||||
|
@ -126,7 +128,7 @@ public interface DockerCompose {
|
|||
* @param activeProfiles a set of the profiles that should be activated
|
||||
* @return a {@link DockerCompose} instance
|
||||
*/
|
||||
static DockerCompose get(DockerComposeFile file, String hostname, Set<String> activeProfiles) {
|
||||
static DockerCompose get(DockerComposeFile file, @Nullable String hostname, Set<String> activeProfiles) {
|
||||
return get(file, hostname, activeProfiles, Collections.emptyList());
|
||||
}
|
||||
|
||||
|
@ -140,7 +142,7 @@ public interface DockerCompose {
|
|||
* @return a {@link DockerCompose} instance
|
||||
* @since 3.4.0
|
||||
*/
|
||||
static DockerCompose get(DockerComposeFile file, String hostname, Set<String> activeProfiles,
|
||||
static DockerCompose get(DockerComposeFile file, @Nullable String hostname, Set<String> activeProfiles,
|
||||
List<String> arguments) {
|
||||
DockerCli cli = new DockerCli(null, new DockerComposeOptions(file, activeProfiles, arguments));
|
||||
return new DefaultDockerCompose(cli, hostname);
|
||||
|
|
|
@ -26,6 +26,8 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -107,7 +109,7 @@ public final class DockerComposeFile {
|
|||
* current directory
|
||||
* @return the located file or {@code null} if no Docker Compose file can be found
|
||||
*/
|
||||
public static DockerComposeFile find(File workingDirectory) {
|
||||
public static @Nullable DockerComposeFile find(@Nullable File workingDirectory) {
|
||||
File base = (workingDirectory != null) ? workingDirectory : new File(".");
|
||||
if (!base.exists()) {
|
||||
return null;
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.core;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.origin.Origin;
|
||||
|
||||
/**
|
||||
|
@ -27,7 +29,7 @@ import org.springframework.boot.origin.Origin;
|
|||
* @author Andy Wilkinson
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public record DockerComposeOrigin(DockerComposeFile composeFile, String serviceName) implements Origin {
|
||||
public record DockerComposeOrigin(@Nullable DockerComposeFile composeFile, String serviceName) implements Origin {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
|
@ -69,7 +71,7 @@ class DockerEnv {
|
|||
return this.map;
|
||||
}
|
||||
|
||||
private record Entry(String key, String value) {
|
||||
private record Entry(String key, @Nullable String value) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.List;
|
|||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -69,7 +71,7 @@ final class DockerHost {
|
|||
* {@link DockerCliContextResponse}
|
||||
* @return a new docker host instance
|
||||
*/
|
||||
static DockerHost get(String host, Supplier<List<DockerCliContextResponse>> contextsSupplier) {
|
||||
static DockerHost get(@Nullable String host, Supplier<List<DockerCliContextResponse>> contextsSupplier) {
|
||||
return get(host, System::getenv, contextsSupplier);
|
||||
}
|
||||
|
||||
|
@ -81,7 +83,7 @@ final class DockerHost {
|
|||
* {@link DockerCliContextResponse}
|
||||
* @return a new docker host instance
|
||||
*/
|
||||
static DockerHost get(String host, Function<String, String> systemEnv,
|
||||
static DockerHost get(@Nullable String host, Function<String, String> systemEnv,
|
||||
Supplier<List<DockerCliContextResponse>> contextsSupplier) {
|
||||
host = (StringUtils.hasText(host)) ? host : fromServicesHostEnv(systemEnv);
|
||||
host = (StringUtils.hasText(host)) ? host : fromDockerHostEnv(systemEnv);
|
||||
|
@ -94,24 +96,24 @@ final class DockerHost {
|
|||
return systemEnv.apply("SERVICES_HOST");
|
||||
}
|
||||
|
||||
private static String fromDockerHostEnv(Function<String, String> systemEnv) {
|
||||
private static @Nullable String fromDockerHostEnv(Function<String, String> systemEnv) {
|
||||
return fromEndpoint(systemEnv.apply("DOCKER_HOST"));
|
||||
}
|
||||
|
||||
private static String fromCurrentContext(Supplier<List<DockerCliContextResponse>> contextsSupplier) {
|
||||
private static @Nullable String fromCurrentContext(Supplier<List<DockerCliContextResponse>> contextsSupplier) {
|
||||
DockerCliContextResponse current = getCurrentContext(contextsSupplier.get());
|
||||
return (current != null) ? fromEndpoint(current.dockerEndpoint()) : null;
|
||||
}
|
||||
|
||||
private static DockerCliContextResponse getCurrentContext(List<DockerCliContextResponse> candidates) {
|
||||
private static @Nullable DockerCliContextResponse getCurrentContext(List<DockerCliContextResponse> candidates) {
|
||||
return candidates.stream().filter(DockerCliContextResponse::current).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private static String fromEndpoint(String endpoint) {
|
||||
private static @Nullable String fromEndpoint(@Nullable String endpoint) {
|
||||
return (StringUtils.hasLength(endpoint)) ? fromUri(URI.create(endpoint)) : null;
|
||||
}
|
||||
|
||||
private static String fromUri(URI uri) {
|
||||
private static @Nullable String fromUri(URI uri) {
|
||||
try {
|
||||
return switch (uri.getScheme()) {
|
||||
case "http", "https", "tcp" -> uri.getHost();
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.core;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +40,7 @@ class ImageName {
|
|||
|
||||
private final String string;
|
||||
|
||||
ImageName(String domain, String path) {
|
||||
ImageName(@Nullable String domain, String path) {
|
||||
Assert.hasText(path, "'path' must not be empty");
|
||||
this.domain = getDomainOrDefault(domain);
|
||||
this.name = getNameWithDefaultPath(this.domain, path);
|
||||
|
@ -90,7 +92,7 @@ class ImageName {
|
|||
return this.string;
|
||||
}
|
||||
|
||||
private String getDomainOrDefault(String domain) {
|
||||
private String getDomainOrDefault(@Nullable String domain) {
|
||||
if (domain == null || LEGACY_DOMAIN.equals(domain)) {
|
||||
return DEFAULT_DOMAIN;
|
||||
}
|
||||
|
@ -104,7 +106,7 @@ class ImageName {
|
|||
return name;
|
||||
}
|
||||
|
||||
static String parseDomain(String value) {
|
||||
static @Nullable String parseDomain(String value) {
|
||||
int firstSlash = value.indexOf('/');
|
||||
String candidate = (firstSlash != -1) ? value.substring(0, firstSlash) : null;
|
||||
if (candidate != null && Regex.DOMAIN.matcher(candidate).matches()) {
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.springframework.boot.docker.compose.core;
|
|||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
|
@ -32,13 +34,13 @@ public final class ImageReference {
|
|||
|
||||
private final ImageName name;
|
||||
|
||||
private final String tag;
|
||||
private final @Nullable String tag;
|
||||
|
||||
private final String digest;
|
||||
private final @Nullable String digest;
|
||||
|
||||
private final String string;
|
||||
|
||||
private ImageReference(ImageName name, String tag, String digest) {
|
||||
private ImageReference(ImageName name, @Nullable String tag, @Nullable String digest) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
this.name = name;
|
||||
this.tag = tag;
|
||||
|
@ -68,7 +70,7 @@ public final class ImageReference {
|
|||
* Return the tag from the reference or {@code null}.
|
||||
* @return the referenced tag
|
||||
*/
|
||||
public String getTag() {
|
||||
public @Nullable String getTag() {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
|
@ -76,7 +78,7 @@ public final class ImageReference {
|
|||
* Return the digest from the reference or {@code null}.
|
||||
* @return the referenced digest
|
||||
*/
|
||||
public String getDigest() {
|
||||
public @Nullable String getDigest() {
|
||||
return this.digest;
|
||||
}
|
||||
|
||||
|
@ -111,7 +113,7 @@ public final class ImageReference {
|
|||
return this.string;
|
||||
}
|
||||
|
||||
private String buildString(String name, String tag, String digest) {
|
||||
private String buildString(String name, @Nullable String tag, @Nullable String digest) {
|
||||
StringBuilder string = new StringBuilder(name);
|
||||
if (tag != null) {
|
||||
string.append(":").append(tag);
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.core;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Exception thrown by {@link ProcessRunner} when the process exits with a non-zero code.
|
||||
*
|
||||
|
@ -37,7 +39,7 @@ class ProcessExitException extends RuntimeException {
|
|||
this(exitCode, command, stdOut, stdErr, null);
|
||||
}
|
||||
|
||||
ProcessExitException(int exitCode, String[] command, String stdOut, String stdErr, Throwable cause) {
|
||||
ProcessExitException(int exitCode, String[] command, String stdOut, String stdErr, @Nullable Throwable cause) {
|
||||
super(buildMessage(exitCode, command, stdOut, stdErr), cause);
|
||||
this.exitCode = exitCode;
|
||||
this.command = command;
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
|
||||
|
@ -47,7 +48,7 @@ class ProcessRunner {
|
|||
|
||||
private static final Log logger = LogFactory.getLog(ProcessRunner.class);
|
||||
|
||||
private final File workingDirectory;
|
||||
private final @Nullable File workingDirectory;
|
||||
|
||||
/**
|
||||
* Create a new {@link ProcessRunner} instance.
|
||||
|
@ -60,7 +61,7 @@ class ProcessRunner {
|
|||
* Create a new {@link ProcessRunner} instance.
|
||||
* @param workingDirectory the working directory for the process
|
||||
*/
|
||||
ProcessRunner(File workingDirectory) {
|
||||
ProcessRunner(@Nullable File workingDirectory) {
|
||||
this.workingDirectory = workingDirectory;
|
||||
}
|
||||
|
||||
|
@ -83,7 +84,7 @@ class ProcessRunner {
|
|||
* @return the output of the command
|
||||
* @throws ProcessExitException if execution failed
|
||||
*/
|
||||
String run(Consumer<String> outputConsumer, String... command) {
|
||||
String run(@Nullable Consumer<String> outputConsumer, String... command) {
|
||||
logger.trace(LogMessage.of(() -> "Running '%s'".formatted(String.join(" ", command))));
|
||||
Process process = startProcess(command);
|
||||
ReaderThread stdOutReader = new ReaderThread(process.getInputStream(), "stdout", outputConsumer);
|
||||
|
@ -133,13 +134,13 @@ class ProcessRunner {
|
|||
|
||||
private final InputStream source;
|
||||
|
||||
private final Consumer<String> outputConsumer;
|
||||
private final @Nullable Consumer<String> outputConsumer;
|
||||
|
||||
private final StringBuilder output = new StringBuilder();
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
ReaderThread(InputStream source, String name, Consumer<String> outputConsumer) {
|
||||
ReaderThread(InputStream source, String name, @Nullable Consumer<String> outputConsumer) {
|
||||
this.source = source;
|
||||
this.outputConsumer = outputConsumer;
|
||||
setName("OutputReader-" + name);
|
||||
|
@ -174,7 +175,7 @@ class ProcessRunner {
|
|||
return this.output.toString();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.springframework.boot.docker.compose.core;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides details of a running Docker Compose service.
|
||||
*
|
||||
|
@ -69,7 +71,7 @@ public interface RunningService {
|
|||
* @return the Docker Compose file
|
||||
* @since 3.5.0
|
||||
*/
|
||||
default DockerComposeFile composeFile() {
|
||||
default @Nullable DockerComposeFile composeFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,4 +17,7 @@
|
|||
/**
|
||||
* Core interfaces and classes for working with Docker Compose.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.docker.compose.core;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aot.AotDetector;
|
||||
import org.springframework.boot.SpringApplicationShutdownHandlers;
|
||||
|
@ -57,11 +58,11 @@ class DockerComposeLifecycleManager {
|
|||
|
||||
private static final String IGNORE_LABEL = "org.springframework.boot.ignore";
|
||||
|
||||
private final File workingDirectory;
|
||||
private final @Nullable File workingDirectory;
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
private final @Nullable ClassLoader classLoader;
|
||||
|
||||
private final SpringApplicationShutdownHandlers shutdownHandlers;
|
||||
|
||||
|
@ -80,10 +81,10 @@ class DockerComposeLifecycleManager {
|
|||
new DockerComposeSkipCheck(), null);
|
||||
}
|
||||
|
||||
DockerComposeLifecycleManager(File workingDirectory, ApplicationContext applicationContext, Binder binder,
|
||||
DockerComposeLifecycleManager(@Nullable File workingDirectory, ApplicationContext applicationContext, Binder binder,
|
||||
SpringApplicationShutdownHandlers shutdownHandlers, DockerComposeProperties properties,
|
||||
Set<ApplicationListener<?>> eventListeners, DockerComposeSkipCheck skipCheck,
|
||||
ServiceReadinessChecks serviceReadinessChecks) {
|
||||
@Nullable ServiceReadinessChecks serviceReadinessChecks) {
|
||||
this.workingDirectory = workingDirectory;
|
||||
this.applicationContext = applicationContext;
|
||||
this.classLoader = applicationContext.getClassLoader();
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.docker.compose.core.RunningService;
|
||||
|
@ -64,7 +66,7 @@ public class DockerComposeProperties {
|
|||
/**
|
||||
* Hostname or IP of the machine where the docker containers are started.
|
||||
*/
|
||||
private String host;
|
||||
private @Nullable String host;
|
||||
|
||||
/**
|
||||
* Start configuration.
|
||||
|
@ -109,11 +111,11 @@ public class DockerComposeProperties {
|
|||
this.lifecycleManagement = lifecycleManagement;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
public @Nullable String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
public void setHost(@Nullable String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.util.Collections;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.SpringApplicationAotProcessor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
@ -44,7 +46,7 @@ class DockerComposeSkipCheck {
|
|||
SKIPPED_STACK_ELEMENTS = Collections.unmodifiableSet(skipped);
|
||||
}
|
||||
|
||||
boolean shouldSkip(ClassLoader classLoader, DockerComposeProperties.Skip properties) {
|
||||
boolean shouldSkip(@Nullable ClassLoader classLoader, DockerComposeProperties.Skip properties) {
|
||||
if (properties.isInTests() && hasAtLeastOneRequiredClass(classLoader)) {
|
||||
Thread thread = Thread.currentThread();
|
||||
for (StackTraceElement element : thread.getStackTrace()) {
|
||||
|
@ -56,7 +58,7 @@ class DockerComposeSkipCheck {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean hasAtLeastOneRequiredClass(ClassLoader classLoader) {
|
||||
private boolean hasAtLeastOneRequiredClass(@Nullable ClassLoader classLoader) {
|
||||
for (String requiredClass : REQUIRED_CLASSES) {
|
||||
if (ClassUtils.isPresent(requiredClass, classLoader)) {
|
||||
return true;
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.docker.compose.lifecycle;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.docker.compose.core.RunningService;
|
||||
|
||||
/**
|
||||
|
@ -33,7 +35,7 @@ class ServiceNotReadyException extends RuntimeException {
|
|||
this(service, message, null);
|
||||
}
|
||||
|
||||
ServiceNotReadyException(RunningService service, String message, Throwable cause) {
|
||||
ServiceNotReadyException(RunningService service, String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
this.service = service;
|
||||
}
|
||||
|
|
|
@ -17,4 +17,7 @@
|
|||
/**
|
||||
* Lifecycle management for Docker Compose with the context of a Spring application.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.docker.compose.lifecycle;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.util.Arrays;
|
|||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
|
||||
import org.springframework.boot.docker.compose.core.DockerComposeFile;
|
||||
|
@ -92,7 +94,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
}
|
||||
|
||||
@Override
|
||||
public final D getConnectionDetails(DockerComposeConnectionSource source) {
|
||||
public final @Nullable D getConnectionDetails(DockerComposeConnectionSource source) {
|
||||
return (!accept(source)) ? null : getDockerComposeConnectionDetails(source);
|
||||
}
|
||||
|
||||
|
@ -112,7 +114,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
* @param source the source
|
||||
* @return the service connection or {@code null}.
|
||||
*/
|
||||
protected abstract D getDockerComposeConnectionDetails(DockerComposeConnectionSource source);
|
||||
protected abstract @Nullable D getDockerComposeConnectionDetails(DockerComposeConnectionSource source);
|
||||
|
||||
/**
|
||||
* Convenient base class for {@link ConnectionDetails} results that are backed by a
|
||||
|
@ -120,9 +122,9 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
*/
|
||||
protected static class DockerComposeConnectionDetails implements ConnectionDetails, OriginProvider {
|
||||
|
||||
private final Origin origin;
|
||||
private final @Nullable Origin origin;
|
||||
|
||||
private volatile SslBundle sslBundle;
|
||||
private volatile @Nullable SslBundle sslBundle;
|
||||
|
||||
/**
|
||||
* Create a new {@link DockerComposeConnectionDetails} instance.
|
||||
|
@ -134,11 +136,11 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
}
|
||||
|
||||
@Override
|
||||
public Origin getOrigin() {
|
||||
public @Nullable Origin getOrigin() {
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
protected SslBundle getSslBundle(RunningService service) {
|
||||
protected @Nullable SslBundle getSslBundle(RunningService service) {
|
||||
if (this.sslBundle != null) {
|
||||
return this.sslBundle;
|
||||
}
|
||||
|
@ -155,7 +157,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
return sslBundle;
|
||||
}
|
||||
|
||||
private SslBundle getJksSslBundle(RunningService service) {
|
||||
private @Nullable SslBundle getJksSslBundle(RunningService service) {
|
||||
JksSslStoreDetails keyStoreDetails = getJksSslStoreDetails(service, "keystore");
|
||||
JksSslStoreDetails trustStoreDetails = getJksSslStoreDetails(service, "truststore");
|
||||
if (keyStoreDetails == null && trustStoreDetails == null) {
|
||||
|
@ -173,13 +175,13 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
options, protocol);
|
||||
}
|
||||
|
||||
private ResourceLoader getResourceLoader(Path workingDirectory) {
|
||||
private ResourceLoader getResourceLoader(@Nullable Path workingDirectory) {
|
||||
ClassLoader classLoader = ApplicationResourceLoader.get().getClassLoader();
|
||||
return ApplicationResourceLoader.get(classLoader,
|
||||
SpringFactoriesLoader.forDefaultResourceLocation(classLoader), workingDirectory);
|
||||
}
|
||||
|
||||
private JksSslStoreDetails getJksSslStoreDetails(RunningService service, String storeType) {
|
||||
private @Nullable JksSslStoreDetails getJksSslStoreDetails(RunningService service, String storeType) {
|
||||
String type = service.labels().get("org.springframework.boot.sslbundle.jks.%s.type".formatted(storeType));
|
||||
String provider = service.labels()
|
||||
.get("org.springframework.boot.sslbundle.jks.%s.provider".formatted(storeType));
|
||||
|
@ -193,7 +195,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
return new JksSslStoreDetails(type, provider, location, password);
|
||||
}
|
||||
|
||||
private Path getWorkingDirectory(RunningService runningService) {
|
||||
private @Nullable Path getWorkingDirectory(RunningService runningService) {
|
||||
DockerComposeFile composeFile = runningService.composeFile();
|
||||
if (composeFile == null || CollectionUtils.isEmpty(composeFile.getFiles())) {
|
||||
return Path.of(".");
|
||||
|
@ -201,7 +203,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
return composeFile.getFiles().get(0).toPath().getParent();
|
||||
}
|
||||
|
||||
private SslOptions createSslOptions(String ciphers, String enabledProtocols) {
|
||||
private SslOptions createSslOptions(@Nullable String ciphers, @Nullable String enabledProtocols) {
|
||||
Set<String> ciphersSet = null;
|
||||
if (StringUtils.hasLength(ciphers)) {
|
||||
ciphersSet = StringUtils.commaDelimitedListToSet(ciphers);
|
||||
|
@ -213,7 +215,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
return SslOptions.of(ciphersSet, enabledProtocolsSet);
|
||||
}
|
||||
|
||||
private SslBundle getPemSslBundle(RunningService service) {
|
||||
private @Nullable SslBundle getPemSslBundle(RunningService service) {
|
||||
PemSslStoreDetails keyStoreDetails = getPemSslStoreDetails(service, "keystore");
|
||||
PemSslStoreDetails trustStoreDetails = getPemSslStoreDetails(service, "truststore");
|
||||
if (keyStoreDetails == null && trustStoreDetails == null) {
|
||||
|
@ -231,7 +233,7 @@ public abstract class DockerComposeConnectionDetailsFactory<D extends Connection
|
|||
PemSslStore.load(trustStoreDetails, resourceLoader)), key, options, protocol);
|
||||
}
|
||||
|
||||
private PemSslStoreDetails getPemSslStoreDetails(RunningService service, String storeType) {
|
||||
private @Nullable PemSslStoreDetails getPemSslStoreDetails(RunningService service, String storeType) {
|
||||
String type = service.labels().get("org.springframework.boot.sslbundle.pem.%s.type".formatted(storeType));
|
||||
String certificate = service.labels()
|
||||
.get("org.springframework.boot.sslbundle.pem.%s.certificate".formatted(storeType));
|
||||
|
|
|
@ -17,4 +17,7 @@
|
|||
/**
|
||||
* Service connection support for Docker Compose.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.docker.compose.service.connection;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.docker.compose.core.DockerCliCommand.ComposeVersion;
|
||||
import org.springframework.boot.docker.compose.core.DockerCliCommand.None;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -85,7 +86,7 @@ class DockerCliCommandTests {
|
|||
assertThat(command.getLogLevel()).isEqualTo(LogLevel.INFO);
|
||||
assertThat(command.getCommand(COMPOSE_VERSION)).containsExactly("up", "--no-color", "--detach", "--wait",
|
||||
"--renew-anon-volumes");
|
||||
assertThat(command.deserialize("[]")).isNull();
|
||||
assertThat(command.deserialize("[]")).isSameAs(None.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -94,7 +95,7 @@ class DockerCliCommandTests {
|
|||
List.of("--remove-orphans"));
|
||||
assertThat(command.getType()).isEqualTo(DockerCliCommand.Type.DOCKER_COMPOSE);
|
||||
assertThat(command.getCommand(COMPOSE_VERSION)).containsExactly("down", "--timeout", "1", "--remove-orphans");
|
||||
assertThat(command.deserialize("[]")).isNull();
|
||||
assertThat(command.deserialize("[]")).isSameAs(None.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -103,7 +104,7 @@ class DockerCliCommandTests {
|
|||
assertThat(command.getType()).isEqualTo(DockerCliCommand.Type.DOCKER_COMPOSE);
|
||||
assertThat(command.getLogLevel()).isEqualTo(LogLevel.INFO);
|
||||
assertThat(command.getCommand(COMPOSE_VERSION)).containsExactly("start", "--dry-run");
|
||||
assertThat(command.deserialize("[]")).isNull();
|
||||
assertThat(command.deserialize("[]")).isSameAs(None.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -111,7 +112,7 @@ class DockerCliCommandTests {
|
|||
DockerCliCommand<?> command = new DockerCliCommand.ComposeStop(Duration.ofSeconds(1), List.of("--dry-run"));
|
||||
assertThat(command.getType()).isEqualTo(DockerCliCommand.Type.DOCKER_COMPOSE);
|
||||
assertThat(command.getCommand(COMPOSE_VERSION)).containsExactly("stop", "--timeout", "1", "--dry-run");
|
||||
assertThat(command.deserialize("[]")).isNull();
|
||||
assertThat(command.deserialize("[]")).isSameAs(None.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue