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); } } }