package com.opensoc.pcapservice;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.NoServerForRegionException;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.log4j.Logger;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import com.google.common.annotations.VisibleForTesting;

/**
 * Singleton class which integrates with HBase table and returns pcaps sorted by
 * timestamp(dsc) for the given list of keys. Creates HConnection if it is not
 * already created and the same connection instance is being used for all
 * requests
 * 
 * @author sheetal
 * @version $Revision: 1.0 $
 */

@Path("/")
public class PcapGetterHBaseImpl implements IPcapGetter {

  /** The pcap getter h base. */
  private static IPcapGetter pcapGetterHBase = null;

  /** The Constant LOG. */
  private static final Logger LOGGER = Logger
      .getLogger(PcapGetterHBaseImpl.class);

  /*
   * (non-Javadoc)
   * 
   * @see com.cisco.opensoc.hbase.client.IPcapGetter#getPcaps(java.util.List,
   * java.lang.String, long, long, boolean, boolean, long)
   */
 
  
	@GET
	@Path("pcap/test")
	@Produces("text/html")
	public Response  index() throws URISyntaxException { 
		return Response.ok("ALL GOOD").build();   
	}
	
	
  public PcapsResponse getPcaps(List<String> keys, String lastRowKey,
      long startTime, long endTime, boolean includeReverseTraffic,
      boolean includeDuplicateLastRow, long maxResultSize) throws IOException {
    Assert
        .isTrue(
            checkIfValidInput(keys, lastRowKey),
            "No valid input. One of the value must be present from {keys, lastRowKey}");
    LOGGER.info(" keys=" + keys.toString() + ";  lastRowKey="
        + lastRowKey);

    PcapsResponse pcapsResponse = new PcapsResponse();
    // 1. Process partial response key
    if (StringUtils.isNotEmpty(lastRowKey)) {
      pcapsResponse = processKey(pcapsResponse, lastRowKey, startTime,
          endTime, true, includeDuplicateLastRow, maxResultSize);
      // LOGGER.debug("after scanning lastRowKey=" +
      // pcapsResponse.toString()+"*********************************************************************");
      if (pcapsResponse.getStatus() == PcapsResponse.Status.PARTIAL) {
        return pcapsResponse;
      }
    }
    // 2. Process input keys
    List<String> sortedKeys = sortKeysByAscOrder(keys, includeReverseTraffic);
    List<String> unprocessedKeys = new ArrayList<String>();
    unprocessedKeys.addAll(sortedKeys);
    if (StringUtils.isNotEmpty(lastRowKey)) {
      unprocessedKeys.clear();
      unprocessedKeys = getUnprocessedSublistOfKeys(sortedKeys,
          lastRowKey);
    }
    LOGGER.info("unprocessedKeys in getPcaps" + unprocessedKeys.toString());
    if (!CollectionUtils.isEmpty(unprocessedKeys)) {
      for (int i = 0; i < unprocessedKeys.size(); i++) {
        pcapsResponse = processKey(pcapsResponse, unprocessedKeys.get(i),
            startTime, endTime, false, includeDuplicateLastRow, maxResultSize);
        // LOGGER.debug("after scanning input unprocessedKeys.get(" + i + ") ="
        // +
        // pcapsResponse.toString()+"*********************************************************************");
        if (pcapsResponse.getStatus() == PcapsResponse.Status.PARTIAL) {
          return pcapsResponse;
        }
      }
    }
    return pcapsResponse;
  }

  /*
   * (non-Javadoc)
   * 
   * @see com.cisco.opensoc.hbase.client.IPcapGetter#getPcaps(java.lang.String, long,
   * long, boolean)
   */
 
  public PcapsResponse getPcaps(String key, long startTime, long endTime,
      boolean includeReverseTraffic) throws IOException {
    Assert.hasText(key, "key must not be null or empty");
    return getPcaps(Arrays.asList(key), null, startTime, endTime,
        includeReverseTraffic, false, ConfigurationUtil.getDefaultResultSize());
  }

  /*
   * (non-Javadoc)
   * 
   * @see com.cisco.opensoc.hbase.client.IPcapGetter#getPcaps(java.util.List)
   */
 
  public PcapsResponse getPcaps(List<String> keys) throws IOException {
    Assert.notEmpty(keys, "'keys' must not be null or empty");
    return getPcaps(keys, null, -1, -1,
        ConfigurationUtil.isDefaultIncludeReverseTraffic(), false,
        ConfigurationUtil.getDefaultResultSize());
  }

