/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat, Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.search.test.configuration.mutablefactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.lucene.index.IndexReader; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.TopDocs; import org.hibernate.search.SearchException; import org.hibernate.search.backend.spi.Work; import org.hibernate.search.backend.spi.WorkType; import org.hibernate.search.engine.spi.EntityIndexBinder; import org.hibernate.search.engine.spi.SearchFactoryImplementor; import org.hibernate.search.spi.SearchFactoryBuilder; import org.hibernate.search.batchindexing.impl.Executors; import org.hibernate.search.indexes.impl.DirectoryBasedIndexManager; import org.hibernate.search.indexes.spi.IndexManager; import org.hibernate.search.spi.SearchFactoryIntegrator; import org.hibernate.search.store.DirectoryProvider; import org.hibernate.search.store.impl.RAMDirectoryProvider; import org.hibernate.search.test.TestConstants; import org.hibernate.search.test.configuration.mutablefactory.generated.Generated; import org.hibernate.search.test.util.HibernateManualConfiguration; import org.hibernate.search.test.util.ManualConfiguration; import org.hibernate.search.test.util.ManualTransactionContext; import org.hibernate.search.util.impl.ClassLoaderHelper; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; /** * @author Emmanuel Bernard */ public class MutableFactoryTest { public static final Log log = LoggerFactory.make(); @Test public void testCreateEmptyFactory() throws Exception { final ManualConfiguration configuration = getTestConfiguration(); SearchFactoryImplementor sf = new SearchFactoryBuilder().configuration( configuration ).buildSearchFactory(); sf.close(); } @Test public void testAddingClassFullModel() throws Exception { ManualConfiguration configuration = getTestConfiguration(); //FIXME downcasting of MSF. create a getDelegate() ? SearchFactoryIntegrator sf = new SearchFactoryBuilder().configuration( configuration ).buildSearchFactory(); final SearchFactoryBuilder builder = new SearchFactoryBuilder(); sf = builder.currentFactory( sf ) .addClass( A.class ) .buildSearchFactory(); ManualTransactionContext tc = new ManualTransactionContext(); doIndexWork( new A(1, "Emmanuel"), 1, sf, tc ); tc.end(); QueryParser parser = new QueryParser( TestConstants.getTargetLuceneVersion(), "name", TestConstants.standardAnalyzer ); Query luceneQuery = parser.parse( "Emmanuel" ); IndexReader indexReader = sf.getIndexReaderAccessor().open( A.class ); IndexSearcher searcher = new IndexSearcher( indexReader ); TopDocs hits = searcher.search( luceneQuery, 1000 ); assertEquals( 1, hits.totalHits ); searcher.close(); sf.getIndexReaderAccessor().close( indexReader ); sf = builder.currentFactory( sf ) .addClass( B.class ) .buildSearchFactory(); tc = new ManualTransactionContext(); doIndexWork( new B(1, "Noel"), 1, sf, tc ); tc.end(); luceneQuery = parser.parse( "Noel" ); indexReader = sf.getIndexReaderAccessor().open( B.class ); searcher = new IndexSearcher( indexReader ); hits = searcher.search( luceneQuery, 1000 ); assertEquals( 1, hits.totalHits ); searcher.close(); sf.getIndexReaderAccessor().close( indexReader ); sf.close(); } @Test public void testAddingClassSimpleAPI() throws Exception { ManualConfiguration configuration = getTestConfiguration(); SearchFactoryIntegrator sf = new SearchFactoryBuilder().configuration( configuration ).buildSearchFactory(); sf.addClasses( A.class ); ManualTransactionContext tc = new ManualTransactionContext(); doIndexWork( new A(1, "Emmanuel"), 1, sf, tc ); tc.end(); QueryParser parser = new QueryParser( TestConstants.getTargetLuceneVersion(), "name", TestConstants.standardAnalyzer ); Query luceneQuery = parser.parse( "Emmanuel" ); IndexReader indexReader = sf.getIndexReaderAccessor().open( A.class ); IndexSearcher searcher = new IndexSearcher( indexReader ); TopDocs hits = searcher.search( luceneQuery, 1000 ); assertEquals( 1, hits.totalHits ); searcher.close(); sf.getIndexReaderAccessor().close( indexReader ); sf.addClasses( B.class, C.class ); tc = new ManualTransactionContext(); doIndexWork( new B(1, "Noel"), 1, sf, tc ); doIndexWork( new C(1, "Vincent"), 1, sf, tc ); tc.end(); luceneQuery = parser.parse( "Noel" ); indexReader = sf.getIndexReaderAccessor().open( B.class ); searcher = new IndexSearcher( indexReader ); hits = searcher.search( luceneQuery, 1000 ); assertEquals( 1, hits.totalHits ); searcher.close(); sf.getIndexReaderAccessor().close( indexReader ); luceneQuery = parser.parse( "Vincent" ); indexReader = sf.getIndexReaderAccessor().open( C.class ); searcher = new IndexSearcher( indexReader ); hits = searcher.search( luceneQuery, 1000 ); assertEquals( 1, hits.totalHits ); searcher.close(); sf.getIndexReaderAccessor().close( indexReader ); sf.close(); } private static void doIndexWork(Object entity, Integer id, SearchFactoryIntegrator sfi, ManualTransactionContext tc) { Work<?> work = new Work<Object>( entity, id, WorkType.INDEX ); sfi.getWorker().performWork( work, tc ); } @Test public void testMultiThreadedAddClasses() throws Exception { QueryParser parser = new QueryParser( TestConstants.getTargetLuceneVersion(), "name", TestConstants.standardAnalyzer ); ManualConfiguration configuration = getTestConfiguration(); SearchFactoryIntegrator sf = new SearchFactoryBuilder().configuration( configuration ).buildSearchFactory(); List<DoAddClasses> runnables = new ArrayList<DoAddClasses>(10); final int nbrOfThread = 10; final int nbrOfClassesPerThread = 10; for (int i = 0 ; i < nbrOfThread; i++) { runnables.add( new DoAddClasses( sf, i, nbrOfClassesPerThread ) ); } final ThreadPoolExecutor poolExecutor = Executors.newFixedThreadPool( nbrOfThread, "SFI classes addition" ); poolExecutor.prestartAllCoreThreads(); for (Runnable runnable : runnables) { poolExecutor.execute( runnable ); } poolExecutor.shutdown(); boolean inProgress; do { Thread.sleep( 100 ); inProgress = false; for ( DoAddClasses runnable : runnables) { inProgress = inProgress || runnable.isFailure() == null; } } while (inProgress); for ( DoAddClasses runnable : runnables) { assertNotNull( "Threads not run # " + runnable.getWorkNumber(), runnable.isFailure() ); assertFalse( "thread failed #" + runnable.getWorkNumber() + " Failure: " + runnable.getFailureInfo(), runnable.isFailure() ); } poolExecutor.awaitTermination( 1, TimeUnit.MINUTES ); for (int i = 0 ; i < nbrOfThread*nbrOfClassesPerThread ; i++) { Query luceneQuery = parser.parse( "Emmanuel" + i); final Class<?> classByNumber = getClassAByNumber( i ); IndexReader indexReader = sf.getIndexReaderAccessor().open( classByNumber ); IndexSearcher searcher = new IndexSearcher( indexReader ); TopDocs hits = searcher.search( luceneQuery, 1000 ); assertEquals( 1, hits.totalHits ); searcher.close(); sf.getIndexReaderAccessor().close( indexReader ); } } private static Class<?> getClassAByNumber(int i) throws ClassNotFoundException { final Class<?> aClass = ClassLoaderHelper.classForName( Generated.A0.class.getName().replace( "A0", "A" + i ), Generated.A0.class.getClassLoader() ); return aClass; } private static class DoAddClasses implements Runnable { private final SearchFactoryIntegrator factory; private final int factorOfClassesPerThread; private final QueryParser parser; private final int nbrOfClassesPerThread; private volatile Boolean failure = false; private volatile String failureInfo; public String getFailureInfo() { return failureInfo; } public Boolean isFailure() { return failure; } public int getWorkNumber() { return factorOfClassesPerThread; } public DoAddClasses(SearchFactoryIntegrator factory, int factorOfClassesPerThread, int nbrOfClassesPerThread) { this.factory = factory; this.factorOfClassesPerThread = factorOfClassesPerThread; this.parser = new QueryParser( TestConstants.getTargetLuceneVersion(), "name", TestConstants.standardAnalyzer ); this.nbrOfClassesPerThread = nbrOfClassesPerThread; } public void run() { try { for (int index = 0 ; index < 10 ; index++) { final int i = factorOfClassesPerThread*nbrOfClassesPerThread + index; final Class<?> aClass = MutableFactoryTest.getClassAByNumber( i ); factory.addClasses( aClass ); Object entity = aClass.getConstructor( Integer.class, String.class ).newInstance(i, "Emmanuel" + i); ManualTransactionContext context = new ManualTransactionContext(); MutableFactoryTest.doIndexWork(entity, i, factory, context ); context.end(); EntityIndexBinder indexBindingForEntity = factory.getIndexBindingForEntity( aClass ); assertNotNull( indexBindingForEntity ); IndexManager[] indexManagers = indexBindingForEntity.getIndexManagers(); assertEquals( 1, indexManagers.length ); DirectoryBasedIndexManager indexManager = (DirectoryBasedIndexManager) indexManagers[0]; DirectoryProvider directoryProvider = indexManager.getDirectoryProvider(); if ( ! ( directoryProvider instanceof RAMDirectoryProvider ) ) { // can't use Assertion in a separate thread throw new SearchException( "Configuration lost: expected RAM directory" ); } Query luceneQuery = parser.parse( "Emmanuel" + i); IndexReader indexReader = factory.getIndexReaderAccessor().open( aClass ); IndexSearcher searcher = new IndexSearcher( indexReader ); TopDocs hits = searcher.search( luceneQuery, 1000 ); if ( hits.totalHits != 1 ) { failure = true; failureInfo = "failure: Emmanuel" + i + " for " + aClass.getName(); return; } searcher.close(); factory.getIndexReaderAccessor().close( indexReader ); } } catch ( Exception e ) { this.failure = true; e.printStackTrace( ); failureInfo = "failure: Emmanuel" + factorOfClassesPerThread + " exception: " + e.toString(); } } } private static ManualConfiguration getTestConfiguration() { return new HibernateManualConfiguration() .addProperty( "hibernate.search.default.directory_provider", "ram" ) .addProperty( "hibernate.search.lucene_version", TestConstants.getTargetLuceneVersion().name() ); } }