/* * 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.batchindexing; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.hibernate.Transaction; import org.hibernate.search.FullTextQuery; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; import org.hibernate.search.batchindexing.impl.SimpleIndexingProgressMonitor; import org.hibernate.search.test.SearchTestBase; import org.hibernate.search.testsupport.concurrency.Poller; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; import org.hibernate.testing.TestForIssue; import org.junit.Assert; import org.junit.Test; @TestForIssue(jiraKey = "HSEARCH-655") public class MassIndexerCancellingTest extends SearchTestBase { private static final Log log = LoggerFactory.make(); @Test public void testMassIndexerCancel() throws InterruptedException { FullTextSession fullTextSession = prepareSomeData( this ); InnerIndexerProgressMonitor monitor = new InnerIndexerProgressMonitor(); int threadsToLoadObjects = 2; Future task = fullTextSession.createIndexer( Book.class ) .threadsToLoadObjects( threadsToLoadObjects ) .batchSizeToLoadObjects( 1 ) .progressMonitor( monitor ) .purgeAllOnStart( true ) .optimizeOnFinish( false ) .start(); // wait a moment to let indexing job start monitor.waitUntilBusyThreads( threadsToLoadObjects ); // cancel indexing job task.cancel( true ); // wait a bit to let indexing threads correctly end monitor.waitUntilBusyThreads( 0 ); fullTextSession.close(); // check 2 indexing thread enlisted Assert.assertTrue( monitor.getThreadNumber() == threadsToLoadObjects ); // verify index is now containing 2 docs Poller.milliseconds( 10_000, 10 ).pollAssertion( () -> { Assert.assertEquals( "Expected index size still not reached after 10 seconds!", 2, getIndexSize() ); } ); // check all indexing thread are interrupted Assert.assertTrue( monitor.massIndexerThreadsAreInterruptedOrDied() ); } class InnerIndexerProgressMonitor extends SimpleIndexingProgressMonitor { public final List<Thread> threads = Collections.synchronizedList( new ArrayList<Thread>() ); private final AtomicInteger busyThreads = new AtomicInteger(); public InnerIndexerProgressMonitor() { super(); } @Override public void documentsBuilt(int number) { super.documentsBuilt( number ); log.debug( "enlist EntityLoader thread [" + Thread.currentThread() + "] and simulate document producer activity" ); threads.add( Thread.currentThread() ); busyThreads.incrementAndGet(); while ( true ) { // simulate activity until thread interrupted if ( Thread.currentThread().isInterrupted() ) { log.tracef( "Indexing thread is interrupted : end activity simulation " ); break; } } busyThreads.decrementAndGet(); } void waitUntilBusyThreads(int expectedNumberBusyThreads) { while ( busyThreads.get() != expectedNumberBusyThreads ) { Thread.yield(); } } public boolean massIndexerThreadsAreInterruptedOrDied() { for ( Thread th : threads ) { if ( th.isAlive() ) { if ( !th.isInterrupted() ) { log.tracef( "Thread [" + th + "] is not interrupted or alive" ); return false; } } } return true; } public int getThreadNumber() { return threads.size(); } } private int getIndexSize() { FullTextSession fullTextSession = Search.getFullTextSession( openSession() ); try { Transaction transaction = fullTextSession.beginTransaction(); Query q = new MatchAllDocsQuery(); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( q, Book.class ); int resultSize = fullTextQuery.getResultSize(); transaction.commit(); return resultSize; } finally { fullTextSession.close(); } } static FullTextSession prepareSomeData(SearchTestBase testCase) { FullTextSession fullTextSession = Search.getFullTextSession( testCase.openSession() ); fullTextSession.beginTransaction(); Nation france = new Nation( "France", "FR" ); fullTextSession.save( france ); Book ceylonBook = new Book(); ceylonBook.setTitle( "Ceylon in Action" ); ceylonBook.setFirstPublishedIn( france ); fullTextSession.save( ceylonBook ); Book hsBook = new Book(); hsBook.setTitle( "HibernateSearch in Action" ); hsBook.setFirstPublishedIn( france ); fullTextSession.save( hsBook ); Book thirdBook = new Book(); thirdBook.setTitle( "Le chateau de ma mère" ); thirdBook.setFirstPublishedIn( france ); fullTextSession.save( thirdBook ); Book fourthBook = new Book(); fourthBook.setTitle( "La gloire de mon père" ); fourthBook.setFirstPublishedIn( france ); fullTextSession.save( fourthBook ); fullTextSession.getTransaction().commit(); return fullTextSession; } @Override public Class<?>[] getAnnotatedClasses() { return new Class[] { Book.class, Nation.class }; } }