From f31b8ec2266d965348492eaae20b67e25ec536ce Mon Sep 17 00:00:00 2001 From: StitzL Date: Wed, 6 Oct 2021 10:48:19 +0200 Subject: [PATCH 1/2] Log URLs for all DataSources This commit logs the path of the H2 console, followed by the comma-separated ordered URLs of each data source that offers a connection. See gh-28204 --- .../h2/H2ConsoleAutoConfiguration.java | 35 +++++++++---- .../h2/H2ConsoleAutoConfigurationTests.java | 51 +++++++++++++++++-- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java index 2a34342119..9681bf1723 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java @@ -17,6 +17,9 @@ package org.springframework.boot.autoconfigure.h2; import java.sql.Connection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import javax.sql.DataSource; @@ -63,18 +66,32 @@ public class H2ConsoleAutoConfiguration { String urlMapping = path + (path.endsWith("/") ? "*" : "/*"); ServletRegistrationBean registration = new ServletRegistrationBean<>(new WebServlet(), urlMapping); configureH2ConsoleSettings(registration, properties.getSettings()); - dataSource.ifAvailable((available) -> { - try (Connection connection = available.getConnection()) { - logger.info("H2 console available at '" + path + "'. Database available at '" - + connection.getMetaData().getURL() + "'"); - } - catch (Exception ex) { - // Continue - } - }); + if (logger.isInfoEnabled()) { + logDataSources(dataSource, path); + } + return registration; } + private void logDataSources(ObjectProvider dataSource, String path) { + List urls = dataSource.orderedStream() + .map((available) -> { + String url = null; + try (Connection connection = available.getConnection()) { + url = connection.getMetaData().getURL(); + } catch (Exception ex) { + + } + return url; + }).filter(Objects::nonNull) + .collect(Collectors.toList()); + if (!urls.isEmpty()) { + String log = urls.stream().collect(Collectors.joining("', '", + "H2 console available at '" + path + "'. Database(s) available at '", "'.")); + logger.info(log); + } + } + private void configureH2ConsoleSettings(ServletRegistrationBean registration, Settings settings) { if (settings.isTrace()) { registration.addInitParameter("trace", ""); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java index c0bdc391b2..4af9187baa 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.h2; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.SQLException; import javax.sql.DataSource; @@ -33,6 +34,7 @@ import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -114,25 +116,42 @@ class H2ConsoleAutoConfigurationTests { @Test @ExtendWith(OutputCaptureExtension.class) - void dataSourceUrlIsLoggedWhenAvailable(CapturedOutput output) { + void singleDataSourceUrlIsLoggedWhenOnlyOneAvailable(CapturedOutput output) { this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) .withPropertyValues("spring.h2.console.enabled=true").run((context) -> { try (Connection connection = context.getBean(DataSource.class).getConnection()) { assertThat(output) - .contains("Database available at '" + connection.getMetaData().getURL() + "'"); + .contains("H2 console available at '/h2-console'. Database(s) available at '" + connection.getMetaData().getURL() + "'"); } }); } @Test + @ExtendWith(OutputCaptureExtension.class) + void allDataSourceUrlsAreLoggedWhenMultipleAvailable(CapturedOutput output) { + this.contextRunner.withUserConfiguration(MultiDataSourceConfiguration.class) + .withPropertyValues("spring.h2.console.enabled=true").run((context) -> + assertThat(output).contains("H2 console available at '/h2-console'. Database(s) available at 'primaryUrl', 'secondaryUrl'")); + } + + @Test + @ExtendWith(OutputCaptureExtension.class) + void noDataSourceIsLoggedWhenNoneAvailable(CapturedOutput output) { + this.contextRunner.withUserConfiguration(FailingDataSourceConfiguration.class) + .withPropertyValues("spring.h2.console.enabled=true") + .run((context) -> assertThat(output).isEmpty()); + } + + + @Test void h2ConsoleShouldNotFailIfDatabaseConnectionFails() { - this.contextRunner.withUserConfiguration(CustomDataSourceConfiguration.class) + this.contextRunner.withUserConfiguration(FailingDataSourceConfiguration.class) .withPropertyValues("spring.h2.console.enabled=true") .run((context) -> assertThat(context.isRunning()).isTrue()); } @Configuration(proxyBeanMethods = false) - static class CustomDataSourceConfiguration { + static class FailingDataSourceConfiguration { @Bean DataSource dataSource() throws SQLException { @@ -143,4 +162,28 @@ class H2ConsoleAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class MultiDataSourceConfiguration extends FailingDataSourceConfiguration { + + @Bean + @Primary + DataSource primaryDataSource() throws SQLException { + return getDataSource("primaryUrl"); + } + + @Bean + DataSource anotherDataSource() throws SQLException { + return getDataSource("secondaryUrl"); + } + + private DataSource getDataSource(String url) throws SQLException { + DataSource dataSource = mock(DataSource.class); + given(dataSource.getConnection()).willReturn(mock(Connection.class)); + given(dataSource.getConnection().getMetaData()).willReturn(mock(DatabaseMetaData.class)); + given(dataSource.getConnection().getMetaData().getURL()).willReturn(url); + return dataSource; + } + + } + } From 0b32215c136cef23248b68cda40c4cfc3dc5024d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 14 Oct 2021 09:18:38 +0200 Subject: [PATCH 2/2] Polish "Log URLs for all DataSources" See gh-28204 --- .../h2/H2ConsoleAutoConfiguration.java | 28 ++++++------- .../h2/H2ConsoleAutoConfigurationTests.java | 41 ++++++++++--------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java index 9681bf1723..85a7fdc8d5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.java @@ -69,26 +69,24 @@ public class H2ConsoleAutoConfiguration { if (logger.isInfoEnabled()) { logDataSources(dataSource, path); } - return registration; } private void logDataSources(ObjectProvider dataSource, String path) { - List urls = dataSource.orderedStream() - .map((available) -> { - String url = null; - try (Connection connection = available.getConnection()) { - url = connection.getMetaData().getURL(); - } catch (Exception ex) { - - } - return url; - }).filter(Objects::nonNull) - .collect(Collectors.toList()); + List urls = dataSource.orderedStream().map((available) -> { + try (Connection connection = available.getConnection()) { + return "'" + connection.getMetaData().getURL() + "'"; + } + catch (Exception ex) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); if (!urls.isEmpty()) { - String log = urls.stream().collect(Collectors.joining("', '", - "H2 console available at '" + path + "'. Database(s) available at '", "'.")); - logger.info(log); + StringBuilder sb = new StringBuilder("H2 console available at '").append(path).append("'. "); + String tmp = (urls.size() > 1) ? "Databases" : "Database"; + sb.append(tmp).append(" available at "); + sb.append(String.join(", ", urls)); + logger.info(sb.toString()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java index 4af9187baa..132b37a0d1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfigurationTests.java @@ -34,7 +34,7 @@ import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; +import org.springframework.core.annotation.Order; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -120,30 +120,30 @@ class H2ConsoleAutoConfigurationTests { this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) .withPropertyValues("spring.h2.console.enabled=true").run((context) -> { try (Connection connection = context.getBean(DataSource.class).getConnection()) { - assertThat(output) - .contains("H2 console available at '/h2-console'. Database(s) available at '" + connection.getMetaData().getURL() + "'"); + assertThat(output).contains("H2 console available at '/h2-console'. Database available at '" + + connection.getMetaData().getURL() + "'"); } }); } - @Test - @ExtendWith(OutputCaptureExtension.class) - void allDataSourceUrlsAreLoggedWhenMultipleAvailable(CapturedOutput output) { - this.contextRunner.withUserConfiguration(MultiDataSourceConfiguration.class) - .withPropertyValues("spring.h2.console.enabled=true").run((context) -> - assertThat(output).contains("H2 console available at '/h2-console'. Database(s) available at 'primaryUrl', 'secondaryUrl'")); - } - @Test @ExtendWith(OutputCaptureExtension.class) void noDataSourceIsLoggedWhenNoneAvailable(CapturedOutput output) { this.contextRunner.withUserConfiguration(FailingDataSourceConfiguration.class) .withPropertyValues("spring.h2.console.enabled=true") - .run((context) -> assertThat(output).isEmpty()); + .run((context) -> assertThat(output).doesNotContain("H2 console available")); } + @Test + @ExtendWith(OutputCaptureExtension.class) + void allDataSourceUrlsAreLoggedWhenMultipleAvailable(CapturedOutput output) { + this.contextRunner + .withUserConfiguration(FailingDataSourceConfiguration.class, MultiDataSourceConfiguration.class) + .withPropertyValues("spring.h2.console.enabled=true").run((context) -> assertThat(output).contains( + "H2 console available at '/h2-console'. Databases available at 'someJdbcUrl', 'anotherJdbcUrl'")); + } - @Test + @Test void h2ConsoleShouldNotFailIfDatabaseConnectionFails() { this.contextRunner.withUserConfiguration(FailingDataSourceConfiguration.class) .withPropertyValues("spring.h2.console.enabled=true") @@ -163,20 +163,21 @@ class H2ConsoleAutoConfigurationTests { } @Configuration(proxyBeanMethods = false) - static class MultiDataSourceConfiguration extends FailingDataSourceConfiguration { + static class MultiDataSourceConfiguration { @Bean - @Primary - DataSource primaryDataSource() throws SQLException { - return getDataSource("primaryUrl"); + @Order(5) + DataSource anotherDataSource() throws SQLException { + return mockDataSource("anotherJdbcUrl"); } @Bean - DataSource anotherDataSource() throws SQLException { - return getDataSource("secondaryUrl"); + @Order(0) + DataSource someDataSource() throws SQLException { + return mockDataSource("someJdbcUrl"); } - private DataSource getDataSource(String url) throws SQLException { + private DataSource mockDataSource(String url) throws SQLException { DataSource dataSource = mock(DataSource.class); given(dataSource.getConnection()).willReturn(mock(Connection.class)); given(dataSource.getConnection().getMetaData()).willReturn(mock(DatabaseMetaData.class));