/* * 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.test.configuration.mutablefactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.Query; import org.hibernate.search.backend.spi.Work; import org.hibernate.search.backend.spi.WorkType; import org.hibernate.search.engine.integration.impl.ExtendedSearchIntegrator; import org.hibernate.search.engine.service.classloading.spi.ClassLoaderService; import org.hibernate.search.engine.service.spi.ServiceManager; import org.hibernate.search.engine.spi.EntityIndexBinding; import org.hibernate.search.indexes.spi.IndexManager; import org.hibernate.search.query.engine.spi.EntityInfo; import org.hibernate.search.query.engine.spi.HSQuery; import org.hibernate.search.spi.SearchIntegrator; import org.hibernate.search.spi.SearchIntegratorBuilder; import org.hibernate.search.test.configuration.mutablefactory.generated.Generated; import org.hibernate.search.testsupport.TestConstants; import org.hibernate.search.testsupport.concurrency.ConcurrentRunner; import org.hibernate.search.testsupport.concurrency.ConcurrentRunner.TaskFactory; import org.hibernate.search.testsupport.junit.ElasticsearchSupportInProgress; import org.hibernate.search.testsupport.setup.SearchConfigurationForTest; import org.hibernate.search.testsupport.setup.TransactionContextForTest; import org.junit.Test; import org.junit.experimental.categories.Category; /** * @author Emmanuel Bernard */ public class MutableFactoryTest { @Test public void testCreateEmptyFactory() throws Exception { try ( SearchIntegrator si = new SearchIntegratorBuilder().configuration( new SearchConfigurationForTest() ).buildSearchIntegrator() ) { // no-op } } @Test public void testAddingClassFullModel() throws Exception { SearchIntegrator searchIntegrator = new SearchIntegratorBuilder().configuration( new SearchConfigurationForTest() ).buildSearchIntegrator(); final SearchIntegratorBuilder builder = new SearchIntegratorBuilder(); searchIntegrator = builder.currentSearchIntegrator( searchIntegrator ) .addClass( A.class ) .buildSearchIntegrator(); TransactionContextForTest tc = new TransactionContextForTest(); doIndexWork( new A( 1, "Emmanuel" ), 1, searchIntegrator, tc ); tc.end(); QueryParser parser = new QueryParser( "name", TestConstants.standardAnalyzer ); Query luceneQuery = parser.parse( "Emmanuel" ); HSQuery hsQuery = searchIntegrator.createHSQuery( luceneQuery, A.class ); int size = hsQuery.queryResultSize(); assertEquals( 1, size ); searchIntegrator = builder.currentSearchIntegrator( searchIntegrator ) .addClass( B.class ) .buildSearchIntegrator(); tc = new TransactionContextForTest(); doIndexWork( new B( 1, "Noel" ), 1, searchIntegrator, tc ); tc.end(); luceneQuery = parser.parse( "Noel" ); hsQuery = searchIntegrator.createHSQuery( luceneQuery, B.class ); size = hsQuery.queryResultSize(); assertEquals( 1, size ); searchIntegrator.close(); } @Test public void testAddingClassSimpleAPI() throws Exception { SearchIntegrator sf = new SearchIntegratorBuilder().configuration( new SearchConfigurationForTest() ).buildSearchIntegrator(); sf.addClasses( A.class ); TransactionContextForTest tc = new TransactionContextForTest(); doIndexWork( new A( 1, "Emmanuel" ), 1, sf, tc ); tc.end(); QueryParser parser = new QueryParser( "name", TestConstants.standardAnalyzer ); Query luceneQuery = parser.parse( "Emmanuel" ); HSQuery hsQuery = sf.createHSQuery( luceneQuery, A.class ); int size = hsQuery.queryResultSize(); assertEquals( 1, size ); sf.addClasses( B.class, C.class ); tc = new TransactionContextForTest(); doIndexWork( new B( 1, "Noel" ), 1, sf, tc ); doIndexWork( new C( 1, "Vincent" ), 1, sf, tc ); tc.end(); luceneQuery = parser.parse( "Noel" ); hsQuery = sf.createHSQuery( luceneQuery, B.class ); size = hsQuery.queryResultSize(); assertEquals( 1, size ); luceneQuery = parser.parse( "Vincent" ); hsQuery = sf.createHSQuery( luceneQuery, C.class ); size = hsQuery.queryResultSize(); assertEquals( 1, size ); sf.close(); } @Test @Category(ElasticsearchSupportInProgress.class) // HSEARCH-2421 Support statistics with Elasticsearch public void testAddingClassSimpleAPIwithJMX() throws Exception { SearchIntegrator sf = new SearchIntegratorBuilder() .configuration( new SearchConfigurationForTest() .addClass( A.class ) .addProperty( "hibernate.search.jmx_enabled" , Boolean.TRUE.toString() ) .addProperty( "hibernate.search.generate_statistics", Boolean.TRUE.toString() ) .addProperty( "com.sun.management.jmxremote", Boolean.TRUE.toString() ) ) .buildSearchIntegrator(); TransactionContextForTest tc = new TransactionContextForTest(); doIndexWork( new A( 1, "Emmanuel" ), 1, sf, tc ); tc.end(); QueryParser parser = new QueryParser( "name", TestConstants.standardAnalyzer ); Query luceneQuery = parser.parse( "Emmanuel" ); HSQuery hsQuery = sf.createHSQuery( luceneQuery, A.class ); hsQuery.getTimeoutManager().start(); List<EntityInfo> entityInfos = hsQuery.queryEntityInfos(); assertEquals( 1 , entityInfos.size() ); assertEquals( 1 , sf.getStatistics().getSearchQueryExecutionCount() ); assertEquals( 1 , sf.getStatistics().getIndexedClassNames().size() ); hsQuery.getTimeoutManager().stop(); sf.addClasses( B.class, C.class ); tc = new TransactionContextForTest(); doIndexWork( new B( 1, "Noel" ), 1, sf, tc ); doIndexWork( new C( 1, "Vincent" ), 1, sf, tc ); tc.end(); luceneQuery = parser.parse( "Noel" ); hsQuery = sf.createHSQuery( luceneQuery, A.class, B.class, C.class ); hsQuery.getTimeoutManager().start(); entityInfos = hsQuery.queryEntityInfos(); assertEquals( 1 , entityInfos.size() ); assertEquals( 2 , sf.getStatistics().getSearchQueryExecutionCount() ); assertEquals( 3 , sf.getStatistics().getIndexedClassNames().size() ); hsQuery.getTimeoutManager().stop(); luceneQuery = parser.parse( "Vincent" ); hsQuery = sf.createHSQuery( luceneQuery, A.class, B.class, C.class ); hsQuery.getTimeoutManager().start(); entityInfos = hsQuery.queryEntityInfos(); assertEquals( 1 , entityInfos.size() ); assertEquals( 3 , sf.getStatistics().getSearchQueryExecutionCount() ); assertEquals( 3 , sf.getStatistics().getIndexedClassNames().size() ); hsQuery.getTimeoutManager().stop(); sf.close(); } private static void doIndexWork(Object entity, Integer id, SearchIntegrator sfi, TransactionContextForTest tc) { Work work = new Work( entity, id, WorkType.INDEX ); sfi.getWorker().performWork( work, tc ); } @Test public void testMultiThreadedAddClasses() throws Exception { QueryParser parser = new QueryParser( "name", TestConstants.standardAnalyzer ); try ( SearchIntegrator sf = new SearchIntegratorBuilder().configuration( new SearchConfigurationForTest() ).buildSearchIntegrator() ) { int numberOfClasses = 100; int numberOfThreads = 10; new ConcurrentRunner( numberOfClasses, numberOfThreads, new TaskFactory() { @Override public Runnable createRunnable(int i) throws Exception { return new DoAddClass( sf, i ); } } ) .setTimeout( 1, TimeUnit.MINUTES ) .execute(); for ( int i = 0; i < numberOfClasses; i++ ) { Query luceneQuery = parser.parse( "Emmanuel" + i ); final Class<?> classByNumber = getClassByNumber( i, sf.getServiceManager() ); HSQuery hsQuery = sf.createHSQuery( luceneQuery, classByNumber ); int size = hsQuery.queryResultSize(); assertEquals( "Expected 1 document for class " + classByNumber, 1, size ); } } } private static Class<?> getClassByNumber(int i, ServiceManager serviceManager) throws ClassNotFoundException { ClassLoaderService classLoaderService = serviceManager.getClassLoaderService(); Class<?> clazz = classLoaderService.classForName( Generated.A0.class.getName().replace( "A0", "A" + i ) ); return clazz; } private static class DoAddClass implements Runnable { private final ExtendedSearchIntegrator extendedIntegrator; private final int index; private final QueryParser parser; public DoAddClass(SearchIntegrator si, int index) { this.extendedIntegrator = si.unwrap( ExtendedSearchIntegrator.class ); this.index = index; this.parser = new QueryParser( "name", TestConstants.standardAnalyzer ); } @Override public void run() { try { String name = "Emmanuel" + index; final Class<?> aClass = MutableFactoryTest.getClassByNumber( index, extendedIntegrator.getServiceManager() ); System.err.println( "Creating index #" + index + " for class " + aClass ); extendedIntegrator.addClasses( aClass ); Object entity = aClass.getConstructor( Integer.class, String.class ) .newInstance( index, name ); TransactionContextForTest context = new TransactionContextForTest(); MutableFactoryTest.doIndexWork( entity, index, extendedIntegrator, context ); context.end(); EntityIndexBinding indexBindingForEntity = extendedIntegrator.getIndexBinding( aClass ); assertNotNull( indexBindingForEntity ); IndexManager[] indexManagers = indexBindingForEntity.getIndexManagers(); assertEquals( 1, indexManagers.length ); Query luceneQuery = parser.parse( name ); HSQuery hsQuery = extendedIntegrator.createHSQuery( luceneQuery, aClass ); assertEquals( "Should have exactly one result for '" + name + "'", 1, hsQuery.queryResultSize() ); } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | InstantiationException e) { throw new IllegalStateException( "Unexpected exception while manipulating dynamically created classes", e ); } catch (ParseException e) { throw new IllegalStateException( "Unexpected exception while parsing query", e ); } } } }