Package com.linkedin.databus2.tools.dtail

Source Code of com.linkedin.databus2.tools.dtail.Dtail

package com.linkedin.databus2.tools.dtail;
/*
*
* 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.IOException;
import java.util.Arrays;
import java.util.Properties;

import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.log4j.Logger;

import com.linkedin.databus.client.DatabusHttpClientImpl;
import com.linkedin.databus.client.DatabusHttpClientImpl.CheckpointPersistenceStaticConfig;
import com.linkedin.databus.client.pub.DatabusClientException;
import com.linkedin.databus.client.pub.DatabusCombinedConsumer;
import com.linkedin.databus.client.pub.ServerInfo.ServerInfoBuilder;
import com.linkedin.databus.client.pub.ServerInfo.ServerInfoSetBuilder;
import com.linkedin.databus.core.BaseCli;
import com.linkedin.databus.core.Checkpoint;
import com.linkedin.databus.core.DbusClientMode;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus2.core.DatabusException;
import com.linkedin.databus2.core.filter.DbusKeyCompositeFilterConfig;
import com.linkedin.databus2.core.filter.KeyFilterConfigHolder.PartitionType;

/**
* The implementation of a command line tool to consume and print events from one or more
* Databus sources.
*/
public class Dtail
{
  public static final Logger LOG = Logger.getLogger(Dtail.class);

  static class Cli extends DtailCliBase
  {
    public static final String BOOTSTRAP_OPT_NAME = "with-bootstrap";
    public static final char BOOTSTRAP_OPT_CHAR = 'b';
    public static final String BOOTSTRAP_SERVERS_OPT_NAME = "bstservers";
    public static final String CONFIG_ROOT_OPT_NAME = "config_root";
    public static final String FABRIC_OPT_NAME = "fabric";
    public static final char FABRIC_OPT_CHAR = 'f';
    public static final String MOD_PARTITION_OPT_NAME = "mod_part";
    public static final String RELAYS_OPT_NAME = "relays";
    public static final char RELAYS_OPT_CHAR = 'R';
    public static final String SOURCES_OPT_NAME = "sources";
    public static final char SOURCES_OPT_CHAR = 's';
    public static final String CONSUMER_CLASS_NAME = "consumer";

    protected String _sourcesString;
    protected String[] _sources;
    protected String _relaysOverride;
    protected String _bstserversOverride;
    protected File _configRoot = new File(".");
    protected long _modPartBase = -1;
    protected String _modPartIds;
    protected boolean _bootstrapEnabled = false;
    protected Class<?> _consumerClass;

    public Cli()
    {
      super(constructCliHelp(), Logger.getLogger(Dtail.class));
    }

    private static BaseCli.CliHelp constructCliHelp()
    {
      return new BaseCli.CliHelpBuilder()
         .className(Dtail.class)
         .startHeader()
           .addSection("Description")
           .addLine("A command-line tool to consume and inspect events from Databus for Oracle")
           .addSection("Options")
         .finish()
         .startFooter()
           .addSection("Examples")
           .addLine("* Print all recent events for source com.linkedin.events.example.Person from " +
                    "the relay relay.host:12345")
           .addLine()
           .addLine("bin/dtail -s com.linkedin.events.example.Person -R relay.host:12345")
           .addLine()
           .addLine("* Print 100 events for sources com.linkedin.events.example.Person and " +
                    "com.linkedin.events.example.Company from the " +
                    "relay relay.host.com:12345 since scn 987654321")
           .addLine()
           .addLine("bin/dtail -s com.linkedin.events.example.Person," +
                    "com.linkedin.events.example.Company -R relay.host.com:12345 " +
                    "--scn 987654321 -n 100")
           .addLine()
           .addLine("* Bootstrap from SCN 0 for source com.linkedin.events.example.Person using" +
                    " relay relay.host:12345 and bootstrap server " +
                    " bootsrap.host:11111 using checkpoint directory my_ckpt/ and print out " +
                    " JSON format suitable for JSON deserialization")
           .addLine()
           .addLine("bin/dtail -s com.linkedin.events.example.Person --scn 0 " +
                    "--relays relay.host:12345 " +
                    "-b --bstservers bootsrap.host:11111 " +
                    " --resume my_ckpt/ -F AVRO_JSON")
           .addLine()
           .addLine("* Consume events for 5 minutes for source com.linkedin.events.example.Person "+
                    "from the relay relay.host.com:12345 since scn 987654321 and print out event" +
                    "metadata rather than the payload")
           .addLine()
           .addLine("bin/dtail -s com.linkedin.events.example.Person," +
                    "com.linkedin.events.example.Company -R relay.host.com:12345 " +
                    "--scn 987654321 -u 5min -F EVENT_INFO")
           .addLine()
         .finish()
         .build();
    }

