Improve null-safety of core/spring-boot-docker-compose

See gh-46926
This commit is contained in:
Moritz Halbritter
2025-08-26 14:22:53 +02:00
parent c76b087025
commit 7ad90fb963
27 changed files with 80 additions and 52 deletions
@@ -88,7 +88,7 @@ class DefaultRunningService implements RunningService, OriginProvider {
}
@Override
public Map<String, String> env() {
public Map<String, @Nullable String> env() {
return this.env.asMap();
}
@@ -44,7 +44,7 @@ import org.springframework.util.CollectionUtils;
*/
class DockerCli {
private static final Map<File, DockerCommands> dockerCommandsCache = new HashMap<>();
private static final Map<@Nullable File, DockerCommands> dockerCommandsCache = new HashMap<>();
private static final Log logger = LogFactory.getLog(DockerCli.class);
@@ -34,7 +34,7 @@ import org.springframework.util.CollectionUtils;
*/
class DockerEnv {
private final Map<String, String> map;
private final Map<String, @Nullable String> map;
/**
* Create a new {@link DockerEnv} instance.
@@ -44,11 +44,11 @@ class DockerEnv {
this.map = parse(env);
}
private Map<String, String> parse(List<String> env) {
private Map<String, @Nullable String> parse(List<String> env) {
if (CollectionUtils.isEmpty(env)) {
return Collections.emptyMap();
}
Map<String, String> result = new LinkedHashMap<>();
Map<String, @Nullable String> result = new LinkedHashMap<>();
env.stream().map(this::parseEntry).forEach((entry) -> result.put(entry.key(), entry.value()));
return Collections.unmodifiableMap(result);
}
@@ -67,7 +67,7 @@ class DockerEnv {
* Return the env as a {@link Map}.
* @return the env as a map
*/
Map<String, String> asMap() {
Map<String, @Nullable String> asMap() {
return this.map;
}
@@ -58,7 +58,7 @@ public interface RunningService {
* Return the environment defined for the service.
* @return the service env
*/
Map<String, String> env();
Map<String, @Nullable String> env();
/**
* Return the labels attached to the service.
@@ -32,7 +32,7 @@ class ActiveMQClassicEnvironment {
private final @Nullable String password;
ActiveMQClassicEnvironment(Map<String, String> env) {
ActiveMQClassicEnvironment(Map<String, @Nullable String> env) {
this.user = env.get("ACTIVEMQ_CONNECTION_USER");
this.password = env.get("ACTIVEMQ_CONNECTION_PASSWORD");
}
@@ -31,7 +31,7 @@ class ActiveMQEnvironment {
private final @Nullable String password;
ActiveMQEnvironment(Map<String, String> env) {
ActiveMQEnvironment(Map<String, @Nullable String> env) {
this.user = env.get("ACTIVEMQ_USERNAME");
this.password = env.get("ACTIVEMQ_PASSWORD");
}
@@ -18,6 +18,8 @@ package org.springframework.boot.amqp.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
/**
* RabbitMQ environment details.
*
@@ -32,7 +34,7 @@ class RabbitEnvironment {
private final String password;
RabbitEnvironment(Map<String, String> env) {
RabbitEnvironment(Map<String, @Nullable String> env) {
this.username = env.getOrDefault("RABBITMQ_DEFAULT_USER", env.getOrDefault("RABBITMQ_USERNAME", "guest"));
this.password = env.getOrDefault("RABBITMQ_DEFAULT_PASS", env.getOrDefault("RABBITMQ_PASSWORD", "guest"));
}
@@ -32,7 +32,7 @@ class ArtemisEnvironment {
private final @Nullable String password;
ArtemisEnvironment(Map<String, String> env) {
ArtemisEnvironment(Map<String, @Nullable String> env) {
this.user = env.get("ARTEMIS_USER");
this.password = env.get("ARTEMIS_PASSWORD");
}
@@ -18,6 +18,8 @@ package org.springframework.boot.cassandra.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
/**
* Cassandra environment details.
*
@@ -27,7 +29,7 @@ class CassandraEnvironment {
private final String datacenter;
CassandraEnvironment(Map<String, String> env) {
CassandraEnvironment(Map<String, @Nullable String> env) {
this.datacenter = env.getOrDefault("CASSANDRA_DC", env.getOrDefault("CASSANDRA_DATACENTER", "datacenter1"));
}
@@ -33,7 +33,7 @@ class ElasticsearchEnvironment {
private final @Nullable String password;
ElasticsearchEnvironment(Map<String, String> env) {
ElasticsearchEnvironment(Map<String, @Nullable String> env) {
Assert.state(!env.containsKey("ELASTIC_PASSWORD_FILE"), "ELASTIC_PASSWORD_FILE is not supported");
this.password = env.get("ELASTIC_PASSWORD");
}
@@ -29,7 +29,7 @@ class HazelcastEnvironment {
private final @Nullable String clusterName;
HazelcastEnvironment(Map<String, String> env) {
HazelcastEnvironment(Map<String, @Nullable String> env) {
this.clusterName = env.get("HZ_CLUSTERNAME");
}
@@ -18,6 +18,8 @@ package org.springframework.boot.jdbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -34,13 +36,13 @@ class ClickHouseEnvironment {
private final String database;
ClickHouseEnvironment(Map<String, String> env) {
ClickHouseEnvironment(Map<String, @Nullable String> env) {
this.username = env.getOrDefault("CLICKHOUSE_USER", "default");
this.password = extractPassword(env);
this.database = env.getOrDefault("CLICKHOUSE_DB", "default");
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
boolean allowEmpty = env.containsKey("ALLOW_EMPTY_PASSWORD");
String password = env.get("CLICKHOUSE_PASSWORD");
Assert.state(StringUtils.hasLength(password) || allowEmpty, "No ClickHouse password found");
@@ -18,6 +18,8 @@ package org.springframework.boot.jdbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -37,18 +39,18 @@ class MariaDbEnvironment {
private final String database;
MariaDbEnvironment(Map<String, String> env) {
MariaDbEnvironment(Map<String, @Nullable String> env) {
this.username = extractUsername(env);
this.password = extractPassword(env);
this.database = extractDatabase(env);
}
private String extractUsername(Map<String, String> env) {
private String extractUsername(Map<String, @Nullable String> env) {
String user = env.get("MARIADB_USER");
return (user != null) ? user : env.getOrDefault("MYSQL_USER", "root");
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
Assert.state(!env.containsKey("MARIADB_RANDOM_ROOT_PASSWORD"), "MARIADB_RANDOM_ROOT_PASSWORD is not supported");
Assert.state(!env.containsKey("MYSQL_RANDOM_ROOT_PASSWORD"), "MYSQL_RANDOM_ROOT_PASSWORD is not supported");
Assert.state(!env.containsKey("MARIADB_ROOT_PASSWORD_HASH"), "MARIADB_ROOT_PASSWORD_HASH is not supported");
@@ -62,7 +64,7 @@ class MariaDbEnvironment {
return (password != null) ? password : "";
}
private String extractDatabase(Map<String, String> env) {
private String extractDatabase(Map<String, @Nullable String> env) {
String database = env.get("MARIADB_DATABASE");
database = (database != null) ? database : env.get("MYSQL_DATABASE");
Assert.state(database != null, "No MARIADB_DATABASE defined");
@@ -18,6 +18,8 @@ package org.springframework.boot.jdbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -37,13 +39,13 @@ class MySqlEnvironment {
private final String database;
MySqlEnvironment(Map<String, String> env) {
MySqlEnvironment(Map<String, @Nullable String> env) {
this.username = env.getOrDefault("MYSQL_USER", "root");
this.password = extractPassword(env);
this.database = extractDatabase(env);
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
Assert.state(!env.containsKey("MYSQL_RANDOM_ROOT_PASSWORD"), "MYSQL_RANDOM_ROOT_PASSWORD is not supported");
boolean allowEmpty = env.containsKey("MYSQL_ALLOW_EMPTY_PASSWORD") || env.containsKey("ALLOW_EMPTY_PASSWORD");
String password = env.get("MYSQL_PASSWORD");
@@ -52,7 +54,7 @@ class MySqlEnvironment {
return (password != null) ? password : "";
}
private String extractDatabase(Map<String, String> env) {
private String extractDatabase(Map<String, @Nullable String> env) {
String database = env.get("MYSQL_DATABASE");
Assert.state(database != null, "No MYSQL_DATABASE defined");
return database;
@@ -18,6 +18,8 @@ package org.springframework.boot.jdbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -34,13 +36,13 @@ class OracleEnvironment {
private final String database;
OracleEnvironment(Map<String, String> env, String defaultDatabase) {
OracleEnvironment(Map<String, @Nullable String> env, String defaultDatabase) {
this.username = env.getOrDefault("APP_USER", "system");
this.password = extractPassword(env);
this.database = env.getOrDefault("ORACLE_DATABASE", defaultDatabase);
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
if (env.containsKey("APP_USER")) {
String password = env.get("APP_USER_PASSWORD");
Assert.state(StringUtils.hasLength(password), "No Oracle app password found");
@@ -49,13 +49,13 @@ class PostgresEnvironment {
private final String database;
PostgresEnvironment(Map<String, String> env) {
PostgresEnvironment(Map<String, @Nullable String> env) {
this.username = extract(env, USERNAME_KEYS, DEFAULT_USERNAME);
this.password = extractPassword(env);
this.database = extract(env, DATABASE_KEYS, this.username);
}
private String extract(Map<String, String> env, String[] keys, String defaultValue) {
private String extract(Map<String, @Nullable String> env, String[] keys, String defaultValue) {
for (String key : keys) {
if (env.containsKey(key)) {
return env.get(key);
@@ -64,7 +64,7 @@ class PostgresEnvironment {
return defaultValue;
}
private @Nullable String extractPassword(Map<String, String> env) {
private @Nullable String extractPassword(Map<String, @Nullable String> env) {
if (isUsingTrustHostAuthMethod(env)) {
return null;
}
@@ -74,7 +74,7 @@ class PostgresEnvironment {
return (password != null) ? password : "";
}
private boolean isUsingTrustHostAuthMethod(Map<String, String> env) {
private boolean isUsingTrustHostAuthMethod(Map<String, @Nullable String> env) {
String hostAuthMethod = env.get("POSTGRES_HOST_AUTH_METHOD");
return "trust".equals(hostAuthMethod);
}
@@ -18,6 +18,8 @@ package org.springframework.boot.jdbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -32,11 +34,11 @@ class SqlServerEnvironment {
private final String password;
SqlServerEnvironment(Map<String, String> env) {
SqlServerEnvironment(Map<String, @Nullable String> env) {
this.password = extractPassword(env);
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
String password = env.get("MSSQL_SA_PASSWORD");
password = (password != null) ? password : env.get("SA_PASSWORD");
Assert.state(StringUtils.hasLength(password), "No MSSQL password found");
@@ -18,6 +18,8 @@ package org.springframework.boot.ldap.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.docker.compose.core.RunningService;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
@@ -56,7 +58,7 @@ class LLdapDockerComposeConnectionDetailsFactory extends DockerComposeConnection
LLdapDockerComposeConnectionDetails(RunningService service) {
super(service);
Map<String, String> env = service.env();
Map<String, @Nullable String> env = service.env();
boolean usesTls = Boolean.parseBoolean(env.getOrDefault("LLDAP_LDAPS_OPTIONS__ENABLED", "false"));
String ldapPort = usesTls ? env.getOrDefault("LLDAP_LDAPS_OPTIONS__PORT", "6360")
: env.getOrDefault("LLDAP_LDAP_PORT", "3890");
@@ -20,6 +20,8 @@ import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.docker.compose.core.RunningService;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
@@ -59,7 +61,7 @@ class OpenLdapDockerComposeConnectionDetailsFactory
OpenLdapDockerComposeConnectionDetails(RunningService service) {
super(service);
Map<String, String> env = service.env();
Map<String, @Nullable String> env = service.env();
boolean usesTls = Boolean.parseBoolean(env.getOrDefault("LDAP_TLS", "true"));
String ldapPort = usesTls ? env.getOrDefault("LDAPS_PORT", "636") : env.getOrDefault("LDAP_PORT", "389");
this.urls = new String[] { "%s://%s:%d".formatted(usesTls ? "ldaps" : "ldap", service.host(),
@@ -38,7 +38,7 @@ class MongoEnvironment {
private final @Nullable String database;
MongoEnvironment(Map<String, String> env) {
MongoEnvironment(Map<String, @Nullable String> env) {
Assert.state(!env.containsKey("MONGO_INITDB_ROOT_USERNAME_FILE"),
"MONGO_INITDB_ROOT_USERNAME_FILE is not supported");
Assert.state(!env.containsKey("MONGO_INITDB_ROOT_PASSWORD_FILE"),
@@ -32,7 +32,7 @@ class Neo4jEnvironment {
private final @Nullable AuthToken authToken;
Neo4jEnvironment(Map<String, String> env) {
Neo4jEnvironment(Map<String, @Nullable String> env) {
AuthToken authToken = parse(env.get("NEO4J_AUTH"));
if (authToken == null && env.containsKey("NEO4J_PASSWORD")) {
authToken = parse("neo4j/" + env.get("NEO4J_PASSWORD"));
@@ -18,6 +18,8 @@ package org.springframework.boot.r2dbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -34,13 +36,13 @@ class ClickHouseEnvironment {
private final String database;
ClickHouseEnvironment(Map<String, String> env) {
ClickHouseEnvironment(Map<String, @Nullable String> env) {
this.username = env.getOrDefault("CLICKHOUSE_USER", "default");
this.password = extractPassword(env);
this.database = env.getOrDefault("CLICKHOUSE_DB", "default");
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
boolean allowEmpty = env.containsKey("ALLOW_EMPTY_PASSWORD");
String password = env.get("CLICKHOUSE_PASSWORD");
Assert.state(StringUtils.hasLength(password) || allowEmpty, "No ClickHouse password found");
@@ -18,6 +18,8 @@ package org.springframework.boot.r2dbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -37,18 +39,18 @@ class MariaDbEnvironment {
private final String database;
MariaDbEnvironment(Map<String, String> env) {
MariaDbEnvironment(Map<String, @Nullable String> env) {
this.username = extractUsername(env);
this.password = extractPassword(env);
this.database = extractDatabase(env);
}
private String extractUsername(Map<String, String> env) {
private String extractUsername(Map<String, @Nullable String> env) {
String user = env.get("MARIADB_USER");
return (user != null) ? user : env.getOrDefault("MYSQL_USER", "root");
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
Assert.state(!env.containsKey("MARIADB_RANDOM_ROOT_PASSWORD"), "MARIADB_RANDOM_ROOT_PASSWORD is not supported");
Assert.state(!env.containsKey("MYSQL_RANDOM_ROOT_PASSWORD"), "MYSQL_RANDOM_ROOT_PASSWORD is not supported");
Assert.state(!env.containsKey("MARIADB_ROOT_PASSWORD_HASH"), "MARIADB_ROOT_PASSWORD_HASH is not supported");
@@ -62,7 +64,7 @@ class MariaDbEnvironment {
return (password != null) ? password : "";
}
private String extractDatabase(Map<String, String> env) {
private String extractDatabase(Map<String, @Nullable String> env) {
String database = env.get("MARIADB_DATABASE");
database = (database != null) ? database : env.get("MYSQL_DATABASE");
Assert.state(database != null, "No MARIADB_DATABASE defined");
@@ -18,6 +18,8 @@ package org.springframework.boot.r2dbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -37,13 +39,13 @@ class MySqlEnvironment {
private final String database;
MySqlEnvironment(Map<String, String> env) {
MySqlEnvironment(Map<String, @Nullable String> env) {
this.username = env.getOrDefault("MYSQL_USER", "root");
this.password = extractPassword(env);
this.database = extractDatabase(env);
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
Assert.state(!env.containsKey("MYSQL_RANDOM_ROOT_PASSWORD"), "MYSQL_RANDOM_ROOT_PASSWORD is not supported");
boolean allowEmpty = env.containsKey("MYSQL_ALLOW_EMPTY_PASSWORD") || env.containsKey("ALLOW_EMPTY_PASSWORD");
String password = env.get("MYSQL_PASSWORD");
@@ -52,7 +54,7 @@ class MySqlEnvironment {
return (password != null) ? password : "";
}
private String extractDatabase(Map<String, String> env) {
private String extractDatabase(Map<String, @Nullable String> env) {
String database = env.get("MYSQL_DATABASE");
Assert.state(database != null, "No MYSQL_DATABASE defined");
return database;
@@ -18,6 +18,8 @@ package org.springframework.boot.r2dbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -34,13 +36,13 @@ class OracleEnvironment {
private final String database;
OracleEnvironment(Map<String, String> env, String defaultDatabase) {
OracleEnvironment(Map<String, @Nullable String> env, String defaultDatabase) {
this.username = env.getOrDefault("APP_USER", "system");
this.password = extractPassword(env);
this.database = env.getOrDefault("ORACLE_DATABASE", defaultDatabase);
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
if (env.containsKey("APP_USER")) {
String password = env.get("APP_USER_PASSWORD");
Assert.state(StringUtils.hasLength(password), "No Oracle app password found");
@@ -49,13 +49,13 @@ class PostgresEnvironment {
private final String database;
PostgresEnvironment(Map<String, String> env) {
PostgresEnvironment(Map<String, @Nullable String> env) {
this.username = extract(env, USERNAME_KEYS, DEFAULT_USERNAME);
this.password = extractPassword(env);
this.database = extract(env, DATABASE_KEYS, this.username);
}
private String extract(Map<String, String> env, String[] keys, String defaultValue) {
private String extract(Map<String, @Nullable String> env, String[] keys, String defaultValue) {
for (String key : keys) {
if (env.containsKey(key)) {
return env.get(key);
@@ -64,7 +64,7 @@ class PostgresEnvironment {
return defaultValue;
}
private @Nullable String extractPassword(Map<String, String> env) {
private @Nullable String extractPassword(Map<String, @Nullable String> env) {
if (isUsingTrustHostAuthMethod(env)) {
return null;
}
@@ -74,7 +74,7 @@ class PostgresEnvironment {
return (password != null) ? password : "";
}
private boolean isUsingTrustHostAuthMethod(Map<String, String> env) {
private boolean isUsingTrustHostAuthMethod(Map<String, @Nullable String> env) {
String hostAuthMethod = env.get("POSTGRES_HOST_AUTH_METHOD");
return "trust".equals(hostAuthMethod);
}
@@ -18,6 +18,8 @@ package org.springframework.boot.r2dbc.docker.compose;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -32,11 +34,11 @@ class SqlServerEnvironment {
private final String password;
SqlServerEnvironment(Map<String, String> env) {
SqlServerEnvironment(Map<String, @Nullable String> env) {
this.password = extractPassword(env);
}
private String extractPassword(Map<String, String> env) {
private String extractPassword(Map<String, @Nullable String> env) {
String password = env.get("MSSQL_SA_PASSWORD");
password = (password != null) ? password : env.get("SA_PASSWORD");
Assert.state(StringUtils.hasLength(password), "No MSSQL password found");