  /*
   * (non-Javadoc)
   * 
   * @see com.cisco.opensoc.hbase.client.IPcapGetter#getPcaps(java.lang.String)
   */
 
  public PcapsResponse getPcaps(String key) throws IOException {
    Assert.hasText(key, "key must not be null or empty");
    return getPcaps(Arrays.asList(key), null, -1, -1,
        ConfigurationUtil.isDefaultIncludeReverseTraffic(), false,
        ConfigurationUtil.getDefaultResultSize());
  }

  /**
   * Always returns the singleton instance.
   * 
   * @return IPcapGetter singleton instance
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  public static IPcapGetter getInstance() throws IOException {
    if (pcapGetterHBase == null) {
      synchronized (PcapGetterHBaseImpl.class) {
        if (pcapGetterHBase == null) {
          pcapGetterHBase = new PcapGetterHBaseImpl();
        }
      }
    }
    return pcapGetterHBase;
  }

  /**
   * Instantiates a new pcap getter h base impl.
   */
  private PcapGetterHBaseImpl() {
  }

  /**
   * Adds reverse keys to the list if the flag 'includeReverseTraffic' is set to
   * true; removes duplicates and sorts the list by ascending order;.
   * 
   * @param keys
   *          input keys
   * @param includeReverseTraffic
   *          flag whether or not to include reverse traffic
   * @return List<String>
   */
  @VisibleForTesting
  List<String> sortKeysByAscOrder(List<String> keys,
      boolean includeReverseTraffic) {
    Assert.notEmpty(keys, "'keys' must not be null");
    if (includeReverseTraffic) {
      keys.addAll(PcapHelper.reverseKey(keys));
    }
    List<String> deDupKeys = removeDuplicateKeys(keys);
    Collections.sort(deDupKeys);
    return deDupKeys;
  }

  /**
   * Removes the duplicate keys.
   * 
   * @param keys
   *          the keys
   * @return the list
   */
  @VisibleForTesting
public
  List<String> removeDuplicateKeys(List<String> keys) {
    Set<String> set = new HashSet<String>(keys);
    return new ArrayList<String>(set);
  }

  /**
   * <p>
   * Returns the sublist starting from the element after the lastRowKey
   * to the last element in the list; if the 'lastRowKey' is not matched
   * the complete list will be returned.
   * </p>
   * 
   * <pre>
   * Eg :
   *  keys = [18800006-1800000b-06-0019-caac, 18800006-1800000b-06-0050-5af6, 18800006-1800000b-11-0035-3810]
   *  lastRowKey = "18800006-1800000b-06-0019-caac-65140-40815"
   *  and the response from this method [18800006-1800000b-06-0050-5af6, 18800006-1800000b-11-0035-3810]
   * </pre>
   * 
   * @param keys
   *          keys
   * @param lastRowKey
   *          last row key of the previous partial response
   * @return List<String>
   */
  @VisibleForTesting
  List<String> getUnprocessedSublistOfKeys(List<String> keys,
      String lastRowKey) {
    Assert.notEmpty(keys, "'keys' must not be null");
    Assert.hasText(lastRowKey, "'lastRowKey' must not be null");
    String partialKey = getTokens(lastRowKey, 5);
    int startIndex = 0;
    for (int i = 0; i < keys.size(); i++) {
      if (partialKey.equals(keys.get(i))) {
        startIndex = i + 1;
        break;
      }
    }
    List<String> unprocessedKeys = keys.subList(startIndex, keys.size());
    return unprocessedKeys;
  }

  /**
   * Returns the first 'noOfTokens' tokens from the given key; token delimiter
   * "-";.
   * 
   * @param key
   *          given key
   * @param noOfTokens
   *          number of tokens to retrieve
   * @return the tokens
   */
  @VisibleForTesting
  String getTokens(String key, int noOfTokens) {
    String delimeter = HBaseConfigConstants.PCAP_KEY_DELIMETER;
    String regex = "\\" + delimeter;
    String[] keyTokens = key.split(regex);
    Assert.isTrue(noOfTokens < keyTokens.length,
        "Invalid value for 'noOfTokens'");
    StringBuffer sbf = new StringBuffer();
    for (int i = 0; i < noOfTokens; i++) {
      sbf.append(keyTokens[i]);
      if (i != (noOfTokens - 1)) {
        sbf.append(HBaseConfigConstants.PCAP_KEY_DELIMETER);
      }

    }
    return sbf.toString();
  }

