/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.cache;
import com.hazelcast.cache.impl.CacheEventType;
import com.hazelcast.cache.impl.CachePartitionEventData;
import com.hazelcast.cache.impl.CacheService;
import com.hazelcast.cache.impl.HazelcastServerCachingProvider;
import com.hazelcast.cache.impl.event.CachePartitionLostEvent;
import com.hazelcast.cache.impl.event.CachePartitionLostEventFilter;
import com.hazelcast.cache.impl.event.CachePartitionLostListener;
import com.hazelcast.config.CacheConfig;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.instance.Node;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.partition.AbstractPartitionLostListenerTest;
import com.hazelcast.spi.partition.IPartition;
import com.hazelcast.spi.partition.IPartitionLostEvent;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.annotation.ParallelTest;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import javax.cache.Cache;
import javax.cache.CacheManager;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import static com.hazelcast.cache.impl.HazelcastServerCachingProvider.createCachingProvider;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class CachePartitionLostListenerTest extends AbstractPartitionLostListenerTest {
@Override
public int getNodeCount() {
return 2;
}
public static class EventCollectingCachePartitionLostListener implements CachePartitionLostListener {
private final List<CachePartitionLostEvent> events
= Collections.synchronizedList(new LinkedList<CachePartitionLostEvent>());
private final int backupCount;
public EventCollectingCachePartitionLostListener(int backupCount) {
this.backupCount = backupCount;
}
@Override
public void partitionLost(CachePartitionLostEvent event) {
this.events.add(event);
}
public List<CachePartitionLostEvent> getEvents() {
synchronized (events) {
return new ArrayList<CachePartitionLostEvent>(events);
}
}
public int getBackupCount() {
return backupCount;
}
}
@Test
public void test_partitionLostListenerInvoked() {
List<HazelcastInstance> instances = getCreatedInstancesShuffledAfterWarmedUp(1);
final HazelcastInstance instance = instances.get(0);
HazelcastServerCachingProvider cachingProvider = createCachingProvider(instance);
CacheManager cacheManager = cachingProvider.getCacheManager();
CacheConfig<Integer, String> config = new CacheConfig<Integer, String>();
Cache<Integer, String> cache = cacheManager.createCache(getIthCacheName(0), config);
ICache iCache = cache.unwrap(ICache.class);
final EventCollectingCachePartitionLostListener listener = new EventCollectingCachePartitionLostListener(0);
iCache.addPartitionLostListener(listener);
final IPartitionLostEvent internalEvent = new IPartitionLostEvent(1, 1, null);
CacheService cacheService = getNode(instance).getNodeEngine().getService(CacheService.SERVICE_NAME);
cacheService.onPartitionLost(internalEvent);
assertTrueEventually(new AssertTask() {
@Override
public void run()
throws Exception {
List<CachePartitionLostEvent> events = listener.getEvents();
assertEquals(1, events.size());
CachePartitionLostEvent event = events.get(0);
assertEquals(internalEvent.getPartitionId(), event.getPartitionId());
assertEquals(getIthCacheName(0), event.getSource());
assertEquals(getIthCacheName(0), event.getName());
assertEquals(instance.getCluster().getLocalMember(), event.getMember());
assertEquals(CacheEventType.PARTITION_LOST, event.getEventType());
}
});
cacheManager.destroyCache(getIthCacheName(0));
cacheManager.close();
cachingProvider.close();
}
@Test
public void test_partitionLostListenerInvoked_whenNodeCrashed() {
List<HazelcastInstance> instances = getCreatedInstancesShuffledAfterWarmedUp(2);
HazelcastInstance survivingInstance = instances.get(0);
HazelcastInstance terminatingInstance = instances.get(1);
HazelcastServerCachingProvider cachingProvider = createCachingProvider(survivingInstance);
CacheManager cacheManager = cachingProvider.getCacheManager();
CacheConfig<Integer, String> config = new CacheConfig<Integer, String>();
config.setBackupCount(0);
Cache<Integer, String> cache = cacheManager.createCache(getIthCacheName(0), config);
ICache iCache = cache.unwrap(ICache.class);
final EventCollectingCachePartitionLostListener listener = new EventCollectingCachePartitionLostListener(0);
iCache.addPartitionLostListener(listener);
final Set<Integer> survivingPartitionIds = new HashSet<Integer>();
Node survivingNode = getNode(survivingInstance);
Address survivingAddress = survivingNode.getThisAddress();
for (IPartition partition : survivingNode.getPartitionService().getPartitions()) {
if (survivingAddress.equals(partition.getReplicaAddress(0))) {
survivingPartitionIds.add(partition.getPartitionId());
}
}
terminatingInstance.getLifecycleService().terminate();
waitAllForSafeState(survivingInstance);
assertTrueEventually(new AssertTask() {
@Override
public void run()
throws Exception {
final List<CachePartitionLostEvent> events = listener.getEvents();
assertFalse(events.isEmpty());
for (CachePartitionLostEvent event : events) {
assertFalse(survivingPartitionIds.contains(event.getPartitionId()));
}
}
});
cacheManager.destroyCache(getIthCacheName(0));
cacheManager.close();
cachingProvider.close();
}
@Test
public void test_cachePartitionEventData_serialization() throws IOException {
CachePartitionEventData cachePartitionEventData = new CachePartitionEventData("cacheName", 1, null);
ObjectDataOutput output = mock(ObjectDataOutput.class);
cachePartitionEventData.writeData(output);
verify(output).writeUTF("cacheName");
verify(output).writeInt(1);
}
@Test
public void test_cachePartitionEventData_deserialization() throws IOException {
CachePartitionEventData cachePartitionEventData = new CachePartitionEventData("", 0, null);
ObjectDataInput input = mock(ObjectDataInput.class);
when(input.readUTF()).thenReturn("cacheName");
when(input.readInt()).thenReturn(1);
cachePartitionEventData.readData(input);
assertEquals("cacheName", cachePartitionEventData.getName());
assertEquals(1, cachePartitionEventData.getPartitionId());
}
@Test
public void testCachePartitionLostEventFilter() {
CachePartitionLostEventFilter filter = new CachePartitionLostEventFilter();
assertEquals(new CachePartitionLostEventFilter(), filter);
assertFalse(filter.eval(null));
}
}