diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/AbstractBuildLog.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/AbstractBuildLog.java index 67feb15433b..5c19d6e0944 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/AbstractBuildLog.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/AbstractBuildLog.java @@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build; import java.util.List; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent; import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent; import org.springframework.boot.buildpack.platform.docker.type.Binding; @@ -45,7 +47,7 @@ public abstract class AbstractBuildLog implements BuildLog { } @Override - public Consumer pullingImage(ImageReference imageReference, ImagePlatform platform, + public Consumer pullingImage(ImageReference imageReference, @Nullable ImagePlatform platform, ImageType imageType) { return (platform != null) ? getProgressConsumer(" > Pulling %s '%s' for platform '%s'".formatted(imageType.getDescription(), @@ -109,7 +111,7 @@ public abstract class AbstractBuildLog implements BuildLog { } @Override - public void failedCleaningWorkDir(Cache cache, Exception exception) { + public void failedCleaningWorkDir(Cache cache, @Nullable Exception exception) { StringBuilder message = new StringBuilder("Warning: Working location " + cache + " could not be cleaned"); if (exception != null) { message.append(": ").append(exception.getMessage()); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ApiVersions.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ApiVersions.java index fcd9458c8c2..a9549648025 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ApiVersions.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ApiVersions.java @@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build; import java.util.Arrays; import java.util.stream.IntStream; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.ApiVersion; import org.springframework.util.StringUtils; @@ -62,7 +64,7 @@ final class ApiVersions { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java index 453d570f72f..36e95910e8c 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java @@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build; import java.io.PrintStream; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent; import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent; import org.springframework.boot.buildpack.platform.docker.type.Binding; @@ -52,7 +54,7 @@ public interface BuildLog { * @param imageType the image type * @return a consumer for progress update events */ - Consumer pullingImage(ImageReference imageReference, ImagePlatform platform, + Consumer pullingImage(ImageReference imageReference, @Nullable ImagePlatform platform, ImageType imageType); /** @@ -124,7 +126,7 @@ public interface BuildLog { * @param exception any exception that caused the failure * @since 3.2.6 */ - void failedCleaningWorkDir(Cache cache, Exception exception); + void failedCleaningWorkDir(Cache cache, @Nullable Exception exception); /** * Log that a binding with a sensitive target has been detected. diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java index efe7dabd9fa..69987467260 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.Binding; import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform; import org.springframework.boot.buildpack.platform.docker.type.ImageReference; @@ -69,9 +71,9 @@ public class BuildRequest { private final ImageReference builder; - private final Boolean trustBuilder; + private final @Nullable Boolean trustBuilder; - private final ImageReference runImage; + private final @Nullable ImageReference runImage; private final Creator creator; @@ -89,23 +91,23 @@ public class BuildRequest { private final List bindings; - private final String network; + private final @Nullable String network; private final List tags; - private final Cache buildWorkspace; + private final @Nullable Cache buildWorkspace; - private final Cache buildCache; + private final @Nullable Cache buildCache; - private final Cache launchCache; + private final @Nullable Cache launchCache; - private final Instant createdDate; + private final @Nullable Instant createdDate; - private final String applicationDirectory; + private final @Nullable String applicationDirectory; - private final List securityOptions; + private final @Nullable List securityOptions; - private final ImagePlatform platform; + private final @Nullable ImagePlatform platform; BuildRequest(ImageReference name, Function applicationContent) { Assert.notNull(name, "'name' must not be null"); @@ -135,11 +137,12 @@ public class BuildRequest { } BuildRequest(ImageReference name, Function applicationContent, ImageReference builder, - Boolean trustBuilder, ImageReference runImage, Creator creator, Map env, boolean cleanCache, - boolean verboseLogging, PullPolicy pullPolicy, boolean publish, List buildpacks, - List bindings, String network, List tags, Cache buildWorkspace, Cache buildCache, - Cache launchCache, Instant createdDate, String applicationDirectory, List securityOptions, - ImagePlatform platform) { + @Nullable Boolean trustBuilder, @Nullable ImageReference runImage, Creator creator, Map env, + boolean cleanCache, boolean verboseLogging, PullPolicy pullPolicy, boolean publish, + List buildpacks, List bindings, @Nullable String network, + List tags, @Nullable Cache buildWorkspace, @Nullable Cache buildCache, + @Nullable Cache launchCache, @Nullable Instant createdDate, @Nullable String applicationDirectory, + @Nullable List securityOptions, @Nullable ImagePlatform platform) { this.name = name; this.applicationContent = applicationContent; this.builder = builder; @@ -538,7 +541,7 @@ public class BuildRequest { * Return the run image that should be used, if provided. * @return the run image */ - public ImageReference getRunImage() { + public @Nullable ImageReference getRunImage() { return this.runImage; } @@ -612,7 +615,7 @@ public class BuildRequest { * @return the network * @since 2.6.0 */ - public String getNetwork() { + public @Nullable String getNetwork() { return this.network; } @@ -629,7 +632,7 @@ public class BuildRequest { * @return the build workspace or {@code null} * @since 3.2.0 */ - public Cache getBuildWorkspace() { + public @Nullable Cache getBuildWorkspace() { return this.buildWorkspace; } @@ -637,7 +640,7 @@ public class BuildRequest { * Return the custom build cache that should be used by the lifecycle. * @return the build cache */ - public Cache getBuildCache() { + public @Nullable Cache getBuildCache() { return this.buildCache; } @@ -645,7 +648,7 @@ public class BuildRequest { * Return the custom launch cache that should be used by the lifecycle. * @return the launch cache */ - public Cache getLaunchCache() { + public @Nullable Cache getLaunchCache() { return this.launchCache; } @@ -653,7 +656,7 @@ public class BuildRequest { * Return the custom created date that should be used by the lifecycle. * @return the created date */ - public Instant getCreatedDate() { + public @Nullable Instant getCreatedDate() { return this.createdDate; } @@ -661,7 +664,7 @@ public class BuildRequest { * Return the application directory that should be used by the lifecycle. * @return the application directory */ - public String getApplicationDirectory() { + public @Nullable String getApplicationDirectory() { return this.applicationDirectory; } @@ -670,7 +673,7 @@ public class BuildRequest { * @return the security options or {@code null} * @since 3.2.0 */ - public List getSecurityOptions() { + public @Nullable List getSecurityOptions() { return this.securityOptions; } @@ -679,7 +682,7 @@ public class BuildRequest { * @return the platform or {@code null} * @since 3.4.0 */ - public ImagePlatform getImagePlatform() { + public @Nullable ImagePlatform getImagePlatform() { return this.platform; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java index 11ffbf21cde..4e4aab00110 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java @@ -20,6 +20,8 @@ import java.io.IOException; import java.util.List; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.DockerApi; import org.springframework.boot.buildpack.platform.docker.DockerLog; import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent; @@ -86,12 +88,12 @@ public class Builder { * @param dockerConfiguration the docker configuration * @since 3.5.0 */ - public Builder(BuildLog log, BuilderDockerConfiguration dockerConfiguration) { + public Builder(BuildLog log, @Nullable BuilderDockerConfiguration dockerConfiguration) { this(log, new DockerApi((dockerConfiguration != null) ? dockerConfiguration.connection() : null, BuildLogAdapter.get(log)), dockerConfiguration); } - Builder(BuildLog log, DockerApi docker, BuilderDockerConfiguration dockerConfiguration) { + Builder(BuildLog log, DockerApi docker, @Nullable BuilderDockerConfiguration dockerConfiguration) { Assert.notNull(log, "'log' must not be null"); this.log = log; this.docker = docker; @@ -109,6 +111,7 @@ public class Builder { Image builderImage = imageFetcher.fetchImage(ImageType.BUILDER, request.getBuilder()); BuilderMetadata builderMetadata = BuilderMetadata.fromImage(builderImage); request = withRunImageIfNeeded(request, builderMetadata); + Assert.state(request.getRunImage() != null, "'request.getRunImage()' must not be null"); Image runImage = imageFetcher.fetchImage(ImageType.RUNNER, request.getRunImage()); assertStackIdsMatch(runImage, builderImage); BuildOwner buildOwner = BuildOwner.fromEnv(builderImage.getConfig().getEnv()); @@ -181,7 +184,7 @@ public class Builder { } } - private ResolvedDockerHost getDockerHost() { + private @Nullable ResolvedDockerHost getDockerHost() { boolean bindToBuilder = this.dockerConfiguration.bindHostToBuilder(); return (bindToBuilder) ? ResolvedDockerHost.from(this.dockerConfiguration.connection()) : null; } @@ -208,7 +211,8 @@ public class Builder { this.log.pushedImage(reference); } - private static String authHeader(DockerRegistryAuthentication authentication, ImageReference reference) { + private static @Nullable String authHeader(@Nullable DockerRegistryAuthentication authentication, + ImageReference reference) { return (authentication != null) ? authentication.getAuthHeader(reference) : null; } @@ -217,14 +221,14 @@ public class Builder { */ private class ImageFetcher { - private final DockerRegistryAuthentication registryAuthentication; + private final @Nullable DockerRegistryAuthentication registryAuthentication; private final PullPolicy pullPolicy; - private ImagePlatform defaultPlatform; + private @Nullable ImagePlatform defaultPlatform; - ImageFetcher(DockerRegistryAuthentication registryAuthentication, PullPolicy pullPolicy, - ImagePlatform platform) { + ImageFetcher(@Nullable DockerRegistryAuthentication registryAuthentication, PullPolicy pullPolicy, + @Nullable ImagePlatform platform) { this.registryAuthentication = registryAuthentication; this.pullPolicy = pullPolicy; this.defaultPlatform = platform; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderBuildpack.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderBuildpack.java index 57320575c7a..c7a893b67fa 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderBuildpack.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderBuildpack.java @@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build; import java.io.IOException; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.Layer; import org.springframework.boot.buildpack.platform.io.IOConsumer; import org.springframework.util.Assert; @@ -59,10 +61,10 @@ class BuilderBuildpack implements Buildpack { * @param reference the buildpack reference * @return the resolved {@link Buildpack} or {@code null} */ - static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { + static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { boolean unambiguous = reference.hasPrefix(PREFIX); BuilderReference builderReference = BuilderReference - .of(unambiguous ? reference.getSubReference(PREFIX) : reference.toString()); + .of(unambiguous ? getSubReference(reference) : reference.toString()); BuildpackMetadata buildpackMetadata = findBuildpackMetadata(context, builderReference); if (unambiguous) { Assert.state(buildpackMetadata != null, () -> "Buildpack '" + reference + "' not found in builder"); @@ -70,7 +72,13 @@ class BuilderBuildpack implements Buildpack { return (buildpackMetadata != null) ? new BuilderBuildpack(buildpackMetadata) : null; } - private static BuildpackMetadata findBuildpackMetadata(BuildpackResolverContext context, + private static String getSubReference(BuildpackReference reference) { + String result = reference.getSubReference(PREFIX); + Assert.state(result != null, "'result' must not be null"); + return result; + } + + private static @Nullable BuildpackMetadata findBuildpackMetadata(BuildpackResolverContext context, BuilderReference builderReference) { for (BuildpackMetadata candidate : context.getBuildpackMetadata()) { if (builderReference.matches(candidate)) { @@ -87,9 +95,9 @@ class BuilderBuildpack implements Buildpack { private final String id; - private final String version; + private final @Nullable String version; - BuilderReference(String id, String version) { + BuilderReference(String id, @Nullable String version) { this.id = id; this.version = version; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderDockerConfiguration.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderDockerConfiguration.java index 831417c6547..6c4fc2717e4 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderDockerConfiguration.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderDockerConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.build; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration; import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication; @@ -32,9 +34,9 @@ import org.springframework.boot.buildpack.platform.docker.configuration.DockerRe * @author Scott Frederick * @since 3.5.0 */ -public record BuilderDockerConfiguration(DockerConnectionConfiguration connection, boolean bindHostToBuilder, - DockerRegistryAuthentication builderRegistryAuthentication, - DockerRegistryAuthentication publishRegistryAuthentication) { +public record BuilderDockerConfiguration(@Nullable DockerConnectionConfiguration connection, boolean bindHostToBuilder, + @Nullable DockerRegistryAuthentication builderRegistryAuthentication, + @Nullable DockerRegistryAuthentication publishRegistryAuthentication) { public BuilderDockerConfiguration() { this(null, false, null, null); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderMetadata.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderMetadata.java index c8752a45ea2..7796fcef532 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderMetadata.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuilderMetadata.java @@ -26,6 +26,7 @@ import java.util.function.Consumer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageConfig; @@ -59,13 +60,31 @@ class BuilderMetadata extends MappedObject { BuilderMetadata(JsonNode node) { super(node, MethodHandles.lookup()); - this.stack = valueAt("/stack", Stack.class); + this.stack = extractStack(); this.runImages = childrenAt("/images", RunImage::new); - this.lifecycle = valueAt("/lifecycle", Lifecycle.class); - this.createdBy = valueAt("/createdBy", CreatedBy.class); + this.lifecycle = extractLifecycle(); + this.createdBy = extractCreatedBy(); this.buildpacks = extractBuildpacks(getNode().at("/buildpacks")); } + private CreatedBy extractCreatedBy() { + CreatedBy result = valueAt("/createdBy", CreatedBy.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + + private Lifecycle extractLifecycle() { + Lifecycle result = valueAt("/lifecycle", Lifecycle.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + + private Stack extractStack() { + Stack result = valueAt("/stack", Stack.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + private List extractBuildpacks(JsonNode node) { if (node.isEmpty()) { return Collections.emptyList(); @@ -219,10 +238,16 @@ class BuilderMetadata extends MappedObject { */ RunImage(JsonNode node) { super(node, MethodHandles.lookup()); - this.image = valueAt("/image", String.class); + this.image = extractImage(); this.mirrors = childrenAt("/mirrors", JsonNode::asText); } + private String extractImage() { + String result = valueAt("/image", String.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + String getImage() { return this.image; } @@ -284,7 +309,7 @@ class BuilderMetadata extends MappedObject { * Return the supported buildpack API versions. * @return the buildpack versions */ - default String[] getBuildpack() { + default String @Nullable [] getBuildpack() { return valueAt(this, "/buildpack/supported", String[].class); } @@ -292,7 +317,7 @@ class BuilderMetadata extends MappedObject { * Return the supported platform API versions. * @return the platform versions */ - default String[] getPlatform() { + default String @Nullable [] getPlatform() { return valueAt(this, "/platform/supported", String[].class); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackCoordinates.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackCoordinates.java index c7fd33c351d..91373ccb9ae 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackCoordinates.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackCoordinates.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; +import org.jspecify.annotations.Nullable; import org.tomlj.Toml; import org.tomlj.TomlParseResult; @@ -39,9 +40,9 @@ final class BuildpackCoordinates { private final String id; - private final String version; + private final @Nullable String version; - private BuildpackCoordinates(String id, String version) { + private BuildpackCoordinates(String id, @Nullable String version) { Assert.hasText(id, "'id' must not be empty"); this.id = id; this.version = version; @@ -59,12 +60,12 @@ final class BuildpackCoordinates { return this.id.replace("/", "_"); } - String getVersion() { + @Nullable String getVersion() { return this.version; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackLayersMetadata.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackLayersMetadata.java index b94f9ee5d80..3fdaaec007b 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackLayersMetadata.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackLayersMetadata.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.databind.JsonNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageConfig; @@ -53,7 +54,7 @@ final class BuildpackLayersMetadata extends MappedObject { * @return the buildpack details or {@code null} if a buildpack with the given ID and * version does not exist in the metadata */ - BuildpackLayerDetails getBuildpack(String id, String version) { + @Nullable BuildpackLayerDetails getBuildpack(String id, @Nullable String version) { return this.buildpacks.getBuildpack(id, version); } @@ -105,7 +106,7 @@ final class BuildpackLayersMetadata extends MappedObject { private final Map buildpacks = new HashMap<>(); - private BuildpackLayerDetails getBuildpack(String id, String version) { + private @Nullable BuildpackLayerDetails getBuildpack(String id, @Nullable String version) { if (this.buildpacks.containsKey(id)) { return this.buildpacks.get(id).getBuildpack(version); } @@ -130,7 +131,7 @@ final class BuildpackLayersMetadata extends MappedObject { private final Map versions = new HashMap<>(); - private BuildpackLayerDetails getBuildpack(String version) { + private @Nullable BuildpackLayerDetails getBuildpack(@Nullable String version) { return this.versions.get(version); } @@ -150,11 +151,11 @@ final class BuildpackLayersMetadata extends MappedObject { static final class BuildpackLayerDetails extends MappedObject { - private final String name; + private final @Nullable String name; - private final String homepage; + private final @Nullable String homepage; - private final String layerDiffId; + private final @Nullable String layerDiffId; private BuildpackLayerDetails(JsonNode node) { super(node, MethodHandles.lookup()); @@ -167,7 +168,7 @@ final class BuildpackLayersMetadata extends MappedObject { * Return the buildpack name. * @return the name */ - String getName() { + @Nullable String getName() { return this.name; } @@ -175,7 +176,7 @@ final class BuildpackLayersMetadata extends MappedObject { * Return the buildpack homepage address. * @return the homepage address */ - String getHomepage() { + @Nullable String getHomepage() { return this.homepage; } @@ -183,7 +184,7 @@ final class BuildpackLayersMetadata extends MappedObject { * Return the buildpack layer {@code diffID}. * @return the layer {@code diffID} */ - String getLayerDiffId() { + @Nullable String getLayerDiffId() { return this.layerDiffId; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackMetadata.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackMetadata.java index fa81188b8e3..06745c37df7 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackMetadata.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackMetadata.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.invoke.MethodHandles; import com.fasterxml.jackson.databind.JsonNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageConfig; @@ -39,17 +40,23 @@ final class BuildpackMetadata extends MappedObject { private final String id; - private final String version; + private final @Nullable String version; - private final String homepage; + private final @Nullable String homepage; private BuildpackMetadata(JsonNode node) { super(node, MethodHandles.lookup()); - this.id = valueAt("/id", String.class); + this.id = extractId(); this.version = valueAt("/version", String.class); this.homepage = valueAt("/homepage", String.class); } + private String extractId() { + String result = valueAt("/id", String.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + /** * Return the buildpack ID. * @return the ID @@ -62,7 +69,7 @@ final class BuildpackMetadata extends MappedObject { * Return the buildpack version. * @return the version */ - String getVersion() { + @Nullable String getVersion() { return this.version; } @@ -70,7 +77,7 @@ final class BuildpackMetadata extends MappedObject { * Return the buildpack homepage address. * @return the homepage */ - String getHomepage() { + @Nullable String getHomepage() { return this.homepage; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackReference.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackReference.java index 8aac115ee04..612260c9513 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackReference.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackReference.java @@ -22,6 +22,8 @@ import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -44,11 +46,11 @@ public final class BuildpackReference { return this.value.startsWith(prefix); } - String getSubReference(String prefix) { + @Nullable String getSubReference(String prefix) { return this.value.startsWith(prefix) ? this.value.substring(prefix.length()) : null; } - Path asPath() { + @Nullable Path asPath() { try { URL url = new URL(this.value); if (url.getProtocol().equals("file")) { @@ -68,7 +70,7 @@ public final class BuildpackReference { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackResolver.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackResolver.java index cde9d50b40d..bab39d26720 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackResolver.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackResolver.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.build; +import org.jspecify.annotations.Nullable; + /** * Strategy interface used to resolve a {@link BuildpackReference} to a {@link Buildpack}. * @@ -31,6 +33,6 @@ interface BuildpackResolver { * @param reference the reference to resolve * @return a resolved {@link Buildpack} instance or {@code null} */ - Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference); + @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Cache.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Cache.java index e67d5a6de99..a3e4f98a815 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Cache.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Cache.java @@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build; import java.util.Objects; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.VolumeName; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -67,7 +69,7 @@ public class Cache { * Return the details of the cache if it is a volume cache. * @return the cache, or {@code null} if it is not a volume cache */ - public Volume getVolume() { + public @Nullable Volume getVolume() { return (this.format.equals(Format.VOLUME)) ? (Volume) this : null; } @@ -75,7 +77,7 @@ public class Cache { * Return the details of the cache if it is a bind cache. * @return the cache, or {@code null} if it is not a bind cache */ - public Bind getBind() { + public @Nullable Bind getBind() { return (this.format.equals(Format.BIND)) ? (Bind) this : null; } @@ -110,7 +112,7 @@ public class Cache { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -147,7 +149,7 @@ public class Cache { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -192,7 +194,7 @@ public class Cache { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpack.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpack.java index b19f623ea9f..14d472116c3 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpack.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpack.java @@ -25,6 +25,8 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.Layer; import org.springframework.boot.buildpack.platform.io.Content; import org.springframework.boot.buildpack.platform.io.FilePermissions; @@ -99,7 +101,7 @@ final class DirectoryBuildpack implements Buildpack { * @param reference the buildpack reference * @return the resolved {@link Buildpack} or {@code null} */ - static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { + static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { Path path = reference.asPath(); if (path != null && Files.exists(path) && Files.isDirectory(path)) { return new DirectoryBuildpack(path); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java index 8e5f1bccfb4..1c78bc3376e 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java @@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build; import java.io.IOException; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageArchive; import org.springframework.boot.buildpack.platform.docker.type.ImageArchive.Update; @@ -63,7 +65,8 @@ class EphemeralBuilder { * @param buildpacks an optional set of buildpacks to apply */ EphemeralBuilder(BuildOwner buildOwner, Image builderImage, ImageReference targetImage, - BuilderMetadata builderMetadata, Creator creator, Map env, Buildpacks buildpacks) { + BuilderMetadata builderMetadata, Creator creator, Map env, + @Nullable Buildpacks buildpacks) { this.name = ImageReference.random("pack.local/builder/").inTaggedForm(); this.buildOwner = buildOwner; this.creator = creator; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java index 1714ee0994a..13c09e95b65 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java @@ -28,6 +28,7 @@ import java.util.List; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.build.BuildpackLayersMetadata.BuildpackLayerDetails; import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException; @@ -37,6 +38,7 @@ import org.springframework.boot.buildpack.platform.docker.type.Layer; import org.springframework.boot.buildpack.platform.docker.type.LayerId; import org.springframework.boot.buildpack.platform.io.IOConsumer; import org.springframework.boot.buildpack.platform.io.TarArchive; +import org.springframework.util.Assert; import org.springframework.util.StreamUtils; /** @@ -54,7 +56,7 @@ final class ImageBuildpack implements Buildpack { private final BuildpackCoordinates coordinates; - private final ExportedLayers exportedLayers; + private final @Nullable ExportedLayers exportedLayers; private ImageBuildpack(BuildpackResolverContext context, ImageReference imageReference) { ImageReference reference = imageReference.inTaggedOrDigestForm(); @@ -95,11 +97,18 @@ final class ImageBuildpack implements Buildpack { * @param reference the buildpack reference * @return the resolved {@link Buildpack} or {@code null} */ - static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { + static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { boolean unambiguous = reference.hasPrefix(PREFIX); try { - ImageReference imageReference = ImageReference - .of((unambiguous) ? reference.getSubReference(PREFIX) : reference.toString()); + ImageReference imageReference; + if (unambiguous) { + String subReference = reference.getSubReference(PREFIX); + Assert.state(subReference != null, "'subReference' must not be null"); + imageReference = ImageReference.of(subReference); + } + else { + imageReference = ImageReference.of(reference.toString()); + } return new ImageBuildpack(context, imageReference); } catch (IllegalArgumentException ex) { diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java index 74a02fd408f..55108b63f8e 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java @@ -25,7 +25,9 @@ import java.util.List; import java.util.function.Consumer; import com.sun.jna.Platform; +import org.jspecify.annotations.Nullable; +import org.springframework.boot.buildpack.platform.build.Cache.Bind; import org.springframework.boot.buildpack.platform.docker.DockerApi; import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent; import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost; @@ -66,7 +68,7 @@ class Lifecycle implements Closeable { private final DockerApi docker; - private final ResolvedDockerHost dockerHost; + private final @Nullable ResolvedDockerHost dockerHost; private final BuildRequest request; @@ -100,7 +102,7 @@ class Lifecycle implements Closeable { * @param request the request to process * @param builder the ephemeral builder used to run the phases */ - Lifecycle(BuildLog log, DockerApi docker, ResolvedDockerHost dockerHost, BuildRequest request, + Lifecycle(BuildLog log, DockerApi docker, @Nullable ResolvedDockerHost dockerHost, BuildRequest request, EphemeralBuilder builder) { this.log = log; this.docker = docker; @@ -189,7 +191,9 @@ class Lifecycle implements Closeable { phase.withApp(this.applicationDirectory, Binding.from(getCacheBindingSource(this.application), this.applicationDirectory)); phase.withPlatform(Directory.PLATFORM); - phase.withRunImage(this.request.getRunImage()); + ImageReference runImage = this.request.getRunImage(); + Assert.state(runImage != null, "'runImage' must not be null"); + phase.withRunImage(runImage); phase.withLayers(Directory.LAYERS, Binding.from(getCacheBindingSource(this.layers), Directory.LAYERS)); phase.withBuildCache(Directory.CACHE, Binding.from(getCacheBindingSource(this.buildCache), Directory.CACHE)); phase.withLaunchCache(Directory.LAUNCH_CACHE, @@ -214,7 +218,9 @@ class Lifecycle implements Closeable { phase.withLaunchCache(Directory.LAUNCH_CACHE, Binding.from(getCacheBindingSource(this.launchCache), Directory.LAUNCH_CACHE)); phase.withLayers(Directory.LAYERS, Binding.from(getCacheBindingSource(this.layers), Directory.LAYERS)); - phase.withRunImage(this.request.getRunImage()); + ImageReference runImage = this.request.getRunImage(); + Assert.state(runImage != null, "'runImage' must not be null"); + phase.withRunImage(runImage); phase.withImageName(this.request.getName()); configureOptions(phase); return phase; @@ -282,12 +288,22 @@ class Lifecycle implements Closeable { } private Cache getBuildWorkspaceBindingSource(Cache buildWorkspace, String suffix) { - return (buildWorkspace.getVolume() != null) ? Cache.volume(buildWorkspace.getVolume().getName() + "-" + suffix) - : Cache.bind(buildWorkspace.getBind().getSource() + "-" + suffix); + if (buildWorkspace.getVolume() != null) { + return Cache.volume(buildWorkspace.getVolume().getName() + "-" + suffix); + } + + Bind bind = buildWorkspace.getBind(); + Assert.state(bind != null, "'bind' must not be null"); + return Cache.bind(bind.getSource() + "-" + suffix); } private String getCacheBindingSource(Cache cache) { - return (cache.getVolume() != null) ? cache.getVolume().getName() : cache.getBind().getSource(); + if (cache.getVolume() != null) { + return cache.getVolume().getName(); + } + Bind bind = cache.getBind(); + Assert.state(bind != null, "'bind' must not be null"); + return bind.getSource(); } private Cache createVolumeCache(String prefix) { @@ -310,7 +326,9 @@ class Lifecycle implements Closeable { phase.withEnv("DOCKER_HOST", this.dockerHost.getAddress()); if (this.dockerHost.isSecure()) { phase.withEnv("DOCKER_TLS_VERIFY", "1"); - phase.withEnv("DOCKER_CERT_PATH", this.dockerHost.getCertificatePath()); + if (this.dockerHost.getCertificatePath() != null) { + phase.withEnv("DOCKER_CERT_PATH", this.dockerHost.getCertificatePath()); + } } } else { diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/LifecycleVersion.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/LifecycleVersion.java index 23b1d2dfacd..bb90740e9a7 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/LifecycleVersion.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/LifecycleVersion.java @@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build; import java.util.Comparator; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -44,7 +46,7 @@ class LifecycleVersion implements Comparable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Phase.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Phase.java index 948e6c30642..d037d33b072 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Phase.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Phase.java @@ -22,6 +22,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.Binding; import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig; import org.springframework.boot.buildpack.platform.docker.type.ImageReference; @@ -48,7 +50,7 @@ class Phase { private final List securityOptions = new ArrayList<>(); - private String networkMode; + private @Nullable String networkMode; private boolean requiresApp; @@ -152,7 +154,7 @@ class Phase { * Update this phase with the network the build container will connect to. * @param networkMode the network */ - void withNetworkMode(String networkMode) { + void withNetworkMode(@Nullable String networkMode) { this.networkMode = networkMode; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/StackId.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/StackId.java index 641ecd4c63b..0f4937eee49 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/StackId.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/StackId.java @@ -16,6 +16,10 @@ package org.springframework.boot.buildpack.platform.build; +import java.util.Objects; + +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageConfig; import org.springframework.util.Assert; @@ -30,21 +34,21 @@ class StackId { private static final String LABEL_NAME = "io.buildpacks.stack.id"; - private final String value; + private final @Nullable String value; - StackId(String value) { + StackId(@Nullable String value) { this.value = value; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } - return this.value.equals(((StackId) obj).value); + return Objects.equals(this.value, ((StackId) obj).value); } boolean hasId() { @@ -53,12 +57,12 @@ class StackId { @Override public int hashCode() { - return this.value.hashCode(); + return Objects.hashCode(this.value); } @Override public String toString() { - return this.value; + return (this.value != null) ? this.value : ""; } /** diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java index 23702dd9321..3b7f097202f 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java @@ -27,6 +27,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.type.Layer; import org.springframework.boot.buildpack.platform.io.IOConsumer; @@ -118,7 +119,7 @@ final class TarGzipBuildpack implements Buildpack { * @param reference the buildpack reference * @return the resolved {@link Buildpack} or {@code null} */ - static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { + static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) { Path path = reference.asPath(); if (path != null && Files.exists(path) && Files.isRegularFile(path)) { return new TarGzipBuildpack(path); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/package-info.java index 0a9dac3bbc3..4a1a0cc06ee 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/package-info.java @@ -17,4 +17,7 @@ /** * Central API for performing a buildpack build. */ +@NullMarked package org.springframework.boot.buildpack.platform.build; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java index 8355940a990..6717f207bbd 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java @@ -27,7 +27,9 @@ import java.util.Objects; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.net.URIBuilder; +import org.jspecify.annotations.Nullable; +import org.springframework.boot.buildpack.platform.docker.PushImageUpdateEvent.ErrorDetail; import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration; import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport; import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response; @@ -81,7 +83,7 @@ public class DockerApi { private final SystemApi system; - private volatile ApiVersion apiVersion = null; + private volatile @Nullable ApiVersion apiVersion; /** * Create a new {@link DockerApi} instance. @@ -96,7 +98,7 @@ public class DockerApi { * @param log a logger used to record output * @since 3.5.0 */ - public DockerApi(DockerConnectionConfiguration connectionConfiguration, DockerLog log) { + public DockerApi(@Nullable DockerConnectionConfiguration connectionConfiguration, DockerLog log) { this(HttpTransport.create(connectionConfiguration), log); } @@ -125,7 +127,7 @@ public class DockerApi { return this.jsonStream; } - private URI buildUrl(String path, Collection params) { + private URI buildUrl(String path, @Nullable Collection params) { return buildUrl(API_VERSION, path, (params != null) ? params.toArray() : null); } @@ -133,7 +135,7 @@ public class DockerApi { return buildUrl(API_VERSION, path, params); } - private URI buildUrl(ApiVersion apiVersion, String path, Object... params) { + private URI buildUrl(ApiVersion apiVersion, String path, Object @Nullable ... params) { verifyApiVersion(apiVersion); try { URIBuilder builder = new URIBuilder("/v" + apiVersion + path); @@ -158,12 +160,10 @@ public class DockerApi { } private ApiVersion getApiVersion() { - ApiVersion apiVersion = this.apiVersion; if (this.apiVersion == null) { - apiVersion = this.system.getApiVersion(); - this.apiVersion = apiVersion; + this.apiVersion = this.system.getApiVersion(); } - return apiVersion; + return this.apiVersion; } /** @@ -220,8 +220,8 @@ public class DockerApi { * @return the {@link ImageApi pulled image} instance * @throws IOException on IO error */ - public Image pull(ImageReference reference, ImagePlatform platform, - UpdateListener listener, String registryAuth) throws IOException { + public Image pull(ImageReference reference, @Nullable ImagePlatform platform, + UpdateListener listener, @Nullable String registryAuth) throws IOException { Assert.notNull(reference, "'reference' must not be null"); Assert.notNull(listener, "'listener' must not be null"); URI createUri = (platform != null) @@ -250,8 +250,8 @@ public class DockerApi { * @param registryAuth registry authentication credentials * @throws IOException on IO error */ - public void push(ImageReference reference, UpdateListener listener, String registryAuth) - throws IOException { + public void push(ImageReference reference, UpdateListener listener, + @Nullable String registryAuth) throws IOException { Assert.notNull(reference, "'reference' must not be null"); Assert.notNull(listener, "'listener' must not be null"); URI pushUri = buildUrl("/images/" + reference + "/push"); @@ -375,8 +375,8 @@ public class DockerApi { * @return a {@link ContainerReference} for the newly created container * @throws IOException on IO error */ - public ContainerReference create(ContainerConfig config, ImagePlatform platform, ContainerContent... contents) - throws IOException { + public ContainerReference create(ContainerConfig config, @Nullable ImagePlatform platform, + ContainerContent... contents) throws IOException { Assert.notNull(config, "'config' must not be null"); Assert.noNullElements(contents, "'contents' must not contain null elements"); ContainerReference containerReference = createContainer(config, platform); @@ -386,7 +386,8 @@ public class DockerApi { return containerReference; } - private ContainerReference createContainer(ContainerConfig config, ImagePlatform platform) throws IOException { + private ContainerReference createContainer(ContainerConfig config, @Nullable ImagePlatform platform) + throws IOException { URI createUri = (platform != null) ? buildUrl(PLATFORM_API_VERSION, "/containers/create", "platform", platform) : buildUrl("/containers/create"); @@ -530,7 +531,7 @@ public class DockerApi { private static final String PREFIX = "Digest:"; - private String digest; + private @Nullable String digest; @Override public void onUpdate(ProgressUpdateEvent event) { @@ -551,7 +552,7 @@ public class DockerApi { private final ImageArchive archive; - private String stream; + private @Nullable String stream; private LoadImageUpdateListener(ImageArchive archive) { this.archive = archive; @@ -584,8 +585,11 @@ public class DockerApi { @Override public void onUpdate(PushImageUpdateEvent event) { - Assert.state(event.getErrorDetail() == null, - () -> "Error response received when pushing image: " + event.getErrorDetail().getMessage()); + ErrorDetail errorDetail = event.getErrorDetail(); + if (errorDetail != null) { + throw new IllegalStateException( + "Error response received when pushing image: " + errorDetail.getMessage()); + } } } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ExportedImageTar.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ExportedImageTar.java index 897e4915be1..3c9c7753903 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ExportedImageTar.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ExportedImageTar.java @@ -34,6 +34,7 @@ import java.util.stream.Stream; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.type.BlobReference; import org.springframework.boot.buildpack.platform.docker.type.ImageArchiveIndex; @@ -101,7 +102,7 @@ class ExportedImageTar implements Closeable { * @return a new {@link TarArchive} instance or {@code null} if this entry is not * a layer. */ - abstract TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry); + abstract @Nullable TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry); /** * Create a new {@link LayerArchiveFactory} for the given tar file using either @@ -129,8 +130,11 @@ class ExportedImageTar implements Closeable { Assert.state(index != null || manifest != null, () -> "Exported image '%s' does not contain 'index.json' or 'manifest.json'" .formatted(reference)); - return (index != null) ? new IndexLayerArchiveFactory(tarFile, index) - : new ManifestLayerArchiveFactory(tarFile, manifest); + if (index != null) { + return new IndexLayerArchiveFactory(tarFile, index); + } + Assert.state(manifest != null, "'manifest' must not be null"); + return new ManifestLayerArchiveFactory(tarFile, manifest); } } @@ -239,7 +243,7 @@ class ExportedImageTar implements Closeable { } @Override - TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) { + @Nullable TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) { String mediaType = this.layerMediaTypes.get(entry.getName()); if (mediaType == null) { return null; @@ -264,7 +268,7 @@ class ExportedImageTar implements Closeable { */ private static class ManifestLayerArchiveFactory extends LayerArchiveFactory { - private Set layers; + private final Set layers; ManifestLayerArchiveFactory(Path tarFile, ImageArchiveManifest manifest) { this.layers = manifest.getEntries() @@ -274,7 +278,7 @@ class ExportedImageTar implements Closeable { } @Override - TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) { + @Nullable TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) { if (!this.layers.contains(entry.getName())) { return null; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ImageProgressUpdateEvent.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ImageProgressUpdateEvent.java index d306b1b799a..07b36bde7c7 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ImageProgressUpdateEvent.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ImageProgressUpdateEvent.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.docker; +import org.jspecify.annotations.Nullable; + /** * A {@link ProgressUpdateEvent} fired for image events. * @@ -25,9 +27,10 @@ package org.springframework.boot.buildpack.platform.docker; */ public class ImageProgressUpdateEvent extends ProgressUpdateEvent { - private final String id; + private final @Nullable String id; - protected ImageProgressUpdateEvent(String id, String status, ProgressDetail progressDetail, String progress) { + protected ImageProgressUpdateEvent(@Nullable String id, String status, @Nullable ProgressDetail progressDetail, + @Nullable String progress) { super(status, progressDetail, progress); this.id = id; } @@ -36,7 +39,7 @@ public class ImageProgressUpdateEvent extends ProgressUpdateEvent { * Returns the ID of the image layer being updated if available. * @return the ID of the updated layer or {@code null} */ - public String getId() { + public @Nullable String getId() { return this.id; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LoadImageUpdateEvent.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LoadImageUpdateEvent.java index e6315844249..696a57c7d14 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LoadImageUpdateEvent.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LoadImageUpdateEvent.java @@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.jspecify.annotations.Nullable; /** * A {@link ProgressUpdateEvent} fired as an image is loaded. @@ -27,13 +28,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class LoadImageUpdateEvent extends ProgressUpdateEvent { - private final String stream; + private final @Nullable String stream; - private final ErrorDetail errorDetail; + private final @Nullable ErrorDetail errorDetail; @JsonCreator - public LoadImageUpdateEvent(String stream, String status, ProgressDetail progressDetail, String progress, - ErrorDetail errorDetail) { + public LoadImageUpdateEvent(@Nullable String stream, String status, ProgressDetail progressDetail, String progress, + @Nullable ErrorDetail errorDetail) { super(status, progressDetail, progress); this.stream = stream; this.errorDetail = errorDetail; @@ -43,7 +44,7 @@ public class LoadImageUpdateEvent extends ProgressUpdateEvent { * Return the stream response or {@code null} if no response is available. * @return the stream response. */ - public String getStream() { + public @Nullable String getStream() { return this.stream; } @@ -52,7 +53,7 @@ public class LoadImageUpdateEvent extends ProgressUpdateEvent { * @return the error detail, if any * @since 3.2.12 */ - public ErrorDetail getErrorDetail() { + public @Nullable ErrorDetail getErrorDetail() { return this.errorDetail; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LogUpdateEvent.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LogUpdateEvent.java index b7a6143fa5a..87796fd0503 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LogUpdateEvent.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/LogUpdateEvent.java @@ -22,6 +22,8 @@ import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.StreamUtils; @@ -80,7 +82,7 @@ public class LogUpdateEvent extends UpdateEvent { } } catch (IllegalStateException ex) { - byte[] message = ex.getMessage().getBytes(StandardCharsets.UTF_8); + byte[] message = (ex.getMessage() == null) ? new byte[0] : ex.getMessage().getBytes(StandardCharsets.UTF_8); consumer.accept(new LogUpdateEvent(StreamType.STD_ERR, message)); StreamUtils.drain(inputStream); } @@ -89,7 +91,7 @@ public class LogUpdateEvent extends UpdateEvent { } } - private static LogUpdateEvent read(InputStream inputStream) throws IOException { + private static @Nullable LogUpdateEvent read(InputStream inputStream) throws IOException { byte[] header = read(inputStream, 8); if (header == null) { return null; @@ -100,10 +102,11 @@ public class LogUpdateEvent extends UpdateEvent { size = (size << 8) + (header[i + 4] & 0xff); } byte[] payload = read(inputStream, size); + Assert.state(payload != null, "'payload' must not be null"); return new LogUpdateEvent(streamType, payload); } - private static byte[] read(InputStream inputStream, long size) throws IOException { + private static byte @Nullable [] read(InputStream inputStream, long size) throws IOException { byte[] data = new byte[(int) size]; int offset = 0; do { diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ProgressUpdateEvent.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ProgressUpdateEvent.java index cdbf1d3e8b0..7351f0ada4b 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ProgressUpdateEvent.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ProgressUpdateEvent.java @@ -17,6 +17,7 @@ package org.springframework.boot.buildpack.platform.docker; import com.fasterxml.jackson.annotation.JsonCreator; +import org.jspecify.annotations.Nullable; /** * An {@link UpdateEvent} that includes progress information. @@ -27,13 +28,14 @@ import com.fasterxml.jackson.annotation.JsonCreator; */ public abstract class ProgressUpdateEvent extends UpdateEvent { - private final String status; + private final @Nullable String status; - private final ProgressDetail progressDetail; + private final @Nullable ProgressDetail progressDetail; - private final String progress; + private final @Nullable String progress; - protected ProgressUpdateEvent(String status, ProgressDetail progressDetail, String progress) { + protected ProgressUpdateEvent(@Nullable String status, @Nullable ProgressDetail progressDetail, + @Nullable String progress) { this.status = status; this.progressDetail = (ProgressDetail.isEmpty(progressDetail)) ? null : progressDetail; this.progress = progress; @@ -43,7 +45,7 @@ public abstract class ProgressUpdateEvent extends UpdateEvent { * Return the status for the update. For example, "Extracting" or "Downloading". * @return the status of the update. */ - public String getStatus() { + public @Nullable String getStatus() { return this.status; } @@ -51,7 +53,7 @@ public abstract class ProgressUpdateEvent extends UpdateEvent { * Return progress details if available. * @return progress details or {@code null} */ - public ProgressDetail getProgressDetail() { + public @Nullable ProgressDetail getProgressDetail() { return this.progressDetail; } @@ -59,7 +61,7 @@ public abstract class ProgressUpdateEvent extends UpdateEvent { * Return a text based progress bar if progress information is available. * @return the progress bar or {@code null} */ - public String getProgress() { + public @Nullable String getProgress() { return this.progress; } @@ -68,12 +70,12 @@ public abstract class ProgressUpdateEvent extends UpdateEvent { */ public static class ProgressDetail { - private final Long current; + private final @Nullable Long current; - private final Long total; + private final @Nullable Long total; @JsonCreator - public ProgressDetail(Long current, Long total) { + public ProgressDetail(@Nullable Long current, @Nullable Long total) { this.current = current; this.total = total; } @@ -84,11 +86,14 @@ public abstract class ProgressUpdateEvent extends UpdateEvent { * @since 3.3.7 */ public int asPercentage() { + if (this.total == null || this.current == null) { + return 0; + } int percentage = (int) ((100.0 / this.total) * this.current); return (percentage < 0) ? 0 : Math.min(percentage, 100); } - private static boolean isEmpty(ProgressDetail progressDetail) { + private static boolean isEmpty(@Nullable ProgressDetail progressDetail) { return progressDetail == null || progressDetail.current == null || progressDetail.total == null; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/PushImageUpdateEvent.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/PushImageUpdateEvent.java index 4f0efb8a702..69d78158338 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/PushImageUpdateEvent.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/PushImageUpdateEvent.java @@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.jspecify.annotations.Nullable; /** * A {@link ProgressUpdateEvent} fired as an image is pushed to a registry. @@ -27,11 +28,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class PushImageUpdateEvent extends ImageProgressUpdateEvent { - private final ErrorDetail errorDetail; + private final @Nullable ErrorDetail errorDetail; @JsonCreator public PushImageUpdateEvent(String id, String status, ProgressDetail progressDetail, String progress, - ErrorDetail errorDetail) { + @Nullable ErrorDetail errorDetail) { super(id, status, progressDetail, progress); this.errorDetail = errorDetail; } @@ -40,7 +41,7 @@ public class PushImageUpdateEvent extends ImageProgressUpdateEvent { * Returns the details of any error encountered during processing. * @return the error */ - public ErrorDetail getErrorDetail() { + public @Nullable ErrorDetail getErrorDetail() { return this.errorDetail; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/Credential.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/Credential.java index 5f73802bfda..7248eeeff52 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/Credential.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/Credential.java @@ -19,8 +19,10 @@ package org.springframework.boot.buildpack.platform.docker.configuration; import java.lang.invoke.MethodHandles; import com.fasterxml.jackson.databind.JsonNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.MappedObject; +import org.springframework.util.Assert; /** * A class that represents credentials for a server as returned from a @@ -40,15 +42,27 @@ class Credential extends MappedObject { private final String secret; - private final String serverUrl; + private final @Nullable String serverUrl; Credential(JsonNode node) { super(node, MethodHandles.lookup()); - this.username = valueAt("/Username", String.class); - this.secret = valueAt("/Secret", String.class); + this.username = extractUsername(); + this.secret = extractSecret(); this.serverUrl = valueAt("/ServerURL", String.class); } + private String extractSecret() { + String result = valueAt("/Secret", String.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + + private String extractUsername() { + String result = valueAt("/Username", String.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + String getUsername() { return this.username; } @@ -57,7 +71,7 @@ class Credential extends MappedObject { return this.secret; } - String getServerUrl() { + @Nullable String getServerUrl() { return this.serverUrl; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/CredentialHelper.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/CredentialHelper.java index e6d11e92600..675c6f1f0d8 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/CredentialHelper.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/CredentialHelper.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Set; import com.sun.jna.Platform; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.SharedObjectMapper; @@ -48,7 +49,7 @@ class CredentialHelper { this.executable = executable; } - Credential get(String serverUrl) throws IOException { + @Nullable Credential get(String serverUrl) throws IOException { ProcessBuilder processBuilder = processBuilder("get"); Process process = start(processBuilder); try (OutputStream request = process.getOutputStream()) { diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConfigurationMetadata.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConfigurationMetadata.java index a131315af9d..26501ddc374 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConfigurationMetadata.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConfigurationMetadata.java @@ -31,6 +31,7 @@ import java.util.function.Supplier; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.NullNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.MappedObject; import org.springframework.boot.buildpack.platform.json.SharedObjectMapper; @@ -124,7 +125,7 @@ final class DockerConfigurationMetadata { } } - private static DockerContext createDockerContext(String configLocation, String currentContext) { + private static DockerContext createDockerContext(String configLocation, @Nullable String currentContext) { if (currentContext == null || DEFAULT_CONTEXT.equals(currentContext)) { return DockerContext.empty(); } @@ -145,7 +146,7 @@ final class DockerConfigurationMetadata { } } - private static String asHash(String currentContext) { + private static @Nullable String asHash(String currentContext) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(currentContext.getBytes(StandardCharsets.UTF_8)); @@ -167,9 +168,9 @@ final class DockerConfigurationMetadata { static final class DockerConfig extends MappedObject { - private final String currentContext; + private final @Nullable String currentContext; - private final String credsStore; + private final @Nullable String credsStore; private final Map credHelpers; @@ -183,11 +184,11 @@ final class DockerConfigurationMetadata { this.auths = mapAt("/auths", Auth::new); } - String getCurrentContext() { + @Nullable String getCurrentContext() { return this.currentContext; } - String getCredsStore() { + @Nullable String getCredsStore() { return this.credsStore; } @@ -211,11 +212,11 @@ final class DockerConfigurationMetadata { static final class Auth extends MappedObject { - private final String username; + private final @Nullable String username; - private final String password; + private final @Nullable String password; - private final String email; + private final @Nullable String email; Auth(JsonNode node) { super(node, MethodHandles.lookup()); @@ -233,15 +234,15 @@ final class DockerConfigurationMetadata { this.email = valueAt("/email", String.class); } - String getUsername() { + @Nullable String getUsername() { return this.username; } - String getPassword() { + @Nullable String getPassword() { return this.password; } - String getEmail() { + @Nullable String getEmail() { return this.email; } @@ -254,20 +255,20 @@ final class DockerConfigurationMetadata { static final class DockerContext extends MappedObject { - private final String dockerHost; + private final @Nullable String dockerHost; - private final Boolean skipTlsVerify; + private final @Nullable Boolean skipTlsVerify; - private final String tlsPath; + private final @Nullable String tlsPath; - private DockerContext(JsonNode node, String tlsPath) { + private DockerContext(JsonNode node, @Nullable String tlsPath) { super(node, MethodHandles.lookup()); this.dockerHost = valueAt("/Endpoints/" + DOCKER_ENDPOINT + "/Host", String.class); this.skipTlsVerify = valueAt("/Endpoints/" + DOCKER_ENDPOINT + "/SkipTLSVerify", Boolean.class); this.tlsPath = tlsPath; } - String getDockerHost() { + @Nullable String getDockerHost() { return this.dockerHost; } @@ -275,7 +276,7 @@ final class DockerConfigurationMetadata { return this.skipTlsVerify != null && !this.skipTlsVerify; } - String getTlsPath() { + @Nullable String getTlsPath() { return this.tlsPath; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConnectionConfiguration.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConnectionConfiguration.java index 68f546dcc2e..ce50b1e4f13 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConnectionConfiguration.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerConnectionConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.docker.configuration; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -33,7 +35,8 @@ public sealed interface DockerConnectionConfiguration { * @param secure if connection is secure * @param certificatePath a path to the certificate used for secure connections */ - record Host(String address, boolean secure, String certificatePath) implements DockerConnectionConfiguration { + record Host(String address, boolean secure, + @Nullable String certificatePath) implements DockerConnectionConfiguration { public Host(String address) { this(address, false, null); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerHost.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerHost.java index f12c15c01ab..2712739fad9 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerHost.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerHost.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.docker.configuration; +import org.jspecify.annotations.Nullable; + /** * Docker host connection options. * @@ -24,23 +26,23 @@ package org.springframework.boot.buildpack.platform.docker.configuration; */ public class DockerHost { - private final String address; + private final @Nullable String address; private final boolean secure; - private final String certificatePath; + private final @Nullable String certificatePath; - public DockerHost(String address) { + public DockerHost(@Nullable String address) { this(address, false, null); } - public DockerHost(String address, boolean secure, String certificatePath) { + public DockerHost(@Nullable String address, boolean secure, @Nullable String certificatePath) { this.address = address; this.secure = secure; this.certificatePath = certificatePath; } - public String getAddress() { + public @Nullable String getAddress() { return this.address; } @@ -48,7 +50,7 @@ public class DockerHost { return this.secure; } - public String getCertificatePath() { + public @Nullable String getCertificatePath() { return this.certificatePath; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryAuthentication.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryAuthentication.java index 1bf72d0716f..2d76a09afd1 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryAuthentication.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryAuthentication.java @@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.configuration; import java.util.function.BiConsumer; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.util.Assert; @@ -43,7 +45,7 @@ public interface DockerRegistryAuthentication { * @return the auth header * @since 3.5.0 */ - default String getAuthHeader(ImageReference imageReference) { + default @Nullable String getAuthHeader(@Nullable ImageReference imageReference) { return getAuthHeader(); } @@ -51,7 +53,7 @@ public interface DockerRegistryAuthentication { * Returns the auth header that should be used for docker authentication. * @return the auth header */ - String getAuthHeader(); + @Nullable String getAuthHeader(); /** * Factory method to that returns a new {@link DockerRegistryAuthentication} instance @@ -76,7 +78,8 @@ public interface DockerRegistryAuthentication { * @return a new {@link DockerRegistryAuthentication} instance * @since 3.5.0 */ - static DockerRegistryAuthentication user(String username, String password, String serverAddress, String email) { + static DockerRegistryAuthentication user(String username, String password, @Nullable String serverAddress, + @Nullable String email) { return new DockerRegistryUserAuthentication(username, password, serverAddress, email); } @@ -90,7 +93,7 @@ public interface DockerRegistryAuthentication { * @since 3.5.0 * @see #configuration(DockerRegistryAuthentication, BiConsumer) */ - static DockerRegistryAuthentication configuration(DockerRegistryAuthentication fallback) { + static DockerRegistryAuthentication configuration(@Nullable DockerRegistryAuthentication fallback) { return configuration(fallback, (message, ex) -> System.out.println(message)); } @@ -106,7 +109,7 @@ public interface DockerRegistryAuthentication { * @since 3.5.0 * @see #configuration(DockerRegistryAuthentication, BiConsumer) */ - static DockerRegistryAuthentication configuration(DockerRegistryAuthentication fallback, + static DockerRegistryAuthentication configuration(@Nullable DockerRegistryAuthentication fallback, BiConsumer credentialHelperExceptionHandler) { Assert.notNull(credentialHelperExceptionHandler, () -> "'credentialHelperExceptionHandler' must not be null"); return new DockerRegistryConfigAuthentication(fallback, credentialHelperExceptionHandler); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryConfigAuthentication.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryConfigAuthentication.java index b93efd935b5..e0bec2ba458 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryConfigAuthentication.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryConfigAuthentication.java @@ -21,10 +21,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Function; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfigurationMetadata.Auth; import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfigurationMetadata.DockerConfig; import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.boot.buildpack.platform.system.Environment; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -42,7 +45,7 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication static Map credentialFromHelperCache = new ConcurrentHashMap<>(); - private final DockerRegistryAuthentication fallback; + private final @Nullable DockerRegistryAuthentication fallback; private final BiConsumer credentialHelperExceptionHandler; @@ -50,13 +53,13 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication private final DockerConfig dockerConfig; - DockerRegistryConfigAuthentication(DockerRegistryAuthentication fallback, + DockerRegistryConfigAuthentication(@Nullable DockerRegistryAuthentication fallback, BiConsumer credentialHelperExceptionHandler) { this(fallback, credentialHelperExceptionHandler, Environment.SYSTEM, (helper) -> new CredentialHelper("docker-credential-" + helper)); } - DockerRegistryConfigAuthentication(DockerRegistryAuthentication fallback, + DockerRegistryConfigAuthentication(@Nullable DockerRegistryAuthentication fallback, BiConsumer credentialHelperExceptionHandler, Environment environment, Function credentialHelperFactory) { this.fallback = fallback; @@ -66,23 +69,23 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication } @Override - public String getAuthHeader() { + public @Nullable String getAuthHeader() { return getAuthHeader(null); } @Override - public String getAuthHeader(ImageReference imageReference) { + public @Nullable String getAuthHeader(@Nullable ImageReference imageReference) { String serverUrl = getServerUrl(imageReference); DockerRegistryAuthentication authentication = getAuthentication(serverUrl); return (authentication != null) ? authentication.getAuthHeader(imageReference) : null; } - private String getServerUrl(ImageReference imageReference) { + private @Nullable String getServerUrl(@Nullable ImageReference imageReference) { String domain = (imageReference != null) ? imageReference.getDomain() : null; return (!DEFAULT_DOMAIN.equals(domain)) ? domain : INDEX_URL; } - private DockerRegistryAuthentication getAuthentication(String serverUrl) { + private @Nullable DockerRegistryAuthentication getAuthentication(@Nullable String serverUrl) { Credential credentialsFromHelper = getCredentialsFromHelper(serverUrl); Map.Entry authConfigEntry = getAuthConfigEntry(serverUrl); Auth authConfig = (authConfigEntry != null) ? authConfigEntry.getValue() : null; @@ -90,14 +93,19 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication return getAuthentication(credentialsFromHelper, authConfig, serverUrl); } if (authConfig != null) { - return DockerRegistryAuthentication.user(authConfig.getUsername(), authConfig.getPassword(), - authConfigEntry.getKey(), authConfig.getEmail()); + Assert.state(authConfigEntry != null, "'authConfigEntry' must not be null"); + String username = authConfig.getUsername(); + String password = authConfig.getPassword(); + Assert.state(username != null, "'username' must not be null"); + Assert.state(password != null, "'password' must not be null"); + return DockerRegistryAuthentication.user(username, password, authConfigEntry.getKey(), + authConfig.getEmail()); } return this.fallback; } - private DockerRegistryAuthentication getAuthentication(Credential credentialsFromHelper, Auth authConfig, - String serverUrl) { + private DockerRegistryAuthentication getAuthentication(Credential credentialsFromHelper, @Nullable Auth authConfig, + @Nullable String serverUrl) { if (credentialsFromHelper.isIdentityToken()) { return DockerRegistryAuthentication.token(credentialsFromHelper.getSecret()); } @@ -109,12 +117,12 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication return DockerRegistryAuthentication.user(username, password, serverAddress, email); } - private Credential getCredentialsFromHelper(String serverUrl) { + private @Nullable Credential getCredentialsFromHelper(@Nullable String serverUrl) { return StringUtils.hasLength(serverUrl) ? credentialFromHelperCache.computeIfAbsent(serverUrl, this::computeCredentialsFromHelper) : null; } - private Credential computeCredentialsFromHelper(String serverUrl) { + private @Nullable Credential computeCredentialsFromHelper(String serverUrl) { CredentialHelper credentialHelper = getCredentialHelper(serverUrl); if (credentialHelper != null) { try { @@ -129,12 +137,15 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication return null; } - private CredentialHelper getCredentialHelper(String serverUrl) { + private @Nullable CredentialHelper getCredentialHelper(String serverUrl) { String name = this.dockerConfig.getCredHelpers().getOrDefault(serverUrl, this.dockerConfig.getCredsStore()); return (StringUtils.hasLength(name)) ? this.credentialHelperFactory.apply(name) : null; } - private Map.Entry getAuthConfigEntry(String serverUrl) { + private Map.@Nullable Entry getAuthConfigEntry(@Nullable String serverUrl) { + if (serverUrl == null) { + return null; + } for (Map.Entry candidate : this.dockerConfig.getAuths().entrySet()) { if (candidate.getKey().equals(serverUrl) || candidate.getKey().endsWith("://" + serverUrl)) { return candidate; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryUserAuthentication.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryUserAuthentication.java index d6308439a76..1db282aa6e6 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryUserAuthentication.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/DockerRegistryUserAuthentication.java @@ -17,6 +17,7 @@ package org.springframework.boot.buildpack.platform.docker.configuration; import com.fasterxml.jackson.annotation.JsonProperty; +import org.jspecify.annotations.Nullable; /** * {@link DockerRegistryAuthentication} for @@ -33,12 +34,12 @@ class DockerRegistryUserAuthentication extends JsonEncodedDockerRegistryAuthenti private final String password; @JsonProperty("serveraddress") - private final String url; + private final @Nullable String url; @JsonProperty - private final String email; + private final @Nullable String email; - DockerRegistryUserAuthentication(String username, String password, String url, String email) { + DockerRegistryUserAuthentication(String username, String password, @Nullable String url, @Nullable String email) { this.username = username; this.password = password; this.url = url; @@ -54,11 +55,11 @@ class DockerRegistryUserAuthentication extends JsonEncodedDockerRegistryAuthenti return this.password; } - String getUrl() { + @Nullable String getUrl() { return this.url; } - String getEmail() { + @Nullable String getEmail() { return this.email; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/JsonEncodedDockerRegistryAuthentication.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/JsonEncodedDockerRegistryAuthentication.java index 60a50836896..a6bf998a426 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/JsonEncodedDockerRegistryAuthentication.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/JsonEncodedDockerRegistryAuthentication.java @@ -20,6 +20,7 @@ import java.util.Base64; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.JsonProcessingException; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.SharedObjectMapper; @@ -32,10 +33,10 @@ import org.springframework.boot.buildpack.platform.json.SharedObjectMapper; class JsonEncodedDockerRegistryAuthentication implements DockerRegistryAuthentication { @JsonIgnore - private String authHeader; + private @Nullable String authHeader; @Override - public String getAuthHeader() { + public @Nullable String getAuthHeader() { return this.authHeader; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java index 1f8fc8103e4..669a4c892cb 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java @@ -20,6 +20,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import com.sun.jna.Platform; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfigurationMetadata.DockerContext; import org.springframework.boot.buildpack.platform.system.Environment; @@ -46,11 +47,11 @@ public class ResolvedDockerHost extends DockerHost { private static final String DOCKER_CONTEXT = "DOCKER_CONTEXT"; - ResolvedDockerHost(String address) { + ResolvedDockerHost(@Nullable String address) { super(address); } - ResolvedDockerHost(String address, boolean secure, String certificatePath) { + ResolvedDockerHost(@Nullable String address, boolean secure, @Nullable String certificatePath) { super(address, secure, certificatePath); } @@ -81,11 +82,12 @@ public class ResolvedDockerHost extends DockerHost { * @param connectionConfiguration the host configuration or {@code null} * @return the resolved docker host */ - public static ResolvedDockerHost from(DockerConnectionConfiguration connectionConfiguration) { + public static ResolvedDockerHost from(@Nullable DockerConnectionConfiguration connectionConfiguration) { return from(Environment.SYSTEM, connectionConfiguration); } - static ResolvedDockerHost from(Environment environment, DockerConnectionConfiguration connectionConfiguration) { + static ResolvedDockerHost from(Environment environment, + @Nullable DockerConnectionConfiguration connectionConfiguration) { DockerConfigurationMetadata environmentConfiguration = DockerConfigurationMetadata.from(environment); if (environment.get(DOCKER_CONTEXT) != null) { DockerContext context = environmentConfiguration.forContext(environment.get(DOCKER_CONTEXT)); @@ -114,7 +116,7 @@ public class ResolvedDockerHost extends DockerHost { return Platform.isWindows() ? WINDOWS_NAMED_PIPE_PATH : DOMAIN_SOCKET_PATH; } - private static boolean isTrue(String value) { + private static boolean isTrue(@Nullable String value) { try { return (value != null) && (Integer.parseInt(value) == 1); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/package-info.java index ced0cecdd84..8a32d1c440e 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/package-info.java @@ -17,4 +17,7 @@ /** * Docker configuration options. */ +@NullMarked package org.springframework.boot.buildpack.platform.docker.configuration; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/package-info.java index 0af086ddf6f..a2989712571 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/package-info.java @@ -17,4 +17,7 @@ /** * A limited Docker API providing the operations needed by pack. */ +@NullMarked package org.springframework.boot.buildpack.platform.docker; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactory.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactory.java index c2487442b74..69ac5e0a090 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactory.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactory.java @@ -28,6 +28,8 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; +import org.jspecify.annotations.Nullable; + /** * Utility methods for creating Java trust material from key and certificate files. * @@ -74,7 +76,7 @@ final class KeyStoreFactory { return keyStore; } - private static PrivateKey getPrivateKey(Path path) throws IOException { + private static @Nullable PrivateKey getPrivateKey(@Nullable Path path) throws IOException { if (path != null && Files.exists(path)) { String text = Files.readString(path); return PemPrivateKeyParser.parse(text); @@ -82,8 +84,8 @@ final class KeyStoreFactory { return null; } - private static void addCertificates(KeyStore keyStore, X509Certificate[] certificates, PrivateKey privateKey, - String alias) throws KeyStoreException { + private static void addCertificates(KeyStore keyStore, X509Certificate[] certificates, + @Nullable PrivateKey privateKey, String alias) throws KeyStoreException { if (privateKey != null) { keyStore.setKeyEntry(alias, privateKey, NO_PASSWORD, certificates); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PemCertificateParser.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PemCertificateParser.java index da04a8888f4..cae730f144e 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PemCertificateParser.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PemCertificateParser.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -56,6 +57,7 @@ final class PemCertificateParser { * @param text the text to parse * @return the parsed certificates */ + @Contract("!null -> !null") static @Nullable List parse(@Nullable String text) { if (text == null) { return null; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/package-info.java index 8d8a93c152e..faf62db5b62 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/package-info.java @@ -17,4 +17,7 @@ /** * Utilities and classes for managing SSL context and keys. */ +@NullMarked package org.springframework.boot.buildpack.platform.docker.ssl; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerConnectionException.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerConnectionException.java index 60c57e63ff7..68954bb2edf 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerConnectionException.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerConnectionException.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.docker.transport; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -45,7 +47,7 @@ public class DockerConnectionException extends RuntimeException { return message.toString(); } - private static String getCauseMessage(Exception cause) { + private static @Nullable String getCauseMessage(Exception cause) { if (cause.getCause() != null && cause.getCause().getClass().getName().equals(JNA_EXCEPTION_CLASS_NAME)) { return cause.getCause().getMessage(); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerEngineException.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerEngineException.java index cfc27db2672..d686a4dc742 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerEngineException.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerEngineException.java @@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.transport; import java.net.URI; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -34,12 +36,12 @@ public class DockerEngineException extends RuntimeException { private final String reasonPhrase; - private final Errors errors; + private final @Nullable Errors errors; - private final Message responseMessage; + private final @Nullable Message responseMessage; - public DockerEngineException(String host, URI uri, int statusCode, String reasonPhrase, Errors errors, - Message responseMessage) { + public DockerEngineException(String host, URI uri, int statusCode, String reasonPhrase, @Nullable Errors errors, + @Nullable Message responseMessage) { super(buildMessage(host, uri, statusCode, reasonPhrase, errors, responseMessage)); this.statusCode = statusCode; this.reasonPhrase = reasonPhrase; @@ -68,7 +70,7 @@ public class DockerEngineException extends RuntimeException { * errors JSON could not be read. * @return the errors or {@code null} */ - public Errors getErrors() { + public @Nullable Errors getErrors() { return this.errors; } @@ -77,12 +79,12 @@ public class DockerEngineException extends RuntimeException { * message JSON could not be read. * @return the message or {@code null} */ - public Message getResponseMessage() { + public @Nullable Message getResponseMessage() { return this.responseMessage; } - private static String buildMessage(String host, URI uri, int statusCode, String reasonPhrase, Errors errors, - Message responseMessage) { + private static String buildMessage(String host, URI uri, int statusCode, String reasonPhrase, + @Nullable Errors errors, @Nullable Message responseMessage) { Assert.notNull(host, "'host' must not be null"); Assert.notNull(uri, "'uri' must not be null"); StringBuilder message = new StringBuilder( diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Errors.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Errors.java index 4a4a2e677fd..cc8e6094780 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Errors.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Errors.java @@ -23,6 +23,7 @@ import java.util.stream.Stream; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.jspecify.annotations.Nullable; /** * Errors returned from the Docker API. @@ -35,7 +36,7 @@ public class Errors implements Iterable { private final List errors; @JsonCreator - Errors(@JsonProperty("errors") List errors) { + Errors(@JsonProperty("errors") @Nullable List errors) { this.errors = (errors != null) ? errors : Collections.emptyList(); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java index 1ad2d215743..6201160ed2c 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java @@ -37,6 +37,7 @@ import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.io.Content; import org.springframework.boot.buildpack.platform.io.IOConsumer; @@ -95,7 +96,7 @@ abstract class HttpClientTransport implements HttpTransport { * @return the operation response */ @Override - public Response post(URI uri, String registryAuth) { + public Response post(URI uri, @Nullable String registryAuth) { return execute(new HttpPost(uri), registryAuth); } @@ -148,7 +149,7 @@ abstract class HttpClientTransport implements HttpTransport { return execute(request); } - private Response execute(HttpUriRequestBase request, String registryAuth) { + private Response execute(HttpUriRequestBase request, @Nullable String registryAuth) { if (StringUtils.hasText(registryAuth)) { request.setHeader(REGISTRY_AUTH_HEADER, registryAuth); } @@ -178,7 +179,7 @@ abstract class HttpClientTransport implements HttpTransport { protected void beforeExecute(HttpRequest request) { } - private byte[] readContent(ClassicHttpResponse response) throws IOException { + private byte @Nullable [] readContent(ClassicHttpResponse response) throws IOException { HttpEntity entity = response.getEntity(); if (entity == null) { return null; @@ -188,7 +189,7 @@ abstract class HttpClientTransport implements HttpTransport { } } - private Errors deserializeErrors(byte[] content) { + private @Nullable Errors deserializeErrors(byte @Nullable [] content) { if (content == null) { return null; } @@ -200,7 +201,7 @@ abstract class HttpClientTransport implements HttpTransport { } } - private Message deserializeMessage(byte[] content) { + private @Nullable Message deserializeMessage(byte @Nullable [] content) { if (content == null) { return null; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpTransport.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpTransport.java index 50f4ed831f8..88efd4ff589 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpTransport.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpTransport.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.net.URI; import org.apache.hc.core5.http.Header; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration; import org.springframework.boot.buildpack.platform.docker.configuration.DockerHost; @@ -61,7 +62,7 @@ public interface HttpTransport { * @return the operation response * @throws IOException on IO error */ - Response post(URI uri, String registryAuth) throws IOException; + Response post(URI uri, @Nullable String registryAuth) throws IOException; /** * Perform an HTTP POST operation. @@ -104,7 +105,7 @@ public interface HttpTransport { * @param connectionConfiguration the Docker host information * @return a {@link HttpTransport} instance */ - static HttpTransport create(DockerConnectionConfiguration connectionConfiguration) { + static HttpTransport create(@Nullable DockerConnectionConfiguration connectionConfiguration) { ResolvedDockerHost host = ResolvedDockerHost.from(connectionConfiguration); HttpTransport remote = RemoteHttpClientTransport.createIfPossible(host); return (remote != null) ? remote : LocalHttpClientTransport.create(host); @@ -122,7 +123,7 @@ public interface HttpTransport { */ InputStream getContent() throws IOException; - default Header getHeader(String name) { + default @Nullable Header getHeader(String name) { throw new UnsupportedOperationException(); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java index 23fa5682b05..2362d44aaf0 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java @@ -39,6 +39,7 @@ import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost; import org.springframework.boot.buildpack.platform.socket.NamedPipeSocket; @@ -85,7 +86,7 @@ final class LocalHttpClientTransport extends HttpClientTransport { .setValidateAfterInactivity(TimeValue.NEG_ONE_MILLISECOND) .build(); - private static final Lookup NO_TLS_SOCKET = (name) -> null; + private static final Lookup<@Nullable TlsSocketStrategy> NO_TLS_SOCKET = (name) -> null; LocalConnectionManager(ResolvedDockerHost dockerHost) { super(createhttpClientConnectionOperator(dockerHost), null); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Message.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Message.java index b327852853e..4fd8640538e 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Message.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/Message.java @@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker.transport; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.jspecify.annotations.Nullable; /** * A message returned from the Docker API. @@ -27,10 +28,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class Message { - private final String message; + private final @Nullable String message; @JsonCreator - Message(@JsonProperty("message") String message) { + Message(@JsonProperty("message") @Nullable String message) { this.message = message; } @@ -38,13 +39,13 @@ public class Message { * Return the message contained in the response. * @return the message */ - public String getMessage() { + public @Nullable String getMessage() { return this.message; } @Override public String toString() { - return this.message; + return (this.message == null) ? "" : this.message; } } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/RemoteHttpClientTransport.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/RemoteHttpClientTransport.java index 271b23f67a0..a47ae5722c7 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/RemoteHttpClientTransport.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/RemoteHttpClientTransport.java @@ -30,6 +30,7 @@ import org.apache.hc.client5.http.ssl.TlsSocketStrategy; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.util.Timeout; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.docker.configuration.DockerHost; import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost; @@ -51,11 +52,11 @@ final class RemoteHttpClientTransport extends HttpClientTransport { super(client, host); } - static RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost) { + static @Nullable RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost) { return createIfPossible(dockerHost, new SslContextFactory()); } - static RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost, + static @Nullable RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost, SslContextFactory sslContextFactory) { if (!dockerHost.isRemote()) { return null; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/package-info.java index e3dd1754581..5cbafe1fe85 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/package-info.java @@ -17,4 +17,7 @@ /** * Docker transport classes providing HTTP operations on a local or remote engine. */ +@NullMarked package org.springframework.boot.buildpack.platform.docker.transport; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ApiVersion.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ApiVersion.java index b2b77dc70ea..9a8b878f1d0 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ApiVersion.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ApiVersion.java @@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.docker.type; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -90,7 +92,7 @@ public final class ApiVersion { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/BlobReference.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/BlobReference.java index 32842630086..6f40653e3c6 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/BlobReference.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/BlobReference.java @@ -21,6 +21,7 @@ import java.lang.invoke.MethodHandles; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.boot.buildpack.platform.json.MappedObject; +import org.springframework.util.Assert; /** * A reference to a blob by its digest. @@ -36,8 +37,20 @@ public class BlobReference extends MappedObject { BlobReference(JsonNode node) { super(node, MethodHandles.lookup()); - this.digest = valueAt("/digest", String.class); - this.mediaType = valueAt("/mediaType", String.class); + this.digest = extractDigest(); + this.mediaType = extractMediaType(); + } + + private String extractMediaType() { + String result = valueAt("/mediaType", String.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + + private String extractDigest() { + String result = valueAt("/digest", String.class); + Assert.state(result != null, "'result' must not be null"); + return result; } /** diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerConfig.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerConfig.java index ebb99d1a08a..241f9264686 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerConfig.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerConfig.java @@ -29,6 +29,7 @@ import java.util.function.Consumer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.SharedObjectMapper; import org.springframework.util.Assert; @@ -48,9 +49,9 @@ public class ContainerConfig { private final String json; - ContainerConfig(String user, ImageReference image, String command, List args, Map labels, - List bindings, Map env, String networkMode, List securityOptions) - throws IOException { + ContainerConfig(@Nullable String user, ImageReference image, String command, List args, + Map labels, List bindings, Map env, @Nullable String networkMode, + List securityOptions) throws IOException { Assert.notNull(image, "'image' must not be null"); Assert.hasText(command, "'command' must not be empty"); ObjectMapper objectMapper = SharedObjectMapper.get(); @@ -112,9 +113,9 @@ public class ContainerConfig { private final ImageReference image; - private String user; + private @Nullable String user; - private String command; + private @Nullable String command; private final List args = new ArrayList<>(); @@ -124,7 +125,7 @@ public class ContainerConfig { private final Map env = new LinkedHashMap<>(); - private String networkMode; + private @Nullable String networkMode; private final List securityOptions = new ArrayList<>(); @@ -135,6 +136,7 @@ public class ContainerConfig { private ContainerConfig run(Consumer update) { update.accept(this); try { + Assert.state(this.command != null, "'command' must not be null"); return new ContainerConfig(this.user, this.image, this.command, this.args, this.labels, this.bindings, this.env, this.networkMode, this.securityOptions); } @@ -201,7 +203,7 @@ public class ContainerConfig { * connect to. * @param networkMode the network */ - public void withNetworkMode(String networkMode) { + public void withNetworkMode(@Nullable String networkMode) { this.networkMode = networkMode; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerReference.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerReference.java index 4e5755c8b86..5d749eff669 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerReference.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerReference.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.docker.type; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -34,7 +36,7 @@ public final class ContainerReference { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerStatus.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerStatus.java index d5be4cc4cc6..caa02007bd1 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerStatus.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ContainerStatus.java @@ -21,8 +21,11 @@ import java.io.InputStream; import java.lang.invoke.MethodHandles; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.NullNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.MappedObject; +import org.springframework.util.Assert; /** * Status details returned from {@code Docker container wait}. @@ -34,20 +37,26 @@ public class ContainerStatus extends MappedObject { private final int statusCode; - private final String waitingErrorMessage; + private final @Nullable String waitingErrorMessage; - ContainerStatus(int statusCode, String waitingErrorMessage) { - super(null, null); + ContainerStatus(int statusCode, @Nullable String waitingErrorMessage) { + super(NullNode.getInstance(), MethodHandles.lookup()); this.statusCode = statusCode; this.waitingErrorMessage = waitingErrorMessage; } ContainerStatus(JsonNode node) { super(node, MethodHandles.lookup()); - this.statusCode = valueAt("/StatusCode", Integer.class); + this.statusCode = extractStatusCode(); this.waitingErrorMessage = valueAt("/Error/Message", String.class); } + private Integer extractStatusCode() { + Integer result = valueAt("/StatusCode", Integer.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + /** * Return the container exit status code. * @return the exit status code @@ -60,7 +69,7 @@ public class ContainerStatus extends MappedObject { * Return a message indicating an error waiting for a container to stop. * @return the waiting error message */ - public String getWaitingErrorMessage() { + public @Nullable String getWaitingErrorMessage() { return this.waitingErrorMessage; } @@ -81,7 +90,7 @@ public class ContainerStatus extends MappedObject { * @param errorMessage the error message * @return a new {@link ContainerStatus} instance */ - public static ContainerStatus of(int statusCode, String errorMessage) { + public static ContainerStatus of(int statusCode, @Nullable String errorMessage) { return new ContainerStatus(statusCode, errorMessage); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java index bb211938663..42930a2526d 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.List; import com.fasterxml.jackson.databind.JsonNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.MappedObject; import org.springframework.util.StringUtils; @@ -43,13 +44,13 @@ public class Image extends MappedObject { private final List layers; - private final String os; + private final @Nullable String os; - private final String architecture; + private final @Nullable String architecture; - private final String variant; + private final @Nullable String variant; - private final String created; + private final @Nullable String created; Image(JsonNode node) { super(node, MethodHandles.lookup()); @@ -62,7 +63,7 @@ public class Image extends MappedObject { this.created = valueAt("/Created", String.class); } - private List extractLayers(String[] layers) { + private List extractLayers(String @Nullable [] layers) { if (layers == null) { return Collections.emptyList(); } @@ -105,7 +106,7 @@ public class Image extends MappedObject { * Return the architecture of the image. * @return the image architecture */ - public String getArchitecture() { + public @Nullable String getArchitecture() { return this.architecture; } @@ -113,7 +114,7 @@ public class Image extends MappedObject { * Return the variant of the image. * @return the image variant */ - public String getVariant() { + public @Nullable String getVariant() { return this.variant; } @@ -121,7 +122,7 @@ public class Image extends MappedObject { * Return the created date of the image. * @return the image created date */ - public String getCreated() { + public @Nullable String getCreated() { return this.created; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java index 8d078dc02b2..df181d35528 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java @@ -33,6 +33,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.io.Content; import org.springframework.boot.buildpack.platform.io.IOConsumer; @@ -72,20 +73,21 @@ public class ImageArchive implements TarArchive { private final Instant createDate; - private final ImageReference tag; + private final @Nullable ImageReference tag; private final String os; - private final String architecture; + private final @Nullable String architecture; - private final String variant; + private final @Nullable String variant; private final List existingLayers; private final List newLayers; - ImageArchive(ObjectMapper objectMapper, ImageConfig imageConfig, Instant createDate, ImageReference tag, String os, - String architecture, String variant, List existingLayers, List newLayers) { + ImageArchive(ObjectMapper objectMapper, ImageConfig imageConfig, Instant createDate, @Nullable ImageReference tag, + String os, @Nullable String architecture, @Nullable String variant, List existingLayers, + List newLayers) { this.objectMapper = objectMapper; this.imageConfig = imageConfig; this.createDate = createDate; @@ -117,7 +119,7 @@ public class ImageArchive implements TarArchive { * Return the tag of the archive. * @return the tag */ - public ImageReference getTag() { + public @Nullable ImageReference getTag() { return this.tag; } @@ -257,9 +259,9 @@ public class ImageArchive implements TarArchive { private ImageConfig config; - private Instant createDate; + private @Nullable Instant createDate; - private ImageReference tag; + private @Nullable ImageReference tag; private final List newLayers = new ArrayList<>(); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveIndex.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveIndex.java index 6b9999583f7..f18efb88411 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveIndex.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveIndex.java @@ -24,6 +24,7 @@ import java.util.List; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.boot.buildpack.platform.json.MappedObject; +import org.springframework.util.Assert; /** * Image archive index information as provided by {@code index.json}. @@ -42,10 +43,16 @@ public class ImageArchiveIndex extends MappedObject { protected ImageArchiveIndex(JsonNode node) { super(node, MethodHandles.lookup()); - this.schemaVersion = valueAt("/schemaVersion", Integer.class); + this.schemaVersion = extractSchemaVersion(); this.manifests = childrenAt("/manifests", BlobReference::new); } + private Integer extractSchemaVersion() { + Integer result = valueAt("/schemaVersion", Integer.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + public Integer getSchemaVersion() { return this.schemaVersion; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java index aeee1f3216a..88be3fe960e 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageName.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.docker.type; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -41,7 +43,7 @@ public 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); @@ -65,7 +67,7 @@ public class ImageName { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -100,7 +102,7 @@ public 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; } @@ -135,7 +137,7 @@ public class ImageName { return new ImageName(domain, path); } - 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()) { diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImagePlatform.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImagePlatform.java index 99ec9d0b46e..75bfbfbcaaf 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImagePlatform.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImagePlatform.java @@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.type; import java.util.Objects; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -30,11 +32,11 @@ public class ImagePlatform { private final String os; - private final String architecture; + private final @Nullable String architecture; - private final String variant; + private final @Nullable String variant; - ImagePlatform(String os, String architecture, String variant) { + ImagePlatform(String os, @Nullable String architecture, @Nullable String variant) { Assert.hasText(os, "'os' must not be empty"); this.os = os; this.architecture = architecture; @@ -42,7 +44,7 @@ public class ImagePlatform { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java index 477b50e9d56..1e3b111c919 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageReference.java @@ -21,6 +21,8 @@ import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -41,13 +43,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; @@ -77,7 +79,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; } @@ -85,12 +87,12 @@ 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; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -124,7 +126,7 @@ public final class ImageReference { return buildString(this.name.toLegacyString(), this.tag, this.digest); } - 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); diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/LayerId.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/LayerId.java index 597618e2995..01a8e5cf88d 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/LayerId.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/LayerId.java @@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.type; import java.math.BigInteger; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -57,7 +59,7 @@ public final class LayerId { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Manifest.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Manifest.java index 9bce644de91..77738525d71 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Manifest.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Manifest.java @@ -22,8 +22,10 @@ import java.lang.invoke.MethodHandles; import java.util.List; import com.fasterxml.jackson.databind.JsonNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.MappedObject; +import org.springframework.util.Assert; /** * A manifest as defined in {@code application/vnd.docker.distribution.manifest} or @@ -38,22 +40,28 @@ public class Manifest extends MappedObject { private final Integer schemaVersion; - private final String mediaType; + private final @Nullable String mediaType; private final List layers; protected Manifest(JsonNode node) { super(node, MethodHandles.lookup()); - this.schemaVersion = valueAt("/schemaVersion", Integer.class); + this.schemaVersion = extractSchemaVersion(); this.mediaType = valueAt("/mediaType", String.class); this.layers = childrenAt("/layers", BlobReference::new); } + private Integer extractSchemaVersion() { + Integer result = valueAt("/schemaVersion", Integer.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + public Integer getSchemaVersion() { return this.schemaVersion; } - public String getMediaType() { + public @Nullable String getMediaType() { return this.mediaType; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ManifestList.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ManifestList.java index 3ee273ca265..8dab9fb3203 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ManifestList.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ManifestList.java @@ -23,8 +23,10 @@ import java.util.List; import java.util.stream.Stream; import com.fasterxml.jackson.databind.JsonNode; +import org.jspecify.annotations.Nullable; import org.springframework.boot.buildpack.platform.json.MappedObject; +import org.springframework.util.Assert; /** * A distribution manifest list as defined in @@ -39,22 +41,28 @@ public class ManifestList extends MappedObject { private final Integer schemaVersion; - private final String mediaType; + private final @Nullable String mediaType; private final List manifests; protected ManifestList(JsonNode node) { super(node, MethodHandles.lookup()); - this.schemaVersion = valueAt("/schemaVersion", Integer.class); + this.schemaVersion = extractSchemaVersion(); this.mediaType = valueAt("/mediaType", String.class); this.manifests = childrenAt("/manifests", BlobReference::new); } + private Integer extractSchemaVersion() { + Integer result = valueAt("/schemaVersion", Integer.class); + Assert.state(result != null, "'result' must not be null"); + return result; + } + public Integer getSchemaVersion() { return this.schemaVersion; } - public String getMediaType() { + public @Nullable String getMediaType() { return this.mediaType; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/VolumeName.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/VolumeName.java index 6e5600496df..68f306a8158 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/VolumeName.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/VolumeName.java @@ -22,6 +22,8 @@ import java.security.NoSuchAlgorithmException; import java.util.HexFormat; import java.util.function.Function; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; /** @@ -39,7 +41,7 @@ public final class VolumeName { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/package-info.java index 4c218656d43..993df9b3f6f 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/package-info.java @@ -17,4 +17,7 @@ /** * Docker types. */ +@NullMarked package org.springframework.boot.buildpack.platform.docker.type; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/InspectedContent.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/InspectedContent.java index 0916b6064f5..ae26cd7c054 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/InspectedContent.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/InspectedContent.java @@ -24,6 +24,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import org.jspecify.annotations.Nullable; + import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; import org.springframework.util.StreamUtils; @@ -137,7 +139,7 @@ public class InspectedContent implements Content { private OutputStream delegate; - private File tempFile; + private @Nullable File tempFile; private final byte[] singleByteBuffer = new byte[0]; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/package-info.java index d793582fbc1..c94541f8edc 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/package-info.java @@ -17,4 +17,7 @@ /** * IO classes and utilities. */ +@NullMarked package org.springframework.boot.buildpack.platform.io; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/JsonStream.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/JsonStream.java index 7422bbcdeac..bae1a9ab6c7 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/JsonStream.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/JsonStream.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.jspecify.annotations.Nullable; /** * Utility class that allows JSON to be parsed and processed as it's received. @@ -78,7 +79,7 @@ public class JsonStream { } @SuppressWarnings("unchecked") - private T read(JsonParser parser, Class type) throws IOException { + private @Nullable T read(JsonParser parser, Class type) throws IOException { if (ObjectNode.class.isAssignableFrom(type)) { ObjectNode node = this.objectMapper.readTree(parser); if (node == null || node.isMissingNode() || node.isEmpty()) { diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/MappedObject.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/MappedObject.java index fa9b059b16c..ad3494a4273 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/MappedObject.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/MappedObject.java @@ -32,6 +32,7 @@ import java.util.function.Function; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.StreamUtils; @@ -74,7 +75,7 @@ public class MappedObject { * @param type the desired type. May be a simple JSON type or an interface * @return the value */ - protected T valueAt(String expression, Class type) { + protected @Nullable T valueAt(String expression, Class type) { return valueAt(this, this.node, this.lookup, expression, type); } @@ -104,7 +105,7 @@ public class MappedObject { * @return a list of children * @since 3.2.6 */ - protected List childrenAt(String expression, Function factory) { + protected List childrenAt(@Nullable String expression, Function factory) { JsonNode node = (expression != null) ? this.node.at(expression) : this.node; if (node.isEmpty()) { return Collections.emptyList(); @@ -120,13 +121,14 @@ public class MappedObject { return (T) handler.root; } - protected static T valueAt(Object proxy, String expression, Class type) { + protected static @Nullable T valueAt(Object proxy, String expression, Class type) { MappedInvocationHandler handler = (MappedInvocationHandler) Proxy.getInvocationHandler(proxy); return valueAt(handler.root, handler.node, handler.lookup, expression, type); } @SuppressWarnings("unchecked") - private static T valueAt(MappedObject root, JsonNode node, Lookup lookup, String expression, Class type) { + private static @Nullable T valueAt(MappedObject root, JsonNode node, Lookup lookup, String expression, + Class type) { JsonNode result = node.at(expression); if (result.isMissingNode() && expression.startsWith("/") && expression.length() > 1 && Character.isLowerCase(expression.charAt(1))) { @@ -233,7 +235,7 @@ public class MappedObject { } @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class declaringClass = method.getDeclaringClass(); if (method.isDefault()) { Lookup lookup = this.lookup.in(declaringClass); @@ -262,7 +264,7 @@ public class MappedObject { return result.toString(); } - private Object valueForProperty(String name, Class type) { + private @Nullable Object valueForProperty(String name, Class type) { return valueAt(this.root, this.node, this.lookup, "/" + name, type); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/package-info.java index 16c0f6e6276..2a3a3294e2d 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/json/package-info.java @@ -17,4 +17,7 @@ /** * Utilities and classes for JSON processing. */ +@NullMarked package org.springframework.boot.buildpack.platform.json; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/AbstractSocket.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/AbstractSocket.java index 31e623cfe85..931d853b864 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/AbstractSocket.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/AbstractSocket.java @@ -21,6 +21,8 @@ import java.net.InetAddress; import java.net.Socket; import java.net.SocketAddress; +import org.jspecify.annotations.Nullable; + /** * Abstract base class for custom socket implementation. * @@ -57,22 +59,22 @@ class AbstractSocket extends Socket { } @Override - public InetAddress getInetAddress() { + public @Nullable InetAddress getInetAddress() { return null; } @Override - public InetAddress getLocalAddress() { + public @Nullable InetAddress getLocalAddress() { return null; } @Override - public SocketAddress getLocalSocketAddress() { + public @Nullable SocketAddress getLocalSocketAddress() { return null; } @Override - public SocketAddress getRemoteSocketAddress() { + public @Nullable SocketAddress getRemoteSocketAddress() { return null; } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/package-info.java index 476205a6030..4eeb52fd0aa 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/package-info.java @@ -17,4 +17,7 @@ /** * Low-level {@link java.net.Socket} implementations required for local Docker access. */ +@NullMarked package org.springframework.boot.buildpack.platform.socket; + +import org.jspecify.annotations.NullMarked; diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/Environment.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/Environment.java index ccba1dc242f..c6e8306eb61 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/Environment.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/Environment.java @@ -16,6 +16,8 @@ package org.springframework.boot.buildpack.platform.system; +import org.jspecify.annotations.Nullable; + /** * Provides access to environment variable values. * @@ -38,6 +40,6 @@ public interface Environment { * @return the string value of the variable, or {@code null} if the variable is not * defined in the environment */ - String get(String name); + @Nullable String get(String name); } diff --git a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/package-info.java b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/package-info.java index 9e5ad1943df..404f99a0552 100644 --- a/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/package-info.java +++ b/buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/system/package-info.java @@ -17,4 +17,7 @@ /** * System abstractions. */ +@NullMarked package org.springframework.boot.buildpack.platform.system; + +import org.jspecify.annotations.NullMarked; diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml index 66d71260f6a..71f6d6b0a07 100644 --- a/config/checkstyle/checkstyle-suppressions.xml +++ b/config/checkstyle/checkstyle-suppressions.xml @@ -92,4 +92,5 @@ +