    @SuppressWarnings("static-access")
    @Override
    protected void constructCommandLineOptions()
    {
      super.constructCommandLineOptions();

      Option sourcesOption = OptionBuilder.withLongOpt(SOURCES_OPT_NAME)
                                          .hasArg()
                                          .withArgName("sources")
                                          .withDescription("comma-separated list of sources:")
                                          .create(SOURCES_OPT_CHAR);
      Option configRootOption = OptionBuilder.withLongOpt(CONFIG_ROOT_OPT_NAME)
                                             .hasArg()
                                             .withArgName("config_rootdir")
                                             .withDescription("directory with all config files; default: .")
                                             .create();
      Option relaysOption = OptionBuilder.withLongOpt(RELAYS_OPT_NAME)
                                         .hasArg()
                                         .withArgName("relay_list")
                                         .withDescription("semicolon-separated list of server:port")
                                         .create(RELAYS_OPT_CHAR);
      Option modPartOption = OptionBuilder.withLongOpt(MOD_PARTITION_OPT_NAME)
                                          .hasArg()
                                          .withArgName("div:[id1,id2,...]")
                                          .withDescription("returns only events for which hash(key) mod div in {id1, ...}")
                                          .create();
      Option bootstrapOption = OptionBuilder.withLongOpt(BOOTSTRAP_OPT_NAME)
                                            .hasOptionalArg()
                                            .withArgName("[true|false]")
                                            .withDescription("enable/disable bootstrap; Default: disabled")
                                            .create(BOOTSTRAP_OPT_CHAR);
      Option bstserversOption = OptionBuilder.withLongOpt(BOOTSTRAP_SERVERS_OPT_NAME)
                                             .hasArg()
                                             .withArgName("bootstrap_server_list")
                                             .withDescription("semicolon-separated list of server:port")
                                             .create();
      Option consumerClassOption = OptionBuilder.withLongOpt(CONSUMER_CLASS_NAME)
                                              .hasArg()
                                              .withArgName("callback_class")
                                              .withDescription("a name of a class that implements " +
                                                  "the DatabusCombinedConsumer interface and " +
                                                  "a default constructor. Add your jars to the" +
                                                  "lib/ directory.")
                                              .create();

      _cliOptions.addOption(configRootOption);
      _cliOptions.addOption(sourcesOption);
      _cliOptions.addOption(relaysOption);
      _cliOptions.addOption(modPartOption);
      _cliOptions.addOption(bootstrapOption);
      _cliOptions.addOption(bstserversOption);
      _cliOptions.addOption(consumerClassOption);
    }

    private boolean processSources()
    {
      if (! _cmd.hasOption(SOURCES_OPT_CHAR))
      {
        printError("sources list expected", true);
        return false;
      }

      _sourcesString = _cmd.getOptionValue(SOURCES_OPT_CHAR);
      LOG.debug("sources from cmd=" + _sourcesString);
      _sources = _sourcesString.split(",");
      return true;
    }

    private boolean processConfigRoot()
    {
      if (_cmd.hasOption(CONFIG_ROOT_OPT_NAME))
      {
        String configRootName = _cmd.getOptionValue(CONFIG_ROOT_OPT_NAME);
        File f = new File(configRootName);
        if (!f.exists())
        {
          printError("config directory " + configRootName + " does not exist", false);
          return false;
        }

        if (!f.isDirectory())
        {
          printError("config directory " + configRootName + " is not a directory", false);
          return false;
        }

        _configRoot = f;
      }
      LOG.info("using config root: " + _configRoot.getAbsolutePath());
      return true;
    }

    private void processRelaysOverride()
    {
      if (_cmd.hasOption(RELAYS_OPT_CHAR))
      {
        _relaysOverride = _cmd.getOptionValue(RELAYS_OPT_CHAR);
        if (! _relaysOverride.endsWith(String.valueOf(ServerInfoSetBuilder.SERVER_INFO_SEPARATOR)))
        {
          _relaysOverride = _relaysOverride + ServerInfoSetBuilder.SERVER_INFO_SEPARATOR;
        }
        LOG.info("Using relays override: " + _relaysOverride);
        _relaysOverride = _relaysOverride.replaceAll(
                                                     String.valueOf(ServerInfoSetBuilder.SERVER_INFO_SEPARATOR),
                                                                    ServerInfoBuilder.SOURCES_LIST_SEPARATOR + _sourcesString +
                                                                    ServerInfoSetBuilder.SERVER_INFO_SEPARATOR);
      }
    }

