From 7b94b40a19fa363c3602341a1a61e05aeb35918d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 9 Jul 2021 11:12:52 +0100 Subject: [PATCH] Ensure that Session JDBC schema is in place before DB is accessed Fixes gh-27208 --- .../session/JdbcSessionConfiguration.java | 39 +++++++++++-- .../SessionAutoConfigurationJdbcTests.java | 56 +++++++++++++++++-- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java index e8e749a8a9..38cd8b25dc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/JdbcSessionConfiguration.java @@ -20,12 +20,16 @@ import java.time.Duration; import javax.sql.DataSource; +import liquibase.integration.spring.SpringLiquibase; +import org.flywaydb.core.Flyway; + import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -81,15 +85,40 @@ class JdbcSessionConfiguration { } + @Configuration(proxyBeanMethods = false) + static class JdbcIndexedSessionRepositoryDependencyConfiguration { + + @Bean + JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor dataSourceInitializerJdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor() { + return new JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor( + JdbcSessionDataSourceInitializer.class); + } + + @Bean + @ConditionalOnClass(name = "org.flywaydb.core.Flyway") + JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor flywayJdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor() { + return new JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor(FlywayMigrationInitializer.class, + Flyway.class); + } + + @Bean + @ConditionalOnClass(name = "liquibase.integration.spring.SpringLiquibase") + JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor liquibaseJdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor() { + return new JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor(FlywayMigrationInitializer.class, + SpringLiquibase.class); + } + + } + /** - * Post processor to ensure that {@link JdbcIndexedSessionRepository} beans depend on - * any {@link JdbcSessionDataSourceInitializer} beans. + * {@link AbstractDependsOnBeanFactoryPostProcessor} for Spring Session JDBC's + * {@link JdbcIndexedSessionRepository}. */ - static class DataSourceInitializationJdbcIndexedSessionRepositoryDependencyConfiguration + static class JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor extends AbstractDependsOnBeanFactoryPostProcessor { - DataSourceInitializationJdbcIndexedSessionRepositoryDependencyConfiguration() { - super(JdbcIndexedSessionRepository.class, JdbcSessionDataSourceInitializer.class); + JdbcIndexedSessionRepositoryDependsOnBeanFactoryPostProcessor(Class... dependencyTypes) { + super(JdbcIndexedSessionRepository.class, dependencyTypes); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java index de6e62d29e..024b70c304 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationJdbcTests.java @@ -21,10 +21,13 @@ import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.session.JdbcSessionConfiguration.SpringBootJdbcHttpSessionConfiguration; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.jdbc.DataSourceInitializationMode; @@ -57,9 +60,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfigurationTests { private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class, - SessionAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(DataSourceTransactionManagerAutoConfiguration.class, + JdbcTemplateAutoConfiguration.class, SessionAutoConfiguration.class)) + .withUserConfiguration(EmbeddedDataSourceConfiguration.class) .withPropertyValues("spring.datasource.generate-unique-name=true"); @Test @@ -190,6 +193,51 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration }); } + @Test + void sessionRepositoryBeansDependOnJdbcSessionDataSourceInitializer() { + this.contextRunner.withPropertyValues("spring.session.store-type=jdbc").run((context) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + String[] sessionRepositoryNames = beanFactory.getBeanNamesForType(JdbcIndexedSessionRepository.class); + assertThat(sessionRepositoryNames).isNotEmpty(); + for (String sessionRepositoryName : sessionRepositoryNames) { + assertThat(beanFactory.getBeanDefinition(sessionRepositoryName).getDependsOn()) + .contains("jdbcSessionDataSourceInitializer"); + } + }); + } + + @Test + void sessionRepositoryBeansDependOnFlyway() { + this.contextRunner.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class)) + .withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.initialize-schema=never") + .run((context) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + String[] sessionRepositoryNames = beanFactory + .getBeanNamesForType(JdbcIndexedSessionRepository.class); + assertThat(sessionRepositoryNames).isNotEmpty(); + for (String sessionRepositoryName : sessionRepositoryNames) { + assertThat(beanFactory.getBeanDefinition(sessionRepositoryName).getDependsOn()) + .contains("flyway", "flywayInitializer"); + } + }); + } + + @Test + void sessionRepositoryBeansDependOnLiquibase() { + this.contextRunner.withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class)) + .withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.initialize-schema=never") + .run((context) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + String[] sessionRepositoryNames = beanFactory + .getBeanNamesForType(JdbcIndexedSessionRepository.class); + assertThat(sessionRepositoryNames).isNotEmpty(); + for (String sessionRepositoryName : sessionRepositoryNames) { + assertThat(beanFactory.getBeanDefinition(sessionRepositoryName).getDependsOn()) + .contains("liquibase"); + } + }); + } + @Configuration static class SessionDataSourceConfiguration {