  /**
   * Process key.
   * 
   * @param pcapsResponse
   *          the pcaps response
   * @param key
   *          the key
   * @param startTime
   *          the start time
   * @param endTime
   *          the end time
   * @param isPartialResponse
   *          the is partial response
   * @param includeDuplicateLastRow
   *          the include duplicate last row
   * @param maxResultSize
   *          the max result size
   * @return the pcaps response
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  @VisibleForTesting
  PcapsResponse processKey(PcapsResponse pcapsResponse, String key,
      long startTime, long endTime, boolean isPartialResponse,
      boolean includeDuplicateLastRow, long maxResultSize) throws IOException {
    HTable table = null;
    Scan scan = null;
    List<Cell> scannedCells = null;
    try {
      // 1. Create start and stop row for the key;
      Map<String, String> keysMap = createStartAndStopRowKeys(key,
          isPartialResponse, includeDuplicateLastRow);

      // 2. if the input key contains all fragments (7) and it is not part
      // of previous partial response (isPartialResponse),
      // 'keysMap' will be null; do a Get; currently not doing any
      // response size related checks for Get;
      // by default all cells from a specific row are sorted by timestamp
      if (keysMap == null) {
        Get get = createGetRequest(key, startTime, endTime);
        List<Cell> cells = executeGetRequest(table, get);
        for (Cell cell : cells) {
          pcapsResponse.addPcaps(CellUtil.cloneValue(cell));
        }
        return pcapsResponse;
      }
      // 3. Create and execute Scan request
      scan = createScanRequest(pcapsResponse, keysMap, startTime, endTime,
          maxResultSize);
      scannedCells = executeScanRequest(table, scan);
      LOGGER.info("scannedCells size :" + scannedCells.size());
      addToResponse(pcapsResponse, scannedCells, maxResultSize);

    } catch (IOException e) {
      LOGGER.error("Exception occurred while fetching Pcaps for the keys :"
          + key, e);
      if (e instanceof ZooKeeperConnectionException
          || e instanceof MasterNotRunningException
          || e instanceof NoServerForRegionException) {
        int maxRetryLimit = ConfigurationUtil.getConnectionRetryLimit();
        System.out.println("maxRetryLimit =" + maxRetryLimit);
        for (int attempt = 1; attempt <= maxRetryLimit; attempt++) {
          System.out.println("attempting  =" + attempt);
          try {
            HBaseConfigurationUtil.closeConnection(); // closing the
            // existing
            // connection
            // and retry,
            // it will
            // create a new
            // HConnection
            scannedCells = executeScanRequest(table, scan);
            addToResponse(pcapsResponse, scannedCells, maxResultSize);
            break;
          } catch (IOException ie) {
            if (attempt == maxRetryLimit) {
              LOGGER.error("Throwing the exception after retrying "
                  + maxRetryLimit + " times.");
              throw e;
            }
          }
        }
      }

    } finally {
      if (table != null) {
        table.close();
      }
    }
    return pcapsResponse;
  }

  /**
   * Adds the to response.
   * 
   * @param pcapsResponse
   *          the pcaps response
   * @param scannedCells
   *          the scanned cells
   * @param maxResultSize
   *          the max result size
   */
  private void addToResponse(PcapsResponse pcapsResponse,
      List<Cell> scannedCells, long maxResultSize) {
    String lastKeyFromCurrentScan = null;
    if (scannedCells != null && scannedCells.size() > 0) {
      lastKeyFromCurrentScan = new String(CellUtil.cloneRow(scannedCells
          .get(scannedCells.size() - 1)));
    }
    // 4. calculate the response size
    Collections.sort(scannedCells, PcapHelper.getCellTimestampComparator());
    for (Cell sortedCell : scannedCells) {
      pcapsResponse.addPcaps(CellUtil.cloneValue(sortedCell));
    }
    if (!pcapsResponse.isResonseSizeWithinLimit(maxResultSize)) {
      pcapsResponse.setStatus(PcapsResponse.Status.PARTIAL); // response size
                                                             // reached
      pcapsResponse.setLastRowKey(new String(lastKeyFromCurrentScan));
    }
  }

