From 126e87e44dba044f339f48933adee3d5beb26f6d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 19 Jun 2024 15:42:18 +0100 Subject: [PATCH] Fix appending of JDBC parameters to SQL Server JDBC URL Fixes gh-41146 --- .../connection/jdbc/JdbcUrlBuilder.java | 23 +++++++++++++++---- ...DockerComposeConnectionDetailsFactory.java | 19 ++++++++++++--- .../connection/jdbc/JdbcUrlBuilderTests.java | 16 ++++++++++++- ...nectionDetailsFactoryIntegrationTests.java | 16 ++++++++++++- ...qlserver-with-jdbc-parameters-compose.yaml | 11 +++++++++ 5 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/sqlserver/mssqlserver-with-jdbc-parameters-compose.yaml diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilder.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilder.java index 144686bd321..da4815943c4 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilder.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,20 +68,33 @@ public class JdbcUrlBuilder { private String urlFor(RunningService service, String database) { Assert.notNull(service, "Service must not be null"); - String parameters = getParameters(service); StringBuilder url = new StringBuilder("jdbc:%s://%s:%d".formatted(this.driverProtocol, service.host(), service.ports().get(this.containerPort))); if (StringUtils.hasLength(database)) { url.append("/"); url.append(database); } - url.append(parameters); + String parameters = getParameters(service); + if (StringUtils.hasLength(parameters)) { + appendParameters(url, parameters); + } return url.toString(); } + /** + * Appends to the given {@code url} the given {@code parameters}. + *

+ * The default implementation appends a {@code ?} followed by the {@code parameters}. + * @param url the url + * @param parameters the parameters + * @since 3.2.7 + */ + protected void appendParameters(StringBuilder url, String parameters) { + url.append("?").append(parameters); + } + private String getParameters(RunningService service) { - String parameters = service.labels().get(PARAMETERS_LABEL); - return (StringUtils.hasLength(parameters)) ? "?" + parameters : ""; + return service.labels().get(PARAMETERS_LABEL); } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactory.java index 90d68b909f2..c10f6a58573 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ class SqlServerJdbcDockerComposeConnectionDetailsFactory static class SqlServerJdbcDockerComposeConnectionDetails extends DockerComposeConnectionDetails implements JdbcConnectionDetails { - private static final JdbcUrlBuilder jdbcUrlBuilder = new JdbcUrlBuilder("sqlserver", 1433); + private static final JdbcUrlBuilder jdbcUrlBuilder = new SqlServerJdbcUrlBuilder("sqlserver", 1433); private final SqlServerEnvironment environment; @@ -56,7 +56,7 @@ class SqlServerJdbcDockerComposeConnectionDetailsFactory SqlServerJdbcDockerComposeConnectionDetails(RunningService service) { super(service); this.environment = new SqlServerEnvironment(service.env()); - this.jdbcUrl = disableEncryptionIfNecessary(jdbcUrlBuilder.build(service, "")); + this.jdbcUrl = disableEncryptionIfNecessary(jdbcUrlBuilder.build(service)); } private String disableEncryptionIfNecessary(String jdbcUrl) { @@ -86,6 +86,19 @@ class SqlServerJdbcDockerComposeConnectionDetailsFactory return this.jdbcUrl; } + private static final class SqlServerJdbcUrlBuilder extends JdbcUrlBuilder { + + private SqlServerJdbcUrlBuilder(String driverProtocol, int containerPort) { + super(driverProtocol, containerPort); + } + + @Override + protected void appendParameters(StringBuilder url, String parameters) { + url.append(";").append(parameters); + } + + } + } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilderTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilderTests.java index 6a3e15d8527..bb0589a16ae 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilderTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/jdbc/JdbcUrlBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,6 +67,20 @@ class JdbcUrlBuilderTests { assertThat(url).isEqualTo("jdbc:mydb://myhost:456/mydb?foo=bar"); } + @Test + void buildWithCustomAppendParametersWhenHasParamsLabelBuildsUrl() { + RunningService service = mockService(456, Map.of("org.springframework.boot.jdbc.parameters", "foo=bar")); + String url = new JdbcUrlBuilder("mydb", 1234) { + + @Override + protected void appendParameters(StringBuilder url, String parameters) { + url.append(";").append(parameters); + } + + }.build(service, "mydb"); + assertThat(url).isEqualTo("jdbc:mydb://myhost:456/mydb;foo=bar"); + } + @Test void buildWhenServiceIsNullThrowsException() { assertThatIllegalArgumentException().isThrownBy(() -> this.builder.build(null, "mydb")) diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactoryIntegrationTests.java index 74b1ea45f4f..f4983cf3501 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/service/connection/sqlserver/SqlServerJdbcDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -40,13 +40,27 @@ import static org.assertj.core.api.Assertions.assertThat; disabledReason = "The SQL server image has no ARM support") class SqlServerJdbcDockerComposeConnectionDetailsFactoryIntegrationTests { - @SuppressWarnings("unchecked") @DockerComposeTest(composeFile = "mssqlserver-compose.yaml", image = TestImage.SQL_SERVER) void runCreatesConnectionDetailsThatCanBeUsedToAccessDatabase(JdbcConnectionDetails connectionDetails) throws ClassNotFoundException, LinkageError { assertThat(connectionDetails.getUsername()).isEqualTo("SA"); assertThat(connectionDetails.getPassword()).isEqualTo("verYs3cret"); assertThat(connectionDetails.getJdbcUrl()).startsWith("jdbc:sqlserver://"); + checkDatabaseAccess(connectionDetails); + } + + @DockerComposeTest(composeFile = "mssqlserver-with-jdbc-parameters-compose.yaml", image = TestImage.SQL_SERVER) + void runWithJdbcParametersCreatesConnectionDetailsThatCanBeUsedToAccessDatabase( + JdbcConnectionDetails connectionDetails) throws ClassNotFoundException { + assertThat(connectionDetails.getUsername()).isEqualTo("SA"); + assertThat(connectionDetails.getPassword()).isEqualTo("verYs3cret"); + assertThat(connectionDetails.getJdbcUrl()).startsWith("jdbc:sqlserver://") + .contains(";sendStringParametersAsUnicode=false;"); + checkDatabaseAccess(connectionDetails); + } + + @SuppressWarnings("unchecked") + private void checkDatabaseAccess(JdbcConnectionDetails connectionDetails) throws ClassNotFoundException { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setUrl(connectionDetails.getJdbcUrl()); dataSource.setUsername(connectionDetails.getUsername()); diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/sqlserver/mssqlserver-with-jdbc-parameters-compose.yaml b/spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/sqlserver/mssqlserver-with-jdbc-parameters-compose.yaml new file mode 100644 index 00000000000..76dd4998aee --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/test/resources/org/springframework/boot/docker/compose/service/connection/sqlserver/mssqlserver-with-jdbc-parameters-compose.yaml @@ -0,0 +1,11 @@ +services: + database: + image: '{imageName}' + ports: + - '1433' + environment: + - 'MSSQL_PID=express' + - 'MSSQL_SA_PASSWORD=verYs3cret' + - 'ACCEPT_EULA=yes' + labels: + org.springframework.boot.jdbc.parameters: sendStringParametersAsUnicode=false \ No newline at end of file