    private boolean processModPartition()
    {
      if (_cmd.hasOption(MOD_PARTITION_OPT_NAME))
      {
        String modPartStr = _cmd.getOptionValue(MOD_PARTITION_OPT_NAME);
        String[] parts = modPartStr.split(":");
        if (parts.length != 2)
        {
          printError("invalid mod partition specification: " + modPartStr, true);
          return false;
        }
        _modPartBase = Long.parseLong(parts[0]);
        if (_modPartBase <= 0)
        {
          printError("invalid  mod partition specification: " + modPartStr, true);
          return false;
        }
        _modPartIds = parts[1];
      }
      return true;
    }

    private void processBootstrap()
    {
      if (_cmd.hasOption(BOOTSTRAP_OPT_CHAR))
      {
        String bstValue = _cmd.getOptionValue(BOOTSTRAP_OPT_CHAR);
        if (null == bstValue || 0 == bstValue.trim().length())
        {
          _bootstrapEnabled = true;
        }
        else
        {
          _bootstrapEnabled = Boolean.valueOf(bstValue.trim().toLowerCase());
        }
        LOG.info("with boostrap enabled: " + _bootstrapEnabled);
      }
    }

    private void processBootstrapServers()
    {
      if (_cmd.hasOption(BOOTSTRAP_SERVERS_OPT_NAME))
      {
        _bstserversOverride = _cmd.getOptionValue(BOOTSTRAP_SERVERS_OPT_NAME);
        if (! _bstserversOverride.endsWith(String.valueOf(ServerInfoSetBuilder.SERVER_INFO_SEPARATOR)))
        {
          _bstserversOverride = _bstserversOverride + ServerInfoSetBuilder.SERVER_INFO_SEPARATOR;
        }
        LOG.info("Using bootstrap servers override: " + _bstserversOverride);
        _bstserversOverride = _bstserversOverride.replaceAll(
                                                             String.valueOf(ServerInfoSetBuilder.SERVER_INFO_SEPARATOR),
                                                                            ServerInfoBuilder.SOURCES_LIST_SEPARATOR + _sourcesString +
                                                                            ServerInfoSetBuilder.SERVER_INFO_SEPARATOR);
      }
    }

    protected boolean processConsumerClass()
    {
      if (_cmd.hasOption(CONSUMER_CLASS_NAME))
      {
        String className = _cmd.getOptionValue(CONSUMER_CLASS_NAME);
        try
        {
          _consumerClass = getClass().getClassLoader().loadClass(className);
        }
        catch (ClassNotFoundException e)
        {
          printError("unable to find consumer callback class " + className, false);
          return false;
        }

        if (! DatabusCombinedConsumer.class.isAssignableFrom(_consumerClass))
        {
          printError("consumer callback class " + _consumerClass +
                " does not implement DatabusCombinedConsumer", false);
          return false;
        }
      }
      return true;
    }

    @Override
    public boolean processCommandLineArgs(String[] cliArgs)
    {
      super.processCommandLineArgs(cliArgs);

      if (!processSources())
      {
        return false;
      }
      if (!processConfigRoot())
      {
        return false;
      }
      processRelaysOverride();
      if (!processModPartition())
      {
        return false;
      }
      processBootstrap();
      processBootstrapServers();
      if (!processConsumerClass())
      {
        return false;
      }

      return true;
    }

    public String[] getSources()
    {
      return _sources;
    }

    public File getConfigRoot()
    {
      return _configRoot;
    }

    public String getRelaysOverride()
    {
      return _relaysOverride;
    }

    public long getModPartBase()
    {
      return _modPartBase;
    }

    public String getModPartIds()
    {
      return _modPartIds;
    }

    public boolean isBootstrapEnabled()
    {
      return _bootstrapEnabled;
    }

    public String getBstserversOverride()
    {
      return _bstserversOverride;
    }

    public Class<?> getConsumerClass()
    {
      return _consumerClass;
    }

  }

  private class ShutdownThread extends Thread
  {

    @Override
    public void run()
    {
       LOG.info("Shutdown hook started");
       if (_cli.isShowStats() && null != _consumer && (_consumer instanceof DtailPrinter))
       {
          LOG.info("Generating stats");
       ((DtailPrinter)_consumer).printStats();
       }
       LOG.info("Shutdown hook finished");
    }

  }

  private final Cli _cli;
  private final DatabusHttpClientImpl _client;
  DatabusCombinedConsumer _consumer = null;

