/*
* Copyright 2014-2016 CyberVision, Inc.
*
* 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 org.kaaproject.kaa.server.common.zk;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.test.TestingCluster;
import org.apache.curator.test.Timing;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.kaaproject.kaa.server.common.zk.bootstrap.BootstrapNode;
import org.kaaproject.kaa.server.common.zk.gen.BootstrapNodeInfo;
import org.kaaproject.kaa.server.common.zk.gen.ConnectionInfo;
import org.kaaproject.kaa.server.common.zk.gen.LoadInfo;
import org.kaaproject.kaa.server.common.zk.gen.OperationsNodeInfo;
import org.kaaproject.kaa.server.common.zk.operations.OperationsNode;
import org.kaaproject.kaa.server.common.zk.operations.OperationsNodeListener;
import java.io.IOException;
import java.nio.ByteBuffer;
public class OperationsNodeIT {
private static final int NEW_HTTP_ID = BootstrapNodeIT.HTTP_ID + 1;
private static final String BOOTSTRAP_NODE_HOST = "192.168.0.202";
private static final String ENDPOINT_NODE_HOST = "192.168.0.101";
private CuratorFramework zkClient;
private TestingCluster cluster;
@Before
public void beforeTest() {
try {
cluster = new TestingCluster(3);
cluster.start();
zkClient = CuratorFrameworkFactory.newClient(cluster.getConnectString(), buildDefaultRetryPolicy());
zkClient.start();
} catch (Exception e) {
System.err.println("Unable to initialize cluster before test! " + e);
}
}
@After
public void afterTest() {
try {
zkClient.close();
cluster.close();
} catch (Exception e) {
System.err.println("Unable to shutdown cluster after test! " + e);
}
}
@Test
public void endpointListenerTest() throws Exception {
Timing timing = new Timing();
OperationsNodeInfo endpointNodeInfo = buildOperationsNodeInfo();
BootstrapNodeInfo bootstrapNodeInfo = buildBootstrapNodeInfo();
BootstrapNode bootstrapNode = new BootstrapNode(bootstrapNodeInfo, zkClient);
OperationsNodeListener mockListener = mock(OperationsNodeListener.class);
bootstrapNode.addListener(mockListener);
bootstrapNode.start();
OperationsNode endpointNode = new OperationsNode(endpointNodeInfo, zkClient);
endpointNode.start();
timing.sleepABit();
verify(mockListener).onNodeAdded(endpointNodeInfo);
assertNotNull(bootstrapNode.getCurrentOperationServerNodes());
assertEquals(1, bootstrapNode.getCurrentOperationServerNodes().size());
OperationsNodeInfo testNodeInfo = bootstrapNode.getCurrentOperationServerNodes().get(0);
assertNotNull(testNodeInfo.getTransports());
assertEquals(2, testNodeInfo.getTransports().size());
assertNotNull(testNodeInfo.getTransports().get(0));
assertEquals(BootstrapNodeIT.HTTP_ID, testNodeInfo.getTransports().get(0).getId().intValue());
assertEquals(BootstrapNodeIT.TCP_ID, testNodeInfo.getTransports().get(1).getId().intValue());
assertNotNull(testNodeInfo.getTransports().get(0).getConnectionInfo());
endpointNodeInfo.getTransports().get(0).setId(NEW_HTTP_ID);
endpointNode.updateNodeData(endpointNodeInfo);
timing.sleepABit();
verify(mockListener).onNodeUpdated(endpointNodeInfo);
assertNotNull(bootstrapNode.getCurrentOperationServerNodes());
assertEquals(1, bootstrapNode.getCurrentOperationServerNodes().size());
assertNotNull(bootstrapNode.getCurrentOperationServerNodes().get(0));
testNodeInfo = bootstrapNode.getCurrentOperationServerNodes().get(0);
assertNotNull(testNodeInfo.getTransports());
assertEquals(NEW_HTTP_ID, testNodeInfo.getTransports().get(0).getId().intValue());
endpointNode.close();
timing.sleepABit();
verify(mockListener).onNodeRemoved(endpointNodeInfo);
Assert.assertTrue(bootstrapNode.removeListener(mockListener));
Assert.assertFalse(bootstrapNode.removeListener(mockListener));
bootstrapNode.close();
}
@Test
public void endpointExceptionTest() throws Exception {
OperationsNodeInfo endpointNodeInfo = buildOperationsNodeInfo();
OperationsNode endpointNode = new OperationsNode(endpointNodeInfo, zkClient);
endpointNode.start();
endpointNode.doZkClientAction(new ControlNodeTracker.ZkClientAction() {
@Override
public void doWithZkClient(CuratorFramework client) throws Exception {
throw new Exception("for test");
}
});
//check if operations node changed and deleted child node
//after unexpected exception, as child node was ephemeral and connection lost
String OPERATIONS_SERVER_NODE_PATH = "/operationsServerNodes";
Stat stat = zkClient.checkExists().forPath(OPERATIONS_SERVER_NODE_PATH);
int cversion = stat.getCversion();
int numChildren = stat.getNumChildren();
Assert.assertEquals(cversion, 2);
Assert.assertEquals(numChildren, 0);
endpointNode.close();
}
@Test(expected = IOException.class)
public void endpointIOExceptionTest() throws Exception {
OperationsNodeInfo endpointNodeInfo = buildOperationsNodeInfo();
OperationsNode endpointNode = new OperationsNode(endpointNodeInfo, zkClient);
endpointNode.start();
endpointNode.doZkClientAction(new ControlNodeTracker.ZkClientAction() {
@Override
public void doWithZkClient(CuratorFramework client) throws Exception {
throw new Exception("for test");
}
}, true);
Assert.assertFalse(endpointNode.isConnected());
endpointNode.close();
}
@Test
public void outdatedRemovalTest() throws Exception {
Timing timing = new Timing();
OperationsNodeInfo endpointNodeInfo = buildOperationsNodeInfo();
BootstrapNodeInfo bootstrapNodeInfo = buildBootstrapNodeInfo();
BootstrapNode bootstrapNode = new BootstrapNode(bootstrapNodeInfo, zkClient);
OperationsNodeListener mockListener = mock(OperationsNodeListener.class);
bootstrapNode.addListener(mockListener);
bootstrapNode.start();
OperationsNode endpointNode = new OperationsNode(endpointNodeInfo, zkClient);
endpointNode.start();
timing.sleepABit();
verify(mockListener).onNodeAdded(endpointNodeInfo);
OperationsNodeInfo endpointNodeInfoWithGreaterTimeStarted = buildOperationsNodeInfo();
OperationsNode endpointNodeWithGreaterTimeStarted = new OperationsNode(endpointNodeInfoWithGreaterTimeStarted, zkClient);
endpointNodeWithGreaterTimeStarted.start();
timing.sleepABit();
endpointNode.close();
timing.sleepABit();
verify(mockListener, never()).onNodeRemoved(endpointNodeInfo);
endpointNodeWithGreaterTimeStarted.close();
timing.sleepABit();
verify(mockListener).onNodeRemoved(endpointNodeInfoWithGreaterTimeStarted);
bootstrapNode.close();
}
private RetryPolicy buildDefaultRetryPolicy() {
return new ExponentialBackoffRetry(100, 1);
}
private BootstrapNodeInfo buildBootstrapNodeInfo() {
BootstrapNodeInfo nodeInfo = new BootstrapNodeInfo();
ByteBuffer testKeyData = ByteBuffer.wrap(new byte[]{10, 11, 12, 45, 34, 23, 67, 89, 66, 12});
nodeInfo.setConnectionInfo(new ConnectionInfo(BOOTSTRAP_NODE_HOST, 1000, testKeyData));
nodeInfo.setTimeStarted(System.currentTimeMillis());
nodeInfo.setTransports(BootstrapNodeIT.getHttpAndTcpTransportMD());
return nodeInfo;
}
private OperationsNodeInfo buildOperationsNodeInfo() {
OperationsNodeInfo nodeInfo = new OperationsNodeInfo();
ByteBuffer testKeyData = ByteBuffer.wrap(new byte[]{10, 11, 12, 45, 34, 23, 67, 89, 66, 12});
nodeInfo.setConnectionInfo(new ConnectionInfo(ENDPOINT_NODE_HOST, 1000, testKeyData));
nodeInfo.setLoadInfo(new LoadInfo(1, 1.0));
nodeInfo.setTimeStarted(System.currentTimeMillis());
nodeInfo.setTransports(BootstrapNodeIT.getHttpAndTcpTransportMD());
return nodeInfo;
}
}