package org.infinispan.query.tx;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.hibernate.search.exception.SearchException;
import org.infinispan.Cache;
import org.infinispan.commands.tx.PrepareCommand;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.Index;
import org.infinispan.context.impl.TxInvocationContext;
import org.infinispan.interceptors.base.CommandInterceptor;
import org.infinispan.interceptors.impl.EntryWrappingInterceptor;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.query.CacheQuery;
import org.infinispan.query.Search;
import org.infinispan.query.SearchManager;
import org.infinispan.query.test.Person;
import org.infinispan.test.SingleCacheManagerTest;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.TransactionMode;
import org.infinispan.util.concurrent.IsolationLevel;
import org.testng.Assert;
import org.testng.annotations.Test;
@Test(groups = "functional", testName = "query.tx.TwoPhaseCommitIndexingTest")
public class TwoPhaseCommitIndexingTest extends SingleCacheManagerTest {
private final AtomicBoolean injectFailures = new AtomicBoolean();
private final BlowUpInterceptor nastyInterceptor = new BlowUpInterceptor(injectFailures);
@Override
protected EmbeddedCacheManager createCacheManager() throws Exception {
ConfigurationBuilder cfg = getDefaultStandaloneCacheConfig(true);
cfg
.customInterceptors()
.addInterceptor()
.after(EntryWrappingInterceptor.class)
.interceptor(nastyInterceptor)
.transaction()
.transactionMode(TransactionMode.TRANSACTIONAL)
.use1PcForAutoCommitTransactions(false)
.indexing()
.index(Index.ALL)
.addIndexedEntity(Person.class)
.addProperty("default.directory_provider", "ram")
.addProperty("lucene_version", "LUCENE_CURRENT")
.locking().isolationLevel(IsolationLevel.READ_COMMITTED);
return TestCacheManagerFactory.createCacheManager(cfg);
}
public void testQueryAfterAddingNewNode() throws Exception {
//We'll fail the first operation by having an exception thrown after prepare
//but before the commit:
store("Astronaut", new Person("Astronaut","is asking his timezone", 32), true);
//Nothing should be applied to the indexes:
assertFind("timezone", 0);
assertFind("asking", 0);
assertFind("cat", 0);
store("Astronaut", new Person("Astronaut","is asking his timezone", 32), false);
assertFind("timezone", 1);
assertFind("asking", 1);
assertFind("cat", 0);
}
private void assertFind(String keyword, int expectedCount) {
assertFind(cache, keyword, expectedCount);
}
private static void assertFind(Cache cache, String keyword, int expectedCount) {
SearchManager queryFactory = Search.getSearchManager(cache);
Query luceneQuery = new TermQuery(new Term("blurb", keyword));
CacheQuery<?> cacheQuery = queryFactory.getQuery(luceneQuery, Person.class);
int resultSize = cacheQuery.getResultSize();
Assert.assertEquals(resultSize, expectedCount);
}
private void store(final String key, final Object value, boolean failTheOperation) {
if (failTheOperation) {
injectFailures.set(true);
try {
cache.put(key, value);
Assert.fail("Should have failed the implicit transaction");
}
catch (Exception e) {
//Expected
}
finally {
injectFailures.set(false);
}
}
else {
cache.put(key, value);
}
}
private static class BlowUpInterceptor extends CommandInterceptor {
private final AtomicBoolean injectFailures;
public BlowUpInterceptor(AtomicBoolean injectFailures) {
this.injectFailures = injectFailures;
}
@Override
public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable {
if (injectFailures.get()) {
throw new SearchException("Test");
}
else {
return super.visitPrepareCommand(ctx, command);
}
}
}
}