Package com.linkedin.databus.bootstrap.utils.bst_reader

Source Code of com.linkedin.databus.bootstrap.utils.bst_reader.BootstrapReaderV2Main$Cli

/*
*
* 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.
*
*/
package com.linkedin.databus.bootstrap.utils.bst_reader;

import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import com.linkedin.databus.bootstrap.common.BootstrapConfig;
import com.linkedin.databus.bootstrap.common.BootstrapConfigBase;
import com.linkedin.databus.bootstrap.common.BootstrapReadOnlyConfig;
import com.linkedin.databus.bootstrap.utils.BootstrapDBReader;
import com.linkedin.databus.bootstrap.utils.BootstrapReaderEventHandler;
import com.linkedin.databus.bootstrap.utils.bst_reader.filter.BootstrapReaderFilter;
import com.linkedin.databus.bootstrap.utils.bst_reader.filter.PayloadFieldEqFilter;
import com.linkedin.databus.bootstrap.utils.bst_reader.filter.PayloadFieldGtFilter;
import com.linkedin.databus.bootstrap.utils.bst_reader.filter.PayloadFieldLtFilter;
import com.linkedin.databus.core.BaseCli;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus2.schemas.FileSystemVersionedSchemaSetProvider;
import com.linkedin.databus2.schemas.ResourceVersionedSchemaSetProvider;
import com.linkedin.databus2.schemas.VersionedSchemaSet;
import com.linkedin.databus2.schemas.VersionedSchemaSetProvider;

/**
* A helper utility to read  the contents of the bootstrap database
*/
public class BootstrapReaderV2Main
{
  public static final Logger LOG = Logger.getLogger(BootstrapReaderV2Main.class);

  public static class Cli extends BaseCli
  {
    /**
     *
     */
    private static final int QUERY_BATCH_SIZE = 50;
    public static final String BST_DBNAME_OPT_NAME = "db";
    public static final char BST_DBNAME_OPT_CHAR = 'D';
    public static final String MYSQL_HOST_OPT_NAME = "mysql_host";
    public static final char MYSQL_HOST_OPT_CHAR = 'H';
    public static final String BST_USER_OPT_NAME = "user";
    public static final char BST_USER_OPT_CHAR = 'u';
    public static final String BST_PASSWORD_OPT_NAME = "password";
    public static final char BST_PASSWORD_OPT_CHAR = 'p';
    public static final String WITH_SNAPSHOT_OPT_NAME = "with-snapshot-table";
    public static final char WITH_SNAPSHOT_OPT_CHAR = 'S';
    public static final String WITH_LOG_OPT_NAME = "with-log-tables";
    public static final char WITH_LOG_OPT_CHAR = 'L';
    public static final String SCNS_OPT_NAME = "scns";
    public static final String KEYS_OPT_NAME = "keys";
    public static final String SRCID_OPT_NAME = "srcid";
    public static final String SRCNAME_OPT_NAME = "srcname";
    public static final String SCHEMAS_PATH_OPT_NAME = "schemas_registry";
    public static final String PAYLOAD_COND_OPT_NAME = "payload_matches";

    private static final Pattern PAYLOAD_COND_REGEX = Pattern.compile("(\\w+)(<=|<|>|>=|==|!=|~)(.+)");

    private final BootstrapConfig _bstCfg;
    private short _srcId = (short)-1;
    private final MetaDataFiltersBuilder _metaBuilder =
        new MetaDataFiltersBuilder();
    private final List<String> _tableNames = new ArrayList<String>();
    private final List<BootstrapReaderFilter> _eventFilters =
        new ArrayList<BootstrapReaderFilter>();
    private VersionedSchemaSetProvider _schemaSetProvider =
        new ResourceVersionedSchemaSetProvider(this.getClass().getClassLoader());

    public Cli(Logger log) throws IOException
    {
      super(constructCliHelp(), log);
      _bstCfg = new BootstrapConfig();
      _bstCfg.setBootstrapBatchSize(QUERY_BATCH_SIZE);
    }

