package org.infinispan.lucene.impl; import static org.infinispan.test.TestingUtil.extractComponent; import static org.infinispan.test.TestingUtil.wrapInboundInvocationHandler; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.io.IOException; import java.util.Set; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.StringField; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.store.Directory; import org.infinispan.Cache; import org.infinispan.atomic.Delta; import org.infinispan.commands.ReplicableCommand; import org.infinispan.commands.remote.CacheRpcCommand; import org.infinispan.commands.remote.SingleRpcCommand; import org.infinispan.commands.write.AbstractDataWriteCommand; import org.infinispan.commands.write.PutKeyValueCommand; import org.infinispan.commons.util.concurrent.ConcurrentHashSet; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.container.DataContainer; import org.infinispan.container.entries.InternalCacheEntry; import org.infinispan.lucene.FileListCacheKey; import org.infinispan.lucene.directory.DirectoryBuilder; import org.infinispan.lucene.testutils.LuceneSettings; import org.infinispan.remoting.inboundhandler.DeliverOrder; import org.infinispan.remoting.inboundhandler.PerCacheInboundInvocationHandler; import org.infinispan.remoting.inboundhandler.Reply; import org.infinispan.test.MultipleCacheManagersTest; import org.testng.annotations.Test; /** * Test to verify the FileListCacheValue is being replicated incrementally * * @author gustavonalle * @since 7.0 */ @Test(groups = "functional", testName = "lucene.DeltaReplicationTest") public class DeltaReplicationTest extends MultipleCacheManagersTest { private static final String INDEX_NAME = "index"; @Test public void testDeltasAreSent() throws Exception { Cache<Object, Object> cache0 = cache(0); Cache<Object, Object> cache1 = cache(1); Directory dir = DirectoryBuilder.newDirectoryInstance(cache0, cache0, cache0, INDEX_NAME).create(); InboundInvocationHandlerDecorator handler0 = replaceOn(cache0); InboundInvocationHandlerDecorator handler1 = replaceOn(cache1); writeSingleDocument(dir); assertFileListMatch(cache0, cache1, INDEX_NAME); assertOnlyDeltasWereSent(handler0, FileListCacheKey.class); assertOnlyDeltasWereSent(handler1, FileListCacheKey.class); } private void assertOnlyDeltasWereSent(InboundInvocationHandlerDecorator handler, Class<?> clazz) { Set<AbstractDataWriteCommand> writeCommands = handler.writeCommands; for (AbstractDataWriteCommand command : writeCommands) { if (command instanceof PutKeyValueCommand) { PutKeyValueCommand putKeyValueCommand = (PutKeyValueCommand) command; if (putKeyValueCommand.getKey().getClass().equals(clazz)) { Object value = putKeyValueCommand.getValue(); assertTrue(value instanceof Delta); } } } } private void assertFileListMatch(Cache cache, Cache another, String index) { assertEquals(extract(cache, index), extract(another, index)); } private FileListCacheValue extract(Cache cache, String index) { DataContainer dataContainer = extractComponent(cache, DataContainer.class); InternalCacheEntry ice = dataContainer.get(new FileListCacheKey(index, -1)); return (FileListCacheValue) ice.getValue(); } @Override protected void createCacheManagers() throws Throwable { ConfigurationBuilder c = getDefaultClusteredCacheConfig(CacheMode.REPL_SYNC, false); createCluster(c, 2); waitForClusterToForm(); } private InboundInvocationHandlerDecorator replaceOn(Cache cache0) { return wrapInboundInvocationHandler(cache0, InboundInvocationHandlerDecorator::new); } private void writeSingleDocument(Directory dir) throws IOException { IndexWriter indexWriter = LuceneSettings.openWriter(dir, 10); Document document = new Document(); document.add(new StringField("field", "value", Field.Store.YES)); indexWriter.addDocument(document); indexWriter.close(); } class InboundInvocationHandlerDecorator implements PerCacheInboundInvocationHandler { final Set<AbstractDataWriteCommand> writeCommands = new ConcurrentHashSet<>(); final PerCacheInboundInvocationHandler delegate; InboundInvocationHandlerDecorator(PerCacheInboundInvocationHandler delegate) { this.delegate = delegate; } @Override public void handle(CacheRpcCommand cmd, Reply reply, DeliverOrder order) { if (cmd instanceof SingleRpcCommand) { SingleRpcCommand singleRpcCommand = (SingleRpcCommand) cmd; ReplicableCommand command = singleRpcCommand.getCommand(); if (command instanceof AbstractDataWriteCommand) { writeCommands.add((AbstractDataWriteCommand) command); } } delegate.handle(cmd, reply, order); } } }