Skip to content

Commit 58aa344

Browse files
committed
Wait for DataSource init before allowing context refresh to complete
Closes gh-22852
1 parent b94fe90 commit 58aa344

File tree

1 file changed

+69
-10
lines changed

1 file changed

+69
-10
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.autoconfigure.orm.jpa;
1818

1919
import java.util.Map;
20+
import java.util.concurrent.Future;
2021
import java.util.function.Supplier;
2122

2223
import javax.persistence.EntityManager;
@@ -29,12 +30,17 @@
2930
import org.springframework.beans.factory.annotation.Autowired;
3031
import org.springframework.beans.factory.config.BeanDefinition;
3132
import org.springframework.beans.factory.config.BeanPostProcessor;
33+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
34+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3235
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
33-
import org.springframework.beans.factory.support.GenericBeanDefinition;
3436
import org.springframework.boot.autoconfigure.jdbc.DataSourceSchemaCreatedEvent;
3537
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
3638
import org.springframework.context.ApplicationContext;
39+
import org.springframework.context.ApplicationListener;
3740
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
41+
import org.springframework.context.event.ContextRefreshedEvent;
42+
import org.springframework.core.Ordered;
43+
import org.springframework.core.task.AsyncTaskExecutor;
3844
import org.springframework.core.type.AnnotationMetadata;
3945
import org.springframework.orm.jpa.JpaDialect;
4046
import org.springframework.orm.jpa.JpaVendorAdapter;
@@ -59,6 +65,12 @@ class DataSourceInitializedPublisher implements BeanPostProcessor {
5965

6066
private DataSourceSchemaCreatedPublisher schemaCreatedPublisher;
6167

68+
private DataSourceInitializationCompletionListener initializationCompletionListener;
69+
70+
DataSourceInitializedPublisher(DataSourceInitializationCompletionListener completionListener) {
71+
this.initializationCompletionListener = completionListener;
72+
}
73+
6274
@Override
6375
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
6476
if (bean instanceof LocalContainerEntityManagerFactoryBean) {
@@ -119,26 +131,68 @@ private boolean isInitializingDatabase(DataSource dataSource) {
119131
return hibernate.containsKey("hibernate.hbm2ddl.auto");
120132
}
121133

134+
/**
135+
* {@link ApplicationListener} that, upon receiving {@link ContextRefreshedEvent},
136+
* blocks until any asynchronous DataSource initialization has completed.
137+
*/
138+
static class DataSourceInitializationCompletionListener
139+
implements ApplicationListener<ContextRefreshedEvent>, Ordered {
140+
141+
private volatile Future<?> dataSourceInitialization;
142+
143+
@Override
144+
public void onApplicationEvent(ContextRefreshedEvent event) {
145+
Future<?> dataSourceInitialization = this.dataSourceInitialization;
146+
if (dataSourceInitialization != null) {
147+
try {
148+
dataSourceInitialization.get();
149+
}
150+
catch (Exception ex) {
151+
throw new RuntimeException(ex);
152+
}
153+
}
154+
}
155+
156+
@Override
157+
public int getOrder() {
158+
return Ordered.HIGHEST_PRECEDENCE;
159+
}
160+
161+
}
162+
122163
/**
123164
* {@link ImportBeanDefinitionRegistrar} to register the
124165
* {@link DataSourceInitializedPublisher} without causing early bean instantiation
125166
* issues.
126167
*/
127168
static class Registrar implements ImportBeanDefinitionRegistrar {
128169

129-
private static final String BEAN_NAME = "dataSourceInitializedPublisher";
170+
private static final String PUBLISHER_BEAN_NAME = "dataSourceInitializedPublisher";
171+
172+
private static final String COMPLETION_LISTENER_BEAN_BEAN = DataSourceInitializationCompletionListener.class
173+
.getName();
130174

131175
@Override
132176
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
133177
BeanDefinitionRegistry registry) {
134-
if (!registry.containsBeanDefinition(BEAN_NAME)) {
135-
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
136-
beanDefinition.setBeanClass(DataSourceInitializedPublisher.class);
137-
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
178+
if (!registry.containsBeanDefinition(PUBLISHER_BEAN_NAME)) {
179+
DataSourceInitializationCompletionListener completionListener = new DataSourceInitializationCompletionListener();
180+
DataSourceInitializedPublisher publisher = new DataSourceInitializedPublisher(completionListener);
181+
AbstractBeanDefinition publisherDefinition = BeanDefinitionBuilder
182+
.genericBeanDefinition(DataSourceInitializedPublisher.class, () -> publisher)
183+
.getBeanDefinition();
184+
publisherDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
138185
// We don't need this one to be post processed otherwise it can cause a
139186
// cascade of bean instantiation that we would rather avoid.
140-
beanDefinition.setSynthetic(true);
141-
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
187+
publisherDefinition.setSynthetic(true);
188+
registry.registerBeanDefinition(PUBLISHER_BEAN_NAME, publisherDefinition);
189+
AbstractBeanDefinition listenerDefinition = BeanDefinitionBuilder.genericBeanDefinition(
190+
DataSourceInitializationCompletionListener.class, () -> completionListener).getBeanDefinition();
191+
listenerDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
192+
// We don't need this one to be post processed otherwise it can cause a
193+
// cascade of bean instantiation that we would rather avoid.
194+
listenerDefinition.setSynthetic(true);
195+
registry.registerBeanDefinition(COMPLETION_LISTENER_BEAN_BEAN, listenerDefinition);
142196
}
143197
}
144198

@@ -193,7 +247,12 @@ public Class<? extends EntityManager> getEntityManagerInterface() {
193247
@Override
194248
public void postProcessEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
195249
this.delegate.postProcessEntityManagerFactory(entityManagerFactory);
196-
publishEventIfRequired(this.factoryBean, entityManagerFactory);
250+
AsyncTaskExecutor bootstrapExecutor = this.factoryBean.getBootstrapExecutor();
251+
if (bootstrapExecutor != null) {
252+
DataSourceInitializedPublisher.this.initializationCompletionListener.dataSourceInitialization = bootstrapExecutor
253+
.submit(() -> DataSourceInitializedPublisher.this.publishEventIfRequired(this.factoryBean,
254+
entityManagerFactory));
255+
}
197256
}
198257

199258
}

0 commit comments

Comments
 (0)