/* * 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.map.impl; import com.hazelcast.instance.NodeExtension; import com.hazelcast.logging.ILogger; import com.hazelcast.logging.Logger; import com.hazelcast.map.MapInterceptor; import com.hazelcast.spi.impl.operationexecutor.OperationRunner; import com.hazelcast.spi.impl.operationexecutor.impl.DefaultOperationQueue; import com.hazelcast.spi.impl.operationexecutor.impl.OperationQueue; import com.hazelcast.spi.impl.operationexecutor.impl.PartitionOperationThread; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.RequireAssertEnabled; import com.hazelcast.test.annotation.NightlyTest; 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 java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; import static org.mockito.Mockito.mock; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class InterceptorRegistryTest extends HazelcastTestSupport { private static final ILogger LOGGER = Logger.getLogger(InterceptorRegistryTest.class); private final InterceptorRegistry registry = new InterceptorRegistry(); private final TestMapInterceptor interceptor = new TestMapInterceptor(); @Test public void testRegister() { registry.register(interceptor.id, interceptor); assertInterceptorRegistryContainsInterceptor(); } @Test public void testRegister_whenRegisteredTwice_doNothing() { registry.register(interceptor.id, interceptor); registry.register(interceptor.id, interceptor); assertInterceptorRegistryContainsInterceptor(); } @Test @RequireAssertEnabled public void testRegister_fromPartitionOperationThread() throws Exception { OperationQueue queue = new DefaultOperationQueue(); PartitionOperationThread thread = getPartitionOperationThread(queue); thread.start(); final CountDownLatch latch = new CountDownLatch(1); Object task = new Runnable() { @Override public void run() { try { registry.register(interceptor.id, interceptor); } catch (AssertionError e) { e.printStackTrace(); latch.countDown(); } } }; queue.add(task, false); latch.await(); thread.shutdown(); thread.join(); assertInterceptorRegistryContainsNotInterceptor(); } @Test public void testDeregister() { registry.register(interceptor.id, interceptor); registry.deregister(interceptor.id); assertInterceptorRegistryContainsNotInterceptor(); } @Test public void testDeregister_whenInterceptorWasNotRegistered_thenDoNothing() { registry.deregister(interceptor.id); assertInterceptorRegistryContainsNotInterceptor(); } @Test @RequireAssertEnabled public void testDeregister_fromPartitionOperationThread() throws Exception { OperationQueue queue = new DefaultOperationQueue(); PartitionOperationThread thread = getPartitionOperationThread(queue); thread.start(); registry.register(interceptor.id, interceptor); final CountDownLatch latch = new CountDownLatch(1); Object task = new Runnable() { @Override public void run() { try { registry.deregister(interceptor.id); } catch (AssertionError e) { e.printStackTrace(); latch.countDown(); } } }; queue.add(task, false); latch.await(); thread.shutdown(); thread.join(); assertInterceptorRegistryContainsInterceptor(); } @Test @Category(NightlyTest.class) public void test_afterConcurrentRegisterDeregister_thenInternalStructuresAreEmpty() throws Exception { final AtomicBoolean stop = new AtomicBoolean(false); List<Thread> threads = new ArrayList<Thread>(); for (int i = 0; i < 10; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { TestMapInterceptor interceptor = new TestMapInterceptor(); while (!stop.get()) { registry.register(interceptor.id, interceptor); registry.deregister(interceptor.id); } } }); thread.start(); threads.add(thread); } sleepSeconds(10); stop.set(true); for (Thread thread : threads) { thread.join(); } // expecting internals empty assertTrue("Interceptor list should be empty", registry.getInterceptors().isEmpty()); assertTrue("Id2Interceptor map should be empty", registry.getId2InterceptorMap().isEmpty()); } private void assertInterceptorRegistryContainsInterceptor() { List<MapInterceptor> interceptors = registry.getInterceptors(); assertContains(interceptors, interceptor); Map<String, MapInterceptor> id2InterceptorMap = registry.getId2InterceptorMap(); assertTrue(id2InterceptorMap.containsKey(interceptor.id)); assertTrue(id2InterceptorMap.containsValue(interceptor)); } private void assertInterceptorRegistryContainsNotInterceptor() { List<MapInterceptor> interceptors = registry.getInterceptors(); assertNotContains(interceptors, interceptor); Map<String, MapInterceptor> id2InterceptorMap = registry.getId2InterceptorMap(); assertFalse(id2InterceptorMap.containsKey(interceptor.id)); assertFalse(id2InterceptorMap.containsValue(interceptor)); } private PartitionOperationThread getPartitionOperationThread(OperationQueue queue) { NodeExtension nodeExtension = mock(NodeExtension.class); OperationRunner operationRunner = mock(OperationRunner.class); OperationRunner[] operationRunners = new OperationRunner[]{operationRunner}; return new PartitionOperationThread("threadName", 0, queue, LOGGER, nodeExtension, operationRunners, getClass().getClassLoader()); } private static class TestMapInterceptor implements MapInterceptor { public final String id = TestMapInterceptor.class.toString(); @Override public Object interceptGet(Object value) { return null; } @Override public void afterGet(Object value) { } @Override public Object interceptPut(Object oldValue, Object newValue) { return null; } @Override public void afterPut(Object value) { } @Override public Object interceptRemove(Object removedValue) { return null; } @Override public void afterRemove(Object value) { } @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TestMapInterceptor that = (TestMapInterceptor) o; return id.equals(that.id); } } }