/*
* Copyright 2017 NAVER Corp.
*
* 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.navercorp.pinpoint.collector.cluster.zookeeper;
import com.navercorp.pinpoint.collector.TestAwaitTaskUtils;
import com.navercorp.pinpoint.collector.TestAwaitUtils;
import com.navercorp.pinpoint.collector.cluster.zookeeper.exception.PinpointZookeeperException;
import com.navercorp.pinpoint.rpc.packet.HandshakePropertyType;
import com.navercorp.pinpoint.rpc.server.PinpointServer;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @Author Taejin Koo
*/
public class ZookeeperJobWorkerTest {
private static final String PINPOINT_CLUSTER_PATH = "/pinpoint-cluster";
private static final String PINPOINT_COLLECTOR_CLUSTER_PATH = PINPOINT_CLUSTER_PATH + "/collector";
private static final String IDENTIFIER = "ZookeeperJobWorkerTest";
private static final String PATH = "/pinpoint-cluster/collector/" + IDENTIFIER;
private static final String EMPTY_STRING = "";
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final TestAwaitUtils awaitUtils = new TestAwaitUtils(50, 3000);
@Test
public void test1() throws Exception {
MockZookeeperClient zookeeperClient = new MockZookeeperClient();
zookeeperClient.connect();
ZookeeperJobWorker zookeeperWorker = new ZookeeperJobWorker(zookeeperClient, IDENTIFIER);
zookeeperWorker.start();
// To check for handling when multiple started. (goal: nothing happen)
zookeeperWorker.start();
try {
int random = ThreadLocalRandom.current().nextInt(10, 20);
for (int i = 0; i < random; i++) {
PinpointServer mockServer = createMockPinpointServer("app" + i, "agent" + i, System.currentTimeMillis());
zookeeperWorker.addPinpointServer(mockServer);
}
waitZookeeperServerData(random, zookeeperClient);
Assert.assertEquals(random, decodeServerData(zookeeperWorker.getClusterData()).size());
} finally {
zookeeperWorker.stop();
}
}
@Test
public void test2() throws Exception {
MockZookeeperClient zookeeperClient = new MockZookeeperClient();
zookeeperClient.connect();
ZookeeperJobWorker zookeeperWorker = new ZookeeperJobWorker(zookeeperClient, IDENTIFIER);
zookeeperWorker.start();
try {
PinpointServer mockServer = createMockPinpointServer("app", "agent", System.currentTimeMillis());
zookeeperWorker.addPinpointServer(mockServer);
zookeeperWorker.addPinpointServer(mockServer);
waitZookeeperServerData(1, zookeeperClient);
Assert.assertEquals(1, decodeServerData(zookeeperWorker.getClusterData()).size());
zookeeperWorker.removePinpointServer(mockServer);
waitZookeeperServerData(0, zookeeperClient);
Assert.assertEquals(0, decodeServerData(zookeeperWorker.getClusterData()).size());
} finally {
zookeeperWorker.stop();
}
}
@Test
public void test3() throws Exception {
MockZookeeperClient zookeeperClient = new MockZookeeperClient();
zookeeperClient.connect();
ZookeeperJobWorker zookeeperWorker = new ZookeeperJobWorker(zookeeperClient, IDENTIFIER);
zookeeperWorker.start();
try {
PinpointServer mockServer = createMockPinpointServer("app", "agent", System.currentTimeMillis());
zookeeperWorker.addPinpointServer(mockServer);
waitZookeeperServerData(1, zookeeperClient);
Assert.assertEquals(1, decodeServerData(zookeeperWorker.getClusterData()).size());
zookeeperWorker.clear();
waitZookeeperServerData(0, zookeeperClient);
Assert.assertEquals(0, decodeServerData(zookeeperWorker.getClusterData()).size());
zookeeperWorker.addPinpointServer(mockServer);
waitZookeeperServerData(1, zookeeperClient);
Assert.assertEquals(1, decodeServerData(zookeeperWorker.getClusterData()).size());
} finally {
zookeeperWorker.stop();
}
}
@Test
public void test4() throws Exception {
MockZookeeperClient zookeeperClient = new MockZookeeperClient();
zookeeperClient.connect();
ZookeeperJobWorker zookeeperWorker = new ZookeeperJobWorker(zookeeperClient, IDENTIFIER);
zookeeperWorker.start();
try {
PinpointServer mockServer1 = createMockPinpointServer("app", "agent", System.currentTimeMillis());
zookeeperWorker.addPinpointServer(mockServer1);
PinpointServer mockServer2 = createMockPinpointServer("app", "agent", System.currentTimeMillis() + 1000);
zookeeperWorker.addPinpointServer(mockServer2);
waitZookeeperServerData(2, zookeeperClient);
Assert.assertEquals(2, decodeServerData(zookeeperWorker.getClusterData()).size());
zookeeperWorker.removePinpointServer(mockServer1);
waitZookeeperServerData(1, zookeeperClient);
Assert.assertEquals(1, decodeServerData(zookeeperWorker.getClusterData()).size());
} finally {
zookeeperWorker.stop();
}
}
private PinpointServer createMockPinpointServer(String applicationName, String agentId, long startTimeStamp) {
Map<Object, Object> properties = new HashMap<>();
properties.put(HandshakePropertyType.APPLICATION_NAME.getName(), applicationName);
properties.put(HandshakePropertyType.AGENT_ID.getName(), agentId);
properties.put(HandshakePropertyType.START_TIMESTAMP.getName(), startTimeStamp);
PinpointServer mockServer = mock(PinpointServer.class);
when(mockServer.getChannelProperties()).thenReturn(properties);
return mockServer;
}
private List<String> getServerData(ZookeeperClient zookeeperClient) throws PinpointZookeeperException, InterruptedException {
return decodeServerData(zookeeperClient.getData(PATH));
}
private List<String> decodeServerData(byte[] serverData) throws PinpointZookeeperException, InterruptedException {
if (serverData == null) {
return Collections.emptyList();
}
List<String> servers = new ArrayList<>();
String[] allData = new String(serverData).split("\r\n");
for (String data : allData) {
if (!EMPTY_STRING.equals(data.trim())) {
servers.add(data);
}
}
return servers;
}
private void waitZookeeperServerData(final int expectedServerDataCount, final MockZookeeperClient zookeeperClient) {
boolean pass = awaitUtils.await(new TestAwaitTaskUtils() {
@Override
public boolean checkCompleted() {
try {
return expectedServerDataCount == getServerData(zookeeperClient).size();
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
return false;
}
});
Assert.assertTrue(pass);
}
class MockZookeeperClient implements ZookeeperClient {
private final AtomicInteger intAdder = new AtomicInteger(0);
private final byte[] EMPTY_BYTE = new byte[]{};
private final Map<String, byte[]> contents = new HashMap<>();
private volatile boolean connected = false;
@Override
public void connect() throws IOException {
connected = true;
}
@Override
public synchronized void reconnectWhenSessionExpired() {
connected = true;
}
@Override
public synchronized void createPath(String path) throws PinpointZookeeperException, InterruptedException {
contents.put(path, EMPTY_BYTE);
}
@Override
public synchronized void createPath(String path, boolean createEndNode) throws PinpointZookeeperException, InterruptedException {
contents.put(path, EMPTY_BYTE);
}
@Override
public synchronized String createNode(String zNodePath, byte[] data) throws PinpointZookeeperException, InterruptedException {
contents.put(zNodePath, data);
return "";
}
@Override
public synchronized byte[] getData(String path) throws PinpointZookeeperException, InterruptedException {
byte[] bytes = contents.get(path);
return bytes;
}
@Override
public synchronized void setData(String path, byte[] data) throws PinpointZookeeperException, InterruptedException {
// for check retry
if (intAdder.incrementAndGet() % 2 == 1) {
throw new PinpointZookeeperException("exception");
}
if (!contents.containsKey(path)) {
throw new PinpointZookeeperException("can't find path.");
}
contents.put(path, data);
}
@Override
public synchronized void delete(String path) throws PinpointZookeeperException, InterruptedException {
contents.remove(path);
}
@Override
public synchronized boolean exists(String path) throws PinpointZookeeperException, InterruptedException {
return contents.containsKey(path);
}
@Override
public synchronized boolean isConnected() {
return connected;
}
@Override
public synchronized List<String> getChildrenNode(String path, boolean watch) throws PinpointZookeeperException, InterruptedException {
return new ArrayList<>();
}
@Override
public synchronized void close() {
connected = false;
}
}
}