  /**
   * Builds start and stop row keys according to the following logic : 1.
   * Creates tokens out of 'key' using pcap_id delimiter ('-') 2. if the input
   * 'key' contains (assume : configuredTokensInRowKey=7 and
   * minimumTokensIninputKey=5): a). 5 tokens
   * ("srcIp-dstIp-protocol-srcPort-dstPort") startKey =
   * "srcIp-dstIp-protocol-srcPort-dstPort-00000-00000" stopKey =
   * "srcIp-dstIp-protocol-srcPort-dstPort-99999-99999" b). 6 tokens
   * ("srcIp-dstIp-protocol-srcPort-dstPort-id1") startKey =
   * "srcIp-dstIp-protocol-srcPort-dstPort-id1-00000" stopKey =
   * "srcIp-dstIp-protocol-srcPort-dstPort-id1-99999"
   * 
   * c). 7 tokens ("srcIp-dstIp-protocol-srcPort-dstPort-id1-id2") 1>. if the
   * key is NOT part of the partial response from previous request, return
   * 'null' 2>. if the key is part of partial response from previous request
   * startKey = "srcIp-dstIp-protocol-srcPort-dstPort-id1-(id2+1)"; 1 is added
   * to exclude this key as it was included in the previous request stopKey =
   * "srcIp-dstIp-protocol-srcPort-dstPort-99999-99999"
   * 
   * @param key
   *          the key
   * @param isLastRowKey
   *          if the key is part of partial response
   * @param includeDuplicateLastRow
   *          the include duplicate last row
   * @return Map<String, String>
   */
  @VisibleForTesting
  Map<String, String> createStartAndStopRowKeys(String key,
      boolean isLastRowKey, boolean includeDuplicateLastRow) {
    String delimeter = HBaseConfigConstants.PCAP_KEY_DELIMETER;
    String regex = "\\" + delimeter;
    String[] keyTokens = key.split(regex);

    String startKey = null;
    String endKey = null;
    Map<String, String> map = new HashMap<String, String>();

    int configuredTokensInRowKey = ConfigurationUtil
        .getConfiguredTokensInRowkey();
    int minimumTokensIninputKey = ConfigurationUtil
        .getMinimumTokensInInputkey();
    Assert
        .isTrue(
            minimumTokensIninputKey <= configuredTokensInRowKey,
            "tokens in the input key (separated by '-'), must be less than or equal to the tokens used in hbase table row key ");
    // in case if the input key contains 'configuredTokensInRowKey' tokens and
    // it is NOT a
    // partial response key, do a Get instead of Scan
    if (keyTokens.length == configuredTokensInRowKey) {
      if (!isLastRowKey) {
        return null;
      }
      // it is a partial response key; 'startKey' is same as input partial
      // response key; 'endKey' can be built by replacing
      // (configuredTokensInRowKey - minimumTokensIninputKey) tokens
      // of input partial response key with '99999'
      if (keyTokens.length == minimumTokensIninputKey) {
        return null;
      }
      int appendingTokenSlots = configuredTokensInRowKey
          - minimumTokensIninputKey;
      if (appendingTokenSlots > 0) {
        String partialKey = getTokens(key, minimumTokensIninputKey);
        StringBuffer sbfStartNew = new StringBuffer(partialKey);
        StringBuffer sbfEndNew = new StringBuffer(partialKey);
        for (int i = 0; i < appendingTokenSlots; i++) {
          if (i == (appendingTokenSlots - 1)) {
            if (!includeDuplicateLastRow) {
              sbfStartNew
                  .append(HBaseConfigConstants.PCAP_KEY_DELIMETER)
                  .append(
                      Integer.valueOf(keyTokens[minimumTokensIninputKey + i]) + 1);
            } else {
              sbfStartNew.append(HBaseConfigConstants.PCAP_KEY_DELIMETER)
                  .append(keyTokens[minimumTokensIninputKey + i]);
            }
          } else {
            sbfStartNew.append(HBaseConfigConstants.PCAP_KEY_DELIMETER).append(
                keyTokens[minimumTokensIninputKey + i]);
          }
          sbfEndNew.append(HBaseConfigConstants.PCAP_KEY_DELIMETER).append(
              getMaxLimitForAppendingTokens());
        }
        startKey = sbfStartNew.toString();
        endKey = sbfEndNew.toString();
      }
    } else {
      StringBuffer sbfStart = new StringBuffer(key);
      StringBuffer sbfEnd = new StringBuffer(key);
      for (int i = keyTokens.length; i < configuredTokensInRowKey; i++) {
        sbfStart.append(HBaseConfigConstants.PCAP_KEY_DELIMETER).append(
            getMinLimitForAppendingTokens());
        sbfEnd.append(HBaseConfigConstants.PCAP_KEY_DELIMETER).append(
            getMaxLimitForAppendingTokens());
      }
      startKey = sbfStart.toString();
      endKey = sbfEnd.toString();
    }
    map.put(HBaseConfigConstants.START_KEY, startKey);
    map.put(HBaseConfigConstants.END_KEY, endKey);

    return map;
  }

