Merge branch '2.7.x'

This commit is contained in:
Stephane Nicoll 2021-12-16 17:30:45 +01:00
commit 75d2c36846
10 changed files with 277 additions and 22 deletions

View File

@ -34,9 +34,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
@ -103,6 +105,7 @@ public class BatchAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(DatabasePopulator.class)
@Conditional(OnBatchDatasourceInitializationCondition.class)
static class DataSourceInitializerConfiguration {
@Bean
@ -116,4 +119,12 @@ public class BatchAutoConfiguration {
}
static class OnBatchDatasourceInitializationCondition extends OnDatabaseInitializationCondition {
OnBatchDatasourceInitializationCondition() {
super("Batch", "spring.batch.jdbc.initialize-schema", "spring.batch.initialize-schema");
}
}
}

View File

@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
import org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition;
import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
@ -233,6 +234,7 @@ public class IntegrationAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(JdbcMessageStore.class)
@ConditionalOnSingleCandidate(DataSource.class)
@Conditional(OnIntegrationDatasourceInitializationCondition.class)
protected static class IntegrationJdbcConfiguration {
@Bean
@ -342,4 +344,12 @@ public class IntegrationAutoConfiguration {
}
static class OnIntegrationDatasourceInitializationCondition extends OnDatabaseInitializationCondition {
OnIntegrationDatasourceInitializationCondition() {
super("Integration", "spring.integration.jdbc.initialize-schema");
}
}
}

View File

@ -37,10 +37,12 @@ import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
@ -134,6 +136,7 @@ public class QuartzAutoConfiguration {
@SuppressWarnings("deprecation")
@ConditionalOnMissingBean({ QuartzDataSourceScriptDatabaseInitializer.class,
QuartzDataSourceInitializer.class })
@Conditional(OnQuartzDatasourceInitializationCondition.class)
public QuartzDataSourceScriptDatabaseInitializer quartzDataSourceScriptDatabaseInitializer(
DataSource dataSource, @QuartzDataSource ObjectProvider<DataSource> quartzDataSource,
QuartzProperties properties) {
@ -141,6 +144,14 @@ public class QuartzAutoConfiguration {
return new QuartzDataSourceScriptDatabaseInitializer(dataSourceToUse, properties);
}
static class OnQuartzDatasourceInitializationCondition extends OnDatabaseInitializationCondition {
OnQuartzDatasourceInitializationCondition() {
super("Quartz", "spring.quartz.jdbc.initialize-schema");
}
}
}
}

View File

