Package com.linkedin.databus2.core.container.netty

Source Code of com.linkedin.databus2.core.container.netty.ServerContainer$Cli

package com.linkedin.databus2.core.container.netty;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* 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.
*
*/


import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import javax.management.MBeanServer;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.NameAlreadyBoundException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.DirectChannelBufferFactory;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.jboss.netty.logging.Log4JLoggerFactory;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.ThreadNameDeterminer;
import org.jboss.netty.util.ThreadRenamingRunnable;
import org.jboss.netty.util.Timer;

import com.linkedin.databus.core.DatabusComponentStatus;
import com.linkedin.databus.core.monitoring.mbean.AggregatedDbusEventsStatisticsCollector;
import com.linkedin.databus.core.monitoring.mbean.DbusEventsStatisticsCollector;
import com.linkedin.databus.core.monitoring.mbean.StatsCollectorMergeable;
import com.linkedin.databus.core.monitoring.mbean.StatsCollectors;
import com.linkedin.databus.core.util.ConfigApplier;
import com.linkedin.databus.core.util.ConfigBuilder;
import com.linkedin.databus.core.util.ConfigManager;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus.core.util.JsonUtils;
import com.linkedin.databus.core.util.NamedThreadFactory;
import com.linkedin.databus2.core.DatabusException;
import com.linkedin.databus2.core.container.JmxStaticConfig;
import com.linkedin.databus2.core.container.JmxStaticConfigBuilder;
import com.linkedin.databus2.core.container.monitoring.mbean.ContainerStatisticsCollector;
import com.linkedin.databus2.core.container.monitoring.mbean.DatabusComponentAdmin;
import com.linkedin.databus2.core.container.request.CommandsRegistry;
import com.linkedin.databus2.core.container.request.ContainerAdminRequestProcessor;
import com.linkedin.databus2.core.container.request.ContainerStatsRequestProcessor;
import com.linkedin.databus2.core.container.request.JavaStatsRequestProcessor;
import com.linkedin.databus2.core.container.request.RequestProcessorRegistry;

/**
* A serving container
*/
public abstract class ServerContainer
{
  public static final String MODULE = ServerContainer.class.getName();
  public static final Logger LOG = Logger.getLogger(MODULE);
  public static final int GLOBAL_STATS_MERGE_INTERVAL_MS = 10000;

  private static final int SHUTDOWN_TIMEOUT_MS = 30000;
  private Timer _networkTimeoutTimer = null;

  protected final StaticConfig _containerStaticConfig;
  private final MBeanServer _mbeanServer;
  private final ThreadPoolExecutor _defaultExecutorService;
  private final ExecutorService _ioExecutorService;
  private final ExecutorService _bossExecutorService;
  //FIXME Deadlock problems with the TrackingExecutorService
  /*private final CallTracker _defaultExecutorServiceTracker;
  private final EnabledTrackingExecutorServiceState _enabledTrackingExecutorServiceState;
  private final DisabledTrackingExecutorServiceState _disabledTrackingExecutorServiceState;
  private final TrackingExecutorService _defaultTrackingExecutorService;*/
  private final ExecutionHandler _nettyExecHandler;
  private final ConfigManager<RuntimeConfig> _containerRuntimeConfigMgr;
  private final ContainerStatisticsCollector _containerStatsCollector;
  private final ReentrantLock _controlLock = new ReentrantLock(true);
  private final Condition _shutdownCondition = _controlLock.newCondition();
  private final Condition _shutdownFinishedCondition = _controlLock.newCondition();
  protected final StatsCollectors<DbusEventsStatisticsCollector> _inBoundStatsCollectors;
  protected final StatsCollectors<DbusEventsStatisticsCollector> _outBoundStatsCollectors;
  private final DatabusComponentAdmin _componentAdmin;
  private final DatabusComponentStatus _componentStatus;
  protected final GlobalStatsCalc _globalStatsMerger;

  private final Thread _globalStatsThread;

  /**
   * Eventually the processor registry will be migrated to the commands registry. Until all
   * commands have been migrated, we have to keep it. */
  protected final RequestProcessorRegistry _processorRegistry;
  protected final CommandsRegistry _commandsRegistry;
  private JMXConnectorServer _jmxConnServer;
  private JmxShutdownThread _jmxShutdownThread;
  private NettyShutdownThread _nettyShutdownThread;
  private Thread _containerShutdownHook;
  private volatile boolean _shutdown = false;
  private boolean _shutdownRequest = false;
  private boolean _started = false;
  private String _baseDir = "."; //if deployed with glu - usually /.../i001/ directory

  //The byte order used for the Dbusevent serialization.
  private final ByteOrder _dbusEventByteOrder;

  protected ServerBootstrap _httpBootstrap;
  protected ServerBootstrap _tcpBootstrap;

  protected Channel _httpServerChannel;
  protected Channel _tcpServerChannel;
  protected int _containerPort = -1;

  /** Helper field until we migrate the callers of the static CLI functions to the Cli class */
  private static Cli _staticCliToBeDeprecated = new Cli();

  /**
   * Channel group for the server channel and all per-connection channels. Used to close all of
   * them on shutdown
   **/
  protected ChannelGroup _tcpChannelGroup;
  protected ChannelGroup _httpChannelGroup;
  public static final String JMX_DOMAIN = "com.linkedin.databus2";

  @Deprecated
  public static Properties processCommandLineArgs(String[] cliArgs)
         throws IOException, DatabusException
  {
    _staticCliToBeDeprecated.processCommandLineArgs(cliArgs);
    return _staticCliToBeDeprecated.getConfigProps();
  }

  public RequestProcessorRegistry getProcessorRegistry()
  {
    return _processorRegistry;
  }

  public ByteOrder getDbusEventByteOrder()
  {
    return _dbusEventByteOrder;
  }

