package org.infinispan.client.hotrod.event;
import static org.infinispan.server.hotrod.test.HotRodTestingUtil.hotRodCacheConfiguration;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNull;
import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.annotation.ClientCacheEntryCreated;
import org.infinispan.client.hotrod.annotation.ClientListener;
import org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller;
import org.infinispan.client.hotrod.query.testdomain.protobuf.UserPB;
import org.infinispan.client.hotrod.query.testdomain.protobuf.marshallers.MarshallerRegistration;
import org.infinispan.client.hotrod.test.MultiHotRodServersTest;
import org.infinispan.commons.util.Util;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.filter.NamedFactory;
import org.infinispan.marshall.core.ExternalPojo;
import org.infinispan.metadata.Metadata;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilter;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilterFactory;
import org.infinispan.notifications.cachelistener.filter.EventType;
import org.infinispan.query.dsl.embedded.testdomain.User;
import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants;
import org.testng.annotations.Test;
/**
* A simple remote listener test with filter and protobuf marshalling. This test uses raw key/value in events.
*
* @author anistor@redhat.com
* @since 7.2
*/
@Test(groups = "functional", testName = "client.hotrod.event.ClientListenerWithFilterAndRawProtobufTest")
public class ClientListenerWithFilterAndRawProtobufTest extends MultiHotRodServersTest {
private final int NUM_NODES = 2;
private RemoteCache<Object, Object> remoteCache;
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder cfgBuilder = hotRodCacheConfiguration(getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, false));
createHotRodServers(NUM_NODES, cfgBuilder);
waitForClusterToForm();
for (int i = 0; i < NUM_NODES; i++) {
server(i).addCacheEventFilterFactory("custom-filter-factory", new CustomCacheEventFilterFactory());
}
//initialize server-side serialization context
RemoteCache<String, String> metadataCache = client(0).getCache(ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME);
metadataCache.put("sample_bank_account/bank.proto", Util.read(Util.getResourceAsStream("/sample_bank_account/bank.proto", getClass().getClassLoader())));
assertFalse(metadataCache.containsKey(ProtobufMetadataManagerConstants.ERRORS_KEY_SUFFIX));
//initialize client-side serialization context
MarshallerRegistration.registerMarshallers(ProtoStreamMarshaller.getSerializationContext(client(0)));
remoteCache = client(0).getCache();
}
@Override
protected org.infinispan.client.hotrod.configuration.ConfigurationBuilder createHotRodClientConfigurationBuilder(int serverPort) {
return super.createHotRodClientConfigurationBuilder(serverPort)
.marshaller(new ProtoStreamMarshaller());
}
public void testEventFilter() throws Exception {
Object[] filterFactoryParams = new Object[]{"string_key_1", "user_1"};
ClientEntryListener listener = new ClientEntryListener();
remoteCache.addClientListener(listener, filterFactoryParams, null);
User user1 = new UserPB();
user1.setId(1);
user1.setName("John");
user1.setSurname("Doe");
user1.setGender(User.Gender.MALE);
user1.setAge(22);
remoteCache.put("string_key_1", "string value 1");
remoteCache.put("string_key_2", "string value 2");
remoteCache.put("user_1", user1);
assertEquals(3, remoteCache.keySet().size());
ClientCacheEntryCreatedEvent e = listener.createEvents.poll(5, TimeUnit.SECONDS);
assertEquals("string_key_1", e.getKey());
e = listener.createEvents.poll(5, TimeUnit.SECONDS);
assertEquals("user_1", e.getKey());
e = listener.createEvents.poll(5, TimeUnit.SECONDS);
assertNull("No more elements expected in queue!", e);
}
@ClientListener(filterFactoryName = "custom-filter-factory", useRawData = true)
public static class ClientEntryListener {
public final BlockingQueue<ClientCacheEntryCreatedEvent> createEvents = new LinkedBlockingQueue<>();
@ClientCacheEntryCreated
@SuppressWarnings("unused")
public void handleClientCacheEntryCreatedEvent(ClientCacheEntryCreatedEvent event) {
createEvents.add(event);
}
}
@NamedFactory(name = "custom-filter-factory")
public static class CustomCacheEventFilterFactory implements CacheEventFilterFactory {
private final ProtoStreamMarshaller marshaller = new ProtoStreamMarshaller();
@Override
public CacheEventFilter<byte[], byte[]> getFilter(Object[] params) {
String firstParam;
String secondParam;
try {
firstParam = (String) marshaller.objectFromByteBuffer((byte[]) params[0]);
secondParam = (String) marshaller.objectFromByteBuffer((byte[]) params[1]);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return new CustomEventFilter(firstParam, secondParam);
}
}
public static class CustomEventFilter implements CacheEventFilter<byte[], byte[]>, Serializable, ExternalPojo {
private transient ProtoStreamMarshaller marshaller;
private String firstParam;
private String secondParam;
public CustomEventFilter(String firstParam, String secondParam) {
this.firstParam = firstParam;
this.secondParam = secondParam;
}
private ProtoStreamMarshaller getMarshaller() {
if (marshaller == null) {
marshaller = new ProtoStreamMarshaller();
}
return marshaller;
}
@Override
public boolean accept(byte[] key, byte[] oldValue, Metadata oldMetadata, byte[] newValue, Metadata newMetadata, EventType eventType) {
try {
String stringKey = (String) getMarshaller().objectFromByteBuffer(key);
// this filter accepts only the two keys it received as params
return firstParam.equals(stringKey) || secondParam.equals(stringKey);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}