@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.orm.jpa;
import java.io.IOException ;
import java.io.IOException ;
import java.net.URL ;
import java.net.URL ;
import java.net.URLClassLoader ;
import java.net.URLClassLoader ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Arrays ;
import java.util.Collections ;
import java.util.Collections ;
import java.util.Enumeration ;
import java.util.Enumeration ;
@ -46,9 +47,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations ;
import org.springframework.boot.autoconfigure.AutoConfigurations ;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage ;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage ;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration ;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration ;
import org.springframework.boot.autoconfigure.jdbc.DataSourceSchemaCreatedEvent ;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration ;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration ;
import org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration ;
import org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration ;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration ;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration ;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurationTests.JpaUsingApplicationListenerConfiguration.EventCapturingApplicationListener ;
import org.springframework.boot.autoconfigure.orm.jpa.mapping.NonAnnotatedEntity ;
import org.springframework.boot.autoconfigure.orm.jpa.mapping.NonAnnotatedEntity ;
import org.springframework.boot.autoconfigure.orm.jpa.test.City ;
import org.springframework.boot.autoconfigure.orm.jpa.test.City ;
import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration ;
import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration ;
@ -57,10 +60,13 @@ import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy ;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy ;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext ;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext ;
import org.springframework.boot.test.context.runner.ContextConsumer ;
import org.springframework.boot.test.context.runner.ContextConsumer ;
import org.springframework.context.ApplicationEvent ;
import org.springframework.context.ApplicationListener ;
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.orm.jpa.JpaTransactionManager ;
import org.springframework.orm.jpa.JpaTransactionManager ;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean ;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean ;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.entry ;
import static org.assertj.core.api.Assertions.entry ;
@ -383,6 +389,52 @@ public class HibernateJpaAutoConfigurationTests
. run ( ( context ) - > assertThat ( context ) . doesNotHaveBean ( City . class ) ) ;
. run ( ( context ) - > assertThat ( context ) . doesNotHaveBean ( City . class ) ) ;
}
}
@Test
public void withSyncBootstrappingAnApplicationListenerThatUsesJpaDoesNotTriggerABeanCurrentlyInCreationException ( ) {
contextRunner ( )
. withUserConfiguration ( JpaUsingApplicationListenerConfiguration . class )
. withPropertyValues ( "spring.datasource.initialization-mode=never" )
. run ( ( context ) - > {
assertThat ( context ) . hasNotFailed ( ) ;
assertThat ( context
. getBean ( EventCapturingApplicationListener . class ) . events
. stream ( )
. filter ( DataSourceSchemaCreatedEvent . class : : isInstance ) )
. hasSize ( 1 ) ;
} ) ;
}
@Test
public void withAsyncBootstrappingAnApplicationListenerThatUsesJpaDoesNotTriggerABeanCurrentlyInCreationException ( ) {
contextRunner ( )
. withUserConfiguration ( AsyncBootstrappingConfiguration . class ,
JpaUsingApplicationListenerConfiguration . class )
. withPropertyValues ( "spring.datasource.initialization-mode=never" )
. run ( ( context ) - > {
assertThat ( context ) . hasNotFailed ( ) ;
EventCapturingApplicationListener listener = context
. getBean ( EventCapturingApplicationListener . class ) ;
long end = System . currentTimeMillis ( ) + 30000 ;
while ( ( System . currentTimeMillis ( ) < end )
& & ! dataSourceSchemaCreatedEventReceived ( listener ) ) {
Thread . sleep ( 100 ) ;
}
assertThat ( listener . events . stream ( )
. filter ( DataSourceSchemaCreatedEvent . class : : isInstance ) )
. hasSize ( 1 ) ;
} ) ;
}
private boolean dataSourceSchemaCreatedEventReceived (
EventCapturingApplicationListener listener ) {
for ( ApplicationEvent event : listener . events ) {
if ( event instanceof DataSourceSchemaCreatedEvent ) {
return true ;
}
}
return false ;
}
@Configuration
@Configuration
@TestAutoConfigurationPackage ( City . class )
@TestAutoConfigurationPackage ( City . class )
static class TestInitializedJpaConfiguration {
static class TestInitializedJpaConfiguration {
@ -506,4 +558,45 @@ public class HibernateJpaAutoConfigurationTests
}
}
@org.springframework.context.annotation.Configuration
static class JpaUsingApplicationListenerConfiguration {
@Bean
public EventCapturingApplicationListener jpaUsingApplicationListener (
EntityManagerFactory emf ) {
return new EventCapturingApplicationListener ( ) ;
}
static class EventCapturingApplicationListener
implements ApplicationListener < ApplicationEvent > {
private final List < ApplicationEvent > events = new ArrayList < > ( ) ;
@Override
public void onApplicationEvent ( ApplicationEvent event ) {
this . events . add ( event ) ;
}
}
}
@Configuration
static class AsyncBootstrappingConfiguration {
@Bean
ThreadPoolTaskExecutor ThreadPoolTaskExecutor ( ) {
return new ThreadPoolTaskExecutor ( ) ;
}
@Bean
public EntityManagerFactoryBuilderCustomizer asyncBoostrappingCustomizer (
ThreadPoolTaskExecutor executor ) {
return ( builder ) - > {
builder . setBootstrapExecutor ( executor ) ;
} ;
}
}
}
}