  public Dtail(Cli cli) throws IOException, DatabusException, DatabusClientException,
                               InstantiationException, IllegalAccessException
  {
    _cli = cli;

    Properties props = _cli.getConfigProps();
    DatabusHttpClientImpl.Config configBuilder =
        DatabusHttpClientImpl.createConfigBuilder("dtail.", props);

    String relaysOverride = _cli.getRelaysOverride();
    configBuilder.getRuntime().setRelaysList(relaysOverride);

    String bstserverOverrides = _cli.getBstserversOverride();
    configBuilder.getRuntime().getBootstrap().setServicesList(bstserverOverrides);

    if (null == _cli.getCheckpointDirName())
    {
      configBuilder.getCheckpointPersistence().setType(CheckpointPersistenceStaticConfig.ProviderType.NONE.toString());
    }
    else
    {
      configBuilder.getCheckpointPersistence().setType(CheckpointPersistenceStaticConfig.ProviderType.FILE_SYSTEM.toString());
      configBuilder.getCheckpointPersistence().getFileSystem().setRootDirectory(_cli.getCheckpointDirName());
    }

    if (cli.isBootstrapEnabled())
    {
      configBuilder.getRuntime().getBootstrap().setEnabled(true);
    }

    Checkpoint ckpt = new Checkpoint();
    if (DtailCliBase.BOB_SCN == cli.getSinceScn())
    {
      ckpt.setConsumptionMode(DbusClientMode.ONLINE_CONSUMPTION);
      ckpt.setWindowScn(0L);
      ckpt.setFlexible();
    }
    else if (DtailCliBase.EOB_SCN == cli.getSinceScn())
    {
      throw new DatabusException("EOB checkpoint not supported by dtail yet");
    }
    else
    {
      ckpt.setConsumptionMode(DbusClientMode.ONLINE_CONSUMPTION);
      ckpt.setWindowScn(cli.getSinceScn());
      ckpt.setWindowOffset(-1);
    }

    _client = new DatabusHttpClientImpl(configBuilder);
    if (null != _client.getCheckpointPersistenceProvider())
    {
      _client.getCheckpointPersistenceProvider().storeCheckpoint(Arrays.asList(cli.getSources()), ckpt);
    }

    //register consumers
    DtailPrinter.StaticConfigBuilder consConfBuilder = new DtailPrinter.StaticConfigBuilder();
    consConfBuilder.setPrintPrintVerbosity(cli.getPrintVerbosity());
    consConfBuilder.setMaxEventsNum(cli.getMaxEventNum());
    consConfBuilder.setMaxDurationMs(cli.getDurationMs());
    consConfBuilder.setPrintStats(cli.isShowStats());

    if (null != cli.getConsumerClass())
    {
      _consumer = (DatabusCombinedConsumer)cli.getConsumerClass().newInstance();
    }
    else
    {
      switch (cli.getOutputFormat())
      {
      case JSON: _consumer = new JsonDtailPrinter(_client, consConfBuilder.build(), cli.getOut()); break;
      case AVRO_JSON: _consumer = new AvroJsonDtailPrinter(_client, consConfBuilder.build(),
                                                           cli.getOut()); break;
      case AVRO_BIN: _consumer = new AvroBinaryDtailPrinter(_client, consConfBuilder.build(),
                                                           cli.getOut()); break;
      case NOOP: _consumer = new NoopDtailPrinter(_client, consConfBuilder.build(), cli.getOut()); break;
      case EVENT_INFO: _consumer = new EventInfoDtailPrinter(_client, consConfBuilder.build(),
                                                             cli.getOut()); break;
      default: throw new InvalidConfigException("unsupported output format: " + cli.getOutputFormat());
      }
    }

    DbusKeyCompositeFilterConfig filterConfig = null;

    if (_cli.getModPartBase() > 0)
    {
      DbusKeyCompositeFilterConfig.Config builder = new DbusKeyCompositeFilterConfig.Config();
      for (String src: cli.getSources())
      {
        builder.getFilter(src).setType(PartitionType.MOD.toString());
        builder.getFilter(src).getMod().setNumBuckets(cli.getModPartBase());
        builder.getFilter(src).getMod().setBuckets(cli.getModPartIds());
      }
      filterConfig = new DbusKeyCompositeFilterConfig(builder.build());
    }

    _client.registerDatabusStreamListener(_consumer, filterConfig, cli.getSources());
    _client.registerDatabusBootstrapListener(_consumer, filterConfig, cli.getSources());

    Runtime.getRuntime().addShutdownHook(new ShutdownThread());
  }

  public void start()
  {
    _client.start();
    _client.awaitShutdown();
  }

  public static void main(String[] args) throws Exception
  {
    Cli cli = new Cli();
    if (!cli.processCommandLineArgs(args))
    {
      System.exit(1);
    }

    Dtail dtail = new Dtail(cli);
    dtail.start();
  }

}
TOP

Related Classes of com.linkedin.databus2.tools.dtail.Dtail

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.