11package io .quarkus .hibernate .orm .deployment ;
22
3+ import java .lang .annotation .Annotation ;
34import java .util .ArrayList ;
45import java .util .Arrays ;
56import java .util .List ;
89import java .util .stream .Collectors ;
910
1011import jakarta .enterprise .context .ApplicationScoped ;
11- import jakarta .enterprise .inject .Any ;
1212import jakarta .enterprise .inject .Default ;
13- import jakarta .enterprise .inject .Instance ;
1413import jakarta .inject .Singleton ;
14+ import jakarta .interceptor .Interceptor ;
1515import jakarta .persistence .AttributeConverter ;
1616import jakarta .transaction .TransactionManager ;
1717
1818import org .hibernate .Session ;
1919import org .hibernate .SessionFactory ;
2020import org .hibernate .StatelessSession ;
21- import org .jboss .jandex .AnnotationInstance ;
2221import org .jboss .jandex .AnnotationTarget .Kind ;
2322import org .jboss .jandex .AnnotationValue ;
2423import org .jboss .jandex .ClassType ;
2524import org .jboss .jandex .DotName ;
2625import org .jboss .jandex .FieldInfo ;
27- import org .jboss .jandex .ParameterizedType ;
2826import org .jboss .jandex .Type ;
2927
30- import io .agroal .api .AgroalDataSource ;
3128import io .quarkus .agroal .spi .JdbcDataSourceBuildItem ;
29+ import io .quarkus .arc .Arc ;
30+ import io .quarkus .arc .ArcContainer ;
31+ import io .quarkus .arc .InstanceHandle ;
3232import io .quarkus .arc .deployment .AdditionalBeanBuildItem ;
3333import io .quarkus .arc .deployment .AnnotationsTransformerBuildItem ;
3434import io .quarkus .arc .deployment .BeanDefiningAnnotationBuildItem ;
35+ import io .quarkus .arc .deployment .ObserverRegistrationPhaseBuildItem ;
3536import io .quarkus .arc .deployment .SyntheticBeanBuildItem ;
3637import io .quarkus .arc .deployment .SyntheticBeanBuildItem .ExtendedBeanConfigurator ;
3738import io .quarkus .arc .deployment .UnremovableBeanBuildItem ;
4748import io .quarkus .deployment .annotations .ExecutionTime ;
4849import io .quarkus .deployment .annotations .Record ;
4950import io .quarkus .deployment .builditem .CombinedIndexBuildItem ;
51+ import io .quarkus .gizmo .MethodDescriptor ;
52+ import io .quarkus .gizmo .ResultHandle ;
5053import io .quarkus .hibernate .orm .PersistenceUnit ;
5154import io .quarkus .hibernate .orm .runtime .HibernateOrmRecorder ;
5255import io .quarkus .hibernate .orm .runtime .HibernateOrmRuntimeConfig ;
5659import io .quarkus .hibernate .orm .runtime .RequestScopedStatelessSessionHolder ;
5760import io .quarkus .hibernate .orm .runtime .TransactionSessions ;
5861import io .quarkus .hibernate .orm .runtime .cdi .QuarkusArcBeanContainer ;
62+ import io .quarkus .runtime .ShutdownEvent ;
5963
6064@ BuildSteps (onlyIf = HibernateOrmEnabled .class )
6165public class HibernateOrmCdiProcessor {
6266
67+ private static final int JPA_CONFIG_SHUTDOWN_PRIORITY = Interceptor .Priority .LIBRARY_AFTER + 100 ;
6368 private static final List <DotName > SESSION_FACTORY_EXPOSED_TYPES = Arrays .asList (ClassNames .ENTITY_MANAGER_FACTORY ,
6469 ClassNames .SESSION_FACTORY );
6570 private static final List <DotName > SESSION_EXPOSED_TYPES = Arrays .asList (ClassNames .ENTITY_MANAGER , ClassNames .SESSION );
@@ -131,7 +136,6 @@ public void transform(TransformationContext transformationContext) {
131136 @ BuildStep
132137 @ Record (ExecutionTime .RUNTIME_INIT )
133138 void generateJpaConfigBean (HibernateOrmRecorder recorder ,
134- Capabilities capabilities ,
135139 HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig ,
136140 BuildProducer <SyntheticBeanBuildItem > syntheticBeanBuildItemBuildProducer ) {
137141 ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem
@@ -140,28 +144,46 @@ void generateJpaConfigBean(HibernateOrmRecorder recorder,
140144 .scope (Singleton .class )
141145 .unremovable ()
142146 .setRuntimeInit ()
143- .supplier (recorder .jpaConfigSupplier (hibernateOrmRuntimeConfig ))
144- .destroyer (JPAConfig .Destroyer .class );
145-
146- // Add a synthetic dependency from JPAConfig to any datasource/pool,
147- // so that JPAConfig is destroyed before the datasource/pool.
148- // The alternative would be adding an application destruction observer
149- // (@Observes @BeforeDestroyed(ApplicationScoped.class)) to JPAConfig,
150- // but that would force initialization of JPAConfig upon application shutdown,
151- // which may cause cascading failures if the shutdown happened before JPAConfig was initialized.
152- if (capabilities .isPresent (Capability .HIBERNATE_REACTIVE )) {
153- configurator .addInjectionPoint (ParameterizedType .create (DotName .createSimple (Instance .class ),
154- new Type [] { ClassType .create (DotName .createSimple ("io.vertx.sqlclient.Pool" )) }, null ),
155- AnnotationInstance .builder (Any .class ).build ());
156- } else {
157- configurator .addInjectionPoint (ParameterizedType .create (DotName .createSimple (Instance .class ),
158- new Type [] { ClassType .create (DotName .createSimple (AgroalDataSource .class )) }, null ),
159- AnnotationInstance .builder (Any .class ).build ());
160- }
147+ .supplier (recorder .jpaConfigSupplier (hibernateOrmRuntimeConfig ));
161148
162149 syntheticBeanBuildItemBuildProducer .produce (configurator .done ());
163150 }
164151
152+ @ BuildStep
153+ @ Record (ExecutionTime .RUNTIME_INIT )
154+ void generateJpaConfigBeanObserver (
155+ HibernateOrmRecorder recorder ,
156+ ObserverRegistrationPhaseBuildItem observerRegistrationPhase ,
157+ BuildProducer <ObserverRegistrationPhaseBuildItem .ObserverConfiguratorBuildItem > observerConfigurationRegistry ) {
158+ observerConfigurationRegistry .produce (
159+ new ObserverRegistrationPhaseBuildItem .ObserverConfiguratorBuildItem (observerRegistrationPhase .getContext ()
160+ .configure ()
161+ .beanClass (DotName .createSimple ("io.quarkus.hibernate.orm.runtime.JPAConfig" ))
162+ .observedType (ShutdownEvent .class )
163+ .priority (JPA_CONFIG_SHUTDOWN_PRIORITY )
164+ .notify (mc -> {
165+ // Essentially do the following:
166+ // Arc.container().instance( JPAConfig.class ).get().shutdown();
167+ ResultHandle arcContainer = mc .invokeStaticMethod (
168+ MethodDescriptor .ofMethod (Arc .class , "container" , ArcContainer .class ));
169+ ResultHandle jpaConfigInstance = mc .invokeInterfaceMethod (
170+ MethodDescriptor .ofMethod (ArcContainer .class , "instance" , InstanceHandle .class ,
171+ Class .class , Annotation [].class ),
172+ arcContainer ,
173+ mc .loadClassFromTCCL (JPAConfig .class ),
174+ mc .newArray (Annotation .class , 0 ));
175+ ResultHandle jpaConfig = mc .invokeInterfaceMethod (
176+ MethodDescriptor .ofMethod (InstanceHandle .class , "get" , Object .class ),
177+ jpaConfigInstance );
178+
179+ mc .invokeVirtualMethod (
180+ MethodDescriptor .ofMethod (JPAConfig .class , "shutdown" , void .class ),
181+ jpaConfig );
182+
183+ mc .returnValue (null );
184+ })));
185+ }
186+
165187 // These beans must be initialized at runtime because their initialization
166188 // depends on runtime configuration (to activate/deactivate a persistence unit)
167189 @ Record (ExecutionTime .RUNTIME_INIT )
0 commit comments