  /**
   * Returns false if keys is empty or null AND lastRowKey is null or
   * empty; otherwise returns true;.
   * 
   * @param keys
   *          input row keys
   * @param lastRowKey
   *          partial response key
   * @return boolean
   */
  @VisibleForTesting
  boolean checkIfValidInput(List<String> keys, String lastRowKey) {
    if (CollectionUtils.isEmpty(keys)
        && StringUtils.isEmpty(lastRowKey)) {
      return false;
    }
    return true;
  }

  /**
   * Executes the given Get request.
   * 
   * @param table
   *          hbase table
   * @param get
   *          Get
   * @return List<Cell>
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  private List<Cell> executeGetRequest(HTable table, Get get)
      throws IOException {
    LOGGER.info("Get :" + get.toString());
    table = (HTable) HBaseConfigurationUtil.getConnection().getTable(
        ConfigurationUtil.getTableName());
    Result result = table.get(get);
    List<Cell> cells = result.getColumnCells(
        ConfigurationUtil.getColumnFamily(),
        ConfigurationUtil.getColumnQualifier());
    return cells;
  }

  /**
   * Execute scan request.
   * 
   * @param table
   *          hbase table
   * @param scan
   *          the scan
   * @return the list
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  private List<Cell> executeScanRequest(HTable table, Scan scan)
      throws IOException {
    LOGGER.info("Scan :" + scan.toString());
    table = (HTable) HBaseConfigurationUtil.getConnection().getTable(
    		ConfigurationUtil.getConfiguration().getString("hbase.table.name"));
    ResultScanner resultScanner = table.getScanner(scan);
    List<Cell> scannedCells = new ArrayList<Cell>();
    for (Result result = resultScanner.next(); result != null; result = resultScanner
        .next()) {
      List<Cell> cells = result.getColumnCells(
          ConfigurationUtil.getColumnFamily(),
          ConfigurationUtil.getColumnQualifier());
      if (cells != null) {
        for (Cell cell : cells) {
          scannedCells.add(cell);
        }
      }
    }
    return scannedCells;
  }

  /**
   * Creates the get request.
   * 
   * @param key
   *          the key
   * @param startTime
   *          the start time
   * @param endTime
   *          the end time
   * @return the gets the
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  @VisibleForTesting
  Get createGetRequest(String key, long startTime, long endTime)
      throws IOException {
    Get get = new Get(Bytes.toBytes(key));
    // set family name
    get.addFamily(ConfigurationUtil.getColumnFamily());

    // set column family, qualifier
    get.addColumn(ConfigurationUtil.getColumnFamily(),
        ConfigurationUtil.getColumnQualifier());

    // set max versions
    get.setMaxVersions(ConfigurationUtil.getMaxVersions());

    // set time range
    setTimeRangeOnGet(get, startTime, endTime);
    return get;
  }

  /**
   * Creates the scan request.
   * 
   * @param pcapsResponse
   *          the pcaps response
   * @param keysMap
   *          the keys map
   * @param startTime
   *          the start time
   * @param endTime
   *          the end time
   * @param maxResultSize
   *          the max result size
   * @return the scan
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  @VisibleForTesting
  Scan createScanRequest(PcapsResponse pcapsResponse,
      Map<String, String> keysMap, long startTime, long endTime,
      long maxResultSize) throws IOException {
    Scan scan = new Scan();
    // set column family, qualifier
    scan.addColumn(ConfigurationUtil.getColumnFamily(),
        ConfigurationUtil.getColumnQualifier());

    // set start and stop keys
    scan.setStartRow(keysMap.get(HBaseConfigConstants.START_KEY).getBytes());
    scan.setStopRow(keysMap.get(HBaseConfigConstants.END_KEY).getBytes());

    // set max results size : remaining size = max results size - ( current
    // pcaps response size + possible maximum row size)
    long remainingSize = maxResultSize
        - (pcapsResponse.getResponseSize() + ConfigurationUtil.getMaxRowSize());

    if (remainingSize > 0) {
      scan.setMaxResultSize(remainingSize);
    }
    // set max versions
    scan.setMaxVersions(ConfigurationUtil.getConfiguration().getInt(
        "hbase.table.column.maxVersions"));

    // set time range
    setTimeRangeOnScan(scan, startTime, endTime);
    return scan;
  }

  /**
   * Sets the time range on scan.
   * 
   * @param scan
   *          the scan
   * @param startTime
   *          the start time
   * @param endTime
   *          the end time
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  private void setTimeRangeOnScan(Scan scan, long startTime, long endTime)
      throws IOException {
    boolean setTimeRange = true;
    if (startTime < 0 && endTime < 0) {
      setTimeRange = false;
    }
    if (setTimeRange) {
      if (startTime < 0) {
        startTime = 0;
      } else {
        startTime = PcapHelper.convertToDataCreationTimeUnit(startTime);
      }
      if (endTime < 0) {
        endTime = Long.MAX_VALUE;
      } else {
        endTime = PcapHelper.convertToDataCreationTimeUnit(endTime);
      }
      Assert.isTrue(startTime < endTime,
          "startTime value must be less than endTime value");
      scan.setTimeRange(startTime, endTime);
    }
  }

  /**
   * Sets the time range on get.
   * 
   * @param get
   *          the get
   * @param startTime
   *          the start time
   * @param endTime
   *          the end time
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  private void setTimeRangeOnGet(Get get, long startTime, long endTime)
      throws IOException {
    boolean setTimeRange = true;
    if (startTime < 0 && endTime < 0) {
      setTimeRange = false;
    }
    if (setTimeRange) {
      if (startTime < 0) {
        startTime = 0;
      } else {
        startTime = PcapHelper.convertToDataCreationTimeUnit(startTime);
      }
      if (endTime < 0) {
        endTime = Long.MAX_VALUE;
      } else {
        endTime = PcapHelper.convertToDataCreationTimeUnit(endTime);
      }
      Assert.isTrue(startTime < endTime,
          "startTime value must be less than endTime value");
      get.setTimeRange(startTime, endTime);
    }
  }

  /**
   * Gets the min limit for appending tokens.
   * 
   * @return the min limit for appending tokens
   */
  private String getMinLimitForAppendingTokens() {
    int digits = ConfigurationUtil.getAppendingTokenDigits();
    StringBuffer sbf = new StringBuffer();
    for (int i = 0; i < digits; i++) {
      sbf.append("0");
    }
    return sbf.toString();
  }