    private static BaseCli.CliHelp constructCliHelp()
    {
      return new BaseCli.CliHelpBuilder()
         .className(BootstrapReaderV2Main.class)
         .startHeader()
           .addSection("Description")
           .addLine("A simple tool to inspect the contents for MySQL bootstrap database")
           .addSection("Options")
         .finish()
         .startFooter()
           .addSection("Examples")
           .addLine("* Find records in tab_201 with key 12345")
           .addLine()
           .add("java ")
           .add(BootstrapReaderV2Main.class.getName())
           .addLine("-H example.host.com --with-snaptshot-table --keys 12345,12345 --srcid 201")
           .addLine()
           .addLine("* Find records in tab_201 with key from \"abc\" to \"abd\" and scn <= 1000 " +
                    "(key range works as expected only for true string keys)")
           .addLine()
           .add("java ")
           .add(BootstrapReaderV2Main.class.getName())
           .addLine("-H example.host.com --with-snaptshot-table --keys abc,abd --scn ,1000 --srcid 201")
           .addLine()
           .addLine("* Find records in tab_201, log_201_100, ..., log_201_200 with key > 123  which " +
                    "are not deleted (works for number keys but requires a full table scan)")
           .addLine()
           .add("java ")
           .add(BootstrapReaderV2Main.class.getName())
           .addLine("-H example.host.com --with-snaptshot-table --with-log-tables 100,200  " +
                    "--srcid 201 --payload_matches key>123 --payload_matches isDeleted==N")
           .addLine()
         .finish()
         .build();
    }

    @SuppressWarnings("static-access")
    @Override
    protected void constructCommandLineOptions()
    {
      super.constructCommandLineOptions();
      _cliOptions.addOption(
          OptionBuilder.withLongOpt(BST_DBNAME_OPT_NAME)
                       .withDescription("bootstrap database name; default is " +
                                        BootstrapConfigBase.DEFAULT_BOOTSTRAP_DB_NAME)
                       .hasArg()
                       .withArgName("db_name")
                       .create(BST_DBNAME_OPT_CHAR));
      _cliOptions.addOption(
          OptionBuilder.withLongOpt(MYSQL_HOST_OPT_NAME)
                       .withDescription("bootstrap MySQL host; default is " +
                                        BootstrapConfigBase.DEFAULT_BOOTSTRAP_DB_HOSTNAME)
                       .hasArg()
                       .withArgName("host_name")
                       .create(MYSQL_HOST_OPT_CHAR));
      _cliOptions.addOption(
          OptionBuilder.withLongOpt(BST_USER_OPT_NAME)
                       .withDescription("bootstrap user name; default is " +
                                        BootstrapConfigBase.DEFAULT_BOOTSTRAP_DB_USERNAME)
                       .hasArg()
                       .withArgName("user_name")
                       .create(BST_USER_OPT_CHAR));
      _cliOptions.addOption(
          OptionBuilder.withLongOpt(BST_PASSWORD_OPT_NAME)
                       .withDescription("bootstrap user password; default is " +
                                        BootstrapConfigBase.DEFAULT_BOOTSTRAP_DB_PASSWORD)
                       .hasOptionalArg()
                       .withArgName("password")
                       .create(BST_PASSWORD_OPT_CHAR));
      _cliOptions.addOption(
          OptionBuilder.withLongOpt(WITH_SNAPSHOT_OPT_NAME)
                       .withDescription("scan snapshot table")
                       .create(WITH_SNAPSHOT_OPT_CHAR));
      _cliOptions.addOption(
          OptionBuilder.withLongOpt(WITH_LOG_OPT_NAME)
                       .withDescription("scan log tables. If min_log_id is not specified, the" +
                                        " first available log will be used. if max_log_id is not" +
                                        " specified, the last available log table will be used.")
                       .hasOptionalArg()
                       .withArgName("[[min_log_id],[max_log_id]]")
                       .create(WITH_LOG_OPT_CHAR));
      _cliOptions.addOption(
          OptionBuilder.withDescription("scn range to return")
                       .hasArg()
                       .withArgName("[min_scn],[max_scn]")
                       .create(SCNS_OPT_NAME));
      _cliOptions.addOption(
          OptionBuilder.withDescription("key range to return")
                       .hasArg()
                       .withArgName("[min_key],[max_key]")
                       .create(KEYS_OPT_NAME));
      _cliOptions.addOption(
          OptionBuilder.withDescription("logical source id to scan; exactly one of --srcid and " +
                                        "--srcname should be specified")
                       .hasArg()
                       .withArgName("srcid")
                       .create(SRCID_OPT_NAME));
      _cliOptions.addOption(
          OptionBuilder.withDescription("logical source name to scan; exactly one of --srcid and " +
                                        "--srcname should be specified")
                       .hasArg()
                       .withArgName("source_name")
                       .create(SRCNAME_OPT_NAME));
      _cliOptions.addOption(
          OptionBuilder.withDescription("path to the schemas registry with the Avro schemas to use for decoding")
                       .hasArg()
                       .withArgName("schemas_path")
                       .create(SCHEMAS_PATH_OPT_NAME));
      _cliOptions.addOption(
          OptionBuilder.withDescription("payload predicate: <OP> can be <=,<,>,>=,==,!=,~")
                       .hasArg()
                       .withArgName("field_name<OP>constant")
                       .create(PAYLOAD_COND_OPT_NAME));
    }

