/*
* 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.batchindexing;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import junit.framework.Assert;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.Lock;
import org.apache.lucene.store.LockFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Projections;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.batchindexing.MassIndexerProgressMonitor;
import org.hibernate.search.engine.spi.SearchFactoryImplementor;
import org.hibernate.search.indexes.impl.DirectoryBasedIndexManager;
import org.hibernate.search.test.util.FullTextSessionBuilder;
import org.hibernate.search.test.util.textbuilder.SentenceInventor;
import org.hibernate.search.util.logging.impl.Log;
import org.hibernate.search.util.logging.impl.LoggerFactory;
import static org.junit.Assert.assertEquals;
/**
* Tests the fullTextSession.createIndexer() API for basic functionality.
*
* @author Sanne Grinovero
*/
public class IndexingGeneratedCorpusTest {
private static final Log log = LoggerFactory.make();
private final int BOOK_NUM = 140;
private final int ANCIENTBOOK_NUM = 120;
private final int SECRETBOOK_NUM = 20;
private final int DVD_NUM = 200;
private SentenceInventor sentenceInventor = new SentenceInventor( 7L, 4000 );
private FullTextSessionBuilder builder;
private int totalEntitiesInDB = 0;
@Before
public void setUp() throws Exception {
builder = new FullTextSessionBuilder();
builder
.addAnnotatedClass( Book.class )
.addAnnotatedClass( Dvd.class )
.addAnnotatedClass( AncientBook.class )
.addAnnotatedClass( Nation.class )
.addAnnotatedClass( SecretBook.class )
.setProperty( "hibernate.show_sql", "false" ) // too verbose for this test
.setProperty( "hibernate.search.DVDS.exclusive_index_use", "false" ) // to test lock release
.setProperty( "hibernate.search.default.worker.thread_pool.size", "4" )
.build();
createMany( Book.class, BOOK_NUM );
createMany( Dvd.class, DVD_NUM );
createMany( AncientBook.class, ANCIENTBOOK_NUM );
createMany( SecretBook.class, SECRETBOOK_NUM );
storeAllBooksInNation();
}
@After
public void tearDown() {
builder.close();
}
private void createMany(Class<? extends TitleAble> entityType, int amount)
throws InstantiationException, IllegalAccessException {
FullTextSession fullTextSession = builder.openFullTextSession();
try {
Transaction tx = fullTextSession.beginTransaction();
fullTextSession.persist( new Nation( "Italy", "IT" ) );
tx.commit();
tx = fullTextSession.beginTransaction();
for ( int i = 0; i < amount; i++ ) {
TitleAble instance = entityType.newInstance();
instance.setTitle( sentenceInventor.nextSentence() );
//to test for HSEARCH-512 we make all entities share some proxy
Nation country = (Nation) fullTextSession.load( Nation.class, 1 );
instance.setFirstPublishedIn( country );
fullTextSession.persist( instance );
totalEntitiesInDB++;
if ( i % 250 == 249 ) {
tx.commit();
fullTextSession.clear();
System.out.println( "Test preparation: " + totalEntitiesInDB + " entities persisted" );
tx = fullTextSession.beginTransaction();
}
}
tx.commit();
}
finally {
fullTextSession.close();
}
}
/**
* Adds all stored books to the Nation.
* Needed to test for HSEARCH-534 and makes the dataset to index quite bigger.
*/
private void storeAllBooksInNation() {
FullTextSession fullTextSession = builder.openFullTextSession();
try {
Transaction tx = fullTextSession.beginTransaction();
List<Book> allBooks = fullTextSession.createCriteria( Book.class ).list();
Nation italy = (Nation) fullTextSession.load( Nation.class, 1 );
italy.getLibrariesHave().addAll( allBooks );
tx.commit();
}
finally {
fullTextSession.close();
}
}
@Test
public void testBatchIndexing() throws InterruptedException, IOException {
verifyResultNumbers(); //initial count of entities should match expectations
purgeAll(); // empty indexes
verifyIsEmpty();
reindexAll(); // rebuild the indexes
verifyResultNumbers(); // verify the count match again
reindexAll(); //tests that purgeAll is automatic:
verifyResultNumbers(); //..same numbers again
verifyIndexIsLocked( false, Dvd.class ); //non exclusive index configured
verifyIndexIsLocked( true, Book.class ); //exclusive index enabled
}
private void reindexAll() throws InterruptedException {
FullTextSession fullTextSession = builder.openFullTextSession();
SilentProgressMonitor progressMonitor = new SilentProgressMonitor();
Assert.assertFalse( progressMonitor.finished );
try {
fullTextSession.createIndexer( Object.class )
.threadsForSubsequentFetching( 8 )
.threadsToLoadObjects( 4 )
.batchSizeToLoadObjects( 30 )
.progressMonitor( progressMonitor )
.startAndWait();
}
finally {
fullTextSession.close();
}
Assert.assertTrue( progressMonitor.finished );
}
private void purgeAll() {
FullTextSession fullTextSession = builder.openFullTextSession();
try {
Transaction tx = fullTextSession.beginTransaction();
fullTextSession.purgeAll( Object.class );
tx.commit();
}
finally {
fullTextSession.close();
}
}
private void verifyIndexIsLocked(boolean isLocked, Class type) throws IOException {
SearchFactoryImplementor searchFactory = (SearchFactoryImplementor) builder.getSearchFactory();
DirectoryBasedIndexManager indexManager = (DirectoryBasedIndexManager) searchFactory.getIndexBindingForEntity( type ).getIndexManagers()[0];
Directory directory = indexManager.getDirectoryProvider().getDirectory();
LockFactory lockFactory = directory.getLockFactory();
Lock writeLock = lockFactory.makeLock( "write.lock" );
Assert.assertEquals( isLocked, writeLock.isLocked() );
}
@SuppressWarnings("unchecked")
private void verifyResultNumbers() {
assertEquals(
DVD_NUM,
countByFT( Dvd.class )
);
assertEquals(
ANCIENTBOOK_NUM + BOOK_NUM,
countByFT( Book.class )
);
assertEquals(
ANCIENTBOOK_NUM + BOOK_NUM + SECRETBOOK_NUM,
countByDatabaseCriteria( Book.class )
);
assertEquals(
SECRETBOOK_NUM,
countByDatabaseCriteria( SecretBook.class )
);
assertEquals(
ANCIENTBOOK_NUM,
countByFT( AncientBook.class )
);
assertEquals(
DVD_NUM + ANCIENTBOOK_NUM + BOOK_NUM,
countByFT( AncientBook.class, Book.class, Dvd.class )
);
assertEquals(
DVD_NUM + ANCIENTBOOK_NUM,
countByFT( AncientBook.class, Dvd.class )
);
}
@SuppressWarnings("unchecked")
private void verifyIsEmpty() {
assertEquals( 0, countByFT( Dvd.class ) );
assertEquals( 0, countByFT( Book.class ) );
assertEquals( 0, countByFT( AncientBook.class ) );
assertEquals( 0, countByFT( AncientBook.class, Book.class, Dvd.class ) );
}
private int countByFT(Class<? extends TitleAble>... types) {
Query findAll = new MatchAllDocsQuery();
int bySize = 0;
int byResultSize = 0;
FullTextSession fullTextSession = builder.openFullTextSession();
try {
Transaction tx = fullTextSession.beginTransaction();
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( findAll, types );
bySize = fullTextQuery.list().size();
byResultSize = fullTextQuery.getResultSize();
tx.commit();
}
finally {
fullTextSession.close();
}
assertEquals( bySize, byResultSize );
return bySize;
}
private long countByDatabaseCriteria(Class<? extends TitleAble> type) {
Session session = builder.openFullTextSession();
try {
Number countAsNumber = (Number) session
.createCriteria( type )
.setProjection( Projections.rowCount() )
.uniqueResult();
return countAsNumber.longValue();
}
finally {
session.close();
}
}
private static class SilentProgressMonitor implements MassIndexerProgressMonitor {
final AtomicLong objectsCounter = new AtomicLong();
volatile boolean finished = false;
public void documentsAdded(long increment) {
}
public void documentsBuilt(int number) {
}
public void entitiesLoaded(int size) {
}
public void addToTotalCount(long count) {
objectsCounter.addAndGet( count );
}
public void indexingCompleted() {
finished = true;
log.debug( "Finished indexing " + objectsCounter.get() + " entities" );
}
}
}