  /**
   * Gets the max limit for appending tokens.
   * 
   * @return the max limit for appending tokens
   */
  private String getMaxLimitForAppendingTokens() {
    int digits = ConfigurationUtil.getAppendingTokenDigits();
    StringBuffer sbf = new StringBuffer();
    for (int i = 0; i < digits; i++) {
      sbf.append("9");
    }
    return sbf.toString();
  }

  /**
   * The main method.
   * 
   * @param args
   *          the arguments
   * 
   * @throws IOException
   *           Signals that an I/O exception has occurred.
   */
  public static void main(String[] args) throws IOException {
    if (args == null || args.length < 2) {
      usage();
      return;
    }
    String outputFileName = null;
    outputFileName = args[1];
    List<String> keys = Arrays.asList(StringUtils.split(args[2], ","));
    System.out.println("Geting keys " + keys);
    long startTime = 0;
    long endTime = Long.MAX_VALUE;
    if (args.length > 3) {
      startTime = Long.valueOf(args[3]);
    }
    if (args.length > 4) {
      endTime = Long.valueOf(args[4]);
    }
    System.out.println("With start time " + startTime + " and end time "
        + endTime);
    PcapGetterHBaseImpl downloader = new PcapGetterHBaseImpl();
    PcapsResponse pcaps = downloader.getPcaps(keys, null, startTime, endTime,
        false, false, 6);
    File file = new File(outputFileName);
    FileUtils.write(file, "", false);
    FileUtils.writeByteArrayToFile(file, pcaps.getPcaps(), true);
  }

  /**
   * Usage.
   */
  private static void usage() {
    System.out.println("java " + PcapGetterHBaseImpl.class.getName() // $codepro.audit.disable
        // debuggingCode
        + " <zk quorum> <output file> <start key> [stop key]");
  }

}