    @Override
    public boolean processCommandLineArgs(String[] cliArgs)
    {
      if (!super.processCommandLineArgs(cliArgs))
      {
        return false;
      }

      if (!processBootstrapOptions())
      {
        return false;
      }
      if (!processTables())
      {
        return false;
      }
      if (!processMetadataFilters())
      {
        return false;
      }
      if (!processEventFilters())
      {
        return false;
      }
      if (!processSchemasPath())
      {
        return false;
      }

      return true;
    }

    private boolean processTables()
    {
      if (_cmd.hasOption(SRCID_OPT_NAME))
      {
        try
        {
          _srcId = Short.parseShort(_cmd.getOptionValue(SRCID_OPT_NAME));
        }
        catch (NumberFormatException e)
        {
          printError("invalid logical source id: " + _cmd.getOptionValue(SRCID_OPT_NAME), false);
          return false;
        }
      }
      if (_cmd.hasOption(SRCNAME_OPT_NAME))
      {
        //TODO add support for mapping a logical source name to an id
        printError("option not supported yet: " + SRCNAME_OPT_NAME, false);
        return false;
      }
      if (_srcId <= 0)
      {
        printError("no logical source specified", true);
        return false;
      }
      if (_cmd.hasOption(WITH_SNAPSHOT_OPT_CHAR))
      {
        _tableNames.add("tab_" + _srcId);
      }
      if (_cmd.hasOption(WITH_LOG_OPT_CHAR))
      {
        int minLogId = -1;
        int maxLogId = -1;

        String logOpt = _cmd.getOptionValue(WITH_LOG_OPT_CHAR);
        if (null != logOpt)
        {
          int commaPos = logOpt.indexOf(',');
          try
          {
            if (-1 == commaPos)
            {
              minLogId = Integer.parseInt(logOpt);
              maxLogId = minLogId;
            }
            else
            {
              if (commaPos > 0)
              {
                minLogId = Integer.parseInt(logOpt.substring(0, commaPos));
              }
              if (commaPos < logOpt.length() - 1)
              {
                maxLogId = Integer.parseInt(logOpt.substring(commaPos + 1));
              }
            }
          }
          catch (NumberFormatException e)
          {
            printError("invalid log table number(s): " + logOpt, true);
            return false;
          }
        }

        if (0 > minLogId || 0 > maxLogId)
        {
          //TODO add the ability to figure out the oldest and/or newest log table
          printError("log table number wildcards are not supported yet", false);
          return false;
        }

        for (int lid = minLogId; lid <= maxLogId; ++lid)
        {
          _tableNames.add("log_" + _srcId + "_" + lid);
        }
      }

      if (0 == _tableNames.size())
      {
        printError("At least one of --" + WITH_SNAPSHOT_OPT_NAME + " or --" + WITH_LOG_OPT_NAME +
                   " is required.", true);
        return false;
      }

      return true;
    }

    private boolean processEventFilters()
    {
      if (_cmd.hasOption(PAYLOAD_COND_OPT_NAME))
      {
        for (String cond: _cmd.getOptionValues(PAYLOAD_COND_OPT_NAME))
        {
          if (!processEventFilter(cond))
          {
            return false;
          }
        }
      }
      return true;
    }

    private boolean processEventFilter(String cond)
    {
      Matcher m  = PAYLOAD_COND_REGEX.matcher(cond);
      if (! m.matches())
      {
        printError("invalid payload predicate: " + cond, true);
        return false;
      }
      //try to guess the type of the constant
      //TODO make this smarter -- inspect the schema
      String constStr = m.group(3);
      Object constArg = constStr;
      Long constLong = null;
      Double constDouble = null;
      try
      {
        constLong = Long.parseLong(constStr);
        constArg = constLong;
      }
      catch (NumberFormatException e)
      {
        //try next type
      }
      if (null == constLong)
      {
        try
        {
          constDouble = Double.parseDouble(constStr);
          constArg = constDouble;
        }
        catch (NumberFormatException e)
        {
          //use string
        }
      }
      String oper = m.group(2);
      if ("<".equals(oper))
      {
        BootstrapReaderFilter f = new PayloadFieldLtFilter(m.group(1), constArg);
        _eventFilters.add(f);
      }
      else if (">".equals(oper))
      {
        BootstrapReaderFilter f = new PayloadFieldGtFilter(m.group(1), constArg);
        _eventFilters.add(f);
      }
      else if ("==".equals(oper))
      {
        BootstrapReaderFilter f = new PayloadFieldEqFilter(m.group(1), constArg);
        _eventFilters.add(f);
      }
      else
      {
        printError("unsupported relationship: " + oper, false);
        return false;
      }
      //TODO add column name validation
      return true;
    }

