/* * 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.service.initialization; import static org.kaaproject.kaa.server.common.thrift.KaaThriftService.BOOTSTRAP_SERVICE; import static org.kaaproject.kaa.server.common.thrift.KaaThriftService.KAA_NODE_SERVICE; import static org.kaaproject.kaa.server.common.thrift.KaaThriftService.OPERATIONS_SERVICE; import org.apache.curator.framework.CuratorFramework; import org.apache.thrift.TMultiplexedProcessor; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.server.TThreadPoolServer.Args; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TServerTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransportException; import org.kaaproject.kaa.server.common.thrift.gen.bootstrap.BootstrapThriftService; import org.kaaproject.kaa.server.common.thrift.gen.node.KaaNodeThriftService; import org.kaaproject.kaa.server.common.thrift.gen.operations.OperationsThriftService; import org.kaaproject.kaa.server.common.thrift.util.ThriftExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @Service public class KaaNodeInitializationService extends AbstractInitializationService { private static final Logger LOG = LoggerFactory.getLogger(KaaNodeInitializationService.class); private final Set<TSocketWrapper> openedSockets = new HashSet<TSocketWrapper>(); private TServer server; private ExecutorService executorService; @Autowired private KaaNodeThriftService.Iface kaaNodeThriftService; @Autowired @Lazy private BootstrapThriftService.Iface bootstrapThriftService; @Autowired @Lazy private OperationsThriftService.Iface operationsThriftService; @Autowired @Lazy private InitializationService controlInitializationService; @Autowired @Lazy private InitializationService bootstrapInitializationService; @Autowired @Lazy private InitializationService operationsInitializationService; @Autowired private CuratorFramework zkClient; /* * (non-Javadoc) * * @see org.kaaproject.kaa.server.node.bootstrap.KaaNodeBootstrapService#start() */ @Override public void start() { final CountDownLatch thriftStartupLatch = new CountDownLatch(1); final CountDownLatch thriftShutdownLatch = new CountDownLatch(1); startThrift(thriftStartupLatch, thriftShutdownLatch); try { thriftStartupLatch.await(); } catch (InterruptedException ex) { LOG.error("Interrupted while waiting for thrift to start...", ex); } if (waitZkConnection()) { if (getNodeConfig().isControlServiceEnabled()) { controlInitializationService.start(); } if (getNodeConfig().isBootstrapServiceEnabled()) { bootstrapInitializationService.start(); } if (getNodeConfig().isOperationsServiceEnabled()) { operationsInitializationService.start(); } LOG.info("Kaa Node Server Started."); } else { LOG.error("Failed to connect to Zookeeper within {} minutes." + " Kaa Node Server will be stopped.", getNodeConfig().getZkWaitConnectionTime()); stopThrift(); } try { thriftShutdownLatch.await(); } catch (InterruptedException ex) { LOG.error("Interrupted while waiting for thrift to stop...", ex); } } private boolean waitZkConnection() { if (!zkClient.isStarted()) { zkClient.start(); } try { LOG.info("Waiting connection to Zookeeper at ", getNodeConfig().getZkHostPortList()); return zkClient.blockUntilConnected(getNodeConfig().getZkWaitConnectionTime(), TimeUnit.MINUTES); } catch (InterruptedException ex) { LOG.error("Zookeeper client was interrupted while waiting for connection! ", getNodeConfig().getZkHostPortList(), ex); return false; } } /* * (non-Javadoc) * * @see org.kaaproject.kaa.server.node.bootstrap.KaaNodeBootstrapService#stop() */ @Override public void stop() { if (getNodeConfig().isControlServiceEnabled()) { controlInitializationService.stop(); } if (getNodeConfig().isBootstrapServiceEnabled()) { bootstrapInitializationService.stop(); } if (getNodeConfig().isOperationsServiceEnabled()) { operationsInitializationService.stop(); } zkClient.close(); server.stop(); ThriftExecutor.shutdown(); LOG.info("Kaa Node Server Stopped."); } /** * Stop thrift. */ public void stopThrift() { if (zkClient != null) { zkClient.close(); } if (server != null) { server.stop(); } ThriftExecutor.shutdown(); LOG.info("Kaa Node Server Stopped."); } /** * Start thrift. */ private void startThrift(final CountDownLatch thriftStartupLatch, final CountDownLatch thriftShutdownLatch) { Runnable thriftRunnable = new Runnable() { @Override public void run() { LOG.info("Initializing Thrift Service for Kaa Node Server...."); LOG.info("host: " + getNodeConfig().getThriftHost()); LOG.info("port: " + getNodeConfig().getThriftPort()); try { TMultiplexedProcessor processor = new TMultiplexedProcessor(); KaaNodeThriftService.Processor<KaaNodeThriftService.Iface> kaaNodeProcessor = new KaaNodeThriftService.Processor<>(kaaNodeThriftService); processor.registerProcessor(KAA_NODE_SERVICE.getServiceName(), kaaNodeProcessor); if (getNodeConfig().isBootstrapServiceEnabled()) { BootstrapThriftService.Processor<BootstrapThriftService.Iface> bootstrapProcessor = new BootstrapThriftService.Processor<>(bootstrapThriftService); processor.registerProcessor(BOOTSTRAP_SERVICE.getServiceName(), bootstrapProcessor); } if (getNodeConfig().isOperationsServiceEnabled()) { OperationsThriftService.Processor<OperationsThriftService.Iface> operationsProcessor = new OperationsThriftService.Processor<>(operationsThriftService); processor.registerProcessor(OPERATIONS_SERVICE.getServiceName(), operationsProcessor); } TServerTransport serverTransport = createServerSocket(); server = createServer(serverTransport, processor); LOG.info("Thrift Kaa Node Server Started."); thriftStartupLatch.countDown(); server.serve(); if (executorService != null && !executorService.isTerminated()) { for (TSocketWrapper socket : new ArrayList<>(openedSockets)) { if (socket.getSocket() != null && !socket.getSocket().isClosed()) { socket.close(); } } LOG.info("Terminating executor service."); executorService.shutdownNow(); } LOG.info("Thrift Kaa Node Server Stopped."); thriftShutdownLatch.countDown(); } catch (TTransportException ex) { LOG.error("TTransportException", ex); } finally { if (thriftStartupLatch.getCount() > 0) { thriftStartupLatch.countDown(); } if (thriftShutdownLatch.getCount() > 0) { LOG.info("Thrift Kaa Node Server Stopped."); thriftShutdownLatch.countDown(); } } } }; new Thread(thriftRunnable).start(); } /** * Creates the server socket. * * @return the t server transport * @throws TTransportException the t transport exception */ public TServerTransport createServerSocket() throws TTransportException { return new TServerSocket( new InetSocketAddress(getNodeConfig().getThriftHost(), getNodeConfig().getThriftPort())) { @Override protected TSocket acceptImpl() throws TTransportException { ServerSocket serverSocket = getServerSocket(); if (serverSocket == null) { throw new TTransportException( TTransportException.NOT_OPEN, "No underlying server socket." ); } try { Socket result = serverSocket.accept(); TSocketWrapper result2 = new TSocketWrapper(result); result2.setTimeout(0); openedSockets.add(result2); return result2; } catch (IOException iox) { throw new TTransportException(iox); } } }; } /** * Creates the server. * * @param serverTransport the server transport * @param processor the processor * @return the t server */ public TServer createServer(TServerTransport serverTransport, TMultiplexedProcessor processor) { TThreadPoolServer.Args args = new Args(serverTransport).processor(processor); args.stopTimeoutVal = 3; args.stopTimeoutUnit = TimeUnit.SECONDS; SynchronousQueue<Runnable> executorQueue = // NOSONAR new SynchronousQueue<Runnable>(); executorService = new ThreadPoolExecutor(args.minWorkerThreads, args.maxWorkerThreads, 60, TimeUnit.SECONDS, executorQueue); args.executorService = executorService; return new TThreadPoolServer(args); } class TSocketWrapper extends TSocket { public TSocketWrapper(Socket socket) throws TTransportException { super(socket); } @Override public void close() { super.close(); openedSockets.remove(this); } } }