@Before
  public void setUp() throws Exception {
    client = new AerospikeClient("localhost", 3000);
    Log.setCallback(
        new Callback() {

          @Override
          public void log(Level level, String message) {
            switch (level) {
              case INFO:
                log.info(message);
                break;
              case WARN:
                log.warn(message);
                break;
              case DEBUG:
                log.debug(message);
                break;
              case ERROR:
                log.error(message);
                break;
            }
          }
        });
    key = new Key("test", "demo", "the-queue-001");
    client.delete(null, key);
    subject = new LDTQueue<String>(client, key, "the-queue");
  }
  public Main(String[] commandLineArgs) throws Exception {
    Options options = new Options();
    options.addOption("h", "hosts", true, "Set the Aerospike host node.");
    options.addOption("p", "port", true, "Set the port on which to connect to Aerospike.");
    options.addOption("U", "user", true, "User name.");
    options.addOption("P", "password", true, "Password.");
    options.addOption("n", "namespace", true, "Set the Aerospike namespace. Default: test.");
    options.addOption("s", "set", true, "Set the Aerospike set name. Default: set.");
    options.addOption(
        "k",
        "keys",
        true,
        "Set the number of keys the client is dealing with. "
            + "If using an 'insert' workload (detailed below), the client will write this "
            + "number of keys, starting from value = start_value. Otherwise, the client "
            + "will read and update randomly across the values between start_value and "
            + "start_value + num_keys.");

    options.addOption(
        "o",
        "objectSpec",
        true,
        "I | S:<size> | M:<type>:<size>\n"
            + "Set the type of object(s) to use in Aerospike transactions. Type can be 'I' "
            + "for integer, 'S' for string, or 'M' for map. If type is 'I' (integer), "
            + "do not set a size (integers are always 8 bytes). If type is 'S' "
            + "(string), this value represents the length of the string. If type is 'M' "
            + "(map), type represents the type of data which can be 'I' or 'S', and size "
            + "represents the length of the string if type is 'S'.");
    options.addOption(
        "R",
        "random",
        false,
        "Use dynamically generated random bin values instead of default static fixed bin values.");
    options.addOption(
        "S",
        "startkey",
        true,
        "Set the starting value of the working set of keys. "
            + "If using an 'insert' workload, the start_value indicates the first value to write. "
            + "Otherwise, the start_value indicates the smallest value in the working set of keys.");
    options.addOption(
        "w",
        "workload",
        true,
        "I | RU,{o,%}\n"
            + "Set the desired workload.\n\n"
            + "   -w I sets a linear 'insert' workload.\n\n"
            + "   -w RU,o,80 picks one item randomly and sets a random read-update workload with 80% reads and 20% writes.\n\n"
            + "   -w RU,50,40 for 50% of items, sets a random read-update workload with 40% reads and 60% writes.");
    options.addOption(
        "g",
        "throughput",
        true,
        "Set a target transactions per second for the client. The client should not exceed this "
            + "average throughput.");
    options.addOption(
        "t",
        "transactions",
        true,
        "Number of transactions to perform in read/write mode before shutting down. "
            + "The default is to run indefinitely.");

    options.addOption(
        "T", "timeout", true, "Set read and write transaction timeout in milliseconds.");

    options.addOption(
        "z",
        "threads",
        true,
        "Set the number of threads the client will use to generate load. "
            + "It is not recommended to use a value greater than 125.");
    options.addOption(
        "latency",
        true,
        "<number of latency columns>,<range shift increment>\n"
            + "Show transaction latency percentages using elapsed time ranges.\n"
            + "<number of latency columns>: Number of elapsed time ranges.\n"
            + "<range shift increment>: Power of 2 multiple between each range starting at column 3.\n\n"
            + "A latency definition of '-latency 7,1' results in this layout:\n"
            + "    <=1ms >1ms >2ms >4ms >8ms >16ms >32ms\n"
            + "       x%   x%   x%   x%   x%    x%    x%\n"
            + "A latency definition of '-latency 4,3' results in this layout:\n"
            + "    <=1ms >1ms >8ms >64ms\n"
            + "       x%   x%   x%    x%\n\n"
            + "Latency columns are cumulative. If a transaction takes 9ms, it will be included in both the >1ms and >8ms columns.");

    options.addOption("D", "debug", false, "Run benchmarks in debug mode.");
    options.addOption("u", "usage", false, "Print usage.");

    options.addOption("PS", "pageSize", true, "Page size in kilobyte. Default: 4K");
    options.addOption("IC", "itemCount", true, "Number of items in LDT. Default: 100");
    options.addOption("IS", "itemSize", true, "Item size in byte. Default: 8");

    // Parse the command line arguments.
    CommandLineParser parser = new PosixParser();
    CommandLine line = parser.parse(options, commandLineArgs);
    String[] extra = line.getArgs();

    if (line.hasOption("u")) {
      logUsage(options);
      throw new UsageException();
    }

    if (extra.length > 0) {
      throw new Exception("Unexpected arguments: " + Arrays.toString(extra));
    }

    args.readPolicy = clientPolicy.readPolicyDefault;
    args.writePolicy = clientPolicy.writePolicyDefault;

    if (line.hasOption("hosts")) {
      this.hosts = line.getOptionValue("hosts").split(",");
    } else {
      this.hosts = new String[1];
      this.hosts[0] = "127.0.0.1";
    }

    if (line.hasOption("port")) {
      this.port = Integer.parseInt(line.getOptionValue("port"));
    } else {
      this.port = 3000;
    }

    clientPolicy.user = line.getOptionValue("user");
    clientPolicy.password = line.getOptionValue("password");

    if (clientPolicy.user != null && clientPolicy.password == null) {
      java.io.Console console = System.console();

      if (console != null) {
        char[] pass = console.readPassword("Enter password:");

        if (pass != null) {
          clientPolicy.password = new String(pass);
        }
      }
    }

    if (line.hasOption("pageSize")) {
      args.pageSize = Integer.parseInt(line.getOptionValue("pageSize"));
    } else {
      args.pageSize = 4096; // Default LDT page size: 4K.
    }

    if (line.hasOption("itemCount")) {
      args.itemCount = Integer.parseInt(line.getOptionValue("itemCount"));
    } else {
      args.itemCount = 100; // Default LDT item count.
    }

    if (line.hasOption("itemSize")) {
      args.itemSize = Integer.parseInt(line.getOptionValue("itemSize"));
    } else {
      args.itemSize = 8; // Default LDT item size.
    }

    if (line.hasOption("namespace")) {
      args.namespace = line.getOptionValue("namespace");
    } else {
      args.namespace = "test";
    }

    if (line.hasOption("set")) {
      args.setName = line.getOptionValue("set");
    } else {
      args.setName = "set";
    }

    if (line.hasOption("keys")) {
      this.nKeys = Integer.parseInt(line.getOptionValue("keys"));
    } else {
      this.nKeys = 100000;
    }

    if (line.hasOption("startkey")) {
      this.startKey = Integer.parseInt(line.getOptionValue("startkey"));
    }

    if (line.hasOption("objectSpec")) {
      String[] objectsArr = line.getOptionValue("objectSpec").split(",");
      args.objectSpec = new DBObjectSpec();

      for (int i = 0; i < objectsArr.length; i++) {
        String[] objarr = objectsArr[i].split(":");
        DBObjectSpec dbobj = new DBObjectSpec();

        if ((DBObjectSpec.type = objarr[0].charAt(0)) == 'M') {
          DBObjectSpec.mapDataType = objarr[1].charAt(0);

          if (DBObjectSpec.mapDataType == 'S') {
            DBObjectSpec.mapDataSize = Integer.parseInt(objarr[2]);

            if (DBObjectSpec.mapDataSize <= 0) {
              throw new Exception("String length must be > 0.");
            }

            // How many entries inside the map.
            DBObjectSpec.mapValCount =
                (int) Math.ceil((float) args.itemSize / DBObjectSpec.mapDataSize);
            // System.out.println("********" + DBObjectSpec.mapValCount);
          } else {
            DBObjectSpec.mapValCount =
                (int) Math.ceil((float) args.itemSize / 8); // How many entries inside the map.
            System.out.println("mapValCount: " + DBObjectSpec.mapValCount);
          }
        }

        if (objarr.length > 1 && DBObjectSpec.type == 'S') { // There is a size value.
          DBObjectSpec.size = Integer.parseInt(objarr[1]);
          if (DBObjectSpec.size <= 0) {
            throw new Exception("String length must be > 0.");
          }
        }
        args.objectSpec = dbobj;
        System.out.println("type: " + DBObjectSpec.type);
        System.out.println("size: " + DBObjectSpec.size);
        System.out.println("mapType " + DBObjectSpec.mapDataType);
        System.out.println("mapSize " + DBObjectSpec.mapDataSize);
        System.out.println(
            "mapValCount "
                + args.itemSize
                + "/"
                + DBObjectSpec.mapDataSize
                + " = "
                + DBObjectSpec.mapValCount);
      }
    } else {
      args.objectSpec = new DBObjectSpec();
      DBObjectSpec dbobj = new DBObjectSpec();
      DBObjectSpec.type = 'I'; // If the object is not specified, it has one bin of integer type.
      args.objectSpec = dbobj;
    }

    args.readPct = 50;

    if (line.hasOption("workload")) {
      String[] workloadOpts = line.getOptionValue("workload").split(",");
      String workloadType = workloadOpts[0];

      if (workloadType.equals("I")) {
        args.workload = Workload.INITIALIZE;
        this.initialize = true;

        if (workloadOpts.length > 1) {
          throw new Exception(
              "Invalid workload number of arguments: " + workloadOpts.length + " Expected 1.");
        }
      } else if (workloadType.equals("RU")) {
        args.workload = Workload.READ_UPDATE;

        if (workloadOpts.length != 3) {
          throw new Exception(
              "Invalid workload number of arguments: " + workloadOpts.length + " Expected 3.");
        } else {
          args.readPct = Integer.parseInt(workloadOpts[2]);
          if (workloadOpts[1].charAt(0) == 'o') {
            args.updateOne = true;
          } else {
            args.updatePct = Integer.parseInt(workloadOpts[1]); // Get the %.
          }

          if (args.readPct < 0 || args.readPct > 100) {
            throw new Exception("Read-update workload read percentage must be between 0 and 100.");
          }
        }
      } else {
        throw new Exception("Unknown workload: " + workloadType);
      }
    }

    if (line.hasOption("throughput")) {
      args.throughput = Integer.parseInt(line.getOptionValue("throughput"));
    }

    if (line.hasOption("transactions")) {
      args.transactionLimit = Long.parseLong(line.getOptionValue("transactions"));
    }

    if (line.hasOption("timeout")) {
      int timeout = Integer.parseInt(line.getOptionValue("timeout"));
      args.readPolicy.timeout = timeout;
      args.writePolicy.timeout = timeout;
    }

    if (line.hasOption("threads")) {
      this.nThreads = Integer.parseInt(line.getOptionValue("threads"));

      if (this.nThreads < 1) {
        throw new Exception("Client threads (-z) must be > 0");
      }
    } else {
      this.nThreads = 16;
    }

    if (line.hasOption("debug")) {
      args.debug = true;
    }

    if (line.hasOption("latency")) {
      String[] latencyOpts = line.getOptionValue("latency").split(",");

      if (latencyOpts.length != 2) {
        throw new Exception("Latency expects 2 arguments. Received: " + latencyOpts.length);
      }
      int columns = Integer.parseInt(latencyOpts[0]);
      int bitShift = Integer.parseInt(latencyOpts[1]);
      counters.read.latency = new LatencyManager(columns, bitShift);
      counters.write.latency = new LatencyManager(columns, bitShift);
    }

    if (!line.hasOption("random")) {
      args.setFixedBins();
    }

    System.out.println(
        "Benchmark: "
            + this.hosts[0]
            + ":"
            + this.port
            + ", namespace: "
            + args.namespace
            + ", set: "
            + (args.setName.length() > 0 ? args.setName : "<empty>")
            + ", threads: "
            + this.nThreads
            + ", workload: "
            + args.workload);

    if (args.workload == Workload.READ_UPDATE) {
      System.out.print("read: " + args.readPct + '%');
      // System.out.print(" (all bins: " + args.readMultiBinPct + '%');
      // System.out.print(", single bin: " + (100 - args.readMultiBinPct) + "%)");

      System.out.print(", write: " + (100 - args.readPct) + '%');
      // System.out.print(" (all bins: " + args.writeMultiBinPct + '%');
      // System.out.println(", single bin: " + (100 - args.writeMultiBinPct) + "%)");
    }

    System.out.println(
        "keys: "
            + this.nKeys
            + ", start key: "
            + this.startKey
            + ", transactions: "
            + args.transactionLimit
            + ", items: "
            + args.itemCount
            + ", random values: "
            + (args.fixedLDTBins == null)
            + ", throughput: "
            + (args.throughput == 0 ? "unlimited" : (args.throughput + " tps")));

    if (args.workload != Workload.INITIALIZE) {
      System.out.println("read policy: timeout: " + args.readPolicy.timeout);
    }

    System.out.println("write policy: timeout: " + args.writePolicy.timeout);

    System.out.print("ldt-bin[" + args.itemCount + "]: ");

    switch (DBObjectSpec.type) {
      case 'I':
        System.out.println("integer");
        break;

      case 'S':
        System.out.println("string[" + DBObjectSpec.size + "]");
        break;
      case 'M':
        switch (DBObjectSpec.mapDataType) {
          case 'I':
            System.out.println("map of " + "integer");
          case 'S':
            System.out.println("map of " + "string[" + DBObjectSpec.mapDataSize + "]");
          default:
            throw new Exception("Unknown DataType: " + DBObjectSpec.type);
        }
    }

    System.out.println("debug: " + args.debug);

    Log.Level level = (args.debug) ? Log.Level.DEBUG : Log.Level.INFO;
    Log.setLevel(level);
    Log.setCallback(this);

    clientPolicy.failIfNotConnected = true;
  }