/**
   * Asynchronous HBase scan read as RAW qualifier
   *
   * @param scan
   * @param listener
   * @throws Exception
   */
  protected InternalReadReport asyncStreamRead(
      EntityDefinition ed, Scan scan, QualifierCreationListener listener) throws IOException {
    //		_init();
    long counter = 0;
    long startTimestamp = 0;
    long stopTimestamp = 0;
    InternalScanner scanner = this.getCurrentRegion().getScanner(scan);
    List<Cell> results = new ArrayList<Cell>();
    try {
      boolean hasMoreRows; // false by default
      do {
        hasMoreRows = scanner.next(results);
        Map<String, byte[]> kvMap = new HashMap<String, byte[]>();
        if (!results.isEmpty()) {
          counter++;
          byte[] row = results.get(0).getRow();
          //					if(ed.isTimeSeries()){
          long timestamp = RowkeyBuilder.getTimestamp(row, ed);
          // Min
          if (startTimestamp == 0 || startTimestamp > timestamp) {
            startTimestamp = timestamp;
          }
          // Max
          if (stopTimestamp == 0 || stopTimestamp < timestamp) {
            stopTimestamp = timestamp;
          }
          //					}

          for (Cell kv : results) {
            String qualifierName = Bytes.toString(kv.getQualifier());
            Qualifier qualifier = null;
            if (!ed.isTag(qualifierName)) {
              qualifier = ed.getQualifierNameMap().get(qualifierName);
              if (qualifier == null) {
                LOG.error("qualifier for field " + qualifierName + " not exist");
                throw new IOException(
                    new NullPointerException("qualifier for field " + qualifierName + " is null"));
              }
              qualifierName = qualifier.getDisplayName();
            }
            if (kv.getValue() != null) kvMap.put(qualifierName, kv.getValue());
          }

          //					LOG.info("DEBUG: timestamp="+timestamp+",
          // keys=["+StringUtils.join(kvMap.keySet(),",")+"]");

          if (!kvMap.isEmpty()) listener.qualifierCreated(kvMap);
          results.clear();
        } else {
          if (LOG.isDebugEnabled()) LOG.warn("Empty batch of KeyValue");
        }
      } while (hasMoreRows);
    } catch (IOException ex) {
      LOG.error(ex.getMessage(), ex);
      throw ex;
    } finally {
      if (scanner != null) {
        scanner.close();
      }
    }

    return new InternalReadReport(counter, startTimestamp, stopTimestamp);
  }
  /**
   * Asynchronous HBase scan read as entity
   *
   * @param scan
   * @throws java.io.IOException
   */
  protected InternalReadReport asyncStreamRead(
      EntityDefinition ed, Scan scan, EntityCreationListener listener) throws IOException {
    //		_init();
    long counter = 0;
    long startTimestamp = 0;
    long stopTimestamp = 0;

    InternalScanner scanner = this.getCurrentRegion().getScanner(scan);
    List<Cell> results = new ArrayList<Cell>();
    try {
      boolean hasMoreRows;
      GenericMetricShadowEntity singleMetricEntity = null;
      do {
        hasMoreRows = scanner.next(results);
        Map<String, byte[]> kvMap = new HashMap<String, byte[]>();
        if (!results.isEmpty()) {
          counter++;
          byte[] row = results.get(0).getRow();
          long timestamp = RowkeyBuilder.getTimestamp(row, ed);

          // Min
          if (startTimestamp == 0 || startTimestamp > timestamp) {
            startTimestamp = timestamp;
          }

          // Max
          if (stopTimestamp == 0 || stopTimestamp < timestamp) {
            stopTimestamp = timestamp;
          }

          for (Cell kv : results) {
            String qualifierName = Bytes.toString(kv.getQualifier());
            //						Qualifier qualifier = null;
            //						if(!ed.isTag(qualifierName)){
            //							qualifier = ed.getQualifierNameMap().get(qualifierName);
            //							if(qualifier == null){
            //								LOG.error("qualifier for   " + qualifierName + " not exist");
            //								throw new NullPointerException("qualifier for field "+qualifierName+" not
            // exist");
            //							}
            //						}
            if (kv.getValue() != null) kvMap.put(qualifierName, kv.getValue());
          }

          // LOG.info("DEBUG: timestamp="+timestamp+",
          // keys=["+StringUtils.join(kvMap.keySet(),",")+"]");

          InternalLog internalLog = HBaseInternalLogHelper.buildObject(ed, row, timestamp, kvMap);
          if (internalLog != null) {
            TaggedLogAPIEntity logAPIEntity = null;
            try {
              logAPIEntity = HBaseInternalLogHelper.buildEntity(internalLog, ed);
              if (logAPIEntity instanceof GenericMetricEntity) {
                if (singleMetricEntity == null)
                  singleMetricEntity = new GenericMetricShadowEntity();
                GenericMetricEntity e = (GenericMetricEntity) logAPIEntity;
                if (e.getValue() != null) {
                  int count = e.getValue().length;
                  @SuppressWarnings("unused")
                  Class<?> cls = ed.getMetricDefinition().getSingleTimestampEntityClass();
                  for (int i = 0; i < count; i++) {
                    long ts =
                        logAPIEntity.getTimestamp() + i * ed.getMetricDefinition().getInterval();
                    // exclude those entity which is not within the time range in search condition.
                    // [start, end)
                    singleMetricEntity.setTimestamp(ts);
                    singleMetricEntity.setTags(e.getTags());
                    singleMetricEntity.setValue(e.getValue()[i]);
                    // Min
                    if (startTimestamp == 0 || startTimestamp > ts) startTimestamp = ts;
                    // Max
                    if (stopTimestamp == 0 || stopTimestamp < ts) stopTimestamp = ts;
                    listener.entityCreated(singleMetricEntity);
                  }
                }
              } else {
                // LOG.info("DEBUG: rowKey="+logAPIEntity.getEncodedRowkey());
                listener.entityCreated(logAPIEntity);
              }
            } catch (Exception e) {
              if (internalLog != null) {
                LOG.error(
                    "Got exception to handle " + internalLog.toString() + ": " + e.getMessage(), e);
              }
              throw new IOException(e);
            }
          } else {
            LOG.error(
                "Got null to parse internal log for row: " + row.length + " with fields: " + kvMap);
          }
          results.clear();
        } else {
          if (LOG.isDebugEnabled()) LOG.warn("Empty batch of KeyValue");
        }
      } while (hasMoreRows);
    } catch (IOException ex) {
      LOG.error(ex.getMessage(), ex);
      throw ex;
    } finally {
      if (scanner != null) {
        scanner.close();
      }
    }
    return new InternalReadReport(counter, startTimestamp, stopTimestamp);
  }