/* * Copyright 2011 LiveRamp * * 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.liveramp.hank.client; import java.io.IOException; import org.apache.log4j.PropertyConfigurator; import org.apache.thrift.TException; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.server.THsHaServer; import org.apache.thrift.server.THsHaServer.Args; import org.apache.thrift.server.TServer; import org.apache.thrift.transport.TNonblockingServerSocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.liveramp.hank.config.SmartClientDaemonConfigurator; import com.liveramp.hank.config.yaml.YamlSmartClientDaemonConfigurator; import com.liveramp.hank.coordinator.Coordinator; import com.liveramp.hank.generated.SmartClient; import com.liveramp.hank.util.CommandLineChecker; /** * Run a HankSmartClient inside a Thrift server so non-Java clients can * communicate with Hank. */ public class SmartClientDaemon { private static final Logger LOG = LoggerFactory.getLogger(SmartClientDaemon.class); private final SmartClientDaemonConfigurator configurator; private final Coordinator coordinator; private final String ringGroupName; private Thread serverThread; private TServer server; private Throwable serverReasonFailed; private static final int WAITING_FOR_SERVER_MAX_TENTATIVES = 30; private static final int WAITING_FOR_SERVER_TIMEOUT_MS = 1000; public SmartClientDaemon(SmartClientDaemonConfigurator configurator) { this.configurator = configurator; this.coordinator = configurator.createCoordinator(); this.ringGroupName = configurator.getRingGroupName(); } /** * start serving the thrift server. doesn't return. * * @throws IOException * @throws TException */ private void serve() throws IOException, TException { // set up the service handler HankSmartClient handler = new HankSmartClient(coordinator, ringGroupName); // launch the thrift server TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(configurator.getPortNumber()); Args options = new THsHaServer.Args(serverSocket); options.processor(new SmartClient.Processor(handler)); options.workerThreads(configurator.getNumThreads()); options.protocolFactory(new TCompactProtocol.Factory()); server = new THsHaServer(options); server.serve(); } public void downServer() { server.stop(); try { serverThread.join(); } catch (InterruptedException e) { // we're probably shutting down LOG.debug("Interrupted waiting for server thread to exit.", e); } server = null; serverThread = null; } /** * Start serving the Thrift server. Returns when the server is up. */ public void startServer() { Runnable r = new Runnable() { @Override public void run() { try { serve(); } catch (Throwable t) { serverReasonFailed = t; LOG.error("Unexpected error in smart client server", t); } } }; serverThread = new Thread(r, "Smart client server thread"); serverReasonFailed = null; serverThread.start(); try { int tentative = 0; // Wait for thrift server to come online while (tentative < WAITING_FOR_SERVER_MAX_TENTATIVES && (server == null || !server.isServing()) && serverReasonFailed == null) { LOG.debug("Waiting for smart client server to come online..."); Thread.sleep(WAITING_FOR_SERVER_TIMEOUT_MS); ++tentative; } // Check if Thrift server failed due to an exception if (serverReasonFailed != null) { throw new RuntimeException("Smart client server failed to start", serverReasonFailed); } // Check if Thrift server failed to come online if (server == null || !server.isServing()) { throw new RuntimeException( String.format("Waited for smart client server to come online for %d seconds but it did not.", (WAITING_FOR_SERVER_MAX_TENTATIVES * WAITING_FOR_SERVER_TIMEOUT_MS) / 1000)); } LOG.debug("Smart client server is online."); } catch (InterruptedException e) { throw new RuntimeException("Interrupted waiting for smart client server thread to start", e); } } private void run() { startServer(); while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { LOG.debug("Interrupted main thread. Exiting.", e); break; } } downServer(); } public static void main(String[] args) throws Throwable { CommandLineChecker.check(args, new String[]{"configuration_file_path", "log4j_properties_file_path"}, SmartClientDaemon.class); String configPath = args[0]; String log4jProps = args[1]; PropertyConfigurator.configure(log4jProps); new SmartClientDaemon(new YamlSmartClientDaemonConfigurator(configPath)).run(); } }