    private boolean processMetadataFilters()
    {
      if (_cmd.hasOption(KEYS_OPT_NAME))
      {
        String keysOpt = _cmd.getOptionValue(KEYS_OPT_NAME);
        String[] optParts = keysOpt.split(",");
        if (optParts.length > 2)
        {
          printError("invalid key range specifier:" + keysOpt, true);
          return false;
        }
        if (optParts[0].length() > 0)
        {
          _metaBuilder.minKey(optParts[0]);
        }
        if (2 == optParts.length && optParts[1].length() > 0)
        {
          _metaBuilder.maxKey(optParts[1]);
        }
      }

      if (_cmd.hasOption(SCNS_OPT_NAME))
      {
        String scnsOpt = _cmd.getOptionValue(SCNS_OPT_NAME);
        String[] optParts = scnsOpt.split(",");
        if (optParts.length > 2)
        {
          printError("invalid SCN range specifier:" + scnsOpt, true);
          return false;
        }
        try
        {
          if (optParts[0].length() > 0)
          {
            _metaBuilder.minScn(Long.parseLong(optParts[0]));
          }
          if (2 == optParts.length && optParts[1].length() > 0)
          {
            _metaBuilder.maxScn(Long.parseLong(optParts[1]));
          }
        }
        catch (NumberFormatException e)
        {
          printError("invalid SCN range specifier:" + scnsOpt, true);
          return false;
        }
      }

      return true;
    }

    private boolean processBootstrapOptions()
    {
       if (_cmd.hasOption(BST_DBNAME_OPT_CHAR))
       {
         _bstCfg.setBootstrapDBName(_cmd.getOptionValue(BST_DBNAME_OPT_CHAR));
       }
       if (_cmd.hasOption(MYSQL_HOST_OPT_CHAR))
       {
         _bstCfg.setBootstrapDBHostname(_cmd.getOptionValue(MYSQL_HOST_OPT_CHAR));
       }
       if (_cmd.hasOption(BST_USER_OPT_CHAR))
       {
         _bstCfg.setBootstrapDBUsername(_cmd.getOptionValue(BST_USER_OPT_CHAR));
       }
       if (_cmd.hasOption(BST_PASSWORD_OPT_CHAR))
       {
         String pwd = _cmd.getOptionValue(BST_PASSWORD_OPT_CHAR);
         if (null == pwd)
         {
           Console console = System.console();
           if (null == console)
           {
             printError("no password specified", true);
             return false;
           }
           char[] pwdChars = console.readPassword("Enter bootstrap MySQL password:");
           if (null == pwdChars)
           {
             printError("no password specified", true);
             return false;
           }
           pwd = new String(pwdChars);
         }
         _bstCfg.setBootstrapDBPassword(pwd);
       }
       return true;
    }

    public boolean processSchemasPath()
    {
      if (_cmd.hasOption(SCHEMAS_PATH_OPT_NAME))
      {
        _schemaSetProvider =
            new FileSystemVersionedSchemaSetProvider(
                new File(_cmd.getOptionValue(SCHEMAS_PATH_OPT_NAME)));
      }
      return true;
    }

    public BootstrapReadOnlyConfig getBstCfg() throws InvalidConfigException
    {
      return _bstCfg.build();
    }

    public MetaDataFilters getMetadataFilters()
    {
      return _metaBuilder.build();
    }

    public List<String> getTableNames()
    {
      return Collections.unmodifiableList(_tableNames);
    }

    public List<BootstrapReaderFilter> getEventFilters()
    {
      return _eventFilters;
    }

    public VersionedSchemaSet getSchemaSet()
    {
      return _schemaSetProvider.loadSchemas();
    }
  }

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

    BootstrapReaderEventHandler eventHandler = new BootstrapDBReader.DumpEventHandler();

    for (String table: cli.getTableNames())
    {
      LOG.info("scanning table " + table);
      BootstrapTableReaderV2 reader = new
          BootstrapTableReaderV2(table, cli.getMetadataFilters(), cli.getSchemaSet(), eventHandler,
                                  cli.getEventFilters(), cli.getBstCfg(), null);
      reader.execute();
    }
  }

}
TOP

Related Classes of com.linkedin.databus.bootstrap.utils.bst_reader.BootstrapReaderV2Main$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.