/* * Hibernate Search, full-text search for your domain model * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.search.hcore.impl; import java.util.concurrent.CompletableFuture; import org.hibernate.SessionFactory; import org.hibernate.SessionFactoryObserver; import org.hibernate.boot.Metadata; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.jndi.spi.JndiService; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.search.cfg.impl.SearchConfigurationFromHibernateCore; import org.hibernate.search.engine.Version; import org.hibernate.search.engine.integration.impl.ExtendedSearchIntegrator; import org.hibernate.search.event.impl.FullTextIndexEventListener; import org.hibernate.search.hcore.spi.BeanResolver; import org.hibernate.search.hcore.spi.EnvironmentSynchronizer; import org.hibernate.search.spi.SearchIntegrator; import org.hibernate.search.spi.SearchIntegratorBuilder; /** * A {@code SessionFactoryObserver} registered with Hibernate ORM during the integration phase. This observer will * create the Search factory once the {@code SessionFactory} is built. * * @author Hardy Ferentschik * @see HibernateSearchIntegrator */ public class HibernateSearchSessionFactoryObserver implements SessionFactoryObserver { static { Version.touch(); } private final ConfigurationService configurationService; private final JndiService namingService; private final ClassLoaderService classLoaderService; private final EnvironmentSynchronizer environmentSynchronizer; private final BeanResolver beanResolver; private final FullTextIndexEventListener listener; private final Metadata metadata; private final CompletableFuture<ExtendedSearchIntegrator> extendedSearchIntegratorFuture = new CompletableFuture<>(); //Guarded by synchronization on this private JMXHook jmx; public HibernateSearchSessionFactoryObserver( Metadata metadata, ConfigurationService configurationService, FullTextIndexEventListener listener, ClassLoaderService classLoaderService, EnvironmentSynchronizer environmentSynchronizer, BeanResolver beanResolver, JndiService namingService) { this.metadata = metadata; this.configurationService = configurationService; this.listener = listener; this.classLoaderService = classLoaderService; this.environmentSynchronizer = environmentSynchronizer; this.beanResolver = beanResolver; this.namingService = namingService; } @Override public void sessionFactoryCreated(SessionFactory factory) { boolean failedBootScheduling = true; try { listener.initialize( extendedSearchIntegratorFuture ); if ( environmentSynchronizer != null ) { environmentSynchronizer.whenEnvironmentReady( () -> boot( factory ) ); } else { boot( factory ); } failedBootScheduling = false; } finally { if ( failedBootScheduling ) { cancelBoot(); } } } /** * Boot Hibernate Search if it hasn't booted already, * and complete {@link #extendedSearchIntegratorFuture}. * <p> * This method is synchronized in order to avoid booting Hibernate Search * after (or while) the boot has been canceled. * * @param factory The factory on which to graft Hibernate Search. * * @see #cancelBoot() */ private synchronized void boot(SessionFactory factory) { if ( extendedSearchIntegratorFuture.isDone() ) { return; } boolean failedBoot = true; try { HibernateSessionFactoryService sessionService = new DefaultHibernateSessionFactoryService( factory ); SearchIntegrator searchIntegrator = new SearchIntegratorBuilder() .configuration( new SearchConfigurationFromHibernateCore( metadata, configurationService, classLoaderService, beanResolver, sessionService, namingService ) ) .buildSearchIntegrator(); ExtendedSearchIntegrator extendedIntegrator = searchIntegrator.unwrap( ExtendedSearchIntegrator.class ); this.jmx = new JMXHook( configurationService ); this.jmx.registerIfEnabled( extendedIntegrator, factory ); extendedSearchIntegratorFuture.complete( extendedIntegrator ); //Register the SearchFactory in the ORM ServiceRegistry (for convenience of lookup) final SessionFactoryImplementor factoryImplementor = (SessionFactoryImplementor) factory; factoryImplementor.getServiceRegistry().getService( SearchFactoryReference.class ).initialize( extendedIntegrator ); failedBoot = false; } catch (RuntimeException e) { extendedSearchIntegratorFuture.completeExceptionally( e ); throw e; } finally { if ( failedBoot ) { factory.close(); } } } @Override public synchronized void sessionFactoryClosing(SessionFactory factory) { cancelBoot(); } /** * Cancel the planned boot if it hasn't happened already. * <p> * This method is synchronized in order to avoid canceling the boot while it is ongoing, * which could lead to resource leaks. * * @see #boot(SessionFactory) */ private synchronized void cancelBoot() { extendedSearchIntegratorFuture.cancel( false ); } @Override public void sessionFactoryClosed(SessionFactory factory) { extendedSearchIntegratorFuture.thenAccept( this::cleanup ); } private synchronized void cleanup(ExtendedSearchIntegrator extendedIntegrator) { if ( extendedIntegrator != null ) { extendedIntegrator.close(); } jmx.unRegisterIfRegistered(); } }