@Override
  public void handle(ChannelHandlerContext ctx, FullHttpRequest request) {

    Tracker.getInstance().track(request);

    final String tenantId = request.headers().get("tenantId");

    HttpRequestWithDecodedQueryParams requestWithParams =
        (HttpRequestWithDecodedQueryParams) request;

    // get the query param
    List<String> query = requestWithParams.getQueryParams().get("query");
    if (query == null || query.size() != 1) {
      DefaultHandler.sendErrorResponse(
          ctx, request, "Invalid Query String", HttpResponseStatus.BAD_REQUEST);
      return;
    }

    // get the include_enum_values param to determine if results should contain enum values if
    // applicable
    List<String> includeEnumValues = requestWithParams.getQueryParams().get("include_enum_values");

    if ((includeEnumValues != null)
        && (includeEnumValues.size() != 0)
        && (includeEnumValues.get(0).compareToIgnoreCase("true") == 0)) {
      // include_enum_values is present and set to true, use the ENUMS_DISCOVERY_MODULES as the
      // discoveryHandle
      discoveryHandle =
          (DiscoveryIO)
              ModuleLoader.getInstance(DiscoveryIO.class, CoreConfig.ENUMS_DISCOVERY_MODULES);
    } else {
      // default discoveryHandle to DISCOVERY_MODULES
      discoveryHandle =
          (DiscoveryIO) ModuleLoader.getInstance(DiscoveryIO.class, CoreConfig.DISCOVERY_MODULES);
    }

    if (discoveryHandle == null) {
      sendResponse(ctx, request, null, HttpResponseStatus.NOT_FOUND);
      return;
    }

    try {
      List<SearchResult> searchResults = discoveryHandle.search(tenantId, query.get(0));
      sendResponse(ctx, request, getSerializedJSON(searchResults), HttpResponseStatus.OK);
    } catch (Exception e) {
      log.error(
          String.format("Exception occurred while trying to get metrics index for %s", tenantId),
          e);
      DefaultHandler.sendErrorResponse(
          ctx, request, "Error getting metrics index", HttpResponseStatus.INTERNAL_SERVER_ERROR);
    }
  }
  private void sendResponse(
      ChannelHandlerContext channel,
      HttpRequest request,
      String messageBody,
      HttpResponseStatus status) {

    HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);

    if (messageBody != null && !messageBody.isEmpty()) {
      response.setContent(ChannelBuffers.copiedBuffer(messageBody, Constants.DEFAULT_CHARSET));
    }

    Tracker.getInstance().trackResponse(request, response);
    HttpResponder.respond(channel, request, response);
  }
  @Override
  public void handle(ChannelHandlerContext ctx, HttpRequest request) {

    Tracker.getInstance().track(request);

    final String tenantId = request.getHeader("tenantId");

    if (!(request instanceof HTTPRequestWithDecodedQueryParams)) {
      sendResponse(
          ctx, request, "Missing query params: from, to, points", HttpResponseStatus.BAD_REQUEST);
      return;
    }

    final String body = request.getContent().toString(Constants.DEFAULT_CHARSET);

    if (body == null || body.isEmpty()) {
      sendResponse(
          ctx,
          request,
          "Invalid body. Expected JSON array of metrics.",
          HttpResponseStatus.BAD_REQUEST);
      return;
    }

    List<String> locators = new ArrayList<String>();
    try {
      locators.addAll(getLocatorsFromJSONBody(tenantId, body));
    } catch (Exception ex) {
      log.debug(ex.getMessage(), ex);
      sendResponse(ctx, request, ex.getMessage(), HttpResponseStatus.BAD_REQUEST);
      return;
    }

    if (locators.size() > maxMetricsPerRequest) {
      sendResponse(
          ctx,
          request,
          "Too many metrics fetch in a single call. Max limit is " + maxMetricsPerRequest + ".",
          HttpResponseStatus.BAD_REQUEST);
      return;
    }

    HTTPRequestWithDecodedQueryParams requestWithParams =
        (HTTPRequestWithDecodedQueryParams) request;
    final Timer.Context httpBatchMetricsFetchTimerContext = httpBatchMetricsFetchTimer.time();
    try {
      RollupsQueryParams params = PlotRequestParser.parseParams(requestWithParams.getQueryParams());
      Map<Locator, MetricData> results =
          getRollupByGranularity(
              tenantId,
              locators,
              params.getRange().getStart(),
              params.getRange().getStop(),
              params.getGranularity(tenantId));
      JSONObject metrics = serializer.transformRollupData(results, params.getStats());
      final JsonElement element = parser.parse(metrics.toString());
      final String jsonStringRep = gson.toJson(element);
      sendResponse(ctx, request, jsonStringRep, HttpResponseStatus.OK);
    } catch (InvalidRequestException e) {
      log.debug(e.getMessage());
      sendResponse(ctx, request, e.getMessage(), HttpResponseStatus.BAD_REQUEST);
    } catch (SerializationException e) {
      log.debug(e.getMessage(), e);
      sendResponse(ctx, request, e.getMessage(), HttpResponseStatus.INTERNAL_SERVER_ERROR);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      sendResponse(ctx, request, e.getMessage(), HttpResponseStatus.INTERNAL_SERVER_ERROR);
    } finally {
      httpBatchMetricsFetchTimerContext.stop();
    }
  }