/*
* 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.client;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.client.connection.ClientConnectionManager;
import com.hazelcast.client.connection.nio.ClientConnection;
import com.hazelcast.client.impl.ClientTestUtil;
import com.hazelcast.client.impl.HazelcastClientInstanceImpl;
import com.hazelcast.client.spi.impl.ClusterListenerSupport;
import com.hazelcast.client.test.ClientTestSupport;
import com.hazelcast.client.test.TestHazelcastFactory;
import com.hazelcast.config.Config;
import com.hazelcast.config.ListenerConfig;
import com.hazelcast.core.Client;
import com.hazelcast.core.ClientListener;
import com.hazelcast.core.ClientService;
import com.hazelcast.core.EntryAdapter;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import com.hazelcast.core.LifecycleEvent;
import com.hazelcast.core.LifecycleListener;
import com.hazelcast.nio.Address;
import com.hazelcast.spi.properties.GroupProperty;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.annotation.NightlyTest;
import com.hazelcast.test.annotation.ParallelTest;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.After;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class ClientServiceTest extends ClientTestSupport {
private final TestHazelcastFactory hazelcastFactory = new TestHazelcastFactory();
@After
public void cleanup() {
hazelcastFactory.terminateAll();
}
@Test(expected = NullPointerException.class)
public void testAddClientListener_whenListenerIsNull() {
HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
ClientService clientService = instance.getClientService();
clientService.addClientListener(null);
}
@Test(expected = NullPointerException.class)
public void testRemoveClientListener_whenIdIsNull() {
HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
ClientService clientService = instance.getClientService();
clientService.removeClientListener(null);
}
@Test(timeout = 120000)
public void testRemoveClientListener_whenListenerAlreadyRemoved() {
HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
ClientService clientService = instance.getClientService();
ClientListener clientListener = mock(ClientListener.class);
String id = clientService.addClientListener(clientListener);
// first time remove
assertTrue(clientService.removeClientListener(id));
// second time remove
assertFalse(clientService.removeClientListener(id));
}
@Test(timeout = 120000)
public void testRemoveClientListener_whenNonExistingId() {
HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
ClientService clientService = instance.getClientService();
assertFalse(clientService.removeClientListener("foobar"));
}
@Test(timeout = 120000)
public void testNumberOfClients_afterUnAuthenticatedClient() {
final HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
final ClientConfig clientConfig = new ClientConfig();
clientConfig.getGroupConfig().setPassword("wrongPassword");
try {
hazelcastFactory.newHazelcastClient(clientConfig);
} catch (IllegalStateException ignored) {
}
assertEquals(0, instance.getClientService().getConnectedClients().size());
}
@Test(timeout = 120000)
public void testNumberOfClients_afterUnAuthenticatedClient_withTwoNode() {
final HazelcastInstance instance1 = hazelcastFactory.newHazelcastInstance();
final HazelcastInstance instance2 = hazelcastFactory.newHazelcastInstance();
final ClientConfig clientConfig = new ClientConfig();
clientConfig.getGroupConfig().setPassword("wrongPassword");
try {
hazelcastFactory.newHazelcastClient(clientConfig);
} catch (IllegalStateException ignored) {
}
assertEquals(0, instance1.getClientService().getConnectedClients().size());
assertEquals(0, instance2.getClientService().getConnectedClients().size());
}
@Test(timeout = 120000)
@Category(NightlyTest.class)
public void testNumberOfClients_afterUnAuthenticatedClient_withTwoNode_twoClient() {
final HazelcastInstance instance1 = hazelcastFactory.newHazelcastInstance();
final HazelcastInstance instance2 = hazelcastFactory.newHazelcastInstance();
final ClientConfig clientConfig = new ClientConfig();
clientConfig.getGroupConfig().setPassword("wrongPassword");
try {
hazelcastFactory.newHazelcastClient(clientConfig);
} catch (IllegalStateException ignored) {
}
try {
hazelcastFactory.newHazelcastClient(clientConfig);
} catch (IllegalStateException ignored) {
}
assertEquals(0, instance1.getClientService().getConnectedClients().size());
assertEquals(0, instance2.getClientService().getConnectedClients().size());
}
@Test(timeout = 120000)
public void testConnectedClients() {
final HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
final HazelcastInstance client1 = hazelcastFactory.newHazelcastClient();
final HazelcastInstance client2 = hazelcastFactory.newHazelcastClient();
final ClientService clientService = instance.getClientService();
final Collection<Client> connectedClients = clientService.getConnectedClients();
assertEquals(2, connectedClients.size());
final String uuid1 = client1.getLocalEndpoint().getUuid();
final String uuid2 = client2.getLocalEndpoint().getUuid();
for (Client connectedClient : connectedClients) {
final String uuid = connectedClient.getUuid();
assertTrue(uuid.equals(uuid1) || uuid.equals(uuid2));
}
}
@Test(timeout = 120000)
public void testClientListener() throws InterruptedException {
final HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
final ClientService clientService = instance.getClientService();
final CountDownLatch latchAdd = new CountDownLatch(2);
final CountDownLatch latchRemove = new CountDownLatch(2);
final AtomicInteger totalAdd = new AtomicInteger(0);
final ClientListener clientListener = new ClientListener() {
@Override
public void clientConnected(Client client) {
totalAdd.incrementAndGet();
latchAdd.countDown();
}
@Override
public void clientDisconnected(Client client) {
latchRemove.countDown();
}
};
final String id = clientService.addClientListener(clientListener);
final HazelcastInstance client1 = hazelcastFactory.newHazelcastClient();
final HazelcastInstance client2 = hazelcastFactory.newHazelcastClient();
client1.getLifecycleService().shutdown();
client2.getLifecycleService().shutdown();
assertOpenEventually(latchAdd);
assertOpenEventually(latchRemove);
assertTrue(clientService.removeClientListener(id));
assertFalse(clientService.removeClientListener("foo"));
assertEquals(0, clientService.getConnectedClients().size());
hazelcastFactory.newHazelcastClient();
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(1, clientService.getConnectedClients().size());
}
});
assertEquals(2, totalAdd.get());
}
@Test(timeout = 120000)
public void testConnectedClientsWithReAuth() throws InterruptedException {
final ClientConfig clientConfig = new ClientConfig();
clientConfig.getNetworkConfig().setConnectionAttemptPeriod(1000 * 5);
clientConfig.getNetworkConfig().setConnectionAttemptLimit(Integer.MAX_VALUE);
final CountDownLatch countDownLatch = new CountDownLatch(2);
clientConfig.addListenerConfig(new ListenerConfig(new LifecycleListener() {
@Override
public void stateChanged(LifecycleEvent event) {
if (event.getState() == LifecycleEvent.LifecycleState.CLIENT_CONNECTED) {
countDownLatch.countDown();
}
}
}));
HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
final HazelcastInstance client = hazelcastFactory.newHazelcastClient(clientConfig);
//restart the node
instance.shutdown();
final HazelcastInstance restartedInstance = hazelcastFactory.newHazelcastInstance();
client.getMap(randomMapName()).size(); // do any operation
assertOpenEventually(countDownLatch); //wait for clients to reconnect & reAuth
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(1, restartedInstance.getClientService().getConnectedClients().size());
}
});
}
@Test(timeout = 120000)
public void testClientListenerForBothNodes() {
final HazelcastInstance instance1 = hazelcastFactory.newHazelcastInstance();
final HazelcastInstance instance2 = hazelcastFactory.newHazelcastInstance();
final ClientConnectedListenerLatch clientListenerLatch = new ClientConnectedListenerLatch(2);
final ClientService clientService1 = instance1.getClientService();
clientService1.addClientListener(clientListenerLatch);
final ClientService clientService2 = instance2.getClientService();
clientService2.addClientListener(clientListenerLatch);
final HazelcastInstance client = hazelcastFactory.newHazelcastClient();
final String instance1Key = generateKeyOwnedBy(instance1);
final String instance2Key = generateKeyOwnedBy(instance2);
final IMap<Object, Object> map = client.getMap("map");
map.put(instance1Key, 0);
map.put(instance2Key, 0);
assertClientConnected(clientService1, clientService2);
assertOpenEventually(clientListenerLatch);
}
@Test(timeout = 120000)
public void testClientListenerDisconnected() throws InterruptedException {
Config config = new Config();
config.setProperty(GroupProperty.IO_THREAD_COUNT.getName(), "1");
final HazelcastInstance hz = hazelcastFactory.newHazelcastInstance(config);
final HazelcastInstance hz2 = hazelcastFactory.newHazelcastInstance(config);
int clientCount = 10;
ClientDisconnectedListenerLatch listenerLatch = new ClientDisconnectedListenerLatch(2 * clientCount);
hz.getClientService().addClientListener(listenerLatch);
hz2.getClientService().addClientListener(listenerLatch);
Collection<HazelcastInstance> clients = new LinkedList<HazelcastInstance>();
for (int i = 0; i < clientCount; i++) {
HazelcastInstance client = hazelcastFactory.newHazelcastClient();
IMap<Object, Object> map = client.getMap(randomMapName());
map.addEntryListener(new EntryAdapter<Object, Object>(), true);
map.put(generateKeyOwnedBy(hz), "value");
map.put(generateKeyOwnedBy(hz2), "value");
clients.add(client);
}
ExecutorService ex = Executors.newFixedThreadPool(4);
try {
for (final HazelcastInstance client : clients) {
ex.execute(new Runnable() {
@Override
public void run() {
client.shutdown();
}
});
}
assertOpenEventually(listenerLatch);
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(0, hz.getClientService().getConnectedClients().size());
}
});
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(0, hz2.getClientService().getConnectedClients().size());
}
});
} finally {
ex.shutdown();
}
}
@Test(timeout = 120000)
public void testPendingEventPacketsWithEvents() throws InterruptedException, UnknownHostException {
HazelcastInstance hazelcastInstance = hazelcastFactory.newHazelcastInstance();
HazelcastInstance client = hazelcastFactory.newHazelcastClient();
IMap map = client.getMap(randomName());
map.addEntryListener(new EntryAdapter(), false);
for (int i = 0; i < 10; i++) {
map.put(randomString(), randomString());
}
HazelcastClientInstanceImpl clientInstanceImpl = ClientTestUtil.getHazelcastClientInstanceImpl(client);
InetSocketAddress socketAddress = hazelcastInstance.getCluster().getLocalMember().getSocketAddress();
Address address = new Address(socketAddress.getAddress().getHostAddress(), socketAddress.getPort());
ClientConnectionManager connectionManager = clientInstanceImpl.getConnectionManager();
final ClientConnection connection = (ClientConnection) connectionManager.getConnection(address);
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(0, connection.getPendingPacketCount());
}
});
}
private void assertClientConnected(ClientService... services) {
for (final ClientService service : services) {
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(1, service.getConnectedClients().size());
}
});
}
}
static class ClientConnectedListenerLatch extends CountDownLatch implements ClientListener {
public ClientConnectedListenerLatch(int count) {
super(count);
}
@Override
public void clientConnected(Client client) {
countDown();
}
@Override
public void clientDisconnected(Client client) {
}
}
static class ClientDisconnectedListenerLatch extends CountDownLatch implements ClientListener {
public ClientDisconnectedListenerLatch(int count) {
super(count);
}
@Override
public void clientConnected(Client client) {
}
@Override
public void clientDisconnected(Client client) {
countDown();
}
}
@Test
public void testClientShutdownDuration_whenServersDown() {
HazelcastInstance hazelcastInstance = hazelcastFactory.newHazelcastInstance();
final CountDownLatch countDownLatch = new CountDownLatch(1);
ClientConfig config = new ClientConfig();
config.getNetworkConfig().setConnectionAttemptPeriod(0).setConnectionAttemptLimit(1);
HazelcastInstance client = hazelcastFactory.newHazelcastClient(config);
client.getLifecycleService().addLifecycleListener(new LifecycleListener() {
@Override
public void stateChanged(LifecycleEvent event) {
if (event.getState() == LifecycleEvent.LifecycleState.SHUTDOWN) {
countDownLatch.countDown();
}
}
});
hazelcastInstance.shutdown();
assertOpenEventually(countDownLatch, ClusterListenerSupport.TERMINATE_TIMEOUT_SECONDS);
}
@Test
public void testClientListener_fromConfig() {
Config config = new Config();
final CountDownLatch connectedLatch = new CountDownLatch(1);
final CountDownLatch disconnectedLatch = new CountDownLatch(1);
ListenerConfig listenerConfig = new ListenerConfig(new ClientListener() {
@Override
public void clientConnected(Client client) {
connectedLatch.countDown();
}
@Override
public void clientDisconnected(Client client) {
disconnectedLatch.countDown();
}
});
config.addListenerConfig(listenerConfig);
HazelcastInstance instance = hazelcastFactory.newHazelcastInstance(config);
HazelcastInstance client = hazelcastFactory.newHazelcastClient();
client.shutdown();
assertOpenEventually(connectedLatch);
assertOpenEventually(disconnectedLatch);
instance.shutdown();
}
@Test
public void testClientListener_withDummyClient() {
Config config = new Config();
final CountDownLatch latch = new CountDownLatch(2);
final AtomicInteger eventCount = new AtomicInteger();
ListenerConfig listenerConfig = new ListenerConfig(new ClientListener() {
@Override
public void clientConnected(Client client) {
eventCount.incrementAndGet();
latch.countDown();
}
@Override
public void clientDisconnected(Client client) {
eventCount.incrementAndGet();
latch.countDown();
}
});
config.addListenerConfig(listenerConfig);
hazelcastFactory.newHazelcastInstance(config);
hazelcastFactory.newHazelcastInstance(config);
HazelcastInstance client = hazelcastFactory.newHazelcastClient();
client.shutdown();
assertOpenEventually(latch);
//client events will only be fired from one of the nodes.
assertTrueAllTheTime(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(2, eventCount.get());
}
}, 4);
}
@Test
public void testClientListener_withShuttingDownOwnerMember() {
Config config = new Config();
final CountDownLatch latch = new CountDownLatch(1);
final AtomicInteger atomicInteger = new AtomicInteger();
ListenerConfig listenerConfig = new ListenerConfig(new ClientListener() {
@Override
public void clientConnected(Client client) {
atomicInteger.incrementAndGet();
latch.countDown();
}
@Override
public void clientDisconnected(Client client) {
}
});
config.addListenerConfig(listenerConfig);
HazelcastInstance instance = hazelcastFactory.newHazelcastInstance();
//first member is owner connection
hazelcastFactory.newHazelcastClient();
hazelcastFactory.newHazelcastInstance(config);
//make sure connected to second one before proceeding
assertOpenEventually(latch);
//when first node is dead, client selects second one as owner
instance.shutdown();
//Testing that shutting down first member does not cause any client connected/disconnected event
assertTrueAllTheTime(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(1, atomicInteger.get());
}
}, 4);
}
}