/* * 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.node; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TMultiplexedProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.kaaproject.kaa.common.avro.AvroByteArrayConverter; import org.kaaproject.kaa.server.common.nosql.mongo.dao.MongoDBTestRunner; import org.kaaproject.kaa.server.common.thrift.KaaThriftService; import org.kaaproject.kaa.server.common.thrift.gen.node.KaaNodeThriftService; import org.kaaproject.kaa.server.common.utils.KaaUncaughtExceptionHandler; import org.kaaproject.kaa.server.common.zk.gen.OperationsNodeInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import com.google.common.io.Closeables; /** * The Class KaaNodeServerLauncherIT. */ public class KaaNodeServerLauncherIT { /** * The Constant LOG. */ private static final Logger LOG = LoggerFactory.getLogger(KaaNodeServerLauncherIT.class); /** * The Constant HOST. */ private static final String HOST = "localhost"; /** * The Constant PORT. */ private static final int PORT = 10090; private static final String OPERATIONS_SERVER_NODE_PATH = "/operationsServerNodes"; private static final AvroByteArrayConverter<OperationsNodeInfo> OPERATIONS_NODE_INFO_CONVERTER = new AvroByteArrayConverter<>(OperationsNodeInfo.class); private static final int KAA_NODE_START_TIMEOUT_SEC = 60; private static final int KAA_NODE_STOP_TIMEOUT_SEC = 30; /** * Inits the. * * @throws Exception the exception */ @BeforeClass public static void init() throws Exception { MongoDBTestRunner.setUp(); } /** * After. * * @throws Exception the exception */ @AfterClass public static void after() throws Exception { MongoDBTestRunner.getDB().dropDatabase(); MongoDBTestRunner.tearDown(); } /** * Test start kaa node server application. * * @throws Exception the exception */ @Test public void testStartKaaNodeServerApplication() throws Exception { TestingCluster zkCluster = null; TTransport transport = null; Thread kaaNodeServerLauncherThread = null; KaaNodeThriftService.Client client = null; CuratorFramework zkClient = null; CountDownLatch latch = new CountDownLatch(1); boolean kaaNodeStarted = false; TestKaaNodeLauncher launcher = new TestKaaNodeLauncher(); try { zkCluster = new TestingCluster(new InstanceSpec(null, 2185, -1, -1, true, -1, -1, -1)); zkCluster.start(); zkClient = CuratorFrameworkFactory.newClient(zkCluster.getConnectString(), new RetryOneTime(100)); zkClient.start(); kaaNodeServerLauncherThread = new Thread(launcher); kaaNodeServerLauncherThread.start(); OperationsNodeStartupListener operationsNodeStartupListener = new OperationsNodeStartupListener(); zkClient.getCuratorListenable().addListener(operationsNodeStartupListener); zkClient.getChildren().inBackground(latch).forPath(OPERATIONS_SERVER_NODE_PATH); // Wait for operations service to start kaaNodeStarted = latch.await(KAA_NODE_START_TIMEOUT_SEC, TimeUnit.SECONDS); zkClient.getCuratorListenable().removeListener(operationsNodeStartupListener); transport = new TSocket(HOST, PORT); TProtocol protocol = new TBinaryProtocol(transport); TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, KaaThriftService.KAA_NODE_SERVICE.getServiceName()); client = new KaaNodeThriftService.Client(mp); transport.open(); client.shutdown(); } finally { boolean shutdownFailed = false; Closeables.close(zkClient, true); if (transport != null && transport.isOpen()) { Closeables.close(transport, true); } if (kaaNodeServerLauncherThread != null) { kaaNodeServerLauncherThread.join(30000); shutdownFailed = kaaNodeServerLauncherThread.isAlive(); } Closeables.close(zkCluster, true); if (launcher != null) { ConfigurableApplicationContext appContext = launcher.getApplicationContext(); if (appContext.isActive()) { Closeables.close(appContext, true); } } if (!kaaNodeStarted) { throw new TimeoutException("Timeout (" + KAA_NODE_START_TIMEOUT_SEC + " sec) occured while waiting kaa node server to start!"); } else if (shutdownFailed) { throw new TimeoutException("Timeout (" + KAA_NODE_STOP_TIMEOUT_SEC + " sec) occured while waiting kaa node server shutdown thread!"); } } } /** * Test start kaa node server application without Zk started. * * @throws Exception the exception */ @Ignore("KAA-1281 Kaa node should block startup process if zookeeper is unavailable.") @Test public void testStartKaaNodeServerApplicationWithoutZkStarted() throws Exception { TTransport transport = null; Thread kaaNodeServerLauncherThread = null; KaaNodeThriftService.Client client = null; try { kaaNodeServerLauncherThread = new Thread(new Runnable() { @Override public void run() { LOG.info("Starting Kaa Node Server ..."); KaaNodeApplication.main(new String[]{"common-zk-test-context.xml", "kaa-node-zk-test.properties"}); LOG.info("Kaa Node Server Stopped"); } }); kaaNodeServerLauncherThread.start(); Thread.sleep(30000); transport = new TSocket(HOST, PORT); TProtocol protocol = new TBinaryProtocol(transport); TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, KaaThriftService.KAA_NODE_SERVICE.getServiceName()); client = new KaaNodeThriftService.Client(mp); transport.open(); client.shutdown(); } finally { if (transport != null && transport.isOpen()) { try { transport.close(); } catch (Exception e) { } } if (kaaNodeServerLauncherThread != null) { kaaNodeServerLauncherThread.join(30000); if (kaaNodeServerLauncherThread.isAlive()) { throw new TimeoutException( "Timeout (30 sec) occured while waiting kaa node server shutdown thread!"); } } } } private class OperationsNodeStartupListener implements CuratorListener { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if (event.getType() == CuratorEventType.CHILDREN) { if (event.getChildren().isEmpty()) { client.getChildren().inBackground(event.getContext()).forPath(event.getPath()); } else { String path = event.getPath() + "/" + event.getChildren().get(0); LOG.info("Operations Node registered in ZK. Waiting for transports configration"); client.getData().inBackground(event.getContext()).forPath(path); } } else if (event.getType() == CuratorEventType.GET_DATA) { if (event.getData() == null) { client.getData().inBackground(event.getContext()).forPath(event.getPath()); } else { OperationsNodeInfo nodeInfo = OPERATIONS_NODE_INFO_CONVERTER.fromByteArray(event.getData()); boolean isTransportInitialized = !nodeInfo.getTransports().isEmpty(); if (isTransportInitialized) { LOG.info("Operations Node updated tarnsports configuration in ZK"); ((CountDownLatch) event.getContext()).countDown(); } else { client.getData().inBackground(event.getContext()).forPath(event.getPath()); } } } } } private static class TestKaaNodeApplication extends KaaNodeApplication { private static final String[] DEFAULT_APPLICATION_CONTEXT_XMLS = new String[] {"kaaNodeContext.xml"}; private static final String[] DEFAULT_APPLICATION_CONFIGURATION_FILES = new String[] {"kaa-node.properties", "sql-dao.properties", "nosql-dao.properties"}; ConfigurableApplicationContext applicationContext; public TestKaaNodeApplication() { super(DEFAULT_APPLICATION_CONTEXT_XMLS, DEFAULT_APPLICATION_CONFIGURATION_FILES); } @Override protected void init(ApplicationContext applicationContext) { this.applicationContext = (ConfigurableApplicationContext) applicationContext; super.init(applicationContext); } } private class TestKaaNodeLauncher implements Runnable { private TestKaaNodeApplication app; @Override public void run() { LOG.info("Starting Kaa Node Server ..."); Thread.setDefaultUncaughtExceptionHandler(new KaaUncaughtExceptionHandler()); app = new TestKaaNodeApplication(); app.startAndWait(new String[] {"common-test-context.xml", "kaa-node-test.properties"}); LOG.info("Kaa Node Server Stopped"); } private ConfigurableApplicationContext getApplicationContext() { if (app != null) { return app.applicationContext; } return null; } } }