  public ServerContainer(StaticConfig config, ByteOrder byteOrder)
  throws IOException, InvalidConfigException, DatabusException
  {
   _containerStaticConfig = config;
   _baseDir = config.getContainerBaseDir();
   //by default we have 5ms timeout precision
   _networkTimeoutTimer = new HashedWheelTimer(5, TimeUnit.MILLISECONDS);

   _processorRegistry = new RequestProcessorRegistry();
   _commandsRegistry = new CommandsRegistry();

   _mbeanServer = _containerStaticConfig.getOrCreateMBeanServer();
   _containerStaticConfig.getRuntime().setManagedInstance(this);

    _dbusEventByteOrder = byteOrder;

   //TODO (DDSDBUS-105) HIGH we have to use the builder here instead of a read-only copy because we have a
   //bootstrapping circular reference RuntimeConfig -> ContainerStatisticsCollector.RuntimeConfig
   // -> ContainerStatisticsCollector -> RuntimeConfig
   RuntimeConfigBuilder runtimeConfig = _containerStaticConfig.getRuntime();

   //ExecutorConfigBuilder ioThreadsConfig = runtimeConfig.getIoExecutor();
   /* commented out because of deadlock problems
    * _ioExecutorService = new ThreadPoolExecutor(
       ioThreadsConfig.getCoreThreadsNum(),
       ioThreadsConfig.getMaxThreadsNum(),
       ioThreadsConfig.getKeepAliveMs(),
       TimeUnit.MILLISECONDS,
       ioThreadsConfig.getMaxQueueSize() <= 0 ?
           new LinkedBlockingQueue<Runnable>() :
           new ArrayBlockingQueue<Runnable>(ioThreadsConfig.getMaxQueueSize()));*/
   _ioExecutorService = Executors.newCachedThreadPool(new NamedThreadFactory("io" + _containerStaticConfig.getId()));
   _bossExecutorService = Executors.newCachedThreadPool(new NamedThreadFactory("boss" + _containerStaticConfig.getId()));

    _defaultExecutorService =
      new OrderedMemoryAwareThreadPoolExecutor(runtimeConfig.getDefaultExecutor().getMaxThreadsNum(),
                             0,
                             0,
                             runtimeConfig.getDefaultExecutor().getKeepAliveMs(),
                             TimeUnit.MILLISECONDS,
                             new NamedThreadFactory("worker" + _containerStaticConfig.getId()));

   _containerStatsCollector = _containerStaticConfig.getOrCreateContainerStatsCollector();
    DbusEventsStatisticsCollector inboundEventStatisticsCollector = new AggregatedDbusEventsStatisticsCollector(getContainerStaticConfig().getId(),
                                                        "eventsInbound",
                                                        true,
                                                        true,
                                                        getMbeanServer());

    DbusEventsStatisticsCollector outboundEventStatisticsCollector = new AggregatedDbusEventsStatisticsCollector(getContainerStaticConfig().getId(),
                                                        "eventsOutbound",
                                                        true,
                                                        true,
                                                        getMbeanServer());


    _inBoundStatsCollectors = new StatsCollectors<DbusEventsStatisticsCollector>(inboundEventStatisticsCollector);
    _outBoundStatsCollectors = new StatsCollectors<DbusEventsStatisticsCollector>(outboundEventStatisticsCollector);

   _containerRuntimeConfigMgr = new ConfigManager<RuntimeConfig>(
       _containerStaticConfig.getRuntimeConfigPropertyPrefix(), _containerStaticConfig.getRuntime());

    //FIXME MED using _defaultTrackingExecutorService for _nettyExecHandler seems to hang
    /* _defaultExecutorServiceTracker = new CallTrackerImpl(new CallTrackerImpl.Config());
    _enabledTrackingExecutorServiceState =
          new EnabledTrackingExecutorServiceState("enabledTrackingExecutorServiceState",
                                                  _defaultExecutorService,
                                                  _defaultExecutorServiceTracker,
                                                  false);
    _disabledTrackingExecutorServiceState =
          new DisabledTrackingExecutorServiceState(_defaultExecutorService, new SystemClock());

    _defaultTrackingExecutorService =
      new TrackingExecutorService(_enabledTrackingExecutorServiceState,
                                  _disabledTrackingExecutorServiceState,
                                  runtimeConfig.getDefaultExecutor().isTrackerEnabled());*/
    _nettyExecHandler = new ExecutionHandler(_defaultExecutorService);

    _componentStatus = createComponentStatus();
    _componentAdmin = createComponentAdmin();
    _componentAdmin.registerAsMBean();

    _globalStatsMerger = new GlobalStatsCalc(GLOBAL_STATS_MERGE_INTERVAL_MS);
    _globalStatsThread = new Thread(_globalStatsMerger, "GlobalStatsThread");
    _globalStatsThread.setDaemon(true);

    initializeContainerNetworking(byteOrder);
    initializeContainerJmx();
    initializeContainerCommandProcessors();
    initializeStatsMerger();
  }

  private void initializeStatsMerger()
  {
    _globalStatsMerger.registerStatsCollector(_inBoundStatsCollectors);
    _globalStatsMerger.registerStatsCollector(_outBoundStatsCollectors);
  }

  protected void initializeContainerCommandProcessors() throws DatabusException
  {
    _processorRegistry.register(ContainerStatsRequestProcessor.COMMAND_NAME,
                                new ContainerStatsRequestProcessor(null, this));
    _processorRegistry.register(JavaStatsRequestProcessor.COMMAND_NAME,
                                new JavaStatsRequestProcessor(null));
    String healthcheckPrefix = ContainerAdminRequestProcessor.extractCommandRoot(
        _containerStaticConfig.getHealthcheckPath());
    LOG.info("healthcheck command root: " + healthcheckPrefix);
    _processorRegistry.register(healthcheckPrefix,
        new ContainerAdminRequestProcessor(null, _componentStatus,
                                           _containerStaticConfig.getHealthcheckPath()));
  }

  protected abstract DatabusComponentAdmin createComponentAdmin();

  protected DatabusComponentStatus createComponentStatus()
  {
    return new DatabusComponentStatus("Relay",
                                      DatabusComponentStatus.Status.INITIALIZING,
                                      DatabusComponentStatus.INITIALIZING_MESSAGE);
  }

  protected void initializeContainerNetworking(ByteOrder byteOrder) throws IOException, DatabusException
  {
    //instruct netty not to rename our threads in the I/O and boss thread pools
    ThreadRenamingRunnable.setThreadNameDeterminer(ThreadNameDeterminer.CURRENT);

    _httpBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(_bossExecutorService,
                                                                           _ioExecutorService));
    _httpBootstrap.setPipelineFactory(new HttpServerPipelineFactory(this));
    _httpBootstrap.setOption("bufferFactory", DirectChannelBufferFactory.getInstance(byteOrder));
    _httpBootstrap.setOption("child.bufferFactory", DirectChannelBufferFactory.getInstance(byteOrder));

    if (_containerStaticConfig.getTcp().isEnabled())
    {
      _tcpBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(_bossExecutorService,
                                                                            _ioExecutorService));
      _tcpBootstrap.setPipelineFactory(new TcpServerPipelineFactory(this, byteOrder));
      _tcpBootstrap.setOption("bufferFactory", DirectChannelBufferFactory.getInstance(byteOrder));
      _tcpBootstrap.setOption("child.bufferFactory", DirectChannelBufferFactory.getInstance(byteOrder));

