commit
91778e9f96
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2022 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -46,6 +46,7 @@ import org.springframework.core.log.LogMessage;
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Marten Deinum
|
* @author Marten Deinum
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Phillip Webb
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
|
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
|
||||||
|
|
@ -57,46 +58,25 @@ public class H2ConsoleAutoConfiguration {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(H2ConsoleAutoConfiguration.class);
|
private static final Log logger = LogFactory.getLog(H2ConsoleAutoConfiguration.class);
|
||||||
|
|
||||||
|
private final H2ConsoleProperties properties;
|
||||||
|
|
||||||
|
H2ConsoleAutoConfiguration(H2ConsoleProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ServletRegistrationBean<JakartaWebServlet> h2Console(H2ConsoleProperties properties,
|
public ServletRegistrationBean<JakartaWebServlet> h2Console() {
|
||||||
ObjectProvider<DataSource> dataSource) {
|
String path = this.properties.getPath();
|
||||||
String path = properties.getPath();
|
|
||||||
String urlMapping = path + (path.endsWith("/") ? "*" : "/*");
|
String urlMapping = path + (path.endsWith("/") ? "*" : "/*");
|
||||||
ServletRegistrationBean<JakartaWebServlet> registration = new ServletRegistrationBean<>(new JakartaWebServlet(),
|
ServletRegistrationBean<JakartaWebServlet> registration = new ServletRegistrationBean<>(new JakartaWebServlet(),
|
||||||
urlMapping);
|
urlMapping);
|
||||||
configureH2ConsoleSettings(registration, properties.getSettings());
|
configureH2ConsoleSettings(registration, this.properties.getSettings());
|
||||||
if (logger.isInfoEnabled()) {
|
|
||||||
withThreadContextClassLoader(getClass().getClassLoader(), () -> logDataSources(dataSource, path));
|
|
||||||
}
|
|
||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void withThreadContextClassLoader(ClassLoader classLoader, Runnable action) {
|
@Bean
|
||||||
ClassLoader previous = Thread.currentThread().getContextClassLoader();
|
H2ConsoleLogger h2ConsoleLogger(ObjectProvider<DataSource> dataSource) {
|
||||||
try {
|
return new H2ConsoleLogger(dataSource, this.properties.getPath());
|
||||||
Thread.currentThread().setContextClassLoader(classLoader);
|
|
||||||
action.run();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Thread.currentThread().setContextClassLoader(previous);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logDataSources(ObjectProvider<DataSource> dataSource, String path) {
|
|
||||||
List<String> urls = dataSource.orderedStream().map(this::getConnectionUrl).filter(Objects::nonNull).toList();
|
|
||||||
if (!urls.isEmpty()) {
|
|
||||||
logger.info(LogMessage.format("H2 console available at '%s'. %s available at %s", path,
|
|
||||||
(urls.size() > 1) ? "Databases" : "Database", String.join(", ", urls)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getConnectionUrl(DataSource dataSource) {
|
|
||||||
try (Connection connection = dataSource.getConnection()) {
|
|
||||||
return "'" + connection.getMetaData().getURL() + "'";
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureH2ConsoleSettings(ServletRegistrationBean<JakartaWebServlet> registration,
|
private void configureH2ConsoleSettings(ServletRegistrationBean<JakartaWebServlet> registration,
|
||||||
|
|
@ -112,4 +92,46 @@ public class H2ConsoleAutoConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class H2ConsoleLogger {
|
||||||
|
|
||||||
|
H2ConsoleLogger(ObjectProvider<DataSource> dataSources, String path) {
|
||||||
|
if (logger.isInfoEnabled()) {
|
||||||
|
ClassLoader classLoader = getClass().getClassLoader();
|
||||||
|
withThreadContextClassLoader(classLoader, () -> log(getConnectionUrls(dataSources), path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void withThreadContextClassLoader(ClassLoader classLoader, Runnable action) {
|
||||||
|
ClassLoader previous = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
Thread.currentThread().setContextClassLoader(classLoader);
|
||||||
|
action.run();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getConnectionUrls(ObjectProvider<DataSource> dataSources) {
|
||||||
|
return dataSources.orderedStream().map(this::getConnectionUrl).filter(Objects::nonNull).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getConnectionUrl(DataSource dataSource) {
|
||||||
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
|
return "'" + connection.getMetaData().getURL() + "'";
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void log(List<String> urls, String path) {
|
||||||
|
if (!urls.isEmpty()) {
|
||||||
|
logger.info(LogMessage.format("H2 console available at '%s'. %s available at %s", path,
|
||||||
|
(urls.size() > 1) ? "Databases" : "Database", String.join(", ", urls)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -30,15 +30,20 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindException;
|
import org.springframework.boot.context.properties.ConfigurationPropertiesBindException;
|
||||||
import org.springframework.boot.context.properties.bind.BindException;
|
import org.springframework.boot.context.properties.bind.BindException;
|
||||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
import org.springframework.boot.test.system.CapturedOutput;
|
import org.springframework.boot.test.system.CapturedOutput;
|
||||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||||
|
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
@ -51,6 +56,7 @@ import static org.mockito.Mockito.mock;
|
||||||
* @author Marten Deinum
|
* @author Marten Deinum
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Shraddha Yeole
|
* @author Shraddha Yeole
|
||||||
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
class H2ConsoleAutoConfigurationTests {
|
class H2ConsoleAutoConfigurationTests {
|
||||||
|
|
||||||
|
|
@ -163,6 +169,22 @@ class H2ConsoleAutoConfigurationTests {
|
||||||
.run((context) -> assertThat(context.isRunning()).isTrue());
|
.run((context) -> assertThat(context.isRunning()).isTrue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@ExtendWith(OutputCaptureExtension.class)
|
||||||
|
void dataSourceIsNotInitializedEarly(CapturedOutput output) {
|
||||||
|
new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
|
||||||
|
.withConfiguration(AutoConfigurations.of(H2ConsoleAutoConfiguration.class,
|
||||||
|
ServletWebServerFactoryAutoConfiguration.class))
|
||||||
|
.withUserConfiguration(EarlyInitializationConfiguration.class)
|
||||||
|
.withPropertyValues("spring.h2.console.enabled=true", "server.port=0")
|
||||||
|
.run((context) -> {
|
||||||
|
try (Connection connection = context.getBean(DataSource.class).getConnection()) {
|
||||||
|
assertThat(output).contains("H2 console available at '/h2-console'. Database available at '"
|
||||||
|
+ connection.getMetaData().getURL() + "'");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class FailingDataSourceConfiguration {
|
static class FailingDataSourceConfiguration {
|
||||||
|
|
||||||
|
|
@ -206,4 +228,15 @@ class H2ConsoleAutoConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class EarlyInitializationConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
DataSource dataSource(ConfigurableApplicationContext applicationContext) {
|
||||||
|
assertThat(applicationContext.getBeanFactory().isConfigurationFrozen()).isTrue();
|
||||||
|
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue