Provide more pluggable way to indicate DataSource init dependencies
Closes gh-17619 Closes gh-25559pull/25562/head
parent
99b7d29c2c
commit
ed72bca6c8
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.flyway;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDataSourceInitializerDetector;
|
||||
import org.springframework.boot.jdbc.init.DataSourceInitializerDetector;
|
||||
|
||||
/**
|
||||
* A {@link DataSourceInitializerDetector} for {@link FlywayMigrationInitializer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class FlywayMigrationInitializerDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
|
||||
return Collections.singleton(FlywayMigrationInitializer.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDataSourceInitializerDetector;
|
||||
import org.springframework.boot.jdbc.init.DataSourceInitializerDetector;
|
||||
|
||||
/**
|
||||
* A {@link DataSourceInitializerDetector} for {@link DataSourceInitialization}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class DataSourceInitializationDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
|
||||
return Collections.singleton(DataSourceInitialization.class);
|
||||
}
|
||||
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* 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.orm.jpa;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.resource.transaction.spi.DdlTransactionIsolator;
|
||||
import org.hibernate.tool.schema.TargetType;
|
||||
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
|
||||
import org.hibernate.tool.schema.internal.exec.GenerationTarget;
|
||||
import org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase;
|
||||
import org.hibernate.tool.schema.internal.exec.JdbcContext;
|
||||
import org.hibernate.tool.schema.spi.ExecutionOptions;
|
||||
import org.hibernate.tool.schema.spi.SchemaCreator;
|
||||
import org.hibernate.tool.schema.spi.SourceDescriptor;
|
||||
import org.hibernate.tool.schema.spi.TargetDescriptor;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer;
|
||||
|
||||
/**
|
||||
* Spring Boot {@link SchemaCreator}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SpringBootSchemaCreator implements SchemaCreator {
|
||||
|
||||
private static final CoreMessageLogger log = CoreLogging.messageLogger(SpringBootSchemaCreator.class);
|
||||
|
||||
private final HibernateSchemaManagementTool tool;
|
||||
|
||||
private final DataSourceInitializer dataSourceInitializer;
|
||||
|
||||
private final SchemaCreator creator;
|
||||
|
||||
SpringBootSchemaCreator(HibernateSchemaManagementTool tool, SchemaCreator creator,
|
||||
DataSourceInitializer dataSourceInitializer) {
|
||||
this.tool = tool;
|
||||
this.creator = creator;
|
||||
this.dataSourceInitializer = dataSourceInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCreation(Metadata metadata, ExecutionOptions options, SourceDescriptor sourceDescriptor,
|
||||
TargetDescriptor targetDescriptor) {
|
||||
if (!targetDescriptor.getTargetTypes().contains(TargetType.DATABASE)) {
|
||||
this.creator.doCreation(metadata, options, sourceDescriptor, targetDescriptor);
|
||||
return;
|
||||
}
|
||||
GenerationTarget databaseTarget = getDatabaseTarget(options, targetDescriptor);
|
||||
databaseTarget.prepare();
|
||||
try {
|
||||
this.creator.doCreation(metadata, options, sourceDescriptor, targetDescriptor);
|
||||
this.dataSourceInitializer.initializeDataSource();
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
databaseTarget.release();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.debugf("Problem releasing GenerationTarget [%s] : %s", databaseTarget, ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GenerationTarget getDatabaseTarget(ExecutionOptions options, TargetDescriptor targetDescriptor) {
|
||||
JdbcContext jdbcContext = this.tool.resolveJdbcContext(options.getConfigurationValues());
|
||||
DdlTransactionIsolator ddlTransactionIsolator = this.tool.getDdlTransactionIsolator(jdbcContext);
|
||||
return new GenerationTargetToDatabase(ddlTransactionIsolator);
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.orm.jpa;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
|
||||
import org.hibernate.tool.schema.spi.SchemaCreator;
|
||||
import org.hibernate.tool.schema.spi.SchemaManagementTool;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer;
|
||||
|
||||
/**
|
||||
* Spring Boot {@link SchemaManagementTool}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SpringBootSchemaManagementTool extends HibernateSchemaManagementTool {
|
||||
|
||||
private final DataSourceInitializer dataSourceInitializer;
|
||||
|
||||
SpringBootSchemaManagementTool(DataSourceInitializer dataSourceInitializer) {
|
||||
this.dataSourceInitializer = dataSourceInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public SchemaCreator getSchemaCreator(Map options) {
|
||||
SchemaCreator creator = super.getSchemaCreator(options);
|
||||
return new SpringBootSchemaCreator(this, creator, this.dataSourceInitializer);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.flyway;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.flywaydb.core.Flyway;
|
||||
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDataSourceInitializerDetector;
|
||||
import org.springframework.boot.jdbc.init.DataSourceInitializerDetector;
|
||||
|
||||
/**
|
||||
* A {@link DataSourceInitializerDetector} for {@link Flyway}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class FlywayDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
|
||||
return Collections.singleton(Flyway.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Custom support for Flyway database migration.
|
||||
*/
|
||||
package org.springframework.boot.flyway;
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDependsOnDataSourceInitializationDetector;
|
||||
import org.springframework.boot.jdbc.init.DependsOnDataSourceInitializationDetector;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
|
||||
/**
|
||||
* {@link DependsOnDataSourceInitializationDetector} for Spring Framework's JDBC support.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SpringJdbcDependsOnDataSourceInitializationDetector
|
||||
extends AbstractBeansOfTypeDependsOnDataSourceInitializationDetector {
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes() {
|
||||
return new HashSet<>(Arrays.asList(JdbcOperations.class, NamedParameterJdbcOperations.class));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
||||
/**
|
||||
* Base class for {@link DataSourceInitializerDetector DataSourceInitializerDetectors}
|
||||
* that detect {@link DataSource} beans by type.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public abstract class AbstractBeansOfTypeDataSourceInitializerDetector implements DataSourceInitializerDetector {
|
||||
|
||||
/**
|
||||
* Returns the bean types that should be detected as being data source initializers.
|
||||
* @return the data source initializer bean types
|
||||
*/
|
||||
protected abstract Set<Class<?>> getDataSourceInitializerBeanTypes();
|
||||
|
||||
@Override
|
||||
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
|
||||
try {
|
||||
Set<Class<?>> types = getDataSourceInitializerBeanTypes();
|
||||
return new BeansOfTypeDetector(types).detect(beanFactory);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
||||
/**
|
||||
* Base class for {@link DependsOnDataSourceInitializationDetector
|
||||
* InitializedDataSourceDependentDetector} that detect by type beans that depend upon data
|
||||
* source initialization.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public abstract class AbstractBeansOfTypeDependsOnDataSourceInitializationDetector
|
||||
implements DependsOnDataSourceInitializationDetector {
|
||||
|
||||
/**
|
||||
* Returns the bean types that should be detected as depending on data source
|
||||
* initialization.
|
||||
* @return the data source initialization dependent bean types
|
||||
*/
|
||||
protected abstract Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes();
|
||||
|
||||
@Override
|
||||
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
|
||||
try {
|
||||
Set<Class<?>> types = getDependsOnDataSourceInitializationBeanTypes();
|
||||
return new BeansOfTypeDetector(types).detect(beanFactory);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
||||
/**
|
||||
* {@link DependsOnDataSourceInitializationDetector} that detects beans annotated with
|
||||
* {@link DependsOnDataSourceInitialization}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class AnnotationDependsOnDataSourceInitializationDetector implements DependsOnDataSourceInitializationDetector {
|
||||
|
||||
@Override
|
||||
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
|
||||
Set<String> dependentBeans = new HashSet<>();
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
if (beanFactory.findAnnotationOnBean(beanName, DependsOnDataSourceInitialization.class) != null) {
|
||||
dependentBeans.add(beanName);
|
||||
}
|
||||
}
|
||||
return dependentBeans;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
|
||||
/**
|
||||
* Helper class for detecting beans of particular types in a bean factory.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class BeansOfTypeDetector {
|
||||
|
||||
private final Set<Class<?>> types;
|
||||
|
||||
BeansOfTypeDetector(Set<Class<?>> types) {
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
Set<String> detect(ListableBeanFactory beanFactory) {
|
||||
Set<String> beanNames = new HashSet<>();
|
||||
for (Class<?> type : this.types) {
|
||||
try {
|
||||
String[] names = beanFactory.getBeanNamesForType(type, true, false);
|
||||
Arrays.stream(names).map(BeanFactoryUtils::transformedBeanName).forEach(beanNames::add);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
return beanNames;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.boot.util.Instantiator;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configures beans that depend upon DataSource initialization with
|
||||
* {@link BeanDefinition#getDependsOn()} dependencies upon beans that perform
|
||||
* {@link DataSource} initialization. Intended for {@link Import import} in configuration
|
||||
* classes that define {@code DataSource} initialization beans or that define beans that
|
||||
* require DataSource initialization to have completed before they are initialized.
|
||||
* <p>
|
||||
* Beans that initialize a {@link DataSource} are identified by
|
||||
* {@link DataSourceInitializerDetector DataSourceInitializerDetectors}. Beans that depend
|
||||
* upon DataSource initialization are identified by
|
||||
* {@link DependsOnDataSourceInitializationDetector
|
||||
* DependsOnDataSourceInitializationDetectors}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.5.0
|
||||
* @see DataSourceInitializerDetector
|
||||
* @see DependsOnDataSourceInitializationDetector
|
||||
* @see DependsOnDataSourceInitialization
|
||||
*/
|
||||
public class DataSourceInitializationDependencyConfigurer implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
DataSourceInitializationDependencyConfigurer(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
if (registry.containsBeanDefinition(DependsOnDataSourceInitializationPostProcessor.class.getName())) {
|
||||
return;
|
||||
}
|
||||
registry.registerBeanDefinition(DependsOnDataSourceInitializationPostProcessor.class.getName(),
|
||||
BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DependsOnDataSourceInitializationPostProcessor.class,
|
||||
() -> new DependsOnDataSourceInitializationPostProcessor(this.environment))
|
||||
.getBeanDefinition());
|
||||
}
|
||||
|
||||
static class DependsOnDataSourceInitializationPostProcessor implements BeanFactoryPostProcessor {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
DependsOnDataSourceInitializationPostProcessor(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
Set<String> detectedDataSourceInitializers = detectDataSourceInitializers(beanFactory);
|
||||
for (String dependentDefinitionName : detectDependsOnDataSourceInitialization(beanFactory,
|
||||
this.environment)) {
|
||||
BeanDefinition definition = getBeanDefinition(dependentDefinitionName, beanFactory);
|
||||
String[] dependencies = definition.getDependsOn();
|
||||
for (String dependencyName : detectedDataSourceInitializers) {
|
||||
dependencies = StringUtils.addStringToArray(dependencies, dependencyName);
|
||||
}
|
||||
definition.setDependsOn(dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> detectDataSourceInitializers(ConfigurableListableBeanFactory beanFactory) {
|
||||
List<DataSourceInitializerDetector> detectors = instantiateDetectors(beanFactory, this.environment,
|
||||
DataSourceInitializerDetector.class);
|
||||
Set<String> detected = new HashSet<>();
|
||||
for (DataSourceInitializerDetector detector : detectors) {
|
||||
for (String initializerName : detector.detect(beanFactory)) {
|
||||
detected.add(initializerName);
|
||||
beanFactory.getBeanDefinition(initializerName)
|
||||
.setAttribute(DataSourceInitializerDetector.class.getName(), detector.getClass().getName());
|
||||
}
|
||||
}
|
||||
detected = Collections.unmodifiableSet(detected);
|
||||
for (DataSourceInitializerDetector detector : detectors) {
|
||||
detector.detectionComplete(beanFactory, detected);
|
||||
}
|
||||
return detected;
|
||||
}
|
||||
|
||||
private Collection<String> detectDependsOnDataSourceInitialization(ConfigurableListableBeanFactory beanFactory,
|
||||
Environment environment) {
|
||||
List<DependsOnDataSourceInitializationDetector> detectors = instantiateDetectors(beanFactory, environment,
|
||||
DependsOnDataSourceInitializationDetector.class);
|
||||
Set<String> dependentUponDataSourceInitialization = new HashSet<>();
|
||||
for (DependsOnDataSourceInitializationDetector detector : detectors) {
|
||||
dependentUponDataSourceInitialization.addAll(detector.detect(beanFactory));
|
||||
}
|
||||
return dependentUponDataSourceInitialization;
|
||||
}
|
||||
|
||||
private <T> List<T> instantiateDetectors(ConfigurableListableBeanFactory beanFactory, Environment environment,
|
||||
Class<T> detectorType) {
|
||||
List<String> detectorNames = SpringFactoriesLoader.loadFactoryNames(detectorType,
|
||||
beanFactory.getBeanClassLoader());
|
||||
Instantiator<T> instantiator = new Instantiator<>(detectorType,
|
||||
(availableParameters) -> availableParameters.add(Environment.class, environment));
|
||||
List<T> detectors = instantiator.instantiate(detectorNames);
|
||||
return detectors;
|
||||
}
|
||||
|
||||
private static BeanDefinition getBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) {
|
||||
try {
|
||||
return beanFactory.getBeanDefinition(beanName);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
|
||||
if (parentBeanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
return getBeanDefinition(beanName, (ConfigurableListableBeanFactory) parentBeanFactory);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
||||
/**
|
||||
* Detects beans that initialize a {@link DataSource}. Implementations should be
|
||||
* registered in {@code META-INF/spring.factories} under the key
|
||||
* {@code org.springframework.boot.jdbc.init.DependsOnDataSourceInitializationDetector}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public interface DataSourceInitializerDetector {
|
||||
|
||||
/**
|
||||
* Detect beans defined in the given {@code beanFactory} that initialize a
|
||||
* {@link DataSource}.
|
||||
* @param beanFactory bean factory to examine
|
||||
* @return names of the detected {@code DataSource} initializer beans, or an empty set
|
||||
* if none were detected.
|
||||
*/
|
||||
Set<String> detect(ConfigurableListableBeanFactory beanFactory);
|
||||
|
||||
/**
|
||||
* Callback indicating that all known {@code DataSourceInitializerDetectors} have been
|
||||
* called and detection of beans that initialize a {@link DataSource} is complete.
|
||||
* @param beanFactory bean factory that was examined
|
||||
* @param dataSourceInitializerNames names of the {@code DataSource} initializer beans
|
||||
* detected by all known detectors
|
||||
*/
|
||||
default void detectionComplete(ConfigurableListableBeanFactory beanFactory,
|
||||
Set<String> dataSourceInitializerNames) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
||||
/**
|
||||
* Detects beans that depend on {@link DataSource} initialization. Implementations should
|
||||
* be registered in {@code META-INF/spring.factories} under the key
|
||||
* {@code org.springframework.boot.jdbc.init.DependsOnDataSourceInitializationDetector}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public interface DependsOnDataSourceInitializationDetector {
|
||||
|
||||
/**
|
||||
* Detect beans defined in the given {@code beanFactory} that depend on
|
||||
* {@link DataSource} initialization. If no beans are detected, an empty set is
|
||||
* returned.
|
||||
* @param beanFactory bean factory to examine
|
||||
* @return names of any beans that depend upon {@code DataSource} initialization
|
||||
*/
|
||||
Set<String> detect(ConfigurableListableBeanFactory beanFactory);
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* General infrastructure for {@code DataSource} initialization.
|
||||
*/
|
||||
package org.springframework.boot.jdbc.init;
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.jooq;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDependsOnDataSourceInitializationDetector;
|
||||
import org.springframework.boot.jdbc.init.DependsOnDataSourceInitializationDetector;
|
||||
|
||||
/**
|
||||
* {@link DependsOnDataSourceInitializationDetector} for jOOQ.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class JooqDependsOnDataSourceInitializationDetector
|
||||
extends AbstractBeansOfTypeDependsOnDataSourceInitializationDetector {
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes() {
|
||||
return Collections.singleton(DSLContext.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for jOOQ.
|
||||
*
|
||||
* @see org.springframework.boot.json.JsonParser
|
||||
*/
|
||||
package org.springframework.boot.jooq;
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.liquibase;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDataSourceInitializerDetector;
|
||||
import org.springframework.boot.jdbc.init.DataSourceInitializerDetector;
|
||||
|
||||
/**
|
||||
* A {@link DataSourceInitializerDetector} for Liquibase.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class LiquibaseDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
|
||||
return Collections.singleton(SpringLiquibase.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.orm.jpa;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDataSourceInitializerDetector;
|
||||
import org.springframework.boot.jdbc.init.DataSourceInitializerDetector;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link DataSourceInitializerDetector} for JPA.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class JpaDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
JpaDataSourceInitializerDetector(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
|
||||
boolean deferred = this.environment.getProperty("spring.jpa.defer-datasource-initialization", boolean.class,
|
||||
false);
|
||||
return deferred ? Collections.singleton(EntityManagerFactory.class) : Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detectionComplete(ConfigurableListableBeanFactory beanFactory, Set<String> dataSourceInitializerNames) {
|
||||
configureOtherInitializersToDependOnJpaInitializers(beanFactory, dataSourceInitializerNames);
|
||||
}
|
||||
|
||||
private void configureOtherInitializersToDependOnJpaInitializers(ConfigurableListableBeanFactory beanFactory,
|
||||
Set<String> dataSourceInitializerNames) {
|
||||
Set<String> jpaInitializers = new HashSet<>();
|
||||
Set<String> otherInitializers = new HashSet<>(dataSourceInitializerNames);
|
||||
Iterator<String> iterator = otherInitializers.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String initializerName = iterator.next();
|
||||
BeanDefinition initializerDefinition = beanFactory.getBeanDefinition(initializerName);
|
||||
if (JpaDataSourceInitializerDetector.class.getName()
|
||||
.equals(initializerDefinition.getAttribute(DataSourceInitializerDetector.class.getName()))) {
|
||||
iterator.remove();
|
||||
jpaInitializers.add(initializerName);
|
||||
}
|
||||
}
|
||||
for (String otherInitializerName : otherInitializers) {
|
||||
BeanDefinition definition = beanFactory.getBeanDefinition(otherInitializerName);
|
||||
String[] dependencies = definition.getDependsOn();
|
||||
for (String dependencyName : jpaInitializers) {
|
||||
dependencies = StringUtils.addStringToArray(dependencies, dependencyName);
|
||||
}
|
||||
definition.setDependsOn(dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.orm.jpa;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
||||
import org.springframework.boot.jdbc.init.AbstractBeansOfTypeDependsOnDataSourceInitializationDetector;
|
||||
import org.springframework.boot.jdbc.init.DependsOnDataSourceInitializationDetector;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
|
||||
|
||||
/**
|
||||
* {@link DependsOnDataSourceInitializationDetector} for JPA.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class JpaDependsOnDataSourceInitializationDetector
|
||||
extends AbstractBeansOfTypeDependsOnDataSourceInitializationDetector {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
JpaDependsOnDataSourceInitializationDetector(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes() {
|
||||
boolean postpone = this.environment.getProperty("spring.jpa.defer-datasource-initialization", boolean.class,
|
||||
false);
|
||||
return postpone ? Collections.emptySet()
|
||||
: new HashSet<>(Arrays.asList(EntityManagerFactory.class, AbstractEntityManagerFactoryBean.class));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.jdbc.init;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
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.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link DataSourceInitializationDependencyConfigurer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class DataSourceInitializationDependencyConfigurerTests {
|
||||
|
||||
private final ConfigurableEnvironment environment = new MockEnvironment();
|
||||
|
||||
DataSourceInitializerDetector dataSourceInitializerDetector = MockedDataSourceInitializerDetector.mock;
|
||||
|
||||
DependsOnDataSourceInitializationDetector dependsOnDataSourceInitializationDetector = MockedDependsOnDataSourceInitializationDetector.mock;
|
||||
|
||||
@TempDir
|
||||
File temp;
|
||||
|
||||
@BeforeEach
|
||||
void resetMocks() {
|
||||
reset(MockedDataSourceInitializerDetector.mock, MockedDependsOnDataSourceInitializationDetector.mock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenDetectorsAreCreatedThenTheEnvironmentCanBeInjected() {
|
||||
performDetection(Arrays.asList(ConstructorInjectionDataSourceInitializerDetector.class,
|
||||
ConstructorInjectionDependsOnDataSourceInitializationDetector.class), (context) -> {
|
||||
context.refresh();
|
||||
assertThat(ConstructorInjectionDataSourceInitializerDetector.environment)
|
||||
.isEqualTo(this.environment);
|
||||
assertThat(ConstructorInjectionDependsOnDataSourceInitializationDetector.environment)
|
||||
.isEqualTo(this.environment);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenDependenciesAreConfiguredThenBeansThatDependUponDataSourceInitializationDependUponDetectedDataSourceInitializers() {
|
||||
BeanDefinition alpha = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
|
||||
BeanDefinition bravo = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
|
||||
performDetection(Arrays.asList(MockedDataSourceInitializerDetector.class,
|
||||
MockedDependsOnDataSourceInitializationDetector.class), (context) -> {
|
||||
context.registerBeanDefinition("alpha", alpha);
|
||||
context.registerBeanDefinition("bravo", bravo);
|
||||
given(this.dataSourceInitializerDetector.detect(context.getBeanFactory()))
|
||||
.willReturn(Collections.singleton("alpha"));
|
||||
given(this.dependsOnDataSourceInitializationDetector.detect(context.getBeanFactory()))
|
||||
.willReturn(Collections.singleton("bravo"));
|
||||
context.refresh();
|
||||
assertThat(alpha.getAttribute(DataSourceInitializerDetector.class.getName()))
|
||||
.isEqualTo(MockedDataSourceInitializerDetector.class.getName());
|
||||
assertThat(bravo.getAttribute(DataSourceInitializerDetector.class.getName())).isNull();
|
||||
verify(this.dataSourceInitializerDetector).detectionComplete(context.getBeanFactory(),
|
||||
Collections.singleton("alpha"));
|
||||
assertThat(bravo.getDependsOn()).containsExactly("alpha");
|
||||
});
|
||||
}
|
||||
|
||||
private void performDetection(Collection<Class<?>> detectors,
|
||||
Consumer<AnnotationConfigApplicationContext> contextCallback) {
|
||||
DetectorSpringFactoriesClassLoader detectorSpringFactories = new DetectorSpringFactoriesClassLoader(this.temp);
|
||||
detectors.forEach(detectorSpringFactories::register);
|
||||
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
|
||||
context.setEnvironment(this.environment);
|
||||
context.setClassLoader(detectorSpringFactories);
|
||||
context.register(DependencyConfigurerConfiguration.class);
|
||||
contextCallback.accept(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(DataSourceInitializationDependencyConfigurer.class)
|
||||
static class DependencyConfigurerConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class ConstructorInjectionDataSourceInitializerDetector implements DataSourceInitializerDetector {
|
||||
|
||||
private static Environment environment;
|
||||
|
||||
ConstructorInjectionDataSourceInitializerDetector(Environment environment) {
|
||||
ConstructorInjectionDataSourceInitializerDetector.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ConstructorInjectionDependsOnDataSourceInitializationDetector
|
||||
implements DependsOnDataSourceInitializationDetector {
|
||||
|
||||
private static Environment environment;
|
||||
|
||||
ConstructorInjectionDependsOnDataSourceInitializationDetector(Environment environment) {
|
||||
ConstructorInjectionDependsOnDataSourceInitializationDetector.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MockedDataSourceInitializerDetector implements DataSourceInitializerDetector {
|
||||
|
||||
private static DataSourceInitializerDetector mock = Mockito.mock(DataSourceInitializerDetector.class);
|
||||
|
||||
@Override
|
||||
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
|
||||
return MockedDataSourceInitializerDetector.mock.detect(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detectionComplete(ConfigurableListableBeanFactory beanFactory,
|
||||
Set<String> dataSourceInitializerNames) {
|
||||
mock.detectionComplete(beanFactory, dataSourceInitializerNames);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MockedDependsOnDataSourceInitializationDetector implements DependsOnDataSourceInitializationDetector {
|
||||
|
||||
private static DependsOnDataSourceInitializationDetector mock = Mockito
|
||||
.mock(DependsOnDataSourceInitializationDetector.class);
|
||||
|
||||
@Override
|
||||
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
|
||||
return MockedDependsOnDataSourceInitializationDetector.mock.detect(beanFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class DetectorSpringFactoriesClassLoader extends ClassLoader {
|
||||
|
||||
private final Set<Class<DataSourceInitializerDetector>> dataSourceInitializerDetectors = new HashSet<>();
|
||||
|
||||
private final Set<Class<DependsOnDataSourceInitializationDetector>> dependsOnDataSourceInitializationDetectors = new HashSet<>();
|
||||
|
||||
private final File temp;
|
||||
|
||||
DetectorSpringFactoriesClassLoader(File temp) {
|
||||
this.temp = temp;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void register(Class<?> detector) {
|
||||
if (DataSourceInitializerDetector.class.isAssignableFrom(detector)) {
|
||||
this.dataSourceInitializerDetectors.add((Class<DataSourceInitializerDetector>) detector);
|
||||
}
|
||||
else if (DependsOnDataSourceInitializationDetector.class.isAssignableFrom(detector)) {
|
||||
this.dependsOnDataSourceInitializationDetectors
|
||||
.add((Class<DependsOnDataSourceInitializationDetector>) detector);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported detector type '" + detector.getName() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
if (!"META-INF/spring.factories".equals(name)) {
|
||||
return super.findResources(name);
|
||||
}
|
||||
Properties properties = new Properties();
|
||||
properties.put(DataSourceInitializerDetector.class.getName(), String.join(",",
|
||||
this.dataSourceInitializerDetectors.stream().map(Class::getName).collect(Collectors.toList())));
|
||||
properties.put(DependsOnDataSourceInitializationDetector.class.getName(),
|
||||
String.join(",", this.dependsOnDataSourceInitializationDetectors.stream().map(Class::getName)
|
||||
.collect(Collectors.toList())));
|
||||
File springFactories = new File(this.temp, "spring.factories");
|
||||
try (FileWriter writer = new FileWriter(springFactories)) {
|
||||
properties.store(writer, "");
|
||||
}
|
||||
return Collections.enumeration(Collections.singleton(springFactories.toURI().toURL()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
spring.test.mockmvc.print=none
|
||||
spring.datasource.initialization-order=after-jpa
|
||||
spring.jpa.defer-datasource-initialization=true
|
||||
|
Loading…
Reference in New Issue