      //LOG.debug("endianness:" + ((ChannelBufferFactory)_tcpBootstrap.getOption("bufferFactory")).getDefaultOrder());
    }
  }

  protected void initializeContainerJmx()
  {

    if (_containerStaticConfig.getJmx().isRmiEnabled())
    {
      try
      {
        JMXServiceURL jmxServiceUrl =
            new JMXServiceURL("service:jmx:rmi://" +
                              _containerStaticConfig.getJmx().getJmxServiceHost() + ":" +
                              _containerStaticConfig.getJmx().getJmxServicePort() +"/jndi/rmi://" +
                              _containerStaticConfig.getJmx().getRmiRegistryHost() + ":" +
                              _containerStaticConfig.getJmx().getRmiRegistryPort() + "/jmxrmi" +
                              _containerStaticConfig.getJmx().getJmxServicePort());

        _jmxConnServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceUrl, null,
                                                                         getMbeanServer());
      }
      catch (Exception e)
      {
        LOG.warn("Unable to instantiate JMX server", e);
      }
    }
  }

  /** Starts the container synchronously and waits for its shutdown*/
  public void startAndBlock()
  {
    start();
    awaitShutdown();
  }

  /** Starts the container */
  synchronized public void start()
  {
  if ( ! _started )
  {
    doStart();
    _componentStatus.start();
    _started = true;
    LOG.info("Databus service started!");
  } else {
    LOG.info("Databus service has already been started. Skipping this request !!");
  }
  }

  /** Please use start */
  public void startAsynchronously()
  {
    Thread runThread = new Thread(new Runnable()
    {
      @Override
      public void run()
      {
        startAndBlock();
      }
    },"ServerContainerStartAsync");
    runThread.start();
  }

  protected void doStart()
  {
    _controlLock.lock();
    try
    {
      // Bind and start to accept incoming connections.
      int portNum = getContainerStaticConfig().getHttpPort();
      _tcpChannelGroup = new DefaultChannelGroup();
      _httpChannelGroup = new DefaultChannelGroup();

      _httpServerChannel = _httpBootstrap.bind(new InetSocketAddress(portNum));
      InetSocketAddress actualAddress = (InetSocketAddress)_httpServerChannel.getLocalAddress();
      _containerPort = actualAddress.getPort();

      // persist the port number (file name should be unique for the container)
      File portNumFile = new File(getHttpPortFileName());
      portNumFile.deleteOnExit();
      try {
        FileWriter portNumFileW = new FileWriter(portNumFile);
        portNumFileW.write(Integer.toString(_containerPort));
        portNumFileW.close();
        LOG.info("Saving port number in " + portNumFile.getAbsolutePath());
      } catch (IOException e) {
        throw new RuntimeException(e);
      }

      _httpChannelGroup.add(_httpServerChannel);
      LOG.info("Serving container " + getContainerStaticConfig().getId() +
               " HTTP listener on port " + _containerPort);

      if (_containerStaticConfig.getTcp().isEnabled())
      {
        int tcpPortNum = _containerStaticConfig.getTcp().getPort();
        _tcpServerChannel = _tcpBootstrap.bind(new InetSocketAddress(tcpPortNum));
        _tcpChannelGroup.add(_tcpServerChannel);

        LOG.info("Serving container " + getContainerStaticConfig().getId() +
                 " TCP listener on port " + tcpPortNum);
      }

      _nettyShutdownThread = new NettyShutdownThread();
      Runtime.getRuntime().addShutdownHook(_nettyShutdownThread);

      // Start the producer thread after 5 seconds
      if (null != _jmxConnServer && _containerStaticConfig.getJmx().isRmiEnabled())
      {
        try
        {
          _jmxShutdownThread = new JmxShutdownThread(_jmxConnServer);
          Runtime.getRuntime().addShutdownHook(_jmxShutdownThread);

          _jmxConnServer.start();
          LOG.info("JMX server listening on port " + _containerStaticConfig.getJmx().getJmxServicePort());
        }
        catch (IOException ioe)
        {
          if (ioe.getCause() != null && ioe.getCause() instanceof NameAlreadyBoundException)
          {
            LOG.warn("Unable to bind JMX server connector. Likely cause is that the previous instance was not cleanly shutdown: killed in Eclipse?");
            if (_jmxConnServer.isActive())
            {
              LOG.warn("JMX server connector seems to be running anyway. ");
            }
            else
            {
              LOG.warn("Unable to determine if JMX server connector is running");
            }
          }
          else
          {
            LOG.error("Unable to start JMX server connector", ioe);
          }
        }
      }

      _globalStatsThread.start();
    }
    catch (RuntimeException ex)
    {
      LOG.error("Got runtime exception :" + ex, ex);
      throw ex;
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  public void awaitShutdown()
  {
    _controlLock.lock();
    try
    {
      while (! _shutdownRequest)
      {
        LOG.info("waiting for shutdown request for container id: " + _containerStaticConfig.getId());
        _shutdownCondition.awaitUninterruptibly();
      }
    }
    finally
    {
      _controlLock.unlock();
    }

    _controlLock.lock();
    try
    {
      while (!_shutdown)
      {
        LOG.info("Waiting for shutdown complete for serving container: " + _containerStaticConfig.getId());
        _shutdownFinishedCondition.awaitUninterruptibly();
      }
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  public void awaitShutdown(long timeoutMs) throws TimeoutException, InterruptedException
  {
    long startTs = System.currentTimeMillis();
    long endTs = startTs + timeoutMs;
    _controlLock.lock();
    try
    {
      long waitTime;
      while (! _shutdownRequest && (waitTime = endTs - System.currentTimeMillis()) > 0)
      {
        LOG.info("waiting for shutdown request for container id: " + _containerStaticConfig.getId());
        if (!_shutdownCondition.await(waitTime, TimeUnit.MILLISECONDS)) break;
      }
    }
    finally
    {
      _controlLock.unlock();
    }

    if (!_shutdownRequest)
    {
      LOG.error("timeout waiting for a shutdown request");
      throw new TimeoutException("timeout waiting for shutdown request");
    }

    _controlLock.lock();
    try
    {
      long waitTime;
      while (!_shutdown && (waitTime = endTs - System.currentTimeMillis()) > 0)
      {
        LOG.info("Waiting for shutdown complete for serving container: " + _containerStaticConfig.getId());
        if (!_shutdownFinishedCondition.await(waitTime, TimeUnit.MILLISECONDS)) break;
      }
    }
    finally
    {
      _controlLock.unlock();
    }

    if (!_shutdown)
    {
      LOG.error("timeout waiting for shutdown");
      throw new TimeoutException("timeout waiting for shutdown to complete");
    }
  }

  protected void doShutdown()
  {
    unregisterShutdownHook();

    LOG.info("Initializing shutdown for serving container: " + _containerStaticConfig.getId());
    if (null != _jmxShutdownThread && Thread.State.NEW == _jmxShutdownThread.getState())
    {
      try
      {
        Runtime.getRuntime().removeShutdownHook(_jmxShutdownThread);
        _jmxShutdownThread.start();
      }
      catch (IllegalStateException ise)
      {
        LOG.error("Error removing shutdown hook", ise);
      }
    }
    if (null != _nettyShutdownThread && Thread.State.NEW == _nettyShutdownThread.getState())
    {
      try
      {
        Runtime.getRuntime().removeShutdownHook(_nettyShutdownThread);
        _nettyShutdownThread.start();
      }
      catch (IllegalStateException ise)
      {
        LOG.error("Error removing shutdown hook", ise);
      }
    }

    if (_globalStatsMerger != null && !_globalStatsMerger.isHalted())
    {
      _globalStatsMerger.halt();
      _globalStatsThread.interrupt();
    }

    // unregister all mbeans
    getContainerStatsCollector().unregisterMBeans();
    getInboundEventStatisticsCollector().unregisterMBeans();
    getOutboundEventStatisticsCollector().unregisterMBeans();
    for (DbusEventsStatisticsCollector coll: _inBoundStatsCollectors.getStatsCollectors())
    {
      coll.unregisterMBeans();
    }
    for (DbusEventsStatisticsCollector coll: _outBoundStatsCollectors.getStatsCollectors())
    {
      coll.unregisterMBeans();
    }

    _componentAdmin.unregisterAsMBeans();

    LOG.info("joining shutdown threads");

    long startTime = System.currentTimeMillis();
    long timeRemaining = SHUTDOWN_TIMEOUT_MS;

    while (null != _jmxShutdownThread && _jmxShutdownThread.isAlive() && timeRemaining > 0)
    {
      try
      {
        _jmxShutdownThread.join(timeRemaining);
      }
      catch (InterruptedException ie) {}
      timeRemaining = SHUTDOWN_TIMEOUT_MS - (System.currentTimeMillis() - startTime);
    }
    LOG.info("JMX shutdown for container " + _containerStaticConfig.getId() + ":" +
             (null != _jmxShutdownThread ? !_jmxShutdownThread.isAlive() : true) +
             "; ms remaining: " + timeRemaining);

    while (null != _nettyShutdownThread && _nettyShutdownThread.isAlive() && timeRemaining > 0)
    {
      try
      {
        _nettyShutdownThread.join(timeRemaining);
      }
      catch (InterruptedException ie) {}
      timeRemaining = SHUTDOWN_TIMEOUT_MS - (System.currentTimeMillis() - startTime);
    }
    LOG.info("Netty shutdown for container " + _containerStaticConfig.getId() + ":" +
             (null != _nettyShutdownThread ? !_nettyShutdownThread.isAlive() : true) +
             "; ms remaining: " + timeRemaining);

    LOG.info("Done with shutdown for serving container: " + _containerStaticConfig.getId());
  }

  public void shutdown()
  {
    shutdownAsynchronously();
    try
    {
      awaitShutdown(SHUTDOWN_TIMEOUT_MS);
    }
    catch (TimeoutException e)
    {
      LOG.error("shutdown timed out");
    }
    catch (InterruptedException e)
    {
      LOG.warn("shutdown cancelled because of interruption");
    }
  }

  public void shutdownUninteruptibly()
  {
    shutdownAsynchronously();
    awaitShutdown();
  }

  public void shutdownAsynchronously()
  {
    LOG.info("Initiating asynchronous shutdown for serving container: " + _containerStaticConfig.getId());
    _controlLock.lock();
    try
    {
      if (_shutdown) return;

      if (! _shutdownRequest)
      {
        _shutdownRequest = true;
        _shutdownCondition.signalAll();

        Thread shutdownThread = new Thread(new ShutdownRunnable(),
                                           "shutdown thread for container: " + _containerStaticConfig.getId());
        shutdownThread.setDaemon(true);
        shutdownThread.start();
      }
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  //This method shall only be called by AdminMBean impl class
  public DatabusComponentStatus.Status getStatus()
  {
    return _componentStatus.getStatus();
  }

  // This method shall only be called by AdminMBean impl class
  public String getStatusMessage()
  {
    return _componentStatus.getMessage();
  }

  public boolean isRunningStatus()
  {
    return _componentStatus.isRunningStatus();
  }

  public MBeanServer getMbeanServer()
  {
    return _mbeanServer;
  }

  public StaticConfig getContainerStaticConfig()
  {
    return _containerStaticConfig;
  }

  public ExecutionHandler getNettyExecHandler()
  {
    return _nettyExecHandler;
  }

  public ConfigManager<RuntimeConfig> getContainerRuntimeConfigMgr()
  {
    return _containerRuntimeConfigMgr;
  }

  public ThreadPoolExecutor getDefaultExecutorService()
  {
    return _defaultExecutorService;
  }

  public ContainerStatisticsCollector getContainerStatsCollector()
  {
    return _containerStatsCollector;
  }

  public GlobalStatsCalc getGlobalStatsMerger()
  {
    return _globalStatsMerger;
  }

  /**
   * Adds a hook that will cleanly shutdown the container if the JVM is
   * stopped. A NOOP if one has already been created. */
  public void registerShutdownHook()
  {
    _controlLock.lock();
    try
    {
      if (null != _containerShutdownHook) return;
      _containerShutdownHook = new Thread(new Runnable()
      {
        @Override
        public void run()
        {
          shutdown();
        }
      }, "shutdownHook-" + _containerStaticConfig.getId());
      Runtime.getRuntime().addShutdownHook(_containerShutdownHook);
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  /**
   * Removes the previously created shutdown hook. A NOOP if none has been
   * set up. */
  public synchronized void unregisterShutdownHook()
  {
    _controlLock.lock();
    try
    {
      if (null != _containerShutdownHook)
      {
        Runtime.getRuntime().removeShutdownHook(_containerShutdownHook);
        _containerShutdownHook = null;
      }
    }
    catch (IllegalStateException e)
    {
      //noop -- we are already shutting down
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  /** Command-line interface to the server container */
  public static class Cli
  {
    public static final String HELP_OPT_LONG_NAME = "help";
    public static final char HELP_OPT_CHAR = 'h';
    public static final String LOG4J_PROPS_OPT_LONG_NAME = "log_props";
    public static final char LOG4J_PROPS_OPT_CHAR = 'l';
    public static final String CONTAINER_PROPS_OPT_LONG_NAME = "container_props";
    public static final char CONTAINER_PROPS_OPT_CHAR = 'p';
    public static final String CMD_LINE_PROPS_OPT_LONG_NAME = "cmdline_props";
    public static final char CMD_LINE_PROPS_OPT_CHAR = 'c';
    public static final String DEBUG_OPT_LONG_NAME = "debug";
    public static final char DEBUG_OPT_CHAR = 'd';

    private final String _usage;
    protected Options _cliOptions;
    protected CommandLine _cmd;
    protected Properties _configProps;
    HelpFormatter _helpFormatter;
    protected Level _defaultLogLevel = Level.INFO;

    public Cli()
    {
      this("java <class> [options]");
    }

    public Cli(String usage)
    {
      _usage = usage;
      _cliOptions = new Options();
      _helpFormatter = new HelpFormatter();
      _helpFormatter.setWidth(150);
    }

    public void printCliHelp()
    {
      _helpFormatter.printHelp(getUsage(), _cliOptions);
    }

    public String getUsage()
    {
      return _usage;
    }

    public Options getCliOptions()
    {
      return _cliOptions;
    }

    public CommandLine getCmdLine()
    {
      return _cmd;
    }

    public void processCommandLineArgs(String[] cliArgs) throws IOException, DatabusException
    {
      constructCommandLineOptions();

      CommandLineParser cliParser = new GnuParser();

      _cmd = null;
      try
      {
        _cmd = cliParser.parse(_cliOptions, cliArgs);
      }
      catch (ParseException pe)
      {
        System.err.println("HttpServer: failed to parse command-line options: " + pe.toString());
        printCliHelp();
        System.exit(1);
      }

      if (_cmd.hasOption(HELP_OPT_CHAR))
      {
        printCliHelp();
        System.exit(0);
      }

      if (_cmd.hasOption(DEBUG_OPT_CHAR))
      {
        Logger.getRootLogger().setLevel(Level.DEBUG);
      }
      else
      {
        Logger.getRootLogger().setLevel(_defaultLogLevel);
      }

      if (_cmd.hasOption(LOG4J_PROPS_OPT_CHAR))
      {
        String log4jPropFile = _cmd.getOptionValue(LOG4J_PROPS_OPT_CHAR);
        PropertyConfigurator.configure(log4jPropFile);
        LOG.info("Using custom logging settings from file " + log4jPropFile);
      }
      else
      {
        PatternLayout defaultLayout = new PatternLayout("%d{ISO8601} +%r [%t] (%p) {%c{1}} %m%n");
        ConsoleAppender defaultAppender = new ConsoleAppender(defaultLayout);

        Logger.getRootLogger().removeAllAppenders();
        Logger.getRootLogger().addAppender(defaultAppender);

        LOG.info("Using default logging settings");
      }

      _configProps = new Properties(System.getProperties());
      if (_cmd.hasOption(CONTAINER_PROPS_OPT_CHAR))
      {
        for (String propFile: _cmd.getOptionValues(CONTAINER_PROPS_OPT_CHAR))
        {
          LOG.info("Loading container config from properties file " + propFile);
          FileInputStream fis = new FileInputStream(propFile);
          try
          {
            _configProps.load(fis);
          }
          catch (Exception e)
          {
            LOG.error("error processing properties; ignoring:" + e.getMessage(), e);
          }
          finally
          {
            fis.close();
          }
        }
      }
      else
      {
        LOG.info("Using system properties for container config");
      }

      if (_cmd.hasOption(CMD_LINE_PROPS_OPT_CHAR))
      {
        String cmdLinePropString = _cmd.getOptionValue(CMD_LINE_PROPS_OPT_CHAR);
        updatePropsFromCmdLine(cmdLinePropString);
      }
      if (Logger.getRootLogger().isTraceEnabled())
      {
        //Print out Netty Logging only if we want a very detailed log
        InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory());
      }
    }

    private void updatePropsFromCmdLine(String cmdLinePropString)
    {
      String[] cmdLinePropSplit = cmdLinePropString.split(";");
      for(String s : cmdLinePropSplit)
      {
        String[] onePropSplit = s.split("=");
        if (onePropSplit.length != 2)
        {
          LOG.error("CMD line property setting " + s + "is not valid!");
        }
        else
        {
          LOG.info("CMD line Property overwride: " + s);
          _configProps.put(onePropSplit[0], onePropSplit[1]);
        }
      }
    }

    @SuppressWarnings("static-access")
    protected void constructCommandLineOptions()
    {
      Option helpOption = OptionBuilder.withLongOpt(HELP_OPT_LONG_NAME)
                                       .withDescription("Prints command-line options info")
                                       .create(HELP_OPT_CHAR);
      Option log4jPropsOption = OptionBuilder.withLongOpt(LOG4J_PROPS_OPT_LONG_NAME)
                                             .withDescription("Log4j properties to use")
                                             .hasArg()
                                             .withArgName("property_file")
                                             .create(LOG4J_PROPS_OPT_CHAR);
      Option debugPropsOption = OptionBuilder.withLongOpt(DEBUG_OPT_LONG_NAME)
                                             .withDescription("Turns on debugging info")
                                             .create(DEBUG_OPT_CHAR);
      Option containerPropsOption = OptionBuilder.withLongOpt(CONTAINER_PROPS_OPT_LONG_NAME)
                                                 .withDescription("Container config properties to use")
                                                 .hasArg()
                                                 .withArgName("property_file")
                                                 .create(CONTAINER_PROPS_OPT_CHAR);
      Option cmdLinePropsOption = OptionBuilder.withLongOpt(CMD_LINE_PROPS_OPT_LONG_NAME)
                                                 .withDescription("Cmd line override of config properties. Semicolon separated.")
                                                 .hasArg()
                                                 .withArgName("Semicolon_separated_properties")
                                                 .create(CMD_LINE_PROPS_OPT_CHAR);


      _cliOptions.addOption(helpOption);
      _cliOptions.addOption(log4jPropsOption);
      _cliOptions.addOption(debugPropsOption);
      _cliOptions.addOption(containerPropsOption);
      _cliOptions.addOption(cmdLinePropsOption);
    }

    public Properties getConfigProps()
    {
      return _configProps;
    }

    public Level getDefaultLogLevel()
    {
      return _defaultLogLevel;
    }

    public void setDefaultLogLevel(Level defaultLogLevel)
    {
      _defaultLogLevel = defaultLogLevel;
    }

  }

  /** Static configuration for the TCP command interface */
  public static class TcpStaticConfig
  {
    private final boolean _enabled;
    private final int _port;

    public TcpStaticConfig(boolean enabled, int port)
    {
      super();
      _enabled = enabled;
      _port = port;
    }

    /** A flag if the TCP command interface is to be enabled*/
    public boolean isEnabled()
    {
      return _enabled;
    }

    /** The port for the TCP command interface */
    public int getPort()
    {
      return _port;
    }

    @Override
    public String toString()
    {
      return toJsonString(false);
    }

    public String toJsonString(boolean pretty)
    {
      return JsonUtils.toJsonStringSilent(this, pretty);
    }
  }

  /** */
  public static class TcpStaticConfigBuilder implements ConfigBuilder<TcpStaticConfig>
  {
    private boolean _enabled;
    private int _port;

    public TcpStaticConfigBuilder()
    {
      _enabled = false;
      _port = 8180;
    }

    public boolean isEnabled()
    {
      return _enabled;
    }

    public int getPort()
    {
      return _port;
    }

    public void setEnabled(boolean enabled)
    {
      _enabled = enabled;
    }

    public void setPort(int port)
    {
      _port = port;
    }

    @Override
    public TcpStaticConfig build() throws InvalidConfigException
    {
      if (_enabled && 0 >= _port) throw new InvalidConfigException("invalid TCP port: " + _port);
      TcpStaticConfig newConfig = new TcpStaticConfig(_enabled, _port);
      LOG.info("TCP command interface configuration:" + newConfig);
      return newConfig;
    }
  }

  public static class ExecutorConfig implements ConfigApplier<ExecutorConfig>
  {
    private final int _maxThreadsNum;
    private final int _coreThreadsNum;
    private final int _keepAliveMs;
    private final boolean _trackerEnabled;
    private final int _maxQueueSize;

    public ExecutorConfig(int maxThreadsNum,
                          int coreThreadsNum,
                          int keepAliveMs,
                          boolean trackerEnabled,
                          int maxQueueSize) throws InvalidConfigException
    {
      super();
      _maxThreadsNum = maxThreadsNum;
      _coreThreadsNum = coreThreadsNum;
      _keepAliveMs = keepAliveMs;
      _trackerEnabled = trackerEnabled;
      _maxQueueSize = maxQueueSize;
    }

    /** The maximum number of executor threads */
    public int getMaxThreadsNum()
    {
      return _maxThreadsNum;
    }

    /** The default number of executor threads */
    public int getCoreThreadsNum()
    {
      return _coreThreadsNum;
    }

    /** The time in milliseconds to keep an idle thread before killing it */
    public int getKeepAliveMs()
    {
      return _keepAliveMs;
    }

    /** Is the executor service stats tracking enabled. */
    public boolean isTrackerEnabled()
    {
      return _trackerEnabled;
    }

    @Override
    public void applyNewConfig(ExecutorConfig oldConfig)
    {
      // FIXME Add implementation
    }

    @Override
    public boolean equals(Object other)
    {
      if(other == null || !(other instanceof ExecutorConfig)) return false;
      if(this == other) return true;
      return equalsConfig((ExecutorConfig)other);
    }

    @Override
    public boolean equalsConfig(ExecutorConfig otherConfig)
    {
      if(otherConfig == null) return false;
      return getMaxThreadsNum() == otherConfig.getMaxThreadsNum() &&
      getCoreThreadsNum() == otherConfig.getCoreThreadsNum() &&
      getKeepAliveMs() == otherConfig.getKeepAliveMs() &&
      isTrackerEnabled() == otherConfig.isTrackerEnabled();
    }

    @Override
    public int hashCode()
    {
      return _maxThreadsNum ^ _coreThreadsNum ^ _keepAliveMs ^ (_trackerEnabled ? 0xFFFFFFFF : 0) ;
    }

    /** The max number of queued tasks; <= 0 means no limit*/
    public int getMaxQueueSize()
    {
      return _maxQueueSize;
    }
  }

  public static class ExecutorConfigBuilder implements ConfigBuilder<ExecutorConfig>
  {
    public static final Integer DEFAULT_MAX_THREADS_NUM = 100;
    public static final Integer DEFAULT_CORE_THREADS_NUM = 50;
    public static final Integer DEFAULT_KEEPALIVE_MS = 60000;
    public static final Boolean DEFAULT_TRACKER_ENABLED = true;
    public static final Integer DEFAULT_MAX_QUEUE_SIZE = 1000;

    private int _maxThreadsNum               = DEFAULT_MAX_THREADS_NUM;
    private int _coreThreadsNum              = DEFAULT_CORE_THREADS_NUM;
    private int _keepAliveMs                 = DEFAULT_KEEPALIVE_MS;
    private boolean _trackerEnabled          = DEFAULT_TRACKER_ENABLED;
    private int _maxQueueSize                = DEFAULT_MAX_QUEUE_SIZE;
    private ServerContainer _managedInstance = null;

    public ExecutorConfigBuilder()
    {
    }

    public int getMaxThreadsNum()
    {
      return _maxThreadsNum;
    }

    public void setMaxThreadsNum(int maxThreadsNum)
    {
      _maxThreadsNum = maxThreadsNum;
    }

    public int getCoreThreadsNum()
    {
      return _coreThreadsNum;
    }

    public void setCoreThreadsNum(int coreThreadsNum)
    {
      _coreThreadsNum = coreThreadsNum;
    }

    public int getKeepAliveMs()
    {
      return _keepAliveMs;
    }

    public void setKeepAliveMs(int keepAliveMs)
    {
      _keepAliveMs = keepAliveMs;
    }

    public boolean isTrackerEnabled()
    {
      return _trackerEnabled;
    }

    public void setTrackerEnabled(boolean trackerEnabled)
    {
      _trackerEnabled = trackerEnabled;
    }

    @Override
    public ExecutorConfig build() throws InvalidConfigException
    {
      if (null == _managedInstance) throw new InvalidConfigException("Missing server container");
      return new ExecutorConfig(_maxThreadsNum, _coreThreadsNum,
                                                 _keepAliveMs, _trackerEnabled,
                                                 _maxQueueSize);
    }

    ServerContainer getManagedInstance()
    {
      return _managedInstance;
    }

    void setManagedInstance(ServerContainer managedInstance)
    {
      _managedInstance = managedInstance;
    }

    public int getMaxQueueSize()
    {
      return _maxQueueSize;
    }

    public void setMaxQueueSize(int maxQueueSize)
    {
      _maxQueueSize = maxQueueSize;
    }

  }

  public static class RuntimeConfig implements ConfigApplier<RuntimeConfig>
  {
    private final int _requestProcessingBudgetMs;
    private final ExecutorConfig _defaultExecutorConfig;
    private final ExecutorConfig _ioExecutorConfig;

    public RuntimeConfig(int requestProcessingBudgetMs,
                         ExecutorConfig defaultExecutorConfig,
                         ExecutorConfig ioExecutorConfig
                        )
                         throws InvalidConfigException
    {
      super();
      _requestProcessingBudgetMs = requestProcessingBudgetMs;
      _defaultExecutorConfig = defaultExecutorConfig;
      _ioExecutorConfig = ioExecutorConfig;
    }

    /** Maximum time in millisecods to process a request before interrupting the request processing */
    public int getRequestProcessingBudgetMs()
    {
      return _requestProcessingBudgetMs;
    }

    /** Runtime configuration for the request executor */
    public ExecutorConfig getDefaultExecutor()
    {
      return _defaultExecutorConfig;
    }

    @Override
    public void applyNewConfig(RuntimeConfig oldConfig)
    {
      LOG.info("Changing container config");
      //FIXME add logic
      if (null == oldConfig || !getDefaultExecutor().equals(oldConfig.getDefaultExecutor()))
      {
        getDefaultExecutor().applyNewConfig(null != oldConfig ? oldConfig.getDefaultExecutor() : null);
      }

    }

    @Override
    public boolean equals(Object other)
    {
      if(other == null || !(other instanceof RuntimeConfig)) return false;
      if(this == other) return true;
      return equalsConfig((RuntimeConfig)other);
    }

    @Override
    public boolean equalsConfig(RuntimeConfig otherConfig)
    {
      if(otherConfig == null) return false;

      return (getRequestProcessingBudgetMs() == otherConfig.getRequestProcessingBudgetMs()) &&
             (getDefaultExecutor().equals(otherConfig.getDefaultExecutor()));

    }

    @Override
    public int hashCode()
    {
      return _requestProcessingBudgetMs ^ _defaultExecutorConfig.hashCode();

    }

    public ExecutorConfig getIoExecutorConfig()
    {
      return _ioExecutorConfig;
    }
  }

  public static class RuntimeConfigBuilder implements ConfigBuilder<RuntimeConfig>
  {
    private int _requestProcessingBudgetMs                = 100;
    private ExecutorConfigBuilder _defaultExecutor;
    private ExecutorConfigBuilder _ioExecutor;
    private ServerContainer _managedInstance              = null;

    public RuntimeConfigBuilder()
    {
      _defaultExecutor = new ExecutorConfigBuilder();
      _ioExecutor = new ExecutorConfigBuilder();
      _ioExecutor.setCoreThreadsNum(5);
      _ioExecutor.setMaxThreadsNum(2 * Runtime.getRuntime().availableProcessors());
    }

    public int getRequestProcessingBudgetMs()
    {
      return _requestProcessingBudgetMs;
    }

    public void setRequestProcessingBudgetMs(int requestProcessingBudgetMs)
    {
      _requestProcessingBudgetMs = requestProcessingBudgetMs;
    }

    public ExecutorConfigBuilder getDefaultExecutor()
    {
      return _defaultExecutor;
    }

    public void setDefaultExecutor(ExecutorConfigBuilder defaultExecutor)
    {
      _defaultExecutor = defaultExecutor;
    }

    @Override
    public RuntimeConfig build() throws InvalidConfigException
    {
      if (null != _managedInstance)
      {
        return new RuntimeConfig(_requestProcessingBudgetMs,
                                                  _defaultExecutor.build(),
                                                  _ioExecutor.build());
      }

      throw new InvalidConfigException("Server container instance not set");
    }


    public ServerContainer getManagedInstance()
    {
      return _managedInstance;
    }

    public void setManagedInstance(ServerContainer managedInstance)
    {
      if (null != managedInstance)
      {
        _managedInstance = managedInstance;
        _defaultExecutor.setManagedInstance(managedInstance);
        _ioExecutor.setManagedInstance(managedInstance);
      }
    }


    public ExecutorConfigBuilder getIoExecutor()
    {
      return _ioExecutor;
    }

    public void setIoExecutor(ExecutorConfigBuilder ioExecutor)
    {
      _ioExecutor = ioExecutor;
    }
  }

  public static class StaticConfig
  {
    private final int _id;
    private final JmxStaticConfig _jmxConfig;
    private final int _httpPort;
    private final MBeanServer _existingMbeanServer;
    private final String _runtimeConfigPropertyPrefix;
    private final RuntimeConfigBuilder _runtimeConfigBuilder;
    private final String _healthcheckPath;
    private final long _readTimeoutMs;
    private final long _bstReadTimeoutMs;
    private final long _writeTimeoutMs;
    private final TcpStaticConfig _tcp;
    private final boolean _enableHttpCompression;
    private final String _containerBaseDir;

    public StaticConfig(int id, JmxStaticConfig jmxConfig, int httpPort,
                        MBeanServer existingMbeanServer,
                        String runtimeConfigPropertyPrefix,
                        RuntimeConfigBuilder runtimeConfigBuilder,
                        String healthcheckPath,
                        long readTimeoutMs,
                        long bstReadTimeoutMs,
                        long writeTimeoutMs,
                        TcpStaticConfig tcp,
                        boolean enableHttpCompression,
                        String containerBaseDir)
    {
      super();
      _id = id;
      _jmxConfig = jmxConfig;
      _httpPort = httpPort;
      _existingMbeanServer = existingMbeanServer;
      _runtimeConfigPropertyPrefix = runtimeConfigPropertyPrefix;
      _runtimeConfigBuilder = runtimeConfigBuilder;
      _healthcheckPath = healthcheckPath;
      _readTimeoutMs = readTimeoutMs;
      _bstReadTimeoutMs = bstReadTimeoutMs;
      _writeTimeoutMs = writeTimeoutMs;
      _tcp = tcp;
      _enableHttpCompression = enableHttpCompression;
      _containerBaseDir = containerBaseDir;
    }
    /** container base directory */
    public String getContainerBaseDir() {
      return _containerBaseDir;
    }

    /** HTTP port to listen on */
    public int getHttpPort()
    {
      return _httpPort;
    }

    /** JMX static configuration */
    public JmxStaticConfig getJmx()
    {
      return _jmxConfig;
    }

    public MBeanServer getExistingMbeanServer()
    {
      return _existingMbeanServer;
    }

    public String getRuntimeConfigPropertyPrefix()
    {
      return _runtimeConfigPropertyPrefix;
    }

    /** Runtime  configuration */
    public RuntimeConfigBuilder getRuntime()
    {
      return _runtimeConfigBuilder;
    }

    /** Container ID */
    public int getId()
    {
      return _id;
    }


    public MBeanServer getOrCreateMBeanServer()
    {
      return (null != getExistingMbeanServer()) ? getExistingMbeanServer() :
                                                  ManagementFactory.getPlatformMBeanServer();
    }

    public ContainerStatisticsCollector getOrCreateContainerStatsCollector()
    {
      ContainerStatisticsCollector result =
          new ContainerStatisticsCollector(getRuntime().getManagedInstance(), "containerStats",
                                          true,
                                           true, getOrCreateMBeanServer());

      return result;
    }


    /** Path on which the healthcheck will respond (sans the initial /) */
    public String getHealthcheckPath()
    {
      return _healthcheckPath;
    }

    /** Timeout for reading parts of HTTP request */
    public long getReadTimeoutMs()
    {
      return _readTimeoutMs;
    }

    public long getBstReadTimeoutMs()
    {
      return _bstReadTimeoutMs;
    }

    /** Timeout for confirmation from the peer when sending a response */
    public long getWriteTimeoutMs()
    {
      return _writeTimeoutMs;
    }

    /** TCP command interface static configuration*/
    public TcpStaticConfig getTcp()
    {
      return _tcp;
    }

    /** Whether to enable HTTP compression (deflate) for outbound data*/
    public boolean getEnableHttpCompression()
    {
      return _enableHttpCompression;
    }

  @Override
  public String toString() {
    return "StaticConfig [_id=" + _id + ", _jmxConfig=" + _jmxConfig
        + ", _httpPort=" + _httpPort + ", _existingMbeanServer="
        + _existingMbeanServer + ", _runtimeConfigPropertyPrefix="
        + _runtimeConfigPropertyPrefix + ", _runtimeConfigBuilder="
        + _runtimeConfigBuilder + ", _statsCollector="
        + _healthcheckPath + ", _readTimeoutMs=" + _readTimeoutMs
        + ", _writeTimeoutMs=" + _writeTimeoutMs + ", _tcp=" + _tcp
        + ", _enableHttpCompression=" + _enableHttpCompression
        + "]";
  }
  }

  public static class Config implements ConfigBuilder<StaticConfig>
  {
    private int _id;
    private int _httpPort                       = 9000;
    private JmxStaticConfigBuilder _jmx;
    private MBeanServer _existingMbeanServer    = null;
    private String _runtimeConfigPropertyPrefix = "databus.container.runtime";
    private RuntimeConfigBuilder _runtime;
    private int _defaultId;
    private String _healthcheckPath             = "admin";
    private long _readTimeoutMs                 = 15000;
    private long _bstReadTimeoutMs              = 15000;
    private boolean _bstReadTimeoutSet = false;
    private long _writeTimeoutMs                = 15000;
    private final TcpStaticConfigBuilder _tcp;
    private boolean _enableHttpCompression      = false;
    private String _containerBaseDir = ".";

    public Config()
    {
      _jmx = new JmxStaticConfigBuilder();
      _runtime = new RuntimeConfigBuilder();
      _tcp = new TcpStaticConfigBuilder();
      _id = -1;
    }
    public String getContainerBaseDir() {
      return _containerBaseDir;
    }
    public void setContainerBaseDir(String dir) {
      _containerBaseDir = dir;
    }
    public int getHttpPort()
    {
      return _httpPort;
    }

    public void setHttpPort(int httpPort)
    {
      _httpPort = httpPort;
    }

    public JmxStaticConfigBuilder getJmx()
    {
      return _jmx;
    }

    public void setJmx(JmxStaticConfigBuilder jmx)
    {
      _jmx = jmx;
    }

    public MBeanServer getExistingMbeanServer()
    {
      return _existingMbeanServer;
    }

    public void setExistingMbeanServer(MBeanServer existingMbeanServer)
    {
      _existingMbeanServer = existingMbeanServer;
    }

    @Override
    public StaticConfig build() throws InvalidConfigException
    {
      _defaultId = -1;
      try
      {
        int hostHash = InetAddress.getLocalHost().hashCode();
        _defaultId = (hostHash == Integer.MIN_VALUE) ? Integer.MAX_VALUE : Math.abs(hostHash);
        _defaultId = _defaultId % 0x7FFF0000;
      }
      catch (UnknownHostException uhe)
      {
        LOG.error("Error getting localhost info", uhe);
      }

      if (_id == -1) {
        UUID uuid = UUID.randomUUID();
        _id =  Math.abs(_defaultId + uuid.hashCode());
        LOG.info("Using container id: " + _id +
                 "(from defaultId = " + _defaultId + ";hash=" + uuid.hashCode() + ")");
      }

      String realHealthcheckPath = _healthcheckPath.startsWith("/") ? _healthcheckPath.substring(1)
                                                                    : _healthcheckPath;

      LOG.info("Using http port:" + _httpPort);
      LOG.info("Using containerBasedir: " + _containerBaseDir);

      LOG.info("Using healthcheck path: " + realHealthcheckPath);
      return new StaticConfig(_id, _jmx.build(), _httpPort, _existingMbeanServer,
                              _runtimeConfigPropertyPrefix, _runtime,
                              realHealthcheckPath,
                              _readTimeoutMs,
                              _bstReadTimeoutSet ? _bstReadTimeoutMs : _readTimeoutMs,
                              _writeTimeoutMs,
                              _tcp.build(),
                              _enableHttpCompression,
                              _containerBaseDir);
    }


    public String getRuntimeConfigPropertyPrefix()
    {
      return _runtimeConfigPropertyPrefix;
    }

    public void setRuntimeConfigPropertyPrefix(String runtimeConfigPropertyPrefix)
    {
      _runtimeConfigPropertyPrefix = runtimeConfigPropertyPrefix;
    }

    public RuntimeConfigBuilder getRuntime()
    {
      return _runtime;
    }

    public void setRuntime(RuntimeConfigBuilder runtime)
    {
      _runtime = runtime;
    }

    public int getId()
    {
      return _id;
    }

    public void setId(int id)
    {
      _id = id;
    }

    public void setIdFromName(String name)
    {
      int id = name.hashCode();
      if(id < 0)
      {
        id = (id == Integer.MIN_VALUE) ? Integer.MAX_VALUE : Math.abs(id);
      }
      setId(id);
    }

    public String getHealthcheckPath()
    {
      return _healthcheckPath;
    }

    public void setHealthcheckPath(String healthcheckPath)
    {
      _healthcheckPath = healthcheckPath;
    }

    public long getBstReadTimeoutMs()
    {
      if (_bstReadTimeoutSet) {
        return _bstReadTimeoutMs;
      } else {
        return _readTimeoutMs;
      }
    }

    public long getReadTimeoutMs()
    {
      return _readTimeoutMs;
    }

    public void setReadTimeoutMs(long readTimeoutMs)
    {
      _readTimeoutMs = readTimeoutMs;
    }

    public void setBstReadTimeoutMs(long bstReadTimeoutMs)
    {
      _bstReadTimeoutMs = bstReadTimeoutMs;
      _bstReadTimeoutSet = true;
    }

    public long getWriteTimeoutMs()
    {
      return _writeTimeoutMs;
    }

    public void setWriteTimeoutMs(long writeTimeoutMs)
    {
      _writeTimeoutMs = writeTimeoutMs;
    }

    public TcpStaticConfigBuilder getTcp()
    {
      return _tcp;
    }

    public boolean getEnableHttpCompression()
    {
      return _enableHttpCompression;
    }

    public void setEnableHttpCompression(boolean enableHttpCompression)
    {
      _enableHttpCompression = enableHttpCompression;
    }
  }

  public StatsCollectors<DbusEventsStatisticsCollector> getInBoundStatsCollectors()
  {
    return _inBoundStatsCollectors;
  }

  public StatsCollectors<DbusEventsStatisticsCollector> getOutBoundStatsCollectors()
  {
    return _outBoundStatsCollectors;
  }

  public DbusEventsStatisticsCollector getInboundEventStatisticsCollector()
  {
    return _inBoundStatsCollectors.getStatsCollector();
  }

  public DbusEventsStatisticsCollector getOutboundEventStatisticsCollector()
  {
    return  _outBoundStatsCollectors.getStatsCollector();
  }

  public abstract void pause();

  public abstract void resume();

  public abstract void suspendOnError(Throwable cause);

  public DatabusComponentAdmin getComponentAdmin()
  {
    return _componentAdmin;
  }

  public DatabusComponentStatus getComponentStatus()
  {
    return _componentStatus;
  }

  public ExecutorService getIoExecutorService()
  {
    return _ioExecutorService;
  }

  protected ChannelGroup getTcpChannelGroup()
  {
    return _tcpChannelGroup;
  }

  protected ChannelGroup getHttpChannelGroup()
  {
    return _httpChannelGroup;
  }

  public Timer getNetworkTimeoutTimer()
  {
    return _networkTimeoutTimer;
  }

  /** The new registry for parsing and execution of commands */
  public CommandsRegistry getCommandsRegistry()
  {
    return _commandsRegistry;
  }

  public boolean hasClientStarted()
  {
    return _started;
  }

  /**
   * Override _started flag. Used in testing
   */
  protected void setStarted(boolean started)
  {
    _started = started;
  }

  /**
   * Utility class that can keep track of global stats across physical sources/buffers
   *
   */

  static public class GlobalStatsCalc implements Runnable
  {

    private boolean _stopRequested=false;
    private boolean _stopped=false;
    final private int _sleepInMs;
    final private List<StatsCollectors<? extends StatsCollectorMergeable<?>>> _stats;

    public GlobalStatsCalc(int sleepInMs)
    {
      _sleepInMs = sleepInMs;
      _stats = new ArrayList<StatsCollectors<? extends StatsCollectorMergeable<?>>>(5);
    }

    public synchronized void registerStatsCollector(StatsCollectors<?  extends StatsCollectorMergeable<?>> coll)
    {
      _stats.add(coll);
    }

        public synchronized void deregisterStatsCollector(StatsCollectors<?  extends StatsCollectorMergeable<?>> coll)
        {
          _stats.remove(coll);
        }

    public void halt()
    {
      _stopRequested=true;
    }

    public boolean isHalted()
    {
      return _stopped;
    }

    @Override
    public void run()
    {
      try
      {
        _stopped=false;
        while (!_stopRequested)
        {
          synchronized(this)
          {
                    try
                    {
              for( StatsCollectors<?  extends StatsCollectorMergeable<?>> stats : _stats)
              {
                stats.mergeStatsCollectors();
              }
                    } catch (RuntimeException r)
                    {
                      LOG.error("Error during merging of stats ", r);
                    }
          }
                  if (!_stopRequested)
                    Thread.sleep(_sleepInMs);
        }
      }
      catch (InterruptedException e)
      {

      }
      _stopped=true;
    }
    }

  public ExecutorService getBossExecutorService()
  {
    return _bossExecutorService;
  }

  class NettyShutdownThread extends Thread
  {

    public NettyShutdownThread()
    {
      super("Databus2 Netty Shutdown Thread");
    }

    @Override
    public void run()
    {
      LOG.info("Starting shutdown procedure for Netty ...");
      if (null != _httpServerChannel)
      {
        LOG.info("closing http server channel ...");
        _httpServerChannel.close().awaitUninterruptibly();
        _httpChannelGroup.remove(_httpServerChannel);
      }
      if (null != _tcpServerChannel)
      {
        LOG.info("closing tcp server channel ...");
        _tcpServerChannel.close().awaitUninterruptibly();
        _tcpChannelGroup.remove(_tcpServerChannel);
      }

      if (null != _tcpChannelGroup)
      {
        LOG.info("closing tcp channel group with " + _tcpChannelGroup.size() + " channels ...");
        _tcpChannelGroup.close().awaitUninterruptibly();
      }
      if (null != _httpChannelGroup)
      {
        LOG.info("closing http channel group with " + _httpChannelGroup.size() + " channels ...");
        _httpChannelGroup.close().awaitUninterruptibly();
      }
      if (null != _httpBootstrap)
      {
        LOG.info("releasing netty http resources ...");
        _httpBootstrap.releaseExternalResources();
      }
      if (null != _tcpBootstrap)
      {
        LOG.info("releasing netty tcp resources ...");
        _tcpBootstrap.releaseExternalResources();
      }

      LOG.info("stopping network timeout timer");
      _networkTimeoutTimer.stop();
      LOG.info("Done shutting down Netty.");
    }

  }

  private class ShutdownRunnable implements Runnable
  {

    @Override
    public void run()
    {
      _controlLock.lock();
      try
      {
        try
        {
          doShutdown();
        }
        catch (Exception e)
        {
          LOG.error("shutdown error", e);
        }
        _shutdown = true;
        _shutdownFinishedCondition.signalAll();
      }
      finally
      {
        _controlLock.unlock();
      }
    }

  }

  /**
   * http port used by the container
   * @return port num
   */
  public int getHttpPort() {
    return _containerPort;
  }
  /** base directory for the Container */
  public String getBaseDir () {
    return _baseDir;
  }
  public String getHttpPortFileName() {
    return new File(_baseDir, "containerPortNum_" + _containerStaticConfig.getId()).getAbsolutePath();
  }
}

class JmxShutdownThread extends Thread
{
  public static final String MODULE = JmxShutdownThread.class.getName();
  public static final Logger LOG = Logger.getLogger(MODULE);

  private final JMXConnectorServer _jmxConnServer;

  public JmxShutdownThread(JMXConnectorServer jmxConnServer)
  {
    super("Databus2 JMX Shutdown Thread");
    _jmxConnServer = jmxConnServer;
  }

  @Override
  public void run()
  {
    LOG.info("Starting shutdown procedure for JMX server ...");
    try
    {
      if (null != _jmxConnServer && _jmxConnServer.isActive())
      {
        _jmxConnServer.stop();
      }
      LOG.info("JMX server shutdown.");
    }
    catch (Exception e)
    {
      LOG.error("Error shutting down JMX server", e);
    }
  }
}
TOP

Related Classes of com.linkedin.databus2.core.container.netty.ServerContainer$Cli

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.