@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
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.sql.init.OnDatabaseInitializationCondition;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
@ -58,6 +59,7 @@ class JdbcSessionConfiguration {
@SuppressWarnings("deprecation")
@ConditionalOnMissingBean({ JdbcSessionDataSourceScriptDatabaseInitializer.class,
JdbcSessionDataSourceInitializer.class })
@Conditional(OnJdbcSessionDatasourceInitializationCondition.class)
JdbcSessionDataSourceScriptDatabaseInitializer jdbcSessionDataSourceScriptDatabaseInitializer(
@SpringSessionDataSource ObjectProvider<DataSource> sessionDataSource,
ObjectProvider<DataSource> dataSource, JdbcSessionProperties properties) {
@ -84,4 +86,12 @@ class JdbcSessionConfiguration {
}
static class OnJdbcSessionDatasourceInitializationCondition extends OnDatabaseInitializationCondition {
OnJdbcSessionDatasourceInitializationCondition() {
super("Jdbc Session", "spring.session.jdbc.initialize-schema");
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2012-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.sql.init;
import java.util.Locale;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.sql.init.DatabaseInitializationMode;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.StringUtils;
/**
* Condition that checks if the database initialization of a particular component should
* be considered.
*
* @author Stephane Nicoll
* @since 2.6.2
* @see DatabaseInitializationMode
*/
public class OnDatabaseInitializationCondition extends SpringBootCondition {
private final String name;
private final String[] propertyNames;
/**
* Create a new instance with the name of the component and the property names to
* check, in order. If a property is set, its value is used to determine the outcome
* and remaining properties are not tested.
* @param name the name of the component
* @param propertyNames the properties to check (in order)
*/
public OnDatabaseInitializationCondition(String name, String... propertyNames) {
this.name = name;
this.propertyNames = propertyNames;
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String propertyName = getConfiguredProperty(environment);
DatabaseInitializationMode mode = getDatabaseInitializationMode(environment, propertyName);
boolean match = match(mode);
String messagePrefix = (propertyName != null) ? propertyName : "default value";
return new ConditionOutcome(match, ConditionMessage.forCondition(this.name + "Database Initialization")
.because(messagePrefix + " is " + mode));
}
private boolean match(DatabaseInitializationMode mode) {
return !mode.equals(DatabaseInitializationMode.NEVER);
}
private DatabaseInitializationMode getDatabaseInitializationMode(Environment environment, String propertyName) {
if (StringUtils.hasText(propertyName)) {
String candidate = environment.getProperty(propertyName, "embedded").toUpperCase(Locale.ENGLISH);
if (StringUtils.hasText(candidate)) {
return DatabaseInitializationMode.valueOf(candidate);
}
}
return DatabaseInitializationMode.EMBEDDED;
}
private String getConfiguredProperty(Environment environment) {
for (String propertyName : this.propertyNames) {
if (environment.containsProperty(propertyName)) {
return propertyName;
}
}
return null;
}
}

View File

@ -205,6 +205,7 @@ class BatchAutoConfigurationTests {
assertThat(context).hasSingleBean(JobLauncher.class);
assertThat(context.getBean(BatchProperties.class).getJdbc().getInitializeSchema())
.isEqualTo(DatabaseInitializationMode.NEVER);
assertThat(context).doesNotHaveBean(BatchDataSourceScriptDatabaseInitializer.class);
assertThatExceptionOfType(BadSqlGrammarException.class)
.isThrownBy(() -> new JdbcTemplate(context.getBean(DataSource.class))
.queryForList("select * from BATCH_JOB_EXECUTION"));

View File

@ -210,6 +210,7 @@ class IntegrationAutoConfigurationTests {
.withPropertyValues("spring.datasource.generate-unique-name=true",
"spring.integration.jdbc.initialize-schema=never")
.run((context) -> {
assertThat(context).doesNotHaveBean(IntegrationDataSourceScriptDatabaseInitializer.class);
IntegrationProperties properties = context.getBean(IntegrationProperties.class);
assertThat(properties.getJdbc().getInitializeSchema()).isEqualTo(DatabaseInitializationMode.NEVER);
JdbcOperations jdbc = context.getBean(JdbcOperations.class);

View File

@ -114,7 +114,8 @@ class QuartzAutoConfigurationTests {
this.contextRunner.withUserConfiguration(QuartzJobsConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class))
.withPropertyValues("spring.quartz.job-store-type=jdbc").run(assertDataSourceJobStore("dataSource"));
.withPropertyValues("spring.quartz.job-store-type=jdbc")
.run(assertDataSourceInitializedByDataSourceDatabaseScriptInitializer("dataSource"));
}
@Test
@ -134,14 +135,15 @@ class QuartzAutoConfigurationTests {
void withDataSourceNoTransactionManager() {
this.contextRunner.withUserConfiguration(QuartzJobsConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
.withPropertyValues("spring.quartz.job-store-type=jdbc").run(assertDataSourceJobStore("dataSource"));
.withPropertyValues("spring.quartz.job-store-type=jdbc")
.run(assertDataSourceInitializedByDataSourceDatabaseScriptInitializer("dataSource"));
}
@Test
void dataSourceWithQuartzDataSourceQualifierUsedWhenMultiplePresent() {
this.contextRunner.withUserConfiguration(QuartzJobsConfiguration.class, MultipleDataSourceConfiguration.class)
.withPropertyValues("spring.quartz.job-store-type=jdbc")
.run(assertDataSourceJobStore("quartzDataSource"));
.run(assertDataSourceInitializedByDataSourceDatabaseScriptInitializer("quartzDataSource"));
}
@Test
@ -155,23 +157,6 @@ class QuartzAutoConfigurationTests {
});
}
private ContextConsumer<AssertableApplicationContext> assertDataSourceJobStore(String dataSourceName) {
return (context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass()).isAssignableFrom(LocalDataSourceJobStore.class);
JdbcTemplate jdbcTemplate = new JdbcTemplate(context.getBean(dataSourceName, DataSource.class));
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM QRTZ_JOB_DETAILS", Integer.class))
.isEqualTo(2);
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM QRTZ_SIMPLE_TRIGGERS", Integer.class))
.isEqualTo(0);
assertThat(context).hasSingleBean(QuartzDataSourceScriptDatabaseInitializer.class);
QuartzDataSourceScriptDatabaseInitializer initializer = context
.getBean(QuartzDataSourceScriptDatabaseInitializer.class);
assertThat(initializer).hasFieldOrPropertyWithValue("dataSource", context.getBean(dataSourceName));
};
}
@Test
void withTaskExecutor() {
this.contextRunner.withUserConfiguration(MockExecutorConfiguration.class)
@ -277,7 +262,8 @@ class QuartzAutoConfigurationTests {
DataSourceTransactionManagerAutoConfiguration.class, LiquibaseAutoConfiguration.class))
.withPropertyValues("spring.quartz.job-store-type=jdbc", "spring.quartz.jdbc.initialize-schema=never",
"spring.liquibase.change-log=classpath:org/quartz/impl/jdbcjobstore/liquibase.quartz.init.xml")
.run(assertDataSourceJobStore("dataSource"));
.run(assertDataSourceInitialized("dataSource").andThen((context) -> assertThat(context)
.doesNotHaveBean(QuartzDataSourceScriptDatabaseInitializer.class)));
}
@Test
@ -292,7 +278,8 @@ class QuartzAutoConfigurationTests {
.withPropertyValues("spring.quartz.job-store-type=jdbc", "spring.quartz.jdbc.initialize-schema=never",
"spring.flyway.locations=filesystem:" + flywayLocation,
"spring.flyway.baseline-on-migrate=true")
.run(assertDataSourceJobStore("dataSource"));
.run(assertDataSourceInitialized("dataSource").andThen((context) -> assertThat(context)
.doesNotHaveBean(QuartzDataSourceScriptDatabaseInitializer.class)));
}
@Test
@ -352,6 +339,29 @@ class QuartzAutoConfigurationTests {
.hasSingleBean(QuartzDataSourceScriptDatabaseInitializer.class).hasBean("customInitializer"));
}
private ContextConsumer<AssertableApplicationContext> assertDataSourceInitialized(String dataSourceName) {
return (context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass()).isAssignableFrom(LocalDataSourceJobStore.class);
JdbcTemplate jdbcTemplate = new JdbcTemplate(context.getBean(dataSourceName, DataSource.class));
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM QRTZ_JOB_DETAILS", Integer.class))
.isEqualTo(2);
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM QRTZ_SIMPLE_TRIGGERS", Integer.class))
.isEqualTo(0);
};
}
private ContextConsumer<AssertableApplicationContext> assertDataSourceInitializedByDataSourceDatabaseScriptInitializer(
String dataSourceName) {
return assertDataSourceInitialized(dataSourceName).andThen((context) -> {
assertThat(context).hasSingleBean(QuartzDataSourceScriptDatabaseInitializer.class);
QuartzDataSourceScriptDatabaseInitializer initializer = context
.getBean(QuartzDataSourceScriptDatabaseInitializer.class);
assertThat(initializer).hasFieldOrPropertyWithValue("dataSource", context.getBean(dataSourceName));
});
}
private ContextConsumer<AssertableApplicationContext> assertSchedulerName(String schedulerName) {
return (context) -> {
assertThat(context).hasSingleBean(SchedulerFactoryBean.class);

View File

@ -110,6 +110,7 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.initialize-schema=never")
.run((context) -> {
assertThat(context).doesNotHaveBean(JdbcSessionDataSourceScriptDatabaseInitializer.class);
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("tableName", "SPRING_SESSION");

View File

@ -0,0 +1,110 @@
/*
* Copyright 2012-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.sql.init;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link OnDatabaseInitializationCondition}.
*
* @author Stephane Nicoll
*/
class OnDatabaseInitializationConditionTests {
@Test
void getMatchOutcomeWithPropertyNoSetMatches() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode");
ConditionOutcome outcome = condition
.getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.another", "noise")), null);
assertThat(outcome.isMatch()).isTrue();
}
@Test
void getMatchOutcomeWithPropertySetToAlwaysMatches() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode");
ConditionOutcome outcome = condition
.getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=always")), null);
assertThat(outcome.isMatch()).isTrue();
}
@Test
void getMatchOutcomeWithPropertySetToEmbeddedMatches() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode");
ConditionOutcome outcome = condition
.getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=embedded")), null);
assertThat(outcome.isMatch()).isTrue();
}
@Test
void getMatchOutcomeWithPropertySetToNeverDoesNotMatch() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode");
ConditionOutcome outcome = condition
.getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=never")), null);
assertThat(outcome.isMatch()).isFalse();
}
@Test
void getMatchOutcomeWithPropertySetToEmptyStringIsIgnored() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode");
ConditionOutcome outcome = condition
.getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode")), null);
assertThat(outcome.isMatch()).isTrue();
}
@Test
void getMatchOutcomeWithMultiplePropertiesUsesFirstSet() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode",
"test.schema-mode", "test.init-schema-mode");
ConditionOutcome outcome = condition
.getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-schema-mode=embedded")), null);
assertThat(outcome.isMatch()).isTrue();
assertThat(outcome.getMessage()).isEqualTo("TestDatabase Initialization test.init-schema-mode is EMBEDDED");
}
@Test
void getMatchOutcomeHasDedicatedDescription() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode");
ConditionOutcome outcome = condition
.getMatchOutcome(mockConditionContext(TestPropertyValues.of("test.init-mode=embedded")), null);
assertThat(outcome.getMessage()).isEqualTo("TestDatabase Initialization test.init-mode is EMBEDDED");
}
@Test
void getMatchOutcomeHasWhenPropertyIsNotSetHasDefaultDescription() {
OnDatabaseInitializationCondition condition = new OnDatabaseInitializationCondition("Test", "test.init-mode");
ConditionOutcome outcome = condition.getMatchOutcome(mockConditionContext(TestPropertyValues.empty()), null);
assertThat(outcome.getMessage()).isEqualTo("TestDatabase Initialization default value is EMBEDDED");
}
private ConditionContext mockConditionContext(TestPropertyValues propertyValues) {
MockEnvironment environment = new MockEnvironment();
propertyValues.applyTo(environment);
ConditionContext conditionContext = mock(ConditionContext.class);
given(conditionContext.getEnvironment()).willReturn(environment);
return conditionContext;
}
}